islet_rmm/rmi/rec/
exit.rs1use crate::event::realmexit::*;
2use crate::event::RsiHandle;
3use crate::get_granule;
4use crate::get_granule_if;
5use crate::granule::GranuleState;
6use crate::granule::GRANULE_MASK;
7use crate::realm::mm::rtt::RTT_PAGE_LEVEL;
8use crate::realm::mm::stage2_tte::S2TTE;
9use crate::realm::rd::Rd;
10use crate::rec::context::get_reg;
11use crate::rec::sea::inject_sea;
12use crate::rec::{
13 Rec, RmmRecEmulatableAbort::EmulatableAbort, RmmRecEmulatableAbort::NotEmulatableAbort,
14};
15use crate::rmi::error::Error;
16use crate::rmi::rec::run::Run;
17use crate::Monitor;
18use crate::{rmi, rsi};
19use armv9a::{
20 EsrEl2, DFSC_PERM_FAULTS, DFSC_PERM_FAULT_MASK, EMULATABLE_ABORT_MASK, INST_ABORT_MASK, ISS,
21 NON_EMULATABLE_ABORT_MASK, SERROR_MASK, WFX_MASK,
22};
23
24use aarch64_cpu::registers::HPFAR_EL2;
25
26#[derive(Debug)]
27enum AbortHandleType {
28 SeaInject,
29 AddrSizeFaultInject,
30 NonEmulatableExit,
31 EmulatableExit,
32 DataAbortExit,
33}
34
35pub fn handle_realm_exit(
36 realm_exit_res: [usize; 4],
37 rmm: &Monitor,
38 rec: &mut Rec<'_>,
39 run: &mut Run,
40) -> Result<(bool, usize), Error> {
41 let mut return_to_ns = true;
42 let ret = match RecExitReason::from(realm_exit_res[0]) {
43 #[cfg(not(kani))]
44 RecExitReason::Sync(ExitSyncType::RSI) => {
46 trace!("REC_ENTER ret: {:#X?}", realm_exit_res);
47 let cmd = realm_exit_res[1];
48 let mut ret = rmi::SUCCESS;
49
50 let mut rsi_ctx = rsi::constraint::validate(cmd);
51 if rmm.handle_rsi(&mut rsi_ctx, rec, run) == RsiHandle::RET_SUCCESS {
53 if rsi_ctx.ret_slice()[0] == rmi::SUCCESS_REC_ENTER {
54 return_to_ns = false;
55 }
56 ret = rsi_ctx.ret_slice()[0];
57 } else {
58 return_to_ns = false;
59 }
60 ret
61 }
62 RecExitReason::Sync(ExitSyncType::DataAbort) => {
63 match handle_data_abort(realm_exit_res, rec, run)? {
64 rmi::SUCCESS => {
65 run.set_exit_reason(rmi::EXIT_SYNC);
66 run.set_hpfar(realm_exit_res[2] as u64);
67 rmi::SUCCESS
68 }
69 rmi::SUCCESS_REC_ENTER => {
70 return_to_ns = false;
71 rmi::SUCCESS
72 }
73 _ => panic!("shouldn't be reached here"),
74 }
75 }
76 RecExitReason::IRQ => {
77 run.set_exit_reason(rmi::EXIT_IRQ);
78 run.set_esr(0);
79 run.set_hpfar(0);
80 run.set_far(0);
81 rmi::SUCCESS
82 }
83 RecExitReason::SError => {
84 run.set_exit_reason(rmi::EXIT_SERROR);
85 run.set_esr(realm_exit_res[1] as u64 & SERROR_MASK);
86 run.set_hpfar(0);
87 run.set_far(0);
88 rmi::SUCCESS
89 }
90 RecExitReason::Sync(ExitSyncType::InstAbort) => {
91 match handle_inst_abort(realm_exit_res, rec, run)? {
92 rmi::SUCCESS => {
93 run.set_exit_reason(rmi::EXIT_SYNC);
94 run.set_hpfar(realm_exit_res[2] as u64);
95 rmi::SUCCESS
96 }
97 rmi::SUCCESS_REC_ENTER => {
98 return_to_ns = false;
99 rmi::SUCCESS
100 }
101 _ => panic!("shouldn't be reached here"),
102 }
103 }
104 RecExitReason::Sync(ExitSyncType::WFx) => {
105 let esr_el2 = realm_exit_res[1] as u64;
106 run.set_exit_reason(rmi::EXIT_SYNC);
107 run.set_esr(esr_el2 & WFX_MASK);
108 run.set_hpfar(0);
109 run.set_far(0);
110 rmi::SUCCESS
111 }
112 RecExitReason::Sync(ExitSyncType::Undefined) => {
113 run.set_exit_reason(rmi::EXIT_SYNC);
114 run.set_esr(realm_exit_res[1] as u64);
115 run.set_hpfar(realm_exit_res[2] as u64);
116 let _ = match get_sys_write_val(rec, realm_exit_res[1] as u64)? {
117 Some(val) => run.set_gpr(0, val),
118 None => unimplemented!(),
119 };
120 rmi::SUCCESS
121 }
122 _ => rmi::SUCCESS,
123 };
124
125 Ok((return_to_ns, ret))
126}
127
128fn get_sys_write_val(rec: &Rec<'_>, esr_el2: u64) -> Result<Option<u64>, Error> {
129 let esr = ISS::new(esr_el2);
130 let direction = esr.get_masked_value(ISS::Direction);
132 let rt = esr.get_masked_value(ISS::Rt) as usize;
133 if direction != 0 {
138 return Ok(None);
139 }
140 let write_val = match rt == 31 {
142 true => 0, false => get_reg(rec, rt)? as u64,
144 };
146 Ok(Some(write_val))
147}
148
149fn get_write_val(rec: &Rec<'_>, esr_el2: u64) -> Result<u64, Error> {
150 let esr_el2 = EsrEl2::new(esr_el2);
151 let rt = esr_el2.get_masked_value(EsrEl2::SRT) as usize;
152 let write_val = match rt == 31 {
153 true => 0, false => get_reg(rec, rt)? as u64 & esr_el2.get_access_size_mask(),
155 };
156 Ok(write_val)
157}
158
159fn abort_handle_type(
160 rd: &Rd,
161 exit: ExitSyncType,
162 esr_el2: u64,
163 fault_ipa: usize,
164) -> Result<AbortHandleType, Error> {
165 let is_protected_ipa = rd.addr_in_par(fault_ipa);
166 let (s2tte, last_level) =
167 S2TTE::get_s2tte(rd, fault_ipa, RTT_PAGE_LEVEL, Error::RmiErrorRtt(0))?;
168 let esr = EsrEl2::new(esr_el2);
169
170 if is_protected_ipa {
171 if s2tte.is_assigned_empty() || s2tte.is_unassigned_empty() {
172 return Ok(AbortHandleType::SeaInject);
173 }
174 if s2tte.is_unassigned_ram() || s2tte.is_destroyed() {
175 return Ok(AbortHandleType::NonEmulatableExit);
176 }
177 } else if fault_ipa > rd.ipa_size() {
178 return Ok(AbortHandleType::AddrSizeFaultInject);
179 } else {
180 if exit == ExitSyncType::InstAbort {
182 return Ok(AbortHandleType::SeaInject);
183 }
184 let dfsc = esr.get_masked_value(EsrEl2::DFSC) & DFSC_PERM_FAULT_MASK;
185 let mut check_isv = false;
186 if s2tte.is_unassigned_ns()
187 || (s2tte.is_assigned_ns(last_level) && dfsc == DFSC_PERM_FAULTS)
188 {
189 check_isv = true;
190 }
191 if check_isv {
192 if esr.get_masked_value(EsrEl2::ISV) == 1 {
193 return Ok(AbortHandleType::EmulatableExit);
194 } else {
195 return Ok(AbortHandleType::NonEmulatableExit);
196 }
197 }
198 }
199 Ok(AbortHandleType::DataAbortExit)
200}
201
202fn handle_data_abort(
203 realm_exit_res: [usize; 4],
204 rec: &mut Rec<'_>,
205 run: &mut Run,
206) -> Result<usize, Error> {
207 let rd_granule = get_granule_if!(rec.owner()?, GranuleState::RD)?;
208 let rd = rd_granule.content::<Rd>()?;
209
210 let esr_el2 = realm_exit_res[1] as u64;
211 let hpfar_el2 = realm_exit_res[2] as u64;
212 let far_el2 = realm_exit_res[3] as u64;
213
214 let fault_ipa = hpfar_el2 & (HPFAR_EL2::FIPA.mask << HPFAR_EL2::FIPA.shift);
215 let fault_ipa = (fault_ipa << 8) as usize;
216
217 let ret = match abort_handle_type(&rd, ExitSyncType::DataAbort, esr_el2, fault_ipa)? {
218 AbortHandleType::SeaInject | AbortHandleType::AddrSizeFaultInject => {
219 inject_sea(rec, esr_el2, far_el2);
220 rmi::SUCCESS_REC_ENTER
221 }
222 AbortHandleType::NonEmulatableExit => {
223 rec.set_emulatable_abort(NotEmulatableAbort);
224 if rd.addr_in_par(fault_ipa) {
225 run.set_esr(esr_el2 & NON_EMULATABLE_ABORT_MASK);
226 } else {
227 run.set_esr(esr_el2 & NON_EMULATABLE_ABORT_MASK);
228 }
232 run.set_far(0);
233 rmi::SUCCESS
234 }
235 AbortHandleType::EmulatableExit => {
236 if esr_el2 & EsrEl2::WNR != 0 {
237 let write_val = get_write_val(rec, esr_el2)?;
238 run.set_gpr(0, write_val)?;
239 }
240 rec.set_emulatable_abort(EmulatableAbort);
241 run.set_esr(esr_el2 & EMULATABLE_ABORT_MASK);
242 run.set_far(far_el2 & !(GRANULE_MASK as u64));
243 rmi::SUCCESS
244 }
245 AbortHandleType::DataAbortExit => {
246 rec.set_emulatable_abort(NotEmulatableAbort);
247 run.set_esr(esr_el2 & NON_EMULATABLE_ABORT_MASK);
248 run.set_far(0);
249 rmi::SUCCESS
250 }
251 };
252
253 Ok(ret)
254}
255
256fn handle_inst_abort(
257 realm_exit_res: [usize; 4],
258 rec: &mut Rec<'_>,
259 run: &mut Run,
260) -> Result<usize, Error> {
261 let rd_granule = get_granule_if!(rec.owner()?, GranuleState::RD)?;
262 let rd = rd_granule.content::<Rd>()?;
263
264 let esr_el2 = realm_exit_res[1] as u64;
265 let hpfar_el2 = realm_exit_res[2] as u64;
266 let far_el2 = realm_exit_res[3] as u64;
267
268 let fault_ipa = hpfar_el2 & (HPFAR_EL2::FIPA.mask << HPFAR_EL2::FIPA.shift);
269 let fault_ipa = (fault_ipa << 8) as usize;
270
271 let ret = match abort_handle_type(&rd, ExitSyncType::InstAbort, esr_el2, fault_ipa)? {
272 AbortHandleType::SeaInject | AbortHandleType::AddrSizeFaultInject => {
273 inject_sea(rec, esr_el2, far_el2);
274 rmi::SUCCESS_REC_ENTER
275 }
276 AbortHandleType::NonEmulatableExit => {
277 run.set_esr(esr_el2 & INST_ABORT_MASK);
278 run.set_far(0);
279 rmi::SUCCESS
280 }
281 _ => panic!("Shoudn't be reaching here"),
282 };
283 Ok(ret)
284}