Skip to main content

islet_rmm/rec/
mod.rs

1pub mod context;
2pub mod gic;
3pub mod mmio;
4pub mod pauth;
5pub mod pmu;
6pub mod sea;
7pub mod simd;
8pub mod timer;
9
10use crate::realm::rd::Rd;
11use crate::rec::context::Context;
12use crate::rmi::error::Error;
13use crate::rmi::rec::params::NR_AUX;
14use crate::rmm_exit;
15use crate::rsi::attestation::MAX_CHALLENGE_SIZE;
16use aarch64_cpu::registers::*;
17
18use core::cell::OnceCell;
19use vmsa::guard::Content;
20
21const MAX_RECS_ORDER_VALUE: u64 = 4;
22
23#[derive(Copy, Clone, Debug)]
24pub enum RecAuxIndex {
25    SIMD = 0,
26    PMU = 1,
27    Undefined,
28}
29
30#[derive(Clone, Copy, Debug, PartialEq)]
31pub enum RmmRecAttestState {
32    AttestInProgress,
33    NoAttestInProgress,
34}
35
36#[derive(Clone, Copy, Debug, PartialEq)]
37pub enum RmmRecEmulatableAbort {
38    EmulatableAbort,
39    NotEmulatableAbort,
40}
41
42#[derive(Copy, Clone, Debug, PartialEq)]
43pub enum State {
44    Ready = 1,
45    Running = 2,
46}
47
48#[derive(Debug)]
49struct Ripas {
50    start: u64,
51    end: u64,
52    addr: u64,
53    state: u8,
54    flags: u64,
55}
56
57#[repr(C)]
58#[derive(Debug)]
59pub struct Rec<'a> {
60    pub context: Context,
61    attest_state: RmmRecAttestState,
62    // TODO: Create consts for both numbers
63    attest_challenge: [u8; MAX_CHALLENGE_SIZE],
64    attest_token_offset: usize,
65    aux: [u64; NR_AUX], // Addresses of auxiliary Granules
66    emulatable_abort: RmmRecEmulatableAbort,
67    /// PA of RD of Realm which owns this REC
68    ///
69    /// Safety:
70    /// Only immutable fields of Rd must be dereferenced by owner
71    /// by making getter method for the safety
72    owner: OnceCell<&'a Rd>,
73    vcpuid: usize,
74    runnable: bool,
75    psci_pending: bool,
76    state: State,
77    ripas: Ripas,
78    vtcr: u64,
79    host_call_pending: bool,
80}
81
82impl Rec<'_> {
83    pub fn new() -> Self {
84        Self {
85            context: Context::new(),
86            attest_state: RmmRecAttestState::NoAttestInProgress,
87            attest_challenge: [0; MAX_CHALLENGE_SIZE],
88            attest_token_offset: 0,
89            aux: [0; NR_AUX],
90            emulatable_abort: RmmRecEmulatableAbort::NotEmulatableAbort,
91            owner: OnceCell::new(),
92            vcpuid: 0,
93            runnable: false,
94            psci_pending: false,
95            state: State::Ready,
96            ripas: Ripas {
97                start: 0,
98                end: 0,
99                addr: 0,
100                state: 0,
101                flags: 0,
102            },
103            vtcr: 0,
104            host_call_pending: false,
105        }
106    }
107
108    pub fn init(
109        &mut self,
110        owner: usize,
111        vcpuid: usize,
112        flags: u64,
113        aux: [u64; NR_AUX],
114        vttbr: u64,
115        vmpidr: u64,
116    ) -> Result<(), Error> {
117        if owner == 0 {
118            error!("owner should be non-zero");
119            return Err(Error::RmiErrorInput);
120        }
121
122        if let Err(input_owner) = self.owner.set(unsafe { &*(owner as *const Rd) }) {
123            error!(
124                "Rec::init() called twice. cur owner: {:x}, input owner: {:x}",
125                self.get_owner()? as *const Rd as usize,
126                input_owner as *const Rd as usize
127            );
128            return Err(Error::RmiErrorRec);
129        }
130
131        self.vcpuid = vcpuid;
132        self.set_runnable(flags);
133        self.context.sys_regs.vttbr = vttbr;
134        self.context.sys_regs.vmpidr = vmpidr;
135        self.aux.copy_from_slice(&aux);
136        pauth::init_pauth(self);
137        pmu::init_pmu(self);
138        timer::init_timer(self);
139        gic::init_gic(self);
140        simd::init_simd(self)?;
141
142        Ok(())
143    }
144
145    pub fn attest_state(&self) -> RmmRecAttestState {
146        self.attest_state
147    }
148
149    pub fn attest_challenge(&self) -> &[u8] {
150        &self.attest_challenge
151    }
152
153    pub fn attest_token_offset(&self) -> usize {
154        self.attest_token_offset
155    }
156
157    pub fn aux(&self, index: usize) -> u64 {
158        self.aux[index]
159    }
160
161    pub fn emulatable_abort(&self) -> RmmRecEmulatableAbort {
162        self.emulatable_abort
163    }
164
165    pub fn runnable(&self) -> bool {
166        self.runnable
167    }
168
169    pub fn vcpuid(&self) -> usize {
170        self.vcpuid
171    }
172
173    fn get_owner(&self) -> Result<&Rd, Error> {
174        match self.owner.get() {
175            Some(owner) => Ok(owner),
176            None => {
177                // XXX: the below is added not to be reached
178                //      note that it can be assured by Rec's invariants
179                #[cfg(kani)]
180                kani::assume(false);
181                Err(Error::RmiErrorRec)
182            }
183        }
184    }
185
186    pub fn owner(&self) -> Result<usize, Error> {
187        let owner = self.get_owner()?;
188        Ok(owner as *const Rd as usize)
189    }
190
191    pub fn host_call_pending(&self) -> bool {
192        self.host_call_pending
193    }
194
195    pub fn psci_pending(&self) -> bool {
196        self.psci_pending
197    }
198
199    pub fn set_attest_state(&mut self, state: RmmRecAttestState) {
200        self.attest_state = state;
201    }
202
203    pub fn set_attest_challenge(&mut self, challenge: &[u8]) {
204        self.attest_challenge.copy_from_slice(challenge);
205    }
206
207    pub fn set_attest_offset(&mut self, offset: usize) {
208        self.attest_token_offset = offset;
209    }
210
211    pub fn set_emulatable_abort(&mut self, val: RmmRecEmulatableAbort) {
212        self.emulatable_abort = val;
213    }
214
215    pub fn set_host_call_pending(&mut self, val: bool) {
216        self.host_call_pending = val;
217    }
218
219    pub fn set_psci_pending(&mut self, val: bool) {
220        self.psci_pending = val;
221    }
222
223    pub fn set_ripas(&mut self, start: u64, end: u64, state: u8, flags: u64) {
224        self.ripas.start = start;
225        self.ripas.end = end;
226        self.ripas.state = state;
227        self.ripas.flags = flags;
228        self.ripas.addr = start; // reset addr to the start
229    }
230
231    pub fn set_vtcr(&mut self, vtcr: u64) {
232        self.vtcr = vtcr;
233    }
234
235    //TODO: change interface. A Rec state can be set by other Recs in the same Rd.
236    pub fn set_runnable(&mut self, flags: u64) {
237        const RUNNABLE_OFFSET: u64 = 1;
238        self.runnable = match flags & RUNNABLE_OFFSET {
239            0 => false,
240            _ => true,
241        }
242    }
243
244    pub fn set_state(&mut self, state: State) {
245        self.state = state;
246    }
247
248    pub fn get_state(&self) -> State {
249        self.state
250    }
251
252    pub fn set_ripas_addr(&mut self, addr: u64) {
253        self.ripas.addr = addr;
254    }
255
256    pub fn ripas_addr(&self) -> u64 {
257        self.ripas.addr
258    }
259
260    pub fn ripas_start(&self) -> u64 {
261        self.ripas.start
262    }
263
264    pub fn ripas_end(&self) -> u64 {
265        self.ripas.end
266    }
267
268    pub fn ripas_state(&self) -> u8 {
269        self.ripas.state
270    }
271
272    pub fn ripas_flags(&self) -> u64 {
273        self.ripas.flags
274    }
275
276    pub fn vtcr(&self) -> u64 {
277        self.vtcr
278    }
279
280    pub fn realmid(&self) -> Result<usize, Error> {
281        let owner = self.get_owner()?;
282        Ok(owner.id())
283    }
284
285    pub fn ipa_bits(&self) -> Result<usize, Error> {
286        let owner = self.get_owner()?;
287        Ok(owner.ipa_bits())
288    }
289
290    pub fn pmu_config(&self) -> Result<(bool, usize), Error> {
291        let owner = self.get_owner()?;
292        Ok(owner.pmu_config())
293    }
294
295    pub fn from_current(&mut self) {
296        unsafe {
297            Context::from_current(self);
298        }
299    }
300
301    pub fn into_current(&self) {
302        unsafe {
303            Context::into_current(self);
304        }
305    }
306
307    pub fn reset_ctx(&mut self) {
308        self.context.spsr_el2 = (SPSR_EL2::D.mask << SPSR_EL2::D.shift)
309            | (SPSR_EL2::A.mask << SPSR_EL2::A.shift)
310            | (SPSR_EL2::I.mask << SPSR_EL2::I.shift)
311            | (SPSR_EL2::F.mask << SPSR_EL2::F.shift)
312            | (SPSR_EL2::M.mask & u64::from(SPSR_EL2::M::EL1h)) << SPSR_EL2::M.shift;
313
314        self.context.sys_regs.sctlr = 0;
315    }
316}
317
318impl Content for Rec<'_> {}
319
320impl safe_abstraction::raw_ptr::RawPtr for Rec<'_> {}
321
322impl safe_abstraction::raw_ptr::SafetyChecked for Rec<'_> {}
323
324impl safe_abstraction::raw_ptr::SafetyAssured for Rec<'_> {
325    fn is_initialized(&self) -> bool {
326        // The initialization of this memory is guaranteed
327        // according to the RMM Specification A2.2.4 Granule Wiping.
328        // This instance belongs to a REC Granule and has been initialized.
329        true
330    }
331
332    fn verify_ownership(&self) -> bool {
333        // The ownership of this instance is exclusively ensured by the RMM.
334        // under the following conditions:
335        //
336        // 1. A lock on the given address is obtained using the `get_granule*` macros.
337        // 2. The instance is converted from a raw pointer through the `content*` functions.
338        // 3. The instance is accessed only within the lock scope.
339        //
340        // Ownership verification is guaranteed because these criteria are satisfied
341        // in all cases where this object is accessed.
342        true
343    }
344}
345
346// XXX: is using 'static okay here?
347unsafe fn current() -> Option<&'static mut Rec<'static>> {
348    match TPIDR_EL2.get() {
349        0 => None,
350        current => Some(&mut *(current as *mut Rec<'_>)),
351    }
352}
353
354fn enter() -> [usize; 4] {
355    unsafe {
356        if let Some(_rec) = current() {
357            // TODO: add code equivalent to the previous clean()
358            return rmm_exit([0; 4]);
359        }
360        [0, 0, 0, 0]
361    }
362}
363
364fn exit() {
365    unsafe {
366        if let Some(rec) = current() {
367            rec.from_current();
368        }
369    }
370}
371
372// TODO: check the below again
373pub fn run_prepare(rd: &Rd, vcpu: usize, rec: &mut Rec<'_>, incr_pc: usize) -> Result<(), Error> {
374    if incr_pc == 1 {
375        rec.context.elr_el2 += 4;
376    }
377    timer::update_timer_assertion(rec);
378    debug!("resuming: {:#x}", rec.context.elr_el2);
379    rec.into_current();
380
381    trace!("Switched to VCPU {} on Realm {}", vcpu, rd.id());
382    Ok(())
383}
384
385pub fn run() -> Result<[usize; 4], Error> {
386    let ret = enter();
387
388    exit();
389    Ok(ret)
390}
391
392pub fn max_recs_order() -> usize {
393    MAX_RECS_ORDER_VALUE as usize
394}
395
396// Note: Islet intends to manage states only for the realm world.
397//       Handling the ns state here does not align Islet's desgin.
398//       Save the host state only if necessary.
399pub fn save_host_state(rec: &Rec<'_>) {
400    pmu::save_host_state(rec);
401    // TODO: Apply 'ns_state_save' feature to the save_host_state func.
402    // For that, we need to move the code here from the corresponding patches
403    // in the nw-linux.
404    timer::save_host_state(rec);
405}
406
407pub fn restore_host_state(rec: &Rec<'_>) {
408    pmu::restore_host_state(rec);
409    timer::restore_host_state(rec);
410}