`ResourceSize` was a bare type alias for `resource_size_t`, so it inherited every integer operation and `as` cast. That silently permits operations that are meaningless for the size of a hardware resource: mixing it with unrelated integers and truncating casts.
Wrap it in a `#[repr(transparent)]` newtype so each conversion at a boundary becomes explicit and reviewable. The representation is unchanged, so this is ABI-identical; only the spelling at the FFI boundary changes. Provide the minimal conversion surface the call sites need: `from_raw`/`into_raw`, `From` in both directions, and a fallible `TryFrom<ResourceSize> for usize` for the page-count site that can truncate on 32-bit. Update the two producers, `Resource::size` and `SGEntry::dma_len`, and the `request_region` and nova-core consumers so the tree builds green. Add doctest examples for the new type. Suggested-by: Miguel Ojeda <[email protected]> Link: https://github.com/Rust-for-Linux/linux/issues/1203 Signed-off-by: Lorenzo Delgado <[email protected]> --- An earlier attempt at this conversion was posted by Moritz Zielke [1]. This is a fresh, independent implementation that reuses none of that code; it additionally converts the nova-core consumer and adds doctest examples. [1] https://lore.kernel.org/all/[email protected]/ drivers/gpu/nova-core/firmware/gsp.rs | 2 +- rust/kernel/io.rs | 73 +++++++++++++++++++++++++-- rust/kernel/io/resource.rs | 6 +-- rust/kernel/scatterlist.rs | 2 +- 4 files changed, 74 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs index 99a302bae567..d7593888e65b 100644 --- a/drivers/gpu/nova-core/firmware/gsp.rs +++ b/drivers/gpu/nova-core/firmware/gsp.rs @@ -175,7 +175,7 @@ pub(crate) fn radix3_dma_handle(&self) -> DmaAddress { fn map_into_lvl(sg_table: &SGTable<Owned<VVec<u8>>>, mut dst: VVec<u8>) -> Result<VVec<u8>> { for sg_entry in sg_table.iter() { // Number of pages we need to map. - let num_pages = usize::from_safe_cast(sg_entry.dma_len()).div_ceil(GSP_PAGE_SIZE); + let num_pages = usize::try_from(sg_entry.dma_len())?.div_ceil(GSP_PAGE_SIZE); for i in 0..num_pages { let entry = sg_entry.dma_address() diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index fcc7678fd9e3..ebf00b4536d6 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -6,9 +6,12 @@ use crate::{ bindings, + fmt, prelude::*, // }; +use core::num::TryFromIntError; + pub mod mem; pub mod poll; pub mod register; @@ -25,11 +28,73 @@ /// `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. +/// Resource size 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 ResourceSize = bindings::resource_size_t; +/// This wraps 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. +/// +/// # Examples +/// +/// ``` +/// use kernel::io::ResourceSize; +/// +/// let size = ResourceSize::from_raw(0x1000); +/// assert_eq!(size.into_raw(), 0x1000); +/// +/// // Round-trips through the raw C type. +/// let raw: kernel::bindings::resource_size_t = size.into(); +/// assert_eq!(ResourceSize::from(raw), size); +/// +/// // Fallible conversion to `usize` (can truncate on 32-bit). +/// assert_eq!(usize::try_from(size)?, 0x1000); +/// # Ok::<(), core::num::TryFromIntError>(()) +/// ``` +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct ResourceSize(bindings::resource_size_t); + +impl ResourceSize { + /// Creates a resource size from the raw C type. + #[inline] + pub const fn from_raw(value: bindings::resource_size_t) -> Self { + Self(value) + } + + /// Turns this resource size into the raw C type. + #[inline] + pub const fn into_raw(self) -> bindings::resource_size_t { + self.0 + } +} + +impl fmt::Debug for ResourceSize { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:#x}", self.0) + } +} + +impl From<bindings::resource_size_t> for ResourceSize { + #[inline] + fn from(value: bindings::resource_size_t) -> Self { + Self::from_raw(value) + } +} + +impl From<ResourceSize> for bindings::resource_size_t { + #[inline] + fn from(value: ResourceSize) -> Self { + value.into_raw() + } +} + +impl TryFrom<ResourceSize> for usize { + type Error = TryFromIntError; + + #[inline] + fn try_from(value: ResourceSize) -> Result<Self, Self::Error> { + Self::try_from(value.into_raw()) + } +} /// Raw representation of an MMIO region. /// diff --git a/rust/kernel/io/resource.rs b/rust/kernel/io/resource.rs index 17b0c174cfc5..a33a416b289a 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, size.into_raw()) }; } } @@ -114,7 +114,7 @@ pub fn request_region( bindings::__request_region( self.0.get(), start, - size, + size.into_raw(), name.as_char_ptr(), flags.0 as c_int, ) @@ -130,7 +130,7 @@ pub fn request_region( pub fn size(&self) -> ResourceSize { let inner = self.0.get(); // SAFETY: Safe as per the invariants of `Resource`. - unsafe { bindings::resource_size(inner) } + ResourceSize::from_raw(unsafe { bindings::resource_size(inner) }) } /// Returns the start address of the resource. diff --git a/rust/kernel/scatterlist.rs b/rust/kernel/scatterlist.rs index b83c468b5c63..5d67242befd8 100644 --- a/rust/kernel/scatterlist.rs +++ b/rust/kernel/scatterlist.rs @@ -93,7 +93,7 @@ pub fn dma_address(&self) -> dma::DmaAddress { pub fn dma_len(&self) -> ResourceSize { #[allow(clippy::useless_conversion)] // SAFETY: `self.as_raw()` is a valid pointer to a `struct scatterlist`. - unsafe { bindings::sg_dma_len(self.as_raw()) }.into() + ResourceSize::from_raw(unsafe { bindings::sg_dma_len(self.as_raw()) }.into()) } } base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda -- 2.54.0
