[PATCH] rust: io: convert `PhysAddr` type alias to newtype

From: Albab Hasan

Date: Mon May 04 2026 - 00:58:18 EST


Makes `PhysAddr` a newtype wrapper around `bindings::phys_addr_t`.
This restricts what operations are possible with physical address
values, helping prevent mistakes such as mixing them with other
integer quantities.

Arithmetic operations are intentionally not provided; none of the
current users perform address arithmetic in Rust.

Suggested-by: Miguel Ojeda <ojeda@xxxxxxxxxx>
Link: https://github.com/Rust-for-Linux/linux/issues/1204
Signed-off-by: Albab Hasan <albabhasan276@xxxxxxxxx>
---
rust/kernel/devres.rs | 4 +--
rust/kernel/io.rs | 52 ++++++++++++++++++++++++++++++++----
rust/kernel/io/resource.rs | 6 ++---
rust/kernel/iommu/pgtable.rs | 2 +-
4 files changed, 53 insertions(+), 11 deletions(-)

diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 6afe196be42c..0de69157877b 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -72,10 +72,10 @@
/// ///
/// /// [`paddr`, `paddr` + `SIZE`) must be a valid MMIO region that is mappable into the CPUs
/// /// virtual address space.
-/// unsafe fn new(paddr: usize) -> Result<Self>{
+/// unsafe fn new(paddr: PhysAddr) -> 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 addr = unsafe { bindings::ioremap(paddr.into(), SIZE) };
/// if addr.is_null() {
/// return Err(ENOMEM);
/// }
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index e5fba6bf6db0..7c35537edf43 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -4,6 +4,8 @@
//!
//! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h)

+use core::num::TryFromIntError;
+
use crate::{
bindings,
prelude::*, //
@@ -17,9 +19,48 @@

/// Physical address type.
///
-/// This is a type alias to either `u32` or `u64` depending on the config option
+/// This is a transparent wrapper around 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;
+///
+/// # Examples
+///
+/// ```
+/// use kernel::{bindings, io::PhysAddr};
+///
+/// let raw: bindings::phys_addr_t = 0x1000;
+/// let paddr = PhysAddr::from(raw);
+/// let raw2: bindings::phys_addr_t = paddr.into();
+/// assert_eq!(raw, raw2);
+///
+/// let size = usize::try_from(paddr).unwrap();
+/// assert_eq!(size, 0x1000);
+/// ```
+#[repr(transparent)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
+pub struct PhysAddr(bindings::phys_addr_t);
+
+impl From<bindings::phys_addr_t> for PhysAddr {
+ #[inline]
+ fn from(value: bindings::phys_addr_t) -> Self {
+ Self(value)
+ }
+}
+
+impl From<PhysAddr> for bindings::phys_addr_t {
+ #[inline]
+ fn from(value: PhysAddr) -> Self {
+ value.0
+ }
+}
+
+impl TryFrom<PhysAddr> for usize {
+ type Error = TryFromIntError;
+
+ #[inline]
+ fn try_from(value: PhysAddr) -> Result<Self, Self::Error> {
+ usize::try_from(value.0)
+ }
+}

/// Resource Size type.
///
@@ -97,10 +138,10 @@ pub fn maxsize(&self) -> usize {
/// ///
/// /// [`paddr`, `paddr` + `SIZE`) must be a valid MMIO region that is mappable into the CPUs
/// /// virtual address space.
-/// unsafe fn new(paddr: usize) -> Result<Self>{
+/// unsafe fn new(paddr: PhysAddr) -> 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 addr = unsafe { bindings::ioremap(paddr.into(), SIZE) };
/// if addr.is_null() {
/// return Err(ENOMEM);
/// }
@@ -127,7 +168,8 @@ pub fn maxsize(&self) -> usize {
///
///# fn no_run() -> Result<(), Error> {
/// // SAFETY: Invalid usage for example purposes.
-/// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? };
+/// let addr = PhysAddr::from(0xBAAAAAAD as bindings::phys_addr_t);
+/// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(addr)? };
/// iomem.write32(0x42, 0x0);
/// assert!(iomem.try_write32(0x42, 0x0).is_ok());
/// assert!(iomem.try_write32(0x42, 0x4).is_err());
diff --git a/rust/kernel/io/resource.rs b/rust/kernel/io/resource.rs
index b7ac9faf141d..d703c7317980 100644
--- a/rust/kernel/io/resource.rs
+++ b/rust/kernel/io/resource.rs
@@ -58,7 +58,7 @@ fn drop(&mut self) {
};

// SAFETY: Safe as per the invariant of `Region`.
- unsafe { release_fn(start, size) };
+ unsafe { release_fn(start.into(), size) };
}
}

@@ -113,7 +113,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,
@@ -137,7 +137,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 { (*inner).start }.into()
}

/// Returns the name of the resource.
diff --git a/rust/kernel/iommu/pgtable.rs b/rust/kernel/iommu/pgtable.rs
index c88e38fd938a..1d75c09c47bb 100644
--- a/rust/kernel/iommu/pgtable.rs
+++ b/rust/kernel/iommu/pgtable.rs
@@ -182,7 +182,7 @@ pub unsafe fn map_pages(
(map_pages)(
self.raw_ops(),
iova,
- paddr,
+ paddr.into(),
pgsize,
pgcount,
prot as i32,
--
2.43.0