mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
Introduce a powered-up version of our ad-hoc `impl_from_enum_to_u8` macro that allows the definition of an enum type associated to a `Bounded` of a given width, and provides the `From` and `TryFrom` implementations required to use that enum as a register field member. This allows us to generate the required conversion implementations for using the kernel register macro and skip some tedious boilerplate. Acked-by: Danilo Krummrich <dakr@kernel.org> Link: https://patch.msgid.link/20260325-b4-nova-register-v4-1-bdf172f0f6ca@nvidia.com Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
298 lines
10 KiB
Rust
298 lines
10 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! Numerical helpers functions and traits.
|
|
//!
|
|
//! This is essentially a staging module for code to mature until it can be moved to the `kernel`
|
|
//! crate.
|
|
|
|
use kernel::{
|
|
macros::paste,
|
|
prelude::*, //
|
|
};
|
|
|
|
/// Implements safe `as` conversion functions from a given type into a series of target types.
|
|
///
|
|
/// These functions can be used in place of `as`, with the guarantee that they will be lossless.
|
|
macro_rules! impl_safe_as {
|
|
($from:ty as { $($into:ty),* }) => {
|
|
$(
|
|
paste! {
|
|
#[doc = ::core::concat!(
|
|
"Losslessly converts a [`",
|
|
::core::stringify!($from),
|
|
"`] into a [`",
|
|
::core::stringify!($into),
|
|
"`].")]
|
|
///
|
|
/// This conversion is allowed as it is always lossless. Prefer this over the `as`
|
|
/// keyword to ensure no lossy casts are performed.
|
|
///
|
|
/// This is for use from a `const` context. For non `const` use, prefer the
|
|
/// [`FromSafeCast`] and [`IntoSafeCast`] traits.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use crate::num;
|
|
///
|
|
#[doc = ::core::concat!(
|
|
"assert_eq!(num::",
|
|
::core::stringify!($from),
|
|
"_as_",
|
|
::core::stringify!($into),
|
|
"(1",
|
|
::core::stringify!($from),
|
|
"), 1",
|
|
::core::stringify!($into),
|
|
");")]
|
|
/// ```
|
|
#[allow(unused)]
|
|
#[inline(always)]
|
|
pub(crate) const fn [<$from _as_ $into>](value: $from) -> $into {
|
|
kernel::static_assert!(size_of::<$into>() >= size_of::<$from>());
|
|
|
|
value as $into
|
|
}
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
impl_safe_as!(u8 as { u16, u32, u64, usize });
|
|
impl_safe_as!(u16 as { u32, u64, usize });
|
|
impl_safe_as!(u32 as { u64, usize } );
|
|
// `u64` and `usize` have the same size on 64-bit platforms.
|
|
#[cfg(CONFIG_64BIT)]
|
|
impl_safe_as!(u64 as { usize } );
|
|
|
|
// A `usize` fits into a `u64` on 32 and 64-bit platforms.
|
|
#[cfg(any(CONFIG_32BIT, CONFIG_64BIT))]
|
|
impl_safe_as!(usize as { u64 });
|
|
|
|
// A `usize` fits into a `u32` on 32-bit platforms.
|
|
#[cfg(CONFIG_32BIT)]
|
|
impl_safe_as!(usize as { u32 });
|
|
|
|
/// Extension trait providing guaranteed lossless cast to `Self` from `T`.
|
|
///
|
|
/// The standard library's `From` implementations do not cover conversions that are not portable or
|
|
/// future-proof. For instance, even though it is safe today, `From<usize>` is not implemented for
|
|
/// [`u64`] because of the possibility to support larger-than-64bit architectures in the future.
|
|
///
|
|
/// The workaround is to either deal with the error handling of [`TryFrom`] for an operation that
|
|
/// technically cannot fail, or to use the `as` keyword, which can silently strip data if the
|
|
/// destination type is smaller than the source.
|
|
///
|
|
/// Both options are hardly acceptable for the kernel. It is also a much more architecture
|
|
/// dependent environment, supporting only 32 and 64 bit architectures, with some modules
|
|
/// explicitly depending on a specific bus width that could greatly benefit from infallible
|
|
/// conversion operations.
|
|
///
|
|
/// Thus this extension trait that provides, for the architecture the kernel is built for, safe
|
|
/// conversion between types for which such cast is lossless.
|
|
///
|
|
/// In other words, this trait is implemented if, for the current build target and with `t: T`, the
|
|
/// `t as Self` operation is completely lossless.
|
|
///
|
|
/// Prefer this over the `as` keyword to ensure no lossy casts are performed.
|
|
///
|
|
/// If you need to perform a conversion in `const` context, use [`u64_as_usize`], [`u32_as_usize`],
|
|
/// [`usize_as_u64`], etc.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use crate::num::FromSafeCast;
|
|
///
|
|
/// assert_eq!(usize::from_safe_cast(0xf00u32), 0xf00u32 as usize);
|
|
/// ```
|
|
pub(crate) trait FromSafeCast<T> {
|
|
/// Create a `Self` from `value`. This operation is guaranteed to be lossless.
|
|
fn from_safe_cast(value: T) -> Self;
|
|
}
|
|
|
|
impl FromSafeCast<usize> for u64 {
|
|
fn from_safe_cast(value: usize) -> Self {
|
|
usize_as_u64(value)
|
|
}
|
|
}
|
|
|
|
#[cfg(CONFIG_32BIT)]
|
|
impl FromSafeCast<usize> for u32 {
|
|
fn from_safe_cast(value: usize) -> Self {
|
|
usize_as_u32(value)
|
|
}
|
|
}
|
|
|
|
impl FromSafeCast<u32> for usize {
|
|
fn from_safe_cast(value: u32) -> Self {
|
|
u32_as_usize(value)
|
|
}
|
|
}
|
|
|
|
#[cfg(CONFIG_64BIT)]
|
|
impl FromSafeCast<u64> for usize {
|
|
fn from_safe_cast(value: u64) -> Self {
|
|
u64_as_usize(value)
|
|
}
|
|
}
|
|
|
|
/// Counterpart to the [`FromSafeCast`] trait, i.e. this trait is to [`FromSafeCast`] what [`Into`]
|
|
/// is to [`From`].
|
|
///
|
|
/// See the documentation of [`FromSafeCast`] for the motivation.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use crate::num::IntoSafeCast;
|
|
///
|
|
/// assert_eq!(0xf00u32.into_safe_cast(), 0xf00u32 as usize);
|
|
/// ```
|
|
pub(crate) trait IntoSafeCast<T> {
|
|
/// Convert `self` into a `T`. This operation is guaranteed to be lossless.
|
|
fn into_safe_cast(self) -> T;
|
|
}
|
|
|
|
/// Reverse operation for types implementing [`FromSafeCast`].
|
|
impl<S, T> IntoSafeCast<T> for S
|
|
where
|
|
T: FromSafeCast<S>,
|
|
{
|
|
fn into_safe_cast(self) -> T {
|
|
T::from_safe_cast(self)
|
|
}
|
|
}
|
|
|
|
/// Implements lossless conversion of a constant from a larger type into a smaller one.
|
|
macro_rules! impl_const_into {
|
|
($from:ty => { $($into:ty),* }) => {
|
|
$(
|
|
paste! {
|
|
#[doc = ::core::concat!(
|
|
"Performs a build-time safe conversion of a [`",
|
|
::core::stringify!($from),
|
|
"`] constant value into a [`",
|
|
::core::stringify!($into),
|
|
"`].")]
|
|
///
|
|
/// This checks at compile-time that the conversion is lossless, and triggers a build
|
|
/// error if it isn't.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use crate::num;
|
|
///
|
|
/// // Succeeds because the value of the source fits into the destination's type.
|
|
#[doc = ::core::concat!(
|
|
"assert_eq!(num::",
|
|
::core::stringify!($from),
|
|
"_into_",
|
|
::core::stringify!($into),
|
|
"::<1",
|
|
::core::stringify!($from),
|
|
">(), 1",
|
|
::core::stringify!($into),
|
|
");")]
|
|
/// ```
|
|
#[allow(unused)]
|
|
pub(crate) const fn [<$from _into_ $into>]<const N: $from>() -> $into {
|
|
// Make sure that the target type is smaller than the source one.
|
|
static_assert!($from::BITS >= $into::BITS);
|
|
// CAST: we statically enforced above that `$from` is larger than `$into`, so the
|
|
// `as` conversion will be lossless.
|
|
build_assert!(N >= $into::MIN as $from && N <= $into::MAX as $from);
|
|
|
|
N as $into
|
|
}
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
impl_const_into!(usize => { u8, u16, u32 });
|
|
impl_const_into!(u64 => { u8, u16, u32 });
|
|
impl_const_into!(u32 => { u8, u16 });
|
|
impl_const_into!(u16 => { u8 });
|
|
|
|
/// Creates an enum type associated to a [`Bounded`](kernel::num::Bounded), with a [`From`]
|
|
/// conversion to the associated `Bounded` and either a [`TryFrom`] or `From` conversion from the
|
|
/// associated `Bounded`.
|
|
// TODO[FPRI]: This is a temporary solution to be replaced with the corresponding derive macros
|
|
// once they land.
|
|
#[macro_export]
|
|
macro_rules! bounded_enum {
|
|
(
|
|
$(#[$enum_meta:meta])*
|
|
$vis:vis enum $enum_type:ident with $from_impl:ident<Bounded<$width:ty, $length:literal>> {
|
|
$( $(#[doc = $variant_doc:expr])* $variant:ident = $value:expr),* $(,)*
|
|
}
|
|
) => {
|
|
$(#[$enum_meta])*
|
|
$vis enum $enum_type {
|
|
$(
|
|
$(#[doc = $variant_doc])*
|
|
$variant = $value
|
|
),*
|
|
}
|
|
|
|
impl core::convert::From<$enum_type> for kernel::num::Bounded<$width, $length> {
|
|
fn from(value: $enum_type) -> Self {
|
|
match value {
|
|
$($enum_type::$variant =>
|
|
kernel::num::Bounded::<$width, _>::new::<{ $value }>()),*
|
|
}
|
|
}
|
|
}
|
|
|
|
bounded_enum!(@impl_from $enum_type with $from_impl<Bounded<$width, $length>> {
|
|
$($variant = $value),*
|
|
});
|
|
};
|
|
|
|
// `TryFrom` implementation from associated `Bounded` to enum type.
|
|
(@impl_from $enum_type:ident with TryFrom<Bounded<$width:ty, $length:literal>> {
|
|
$($variant:ident = $value:expr),* $(,)*
|
|
}) => {
|
|
impl core::convert::TryFrom<kernel::num::Bounded<$width, $length>> for $enum_type {
|
|
type Error = kernel::error::Error;
|
|
|
|
fn try_from(
|
|
value: kernel::num::Bounded<$width, $length>
|
|
) -> kernel::error::Result<Self> {
|
|
match value.get() {
|
|
$(
|
|
$value => Ok($enum_type::$variant),
|
|
)*
|
|
_ => Err(kernel::error::code::EINVAL),
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// `From` implementation from associated `Bounded` to enum type. Triggers a build-time error if
|
|
// all possible values of the `Bounded` are not covered by the enum type.
|
|
(@impl_from $enum_type:ident with From<Bounded<$width:ty, $length:literal>> {
|
|
$($variant:ident = $value:expr),* $(,)*
|
|
}) => {
|
|
impl core::convert::From<kernel::num::Bounded<$width, $length>> for $enum_type {
|
|
fn from(value: kernel::num::Bounded<$width, $length>) -> Self {
|
|
const MAX: $width = 1 << $length;
|
|
|
|
// Makes the compiler optimizer aware of the possible range of values.
|
|
let value = value.get() & ((1 << $length) - 1);
|
|
match value {
|
|
$(
|
|
$value => $enum_type::$variant,
|
|
)*
|
|
// PANIC: we cannot reach this arm as all possible variants are handled by the
|
|
// match arms above. It is here to make the compiler complain if `$enum_type`
|
|
// does not cover all values of the `0..MAX` range.
|
|
MAX.. => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|