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;