Extend the impl_flags! macro to support conversions between generated flag types and raw C/UAPI integers. Implement TryFrom<Repr> for individual flags (exact variant match) and flag sets (rejecting unknown bits), along with an unsafe from_raw() constructor for flag sets. Additionally, add BitOr and BitOrAssign implementations between the raw representation and flag types.
Suggested-by: Daniel Almeida <[email protected]> Suggested-by: Andreas Hindborg <[email protected]> Signed-off-by: Filipe Xavier <[email protected]> --- Changes in v2: - New Error InvalidFlagValue for TryFrom implementations, mapping invalid flag values to EINVAL. - Replace Tyr's local TryFrom implementation to use from the macro. - Add raw BitOr<$flags> and BitOrAssign<$flags> support to complete operations. - Link to v1: https://lore.kernel.org/r/[email protected] --- drivers/gpu/drm/tyr/vm.rs | 13 ----- rust/kernel/error.rs | 11 +++++ rust/kernel/impl_flags.rs | 122 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 121 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/tyr/vm.rs b/drivers/gpu/drm/tyr/vm.rs index c5e307b1e2416837c85f890c074f62bc74289178..446672b4bc6bfedab789e20c97c4b754d5c019c0 100644 --- a/drivers/gpu/drm/tyr/vm.rs +++ b/drivers/gpu/drm/tyr/vm.rs @@ -141,19 +141,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } -impl TryFrom<u32> for VmMapFlags { - type Error = Error; - - fn try_from(value: u32) -> Result<Self, Self::Error> { - let valid = VmFlag::Readonly as u32 | VmFlag::Noexec as u32 | VmFlag::Uncached as u32; - - if value & !valid != 0 { - return Err(EINVAL); - } - Ok(Self(value)) - } -} - /// Arguments for a virtual memory map operation. struct VmMapArgs<'drm> { /// Access permissions and caching behavior for the mapping. diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index e52793f771966f20258c7d021826f7b63f8cf35e..8d2c4300262743b504d82bcc041076e78636cda5 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -201,6 +201,10 @@ macro_rules! declare_err { #[derive(Clone, Copy, PartialEq, Eq)] pub struct Error(NonZeroI32); +/// Represents an invalid value for a flag type. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct InvalidFlagValue; + impl Error { /// Creates an [`Error`] from a kernel error code. /// @@ -323,6 +327,13 @@ fn from(_: AllocError) -> Error { } } +impl From<InvalidFlagValue> for Error { + #[inline] + fn from(_: InvalidFlagValue) -> Error { + code::EINVAL + } +} + impl From<TryFromIntError> for Error { #[inline] fn from(_: TryFromIntError) -> Error { diff --git a/rust/kernel/impl_flags.rs b/rust/kernel/impl_flags.rs index fdf44d5eea9cb907f6d8d209792a1d9b74b55be6..493cd7f51dcc5f4926e39c4e79c7d4ed7157233d 100644 --- a/rust/kernel/impl_flags.rs +++ b/rust/kernel/impl_flags.rs @@ -14,6 +14,8 @@ /// - The struct and enum types with appropriate `#[repr]` attributes. /// - Implementations of common bitflag operators /// ([`::core::ops::BitOr`], [`::core::ops::BitAnd`], etc.). +/// - Conversions between the Rust-native types and their raw representation. +/// - Validation when converting raw values back into Rust-native types. /// - Utility methods such as `.contains()` to check flags. /// /// # Examples @@ -68,6 +70,25 @@ /// let negated = !read_only; /// assert!(negated.contains(Permission::Write)); /// assert!(!negated.contains(Permission::Read)); +/// +/// // Convert individual flags and flag sets to their raw representation. +/// let raw: u32 = Permission::Read.into(); +/// assert_eq!(raw, 1); +/// let raw: u32 = read_write.into(); +/// +/// // Raw values can be validated before entering the Rust-native API. +/// assert_eq!(Permission::try_from(1), Ok(Permission::Read)); +/// assert!(Permission::try_from(3).is_err()); +/// assert!(Permissions::try_from(3).is_ok()); +/// +/// // Raw C/UAPI fields can be updated without an intermediate conversion. +/// let mut raw = 0u32; +/// raw |= Permission::Read; +/// raw |= Permission::Write; +/// assert_eq!(raw, 3); +/// let read_write = Permission::Read | Permission::Write; +/// raw |= read_write; +/// assert_eq!(raw | read_write, 3); /// ``` #[macro_export] macro_rules! impl_flags { @@ -103,6 +124,13 @@ fn from(value: $flag) -> Self { } } + impl ::core::convert::From<$flag> for $ty { + #[inline] + fn from(value: $flag) -> Self { + value as $ty + } + } + impl ::core::convert::From<$flags> for $ty { #[inline] fn from(value: $flags) -> Self { @@ -110,32 +138,45 @@ fn from(value: $flags) -> Self { } } - impl ::core::ops::BitOr for $flags { - type Output = Self; + impl ::core::convert::TryFrom<$ty> for $flag { + type Error = ::kernel::error::InvalidFlagValue; + #[inline] - fn bitor(self, rhs: Self) -> Self::Output { - Self(self.0 | rhs.0) + fn try_from(value: $ty) -> Result<Self, Self::Error> { + match value { + $( + v if v == ($value as $ty) => Ok($flag::$name), + )+ + _ => Err(::kernel::error::InvalidFlagValue), + } } } - impl ::core::ops::BitOrAssign for $flags { + impl ::core::convert::TryFrom<$ty> for $flags { + type Error = ::kernel::error::InvalidFlagValue; + #[inline] - fn bitor_assign(&mut self, rhs: Self) { - *self = *self | rhs; + fn try_from(value: $ty) -> Result<Self, Self::Error> { + if value & !Self::all_bits() != 0 { + return Err(::kernel::error::InvalidFlagValue); + } + + // SAFETY: All bits set in `value` are valid flag bits. + Ok(unsafe { Self::from_raw(value) }) } } - impl ::core::ops::BitOr<$flag> for $flags { + impl ::core::ops::BitOr for $flags { type Output = Self; #[inline] - fn bitor(self, rhs: $flag) -> Self::Output { - self | Self::from(rhs) + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) } } - impl ::core::ops::BitOrAssign<$flag> for $flags { + impl ::core::ops::BitOrAssign for $flags { #[inline] - fn bitor_assign(&mut self, rhs: $flag) { + fn bitor_assign(&mut self, rhs: Self) { *self = *self | rhs; } } @@ -155,6 +196,21 @@ fn bitand_assign(&mut self, rhs: Self) { } } + impl ::core::ops::BitOr<$flag> for $flags { + type Output = Self; + #[inline] + fn bitor(self, rhs: $flag) -> Self::Output { + self | Self::from(rhs) + } + } + + impl ::core::ops::BitOrAssign<$flag> for $flags { + #[inline] + fn bitor_assign(&mut self, rhs: $flag) { + *self = *self | rhs; + } + } + impl ::core::ops::BitAnd<$flag> for $flags { type Output = Self; #[inline] @@ -240,6 +296,38 @@ fn not(self) -> Self::Output { } } + impl ::core::ops::BitOr<$flag> for $ty { + type Output = Self; + + #[inline] + fn bitor(self, rhs: $flag) -> Self::Output { + self | (rhs as $ty) + } + } + + impl ::core::ops::BitOrAssign<$flag> for $ty { + #[inline] + fn bitor_assign(&mut self, rhs: $flag) { + *self |= rhs as $ty; + } + } + + impl ::core::ops::BitOr<$flags> for $ty { + type Output = Self; + + #[inline] + fn bitor(self, rhs: $flags) -> Self::Output { + self | rhs.0 + } + } + + impl ::core::ops::BitOrAssign<$flags> for $ty { + #[inline] + fn bitor_assign(&mut self, rhs: $flags) { + *self |= rhs.0; + } + } + impl $flags { /// Returns an empty instance where no flags are set. #[inline] @@ -253,6 +341,16 @@ pub const fn all_bits() -> $ty { 0 $( | $value )+ } + /// Creates a flag set from its raw representation without validation. + /// + /// # Safety + /// + /// All bits set in `value` must correspond to valid flags. + #[inline] + pub const unsafe fn from_raw(value: $ty) -> Self { + Self(value) + } + /// Checks if a specific flag is set. #[inline] pub fn contains(self, flag: $flag) -> bool { --- base-commit: 08df884136f1c1197bab2a27814404fd329d9aac change-id: 20260912-add-from-raw-conversions-4647d890bb77 Best regards, -- Filipe Xavier <[email protected]>
