* [PATCH v5 0/2] rust: introduce cv! macro for safe const conversions of integer-like types
@ 2026-10-13 5:58 Eliot Courtney
2026-10-13 5:58 ` [PATCH v5 1/2] rust: num: add cv! macro to create values from constant expressions Eliot Courtney
2026-10-13 5:58 ` [PATCH v5 2/2] gpu: nova-core: use cv! for constant casts Eliot Courtney
0 siblings, 2 replies; 5+ messages in thread
From: Eliot Courtney @ 2026-10-13 5:58 UTC (permalink / raw)
To: Alexandre Courbot, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Onur Özkan, David Airlie, Simona Vetter
Cc: John Hubbard, Alistair Popple, Timur Tabi, rust-for-linux,
linux-kernel, nova-gpu, dri-devel, Eliot Courtney
This series introduces the cv! ("const value") macro for safe compile
time conversions of integer-like types. The syntax is `cv!(literal or
expression)`, or `cv!(literal or expression => type)` when it needs some
help with type inference. The result is that many expressions can be
rewritten into a compile time safe (no `as`) and less visually busy
style, for example:
```
- const DMA_LEN: u32 = casts::usize_into_u32::<{ MEM_BLOCK_ALIGNMENT }>();
+ const DMA_LEN: u32 = cv!(MEM_BLOCK_ALIGNMENT);
- .with_const_msg_type::<{ casts::u8_as_u32(MSG_TYPE_VENDOR_PCI) }>()
+ .with_const_msg_type(cv!(MSG_TYPE_VENDOR_PCI))
- data: [[u8; GSP_PAGE_SIZE]; casts::u32_as_usize(MSGQ_NUM_PAGES)],
+ data: [[u8; GSP_PAGE_SIZE]; cv!(MSGQ_NUM_PAGES)],
-pub const NSEC_PER_SEC: i64 = bindings::NSEC_PER_SEC as i64;
+pub const NSEC_PER_SEC: i64 = cv!(bindings::NSEC_PER_SEC);
-//! let b = Bounded::<u16, 5>::new::<0x18>();
+//! let b: Bounded<u16, 5> = cv!(0x18);
```
This works by defining a trait `FromConst<const V: i128>` with an
associated constant `VALUE`. Then each implementor sets `VALUE` to a
constant expression. This works around not having const traits. An
alternative idea from Gary defined a FromLiteral::from_literal<V>
function [1]. This version essentially moves the implementation of that
function to the const block of the associated constant (since it has to
be executable at compile time anyway).
This is based on ideas from Gary Guo [1], Alice Ryhl [2], and Alexandre
Courbot [3].
One (potential) limitation is that const generic types or values can't
be used with cv!, because it requires the const generic expressions
feature. The const_as! [3] macro did not have this limitation. This is
worked around by special casing conversions to language integral types
in the cv! macro (essentially subsuming const_as!). This special casing
can be removed when const generic expressions can be used.
Some code is taken with permission from Alex's const_as! series.
The structure of this series is as follows:
1. cv! macro
2. updates to nova-core to use cv!
For a follow up series (I've already written this, but to avoid spamming
a ton while we iterate on cv!, I'd send this after):
3. updates to various other code to use cv!
4. misc updates to other code to remove usages of `as`
We have multiple ways of converting - T::from(),
FromSafeCast/IntoSafeCast, cv!, *_as_*. This series uses them in that
order of priority, based on Alex's suggestion [4].
This is based on drm-rust-next.
[1] https://lore.kernel.org/all/DKT6WNPI2OA5.3RCBNYHHAFAD9@garyguo.net/
[2] https://lore.kernel.org/all/CAH5fLgiGcOn+HQLj4w9yDc31V54PbqNUQv8cUFRoEuFzQrAAZA@mail.gmail.com/
[3] https://lore.kernel.org/all/20260825-const_as-v1-0-1ce712225fe2@nvidia.com/
[4] https://lore.kernel.org/all/DKYTZNAX6ON6.1K1372FFAQOF7@nvidia.com/
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
Changes in v5:
- Explain the cv name, say compile time and mention const contexts in the docs (Miguel)
- Add a Pitfalls section on unsuffixed literals defaulting to i32 (Miguel, Alex)
- Fix doc links, comment capitalization and spacing (Miguel)
- Use the kernel import style in the doctest (Danilo)
- Rebase on drm-rust-next
- Link to v4: https://patch.msgid.link/20260917-cv-v4-0-547ef5727451@nvidia.com
Changes in v4:
- Squash the prelude patch into the macro patch (Alex)
- Move the new code below Integer (Alex)
- Comment the explicit primitive arms (Alex)
- FromConst to make the primitive value for NonZero, Bounded and Alignment (Alex)
- const_assert!->assert! in const contexts (Gary)
- Base on drm-rust-next
- Link to v3: https://patch.msgid.link/20260902-cv-v3-0-0f90659e711d@nvidia.com
Changes in v3:
- Add comment about not using FromConst trait directly (Gary)
- Lower case on const assert/panics (Gary)
- match instead of unwrap() for NonZero + message (Gary)
- Add reviewed-by tags (thanks Gary!)
- Link to v2: https://patch.msgid.link/20260901-cv-v2-0-446bc69d2ade@nvidia.com
Changes in v2:
- Improve error messages (Gary)
- Remove pre-req for Alex's nova-core num->kernel series.
- Link to v1: https://patch.msgid.link/20260828-cv-v1-0-48a180dfc4c8@nvidia.com
---
Eliot Courtney (2):
rust: num: add cv! macro to create values from constant expressions
gpu: nova-core: use cv! for constant casts
drivers/gpu/nova-core/falcon.rs | 9 +-
drivers/gpu/nova-core/fb/hal/gb100.rs | 4 +-
drivers/gpu/nova-core/firmware/fwsec/bootloader.rs | 7 +-
drivers/gpu/nova-core/fsp.rs | 3 +-
drivers/gpu/nova-core/gsp/cmdq.rs | 6 +-
drivers/gpu/nova-core/gsp/fw.rs | 38 +++---
drivers/gpu/nova-core/gsp/fw/commands.rs | 2 +-
drivers/gpu/nova-core/num.rs | 55 +-------
rust/kernel/num.rs | 149 +++++++++++++++++++++
rust/kernel/num/bounded.rs | 16 +++
rust/kernel/prelude.rs | 1 +
rust/kernel/ptr.rs | 12 +-
12 files changed, 205 insertions(+), 97 deletions(-)
---
base-commit: 5a1b7121a6c4db6c0239d3ac3fd1c2bb33a40025
change-id: 20260828-cv-8d4e952fc14c
Best regards,
--
Eliot Courtney <ecourtney@nvidia.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v5 1/2] rust: num: add cv! macro to create values from constant expressions
2026-10-13 5:58 [PATCH v5 0/2] rust: introduce cv! macro for safe const conversions of integer-like types Eliot Courtney
@ 2026-10-13 5:58 ` Eliot Courtney
2026-10-13 7:17 ` Alexandre Courbot
2026-10-13 13:18 ` Miguel Ojeda
2026-10-13 5:58 ` [PATCH v5 2/2] gpu: nova-core: use cv! for constant casts Eliot Courtney
1 sibling, 2 replies; 5+ messages in thread
From: Eliot Courtney @ 2026-10-13 5:58 UTC (permalink / raw)
To: Alexandre Courbot, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Onur Özkan, David Airlie, Simona Vetter
Cc: John Hubbard, Alistair Popple, Timur Tabi, rust-for-linux,
linux-kernel, nova-gpu, dri-devel, Eliot Courtney
Currently, using NonZero/Bounded constants is quite verbose. It's
unfortunate because it disincentivizes using it in interface boundaries.
Introduce a macro to make it nicer to use. The macro `cv!` (for constant
value) takes a const integer expression and widens it to i128 (at build
time only) before passing it as a const generic value to a new trait
`FromConst`. The value is then converted and appears in the
associated constant `FromConst::VALUE`. The trait is implemented by
NonZero, Bounded, and Alignment and lets values of each be constructed
from constants without a verbose turbofish syntax.
For example, `const { NonZero::new(1).unwrap() }` can be written as
`cv!(1)`.
Add the `cv!` macro to the prelude so it doesn't need to be imported
explicitly to use it.
Suggested-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Gary Guo <gary@garyguo.net>
Acked-by: Miguel Ojeda <ojeda@kernel.org>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/num.rs | 149 +++++++++++++++++++++++++++++++++++++++++++++
rust/kernel/num/bounded.rs | 16 +++++
rust/kernel/prelude.rs | 1 +
rust/kernel/ptr.rs | 12 +++-
4 files changed, 177 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/num.rs b/rust/kernel/num.rs
index dbe848e30efe..a387a94e97e7 100644
--- a/rust/kernel/num.rs
+++ b/rust/kernel/num.rs
@@ -79,3 +79,152 @@ impl Integer for $type {
i128: Signed,
isize: Signed
);
+
+/// Creates a constant value ("cv") from an integer constant expression, with validity checked at
+/// compile time.
+///
+/// This works for any type that implements [`FromConst`], with the target type inferred from
+/// the context, or named explicitly with `cv!(value => Type)`. It can also be used in const
+/// contexts.
+///
+/// # Pitfalls
+///
+/// Only the target type is inferred from the context. The expression passed to `cv!` is typed by
+/// itself. So, unsuffixed integer literal expressions default to `i32`. For example `cv!(2 << 31)`
+/// does not evaluate to the same value as `cv!(2i64 << 31)`, since `2 << 31` is treated as an `i32`
+/// before being widened to `i128` internally. Literals and arithmetic expressions that overflow
+/// `i32` are a compile error, it's just left shifts that require care.
+///
+/// # Examples
+///
+/// ```
+/// use core::num::NonZero;
+///
+/// use kernel::{
+/// num::Bounded,
+/// ptr::Alignment, //
+/// };
+///
+/// let v: NonZero<usize> = cv!(8);
+/// assert_eq!(v.get(), 8);
+///
+/// // Any integer constant expression works, not only literals.
+/// let m: NonZero<usize> = cv!(usize::MAX);
+/// assert_eq!(m.get(), usize::MAX);
+///
+/// let b: Bounded<u32, 4> = cv!(15);
+/// assert_eq!(b.get(), 15);
+///
+/// let a: Alignment = cv!(4096);
+/// assert_eq!(a.as_usize(), 4096);
+///
+/// // Checked narrowing of integer constants, including in `const` items.
+/// const SMALL: u8 = cv!(200u32);
+/// assert_eq!(SMALL, 200);
+///
+/// const N: NonZero<u8> = cv!(5);
+/// assert_eq!(N.get(), 5);
+///
+/// // The target type can be given explicitly.
+/// let e = cv!(200u32 => u8);
+/// assert_eq!(e, 200);
+///
+/// // With an explicit primitive target, the expression can use generic parameters.
+/// const fn as_u64<const KEY: u16>() -> u64 {
+/// cv!(KEY => u64)
+/// }
+/// assert_eq!(as_u64::<0x40>(), 0x40);
+/// ```
+#[macro_export]
+#[doc(hidden)]
+macro_rules! cv {
+ (@cast $v:expr => $t:ty) => {
+ const {
+ #[allow(unused_comparisons, unused_assignments, clippy::as_underscore)]
+ {
+ let v = $v;
+ let r = v as $t;
+ // Pin `back` to `v`'s type so `as _` casts back to the source type.
+ let mut back = v;
+ back = r as _;
+
+ ::core::assert!(
+ back == v && (v < 0) == (r < 0),
+ "value does not fit into the target type"
+ );
+
+ r
+ }
+ }
+ };
+
+ // Using `FromConst<V>::VALUE` means const generic expressions can't be used, since it requires
+ // the `generic_const_exprs` feature. Provide a special path for each primitive type that allows
+ // const generic expressions.
+ ($v:expr => u8) => { $crate::cv!(@cast $v => u8) };
+ ($v:expr => u16) => { $crate::cv!(@cast $v => u16) };
+ ($v:expr => u32) => { $crate::cv!(@cast $v => u32) };
+ ($v:expr => u64) => { $crate::cv!(@cast $v => u64) };
+ ($v:expr => u128) => { $crate::cv!(@cast $v => u128) };
+ ($v:expr => usize) => { $crate::cv!(@cast $v => usize) };
+ ($v:expr => i8) => { $crate::cv!(@cast $v => i8) };
+ ($v:expr => i16) => { $crate::cv!(@cast $v => i16) };
+ ($v:expr => i32) => { $crate::cv!(@cast $v => i32) };
+ ($v:expr => i64) => { $crate::cv!(@cast $v => i64) };
+ ($v:expr => i128) => { $crate::cv!(@cast $v => i128) };
+ ($v:expr => isize) => { $crate::cv!(@cast $v => isize) };
+ ($v:expr => $t:ty) => {
+ <$t as $crate::num::FromConst<{ $crate::cv!(@cast $v => i128) }>>::VALUE
+ };
+ ($v:expr) => {
+ <_ as $crate::num::FromConst<{ $crate::cv!(@cast $v => i128) }>>::VALUE
+ };
+}
+#[doc(inline)]
+pub use cv;
+
+/// Types that can be created from an integer constant expression validated at compile time.
+///
+/// Implement this trait to make a type usable with [`cv!`]. Use the [`cv!`] macro, not this trait
+/// directly, for creating values.
+#[diagnostic::on_unimplemented(message = "`{Self}` cannot be converted from a constant")]
+pub trait FromConst<const V: i128>: Sized {
+ /// The value that corresponds to the constant `V`.
+ ///
+ /// Fails the build if `V` is not a valid value for [`Self`].
+ const VALUE: Self;
+}
+
+/// Implements [`FromConst`] for primitive integer types and their [`NonZero`](core::num::NonZero)
+/// versions.
+macro_rules! impl_from_const {
+ ($($type:ty)*) => {
+ $(
+ impl<const V: i128> FromConst<V> for $type {
+ const VALUE: Self = {
+ // CAST: The macro is only used for types up to 64 bits wide, so `MIN` and `MAX`
+ // widen to `i128` losslessly.
+ assert!(
+ V >= <$type>::MIN as i128 && V <= <$type>::MAX as i128,
+ "constant cannot be represented by the target type"
+ );
+
+ // CAST: The assert above confirmed that `V` fits in `$type`.
+ V as $type
+ };
+ }
+
+ impl<const V: i128> FromConst<V> for core::num::NonZero<$type> {
+ const VALUE: Self = match core::num::NonZero::new(<$type as FromConst<V>>::VALUE) {
+ Some(value) => value,
+ None => panic!("constant cannot be zero"),
+ };
+ }
+ )*
+ };
+}
+
+impl_from_const!(
+ u8 u16 u32 u64 usize
+ i8 i16 i32 i64 isize
+);
diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
index 2a2b0a4bca5e..0b32f4343922 100644
--- a/rust/kernel/num/bounded.rs
+++ b/rust/kernel/num/bounded.rs
@@ -14,6 +14,7 @@
use kernel::{
num::{
+ FromConst,
Integer,
Unsigned, //
},
@@ -272,6 +273,21 @@ pub const fn new<const VALUE: $type>() -> Self {
unsafe { Self::__new(VALUE) }
}
}
+
+ impl<const N: u32, const V: i128> FromConst<V> for Bounded<$type, N> {
+ const VALUE: Self = {
+ let value = <$type as FromConst<V>>::VALUE;
+ // Statically assert that `value` fits within the set number of bits.
+ assert!(
+ fits_within!(value, $type, N),
+ "constant cannot be represented within the given number of bits"
+ );
+
+ // SAFETY: The assert above confirmed that `value` can be represented within `N`
+ // bits.
+ unsafe { Self::__new(value) }
+ };
+ }
)*
};
}
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index ca396f1f78a6..5facf8f7af90 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -106,6 +106,7 @@
Result, //
},
init::InPlaceInit,
+ num::cv,
pr_alert,
pr_crit,
pr_debug,
diff --git a/rust/kernel/ptr.rs b/rust/kernel/ptr.rs
index 82acb531b17b..2c12e4fd4572 100644
--- a/rust/kernel/ptr.rs
+++ b/rust/kernel/ptr.rs
@@ -11,7 +11,10 @@
};
use core::num::NonZero;
-use crate::const_assert;
+use crate::{
+ const_assert,
+ num::FromConst, //
+};
/// Type representing an alignment, which is always a power of two.
///
@@ -166,6 +169,13 @@ pub const fn mask(self) -> usize {
}
}
+impl<const V: i128> FromConst<V> for Alignment {
+ const VALUE: Self = match Alignment::new_checked(<usize as FromConst<V>>::VALUE) {
+ Some(alignment) => alignment,
+ None => panic!("constant is not a power of two"),
+ };
+}
+
/// Trait for items that can be aligned against an [`Alignment`].
pub trait Alignable: Sized {
/// Aligns `self` down to `alignment`.
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v5 2/2] gpu: nova-core: use cv! for constant casts
2026-10-13 5:58 [PATCH v5 0/2] rust: introduce cv! macro for safe const conversions of integer-like types Eliot Courtney
2026-10-13 5:58 ` [PATCH v5 1/2] rust: num: add cv! macro to create values from constant expressions Eliot Courtney
@ 2026-10-13 5:58 ` Eliot Courtney
1 sibling, 0 replies; 5+ messages in thread
From: Eliot Courtney @ 2026-10-13 5:58 UTC (permalink / raw)
To: Alexandre Courbot, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Onur Özkan, David Airlie, Simona Vetter
Cc: John Hubbard, Alistair Popple, Timur Tabi, rust-for-linux,
linux-kernel, nova-gpu, dri-devel, Eliot Courtney
The new `cv!` macro allows safe casting of constant expressions in a
const context. Update code in nova-core to use it.
Reviewed-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/falcon.rs | 9 ++--
drivers/gpu/nova-core/fb/hal/gb100.rs | 4 +-
drivers/gpu/nova-core/firmware/fwsec/bootloader.rs | 7 +--
drivers/gpu/nova-core/fsp.rs | 3 +-
drivers/gpu/nova-core/gsp/cmdq.rs | 6 +--
drivers/gpu/nova-core/gsp/fw.rs | 38 +++++++--------
drivers/gpu/nova-core/gsp/fw/commands.rs | 2 +-
drivers/gpu/nova-core/num.rs | 55 +---------------------
8 files changed, 28 insertions(+), 96 deletions(-)
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 9015de965a53..0b377bd6524a 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -28,11 +28,8 @@
driver::Bar0,
falcon::hal::LoadMethod,
gpu::Chipset,
- num::{
- self,
- FromSafeCast, //
- },
- regs,
+ num::FromSafeCast,
+ regs, //
};
pub(crate) mod fsp;
@@ -515,7 +512,7 @@ fn dma_wr(
target_mem: FalconMem,
load_offsets: FalconDmaLoadTarget,
) -> Result {
- const DMA_LEN: u32 = num::usize_into_u32::<{ MEM_BLOCK_ALIGNMENT }>();
+ const DMA_LEN: u32 = cv!(MEM_BLOCK_ALIGNMENT);
// DMA transfers can only be done in units of 256 bytes. Compute how many such transfers we
// need to perform.
diff --git a/drivers/gpu/nova-core/fb/hal/gb100.rs b/drivers/gpu/nova-core/fb/hal/gb100.rs
index 9fa094939600..612f70333c11 100644
--- a/drivers/gpu/nova-core/fb/hal/gb100.rs
+++ b/drivers/gpu/nova-core/fb/hal/gb100.rs
@@ -28,7 +28,6 @@
hal::FbHal,
regs, //
},
- num::usize_into_u32,
};
struct Gb100;
@@ -81,8 +80,7 @@ fn write_sysmem_flush_page_gb100(hshub0: Mmio<'_, regs::Hshub0Registers>, addr:
// This PMU reservation size is r570-specific.
pub(super) const fn pmu_reserved_size_gb100() -> u32 {
- usize_into_u32::<{ const_align_up(SZ_8M + SZ_16M + SZ_4K, Alignment::new::<SZ_128K>()).unwrap() }>(
- )
+ cv!(const_align_up(SZ_8M + SZ_16M + SZ_4K, Alignment::new::<SZ_128K>()).unwrap())
}
impl FbHal for Gb100 {
diff --git a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
index 1ff04bc2c7f8..68858b48390e 100644
--- a/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec/bootloader.rs
@@ -17,10 +17,7 @@
Io, //
},
prelude::*,
- ptr::{
- Alignable,
- Alignment, //
- },
+ ptr::Alignable,
sizes,
transmute::AsBytes,
};
@@ -136,7 +133,7 @@ pub(crate) fn new(
let code_size = usize::from_safe_cast(tlv.get_u32(b"CDSZ")?);
let code = blob.get(..code_size).ok_or(EINVAL)?;
let aligned_code_size = code_size
- .align_up(Alignment::new::<{ falcon::MEM_BLOCK_ALIGNMENT }>())
+ .align_up(cv!(falcon::MEM_BLOCK_ALIGNMENT))
.ok_or(EINVAL)?;
let mut ucode = KVec::with_capacity(aligned_code_size, GFP_KERNEL)?;
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index b738dcabcdef..3a4a10487f33 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -50,7 +50,6 @@
NvdmHeader,
NvdmType, //
},
- num,
regs, //
};
@@ -288,7 +287,7 @@ fn new<'a>(
};
let version = hal.cot_version();
- let size = num::usize_into_u16::<{ core::mem::size_of::<NvdmPayloadCot>() }>();
+ let size = cv!(core::mem::size_of::<NvdmPayloadCot>() => u16);
Ok(init!(Self {
header: FspMessageHeader::new(NvdmType::Cot),
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 1b2f346a237f..090e67ac9a26 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -167,7 +167,7 @@ fn read(
#[repr(C, align(0x1000))]
#[derive(Debug)]
struct MsgqData {
- data: [[u8; GSP_PAGE_SIZE]; num::u32_as_usize(MSGQ_NUM_PAGES)],
+ data: [[u8; GSP_PAGE_SIZE]; cv!(MSGQ_NUM_PAGES)],
}
// Annoyingly we are forced to use a literal to specify the alignment of
@@ -240,8 +240,8 @@ unsafe impl FromBytes for GspMem {}
impl<'a> DmaGspMem<'a> {
/// Allocate a new instance and map it for `dev`.
fn new(dev: &'a device::Device<device::Bound>) -> Result<Self> {
- const MSGQ_SIZE: u32 = num::usize_into_u32::<{ size_of::<Msgq>() }>();
- const RX_HDR_OFF: u32 = num::usize_into_u32::<{ mem::offset_of!(Msgq, rx) }>();
+ const MSGQ_SIZE: u32 = cv!(size_of::<Msgq>());
+ const RX_HDR_OFF: u32 = cv!(mem::offset_of!(Msgq, rx));
let mut gsp_mem = CoherentBox::<'_, GspMem>::zeroed(dev, GFP_KERNEL)?;
gsp_mem.cpuq.tx = MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES);
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 918a7ae809eb..1b548bbf146e 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -57,7 +57,7 @@
/// Maximum size of a single GSP message queue element in bytes.
pub(crate) const GSP_MSG_QUEUE_ELEMENT_SIZE_MAX: usize =
- num::u32_as_usize(bindings::GSP_MSG_QUEUE_ELEMENT_SIZE_MAX);
+ cv!(bindings::GSP_MSG_QUEUE_ELEMENT_SIZE_MAX);
/// Empty type to group methods related to heap parameters for running the GSP firmware.
enum GspFwHeapParams {}
@@ -110,20 +110,18 @@ pub(crate) struct LibosParams {
impl LibosParams {
/// Version 2 of the GSP LIBOS (Turing and GA100)
const LIBOS2: LibosParams = LibosParams {
- carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2),
- allowed_heap_size: num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB)
+ carveout_size: cv!(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2),
+ allowed_heap_size: cv!(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB => u64)
* u64::SZ_1M
- ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB) * u64::SZ_1M,
+ ..cv!(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB => u64) * u64::SZ_1M,
};
/// Version 3 of the GSP LIBOS (GA102+)
const LIBOS3: LibosParams = LibosParams {
- carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL),
- allowed_heap_size: num::u32_as_u64(
- bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB,
- ) * u64::SZ_1M
- ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB)
- * u64::SZ_1M,
+ carveout_size: cv!(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL),
+ allowed_heap_size: cv!(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB => u64)
+ * u64::SZ_1M
+ ..cv!(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB => u64) * u64::SZ_1M,
};
/// Returns the libos parameters corresponding to `chipset`.
@@ -682,12 +680,8 @@ fn id8(name: &str) -> u64 {
id8: id8(name),
pa: obj.dma_address(),
size: num::usize_as_u64(obj.size()),
- kind: num::u32_into_u8::<
- { bindings::LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS },
- >(),
- loc: num::u32_into_u8::<
- { bindings::LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM },
- >(),
+ kind: cv!(bindings::LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS),
+ loc: cv!(bindings::LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM),
..Zeroable::init_zeroed()
});
@@ -715,12 +709,12 @@ pub(crate) fn new(msgq_size: u32, rx_hdr_offset: u32, msg_count: u32) -> Self {
Self(bindings::msgqTxHeader {
version: 0,
size: msgq_size,
- msgSize: num::usize_into_u32::<GSP_PAGE_SIZE>(),
+ msgSize: cv!(GSP_PAGE_SIZE),
msgCount: msg_count,
writePtr: 0,
flags: 1,
rxHdrOff: rx_hdr_offset,
- entryOff: num::usize_into_u32::<GSP_PAGE_SIZE>(),
+ entryOff: cv!(GSP_PAGE_SIZE),
})
}
@@ -947,9 +941,9 @@ impl MessageQueueInitArguments {
fn new<'a, 'b>(cmdq: &'a Cmdq<'b>) -> impl Init<Self> + use<'a, 'b> {
init!(MessageQueueInitArguments {
sharedMemPhysAddr: cmdq.dma_addr,
- pageTableEntryCount: num::usize_into_u32::<{ Cmdq::NUM_PTES }>(),
- cmdQueueOffset: num::usize_as_u64(Cmdq::CMDQ_OFFSET),
- statQueueOffset: num::usize_as_u64(Cmdq::STATQ_OFFSET),
+ pageTableEntryCount: cv!(Cmdq::NUM_PTES),
+ cmdQueueOffset: u64::from_safe_cast(Cmdq::CMDQ_OFFSET),
+ statQueueOffset: u64::from_safe_cast(Cmdq::STATQ_OFFSET),
..Zeroable::init_zeroed()
})
}
@@ -969,7 +963,7 @@ impl GspAcrBootGspRmParams {
fn new(target: GspDmaTarget, wpr_meta_addr: u64) -> impl Init<Self> {
let params = init!(Self {
target: target as u32,
- gspRmDescSize: num::usize_into_u32::<{ size_of::<GspFwWprMeta>() }>(),
+ gspRmDescSize: cv!(size_of::<GspFwWprMeta>()),
gspRmDescOffset: wpr_meta_addr,
bIsGspRmBoot: 1,
wprCarveoutOffset: 0,
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
index 567c56fbbfe3..b38eb716448b 100644
--- a/drivers/gpu/nova-core/gsp/fw/commands.rs
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -82,7 +82,7 @@ pub(crate) fn new(offset: u32, value: u32) -> Self {
// We only support DWORD types for now. Support for other types
// will come later if required.
- type_: bindings::REGISTRY_TABLE_ENTRY_TYPE_DWORD as u8,
+ type_: cv!(bindings::REGISTRY_TABLE_ENTRY_TYPE_DWORD),
__bindgen_padding_0: Default::default(),
data: value,
length: 0,
diff --git a/drivers/gpu/nova-core/num.rs b/drivers/gpu/nova-core/num.rs
index f4169235bc24..3b3f1ed2dc75 100644
--- a/drivers/gpu/nova-core/num.rs
+++ b/drivers/gpu/nova-core/num.rs
@@ -96,8 +96,7 @@ pub(crate) const fn [<$from _as_ $into>](value: $from) -> $into {
///
/// 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.
+/// If you need to perform a conversion in `const` context, use [`cv!`](kernel::num::cv).
///
/// # Examples
///
@@ -164,58 +163,6 @@ fn into_safe_cast(self) -> T {
}
}
-/// 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`.
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v5 1/2] rust: num: add cv! macro to create values from constant expressions
2026-10-13 5:58 ` [PATCH v5 1/2] rust: num: add cv! macro to create values from constant expressions Eliot Courtney
@ 2026-10-13 7:17 ` Alexandre Courbot
2026-10-13 13:18 ` Miguel Ojeda
1 sibling, 0 replies; 5+ messages in thread
From: Alexandre Courbot @ 2026-10-13 7:17 UTC (permalink / raw)
To: Eliot Courtney
Cc: Yury Norov, Miguel Ojeda, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Onur Özkan, David Airlie, Simona Vetter, John Hubbard,
Alistair Popple, Timur Tabi, rust-for-linux, linux-kernel,
nova-gpu, dri-devel
On Tue Oct 13, 2026 at 2:58 PM JST, Eliot Courtney wrote:
> Currently, using NonZero/Bounded constants is quite verbose. It's
> unfortunate because it disincentivizes using it in interface boundaries.
> Introduce a macro to make it nicer to use. The macro `cv!` (for constant
> value) takes a const integer expression and widens it to i128 (at build
> time only) before passing it as a const generic value to a new trait
> `FromConst`. The value is then converted and appears in the
> associated constant `FromConst::VALUE`. The trait is implemented by
> NonZero, Bounded, and Alignment and lets values of each be constructed
> from constants without a verbose turbofish syntax.
>
> For example, `const { NonZero::new(1).unwrap() }` can be written as
> `cv!(1)`.
>
> Add the `cv!` macro to the prelude so it doesn't need to be imported
> explicitly to use it.
>
> Suggested-by: Gary Guo <gary@garyguo.net>
> Reviewed-by: Gary Guo <gary@garyguo.net>
> Acked-by: Miguel Ojeda <ojeda@kernel.org>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Acked-by: Danilo Krummrich <dakr@kernel.org>
> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
Acked-by: Alexandre Courbot <acourbot@nvidia.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v5 1/2] rust: num: add cv! macro to create values from constant expressions
2026-10-13 5:58 ` [PATCH v5 1/2] rust: num: add cv! macro to create values from constant expressions Eliot Courtney
2026-10-13 7:17 ` Alexandre Courbot
@ 2026-10-13 13:18 ` Miguel Ojeda
1 sibling, 0 replies; 5+ messages in thread
From: Miguel Ojeda @ 2026-10-13 13:18 UTC (permalink / raw)
To: Eliot Courtney
Cc: Alexandre Courbot, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Danilo Krummrich, Daniel Almeida,
Tamir Duberstein, Onur Özkan, David Airlie, Simona Vetter,
John Hubbard, Alistair Popple, Timur Tabi, rust-for-linux,
linux-kernel, nova-gpu, dri-devel
On Tue, Oct 13, 2026 at 7:58 AM Eliot Courtney <ecourtney@nvidia.com> wrote:
>
> Currently, using NonZero/Bounded constants is quite verbose. It's
> unfortunate because it disincentivizes using it in interface boundaries.
> Introduce a macro to make it nicer to use. The macro `cv!` (for constant
> value) takes a const integer expression and widens it to i128 (at build
> time only) before passing it as a const generic value to a new trait
> `FromConst`. The value is then converted and appears in the
> associated constant `FromConst::VALUE`. The trait is implemented by
> NonZero, Bounded, and Alignment and lets values of each be constructed
> from constants without a verbose turbofish syntax.
>
> For example, `const { NonZero::new(1).unwrap() }` can be written as
> `cv!(1)`.
>
> Add the `cv!` macro to the prelude so it doesn't need to be imported
> explicitly to use it.
>
> Suggested-by: Gary Guo <gary@garyguo.net>
> Reviewed-by: Gary Guo <gary@garyguo.net>
> Acked-by: Miguel Ojeda <ojeda@kernel.org>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Acked-by: Danilo Krummrich <dakr@kernel.org>
> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
Applied to `rust-next` -- thanks everyone!
[ Added a few intra-doc links. - Miguel ]
And thanks Eliot for the quick respin :)
Cheers,
Miguel
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-13 13:18 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-13 5:58 [PATCH v5 0/2] rust: introduce cv! macro for safe const conversions of integer-like types Eliot Courtney
2026-10-13 5:58 ` [PATCH v5 1/2] rust: num: add cv! macro to create values from constant expressions Eliot Courtney
2026-10-13 7:17 ` Alexandre Courbot
2026-10-13 13:18 ` Miguel Ojeda
2026-10-13 5:58 ` [PATCH v5 2/2] gpu: nova-core: use cv! for constant casts Eliot Courtney
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®