Skip to main content

memory_addresses/arch/
x86_64.rs

1//! Physical and virtual addresses manipulation
2
3use align_address::Align;
4use core::fmt;
5#[cfg(feature = "conv-x86")]
6use x86::bits64::paging::{PAddr as x86_PAddr, VAddr as x86_VAddr};
7
8use crate::impl_address;
9
10use x86_64::structures::paging::page_table::PageTableLevel;
11use x86_64::structures::paging::{PageOffset, PageTableIndex};
12
13/// A canonical 64-bit virtual memory address.
14///
15/// This is a wrapper type around an `u64`, so it is always 8 bytes, even when compiled
16/// on non 64-bit systems. The
17/// [`TryFrom`](https://doc.rust-lang.org/std/convert/trait.TryFrom.html) trait can be used for performing conversions
18/// between `u64` and `usize`.
19///
20/// On `x86_64`, only the 48 lower bits of a virtual address can be used. The top 16 bits need
21/// to be copies of bit 47, i.e. the most significant bit. Addresses that fulfil this criterion
22/// are called “canonical”. This type guarantees that it always represents a canonical address.
23#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
24#[repr(transparent)]
25pub struct VirtAddr(u64);
26
27impl_address!(VirtAddr, u64, as_u64);
28
29/// A 64-bit physical memory address.
30///
31/// This is a wrapper type around an `u64`, so it is always 8 bytes, even when compiled
32/// on non 64-bit systems. The
33/// [`TryFrom`](https://doc.rust-lang.org/std/convert/trait.TryFrom.html) trait can be used for performing conversions
34/// between `u64` and `usize`.
35///
36/// On `x86_64`, only the 52 lower bits of a physical address can be used. The top 12 bits need
37/// to be zero. This type guarantees that it always represents a valid physical address.
38#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
39#[repr(transparent)]
40pub struct PhysAddr(u64);
41
42impl_address!(PhysAddr, u64, as_u64);
43
44/// A passed `u64` was not a valid virtual address.
45///
46/// This means that bits 48 to 64 are not
47/// a valid sign extension and are not null either. So automatic sign extension would have
48/// overwritten possibly meaningful bits. This likely indicates a bug, for example an invalid
49/// address calculation.
50///
51/// Contains the invalid address.
52pub struct VirtAddrNotValid(pub u64);
53
54impl core::fmt::Debug for VirtAddrNotValid {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        f.debug_tuple("VirtAddrNotValid")
57            .field(&format_args!("{:#x}", self.0))
58            .finish()
59    }
60}
61
62impl VirtAddr {
63    /// Creates a new canonical virtual address.
64    ///
65    /// The provided address should already be canonical. If you want to check
66    /// whether an address is canonical, use [`try_new`](Self::try_new).
67    ///
68    /// ## Panics
69    ///
70    /// This function panics if the bits in the range 48 to 64 are invalid
71    /// (i.e. are not a proper sign extension of bit 47).
72    #[inline]
73    pub const fn new(addr: u64) -> VirtAddr {
74        // TODO: Replace with .ok().expect(msg) when that works on stable.
75        match Self::try_new(addr) {
76            Ok(v) => v,
77            Err(_) => panic!("virtual address must be sign extended in bits 48 to 64"),
78        }
79    }
80
81    /// Tries to create a new canonical virtual address.
82    ///
83    /// This function checks wether the given address is canonical
84    /// and returns an error otherwise. An address is canonical
85    /// if bits 48 to 64 are a correct sign
86    /// extension (i.e. copies of bit 47).
87    #[inline]
88    pub const fn try_new(addr: u64) -> Result<VirtAddr, VirtAddrNotValid> {
89        let v = Self::new_truncate(addr);
90        if v.0 == addr {
91            Ok(v)
92        } else {
93            Err(VirtAddrNotValid(addr))
94        }
95    }
96
97    /// Creates a new canonical virtual address, throwing out bits 48..64.
98    ///
99    /// This function performs sign extension of bit 47 to make the address
100    /// canonical, overwriting bits 48 to 64. If you want to check whether an
101    /// address is canonical, use [`new`](Self::new) or [`try_new`](Self::try_new).
102    #[inline]
103    pub const fn new_truncate(addr: u64) -> VirtAddr {
104        // By doing the right shift as a signed operation (on a i64), it will
105        // sign extend the value, repeating the leftmost bit.
106        VirtAddr(((addr << 16) as i64 >> 16) as u64)
107    }
108
109    /// Creates a virtual address from the given pointer
110    #[cfg(target_pointer_width = "64")]
111    #[inline]
112    pub fn from_ptr<T: ?Sized>(ptr: *const T) -> Self {
113        Self::new(ptr as *const () as u64)
114    }
115
116    /// Converts the address to a raw pointer.
117    #[cfg(target_pointer_width = "64")]
118    #[inline]
119    pub const fn as_ptr<T>(self) -> *const T {
120        self.as_u64() as *const T
121    }
122
123    /// Converts the address to a mutable raw pointer.
124    #[cfg(target_pointer_width = "64")]
125    #[inline]
126    pub const fn as_mut_ptr<T>(self) -> *mut T {
127        self.as_ptr::<T>() as *mut T
128    }
129
130    #[cfg(target_pointer_width = "64")]
131    // if the target_pointer_width is 64, usize = u64 so we can safely transform.
132    pub const fn as_usize(&self) -> usize {
133        self.0 as usize
134    }
135
136    /// Checks whether the virtual address has the demanded alignment.
137    #[inline]
138    pub fn is_aligned(self, align: u64) -> bool {
139        self.align_down(align).as_u64() == self.as_u64()
140    }
141
142    /// Returns the 12-bit page offset of this virtual address.
143    #[inline]
144    pub const fn page_offset(self) -> PageOffset {
145        PageOffset::new_truncate(self.0 as u16)
146    }
147
148    /// Returns the 9-bit level 1 page table index.
149    #[inline]
150    pub const fn p1_index(self) -> PageTableIndex {
151        PageTableIndex::new_truncate((self.0 >> 12) as u16)
152    }
153
154    /// Returns the 9-bit level 2 page table index.
155    #[inline]
156    pub const fn p2_index(self) -> PageTableIndex {
157        PageTableIndex::new_truncate((self.0 >> 12 >> 9) as u16)
158    }
159
160    /// Returns the 9-bit level 3 page table index.
161    #[inline]
162    pub const fn p3_index(self) -> PageTableIndex {
163        PageTableIndex::new_truncate((self.0 >> 12 >> 9 >> 9) as u16)
164    }
165
166    /// Returns the 9-bit level 4 page table index.
167    #[inline]
168    pub const fn p4_index(self) -> PageTableIndex {
169        PageTableIndex::new_truncate((self.0 >> 12 >> 9 >> 9 >> 9) as u16)
170    }
171
172    /// Returns the 9-bit level page table index.
173    #[inline]
174    pub const fn page_table_index(self, level: PageTableLevel) -> PageTableIndex {
175        PageTableIndex::new_truncate((self.0 >> 12 >> ((level as u8 - 1) * 9)) as u16)
176    }
177}
178
179impl Align<u64> for VirtAddr {
180    #[inline]
181    fn align_down(self, align: u64) -> Self {
182        Self::new_truncate(self.0.align_down(align))
183    }
184
185    #[inline]
186    fn checked_align_up(self, align: u64) -> Option<Self> {
187        let addr = self.0.checked_align_up(align)?;
188        Some(Self::new_truncate(addr))
189    }
190}
191
192#[cfg(target_pointer_width = "64")]
193// if the target_pointer_width is 64, usize = u64 so we can safely transform.
194impl From<usize> for VirtAddr {
195    fn from(addr: usize) -> VirtAddr {
196        Self::new_truncate(addr as u64)
197    }
198}
199
200#[cfg(target_pointer_width = "64")]
201// if the target_pointer_width is 64, usize = u64 so we can safely add
202impl core::ops::Add<usize> for VirtAddr {
203    type Output = Self;
204    #[inline]
205    fn add(self, rhs: usize) -> Self::Output {
206        VirtAddr::new(self.0 + rhs as u64)
207    }
208}
209
210#[cfg(target_pointer_width = "64")]
211// if the target_pointer_width is 64, usize = u64 so we can safely add
212impl core::ops::AddAssign<usize> for VirtAddr {
213    #[inline]
214    fn add_assign(&mut self, rhs: usize) {
215        *self = *self + rhs;
216    }
217}
218
219#[cfg(target_pointer_width = "64")]
220// if the target_pointer_width is 64, usize = u64 so we can safely sub
221impl core::ops::Sub<usize> for VirtAddr {
222    type Output = Self;
223    #[inline]
224    fn sub(self, rhs: usize) -> Self::Output {
225        VirtAddr::new(self.0.checked_sub(rhs as u64).unwrap())
226    }
227}
228
229#[cfg(target_pointer_width = "64")]
230// if the target_pointer_width is 64, usize = u64 so we can safely sub
231impl core::ops::SubAssign<usize> for VirtAddr {
232    #[inline]
233    fn sub_assign(&mut self, rhs: usize) {
234        *self = *self - rhs;
235    }
236}
237
238#[cfg(feature = "conv-x86_64")]
239impl From<x86_64::VirtAddr> for VirtAddr {
240    fn from(addr: x86_64::VirtAddr) -> Self {
241        Self(addr.as_u64())
242    }
243}
244#[cfg(feature = "conv-x86_64")]
245impl From<&x86_64::VirtAddr> for VirtAddr {
246    fn from(addr: &x86_64::VirtAddr) -> Self {
247        Self(addr.as_u64())
248    }
249}
250
251#[cfg(feature = "conv-x86_64")]
252impl From<VirtAddr> for x86_64::VirtAddr {
253    fn from(addr: VirtAddr) -> x86_64::VirtAddr {
254        x86_64::VirtAddr::new(addr.0)
255    }
256}
257
258#[cfg(feature = "conv-x86_64")]
259impl From<&VirtAddr> for x86_64::VirtAddr {
260    fn from(addr: &VirtAddr) -> x86_64::VirtAddr {
261        x86_64::VirtAddr::new(addr.0)
262    }
263}
264
265#[cfg(feature = "conv-x86")]
266impl From<x86_VAddr> for VirtAddr {
267    fn from(addr: x86_VAddr) -> Self {
268        Self(addr.as_u64())
269    }
270}
271#[cfg(feature = "conv-x86")]
272impl From<&x86_VAddr> for VirtAddr {
273    fn from(addr: &x86_VAddr) -> Self {
274        Self(addr.as_u64())
275    }
276}
277
278#[cfg(feature = "conv-x86")]
279impl From<VirtAddr> for x86_VAddr {
280    fn from(addr: VirtAddr) -> x86_VAddr {
281        x86_VAddr(addr.0)
282    }
283}
284
285#[cfg(feature = "conv-x86")]
286impl From<&VirtAddr> for x86_VAddr {
287    fn from(addr: &VirtAddr) -> x86_VAddr {
288        x86_VAddr(addr.0)
289    }
290}
291
292/// A passed `u64` was not a valid physical address.
293///
294/// This means that bits 52 to 64 were not all null.
295///
296/// Contains the invalid address.
297pub struct PhysAddrNotValid(pub u64);
298
299impl core::fmt::Debug for PhysAddrNotValid {
300    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301        f.debug_tuple("PhysAddrNotValid")
302            .field(&format_args!("{:#x}", self.0))
303            .finish()
304    }
305}
306
307impl PhysAddr {
308    /// Creates a new physical address.
309    ///
310    /// ## Panics
311    ///
312    /// This function panics if a bit in the range 52 to 64 is set.
313    #[inline]
314    pub const fn new(addr: u64) -> Self {
315        // TODO: Replace with .ok().expect(msg) when that works on stable.
316        match Self::try_new(addr) {
317            Ok(p) => p,
318            Err(_) => panic!("physical addresses must not have any bits in the range 52 to 64 set"),
319        }
320    }
321
322    /// Creates a new physical address, throwing bits 52..64 away.
323    #[inline]
324    pub const fn new_truncate(addr: u64) -> PhysAddr {
325        PhysAddr(addr % (1 << 52))
326    }
327
328    /// Tries to create a new physical address.
329    ///
330    /// Fails if any bits in the range 52 to 64 are set.
331    #[inline]
332    pub const fn try_new(addr: u64) -> Result<Self, PhysAddrNotValid> {
333        let p = Self::new_truncate(addr);
334        if p.0 == addr {
335            Ok(p)
336        } else {
337            Err(PhysAddrNotValid(addr))
338        }
339    }
340
341    #[cfg(target_pointer_width = "64")]
342    // if the target_pointer_width is 64, usize = u64 so we can safely transform.
343    pub const fn as_usize(&self) -> usize {
344        self.0 as usize
345    }
346}
347
348impl Align<u64> for PhysAddr {
349    #[inline]
350    fn align_down(self, align: u64) -> Self {
351        Self::new(self.as_u64().align_down(align))
352    }
353
354    #[inline]
355    fn checked_align_up(self, align: u64) -> Option<Self> {
356        let addr = self.0.checked_align_up(align)?;
357        let this = Self::try_new(addr).ok()?;
358        Some(this)
359    }
360}
361
362#[cfg(target_pointer_width = "64")]
363// if the target_pointer_width is 64, usize = u64 so we can safely transform.
364impl From<usize> for PhysAddr {
365    fn from(addr: usize) -> PhysAddr {
366        Self::new_truncate(addr as u64)
367    }
368}
369
370#[cfg(target_pointer_width = "64")]
371// if the target_pointer_width is 64, usize = u64 so we can safely add
372impl core::ops::Add<usize> for PhysAddr {
373    type Output = Self;
374    #[inline]
375    fn add(self, rhs: usize) -> Self::Output {
376        PhysAddr::new(self.0 + rhs as u64)
377    }
378}
379
380#[cfg(target_pointer_width = "64")]
381// if the target_pointer_width is 64, usize = u64 so we can safely add
382impl core::ops::AddAssign<usize> for PhysAddr {
383    #[inline]
384    fn add_assign(&mut self, rhs: usize) {
385        *self = *self + rhs;
386    }
387}
388
389#[cfg(target_pointer_width = "64")]
390// if the target_pointer_width is 64, usize = u64 so we can safely sub
391impl core::ops::Sub<usize> for PhysAddr {
392    type Output = Self;
393    #[inline]
394    fn sub(self, rhs: usize) -> Self::Output {
395        PhysAddr::new(self.0.checked_sub(rhs as u64).unwrap())
396    }
397}
398
399#[cfg(target_pointer_width = "64")]
400// if the target_pointer_width is 64, usize = u64 so we can safely sub
401impl core::ops::SubAssign<usize> for PhysAddr {
402    #[inline]
403    fn sub_assign(&mut self, rhs: usize) {
404        *self = *self - rhs;
405    }
406}
407
408#[cfg(feature = "conv-x86_64")]
409impl From<x86_64::PhysAddr> for PhysAddr {
410    fn from(addr: x86_64::PhysAddr) -> Self {
411        Self(addr.as_u64())
412    }
413}
414#[cfg(feature = "conv-x86_64")]
415impl From<&x86_64::PhysAddr> for PhysAddr {
416    fn from(addr: &x86_64::PhysAddr) -> Self {
417        Self(addr.as_u64())
418    }
419}
420
421#[cfg(feature = "conv-x86_64")]
422impl From<PhysAddr> for x86_64::PhysAddr {
423    fn from(addr: PhysAddr) -> x86_64::PhysAddr {
424        x86_64::PhysAddr::new(addr.0)
425    }
426}
427
428#[cfg(feature = "conv-x86_64")]
429impl From<&PhysAddr> for x86_64::PhysAddr {
430    fn from(addr: &PhysAddr) -> x86_64::PhysAddr {
431        x86_64::PhysAddr::new(addr.0)
432    }
433}
434
435#[cfg(feature = "conv-x86")]
436impl From<x86_PAddr> for PhysAddr {
437    fn from(addr: x86_PAddr) -> Self {
438        Self(addr.as_u64())
439    }
440}
441#[cfg(feature = "conv-x86")]
442impl From<&x86_PAddr> for PhysAddr {
443    fn from(addr: &x86_PAddr) -> Self {
444        Self(addr.as_u64())
445    }
446}
447
448#[cfg(feature = "conv-x86")]
449impl From<PhysAddr> for x86_PAddr {
450    fn from(addr: PhysAddr) -> x86_PAddr {
451        x86_PAddr(addr.0)
452    }
453}
454
455#[cfg(feature = "conv-x86")]
456impl From<&PhysAddr> for x86_PAddr {
457    fn from(addr: &PhysAddr) -> x86_PAddr {
458        x86_PAddr(addr.0)
459    }
460}
461
462#[cfg(test)]
463mod tests {
464    use super::*;
465
466    #[test]
467    pub fn virtaddr_new_truncate() {
468        assert_eq!(VirtAddr::new_truncate(0), VirtAddr(0));
469        assert_eq!(VirtAddr::new_truncate(1 << 47), VirtAddr(0xfffff << 47));
470        assert_eq!(VirtAddr::new_truncate(123), VirtAddr(123));
471        assert_eq!(VirtAddr::new_truncate(123 << 47), VirtAddr(0xfffff << 47));
472    }
473
474    #[test]
475    fn test_virt_addr_align_up() {
476        // Make sure the 47th bit is extended.
477        assert_eq!(
478            VirtAddr::new(0x7fff_ffff_ffff).align_up(2u64),
479            VirtAddr::new(0xffff_8000_0000_0000)
480        );
481        assert_eq!(
482            VirtAddr::new(0xffff_ffff_ffff_ffff).checked_align_up(2u64),
483            None
484        );
485    }
486
487    #[test]
488    fn test_virt_addr_align_down() {
489        // Make sure the 47th bit is extended.
490        assert_eq!(
491            VirtAddr::new(0xffff_8000_0000_0000).align_down(1u64 << 48),
492            VirtAddr::new(0)
493        );
494    }
495
496    #[test]
497    fn test_phys_addr_align_up() {
498        assert_eq!(
499            PhysAddr::new(0x000f_ffff_ffff_ffff).checked_align_up(2u64),
500            None
501        );
502    }
503
504    #[test]
505    fn test_from_ptr_array() {
506        let slice = &[1, 2, 3, 4, 5];
507        // Make sure that from_ptr(slice) is the address of the first element
508        assert_eq!(VirtAddr::from_ptr(slice), VirtAddr::from_ptr(&slice[0]));
509    }
510}