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}