Skip to main content

islet_rmm/granule/array/
mod.rs

1pub mod entry;
2
3use self::entry::Entry;
4use self::entry::Granule;
5use crate::config;
6use crate::rmi::error::Error;
7
8pub const GRANULE_SIZE: usize = 4096;
9pub const GRANULE_SHIFT: usize = 12;
10pub const GRANULE_MASK: usize = !((1 << GRANULE_SHIFT) - 1);
11
12#[cfg(any(kani, miri, test, fuzzing))]
13pub const GRANULE_MEM_SIZE: usize = GRANULE_SIZE * GRANULE_STATUS_TABLE_SIZE;
14#[cfg(any(kani, miri, test, fuzzing))]
15// We model the Host memory as a pre-allocated memory region which
16// can avoid a false positive related to invalid memory accesses
17// in model checking. Also, instead of using the same starting
18// address (e.g., 0x8000_0000), we use a mock region filled with
19// non-deterministic contents. It helps to address an issue related
20// to the backend CBMC's pointer encoding, as 0x8000_0000 cannot be
21// distinguished from null pointer in CBMC.
22//
23// NOTE: In MIRI and test evironments, aligned addresses are used,
24//       so the last region cannot be utilized.
25pub static mut GRANULE_REGION: [u8; GRANULE_MEM_SIZE] = [0; GRANULE_MEM_SIZE];
26
27#[cfg(not(any(kani, miri, test, fuzzing)))]
28pub fn validate_addr(addr: usize) -> bool {
29    if !addr.is_multiple_of(GRANULE_SIZE) {
30        // if the address is out of range.
31        warn!("address need to be aligned 0x{:X}", addr);
32        return false;
33    }
34    if !config::is_ns_dram(addr) {
35        // if the address is out of range.
36        warn!("address is strange 0x{:X}", addr);
37        return false;
38    }
39    true
40}
41#[cfg(any(kani, miri, test, fuzzing))]
42// DIFF: check against GRANULE_REGION
43pub fn validate_addr(addr: usize) -> bool {
44    if addr % GRANULE_SIZE != 0 {
45        // if the address is out of range.
46        warn!("address need to be aligned 0x{:X}", addr);
47        return false;
48    }
49    let g_start = unsafe { GRANULE_REGION.as_ptr() as usize };
50    let g_end = g_start + GRANULE_MEM_SIZE;
51    addr >= g_start && addr < g_end
52}
53
54#[cfg(not(any(kani, miri, test, fuzzing)))]
55pub fn granule_addr_to_index(addr: usize) -> usize {
56    let regions = config::NS_DRAM_REGIONS.lock();
57
58    let mut base_idx = 0;
59    for range in regions.iter() {
60        if range.contains(&addr) {
61            return (addr - range.start) / GRANULE_SIZE + base_idx;
62        }
63        base_idx += (range.end - range.start) / GRANULE_SIZE;
64    }
65
66    usize::MAX
67}
68#[cfg(any(kani, miri, test, fuzzing))]
69// DIFF: calculate index using GRANULE_REGION
70pub fn granule_addr_to_index(addr: usize) -> usize {
71    let g_start = unsafe { GRANULE_REGION.as_ptr() as usize };
72    let g_end = g_start + GRANULE_MEM_SIZE;
73    if addr >= g_start && addr < g_end {
74        return (addr - g_start) / GRANULE_SIZE;
75    }
76    usize::MAX
77}
78
79pub fn is_granule_aligned(addr: usize) -> bool {
80    addr.is_multiple_of(GRANULE_SIZE)
81}
82
83#[derive(Clone, Copy, Debug, PartialEq)]
84pub struct GranuleState {
85    pub inner: u8,
86}
87
88#[allow(non_upper_case_globals)]
89impl GranuleState {
90    pub const Undelegated: u8 = 0;
91    pub const Delegated: u8 = 1;
92    pub const RD: u8 = 2;
93    pub const Rec: u8 = 3;
94    pub const RecAux: u8 = 4;
95    pub const Data: u8 = 5;
96    pub const RTT: u8 = 6;
97    pub const Metadata: u8 = 7;
98
99    pub fn new(state: u8) -> Self {
100        Self { inner: state }
101    }
102}
103
104pub fn set_granule(granule: &mut Granule, state: u8) -> Result<(), Error> {
105    granule.set_state(state)
106}
107
108lazy_static! {
109    pub static ref GRANULE_STATUS_TABLE: GranuleStatusTable = GranuleStatusTable::new();
110}
111
112#[cfg(not(any(kani, miri, test, fuzzing)))]
113pub const GRANULE_STATUS_TABLE_SIZE: usize = config::MAX_DRAM_SIZE / GRANULE_SIZE; // == RMM_MAX_GRANULES
114#[cfg(kani)]
115pub const GRANULE_STATUS_TABLE_SIZE: usize = 6;
116#[cfg(any(miri, test))]
117pub const GRANULE_STATUS_TABLE_SIZE: usize = 55;
118#[cfg(fuzzing)]
119pub const GRANULE_STATUS_TABLE_SIZE: usize = 2048;
120
121pub struct GranuleStatusTable {
122    pub entries: [Entry; GRANULE_STATUS_TABLE_SIZE],
123}
124
125impl GranuleStatusTable {
126    pub fn new() -> Self {
127        Self {
128            entries: core::array::from_fn(|_| Entry::new()),
129        }
130    }
131
132    #[cfg(kani)]
133    pub fn is_valid(&self) -> bool {
134        self.entries
135            .iter()
136            .fold(true, |acc, x| acc && x.lock().unwrap().is_valid())
137    }
138}
139
140#[macro_export]
141macro_rules! get_granule {
142    ($addr:expr) => {{
143        use crate::granule::array::{GRANULE_STATUS_TABLE, GRANULE_STATUS_TABLE_SIZE};
144        use crate::granule::{granule_addr_to_index, validate_addr};
145        use crate::rmi::error::Error;
146        if !validate_addr($addr) {
147            Err(Error::RmiErrorInput)
148        } else {
149            let idx = granule_addr_to_index($addr);
150            if idx >= GRANULE_STATUS_TABLE_SIZE {
151                Err(Error::RmiErrorInput)
152            } else {
153                let gst = &GRANULE_STATUS_TABLE;
154                match gst.entries[idx].lock() {
155                    Ok(guard) => Ok(guard),
156                    Err(e) => Err(e),
157                }
158            }
159        }
160    }};
161}
162
163#[macro_export]
164macro_rules! get_granule_if {
165    ($addr:expr, $state:expr) => {{
166        get_granule!($addr).and_then(|guard| {
167            if guard.state() != $state {
168                use crate::rmi::error::Error;
169                Err(Error::RmiErrorInput)
170            } else {
171                Ok(guard)
172            }
173        })
174    }};
175}
176
177pub fn is_not_in_realm(addr: usize) -> bool {
178    match get_granule_if!(addr, GranuleState::Undelegated) {
179        Ok(_) => true,
180        _ => false,
181    }
182}