Skip to main content

islet_rmm/rec/
sea.rs

1use crate::realm::rd::Rd;
2use crate::rec::context::{set_reg, RegOffset};
3use crate::rec::Rec;
4use crate::rmi::error::Error;
5use crate::rmi::rec::run::{EntryFlag, Run};
6
7use aarch64_cpu::registers::Readable;
8use aarch64_cpu::registers::{HPFAR_EL2, SPSR_EL2};
9use armv9a::regs::*;
10use armv9a::InMemoryRegister; // re-exported from tock_registers
11
12pub fn host_sea_inject(rec: &mut Rec<'_>, run: &Run) -> Result<(), Error> {
13    let flags = run.entry_flags();
14
15    // Host has not completed emulation for an Emulatable Abort.
16    if flags.get_masked(EntryFlag::INJECT_SEA) == 0 {
17        return Ok(());
18    }
19
20    let esr_el2 = rec.context.sys_regs.esr_el2;
21    let esr = EsrEl2::new(esr_el2);
22    let ec = esr.get_masked_value(EsrEl2::EC);
23
24    if ec != ESR_EL2_EC_DATA_ABORT {
25        return Ok(());
26    }
27
28    // [Spec] If the most recent exit was due to Data Abort at an Unprotected IPA
29    // and enter.flags.inject_sea == RMI_INJECT_SEA,
30    // then a Synchronous External Abort is taken to the Realm.
31    let fault_ipa = rec.context.sys_regs.hpfar & (HPFAR_EL2::FIPA.mask << HPFAR_EL2::FIPA.shift);
32    let fault_ipa = (fault_ipa << 8) as usize;
33
34    let raw_ptr: *const Rd = rec.owner()? as *const Rd;
35    let rd: &Rd = unsafe { raw_ptr.as_ref().expect("REASON") }; // FIXME
36    if !rd.addr_in_par(fault_ipa) && fault_ipa < rd.ipa_size() {
37        inject_sea(rec, esr_el2, rec.context.sys_regs.far_el2);
38    }
39
40    Ok(())
41}
42
43pub fn inject_sea(rec: &mut Rec<'_>, esr_el2: u64, far_el2: u64) {
44    let mut esr_el1 = esr_el2 & !(EsrEl2::EC | EsrEl2::FNV | EsrEl2::S1PTW | EsrEl2::DFSC);
45    let mut ec = esr_el2 & EsrEl2::EC;
46    let context = &mut rec.context;
47    let spsr_el2: InMemoryRegister<u64, SPSR_EL2::Register> =
48        InMemoryRegister::new(context.spsr_el2);
49    let elr_el2 = context.elr_el2;
50    let spsr_m = spsr_el2.read(SPSR_EL2::M);
51    if spsr_m != SPSR_EL2::M::EL0t.into() {
52        ec |= 1 << EsrEl2::EC.trailing_zeros();
53    }
54    esr_el1 |= ec;
55    esr_el1 |= EsrEl2::EA;
56    esr_el1 |= 0b010000; // Synchronous External Abort (SEA)
57    const VBAR_CURRENT_SP0_OFFSET: u64 = 0x0;
58    const VBAR_CURRENT_SPX_OFFSET: u64 = 0x200;
59    const VBAR_LOWER_AARCH64_OFFSET: u64 = 0x400;
60    let mut vector_entry = {
61        match spsr_el2.read_as_enum(SPSR_EL2::M) {
62            Some(SPSR_EL2::M::Value::EL0t) => VBAR_LOWER_AARCH64_OFFSET,
63            Some(SPSR_EL2::M::Value::EL1t) => VBAR_CURRENT_SP0_OFFSET,
64            Some(SPSR_EL2::M::Value::EL1h) => VBAR_CURRENT_SPX_OFFSET,
65            _ => panic!("shouldn't be reached here"), // Realms run at aarch64 state only (i.e. no aarch32)
66        }
67    };
68    vector_entry += context.sys_regs.vbar;
69
70    let pstate: u64 = (SPSR_EL2::D::SET
71        + SPSR_EL2::A::SET
72        + SPSR_EL2::I::SET
73        + SPSR_EL2::F::SET
74        + SPSR_EL2::M::EL1h)
75        .into();
76
77    context.sys_regs.esr_el1 = esr_el1;
78    context.sys_regs.far = far_el2;
79    context.sys_regs.elr = elr_el2;
80    context.sys_regs.spsr = spsr_el2.get();
81    context.elr_el2 = vector_entry;
82    let _ = set_reg(rec, RegOffset::PSTATE, pstate as usize);
83}