[RFC PATCH 0/2] rust: bounded integer types and use in register macro

Alexandre Courbot
2025-10-01T15:03:53Z

This is a RFC/PoC for an idea I submitted a bit earlier [1] regarding
how to handle truncated bits in the register/bitfield macro field
setters.

Currently, the register macro allows to define fields that are shorter
than the primitive type used to set them. For instance, in the following
register:

register!(NV_PFALCON_FALCON_DMATRFBASE1 @ PFalconBase[0x00000128] {
8:0 base as u16;
});

The `base` field is 9 bits long, but is set using the following
setter method:

fn set_base(self, value: u16) -> Self

which discards bits of `value` higher than 9 without any error or
warning if some happen to be set. And so you happily start a DMA
transfer from the wrong base address...

While this behavior is not UB, this can also be a source of bugs.
Several ideas have been submitted to not let these unnoticed, including
making the register setters fallible, or panicking or printing a warning
whenever extra bits are discarded [2]. These solutions are either too
risky (like panicking), or add extra code and checks on what can be a
critical path as registers are commonly accessed in interrupt handlers.

In pure Rust fashion, we would prefer to guarantee at compile-time,
whenever possible, that no bits are discarded when setting register
fields with a non-round number of bits.

This PoC proposes a possible solution for that. It introduces a new
`BoundedInt` type that wraps a primitive integer type and, using a const
generic, limits the number of bits that can actually be used within it.
This is similar in spirit to `NonZero` or `Alignment` in that it
provides a static guarantee that the value it contains is limited to a
certain subset of the possible values of its primitive type.

Instances of `BoundedInt` can be constructed infallibly when the
compiler is able to guarantee that the passed value fits within the
allowed bounds, or fallibly using `try_from(primitive)`.

This type is then used by the register/bitfield macros to let the fields
getter and (more importantly) setter methods work with the exact number
of bits they can handle. For instance, the above method would become:

fn set_base(self, value: BoundedInt<u32, 9>) -> Self

which guarantees that no bits are ever discarded by the setter, since
the type of `value` carries an invariant that only the 9 lowest bits can
ever be set.

It is then the responsibility of the caller to build the adequate
`BoundedInt`, which very often can be done infallibly, but all the cases
that require a fallible operation are cases that the caller should have
checked anyway (lest you beam out the wrong memory region on your DMA
transfer!).

As mentioned, this is a short and early PoC. The BoundedInt type is
minimal, and defined only for `u32` (as this is the type currently used
by the register macro). It should be defined for all relevant types
using a macro, and extended to support more operations.

Also, instead of replacing the current getter/setter methods of the
register macro, this RFC adds two new ones suffixed with `_bounded` to
demonstrate the use of this new type, and change a couple of setter call
sites in nova-core's `falcon.rs` to showcase sites that can be
statically constructed, and a few that need to be dynamically checked.

Note that this is not designed to replace the other feature of the
register macro ensuring we are in control of all our bits, namely the
conversion of the field from/to enum types with only valid values. Both
features can happily work together (and the enum is preferred when
applicable).

Another side effect of adopting this would be that the bitfield
definitions could be simplified, since their type can now be
automatically inferred. This means that

8:0 base as u16;

could become simply

8:0 base;

And the getter/setters would work with a `BoundedInt<u32, 9>` (provided
the bitfield type is backed by a `u32`).

This is very early work, but feedback would be welcome. I expected this
to be more constraining to use, but so far the disruption to user code
looks minimal, and always justified when it occurs.

I intuitively suspect that such bounded integer types could also be
useful outside of bitfields. If other use-cases come to mind, please let
me know! :)

For convenience, this PoC is based on drm-rust-next. If we decide to
proceed with it, we would do it after the patchset extracting and moving
the bitfield logic [3] lands, as the two would conflict heavily.

[1] https://lore.kernel.org/rust-for-linux/DD5D59FH4JTT.2G5WEXF3RBCQJ@nvidia.com/
[2] https://lore.kernel.org/rust-for-linux/DD68A3TZD9CV.2CL7R7K4UAICU@kernel.org/T/#mcbf44a779389c3664d885224ca422130b4cec5a9
[3] https://lore.kernel.org/rust-for-linux/DD68A3TZD9CV.2CL7R7K4UAICU@kernel.org/T/

To: Joel Fernandes <joelagnelf@nvidia.com>
To: Yury Norov <yury.norov@gmail.com>
To: Danilo Krummrich <dakr@kernel.org>
To: Miguel Ojeda <ojeda@kernel.org>
Cc: rust-for-linux@vger.kernel.org
Cc: linux-kernel@vger.kernel.org

Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
Alexandre Courbot (2):
rust: kernel: add bounded integer types
gpu: nova-core: demonstrate use of BoundedInt

drivers/gpu/nova-core/falcon.rs | 14 ++--
drivers/gpu/nova-core/regs/macros.rs | 34 ++++++++++
rust/kernel/lib.rs | 1 +
rust/kernel/num.rs | 120 +++++++++++++++++++++++++++++++++++
4 files changed, 164 insertions(+), 5 deletions(-)
---
base-commit: 299eb32863e584cfff7c6b667c3e92ae7d4d2bf9
change-id: 20251001-bounded_ints-1d0457d9ae26

Best regards,
--
Alexandre Courbot <acourbot@nvidia.com>