cca_rmm/
lib.rs

1//! Structures and values defined by the Realm Management Monitor
2//!
3//! This library provides structure and value definitions from the RMM
4//! specification v1.0-rel0. For the moment it only provides the definitions
5//! needed for Realm Initial Measurement calculation.
6#![warn(missing_docs)]
7use bitflags::bitflags;
8use core::mem;
9use std::str::FromStr;
10
11use serde::ser::SerializeTuple;
12use serde::{Deserialize, Serialize, Serializer};
13
14/// Size of one Realm measurement, in bytes
15pub const RMM_REALM_MEASUREMENT_WIDTH: usize = 64;
16/// One Realm Measurement (initial or extensible)
17pub type RmmRealmMeasurement = [u8; RMM_REALM_MEASUREMENT_WIDTH];
18
19bitflags! {
20/// Flags provided by the host during Realm creation
21#[derive(Default, Debug, Clone, Copy, Serialize, PartialEq, Eq, Hash)]
22pub struct RmiRealmFlags: u64 {
23    /// Enable Large Physical Addresses
24    const LPA2 = 1 << 0;
25    /// Enable Scalable Vector Extension
26    const SVE = 1 << 1;
27    /// Enable Power Management Unit
28    const PMU = 1 << 2;
29}
30}
31
32bitflags! {
33/// Flags provided by the Host during REC creation
34pub struct RmiRecCreateFlags: u64 {
35    /// The REC is run at reset
36    const RUNNABLE = 1 << 0;
37}
38}
39
40bitflags! {
41/// Flags provided by the Host during DATA Granule creation
42pub struct RmmDataFlags: u64 {
43    /// Measure the content of the DATA granule
44    const MEASURE = 1 << 0;
45}
46}
47
48/// Size of a granule
49pub const RMM_GRANULE: u64 = 0x1000;
50
51/// Error from the RMM library
52#[derive(Debug, thiserror::Error)]
53pub enum RmmError {
54    /// Error while encoding into binary
55    #[error("encoding error")]
56    EncodeError(#[from] bincode::Error),
57
58    /// Unknown hash algorithm
59    #[error("unknown hash algorithm `{0}`")]
60    UnknownHashAlgorithm(String),
61}
62type Result<T> = core::result::Result<T, RmmError>;
63
64/// Hash algorithm used for measurements
65#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Default)]
66pub enum RmiHashAlgorithm {
67    /// The SHA-256 algorithm
68    #[default]
69    RmiHashSha256 = 0,
70    /// The SHA-512 algorithm
71    RmiHashSha512 = 1,
72}
73
74impl TryFrom<u8> for RmiHashAlgorithm {
75    type Error = RmmError;
76    fn try_from(algo: u8) -> Result<Self> {
77        match algo {
78            0 => Ok(RmiHashAlgorithm::RmiHashSha256),
79            1 => Ok(RmiHashAlgorithm::RmiHashSha512),
80            _ => Err(RmmError::UnknownHashAlgorithm("{algo}".to_string())),
81        }
82    }
83}
84
85impl FromStr for RmiHashAlgorithm {
86    type Err = RmmError;
87    fn from_str(s: &str) -> Result<Self> {
88        match s {
89            "sha256" => Ok(RmiHashAlgorithm::RmiHashSha256),
90            "sha512" => Ok(RmiHashAlgorithm::RmiHashSha512),
91            _ => Err(RmmError::UnknownHashAlgorithm(String::from(s))),
92        }
93    }
94}
95
96// serde doesn't support serializing large arrays at the moment, so we need to
97// do it manually: https://github.com/serde-rs/serde/issues/1937
98fn serialize_array<S: Serializer, const N: usize>(
99    t: &[u8; N],
100    serializer: S,
101) -> core::result::Result<S::Ok, S::Error> {
102    let mut ser_tuple = serializer.serialize_tuple(N)?;
103    for e in t {
104        ser_tuple.serialize_element(e)?;
105    }
106    ser_tuple.end()
107}
108
109/// RmiRealmParams with only the fields that are measured for the RIM. The rest
110/// is set to zero (DEN0137 1.0-rel0 B4.3.9.4 RMI_REALM_CREATE initialization of
111/// RIM)
112#[derive(Clone, Debug, Serialize, PartialEq, Default)]
113#[repr(C, packed)]
114pub struct RmiRealmParams {
115    flags: u64,
116    s2sz: u8,
117    _empty1: [u8; 7],
118    sve_vl: u8,
119    _empty2: [u8; 7],
120    num_bps: u8,
121    _empty3: [u8; 7],
122    num_wps: u8,
123    _empty4: [u8; 7],
124    pmu_num_ctrs: u8,
125    _empty5: [u8; 7],
126    hash_algo: u8,
127}
128const RMI_REALM_PARAMS_SIZE: usize = 0x1000;
129
130impl RmiRealmParams {
131    /// Create a new RmiRealmParams instance
132    pub fn new(
133        flags: RmiRealmFlags,
134        s2sz: u8,
135        num_wps: u8,
136        num_bps: u8,
137        pmu_num_ctrs: u8,
138        sve_vl: u8,
139        hash_algo: RmiHashAlgorithm,
140    ) -> RmiRealmParams {
141        RmiRealmParams {
142            flags: flags.bits(),
143            s2sz,
144            num_wps,
145            num_bps,
146            pmu_num_ctrs,
147            sve_vl,
148            hash_algo: hash_algo as u8,
149            ..Default::default()
150        }
151    }
152    /// Convert the packed struct to bytes
153    pub fn to_bytes(&self) -> Result<Vec<u8>> {
154        let mut bytes = bincode::serialize(self)?;
155        assert!(bytes.len() == mem::size_of::<RmiRealmParams>());
156        bytes.resize(RMI_REALM_PARAMS_SIZE, 0);
157        Ok(bytes)
158    }
159}
160
161/// RmiRecParams with only the fields that are measured for the RIM. The rest is
162/// set to zero (DEN0137 1.0-rel0 B4.3.12.4 RMI_REC_CREATE extension of RIM)
163#[derive(Clone, Debug, Serialize, PartialEq)]
164#[repr(C, packed)]
165pub struct RmiRecParams {
166    flags: u64,
167    #[serde(serialize_with = "serialize_array")]
168    _empty1: [u8; 0x200 - 8],
169    pc: u64,
170    #[serde(serialize_with = "serialize_array")]
171    _empty2: [u8; 0x100 - 8],
172    gprs: [u64; 8],
173}
174const RMI_REC_PARAMS_SIZE: usize = 0x1000;
175
176impl RmiRecParams {
177    /// Create a new RmiRecParams instance
178    pub fn new(flags: RmiRecCreateFlags, pc: u64, gprs: [u64; 8]) -> RmiRecParams {
179        RmiRecParams {
180            flags: flags.bits(),
181            // Can't use default() because it doesn't work with large arrays.
182            _empty1: [0; 0x200 - 8],
183            pc,
184            _empty2: [0; 0x100 - 8],
185            gprs,
186        }
187    }
188
189    /// Convert the packed struct to bytes
190    pub fn to_bytes(&self) -> Result<Vec<u8>> {
191        let mut bytes = bincode::serialize(self)?;
192        assert!(bytes.len() == mem::size_of::<RmiRecParams>());
193        bytes.resize(RMI_REC_PARAMS_SIZE, 0);
194        Ok(bytes)
195    }
196}
197
198/// Structure used to calculate the contribution to the RIM of a data granule
199#[derive(Clone, Debug, Serialize, PartialEq)]
200#[repr(C, packed)]
201pub struct RmmMeasurementDescriptorData {
202    desc_type: u8,
203    unused1: [u8; 7],
204    len: u64,
205    #[serde(serialize_with = "serialize_array")]
206    rim: RmmRealmMeasurement,
207    ipa: u64,
208    flags: u64,
209    #[serde(serialize_with = "serialize_array")]
210    content: RmmRealmMeasurement,
211}
212const RMM_REALM_MEASUREMENT_DESCRIPTOR_DATA_SIZE: usize = 0x100;
213
214impl RmmMeasurementDescriptorData {
215    /// Create a new instance of RmmMeasurementDescriptorData
216    pub fn new(
217        rim: &RmmRealmMeasurement,
218        ipa: u64,
219        flags: RmmDataFlags,
220        content: &RmmRealmMeasurement,
221    ) -> RmmMeasurementDescriptorData {
222        RmmMeasurementDescriptorData {
223            desc_type: 0,
224            unused1: [0; 7],
225            len: RMM_REALM_MEASUREMENT_DESCRIPTOR_DATA_SIZE as u64,
226            rim: *rim,
227            ipa,
228            flags: flags.bits(),
229            content: *content,
230        }
231    }
232    /// Convert the packed structure to bytes
233    pub fn to_bytes(&self) -> Result<Vec<u8>> {
234        assert!(self.desc_type == 0);
235        let mut bytes = bincode::serialize(self)?;
236        assert!(bytes.len() == mem::size_of::<RmmMeasurementDescriptorData>());
237        bytes.resize(RMM_REALM_MEASUREMENT_DESCRIPTOR_DATA_SIZE, 0);
238        Ok(bytes)
239    }
240}
241
242/// Structure used to calculate the contribution to the RIM of a REC
243#[derive(Clone, Debug, Serialize, PartialEq)]
244#[repr(C, packed)]
245pub struct RmmMeasurementDescriptorRec {
246    desc_type: u8,
247    unused1: [u8; 7],
248    len: u64,
249    #[serde(serialize_with = "serialize_array")]
250    rim: RmmRealmMeasurement,
251    #[serde(serialize_with = "serialize_array")]
252    content: RmmRealmMeasurement,
253}
254const RMM_REALM_MEASUREMENT_DESCRIPTOR_REC_SIZE: usize = 0x100;
255
256impl RmmMeasurementDescriptorRec {
257    /// Create a new instance of RmmMeasurementDescriptorRec
258    pub fn new(
259        rim: &RmmRealmMeasurement,
260        content: &RmmRealmMeasurement,
261    ) -> RmmMeasurementDescriptorRec {
262        RmmMeasurementDescriptorRec {
263            desc_type: 1,
264            unused1: [0; 7],
265            len: RMM_REALM_MEASUREMENT_DESCRIPTOR_REC_SIZE as u64,
266            rim: *rim,
267            content: *content,
268        }
269    }
270    /// Convert the packed structure to bytes
271    pub fn to_bytes(&self) -> Result<Vec<u8>> {
272        assert!(self.desc_type == 1);
273        let mut bytes = bincode::serialize(self)?;
274        assert!(bytes.len() == mem::size_of::<RmmMeasurementDescriptorRec>());
275        bytes.resize(RMM_REALM_MEASUREMENT_DESCRIPTOR_REC_SIZE, 0);
276        Ok(bytes)
277    }
278}
279
280/// Structure used to calculate the contribution to the RIM of a RIPAS change
281#[derive(Clone, Debug, Serialize, PartialEq)]
282#[repr(C, packed)]
283pub struct RmmMeasurementDescriptorRipas {
284    desc_type: u8,
285    unused1: [u8; 7],
286    len: u64,
287    #[serde(serialize_with = "serialize_array")]
288    rim: RmmRealmMeasurement,
289    base: u64,
290    top: u64,
291}
292const RMM_REALM_MEASUREMENT_DESCRIPTOR_RIPAS_SIZE: usize = 0x100;
293
294impl RmmMeasurementDescriptorRipas {
295    /// Create a new instance of RmmMeasurementDescriptorRipas
296    pub fn new(
297        rim: &RmmRealmMeasurement,
298        base: u64,
299        top: u64,
300    ) -> RmmMeasurementDescriptorRipas {
301        RmmMeasurementDescriptorRipas {
302            desc_type: 2,
303            unused1: [0; 7],
304            len: RMM_REALM_MEASUREMENT_DESCRIPTOR_RIPAS_SIZE as u64,
305            rim: *rim,
306            base,
307            top,
308        }
309    }
310    /// Convert the packed structure to bytes
311    pub fn to_bytes(&self) -> Result<Vec<u8>> {
312        assert!(self.desc_type == 2);
313        let mut bytes = bincode::serialize(self)?;
314        assert!(bytes.len() == mem::size_of::<RmmMeasurementDescriptorRipas>());
315        bytes.resize(RMM_REALM_MEASUREMENT_DESCRIPTOR_RIPAS_SIZE, 0);
316        Ok(bytes)
317    }
318}
319
320#[cfg(test)]
321mod tests {
322    use super::*;
323
324    #[test]
325    fn test_serialize() {
326        let a = 0x12345678u32.to_le();
327        let bytes = bincode::serialize(&a).unwrap();
328
329        // By default, bincode encodes in litte-endian. Make sure of it, since
330        // we rely on that for RMM structs
331        assert!(bytes[0] == 0x78);
332    }
333
334    #[test]
335    fn test_hash_algo() {
336        assert_eq!(
337            RmiHashAlgorithm::try_from(0).unwrap(),
338            RmiHashAlgorithm::RmiHashSha256
339        );
340        assert_eq!(
341            RmiHashAlgorithm::try_from(1).unwrap(),
342            RmiHashAlgorithm::RmiHashSha512
343        );
344        assert!(RmiHashAlgorithm::try_from(2).is_err());
345        let h: RmiHashAlgorithm = "sha256".parse().unwrap();
346        assert_eq!(h, RmiHashAlgorithm::RmiHashSha256);
347        assert_eq!(
348            "sha256".parse::<RmiHashAlgorithm>().unwrap(),
349            RmiHashAlgorithm::RmiHashSha256
350        );
351        assert_eq!(
352            "sha512".parse::<RmiHashAlgorithm>().unwrap(),
353            RmiHashAlgorithm::RmiHashSha512
354        );
355        assert!("hello".parse::<RmiHashAlgorithm>().is_err());
356    }
357}