hal_core/
addr.rs

1use core::{fmt, ops, ptr};
2
3pub trait Address:
4    Copy
5    + ops::Add<usize, Output = Self>
6    + ops::Sub<usize, Output = Self>
7    + ops::AddAssign<usize>
8    + ops::SubAssign<usize>
9    + PartialEq
10    + Eq
11    + PartialOrd
12    + Ord
13    + fmt::Debug
14{
15    fn as_usize(self) -> usize;
16    fn from_usize(u: usize) -> Self;
17
18    /// Aligns `self` up to `align`.
19    ///
20    /// The specified alignment must be a power of two.
21    ///
22    /// # Panics
23    ///
24    /// - If `align` is not a power of two.
25    fn align_up<A: Into<usize>>(self, align: A) -> Self {
26        let align = align.into();
27        assert!(align.is_power_of_two());
28        let mask = align - 1;
29        let u = self.as_usize();
30        if u & mask == 0 {
31            return self;
32        }
33        let aligned = (u | mask) + 1;
34        Self::from_usize(aligned)
35    }
36
37    /// Align `self` up to the required alignment for a value of type `T`.
38    ///
39    /// This is equivalent to
40    /// ```rust
41    /// # use hal_core::Address;
42    /// # fn doc<T: Address>(addr: T) -> T {
43    /// addr.align_up(core::mem::align_of::<T>())
44    /// # }
45    /// ````
46    #[inline]
47    fn align_up_for<T>(self) -> Self {
48        self.align_up(core::mem::align_of::<T>())
49    }
50
51    /// Aligns `self` down to `align`.
52    ///
53    /// The specified alignment must be a power of two.
54    ///
55    /// # Panics
56    ///
57    /// - If `align` is not a power of two.
58    fn align_down<A: Into<usize>>(self, align: A) -> Self {
59        let align = align.into();
60        assert!(align.is_power_of_two());
61        let aligned = self.as_usize() & !(align - 1);
62        Self::from_usize(aligned)
63    }
64
65    /// Align `self` down to the required alignment for a value of type `T`.
66    ///
67    /// This is equivalent to
68    /// ```rust
69    /// # use hal_core::Address;
70    /// # fn doc<T: Address>(addr: T) -> T {
71    /// addr.align_down(core::mem::align_of::<T>())
72    /// # }
73    /// ````
74    #[inline]
75    fn align_down_for<T>(self) -> Self {
76        self.align_down(core::mem::align_of::<T>())
77    }
78
79    /// Offsets this address by `offset` bytes.
80    ///
81    /// If the specified offset would overflow, this function saturates instead.
82    fn offset(self, offset: isize) -> Self {
83        if offset > 0 {
84            self + offset as usize
85        } else {
86            let offset = -offset;
87            self - offset as usize
88        }
89    }
90
91    /// Returns the difference between `self` and `other`.
92    fn difference(self, other: Self) -> isize {
93        if self > other {
94            -(self.as_usize() as isize - other.as_usize() as isize)
95        } else {
96            (other.as_usize() - self.as_usize()) as isize
97        }
98    }
99
100    /// Returns `true` if `self` is aligned on the specified alignment.
101    ///
102    /// # Notes
103    /// `align` must be a power of two. This is asserted in debug builds.
104    fn is_aligned<A: Into<usize>>(self, align: A) -> bool {
105        let align = align.into();
106        debug_assert!(
107            align.is_power_of_two(),
108            "align must be a power of two (actual align: {align})",
109        );
110        self.as_usize() & (align - 1) == 0
111    }
112
113    /// Returns `true` if `self` is aligned on the alignment of the specified
114    /// type.
115    #[inline]
116    fn is_aligned_for<T>(self) -> bool {
117        self.is_aligned(core::mem::align_of::<T>())
118    }
119
120    /// Converts this address into a const pointer to a value of type `T`.
121    ///
122    /// # Panics
123    ///
124    /// - If `self` is not aligned for a `T`-typed value.
125    #[inline]
126    #[track_caller]
127    fn as_ptr<T>(self) -> *const T {
128        // Some architectures permit unaligned reads, but Rust considers
129        // dereferencing a pointer that isn't type-aligned to be UB.
130        assert!(
131            self.is_aligned_for::<T>(),
132            "assertion failed: self.is_aligned_for::<{}>();\n\tself={self:?}",
133            core::any::type_name::<T>(),
134        );
135        ptr::with_exposed_provenance(self.as_usize())
136    }
137
138    /// Converts this address into a mutable pointer to a value of type `T`.
139    ///
140    /// # Panics
141    ///
142    /// - If `self` is not aligned for a `T`-typed value.
143    #[inline]
144    #[track_caller]
145    fn as_mut_ptr<T>(self) -> *mut T {
146        // Some architectures permit unaligned reads, but Rust considers
147        // dereferencing a pointer that isn't type-aligned to be UB.
148        assert!(
149            self.is_aligned_for::<T>(),
150            "assertion failed: self.is_aligned_for::<{}>();\n\tself={self:?}",
151            core::any::type_name::<T>(),
152        );
153        ptr::with_exposed_provenance_mut(self.as_usize())
154    }
155
156    /// Converts this address into a `Option<NonNull<T>>` from a
157    /// `VAddr`, returning `None` if the address is null.
158    ///
159    /// # Panics
160    ///
161    /// - If `self` is not aligned for a `T`-typed value.
162    #[inline]
163    #[track_caller]
164    fn as_non_null<T>(self) -> Option<ptr::NonNull<T>> {
165        ptr::NonNull::new(self.as_mut_ptr::<T>())
166    }
167}
168
169#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
170#[repr(transparent)]
171pub struct PAddr(usize);
172
173#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
174#[repr(transparent)]
175pub struct VAddr(usize);
176
177#[derive(Clone, Debug, thiserror::Error)]
178#[error("invalid address {addr:#x} for target architecture: {msg}")]
179pub struct InvalidAddress {
180    msg: &'static str,
181    addr: usize,
182}
183
184macro_rules! impl_addrs {
185    ($(impl Address for $name:ty {})+) => {
186        $(
187            impl fmt::Debug for $name {
188                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189                    if let Some(width) = f.width() {
190                        write!(f, concat!(stringify!($name), "({:#0width$x})"), self.0, width = width)
191                    } else {
192                        write!(f, concat!(stringify!($name), "({:#x})"), self.0,)
193                    }
194                }
195            }
196
197            impl ops::Add<usize> for $name {
198                type Output = Self;
199                /// Offset `self` up by `rhs`.
200                ///
201                /// # Notes
202                ///
203                /// * The address will be offset by the minimum addressable unit
204                ///   of the target architecture (i.e. probably bytes), *not* by
205                ///   by units of a Rust type like `{*const T, *mut T}::add`.
206                /// * Therefore, resulting address may have a different
207                ///   alignment from the input address.
208                ///
209                /// # Panics
210                ///
211                /// * If the resulting address is invalid.
212                fn add(self, rhs: usize) -> Self {
213                    Self::from_usize(self.0 + rhs)
214                }
215            }
216
217            impl ops::Add for $name {
218                type Output = Self;
219                /// Add `rhs` **bytes** to this this address.
220                ///
221                /// Note that the resulting address may differ in alignment from
222                /// the input address!
223                fn add(self, rhs: Self) -> Self {
224                    Self::from_usize(self.0 + rhs.0)
225                }
226            }
227
228            impl ops::AddAssign for $name {
229                fn add_assign(&mut self, rhs: Self) {
230                    self.0 += rhs.0;
231                }
232            }
233
234            impl ops::AddAssign<usize> for $name {
235                fn add_assign(&mut self, rhs: usize) {
236                    self.0 += rhs;
237                }
238            }
239
240            impl ops::Sub<usize> for $name {
241                type Output = Self;
242                fn sub(self, rhs: usize) -> Self {
243                    Self::from_usize(self.0 - rhs)
244                }
245            }
246
247            impl ops::Sub for $name {
248                type Output = Self;
249                fn sub(self, rhs: Self) -> Self {
250                    Self::from_usize(self.0 - rhs.0)
251                }
252            }
253
254            impl ops::SubAssign for $name {
255                fn sub_assign(&mut self, rhs: Self) {
256                    self.0 -= rhs.0;
257                }
258            }
259
260            impl ops::SubAssign<usize> for $name {
261                fn sub_assign(&mut self, rhs: usize) {
262                    self.0 -= rhs;
263                }
264            }
265
266            impl Address for $name {
267                fn as_usize(self) -> usize {
268                    self.0 as usize
269                }
270
271                /// # Panics
272                ///
273                /// * If debug assertions are enabled and the address is not
274                ///   valid for the target architecture.
275                #[inline]
276                fn from_usize(u: usize) -> Self {
277                    if cfg!(debug_assertions) {
278                        Self::from_usize_checked(u).unwrap()
279                    } else {
280                        Self(u)
281                    }
282                }
283            }
284
285            impl $name {
286                pub const fn zero() -> Self {
287                    Self(0)
288                }
289
290                /// # Panics
291                ///
292                /// * If debug assertions are enabled and the address is not
293                ///   valid for the target architecture.
294                #[cfg(target_pointer_width = "64")]
295                pub fn from_u64(u: u64) -> Self {
296                    Self::from_usize(u as usize)
297                }
298
299                /// # Panics
300                ///
301                /// * If debug assertions are enabled and the address is not
302                ///   valid for the target architecture.
303                #[cfg(target_pointer_width = "32")]
304                pub fn from_u32(u: u32) -> Self {
305                    Self::from_usize(u as usize)
306                }
307
308                /// Aligns `self` up to `align`.
309                ///
310                /// The specified alignment must be a power of two.
311                ///
312                /// # Panics
313                ///
314                /// * If `align` is not a power of two.
315                /// * If debug assertions are enabled and the aligned address is
316                ///   not valid for the target architecture.
317                #[inline]
318                pub fn align_up<A: Into<usize>>(self, align: A) -> Self {
319                    Address::align_up(self, align)
320                }
321
322                /// Aligns `self` down to `align`.
323                ///
324                /// The specified alignment must be a power of two.
325                ///
326                /// # Panics
327                ///
328                /// * If `align` is not a power of two.
329                /// * If debug assertions are enabled and the aligned address is
330                ///   not valid for the target architecture.
331                #[inline]
332                pub fn align_down<A: Into<usize>>(self, align: A) -> Self {
333                    Address::align_down(self, align)
334                }
335
336                /// Offsets this address by `offset` bytes.
337                ///
338                /// If the specified offset would overflow, this function saturates instead.
339                #[inline]
340                pub fn offset(self, offset: isize) -> Self {
341                    Address::offset(self, offset)
342                }
343
344                /// Returns the difference between `self` and `other`.
345                #[inline]
346                pub fn difference(self, other: Self) -> isize {
347                    Address::difference(self, other)
348                }
349
350                /// Returns `true` if `self` is aligned on the specified alignment.
351                #[inline]
352                pub fn is_aligned<A: Into<usize>>(self, align: A) -> bool {
353                    Address::is_aligned(self, align)
354                }
355
356                /// Returns `true` if `self` is aligned on the alignment of the specified
357                /// type.
358                #[inline]
359                pub fn is_aligned_for<T>(self) -> bool {
360                    Address::is_aligned_for::<T>(self)
361                }
362
363                /// Converts this address into a const pointer to a value of type `T`.
364                ///
365                /// # Panics
366                ///
367                /// - If `self` is not aligned for a `T`-typed value.
368                #[inline]
369                #[track_caller]
370                pub fn as_ptr<T>(self) -> *const T {
371                    Address::as_ptr(self)
372                }
373
374                /// Converts this address into a mutable pointer to a value of type `T`.
375                ///
376                /// # Panics
377                ///
378                /// - If `self` is not aligned for a `T`-typed value.
379                #[inline]
380                #[track_caller]
381                pub fn as_mut_ptr<T>(self) -> *mut T {
382                    Address::as_mut_ptr(self)
383                }
384
385                /// Converts this address into a `Option<NonNull<T>>` from a
386                /// `VAddr`, returning `None` if the address is null.
387                ///
388                /// # Panics
389                ///
390                /// - If `self` is not aligned for a `T`-typed value.
391                #[inline]
392                #[track_caller]
393                pub fn as_non_null<T>(self) -> Option<ptr::NonNull<T>> {
394                    ptr::NonNull::new(self.as_mut_ptr::<T>())
395                }
396            }
397        )+
398    }
399}
400
401impl PAddr {
402    #[inline]
403    pub fn from_usize_checked(u: usize) -> Result<Self, InvalidAddress> {
404        #[cfg(target_arch = "x86_64")]
405        {
406            const MASK: usize = 0xFFF0_0000_0000_0000;
407            if u & MASK != 0 {
408                return Err(InvalidAddress::new(
409                    u,
410                    "x86_64 physical addresses may not have the 12 most significant bits set!",
411                ));
412            }
413        }
414
415        Ok(Self(u))
416    }
417}
418
419impl VAddr {
420    #[inline]
421    pub fn from_usize_checked(u: usize) -> Result<Self, InvalidAddress> {
422        #[cfg(target_arch = "x86_64")]
423        {
424            // sign extend 47th bit
425            let s_extend = ((u << 16) as i64 >> 16) as usize;
426            if u != s_extend {
427                return Err(InvalidAddress::new(
428                    u,
429                    "x86_64 virtual addresses must be in canonical form",
430                ));
431            }
432        }
433
434        Ok(Self(u))
435    }
436
437    /// Constructs a `VAddr` from an arbitrary `usize` value *without* checking
438    /// if it's valid.
439    ///
440    /// Pros of this function:
441    /// - can be used in const-eval contexts
442    ///
443    /// Cons of this function:
444    /// - "refer to 'Safety' section"
445    ///
446    /// # Safety
447    ///
448    /// u can use dis function to construct invalid addresses. probably dont do
449    /// that.
450    pub const unsafe fn from_usize_unchecked(u: usize) -> Self {
451        Self(u)
452    }
453
454    /// Constructs a `VAddr` from a `*const T` pointer, exposing its provenance.
455    #[inline]
456    pub fn from_ptr<T: ?Sized>(ptr: *const T) -> Self {
457        Self::from_usize(ptr.expose_provenance())
458    }
459
460    #[inline]
461    pub fn of<T: ?Sized>(pointee: &T) -> Self {
462        Self::from_ptr(pointee as *const T)
463    }
464}
465
466impl_addrs! {
467    impl Address for PAddr {}
468    impl Address for VAddr {}
469}
470
471impl InvalidAddress {
472    fn new(addr: usize, msg: &'static str) -> Self {
473        Self { msg, addr }
474    }
475}
476
477#[cfg(test)]
478mod tests {
479    use super::*;
480
481    #[test]
482    fn align_up_1_aligned() {
483        // TODO(eliza): eventually, this could be a QuickCheck test that asserts
484        // that _all_ addresses align up by 1 to themselves.
485        assert_eq!(
486            PAddr::from_usize(0x0).align_up(1usize),
487            PAddr::from_usize(0x0)
488        );
489        assert_eq!(
490            PAddr::from_usize(0xDEAD_FACE).align_up(1usize),
491            PAddr::from_usize(0xDEAD_FACE)
492        );
493        assert_eq!(
494            PAddr::from_usize(0x000F_FFFF_FFFF_FFFF).align_up(1usize),
495            PAddr::from_usize(0x000F_FFFF_FFFF_FFFF)
496        );
497    }
498
499    #[test]
500    fn align_up() {
501        assert_eq!(PAddr::from_usize(2).align_up(2usize), PAddr::from_usize(2));
502        assert_eq!(
503            PAddr::from_usize(123).align_up(2usize),
504            PAddr::from_usize(124)
505        );
506        assert_eq!(
507            PAddr::from_usize(0x5555).align_up(16usize),
508            PAddr::from_usize(0x5560)
509        );
510    }
511
512    #[cfg(target_arch = "x86_64")]
513    #[test]
514    fn x86_64_vaddr_validation() {
515        let addr = (0xFFFFF << 47) | 123;
516        assert!(
517            VAddr::from_usize_checked(addr).is_ok(),
518            "{addr:#016x} is valid",
519        );
520        let addr = 123;
521        assert!(
522            VAddr::from_usize_checked(addr).is_ok(),
523            "{addr:#016x} is valid",
524        );
525        let addr = 123 | (1 << 47);
526        assert!(
527            VAddr::from_usize_checked(addr).is_err(),
528            "{addr:#016x} is invalid",
529        );
530        let addr = (0x10101 << 47) | 123;
531        assert!(
532            VAddr::from_usize_checked(addr).is_err(),
533            "{addr:#016x} is invalid",
534        );
535    }
536
537    #[cfg(target_arch = "x86_64")]
538    #[test]
539    fn x86_64_paddr_validation() {
540        let addr = 123;
541        assert!(
542            PAddr::from_usize_checked(addr).is_ok(),
543            "{addr:#016x} is valid",
544        );
545        let addr = 0xFFF0_0000_0000_0000 | 123;
546        assert!(
547            PAddr::from_usize_checked(addr).is_err(),
548            "{addr:#016x} is invalid",
549        );
550        let addr = 0x1000_0000_0000_0000 | 123;
551        assert!(
552            PAddr::from_usize_checked(addr).is_err(),
553            "{addr:#016x} is invalid",
554        );
555        let addr = 0x0010_0000_0000_0000 | 123;
556        assert!(
557            PAddr::from_usize_checked(addr).is_err(),
558            "{addr:#016x} is invalid",
559        );
560    }
561}