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