memory_addresses/arch/
aarch64.rs

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