From nobody Tue Dec 2 01:51:34 2025 Received: from mail-ej1-f45.google.com (mail-ej1-f45.google.com [209.85.218.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DE9152FC891 for ; Thu, 20 Nov 2025 19:37:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763667474; cv=none; b=iEzkU3V4VTf2cCDpQsTPE2R0T+2OQPhIl3P0d0RR1YkLtycTRlsI9jxyjNNNqYj5Ldh1wKkN6apbCQ9FWk42Y5wVgpi7STbVloQbXy4fDZdWSG3EIMCHALegvaqgWAq6dfY18jpvwnstclF3XyQ/R18qj0o1kVG5nGOgteTijw8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763667474; c=relaxed/simple; bh=RuzBxUfNldLphdXJ1/YfWQk8i9OCxTF/DE52FRt83F8=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=gl9ZLXgrxF1GfHWdvqOtPyMFRX9Hg1Hf3Iy9vCZImKTWKX/vU5JzTmPuru6VLuXnEiBbjzK/t/YlpPGrXhcMnB3Z8/ovTekU25kOmCjZ1UghNex0f8EPxQf6rdiPnzviks3jSMwHdqgUEf3BpGkZ+JjUvfmmMTkjiVGMw81Juek= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Yn82l8uh; arc=none smtp.client-ip=209.85.218.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Yn82l8uh" Received: by mail-ej1-f45.google.com with SMTP id a640c23a62f3a-b472842981fso162048166b.1 for ; Thu, 20 Nov 2025 11:37:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763667470; x=1764272270; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:from:to:cc:subject:date:message-id:reply-to; bh=Fkdu+LsCUXFBIgM4CB60iYythh6x54mcsqtzaxVDy+I=; b=Yn82l8uh0ZCXN2qykquRyqSLmiUFxiTos2v4NSKf7tpiyfpHsHwX3ypHGUQTwuxl+N yta5gVzY2XX8s77QLfcEge5eV/A5A1hi48tgelO4JbZrky4GgWJrFdCUtSIN5H10Oagk L5JASoADiI+Nbp+kcjzhM2+ewDSIHOaTUr0fncNovthltOWw627If/mXxrh69QhiKO+D xfEi2tVtnJ34YlaWhelFqZ0/l7pSs+o1bK1n/rdboqJ2H0NL7Ga/PSJVwIj6cs2S+ZWJ G9Wg/VELKf/HBNAqkJtvbcn7L8ACWWO4a3DYmQIUthvPR4EkmnO5Fmc6qvBBffLWT7PH 17ng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763667470; x=1764272270; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=Fkdu+LsCUXFBIgM4CB60iYythh6x54mcsqtzaxVDy+I=; b=fsa/34b4xIoWvE3k/e00uvzlJMMliiMvUnAiLCpeHsMLzTmXMLm1GMtwjQ06DrRp/K o9shh5mY7elkWU5Zu19YID0HVxN8KRr0Q2aJp3kj4zRWtueMuGkziJAAsBuZVBdk6gBJ vHQcaDRTAQXLnOKU1RoEPiA5qWwCnQlrKVtmqSrRAqqf1gG/Qcu/eRcl9rtu0043UMI+ FfXTLjLICMq/22EWloPGBKZJKDzBSwv9AC+aEA5nRr6wO/4ndepc8v8gTR8Iw9l244RW 5vQ/gu3D9igte+aaSBGHCr4Fo0WH5GSqbRxGz3/rSJSFl/bPR8UUyhdfuA+Sz8xC9lpy Lbwg== X-Forwarded-Encrypted: i=1; AJvYcCWeKVv3+bTbAAoptOFRbBv4yH2Kj5FHtZ2/0G9PBZ03HKbmG230LKE9ZV21pwpuS8Mro7lmL56GIYSG/9k=@vger.kernel.org X-Gm-Message-State: AOJu0YwVeAP+TffSxs1CNckYku4sBYn6xMQEWw69N6nf/hmSNTtqAToM 0AbJZvx2VSkc+HfcQBR9+G5Etc6T46sWvl8sxVLZrjpdpUXcEuTHbzLc0aC5oo87pHgZBA== X-Gm-Gg: ASbGncuUxx76nK6mdlHhxxNrwB6J5CAjMmClu85cY5EFcegBm9XMrxW9X33gTBj8mWy KILRlK7L51UdqH/yBjd8mAUDE3GRd0PyzYvhyaOXWY+4xiJayUtRLG/qLVbUbBHHX2fhrwxWgcg PWW4IbdRioH3/UK7EPwT0LpNhFhy80eH+lUD1E8SZ62YlOw9lzZ7cOiJYH1N43jbB5qRSr8Iwj7 BBjgO1RoDOnu7B33aRjNRk8Z/VW3ExUVSqdHlRgztSVQio1qilbl9DJHW6o82WsubkSgTgtYpME E/RaxjjZQmoIVaDGZBtv8MwUTFCl10oOWZbmqY48JkkKQA/jVvvxttN3jBEyYA+mTSjntEeqify mEWF9uXdyzfFjmWiqhp90ecxtwBkcK1bV3JPq8QRJr4Js3dAqXt0oUacnfKGG6cufNg== X-Google-Smtp-Source: AGHT+IGxEmSOs/pbgifmxje/0gjywCP4Ex99urrpKg/AuSmsdWA4HdnrOdDNL9hfkXCKnOMhzsmG2w== X-Received: by 2002:a17:907:3e04:b0:b54:7778:c62d with SMTP id a640c23a62f3a-b7654d5cb18mr56015566b.15.1763667469834; Thu, 20 Nov 2025 11:37:49 -0800 (PST) Received: from gentb ([2a02:8071:6480:ee20::6780]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b7654fd43b1sm270508266b.38.2025.11.20.11.37.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 20 Nov 2025 11:37:48 -0800 (PST) From: Gent Binaku To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Gent Binaku , rust-for-linux@vger.kernel.org (open list:RUST), linux-kernel@vger.kernel.org (open list) Subject: [PATCH] rust: Convert PhysAddr type alias into newtype Date: Thu, 20 Nov 2025 20:36:51 +0100 Message-ID: <20251120193656.142234-1-binakugent@gmail.com> X-Mailer: git-send-email 2.52.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Convert the use of `phys_addr_t` in the Rust kernel bindings into a dedicated `PhysAddr` newtype to provide stronger type safety around physical addresses. By wrapping `phys_addr_t` in a transparent Rust newtype, we avoid accidentally mixing physical addresses with virtual addresses or other integer-like values. This allows the API to clearly express intent at call sites and makes misuse harder. The newtype provides: - Explicit construction and extraction via `from_raw()` and `as_raw()`. - Arithmetic helpers (`checked_add`, `wrapping_add`, `wrapping_sub`) with well-defined wrapping behaviour and operators. - Alignment helpers (`align_down`, `align_up`) for common patterns. - Formatting implementations (`Debug`, `Display`, hex, octal, binary). Existing Rust code that interacts with C helpers such as `ioremap*` and resource APIs is updated to use `PhysAddr` at the Rust boundary while still passing the underlying `phys_addr_t` to C via `as_raw()`. This addresses Rust-for-Linux issue #1204 by defining a dedicated PhysAddr type on top of phys_addr_t, constraining operations and avoiding confusion with other integer types. Suggested-by: Miguel Ojeda Link: https://github.com/Rust-for-Linux/linux/issues/1204 Link: https://lore.kernel.org/rust-for-linux/20251112-resource-phys-typedef= s-v2-0-538307384f82@google.com Signed-off-by: Gent Binaku diff --git c/rust/kernel/io/resource.rs i/rust/kernel/io/resource.rs index d0064724a01a..3c5a02d161d8 100644 --- c/rust/kernel/io/resource.rs +++ i/rust/kernel/io/resource.rs @@ -8,10 +8,10 @@ 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; -use crate::phys_addr::PhysAddr; pub use super::{ ResourceSize, // @@ -133,7 +133,7 @@ pub fn size(&self) -> ResourceSize { pub fn start(&self) -> PhysAddr { let inner =3D self.0.get(); // SAFETY: Safe as per the invariants of `Resource`. - unsafe { PhysAddr((*inner).start) } + unsafe { PhysAddr::from_raw((*inner).start) } } /// Returns the name of the resource. diff --git c/rust/kernel/lib.rs i/rust/kernel/lib.rs index 8c50c8147851..5402ab654a50 100644 --- c/rust/kernel/lib.rs +++ i/rust/kernel/lib.rs @@ -115,6 +115,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 c/rust/kernel/phys_addr.rs i/rust/kernel/phys_addr.rs index 15ae6db5baf9..41b635e5d44e 100644 --- c/rust/kernel/phys_addr.rs +++ i/rust/kernel/phys_addr.rs @@ -31,12 +31,7 @@ //! let aligned_addr =3D addr.align_down(8); //! assert_eq!(aligned_addr.as_raw(), 0x1008); //! ``` -#![allow(dead_code)] - -use crate::{ - bindings, - error::{Result}, -}; +use crate::bindings; use core::{ fmt, ops::{Add, Sub}, @@ -50,7 +45,7 @@ /// memory layout, making it safe to use across FFI boundaries. #[repr(transparent)] #[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PhysAddr(pub bindings::phys_addr_t); +pub struct PhysAddr(bindings::phys_addr_t); impl PhysAddr { /// The zero physical address. Equivalent to `PhysAddr::default()`. @@ -76,6 +71,21 @@ pub fn checked_add(self, rhs: bindings::phys_addr_t) -> = Option { 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.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)) @@ -86,27 +96,48 @@ 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 fn align_down(self, align: bindings::phys_addr_t) -> Self { + 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 fn align_up(self, align: bindings::phys_addr_t) -> Self { - // This is the idiomatic way to perform align_up. - self.add(align.wrapping_sub(1)).align_down(align) - } + 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 for PhysAddr { + fn from(addr: bindings::phys_addr_t) -> Self { + Self::from_raw(addr) + } +} + +impl From for bindings::phys_addr_t { + fn from(addr: PhysAddr) -> bindings::phys_addr_t { + addr.as_raw() + } } -// Implement operators for ergonomic pointer arithmetic. -// The convention is that these operators perform wrapping arithmetic. impl Add for PhysAddr { type Output =3D Self; + /// Adds with wrapping on overflow. + /// + /// For checked or saturating arithmetic, use [`checked_add`] or [`sat= urating_add`]. fn add(self, rhs: bindings::phys_addr_t) -> Self::Output { self.wrapping_add(rhs) } @@ -115,6 +146,9 @@ fn add(self, rhs: bindings::phys_addr_t) -> Self::Outpu= t { impl Sub for PhysAddr { type Output =3D Self; + /// Subtracts with wrapping on underflow. + /// + /// For checked or saturating arithmetic, use [`checked_sub`] or [`sat= urating_sub`]. fn sub(self, rhs: bindings::phys_addr_t) -> Self::Output { self.wrapping_sub(rhs) } @@ -133,7 +167,6 @@ fn sub(self, rhs: PhysAddr) -> Self::Output { } } - // Implement standard formatting traits for addresses. impl fmt::Debug for PhysAddr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -162,6 +195,24 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Resu= lt { } } +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 { @@ -169,7 +220,7 @@ mod tests { use crate::bindings::phys_addr_t; #[test] - fn test_creation_and_conversion() -> Result { + fn test_creation_and_conversion() { let addr =3D PhysAddr::from_raw(0x1000); assert_eq!(addr.as_raw(), 0x1000); @@ -177,19 +228,19 @@ fn test_creation_and_conversion() -> Result { assert_eq!(default_addr.as_raw(), 0); assert_eq!(default_addr, PhysAddr::ZERO); - Ok(()) + () } #[test] - fn test_is_null() -> Result { + fn test_is_null() { assert!(PhysAddr::ZERO.is_null()); assert!(PhysAddr::from_raw(0).is_null()); assert!(!PhysAddr::from_raw(1).is_null()); - Ok(()) + () } #[test] - fn test_arithmetic() -> Result { + fn test_arithmetic() { let addr =3D PhysAddr::from_raw(0x1000); // Checked addition @@ -205,14 +256,17 @@ fn test_arithmetic() -> Result { // Wrapping subtraction assert_eq!(addr.wrapping_sub(0x10), PhysAddr::from_raw(0x0ff0)); let zero_addr =3D PhysAddr::from_raw(0); - assert_eq!(zero_addr.wrapping_sub(1), PhysAddr::from_raw(phys_addr= _t::MAX)); + 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)= ); - Ok(()) + () } #[test] - fn test_address_difference() -> Result { + fn test_address_difference() { let addr1 =3D PhysAddr::from_raw(0x1000); let addr2 =3D PhysAddr::from_raw(0x2000); @@ -220,11 +274,11 @@ fn test_address_difference() -> Result { assert_eq!(addr1 - addr2, 0); // Saturating subtraction assert_eq!(addr1 - addr1, 0); - Ok(()) + () } #[test] - fn test_alignment() -> Result { + fn test_alignment() { let addr =3D PhysAddr::from_raw(0x1007); let align =3D 8 as phys_addr_t; @@ -236,11 +290,36 @@ fn test_alignment() -> Result { // 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) - ); + assert_eq!(PhysAddr::from_raw(0).align_up(align), PhysAddr::from_r= aw(0)); - Ok(()) + () + } + + #[test] + fn test_saturating_arithmetic() { + let addr =3D PhysAddr::from_raw(0x1000); + let max_addr =3D 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 =3D PhysAddr::from_raw(0); + assert_eq!(zero.saturating_sub(1), zero); // Caps at 0 + + () + } + + #[test] + fn test_checked_subtraction() { + let addr =3D PhysAddr::from_raw(0x1000); + + assert_eq!(addr.checked_sub(0x10), Some(PhysAddr::from_raw(0x0ff0)= )); + let zero =3D PhysAddr::from_raw(0); + assert_eq!(zero.checked_sub(1), None); // Underflow + + () } } --- rust/kernel/io/resource.rs | 4 +- rust/kernel/lib.rs | 1 + rust/kernel/phys_addr.rs | 139 +++++++++++++++++++++++++++++-------- 3 files changed, 112 insertions(+), 32 deletions(-) diff --git a/rust/kernel/io/resource.rs b/rust/kernel/io/resource.rs index d0064724a01a..3c5a02d161d8 100644 --- a/rust/kernel/io/resource.rs +++ b/rust/kernel/io/resource.rs @@ -8,10 +8,10 @@ use core::ops::Deref; use core::ptr::NonNull; =20 +use crate::phys_addr::PhysAddr; use crate::prelude::*; use crate::str::{CStr, CString}; use crate::types::Opaque; -use crate::phys_addr::PhysAddr; =20 pub use super::{ ResourceSize, // @@ -133,7 +133,7 @@ pub fn size(&self) -> ResourceSize { pub fn start(&self) -> PhysAddr { let inner =3D self.0.get(); // SAFETY: Safe as per the invariants of `Resource`. - unsafe { PhysAddr((*inner).start) } + unsafe { PhysAddr::from_raw((*inner).start) } } =20 /// Returns the name of the resource. diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 8c50c8147851..5402ab654a50 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -115,6 +115,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 index 15ae6db5baf9..41b635e5d44e 100644 --- a/rust/kernel/phys_addr.rs +++ b/rust/kernel/phys_addr.rs @@ -31,12 +31,7 @@ //! let aligned_addr =3D addr.align_down(8); //! assert_eq!(aligned_addr.as_raw(), 0x1008); //! ``` -#![allow(dead_code)] - -use crate::{ - bindings, - error::{Result}, -}; +use crate::bindings; use core::{ fmt, ops::{Add, Sub}, @@ -50,7 +45,7 @@ /// memory layout, making it safe to use across FFI boundaries. #[repr(transparent)] #[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PhysAddr(pub bindings::phys_addr_t); +pub struct PhysAddr(bindings::phys_addr_t); =20 impl PhysAddr { /// The zero physical address. Equivalent to `PhysAddr::default()`. @@ -76,6 +71,21 @@ pub fn checked_add(self, rhs: bindings::phys_addr_t) -> = Option { self.0.checked_add(rhs).map(Self) } =20 + /// Checked subtraction. Returns `None` if overflow occurs. + pub fn checked_sub(self, rhs: bindings::phys_addr_t) -> Option { + 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)) @@ -86,27 +96,48 @@ pub fn wrapping_sub(self, rhs: bindings::phys_addr_t) -= > Self { Self(self.0.wrapping_sub(rhs)) } =20 + /// 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 fn align_down(self, align: bindings::phys_addr_t) -> Self { + pub const fn align_down(self, align: bindings::phys_addr_t) -> Self { Self(self.0 & !(align.wrapping_sub(1))) } =20 /// Aligns the address up to the nearest multiple of `align`. /// /// `align` must be a power of two. - pub fn align_up(self, align: bindings::phys_addr_t) -> Self { - // This is the idiomatic way to perform align_up. - self.add(align.wrapping_sub(1)).align_down(align) - } + 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 for PhysAddr { + fn from(addr: bindings::phys_addr_t) -> Self { + Self::from_raw(addr) + } +} + +impl From for bindings::phys_addr_t { + fn from(addr: PhysAddr) -> bindings::phys_addr_t { + addr.as_raw() + } } =20 -// Implement operators for ergonomic pointer arithmetic. -// The convention is that these operators perform wrapping arithmetic. impl Add for PhysAddr { type Output =3D Self; =20 + /// Adds with wrapping on overflow. + /// + /// For checked or saturating arithmetic, use [`checked_add`] or [`sat= urating_add`]. fn add(self, rhs: bindings::phys_addr_t) -> Self::Output { self.wrapping_add(rhs) } @@ -115,6 +146,9 @@ fn add(self, rhs: bindings::phys_addr_t) -> Self::Outpu= t { impl Sub for PhysAddr { type Output =3D Self; =20 + /// Subtracts with wrapping on underflow. + /// + /// For checked or saturating arithmetic, use [`checked_sub`] or [`sat= urating_sub`]. fn sub(self, rhs: bindings::phys_addr_t) -> Self::Output { self.wrapping_sub(rhs) } @@ -133,7 +167,6 @@ fn sub(self, rhs: PhysAddr) -> Self::Output { } } =20 - // Implement standard formatting traits for addresses. impl fmt::Debug for PhysAddr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -162,6 +195,24 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Resu= lt { } } =20 +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 { @@ -169,7 +220,7 @@ mod tests { use crate::bindings::phys_addr_t; =20 #[test] - fn test_creation_and_conversion() -> Result { + fn test_creation_and_conversion() { let addr =3D PhysAddr::from_raw(0x1000); assert_eq!(addr.as_raw(), 0x1000); =20 @@ -177,19 +228,19 @@ fn test_creation_and_conversion() -> Result { assert_eq!(default_addr.as_raw(), 0); assert_eq!(default_addr, PhysAddr::ZERO); =20 - Ok(()) + () } =20 #[test] - fn test_is_null() -> Result { + fn test_is_null() { assert!(PhysAddr::ZERO.is_null()); assert!(PhysAddr::from_raw(0).is_null()); assert!(!PhysAddr::from_raw(1).is_null()); - Ok(()) + () } =20 #[test] - fn test_arithmetic() -> Result { + fn test_arithmetic() { let addr =3D PhysAddr::from_raw(0x1000); =20 // Checked addition @@ -205,14 +256,17 @@ fn test_arithmetic() -> Result { // Wrapping subtraction assert_eq!(addr.wrapping_sub(0x10), PhysAddr::from_raw(0x0ff0)); let zero_addr =3D PhysAddr::from_raw(0); - assert_eq!(zero_addr.wrapping_sub(1), PhysAddr::from_raw(phys_addr= _t::MAX)); + 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)= ); =20 - Ok(()) + () } =20 #[test] - fn test_address_difference() -> Result { + fn test_address_difference() { let addr1 =3D PhysAddr::from_raw(0x1000); let addr2 =3D PhysAddr::from_raw(0x2000); =20 @@ -220,11 +274,11 @@ fn test_address_difference() -> Result { assert_eq!(addr1 - addr2, 0); // Saturating subtraction assert_eq!(addr1 - addr1, 0); =20 - Ok(()) + () } =20 #[test] - fn test_alignment() -> Result { + fn test_alignment() { let addr =3D PhysAddr::from_raw(0x1007); let align =3D 8 as phys_addr_t; =20 @@ -236,11 +290,36 @@ fn test_alignment() -> Result { // 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) - ); + assert_eq!(PhysAddr::from_raw(0).align_up(align), PhysAddr::from_r= aw(0)); =20 - Ok(()) + () + } + + #[test] + fn test_saturating_arithmetic() { + let addr =3D PhysAddr::from_raw(0x1000); + let max_addr =3D 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 =3D PhysAddr::from_raw(0); + assert_eq!(zero.saturating_sub(1), zero); // Caps at 0 + + () + } + + #[test] + fn test_checked_subtraction() { + let addr =3D PhysAddr::from_raw(0x1000); + + assert_eq!(addr.checked_sub(0x10), Some(PhysAddr::from_raw(0x0ff0)= )); + let zero =3D PhysAddr::from_raw(0); + assert_eq!(zero.checked_sub(1), None); // Underflow + + () } } --=20 2.52.0