memory_addresses/arch/
riscv64.rs

1//! Physical and virtual addresses manipulation for 64-bit RISC-V
2
3use crate::impl_address;
4use core::fmt;
5
6use align_address::Align;
7
8/// Size of a base page (4 KiB)
9pub const BASE_PAGE_SIZE: usize = 4096;
10
11/// Size of a mega page (2 MiB)
12pub const MEGA_PAGE_SIZE: usize = 1024 * 1024 * 2;
13
14/// Size of a giga page (1 GiB)
15pub const GIGA_PAGE_SIZE: usize = 1024 * 1024 * 1024;
16
17/// Size of a tera page (512 GiB)
18pub const TERA_PAGE_SIZE: usize = 1024 * 1024 * 1024 * 512;
19
20/// A virtual memory address on `riscv64`.
21///
22/// 64-bit addresses on riscv64 "must have bits 63–48 all equal to bit 47, or
23/// else a page-fault exception will occur."
24#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25#[repr(transparent)]
26pub struct VirtAddr(u64);
27
28impl_address!(VirtAddr, u64);
29
30/// An invalid virtual address.
31pub struct VirtAddrNotValid(pub u64);
32
33impl core::fmt::Debug for VirtAddrNotValid {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        f.debug_tuple("VirtAddrNotValid")
36            .field(&format_args!("{:#x}", self.0))
37            .finish()
38    }
39}
40
41impl VirtAddr {
42    /// Creates a new canonical virtual address.
43    #[inline]
44    pub const fn new(addr: u64) -> VirtAddr {
45        // TODO: Replace with .ok().expect(msg) when that works on stable.
46        match Self::try_new(addr) {
47            Ok(v) => v,
48            Err(_) => panic!("virtual address must be sign extended in bits 48 to 64"),
49        }
50    }
51
52    /// Tries to create a new canonical virtual address.
53    #[inline]
54    pub const fn try_new(addr: u64) -> Result<VirtAddr, VirtAddrNotValid> {
55        match addr {
56            0..=0xFFFF_FFFF_FFFF | 0xFFFF800000000000..=0xffff_ffff_ffff_ffff => Ok(Self(addr)),
57            _ => Err(VirtAddrNotValid(addr)),
58        }
59    }
60
61    /// Creates a new canonical virtual address.
62    ///
63    /// This function performs sign extension of bit 47 to make the address
64    /// canonical, overwriting bits 48 to 63.
65    #[inline]
66    pub const fn new_truncate(addr: u64) -> VirtAddr {
67        // By doing the right shift as a signed operation (on a i64), it will
68        // sign extend the value, repeating the leftmost bit.
69        VirtAddr(((addr << 16) as i64 >> 16) as u64)
70    }
71
72    /// Creates a virtual address from the given pointer
73    #[inline]
74    pub fn from_ptr<T: ?Sized>(ptr: *const T) -> Self {
75        Self::new(ptr as *const () as u64)
76    }
77
78    /// Converts the address to a raw pointer.
79    #[inline]
80    pub const fn as_ptr<T>(self) -> *const T {
81        self.0 as *const T
82    }
83
84    /// Converts the address to a mutable raw pointer.
85    #[inline]
86    pub const fn as_mut_ptr<T>(self) -> *mut T {
87        self.as_ptr::<T>() as *mut T
88    }
89
90    #[cfg(target_pointer_width = "64")]
91    // if the target_pointer_width is 64, usize = u64 so we can safely transform.
92    pub const fn as_usize(&self) -> usize {
93        self.0 as usize
94    }
95
96    /// Offset within the 4 KiB page.
97    pub fn base_page_offset(self) -> u64 {
98        self.0 & (BASE_PAGE_SIZE as u64 - 1)
99    }
100
101    /// Offset within the 2 MiB page.
102    pub fn large_page_offset(self) -> u64 {
103        self.0 & (MEGA_PAGE_SIZE as u64 - 1)
104    }
105
106    /// Offset within the 1 GiB page.
107    pub fn giga_page_offset(self) -> u64 {
108        self.0 & (GIGA_PAGE_SIZE as u64 - 1)
109    }
110
111    /// Offset within the 512 GiB page.
112    pub fn tera_page_offset(self) -> u64 {
113        self.0 & (TERA_PAGE_SIZE as u64 - 1)
114    }
115}
116
117impl Align<u64> for VirtAddr {
118    #[inline]
119    fn align_down(self, align: u64) -> Self {
120        Self::new_truncate(self.0.align_down(align))
121    }
122
123    #[inline]
124    fn align_up(self, align: u64) -> Self {
125        Self::new_truncate(self.0.align_up(align))
126    }
127}
128
129#[cfg(target_pointer_width = "64")]
130// if the target_pointer_width is 64, usize = u64 so we can safely transform.
131impl From<usize> for VirtAddr {
132    fn from(addr: usize) -> VirtAddr {
133        Self::new_truncate(addr as u64)
134    }
135}
136
137#[cfg(target_pointer_width = "64")]
138// if the target_pointer_width is 64, usize = u64 so we can safely add
139impl core::ops::Add<usize> for VirtAddr {
140    type Output = Self;
141    #[inline]
142    fn add(self, rhs: usize) -> Self::Output {
143        VirtAddr::new(self.0 + rhs as u64)
144    }
145}
146#[cfg(target_pointer_width = "64")]
147// if the target_pointer_width is 64, usize = u64 so we can safely add
148impl core::ops::AddAssign<usize> for VirtAddr {
149    #[inline]
150    fn add_assign(&mut self, rhs: usize) {
151        *self = *self + rhs;
152    }
153}
154#[cfg(target_pointer_width = "64")]
155// if the target_pointer_width is 64, usize = u64 so we can safely sub
156impl core::ops::Sub<usize> for VirtAddr {
157    type Output = Self;
158    #[inline]
159    fn sub(self, rhs: usize) -> Self::Output {
160        VirtAddr::new(self.0.checked_sub(rhs as u64).unwrap())
161    }
162}
163#[cfg(target_pointer_width = "64")]
164// if the target_pointer_width is 64, usize = u64 so we can safely sub
165impl core::ops::SubAssign<usize> for VirtAddr {
166    #[inline]
167    fn sub_assign(&mut self, rhs: usize) {
168        *self = *self - rhs;
169    }
170}
171
172#[cfg(target_pointer_width = "64")]
173// if the target_pointer_width is 64, usize = u64 so we can safely transform.
174impl From<usize> for PhysAddr {
175    fn from(addr: usize) -> PhysAddr {
176        Self::new_truncate(addr as u64)
177    }
178}
179
180/// A physical memory address.
181///
182/// The size of a valid physical address on riscv64 is 44 bit PPN + 12 bit offset = 56 bit
183#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
184#[repr(transparent)]
185pub struct PhysAddr(u64);
186
187impl_address!(PhysAddr, u64);
188
189/// A passed `u64` was not a valid physical address.
190///
191/// This means that bits 54 to 64 were not all null.
192///
193/// Contains the invalid address.
194pub struct PhysAddrNotValid(pub u64);
195
196impl core::fmt::Debug for PhysAddrNotValid {
197    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198        f.debug_tuple("PhysAddrNotValid")
199            .field(&format_args!("{:#x}", self.0))
200            .finish()
201    }
202}
203
204impl PhysAddr {
205    /// Creates a new physical address.
206    #[inline]
207    pub const fn new(addr: u64) -> Self {
208        // TODO: Replace with .ok().expect(msg) when that works on stable.
209        match Self::try_new(addr) {
210            Ok(p) => p,
211            Err(_) => panic!("physical addresses must not have any bits in the range 57 to 64 set"),
212        }
213    }
214
215    /// Creates a new physical address truncating non-address parts.
216    #[inline]
217    pub const fn new_truncate(addr: u64) -> PhysAddr {
218        PhysAddr(addr % (1 << 54))
219    }
220
221    /// Tries to create a new physical address.
222    ///
223    /// Fails if any bits in the range 56 to 64 are set.
224    #[inline]
225    pub const fn try_new(addr: u64) -> Result<Self, PhysAddrNotValid> {
226        match addr {
227            0..=0x3FFFFFFFFFFFFF => Ok(Self(addr)),
228            _ => Err(PhysAddrNotValid(addr)),
229        }
230    }
231
232    #[cfg(target_pointer_width = "64")]
233    // if the target_pointer_width is 64, usize = u64 so we can safely transform.
234    pub const fn as_usize(&self) -> usize {
235        self.0 as usize
236    }
237}
238
239impl Align<u64> for PhysAddr {
240    #[inline]
241    fn align_down(self, align: u64) -> Self {
242        Self::new(self.0.align_down(align))
243    }
244
245    #[inline]
246    fn align_up(self, align: u64) -> Self {
247        Self::new(self.0.align_up(align))
248    }
249}
250
251#[cfg(target_pointer_width = "64")]
252// if the target_pointer_width is 64, usize = u64 so we can safely add
253impl core::ops::Add<usize> for PhysAddr {
254    type Output = Self;
255    #[inline]
256    fn add(self, rhs: usize) -> Self::Output {
257        PhysAddr::new(self.0 + rhs as u64)
258    }
259}
260
261#[cfg(target_pointer_width = "64")]
262// if the target_pointer_width is 64, usize = u64 so we can safely add
263impl core::ops::AddAssign<usize> for PhysAddr {
264    #[inline]
265    fn add_assign(&mut self, rhs: usize) {
266        *self = *self + rhs;
267    }
268}
269
270#[cfg(target_pointer_width = "64")]
271// if the target_pointer_width is 64, usize = u64 so we can safely sub
272impl core::ops::Sub<usize> for PhysAddr {
273    type Output = Self;
274    #[inline]
275    fn sub(self, rhs: usize) -> Self::Output {
276        PhysAddr::new(self.0.checked_sub(rhs as u64).unwrap())
277    }
278}
279
280#[cfg(target_pointer_width = "64")]
281// if the target_pointer_width is 64, usize = u64 so we can safely sub
282impl core::ops::SubAssign<usize> for PhysAddr {
283    #[inline]
284    fn sub_assign(&mut self, rhs: usize) {
285        *self = *self - rhs;
286    }
287}
288
289#[cfg(test)]
290mod tests {
291    use super::*;
292
293    #[test]
294    fn test_virt_addr() {
295        let _ = VirtAddr::new(0x0);
296        let _ = VirtAddr::new(0x1);
297        let _ = VirtAddr::new(0x0000_ffff_ffff_ffff);
298        let _ = VirtAddr::new(0xffff_8000_0000_0000);
299        let _ = VirtAddr::new(0xffff_ffff_ffff_ffff);
300    }
301
302    #[test]
303    #[should_panic]
304    fn test_invalid_virt_addr() {
305        let _ = VirtAddr::new(0xffff_0000_0000_0000);
306        let _ = VirtAddr::new(0x0ff0_0000_0000_0000);
307        let _ = VirtAddr::new(0xffe0_0000_0000_0000);
308        let _ = VirtAddr::new(0x0010_0000_0000_0000);
309    }
310
311    #[test]
312    fn test_phys_addr() {
313        let _ = PhysAddr::new(0x0);
314        let _ = PhysAddr::new(0x1);
315        let _ = PhysAddr::new(0x003F_FFFF_FFFF_FFFF);
316    }
317
318    #[test]
319    #[should_panic]
320    fn test_invalid_phys_addr() {
321        let _ = PhysAddr::new(0x0040_0000_0000_0000);
322        let _ = PhysAddr::new(0x0100_0000_0000_0000);
323        let _ = PhysAddr::new(0xffff_ffff_ffff_ffff);
324    }
325
326    #[test]
327    pub fn virtaddr_new_truncate() {
328        assert_eq!(VirtAddr::new_truncate(0), VirtAddr(0));
329        assert_eq!(VirtAddr::new_truncate(1 << 46), VirtAddr(1 << 46));
330        assert_eq!(VirtAddr::new_truncate(1 << 47), VirtAddr(0xfffff << 47));
331        assert_eq!(VirtAddr::new_truncate(123), VirtAddr(123));
332        assert_eq!(VirtAddr::new_truncate(123 << 46), VirtAddr(0xfffff << 46));
333        assert_eq!(
334            VirtAddr::new_truncate(0xfff0_0000_0000_0000),
335            VirtAddr::new_truncate(0xfff0_0000_0000_0000)
336        );
337        assert_eq!(
338            VirtAddr::new_truncate(0xfff0_0000_0000_1000),
339            VirtAddr::new_truncate(0xfff0_0000_0000_1000)
340        );
341        assert_eq!(
342            VirtAddr::new_truncate(0xffff_ffff_ffff_ffff),
343            VirtAddr::new_truncate(0xffff_ffff_ffff_ffff)
344        );
345    }
346
347    #[test]
348    fn test_virt_addr_align_up() {
349        assert_eq!(
350            VirtAddr::new(0x0000_0000_0000_1234).align_up(0x1000_u64),
351            VirtAddr::new(0x0000_0000_0000_2000)
352        );
353        assert_eq!(
354            VirtAddr::new(0x0000_7fff_ffff_ffff).align_up(2u64),
355            VirtAddr::new(0xffff_8000_0000_0000)
356        );
357    }
358
359    #[test]
360    fn test_virt_addr_align_down() {
361        assert_eq!(
362            VirtAddr::new(0x0000_0000_0000_1005).align_down(0x1000_u64),
363            VirtAddr::new(0x0000_0000_0000_1000)
364        );
365        assert_eq!(
366            VirtAddr::new(0x0000_0000_0000_1000).align_down(0x1_0000_u64),
367            VirtAddr::new(0x0000_0000_0000_0000)
368        );
369        assert_eq!(
370            VirtAddr::new(0xffff_8000_0000_0000).align_down(1u64 << 10),
371            VirtAddr::new(0xffff_8000_0000_0000)
372        );
373    }
374
375    #[test]
376    #[should_panic]
377    fn test_virt_addr_align_up_overflow() {
378        let _ = VirtAddr::new(0xffff_ffff_ffff_ffff).align_up(2u64);
379    }
380
381    #[test]
382    #[should_panic]
383    fn test_phys_addr_align_up_overflow() {
384        PhysAddr::new(0x00ff_ffff_ffff_ffff).align_up(2u64);
385    }
386
387    #[test]
388    fn test_from_ptr_array() {
389        let slice = &[1, 2, 3, 4, 5];
390        // Make sure that from_ptr(slice) is the address of the first element
391        assert_eq!(VirtAddr::from_ptr(slice), VirtAddr::from_ptr(&slice[0]));
392    }
393}