Add a minimal bitfield library for defining in Rust structures (called
bitstruct), similar in concept to bit fields in C structs. This will be used
for defining page table entries and other structures in nova-core.
Signed-off-by: Joel Fernandes <joelagn...@nvidia.com>
---
drivers/gpu/nova-core/bitstruct.rs | 149 +++++++++++++++++++++++++++++
drivers/gpu/nova-core/nova_core.rs | 1 +
2 files changed, 150 insertions(+)
create mode 100644 drivers/gpu/nova-core/bitstruct.rs
diff --git a/drivers/gpu/nova-core/bitstruct.rs
b/drivers/gpu/nova-core/bitstruct.rs
new file mode 100644
index 000000000000..661a75da0a9c
--- /dev/null
+++ b/drivers/gpu/nova-core/bitstruct.rs
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// bitstruct.rs — C-style library for bitfield-packed Rust structures
+//
+// A library that provides support for defining bit fields in Rust
+// structures to circumvent lack of native language support for this.
+//
+// Similar usage syntax to the register! macro.
+
+use kernel::prelude::*;
+
+/// Macro for defining bitfield-packed structures in Rust.
+/// The size of the underlying storage type is specified with #[repr(TYPE)].
+///
+/// # Example (just for illustration)
+/// ```rust
+/// bitstruct! {
+/// #[repr(u64)]
+/// pub struct PageTableEntry {
+/// 0:0 present as bool,
+/// 1:1 writable as bool,
+/// 11:9 available as u8,
+/// 51:12 pfn as u64,
+/// 62:52 available2 as u16,
+/// 63:63 nx as bool,
+/// }
+/// }
+/// ```
+///
+/// This generates a struct with methods:
+/// - Constructor: `default()` sets all bits to zero.
+/// - Field accessors: `present()`, `pfn()`, etc.
+/// - Field setters: `set_present()`, `set_pfn()`, etc.
+/// - Builder methods: `with_present()`, `with_pfn()`, etc.
+/// - Raw conversion: `from_raw()`, `into_raw()`
+#[allow(unused_macros)]
+macro_rules! bitstruct {
+ (
+ #[repr($storage:ty)]
+ $vis:vis struct $name:ident {
+ $(
+ $hi:literal : $lo:literal $field:ident as $field_type:tt
+ ),* $(,)?
+ }
+ ) => {
+ #[repr(transparent)]
+ #[derive(Copy, Clone, Default)]
+ $vis struct $name($storage);
+
+ impl $name {
+ /// Create from raw value
+ #[inline(always)]
+ $vis const fn from_raw(val: $storage) -> Self {
+ Self(val)
+ }
+
+ /// Get raw value
+ #[inline(always)]
+ $vis const fn into_raw(self) -> $storage {
+ self.0
+ }
+ }
+
+ impl core::fmt::Debug for $name {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) ->
core::fmt::Result {
+ write!(f, "{}({:#x})", stringify!($name), self.0)
+ }
+ }
+
+ // Generate all field methods
+ $(
+ bitstruct_field_impl!($vis, $name, $storage, $hi, $lo, $field as
$field_type);
+ )*
+ };
+}
+
+/// Helper to calculate mask for bit fields
+#[allow(unused_macros)]
+macro_rules! bitstruct_mask {
+ ($hi:literal, $lo:literal, $storage:ty) => {{
+ let width = ($hi - $lo + 1) as usize;
+ let storage_bits = 8 * core::mem::size_of::<$storage>();
+ if width >= storage_bits {
+ <$storage>::MAX
+ } else {
+ ((1 as $storage) << width) - 1
+ }
+ }};
+}
+
+#[allow(unused_macros)]
+macro_rules! bitstruct_field_impl {
+ ($vis:vis, $struct_name:ident, $storage:ty, $hi:literal, $lo:literal,
$field:ident as $field_type:tt) => {
+ impl $struct_name {
+ #[inline(always)]
+ $vis const fn $field(&self) -> $field_type {
+ let field_val = (self.0 >> $lo) & bitstruct_mask!($hi, $lo,
$storage);
+ bitstruct_cast_value!(field_val, $field_type)
+ }
+ }
+ bitstruct_make_setters!($vis, $struct_name, $storage, $hi, $lo,
$field, $field_type);
+ };
+}
+
+/// Helper macro to convert extracted value to target type
+///
+/// Special handling for bool types is required because the `as` keyword
+/// cannot be used to convert to bool in Rust. For bool fields, we check
+/// if the extracted value is non-zero. For all other types, we use the
+/// standard `as` conversion.
+#[allow(unused_macros)]
+macro_rules! bitstruct_cast_value {
+ ($field_val:expr, bool) => {
+ $field_val != 0
+ };
+ ($field_val:expr, $field_type:tt) => {
+ $field_val as $field_type
+ };
+}
+
+#[allow(unused_macros)]
+macro_rules! bitstruct_write_bits {
+ ($raw:expr, $hi:literal, $lo:literal, $val:expr, $storage:ty) => {{
+ let mask = bitstruct_mask!($hi, $lo, $storage);
+ ($raw & !(mask << $lo)) | ((($val as $storage) & mask) << $lo)
+ }};
+}
+
+#[allow(unused_macros)]
+macro_rules! bitstruct_make_setters {
+ ($vis:vis, $struct_name:ident, $storage:ty, $hi:literal, $lo:literal,
$field:ident, $field_type:tt) => {
+ ::kernel::macros::paste! {
+ impl $struct_name {
+ #[inline(always)]
+ #[allow(dead_code)]
+ $vis fn [<set_ $field>](&mut self, val: $field_type) {
+ self.0 = bitstruct_write_bits!(self.0, $hi, $lo, val,
$storage);
+ }
+
+ #[inline(always)]
+ #[allow(dead_code)]
+ $vis const fn [<with_ $field>](mut self, val: $field_type) ->
Self {
+ self.0 = bitstruct_write_bits!(self.0, $hi, $lo, val,
$storage);
+ self
+ }
+ }
+ }
+ };
+}
diff --git a/drivers/gpu/nova-core/nova_core.rs
b/drivers/gpu/nova-core/nova_core.rs
index cb2bbb30cba1..54505cad4a73 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -2,6 +2,7 @@
//! Nova Core GPU Driver
+mod bitstruct;
mod dma;
mod driver;
mod falcon;