* [PATCH] rust: kernel: Introduce newtype for `PhysAddr`
@ 2025-11-22 5:52 Gent Binaku
0 siblings, 0 replies; only message in thread
From: Gent Binaku @ 2025-11-22 5:52 UTC (permalink / raw)
To: rust-for-linux; +Cc: ojeda, aliceryhl, wedsonaf, Gent Binaku
This commit introduces a newtype for the `PhysAddr` type alias, replacing
the previous to constrain the operations allowed on physical addresses
and prevent mistakes. This change aligns the usage of `PhysAddr` with the
goals of safety and clarity in the Rust-for-Linux code.
Updates include:
- Definition of the `PhysAddr` newtype in `rust/kernel/phys_addr.rs`
- Adjustments to relevant files (`devres.rs`, `io.rs`, `io/mem.rs`,
`io/resource.rs`, `lib.rs`) to adopt the newtype.
- Addition of tests and examples to ensure coverage of the newtype and its
methods.
This work builds upon the discussed patches linked at:
https://lore.kernel.org/rust-for-linux/CANiq72maV_j1uV=2nPGbTgRabnk8cpc7TNN_FQ+ou52OpZ=k6Q@mail.gmail.com/
Signed-off-by: Gent Binaku <binakugent@gmail.com>
Suggested-by: Miguel Ojeda <ojeda@kernel.org>
Link: https://github.com/Rust-for-Linux/linux/issues/1204
Signed-off-by: Gent Binaku <binakugent@gmail.com>
---
rust/kernel/devres.rs | 5 +-
rust/kernel/io.rs | 11 +-
rust/kernel/io/mem.rs | 4 +-
rust/kernel/io/resource.rs | 8 +-
rust/kernel/lib.rs | 1 +
rust/kernel/phys_addr.rs | 325 +++++++++++++++++++++++++++++++++++++
6 files changed, 338 insertions(+), 16 deletions(-)
create mode 100644 rust/kernel/phys_addr.rs
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index e01e0d36702d..5047db37de19 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -62,8 +62,8 @@ struct Inner<T: Send> {
/// io::{
/// Io,
/// IoRaw,
-/// PhysAddr,
/// },
+/// phys_addr::PhysAddr
/// };
/// use core::ops::Deref;
///
@@ -78,7 +78,8 @@ struct Inner<T: Send> {
/// unsafe fn new(paddr: usize) -> Result<Self>{
/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is
/// // valid for `ioremap`.
-/// let addr = unsafe { bindings::ioremap(paddr as PhysAddr, SIZE) };
+/// let physAddr = PhysAddr::from_raw(paddr as bindings::phys_addr_t);
+/// let addr = unsafe { bindings::ioremap( physAddr.as_raw(), SIZE) };
/// if addr.is_null() {
/// return Err(ENOMEM);
/// }
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index 56a435eb14e3..3ff19c5aa62e 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -13,12 +13,6 @@
pub use resource::Resource;
-/// Physical address type.
-///
-/// This is a type alias to either `u32` or `u64` depending on the config option
-/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures.
-pub type PhysAddr = bindings::phys_addr_t;
-
/// Resource Size type.
///
/// This is a type alias to either `u32` or `u64` depending on the config option
@@ -80,8 +74,8 @@ pub fn maxsize(&self) -> usize {
/// io::{
/// Io,
/// IoRaw,
-/// PhysAddr,
/// },
+/// phys_addr::PhysAddr
/// };
/// use core::ops::Deref;
///
@@ -96,7 +90,8 @@ pub fn maxsize(&self) -> usize {
/// unsafe fn new(paddr: usize) -> Result<Self>{
/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is
/// // valid for `ioremap`.
-/// let addr = unsafe { bindings::ioremap(paddr as PhysAddr, SIZE) };
+/// let physAddr = PhysAddr::from_raw(paddr as bindings::phys_addr_t);
+/// let addr = unsafe { bindings::ioremap( physAddr.as_raw(), SIZE) };
/// if addr.is_null() {
/// return Err(ENOMEM);
/// }
diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs
index 6f99510bfc3a..8bce3fdfde9f 100644
--- a/rust/kernel/io/mem.rs
+++ b/rust/kernel/io/mem.rs
@@ -235,12 +235,12 @@ fn ioremap(resource: &Resource) -> Result<Self> {
// SAFETY:
// - `res_start` and `size` are read from a presumably valid `struct resource`.
// - `size` is known not to be zero at this point.
- unsafe { bindings::ioremap_np(res_start, size) }
+ unsafe { bindings::ioremap_np(res_start.into(), size) }
} else {
// SAFETY:
// - `res_start` and `size` are read from a presumably valid `struct resource`.
// - `size` is known not to be zero at this point.
- unsafe { bindings::ioremap(res_start, size) }
+ unsafe { bindings::ioremap(res_start.into(), size) }
};
if addr.is_null() {
diff --git a/rust/kernel/io/resource.rs b/rust/kernel/io/resource.rs
index 0e86ee9c98d8..1e358fb1c712 100644
--- a/rust/kernel/io/resource.rs
+++ b/rust/kernel/io/resource.rs
@@ -8,12 +8,12 @@
use core::ops::Deref;
use core::ptr::NonNull;
+use crate::phys_addr::PhysAddr;
use crate::prelude::*;
use crate::str::{CStr, CString};
use crate::types::Opaque;
pub use super::{
- PhysAddr,
ResourceSize, //
};
@@ -54,7 +54,7 @@ fn drop(&mut self) {
};
// SAFETY: Safe as per the invariant of `Region`.
- unsafe { release_fn(start, size) };
+ unsafe { release_fn(start.into(), size) };
}
}
@@ -109,7 +109,7 @@ pub fn request_region(
let region = unsafe {
bindings::__request_region(
self.0.get(),
- start,
+ start.into(),
size,
name.as_char_ptr(),
flags.0 as c_int,
@@ -133,7 +133,7 @@ pub fn size(&self) -> ResourceSize {
pub fn start(&self) -> PhysAddr {
let inner = self.0.get();
// SAFETY: Safe as per the invariants of `Resource`.
- unsafe { (*inner).start }
+ unsafe { PhysAddr::from((*inner).start) }
}
/// Returns the name of the resource.
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 235d0d8b1eff..81293b7b80c4 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -116,6 +116,7 @@
pub mod page;
#[cfg(CONFIG_PCI)]
pub mod pci;
+pub mod phys_addr;
pub mod pid_namespace;
pub mod platform;
pub mod prelude;
diff --git a/rust/kernel/phys_addr.rs b/rust/kernel/phys_addr.rs
new file mode 100644
index 000000000000..41b635e5d44e
--- /dev/null
+++ b/rust/kernel/phys_addr.rs
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0
+//! A newtype for physical addresses.
+//!
+//! This module provides `PhysAddr`, a type-safe wrapper around the kernel's
+//! `phys_addr_t`. Using a newtype prevents accidentally mixing physical
+//! addresses with virtual addresses or other integer types, which is a common
+//! source of bugs.
+//!
+//! The API prioritizes explicitness and safety. Conversions to and from the
+//! raw integer type must be done via `from_raw()` and `as_raw()`. Arithmetic
+//! is provided through both explicit methods (`checked_add`, `wrapping_add`)
+//! and ergonomic operators (`+`, `-`) that have well-defined wrapping behavior.
+//!
+//! # Examples
+//!
+//! ```
+//! use kernel::bindings;
+//! use kernel::phys_addr::{PhysAddr};
+//!
+//!
+//! let addr = PhysAddr::from_raw(0x1008 as bindings::phys_addr_t);
+//!
+//! // The `+` operator uses wrapping arithmetic.
+//! assert_eq!(addr + 8, PhysAddr::from_raw(0x1010 as bindings::phys_addr_t));
+//!
+//! // For safety, checked arithmetic is also available.
+//! let new_addr = addr.checked_add(8).expect("Overflow occurred");
+//! assert_eq!(new_addr.as_raw(), 0x1010);
+//!
+//! // Alignment is a common task.
+//! let aligned_addr = addr.align_down(8);
+//! assert_eq!(aligned_addr.as_raw(), 0x1008);
+//! ```
+use crate::bindings;
+use core::{
+ fmt,
+ ops::{Add, Sub},
+};
+use macros::kunit_tests;
+
+/// A newtype wrapper for a physical address.
+///
+/// Its size is guaranteed to match the C kernel's `phys_addr_t` by wrapping
+/// the generated binding. `#[repr(transparent)]` ensures it has an identical
+/// memory layout, making it safe to use across FFI boundaries.
+#[repr(transparent)]
+#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct PhysAddr(bindings::phys_addr_t);
+
+impl PhysAddr {
+ /// The zero physical address. Equivalent to `PhysAddr::default()`.
+ pub const ZERO: Self = Self(0);
+
+ /// Creates a `PhysAddr` from a raw `phys_addr_t` value.
+ pub fn from_raw(addr: bindings::phys_addr_t) -> Self {
+ Self(addr)
+ }
+
+ /// Returns the raw `phys_addr_t` value of the physical address.
+ pub fn as_raw(self) -> bindings::phys_addr_t {
+ self.0
+ }
+
+ /// Returns `true` if the physical address is null (zero).
+ pub fn is_null(self) -> bool {
+ self.0 == 0
+ }
+
+ /// Checked addition. Returns `None` if overflow occurs.
+ pub fn checked_add(self, rhs: bindings::phys_addr_t) -> Option<Self> {
+ self.0.checked_add(rhs).map(Self)
+ }
+
+ /// Checked subtraction. Returns `None` if overflow occurs.
+ pub fn checked_sub(self, rhs: bindings::phys_addr_t) -> Option<Self> {
+ self.0.checked_sub(rhs).map(Self)
+ }
+
+ /// Saturating addition. Caps at `phys_addr_t::MAX` on overflow.
+ pub fn saturating_add(self, rhs: bindings::phys_addr_t) -> Self {
+ Self(self.0.saturating_add(rhs))
+ }
+
+ /// Saturating subtraction. Caps at 0 on underflow.
+ pub fn saturating_sub(self, rhs: bindings::phys_addr_t) -> Self {
+ Self(self.0.saturating_sub(rhs))
+ }
+
+ /// Wrapping addition.
+ pub fn wrapping_add(self, rhs: bindings::phys_addr_t) -> Self {
+ Self(self.0.wrapping_add(rhs))
+ }
+
+ /// Wrapping subtraction.
+ pub fn wrapping_sub(self, rhs: bindings::phys_addr_t) -> Self {
+ Self(self.0.wrapping_sub(rhs))
+ }
+
+ /// Wrapping multiplication.
+ pub fn wrapping_mul(self, rhs: bindings::phys_addr_t) -> Self {
+ Self(self.0.wrapping_mul(rhs))
+ }
+
+ /// Aligns the address down to the nearest multiple of `align`.
+ ///
+ /// `align` must be a power of two.
+ pub const fn align_down(self, align: bindings::phys_addr_t) -> Self {
+ Self(self.0 & !(align.wrapping_sub(1)))
+ }
+
+ /// Aligns the address up to the nearest multiple of `align`.
+ ///
+ /// `align` must be a power of two.
+ pub const fn align_up(self, align: bindings::phys_addr_t) -> Self {
+ self.add_const(align.wrapping_sub(1)).align_down(align)
+ }
+
+ const fn add_const(self, rhs: bindings::phys_addr_t) -> Self {
+ Self(self.0.wrapping_add(rhs))
+ }
+}
+
+impl From<bindings::phys_addr_t> for PhysAddr {
+ fn from(addr: bindings::phys_addr_t) -> Self {
+ Self::from_raw(addr)
+ }
+}
+
+impl From<PhysAddr> for bindings::phys_addr_t {
+ fn from(addr: PhysAddr) -> bindings::phys_addr_t {
+ addr.as_raw()
+ }
+}
+
+impl Add<bindings::phys_addr_t> for PhysAddr {
+ type Output = Self;
+
+ /// Adds with wrapping on overflow.
+ ///
+ /// For checked or saturating arithmetic, use [`checked_add`] or [`saturating_add`].
+ fn add(self, rhs: bindings::phys_addr_t) -> Self::Output {
+ self.wrapping_add(rhs)
+ }
+}
+
+impl Sub<bindings::phys_addr_t> for PhysAddr {
+ type Output = Self;
+
+ /// Subtracts with wrapping on underflow.
+ ///
+ /// For checked or saturating arithmetic, use [`checked_sub`] or [`saturating_sub`].
+ fn sub(self, rhs: bindings::phys_addr_t) -> Self::Output {
+ self.wrapping_sub(rhs)
+ }
+}
+
+// Find the offset between two addresses.
+impl Sub<PhysAddr> for PhysAddr {
+ type Output = bindings::phys_addr_t;
+
+ /// Calculates the offset from `rhs` to `self`.
+ ///
+ /// Performs saturating subtraction. If `rhs` is greater than `self`,
+ /// the result will be 0.
+ fn sub(self, rhs: PhysAddr) -> Self::Output {
+ self.0.saturating_sub(rhs.0)
+ }
+}
+
+// Implement standard formatting traits for addresses.
+impl fmt::Debug for PhysAddr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Use a more descriptive debug output.
+ f.debug_tuple("PhysAddr")
+ .field(&format_args!("0x{:x}", self.0))
+ .finish()
+ }
+}
+
+impl fmt::Display for PhysAddr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "0x{:x}", self.0)
+ }
+}
+
+impl fmt::LowerHex for PhysAddr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl fmt::UpperHex for PhysAddr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl fmt::Octal for PhysAddr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{:o}", self.0)
+ }
+}
+
+impl fmt::Binary for PhysAddr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{:b}", self.0)
+ }
+}
+
+impl fmt::Pointer for PhysAddr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "0x{:x}", self.0)
+ }
+}
+
+// --- KUnit Test Suite ---
+#[kunit_tests(kernel_physadrmod)]
+mod tests {
+ use super::*;
+ use crate::bindings::phys_addr_t;
+
+ #[test]
+ fn test_creation_and_conversion() {
+ let addr = PhysAddr::from_raw(0x1000);
+ assert_eq!(addr.as_raw(), 0x1000);
+
+ let default_addr = PhysAddr::default();
+ assert_eq!(default_addr.as_raw(), 0);
+ assert_eq!(default_addr, PhysAddr::ZERO);
+
+ ()
+ }
+
+ #[test]
+ fn test_is_null() {
+ assert!(PhysAddr::ZERO.is_null());
+ assert!(PhysAddr::from_raw(0).is_null());
+ assert!(!PhysAddr::from_raw(1).is_null());
+ ()
+ }
+
+ #[test]
+ fn test_arithmetic() {
+ let addr = PhysAddr::from_raw(0x1000);
+
+ // Checked addition
+ assert_eq!(addr.checked_add(0x10), Some(PhysAddr::from_raw(0x1010)));
+ let max_addr = PhysAddr::from_raw(phys_addr_t::MAX);
+ assert_eq!(max_addr.checked_add(1), None);
+
+ // Wrapping addition
+ assert_eq!(addr.wrapping_add(0x10), PhysAddr::from_raw(0x1010));
+ assert_eq!(max_addr.wrapping_add(1), PhysAddr::from_raw(0));
+ assert_eq!(max_addr + 2, PhysAddr::from_raw(1));
+
+ // Wrapping subtraction
+ assert_eq!(addr.wrapping_sub(0x10), PhysAddr::from_raw(0x0ff0));
+ let zero_addr = PhysAddr::from_raw(0);
+ assert_eq!(
+ zero_addr.wrapping_sub(1),
+ PhysAddr::from_raw(phys_addr_t::MAX)
+ );
+ assert_eq!(zero_addr - 2, PhysAddr::from_raw(phys_addr_t::MAX - 1));
+
+ ()
+ }
+
+ #[test]
+ fn test_address_difference() {
+ let addr1 = PhysAddr::from_raw(0x1000);
+ let addr2 = PhysAddr::from_raw(0x2000);
+
+ assert_eq!(addr2 - addr1, 0x1000);
+ assert_eq!(addr1 - addr2, 0); // Saturating subtraction
+ assert_eq!(addr1 - addr1, 0);
+
+ ()
+ }
+
+ #[test]
+ fn test_alignment() {
+ let addr = PhysAddr::from_raw(0x1007);
+ let align = 8 as phys_addr_t;
+
+ // align_down
+ assert_eq!(addr.align_down(align), PhysAddr::from_raw(0x1000));
+ let aligned_addr = PhysAddr::from_raw(0x1008);
+ assert_eq!(aligned_addr.align_down(align), aligned_addr);
+
+ // align_up
+ assert_eq!(addr.align_up(align), PhysAddr::from_raw(0x1008));
+ assert_eq!(aligned_addr.align_up(align), aligned_addr);
+ assert_eq!(PhysAddr::from_raw(0).align_up(align), PhysAddr::from_raw(0));
+
+ ()
+ }
+
+ #[test]
+ fn test_saturating_arithmetic() {
+ let addr = PhysAddr::from_raw(0x1000);
+ let max_addr = PhysAddr::from_raw(phys_addr_t::MAX);
+
+ // Saturating add
+ assert_eq!(addr.saturating_add(0x10), PhysAddr::from_raw(0x1010));
+ assert_eq!(max_addr.saturating_add(1), max_addr); // Caps at MAX
+
+ // Saturating sub
+ assert_eq!(addr.saturating_sub(0x10), PhysAddr::from_raw(0x0ff0));
+ let zero = PhysAddr::from_raw(0);
+ assert_eq!(zero.saturating_sub(1), zero); // Caps at 0
+
+ ()
+ }
+
+ #[test]
+ fn test_checked_subtraction() {
+ let addr = PhysAddr::from_raw(0x1000);
+
+ assert_eq!(addr.checked_sub(0x10), Some(PhysAddr::from_raw(0x0ff0)));
+ let zero = PhysAddr::from_raw(0);
+ assert_eq!(zero.checked_sub(1), None); // Underflow
+
+ ()
+ }
+}
--
2.52.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2025-11-22 5:53 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-22 5:52 [PATCH] rust: kernel: Introduce newtype for `PhysAddr` Gent Binaku
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).