Skip to main content

islet_rmm/rsi/
mod.rs

1pub mod attestation;
2pub mod constraint;
3pub mod error;
4pub mod hostcall;
5pub mod measurement;
6pub mod psci;
7pub mod ripas;
8pub mod sealing;
9pub mod version;
10
11use alloc::vec::Vec;
12
13use crate::define_interface;
14use crate::event::RsiHandle;
15use crate::granule::{GranuleState, GRANULE_SIZE};
16use crate::listen;
17use crate::measurement::{HashContext, Measurement, MEASUREMENTS_SLOT_NR, MEASUREMENTS_SLOT_RIM};
18use crate::realm::config::realm_config;
19use crate::realm::mm::address::GuestPhysAddr;
20use crate::realm::mm::rtt::RTT_PAGE_LEVEL;
21use crate::realm::rd::Rd;
22use crate::rec::context::{get_reg, set_reg};
23use crate::rec::{Rec, RmmRecAttestState};
24use crate::rmi;
25use crate::rmi::error::Error;
26use crate::rmi::rec::run::Run;
27use crate::rmi::rtt::validate_ipa;
28use crate::rsi::hostcall::{HostCall, HOST_CALL_NR_GPRS};
29use crate::rsi::ripas::{get_ripas_state, set_ripas_state};
30use crate::rsi::sealing::{realm_sealing_key, SEALING_KEY_SIZE};
31use crate::Monitor;
32use crate::{get_granule, get_granule_if};
33
34use safe_abstraction::raw_ptr::assume_safe;
35
36define_interface! {
37    command {
38        ABI_VERSION             = 0xc400_0190,
39        FEATURES                = 0xc400_0191,
40        MEASUREMENT_READ        = 0xc400_0192,
41        MEASUREMENT_EXTEND      = 0xc400_0193,
42        ATTEST_TOKEN_INIT       = 0xc400_0194,
43        ATTEST_TOKEN_CONTINUE   = 0xc400_0195,
44        REALM_CONFIG            = 0xc400_0196,
45        IPA_STATE_SET           = 0xc400_0197,
46        IPA_STATE_GET           = 0xc400_0198,
47        HOST_CALL               = 0xc400_0199,
48        // PSCI smcs
49        // XXX: SMCCC_VERSION is not defined in the spec, so remove it if it is not used now
50        SMCCC_VERSION           = 0x8000_0000,
51        PSCI_VERSION            = 0x8400_0000,
52        PSCI_CPU_SUSPEND        = 0xC400_0001,
53        PSCI_CPU_OFF            = 0x8400_0002,
54        PSCI_CPU_ON             = 0xC400_0003,
55        PSCI_AFFINITY_INFO      = 0xC400_0004,
56        PSCI_SYSTEM_OFF         = 0x8400_0008,
57        PSCI_SYSTEM_RESET       = 0x8400_0009,
58        PSCI_FEATURES           = 0x8400_000A,
59        // vendor calls
60        ISLET_REALM_SEALING_KEY = 0xC700_0191,
61    }
62}
63
64pub const SUCCESS: usize = 0;
65pub const ERROR_INPUT: usize = 1;
66pub const ERROR_STATE: usize = 2;
67pub const INCOMPLETE: usize = 3;
68
69pub const ABI_VERSION_MAJOR: usize = 1;
70pub const ABI_VERSION_MINOR: usize = 0;
71
72extern crate alloc;
73
74pub fn do_host_call(
75    _arg: &[usize],
76    ret: &mut [usize],
77    _rmm: &Monitor,
78    rec: &mut Rec<'_>,
79    run: &mut Run,
80) -> core::result::Result<(), Error> {
81    let rd_granule = get_granule_if!(rec.owner()?, GranuleState::RD)?;
82    let rd = rd_granule.content::<Rd>()?;
83
84    let ipa = get_reg(rec, 1).unwrap_or(0x0);
85
86    let struct_size = core::mem::size_of::<HostCall>();
87    if !ipa.is_multiple_of(struct_size)
88        || !rd.addr_in_par(ipa)
89        || ipa / GRANULE_SIZE != (ipa + struct_size - 1) / GRANULE_SIZE
90    {
91        set_reg(rec, 0, ERROR_INPUT)?;
92        ret[0] = rmi::SUCCESS_REC_ENTER;
93        return Ok(());
94    }
95
96    let pa = rd
97        .s2_table()
98        .lock()
99        .ipa_to_pa(
100            crate::realm::mm::address::GuestPhysAddr::from(ipa),
101            RTT_PAGE_LEVEL,
102        )
103        .ok_or(Error::RmiErrorInput)?;
104
105    // page mapping result 'pa' is PAGE_SIZE aligned. Get hsot_call_ptr.
106    let host_call_ptr: usize = pa.as_usize() + ipa % crate::config::PAGE_SIZE;
107    let mut host_call = assume_safe::<HostCall>(host_call_ptr)?;
108    let imm = host_call.imm();
109
110    if rec.host_call_pending() {
111        for i in 0..HOST_CALL_NR_GPRS {
112            let val = run.entry_gpr(i)?;
113            host_call.set_gpr(i, val)?
114        }
115        set_reg(rec, 0, SUCCESS)?;
116        rec.set_host_call_pending(false);
117    } else {
118        for i in 0..HOST_CALL_NR_GPRS {
119            let val = host_call.gpr(i)?;
120            run.set_gpr(i, val)?
121        }
122        run.set_imm(imm);
123        run.set_exit_reason(rmi::EXIT_HOST_CALL);
124        rec.set_host_call_pending(true);
125    }
126
127    trace!("HOST_CALL param: {:#X?}", *host_call);
128
129    ret[0] = rmi::SUCCESS;
130    Ok(())
131}
132
133fn get_token_part(
134    rd: &Rd,
135    context: &mut Rec<'_>,
136    size: usize,
137) -> core::result::Result<(Vec<u8>, usize), Error> {
138    let hash_algo = rd.hash_algo();
139    let measurements = rd.measurements;
140
141    // FIXME: This should be stored instead of generating it for every call.
142    let token = crate::rsi::attestation::get_token(
143        context.attest_challenge(),
144        &measurements,
145        rd.personalization_value(),
146        hash_algo,
147    );
148
149    let offset = context.attest_token_offset();
150    let part_size = core::cmp::min(size, token.len() - offset);
151    let part_end = offset + part_size;
152
153    context.set_attest_offset(part_end);
154
155    Ok((token[offset..part_end].to_vec(), token.len() - part_end))
156}
157
158pub fn set_event_handler(rsi: &mut RsiHandle) {
159    listen!(rsi, ATTEST_TOKEN_INIT, |_arg, ret, _rmm, rec, _| {
160        let mut challenge: [u8; 64] = [0; 64];
161
162        for i in 0..8 {
163            let challenge_part = get_reg(rec, i + 1)?;
164            let start_idx = i * 8;
165            let end_idx = start_idx + 8;
166            challenge[start_idx..end_idx].copy_from_slice(&challenge_part.to_le_bytes());
167        }
168
169        rec.set_attest_challenge(&challenge);
170        rec.set_attest_state(RmmRecAttestState::AttestInProgress);
171        rec.set_attest_offset(0);
172
173        set_reg(rec, 0, SUCCESS)?;
174        set_reg(rec, 1, attestation::MAX_CCA_TOKEN_SIZE)?;
175
176        ret[0] = rmi::SUCCESS_REC_ENTER;
177        Ok(())
178    });
179
180    listen!(rsi, ATTEST_TOKEN_CONTINUE, |_arg, ret, _rmm, rec, _| {
181        let rd_granule = get_granule_if!(rec.owner()?, GranuleState::RD)?;
182        let rd = rd_granule.content::<Rd>()?;
183
184        if rec.attest_state() != RmmRecAttestState::AttestInProgress {
185            warn!("Calling attest token continue without init");
186            set_reg(rec, 0, ERROR_STATE)?;
187            ret[0] = rmi::SUCCESS_REC_ENTER;
188            return Ok(());
189        }
190
191        let attest_ipa = get_reg(rec, 1)?;
192        if validate_ipa(&rd, attest_ipa).is_err() {
193            warn!("Wrong ipa passed {}", attest_ipa);
194            set_reg(rec, 0, ERROR_INPUT)?;
195            ret[0] = rmi::SUCCESS_REC_ENTER;
196            return Ok(());
197        }
198
199        let attest_pa: usize = rd
200            .s2_table()
201            .lock()
202            .ipa_to_pa(GuestPhysAddr::from(attest_ipa), RTT_PAGE_LEVEL)
203            .ok_or(Error::RmiErrorInput)?
204            .into();
205
206        let pa_offset = get_reg(rec, 2)?;
207        let buffer_size = get_reg(rec, 3)?;
208
209        let (_, overflowed) = pa_offset.overflowing_add(buffer_size);
210        if overflowed || pa_offset + buffer_size > GRANULE_SIZE {
211            warn!("Buffer addres region invalid");
212            set_reg(rec, 0, ERROR_INPUT)?;
213            ret[0] = rmi::SUCCESS_REC_ENTER;
214            return Ok(());
215        }
216
217        #[cfg(not(kani))]
218        // `rsi` is currently not reachable in model checking harnesses
219        {
220            let (token_part, token_left) = get_token_part(&rd, rec, buffer_size)?;
221
222            unsafe {
223                let pa_ptr = attest_pa as *mut u8;
224                core::ptr::copy(token_part.as_ptr(), pa_ptr.add(pa_offset), token_part.len());
225            }
226
227            if token_left == 0 {
228                set_reg(rec, 0, SUCCESS)?;
229                rec.set_attest_state(RmmRecAttestState::NoAttestInProgress);
230            } else {
231                set_reg(rec, 0, INCOMPLETE)?;
232            }
233
234            set_reg(rec, 1, token_part.len())?;
235        }
236
237        ret[0] = rmi::SUCCESS_REC_ENTER;
238        Ok(())
239    });
240
241    listen!(rsi, FEATURES, |_arg, ret, _rmm, rec, _| {
242        let _index = get_reg(rec, 1);
243
244        set_reg(rec, 0, SUCCESS)?;
245
246        // B5.3.3 In the current version of the interface, this commands returns
247        // zero regardless of the index provided.
248
249        set_reg(rec, 1, 0)?;
250
251        ret[0] = rmi::SUCCESS_REC_ENTER;
252        Ok(())
253    });
254
255    listen!(rsi, HOST_CALL, do_host_call);
256
257    listen!(rsi, ABI_VERSION, |_arg, ret, _rmm, rec, _| {
258        let req = get_reg(rec, 1)?;
259
260        let (req_major, req_minor) = version::decode_version(req);
261
262        if req_major != ABI_VERSION_MAJOR || req_minor != ABI_VERSION_MINOR {
263            warn!(
264                "Wrong unsupported version requested ({}, {})",
265                req_major, req_minor
266            );
267            set_reg(rec, 0, ERROR_INPUT)?;
268            ret[0] = rmi::SUCCESS_REC_ENTER;
269            return Ok(());
270        }
271
272        let lower = version::encode_version();
273        let higher = lower;
274
275        set_reg(rec, 0, SUCCESS)?;
276        set_reg(rec, 1, lower)?;
277        set_reg(rec, 2, higher)?;
278
279        trace!("RSI_ABI_VERSION: {:#X?} {:#X?}", lower, higher);
280        ret[0] = rmi::SUCCESS_REC_ENTER;
281        Ok(())
282    });
283
284    listen!(rsi, MEASUREMENT_READ, |_arg, ret, _rmm, rec, _| {
285        let rd_granule = get_granule_if!(rec.owner()?, GranuleState::RD)?;
286        let rd = rd_granule.content::<Rd>()?;
287        let mut measurement = Measurement::empty();
288        let index = get_reg(rec, 1)?;
289
290        if index >= MEASUREMENTS_SLOT_NR {
291            warn!("Wrong index passed: {}", index);
292            set_reg(rec, 0, ERROR_INPUT)?;
293            ret[0] = rmi::SUCCESS_REC_ENTER;
294            return Ok(());
295        }
296
297        #[cfg(not(kani))]
298        // `rsi` is currently not reachable in model checking harnesses
299        crate::rsi::measurement::read(&rd, index, &mut measurement)?;
300        set_reg(rec, 0, SUCCESS)?;
301        for (ind, chunk) in measurement
302            .as_slice()
303            .chunks_exact(core::mem::size_of::<usize>())
304            .enumerate()
305        {
306            let reg_value = usize::from_le_bytes(chunk.try_into().unwrap());
307            set_reg(rec, ind + 1, reg_value)?;
308        }
309
310        ret[0] = rmi::SUCCESS_REC_ENTER;
311        Ok(())
312    });
313
314    listen!(rsi, MEASUREMENT_EXTEND, |_arg, ret, _rmm, rec, _| {
315        let mut rd_granule = get_granule_if!(rec.owner()?, GranuleState::RD)?;
316        let mut rd = rd_granule.content_mut::<Rd>()?;
317
318        let index = get_reg(rec, 1)?;
319        let size = get_reg(rec, 2)?;
320        let mut buffer = [0u8; 64];
321
322        for i in 0..8 {
323            buffer[i * 8..i * 8 + 8].copy_from_slice(get_reg(rec, i + 3)?.to_le_bytes().as_slice());
324        }
325
326        if size > buffer.len() || index == MEASUREMENTS_SLOT_RIM || index >= MEASUREMENTS_SLOT_NR {
327            warn!(
328                "Wrong index or buffer size passed: idx: {}, size: {}",
329                index, size
330            );
331            set_reg(rec, 0, ERROR_INPUT)?;
332            ret[0] = rmi::SUCCESS_REC_ENTER;
333            return Ok(());
334        }
335
336        #[cfg(not(kani))]
337        // `rsi` is currently not reachable in model checking harnesses
338        HashContext::new(&mut rd)?.extend_measurement(&buffer[0..size], index)?;
339
340        set_reg(rec, 0, SUCCESS)?;
341        ret[0] = rmi::SUCCESS_REC_ENTER;
342        Ok(())
343    });
344
345    listen!(rsi, REALM_CONFIG, |_arg, ret, _rmm, rec, _| {
346        let ipa_bits = rec.ipa_bits()?;
347        let rd_granule = get_granule_if!(rec.owner()?, GranuleState::RD)?;
348        let rd = rd_granule.content::<Rd>()?;
349
350        let config_ipa = get_reg(rec, 1)?;
351        if validate_ipa(&rd, config_ipa).is_err() {
352            set_reg(rec, 0, ERROR_INPUT)?;
353            ret[0] = rmi::SUCCESS_REC_ENTER;
354            return Ok(());
355        }
356
357        realm_config(&rd, config_ipa, ipa_bits)?;
358
359        if set_reg(rec, 0, SUCCESS).is_err() {
360            warn!("Unable to set register 0. rec: {:?}", rec);
361        }
362        ret[0] = rmi::SUCCESS_REC_ENTER;
363        Ok(())
364    });
365
366    listen!(rsi, IPA_STATE_GET, get_ripas_state);
367    listen!(rsi, IPA_STATE_SET, set_ripas_state);
368
369    // ISLET_REALM_SEALING_KEY is a vendor specific RSI for derivation and retrieval of Sealing Keys
370    // Input registers:
371    // x0: function id (0xC7000191)
372    // x1: flags - a bit field, where:
373    //    bit 0: if not set, VHUK_A i used (default), otherwise VHUK_M is used as an input key material
374    //    bit 1: if set, RIM is used as a key metarial
375    //    bit 2: if set, the realm_id is used as a key material
376    //    bit 3: if set, provided SVN is used as a key material
377    // x2: svn - Security Version Number
378    // The resulting key is returned 256 bit long key is returned
379    // in x1, x2, x3, x4 registers
380    listen!(rsi, ISLET_REALM_SEALING_KEY, |_arg, ret, _rmm, rec, _| {
381        let flags = get_reg(rec, 1)?;
382        let svn = get_reg(rec, 2)?;
383        let mut buf = [0u8; SEALING_KEY_SIZE];
384
385        let rd_granule = get_granule_if!(rec.owner()?, GranuleState::RD)?;
386        let rd = rd_granule.content::<Rd>()?;
387
388        if realm_sealing_key(&rd, flags, svn, &mut buf).is_err() {
389            set_reg(rec, 0, ERROR_INPUT)?;
390            ret[0] = rmi::SUCCESS_REC_ENTER;
391            return Ok(());
392        }
393
394        for (ind, chunk) in buf.chunks_exact(core::mem::size_of::<usize>()).enumerate() {
395            let reg_value = usize::from_ne_bytes(chunk.try_into().unwrap());
396            set_reg(rec, ind + 1, reg_value)?;
397        }
398
399        // Clear the sealing key
400        // TODO: use zeroize?
401        unsafe {
402            core::ptr::write_bytes(buf.as_mut_ptr(), 0x0, SEALING_KEY_SIZE);
403        }
404
405        set_reg(rec, 0, SUCCESS)?;
406        ret[0] = rmi::SUCCESS_REC_ENTER;
407        Ok(())
408    });
409}