Skip to main content

islet_rmm/granule/array/
entry.rs

1use crate::config;
2use crate::granule::array::GRANULE_STATUS_TABLE;
3use crate::rmi::error::Error;
4
5use super::{GranuleState, GRANULE_SIZE};
6use core::sync::atomic::{AtomicU8, Ordering};
7use safe_abstraction::raw_ptr;
8use spinning_top::{Spinlock, SpinlockGuard};
9use vmsa::guard::Content;
10
11// Safety: concurrency safety
12//  - For a granule status table that manages granules, it doesn't use a big lock for efficiency.
13//    So, we need to associate "lock" with each granule entry.
14
15#[cfg(not(any(kani, miri, test, fuzzing)))]
16#[derive(Debug)]
17pub struct Granule {
18    /// granule state
19    state: u8,
20    /// granule ref count
21    ref_count: AtomicU8,
22}
23#[cfg(any(kani, miri, test, fuzzing))]
24// DIFF: `gpt` ghost field is added to track GPT entry's status
25pub struct Granule {
26    /// granule state
27    state: u8,
28    /// granule ref count
29    ref_count: AtomicU8,
30    /// granule protection table (ghost field)
31    pub gpt: GranuleGpt,
32}
33
34#[cfg(kani)]
35#[derive(Copy, Clone, PartialEq, kani::Arbitrary)]
36pub enum GranuleGpt {
37    GPT_NS,
38    GPT_OTHER,
39    GPT_REALM,
40}
41
42#[cfg(any(miri, test, fuzzing))]
43#[derive(Copy, Clone, PartialEq)]
44pub enum GranuleGpt {
45    GPT_NS,
46    GPT_OTHER,
47    GPT_REALM,
48}
49
50impl Granule {
51    #[cfg(not(any(kani, miri, test, fuzzing)))]
52    fn new() -> Self {
53        let state = GranuleState::Undelegated;
54        let ref_count = AtomicU8::new(0);
55        Granule { state, ref_count }
56    }
57    #[cfg(any(kani, miri, test, fuzzing))]
58    // DIFF: `state` and `gpt` are filled with non-deterministic values
59    fn new() -> Self {
60        #[cfg(kani)]
61        {
62            let state = kani::any();
63            let ref_count = AtomicU8::new(kani::any());
64            kani::assume(state >= GranuleState::Undelegated && state <= GranuleState::RTT);
65            let gpt = {
66                if state != GranuleState::Undelegated {
67                    GranuleGpt::GPT_REALM
68                } else {
69                    let gpt = kani::any();
70                    kani::assume(gpt != GranuleGpt::GPT_REALM);
71                    gpt
72                }
73            };
74            Granule {
75                state,
76                ref_count,
77                gpt,
78            }
79        }
80
81        #[cfg(any(miri, test, fuzzing))]
82        {
83            Self {
84                state: GranuleState::Undelegated,
85                ref_count: AtomicU8::new(0),
86                gpt: GranuleGpt::GPT_NS,
87            }
88        }
89    }
90
91    pub fn inc_count(&mut self) {
92        let _ = self.ref_count.fetch_add(1, Ordering::SeqCst);
93    }
94
95    pub fn dec_count(&mut self) {
96        let _ = self.ref_count.fetch_sub(1, Ordering::SeqCst);
97    }
98
99    pub fn load_count(&self) -> u8 {
100        self.ref_count.load(Ordering::SeqCst)
101    }
102
103    pub fn num_children(&self) -> usize {
104        self.ref_count.load(Ordering::SeqCst) as usize
105    }
106
107    #[cfg(any(kani, miri, test, fuzzing))]
108    pub fn set_gpt(&mut self, gpt: GranuleGpt) {
109        self.gpt = gpt;
110    }
111
112    #[cfg(any(kani, miri, test, fuzzing))]
113    pub fn is_valid(&self) -> bool {
114        self.state >= GranuleState::Undelegated &&
115        self.state <= GranuleState::RTT &&
116        // XXX: the below condition holds from beta0 to eac4
117        if self.state != GranuleState::Undelegated {
118            self.gpt == GranuleGpt::GPT_REALM
119        } else {
120            self.gpt != GranuleGpt::GPT_REALM
121        }
122    }
123
124    pub fn state(&self) -> u8 {
125        self.state
126    }
127
128    pub fn set_state(&mut self, state: u8) -> Result<(), Error> {
129        let prev = self.state;
130        if (prev == GranuleState::Delegated && state == GranuleState::Undelegated)
131            || (state == GranuleState::Delegated)
132        {
133            self.zeroize();
134        }
135        self.state = state;
136        Ok(())
137    }
138
139    pub fn content_mut<T>(&mut self) -> Result<raw_ptr::SafetyAssumed<T>, Error>
140    where
141        T: Content + raw_ptr::SafetyChecked + raw_ptr::SafetyAssured,
142    {
143        let addr = self.index_to_addr();
144        Ok(raw_ptr::assume_safe::<T>(addr)?)
145    }
146
147    pub fn new_uninit_with<T>(&mut self, value: T) -> Result<raw_ptr::SafetyAssumed<T>, Error>
148    where
149        T: Content + raw_ptr::SafetyChecked + raw_ptr::SafetyAssured,
150    {
151        let addr = self.index_to_addr();
152        Ok(raw_ptr::assume_safe_uninit_with::<T>(addr, value)?)
153    }
154
155    pub fn content<T>(&self) -> Result<raw_ptr::SafetyAssumed<T>, Error>
156    where
157        T: Content + raw_ptr::SafetyChecked + raw_ptr::SafetyAssured,
158    {
159        let addr = self.index_to_addr();
160        Ok(raw_ptr::assume_safe::<T>(addr)?)
161    }
162
163    fn index(&self) -> usize {
164        let entry_size = core::mem::size_of::<Entry>();
165        let granule_size = core::mem::size_of::<Granule>();
166        //  XXX: is there a clever way of getting the Entry from Granule (e.g., container_of())?
167        //  [        Entry        ]
168        //  [  offset ] [ Granule ]
169        let granule_offset = entry_size - granule_size;
170        let granule_addr = self as *const Granule as usize;
171        let entry_addr = granule_addr - granule_offset;
172        let gst = &GRANULE_STATUS_TABLE;
173        let table_base = gst.entries.as_ptr() as usize;
174        (entry_addr - table_base) / core::mem::size_of::<Entry>()
175    }
176
177    #[cfg(not(any(kani, miri, test, fuzzing)))]
178    fn index_to_addr(&self) -> usize {
179        let mut idx = self.index();
180        let regions = config::NS_DRAM_REGIONS.lock();
181
182        for range in regions.iter() {
183            let region_max = (range.end - range.start) / GRANULE_SIZE;
184            if idx < region_max {
185                return range.start + (idx * GRANULE_SIZE);
186            }
187            idx -= region_max;
188        }
189        0
190    }
191    #[cfg(any(kani, miri, test, fuzzing))]
192    // DIFF: calculate addr using GRANULE_REGION
193    pub fn index_to_addr(&self) -> usize {
194        use crate::granule::{GRANULE_REGION, GRANULE_STATUS_TABLE_SIZE};
195        let idx = self.index();
196        assert!(idx < GRANULE_STATUS_TABLE_SIZE);
197
198        #[cfg(any(miri, test, fuzzing))]
199        return crate::test_utils::align_up(unsafe {
200            GRANULE_REGION.as_ptr() as usize + (idx * GRANULE_SIZE)
201        });
202
203        #[cfg(kani)]
204        return unsafe { GRANULE_REGION.as_ptr() as usize + (idx * GRANULE_SIZE) };
205    }
206
207    #[cfg(not(any(kani, miri, test, fuzzing)))]
208    fn zeroize(&mut self) {
209        let addr = self.index_to_addr();
210
211        // Safety: This operation writes to a Granule outside the RMM Memory region,
212        //         thus not violating RMM's Memory Safety.
213        //         (ref. RMM Specification A2.2.4 Granule Wiping)
214        unsafe {
215            core::ptr::write_bytes(addr as *mut u8, 0x0, GRANULE_SIZE);
216        }
217    }
218    #[cfg(any(kani, miri, test, fuzzing))]
219    // DIFF: assertion is added to reduce the proof burden
220    //       `write_bytes()` uses a small count value
221    fn zeroize(&mut self) {
222        let addr = self.index_to_addr();
223        let g_start = unsafe { crate::granule::array::GRANULE_REGION.as_ptr() as usize };
224        let g_end = g_start + crate::granule::array::GRANULE_MEM_SIZE;
225        assert!(addr >= g_start && addr < g_end);
226
227        unsafe {
228            core::ptr::write_bytes(addr as *mut u8, 0x0, 8);
229            assert!(*(addr as *const u8) == 0);
230        }
231    }
232}
233
234pub struct Entry(Spinlock<Granule>);
235impl Entry {
236    #[cfg(not(any(kani, miri, test, fuzzing)))]
237    pub fn new() -> Self {
238        Self(Spinlock::new(Granule::new()))
239    }
240    #[cfg(any(kani, miri, test, fuzzing))]
241    // DIFF: assertion is added to reduce the proof burden
242    pub fn new() -> Self {
243        let granule = Granule::new();
244        assert!(granule.is_valid());
245        Self(Spinlock::new(granule))
246    }
247
248    pub fn lock(&self) -> Result<SpinlockGuard<'_, Granule>, Error> {
249        let granule = self.0.lock();
250        Ok(granule)
251    }
252}