Skip to main content

islet_rmm/rmi/
gpt.rs

1use crate::asm::{smc, SMC_SUCCESS};
2use crate::event::RmiHandle;
3use crate::granule::{set_granule, GranuleState};
4use crate::listen;
5use crate::rmi;
6use crate::rmi::error::Error;
7#[cfg(not(feature = "gst_page_table"))]
8use crate::{get_granule, get_granule_if};
9#[cfg(feature = "gst_page_table")]
10use crate::{get_granule, get_granule_if, set_state_and_get_granule};
11
12#[cfg(feature = "gst_page_table")]
13use vmsa::error::Error as MmError;
14
15extern crate alloc;
16
17// defined in trusted-firmware-a/include/services/rmmd_svc.h
18pub const MARK_REALM: usize = 0xc400_01b0;
19pub const MARK_NONSECURE: usize = 0xc400_01b1;
20
21pub fn set_event_handler(rmi: &mut RmiHandle) {
22    #[cfg(any(not(kani), feature = "mc_rmi_granule_delegate"))]
23    listen!(rmi, rmi::GRANULE_DELEGATE, |arg, _, rmm| {
24        let addr = arg[0];
25
26        #[cfg(feature = "gst_page_table")]
27        let mut granule = match get_granule_if!(addr, GranuleState::Undelegated) {
28            Err(MmError::MmNoEntry) => set_state_and_get_granule!(addr, GranuleState::Undelegated),
29            other => other,
30        }?;
31        #[cfg(not(feature = "gst_page_table"))]
32        let mut granule = get_granule_if!(addr, GranuleState::Undelegated)?;
33
34        // Avoid deadlock in get_granule() in smc() on {miri, test} mode
35        #[cfg(any(miri, test, fuzzing))]
36        core::mem::drop(granule);
37
38        if smc(MARK_REALM, &[addr])[0] != SMC_SUCCESS {
39            return Err(Error::RmiErrorInput);
40        }
41
42        #[cfg(not(kani))]
43        // `page_table` is currently not reachable in model checking harnesses
44        rmm.page_table.map(addr, true);
45
46        #[cfg(any(miri, test, fuzzing))]
47        let mut granule = get_granule_if!(addr, GranuleState::Undelegated)?;
48        set_granule(&mut granule, GranuleState::Delegated).inspect_err(|_| {
49            #[cfg(not(kani))]
50            // `page_table` is currently not reachable in model checking harnesses
51            rmm.page_table.unmap(addr);
52        })?;
53        #[cfg(not(kani))]
54        // `page_table` is currently not reachable in model checking harnesses
55        rmm.page_table.unmap(addr);
56        Ok(())
57    });
58
59    #[cfg(any(not(kani), feature = "mc_rmi_granule_undelegate"))]
60    listen!(rmi, rmi::GRANULE_UNDELEGATE, |arg, _, rmm| {
61        let addr = arg[0];
62        let mut granule = get_granule_if!(addr, GranuleState::Delegated)?;
63
64        // Avoid deadlock in get_granule() in smc() on {miri, test} mode
65        #[cfg(any(miri, test, fuzzing))]
66        core::mem::drop(granule);
67
68        if smc(MARK_NONSECURE, &[addr])[0] != SMC_SUCCESS {
69            panic!(
70                "A delegated granule should only be undelegated on request from RMM. {:X}",
71                addr
72            );
73        }
74
75        #[cfg(any(miri, test, fuzzing))]
76        let mut granule = get_granule_if!(addr, GranuleState::Delegated)?;
77
78        #[cfg(not(kani))]
79        // `page_table` is currently not reachable in model checking harnesses
80        rmm.page_table.map(addr, false);
81        set_granule(&mut granule, GranuleState::Undelegated).inspect_err(|_| {
82            #[cfg(not(kani))]
83            // `page_table` is currently not reachable in model checking harnesses
84            rmm.page_table.unmap(addr);
85        })?;
86        #[cfg(not(kani))]
87        // `page_table` is currently not reachable in model checking harnesses
88        rmm.page_table.unmap(addr);
89        Ok(())
90    });
91}
92
93#[cfg(test)]
94mod test {
95    use crate::rmi::gpt::GranuleState;
96    use crate::rmi::{ERROR_INPUT, GRANULE_DELEGATE, GRANULE_UNDELEGATE, SUCCESS};
97    use crate::test_utils::*;
98    use crate::{get_granule, get_granule_if};
99
100    use alloc::vec;
101
102    #[test]
103    fn rmi_granule_delegate_positive() {
104        let mocking_addr = mock::host::alloc_granule(IDX_RD);
105        let ret = rmi::<GRANULE_DELEGATE>(&[mocking_addr]);
106        assert_eq!(ret[0], SUCCESS);
107        assert!(get_granule_if!(mocking_addr, GranuleState::Delegated).is_ok());
108
109        let ret = rmi::<GRANULE_UNDELEGATE>(&[mocking_addr]);
110        assert_eq!(ret[0], SUCCESS);
111        assert!(get_granule_if!(mocking_addr, GranuleState::Undelegated).is_ok());
112
113        miri_teardown();
114    }
115
116    // Source: https://github.com/ARM-software/cca-rmm-acs
117    // Test Case: cmd_granule_delegate
118    /*
119       Check 1 : gran_align; intent id : 0x0 addr : 0x88300001
120       Check 2 : gran_bound; intent id : 0x1 addr : 0x1C0B0000
121       Check 3 : gran_bound; intent id : 0x2 addr : 0x1000000001000
122       Check 4 : gran_state; intent id : 0x3 addr : 0x8830C000
123       Check 5 : gran_state; intent id : 0x4 addr : 0x88315000
124       Check 6 : gran_state; intent id : 0x5 addr : 0x88351000
125       Check 7 : gran_state; intent id : 0x6 addr : 0x88306000
126       Check 8 : gran_state; intent id : 0x7 addr : 0x88303000
127       Check 9 : gran_gpt; intent id : 0x8 addr : 0x88357000
128       Check 10 : gran_gpt; intent id : 0x9 addr : 0x6000000
129    */
130    #[test]
131    fn rmi_granule_delegate_negative() {
132        let test_data = vec![
133            (0x88300001, ERROR_INPUT),
134            (0x1C0B0000, ERROR_INPUT),
135            (0x1000000001000, ERROR_INPUT),
136            // TODO: Cover all test data
137        ];
138
139        for (input, output) in test_data {
140            let ret = rmi::<GRANULE_DELEGATE>(&[input]);
141            assert_eq!(output, ret[0]);
142        }
143    }
144
145    // Source: https://github.com/ARM-software/cca-rmm-acs
146    // Test Case: cmd_granule_undelegate
147    /*
148       Check 1 : gran_align; intent id : 0x0 addr : 0x88300001
149       Check 2 : gran_bound; intent id : 0x1 addr : 0x1C0B0000
150       Check 3 : gran_bound; intent id : 0x2 addr : 0x1000000001000
151       Check 4 : gran_state; intent id : 0x3 addr : 0x8830C000
152       Check 5 : gran_state; intent id : 0x4 addr : 0x88315000
153       Check 6 : gran_state; intent id : 0x5 addr : 0x88351000
154       Check 7 : gran_state; intent id : 0x6 addr : 0x88306000
155       Check 8 : gran_state; intent id : 0x7 addr : 0x88303000
156    */
157    #[test]
158    fn rmi_granule_undelegate() {
159        let test_data = vec![
160            (0x88300001, ERROR_INPUT),
161            (0x1C0B0000, ERROR_INPUT),
162            (0x1000000001000, ERROR_INPUT),
163            (0x8830C000, ERROR_INPUT),
164            // TODO: Cover all test data
165        ];
166
167        for (input, output) in test_data {
168            let ret = rmi::<GRANULE_UNDELEGATE>(&[input]);
169            assert_eq!(output, ret[0]);
170        }
171
172        miri_teardown();
173    }
174}