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}