Skip to main content

ruvix_physmem/
addr.rs

1//! Physical address types.
2//!
3//! This module provides the `PhysAddr` newtype wrapper for physical addresses,
4//! ensuring type safety when working with physical memory.
5
6use core::fmt;
7use core::ops::{Add, AddAssign, Sub, SubAssign};
8
9use crate::{align_down, align_up, is_page_aligned, PAGE_SIZE};
10
11/// A physical memory address.
12///
13/// This is a newtype wrapper around `u64` that provides type safety and
14/// utility methods for working with physical addresses.
15///
16/// # Invariants
17///
18/// Physical addresses should typically be page-aligned when used for
19/// allocation. The allocator enforces this constraint.
20///
21/// # Examples
22///
23/// ```rust
24/// use ruvix_physmem::PhysAddr;
25///
26/// let addr = PhysAddr::new(0x1000_0000);
27/// assert_eq!(addr.as_u64(), 0x1000_0000);
28/// assert!(addr.is_page_aligned());
29///
30/// let next_page = addr.add_pages(1);
31/// assert_eq!(next_page.as_u64(), 0x1000_1000);
32/// ```
33#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
34#[repr(transparent)]
35pub struct PhysAddr(u64);
36
37impl PhysAddr {
38    /// The null physical address (0x0).
39    pub const NULL: Self = Self(0);
40
41    /// Creates a new physical address.
42    ///
43    /// # Arguments
44    ///
45    /// * `addr` - The raw physical address value.
46    ///
47    /// # Examples
48    ///
49    /// ```rust
50    /// use ruvix_physmem::PhysAddr;
51    ///
52    /// let addr = PhysAddr::new(0x1000);
53    /// assert_eq!(addr.as_u64(), 0x1000);
54    /// ```
55    #[inline]
56    #[must_use]
57    pub const fn new(addr: u64) -> Self {
58        Self(addr)
59    }
60
61    /// Creates a new physical address from a page frame number.
62    ///
63    /// The page frame number is the physical address divided by `PAGE_SIZE`.
64    ///
65    /// # Arguments
66    ///
67    /// * `pfn` - The page frame number.
68    ///
69    /// # Examples
70    ///
71    /// ```rust
72    /// use ruvix_physmem::PhysAddr;
73    ///
74    /// let addr = PhysAddr::from_pfn(1);
75    /// assert_eq!(addr.as_u64(), 0x1000); // Page 1 = 4096
76    ///
77    /// let addr = PhysAddr::from_pfn(256);
78    /// assert_eq!(addr.as_u64(), 0x10_0000); // 256 * 4096 = 1MB
79    /// ```
80    #[inline]
81    #[must_use]
82    pub const fn from_pfn(pfn: u64) -> Self {
83        Self(pfn * PAGE_SIZE as u64)
84    }
85
86    /// Returns the raw physical address value.
87    ///
88    /// # Examples
89    ///
90    /// ```rust
91    /// use ruvix_physmem::PhysAddr;
92    ///
93    /// let addr = PhysAddr::new(0x1234_5000);
94    /// assert_eq!(addr.as_u64(), 0x1234_5000);
95    /// ```
96    #[inline]
97    #[must_use]
98    pub const fn as_u64(self) -> u64 {
99        self.0
100    }
101
102    /// Returns the page frame number for this address.
103    ///
104    /// This is the physical address divided by `PAGE_SIZE`, truncating any
105    /// offset within the page.
106    ///
107    /// # Examples
108    ///
109    /// ```rust
110    /// use ruvix_physmem::PhysAddr;
111    ///
112    /// let addr = PhysAddr::new(0x1000);
113    /// assert_eq!(addr.pfn(), 1);
114    ///
115    /// let addr = PhysAddr::new(0x1234);
116    /// assert_eq!(addr.pfn(), 1); // Offset 0x234 is truncated
117    /// ```
118    #[inline]
119    #[must_use]
120    pub const fn pfn(self) -> u64 {
121        self.0 / PAGE_SIZE as u64
122    }
123
124    /// Returns the offset within the page.
125    ///
126    /// # Examples
127    ///
128    /// ```rust
129    /// use ruvix_physmem::PhysAddr;
130    ///
131    /// let addr = PhysAddr::new(0x1234);
132    /// assert_eq!(addr.page_offset(), 0x234);
133    ///
134    /// let addr = PhysAddr::new(0x1000);
135    /// assert_eq!(addr.page_offset(), 0);
136    /// ```
137    #[inline]
138    #[must_use]
139    pub const fn page_offset(self) -> u64 {
140        self.0 & (PAGE_SIZE as u64 - 1)
141    }
142
143    /// Checks if the address is page-aligned.
144    ///
145    /// # Examples
146    ///
147    /// ```rust
148    /// use ruvix_physmem::PhysAddr;
149    ///
150    /// assert!(PhysAddr::new(0).is_page_aligned());
151    /// assert!(PhysAddr::new(0x1000).is_page_aligned());
152    /// assert!(!PhysAddr::new(0x1001).is_page_aligned());
153    /// ```
154    #[inline]
155    #[must_use]
156    pub const fn is_page_aligned(self) -> bool {
157        is_page_aligned(self.0)
158    }
159
160    /// Checks if the address is null (zero).
161    ///
162    /// # Examples
163    ///
164    /// ```rust
165    /// use ruvix_physmem::PhysAddr;
166    ///
167    /// assert!(PhysAddr::NULL.is_null());
168    /// assert!(PhysAddr::new(0).is_null());
169    /// assert!(!PhysAddr::new(0x1000).is_null());
170    /// ```
171    #[inline]
172    #[must_use]
173    pub const fn is_null(self) -> bool {
174        self.0 == 0
175    }
176
177    /// Aligns the address down to the nearest page boundary.
178    ///
179    /// # Examples
180    ///
181    /// ```rust
182    /// use ruvix_physmem::PhysAddr;
183    ///
184    /// let addr = PhysAddr::new(0x1234);
185    /// assert_eq!(addr.align_down().as_u64(), 0x1000);
186    ///
187    /// let addr = PhysAddr::new(0x2000);
188    /// assert_eq!(addr.align_down().as_u64(), 0x2000);
189    /// ```
190    #[inline]
191    #[must_use]
192    pub const fn align_down(self) -> Self {
193        Self(align_down(self.0))
194    }
195
196    /// Aligns the address up to the nearest page boundary.
197    ///
198    /// # Examples
199    ///
200    /// ```rust
201    /// use ruvix_physmem::PhysAddr;
202    ///
203    /// let addr = PhysAddr::new(0x1001);
204    /// assert_eq!(addr.align_up().as_u64(), 0x2000);
205    ///
206    /// let addr = PhysAddr::new(0x2000);
207    /// assert_eq!(addr.align_up().as_u64(), 0x2000);
208    /// ```
209    #[inline]
210    #[must_use]
211    pub const fn align_up(self) -> Self {
212        Self(align_up(self.0))
213    }
214
215    /// Adds a number of pages to the address.
216    ///
217    /// # Arguments
218    ///
219    /// * `pages` - The number of pages to add.
220    ///
221    /// # Examples
222    ///
223    /// ```rust
224    /// use ruvix_physmem::PhysAddr;
225    ///
226    /// let addr = PhysAddr::new(0x1000);
227    /// assert_eq!(addr.add_pages(1).as_u64(), 0x2000);
228    /// assert_eq!(addr.add_pages(4).as_u64(), 0x5000);
229    /// ```
230    #[inline]
231    #[must_use]
232    pub const fn add_pages(self, pages: usize) -> Self {
233        Self(self.0 + (pages as u64 * PAGE_SIZE as u64))
234    }
235
236    /// Subtracts a number of pages from the address.
237    ///
238    /// # Arguments
239    ///
240    /// * `pages` - The number of pages to subtract.
241    ///
242    /// # Examples
243    ///
244    /// ```rust
245    /// use ruvix_physmem::PhysAddr;
246    ///
247    /// let addr = PhysAddr::new(0x5000);
248    /// assert_eq!(addr.sub_pages(1).as_u64(), 0x4000);
249    /// assert_eq!(addr.sub_pages(4).as_u64(), 0x1000);
250    /// ```
251    #[inline]
252    #[must_use]
253    pub const fn sub_pages(self, pages: usize) -> Self {
254        Self(self.0 - (pages as u64 * PAGE_SIZE as u64))
255    }
256
257    /// Adds a byte offset to the address.
258    ///
259    /// # Arguments
260    ///
261    /// * `offset` - The byte offset to add.
262    ///
263    /// # Examples
264    ///
265    /// ```rust
266    /// use ruvix_physmem::PhysAddr;
267    ///
268    /// let addr = PhysAddr::new(0x1000);
269    /// assert_eq!(addr.add_bytes(0x100).as_u64(), 0x1100);
270    /// ```
271    #[inline]
272    #[must_use]
273    pub const fn add_bytes(self, offset: u64) -> Self {
274        Self(self.0 + offset)
275    }
276
277    /// Calculates the number of pages between two addresses.
278    ///
279    /// Returns the number of complete pages between `self` and `other`.
280    /// Both addresses should be page-aligned for meaningful results.
281    ///
282    /// # Arguments
283    ///
284    /// * `other` - The other address to compare with.
285    ///
286    /// # Examples
287    ///
288    /// ```rust
289    /// use ruvix_physmem::PhysAddr;
290    ///
291    /// let start = PhysAddr::new(0x1000);
292    /// let end = PhysAddr::new(0x5000);
293    /// assert_eq!(start.pages_to(end), 4);
294    /// ```
295    #[inline]
296    #[must_use]
297    pub const fn pages_to(self, other: Self) -> usize {
298        if other.0 >= self.0 {
299            ((other.0 - self.0) / PAGE_SIZE as u64) as usize
300        } else {
301            0
302        }
303    }
304
305    /// Checks if this address is within a range.
306    ///
307    /// # Arguments
308    ///
309    /// * `start` - The start of the range (inclusive).
310    /// * `end` - The end of the range (exclusive).
311    ///
312    /// # Examples
313    ///
314    /// ```rust
315    /// use ruvix_physmem::PhysAddr;
316    ///
317    /// let start = PhysAddr::new(0x1000);
318    /// let end = PhysAddr::new(0x5000);
319    ///
320    /// assert!(PhysAddr::new(0x1000).is_in_range(start, end));
321    /// assert!(PhysAddr::new(0x3000).is_in_range(start, end));
322    /// assert!(!PhysAddr::new(0x5000).is_in_range(start, end));
323    /// assert!(!PhysAddr::new(0x0000).is_in_range(start, end));
324    /// ```
325    #[inline]
326    #[must_use]
327    pub const fn is_in_range(self, start: Self, end: Self) -> bool {
328        self.0 >= start.0 && self.0 < end.0
329    }
330
331    /// Checks if the address is aligned to a power-of-two order.
332    ///
333    /// An address is order-aligned if it is aligned to `2^order * PAGE_SIZE`.
334    ///
335    /// # Arguments
336    ///
337    /// * `order` - The order to check alignment for.
338    ///
339    /// # Examples
340    ///
341    /// ```rust
342    /// use ruvix_physmem::PhysAddr;
343    ///
344    /// let addr = PhysAddr::new(0x4000); // 16KB
345    /// assert!(addr.is_order_aligned(0)); // Aligned to 4KB
346    /// assert!(addr.is_order_aligned(1)); // Aligned to 8KB
347    /// assert!(addr.is_order_aligned(2)); // Aligned to 16KB
348    /// assert!(!addr.is_order_aligned(3)); // Not aligned to 32KB
349    /// ```
350    #[inline]
351    #[must_use]
352    pub const fn is_order_aligned(self, order: usize) -> bool {
353        let alignment = (PAGE_SIZE as u64) << order;
354        self.0 & (alignment - 1) == 0
355    }
356}
357
358impl Add<u64> for PhysAddr {
359    type Output = Self;
360
361    #[inline]
362    fn add(self, rhs: u64) -> Self::Output {
363        Self(self.0 + rhs)
364    }
365}
366
367impl AddAssign<u64> for PhysAddr {
368    #[inline]
369    fn add_assign(&mut self, rhs: u64) {
370        self.0 += rhs;
371    }
372}
373
374impl Sub<u64> for PhysAddr {
375    type Output = Self;
376
377    #[inline]
378    fn sub(self, rhs: u64) -> Self::Output {
379        Self(self.0 - rhs)
380    }
381}
382
383impl SubAssign<u64> for PhysAddr {
384    #[inline]
385    fn sub_assign(&mut self, rhs: u64) {
386        self.0 -= rhs;
387    }
388}
389
390impl Sub<PhysAddr> for PhysAddr {
391    type Output = u64;
392
393    #[inline]
394    fn sub(self, rhs: PhysAddr) -> Self::Output {
395        self.0.saturating_sub(rhs.0)
396    }
397}
398
399impl fmt::Debug for PhysAddr {
400    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401        write!(f, "PhysAddr({:#x})", self.0)
402    }
403}
404
405impl fmt::Display for PhysAddr {
406    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407        write!(f, "{:#x}", self.0)
408    }
409}
410
411impl fmt::LowerHex for PhysAddr {
412    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413        fmt::LowerHex::fmt(&self.0, f)
414    }
415}
416
417impl fmt::UpperHex for PhysAddr {
418    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
419        fmt::UpperHex::fmt(&self.0, f)
420    }
421}
422
423impl From<u64> for PhysAddr {
424    #[inline]
425    fn from(addr: u64) -> Self {
426        Self::new(addr)
427    }
428}
429
430impl From<PhysAddr> for u64 {
431    #[inline]
432    fn from(addr: PhysAddr) -> Self {
433        addr.0
434    }
435}
436
437#[cfg(test)]
438mod tests {
439    extern crate alloc;
440    use alloc::format;
441    use super::*;
442
443    #[test]
444    fn test_new_and_as_u64() {
445        let addr = PhysAddr::new(0x1234_5678);
446        assert_eq!(addr.as_u64(), 0x1234_5678);
447    }
448
449    #[test]
450    fn test_from_pfn() {
451        assert_eq!(PhysAddr::from_pfn(0).as_u64(), 0);
452        assert_eq!(PhysAddr::from_pfn(1).as_u64(), 0x1000);
453        assert_eq!(PhysAddr::from_pfn(256).as_u64(), 0x10_0000);
454    }
455
456    #[test]
457    fn test_pfn() {
458        assert_eq!(PhysAddr::new(0).pfn(), 0);
459        assert_eq!(PhysAddr::new(0x1000).pfn(), 1);
460        assert_eq!(PhysAddr::new(0x1FFF).pfn(), 1);
461        assert_eq!(PhysAddr::new(0x2000).pfn(), 2);
462    }
463
464    #[test]
465    fn test_page_offset() {
466        assert_eq!(PhysAddr::new(0x1000).page_offset(), 0);
467        assert_eq!(PhysAddr::new(0x1001).page_offset(), 1);
468        assert_eq!(PhysAddr::new(0x1FFF).page_offset(), 0xFFF);
469    }
470
471    #[test]
472    fn test_is_page_aligned() {
473        assert!(PhysAddr::new(0).is_page_aligned());
474        assert!(PhysAddr::new(0x1000).is_page_aligned());
475        assert!(!PhysAddr::new(0x1001).is_page_aligned());
476        assert!(!PhysAddr::new(0xFFF).is_page_aligned());
477    }
478
479    #[test]
480    fn test_is_null() {
481        assert!(PhysAddr::NULL.is_null());
482        assert!(PhysAddr::new(0).is_null());
483        assert!(!PhysAddr::new(1).is_null());
484    }
485
486    #[test]
487    fn test_align_down() {
488        assert_eq!(PhysAddr::new(0).align_down().as_u64(), 0);
489        assert_eq!(PhysAddr::new(0x1000).align_down().as_u64(), 0x1000);
490        assert_eq!(PhysAddr::new(0x1001).align_down().as_u64(), 0x1000);
491        assert_eq!(PhysAddr::new(0x1FFF).align_down().as_u64(), 0x1000);
492    }
493
494    #[test]
495    fn test_align_up() {
496        assert_eq!(PhysAddr::new(0).align_up().as_u64(), 0);
497        assert_eq!(PhysAddr::new(0x1000).align_up().as_u64(), 0x1000);
498        assert_eq!(PhysAddr::new(0x1001).align_up().as_u64(), 0x2000);
499        assert_eq!(PhysAddr::new(0x1FFF).align_up().as_u64(), 0x2000);
500    }
501
502    #[test]
503    fn test_add_sub_pages() {
504        let addr = PhysAddr::new(0x1000);
505        assert_eq!(addr.add_pages(1).as_u64(), 0x2000);
506        assert_eq!(addr.add_pages(4).as_u64(), 0x5000);
507        assert_eq!(addr.add_pages(0).as_u64(), 0x1000);
508
509        let addr = PhysAddr::new(0x5000);
510        assert_eq!(addr.sub_pages(1).as_u64(), 0x4000);
511        assert_eq!(addr.sub_pages(4).as_u64(), 0x1000);
512    }
513
514    #[test]
515    fn test_pages_to() {
516        let start = PhysAddr::new(0x1000);
517        let end = PhysAddr::new(0x5000);
518        assert_eq!(start.pages_to(end), 4);
519        assert_eq!(end.pages_to(start), 0);
520        assert_eq!(start.pages_to(start), 0);
521    }
522
523    #[test]
524    fn test_is_in_range() {
525        let start = PhysAddr::new(0x1000);
526        let end = PhysAddr::new(0x5000);
527
528        assert!(PhysAddr::new(0x1000).is_in_range(start, end));
529        assert!(PhysAddr::new(0x2000).is_in_range(start, end));
530        assert!(PhysAddr::new(0x4FFF).is_in_range(start, end));
531        assert!(!PhysAddr::new(0x5000).is_in_range(start, end));
532        assert!(!PhysAddr::new(0x0FFF).is_in_range(start, end));
533    }
534
535    #[test]
536    fn test_is_order_aligned() {
537        // 0x4000 = 16KB = 4 pages
538        let addr = PhysAddr::new(0x4000);
539        assert!(addr.is_order_aligned(0)); // 4KB
540        assert!(addr.is_order_aligned(1)); // 8KB
541        assert!(addr.is_order_aligned(2)); // 16KB
542        assert!(!addr.is_order_aligned(3)); // 32KB
543
544        // 0x8000 = 32KB = 8 pages
545        let addr = PhysAddr::new(0x8000);
546        assert!(addr.is_order_aligned(0));
547        assert!(addr.is_order_aligned(1));
548        assert!(addr.is_order_aligned(2));
549        assert!(addr.is_order_aligned(3));
550        assert!(!addr.is_order_aligned(4)); // 64KB
551    }
552
553    #[test]
554    fn test_arithmetic_ops() {
555        let mut addr = PhysAddr::new(0x1000);
556        assert_eq!((addr + 0x100).as_u64(), 0x1100);
557        assert_eq!((addr - 0x100).as_u64(), 0x0F00);
558
559        addr += 0x1000;
560        assert_eq!(addr.as_u64(), 0x2000);
561
562        addr -= 0x500;
563        assert_eq!(addr.as_u64(), 0x1B00);
564    }
565
566    #[test]
567    fn test_addr_subtraction() {
568        let a = PhysAddr::new(0x5000);
569        let b = PhysAddr::new(0x1000);
570        assert_eq!(a - b, 0x4000);
571        assert_eq!(b - a, 0); // saturating
572    }
573
574    #[test]
575    fn test_display() {
576        let addr = PhysAddr::new(0x1234_5000);
577        assert_eq!(format!("{addr}"), "0x12345000");
578        assert_eq!(format!("{addr:?}"), "PhysAddr(0x12345000)");
579        assert_eq!(format!("{addr:x}"), "12345000");
580        assert_eq!(format!("{addr:X}"), "12345000");
581    }
582
583    #[test]
584    fn test_from_into() {
585        let addr: PhysAddr = 0x1234u64.into();
586        assert_eq!(addr.as_u64(), 0x1234);
587
588        let val: u64 = addr.into();
589        assert_eq!(val, 0x1234);
590    }
591
592    #[test]
593    fn test_ordering() {
594        let a = PhysAddr::new(0x1000);
595        let b = PhysAddr::new(0x2000);
596        let c = PhysAddr::new(0x1000);
597
598        assert!(a < b);
599        assert!(b > a);
600        assert!(a == c);
601        assert!(a <= c);
602        assert!(a >= c);
603    }
604}