Skip to main content

islet_rmm/rmi/rec/
exit.rs

1use 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        // `rsi` is currently not reachable in model checking harnesses
45        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            // set default value
52            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    // direction: 0b0 - write, 0b1 - read
131    let direction = esr.get_masked_value(ISS::Direction);
132    let rt = esr.get_masked_value(ISS::Rt) as usize;
133    // We know that ExitSyncType::Undefined is from sys regs access trap.
134    // Thus, omit checking the EC again.
135
136    // MSR (read) case
137    if direction != 0 {
138        return Ok(None);
139    }
140    // MRS (write) case
141    let write_val = match rt == 31 {
142        true => 0, // xzr
143        false => get_reg(rec, rt)? as u64,
144        //rec.context.gp_regs[rt]);
145    };
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, // xzr
154        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        // for unprotected IPA
181        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                // FIXME: According to the RMM Spec, Non emulatable abort at unprotected ipa
229                // should carry ELR_EL2's IL bit. However, ACS test  checks the opposite.
230                //run.set_esr(esr_el2 & (NON_EMULATABLE_ABORT_MASK | EsrEl2::IL));
231            }
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}