mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
gpu: nova-core: add functions and traits for lossless integer conversions
The core 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. However, the kernel supports a narrower set of architectures, and in the case of Nova we only support 64-bit. This makes it helpful and desirable to provide more infallible conversions, lest we need to rely on the `as` keyword and carry the risk of silently losing data. Thus, introduce a new module `num` that provides safe const functions performing more conversions allowed by the build target, as well as `FromSafeCast` and `IntoSafeCast` traits that are just extensions of `From` and `Into` to conversions that are known to be lossless. Suggested-by: Danilo Krummrich <dakr@kernel.org> Link: https://lore.kernel.org/rust-for-linux/DDK4KADWJHMG.1FUPL3SDR26XF@kernel.org/ Acked-by: Danilo Krummrich <dakr@kernel.org> [acourbot@nvidia.com: fix merge conflicts after rebase.] Signed-off-by: Alexandre Courbot <acourbot@nvidia.com> Message-ID: <20251029-nova-as-v3-4-6a30c7333ad9@nvidia.com>
This commit is contained in:
@@ -13,6 +13,7 @@ mod firmware;
|
||||
mod gfw;
|
||||
mod gpu;
|
||||
mod gsp;
|
||||
mod num;
|
||||
mod regs;
|
||||
mod vbios;
|
||||
|
||||
|
||||
167
drivers/gpu/nova-core/num.rs
Normal file
167
drivers/gpu/nova-core/num.rs
Normal file
@@ -0,0 +1,167 @@
|
||||
// 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);
|
||||
/// ```
|
||||
#[expect(unused)]
|
||||
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);
|
||||
/// ```
|
||||
#[expect(unused)]
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user