irv/
memory.rs

1use core::{
2    marker::PhantomData,
3    mem::{align_of, size_of, transmute},
4    slice,
5};
6
7use crate::{Bus, BusError};
8
9/// An efficient implementation of main memory.
10#[repr(transparent)]
11pub struct Memory {
12    _phantom: PhantomData<*mut u8>,
13    data: [u8],
14}
15
16impl_bus! {
17    u64 u8,
18    u64 u16,
19    u64 u32,
20    u64 u64,
21}
22
23impl Memory {
24    /// Converts a slice to be used as memory for a hart.
25    pub fn new<'d>(data: &'d mut [u64]) -> &'d mut Memory {
26        let ptr = data.as_mut_ptr() as *mut u8;
27        let len = data.len() * size_of::<u64>();
28
29        // Drop `data` so that there are never mutable references to the same
30        // memory at the same time.
31        drop(data);
32
33        // SAFETY: The previous slice has been dropped, so we know that this
34        // mutable reference is not an alias of another mutable reference.
35        let slice = unsafe { slice::from_raw_parts_mut(ptr, len) };
36
37        // SAFETY: We are transmuting between reference types; the inner types
38        // have the same memory layout, and the lifetime is the same.
39        unsafe { transmute::<&'d mut [u8], &'d mut Memory>(slice) }
40    }
41
42    /// Gets the size of memory in bytes.
43    pub const fn size(&self) -> usize {
44        self.data.len()
45    }
46
47    /// Gets a reference to the inner data. You cannot get an immutable reference,
48    /// since &[u8] is Send+Sync, while &Memory is not. In other words, an immutable
49    /// reference to the slice obtained through some other way than this method
50    /// would invoke undefined behavior.
51    pub fn data(&mut self) -> &mut [u8] {
52        &mut self.data
53    }
54
55    #[inline]
56    fn calculate_destination<T>(&self, address: usize) -> Result<*mut T, BusError> {
57        let upper = address.wrapping_add(size_of::<T>());
58
59        if address < upper && upper <= self.size() {
60            // SAFETY: We know that `address` < `self.size()`. Also, we know that
61            // `self.size()` < `isize::MAX`, since this is a requirement on the
62            // length of slices. Finally, we know it lies within the same allocated
63            // object and does not wrap around per the condition of the enclosing if statement.
64            let ptr = unsafe { self.data.as_ptr().add(address) } as *mut T;
65
66            if ptr as usize % align_of::<T>() == 0 {
67                Ok(ptr)
68            } else {
69                Err(BusError::AddressMisaligned)
70            }
71        } else {
72            Err(BusError::AccessFault)
73        }
74    }
75}
76
77macro_rules! impl_bus {
78    ($($addr:ident $val:ident,)*) => {
79        $(impl Bus<$addr, $val> for Memory {
80            fn load(&self, address: $addr) -> Result<$val, BusError> {
81                if address as usize as u64 == address {
82                    let ptr = self.calculate_destination(address as usize)?;
83
84                    // SAFETY: check_address returns a pointer that is guaranteed to be valid.
85                    Ok(unsafe { *ptr })
86                } else {
87                    Err(BusError::AccessFault)
88                }
89            }
90
91            fn store(&self, address: $addr, value: $val) -> Result<(), BusError> {
92                if address as usize as u64 == address {
93                    let ptr = self.calculate_destination(address as usize)?;
94
95                    // SAFETY: check_address returns a pointer that is guaranteed to be valid.
96                    Ok(unsafe { *ptr = value })
97                } else {
98                    Err(BusError::AccessFault)
99                }
100            }
101        })*
102    };
103}
104
105use impl_bus;