[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20260120-register-v1-4-723a1743b557@nvidia.com>
Date: Tue, 20 Jan 2026 15:17:57 +0900
From: Alexandre Courbot <acourbot@...dia.com>
To: Miguel Ojeda <ojeda@...nel.org>, Boqun Feng <boqun.feng@...il.com>,
Gary Guo <gary@...yguo.net>,
Björn Roy Baron <bjorn3_gh@...tonmail.com>,
Benno Lossin <lossin@...nel.org>, Andreas Hindborg <a.hindborg@...nel.org>,
Alice Ryhl <aliceryhl@...gle.com>, Trevor Gross <tmgross@...ch.edu>,
Danilo Krummrich <dakr@...nel.org>, Yury Norov <yury.norov@...il.com>
Cc: John Hubbard <jhubbard@...dia.com>,
Alistair Popple <apopple@...dia.com>,
Joel Fernandes <joelagnelf@...dia.com>, Timur Tabi <ttabi@...dia.com>,
Edwin Peer <epeer@...dia.com>, Eliot Courtney <ecourtney@...dia.com>,
Daniel Almeida <daniel.almeida@...labora.com>,
Dirk Behme <dirk.behme@...bosch.com>, Steven Price <steven.price@....com>,
rust-for-linux@...r.kernel.org, linux-kernel@...r.kernel.org,
Alexandre Courbot <acourbot@...dia.com>
Subject: [PATCH 4/6] rust: bitfield: Add KUNIT tests for bitfield
From: Joel Fernandes <joelagnelf@...dia.com>
Add KUNIT tests to make sure the macro is working correctly.
Signed-off-by: Joel Fernandes <joelagnelf@...dia.com>
[acourbot: update code to latest bitfield! macro.]
Signed-off-by: Alexandre Courbot <acourbot@...dia.com>
---
rust/kernel/bitfield.rs | 318 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 318 insertions(+)
diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
index 2926ab802227..a1505816a590 100644
--- a/rust/kernel/bitfield.rs
+++ b/rust/kernel/bitfield.rs
@@ -501,3 +501,321 @@ fn default() -> Self {
}
};
}
+
+#[::kernel::macros::kunit_tests(kernel_bitfield)]
+mod tests {
+ use core::convert::TryFrom;
+
+ use kernel::num::Bounded;
+
+ // Enum types for testing => and ?=> conversions
+ #[derive(Debug, Default, Clone, Copy, PartialEq)]
+ enum MemoryType {
+ #[default]
+ Unmapped = 0,
+ Normal = 1,
+ Device = 2,
+ Reserved = 3,
+ }
+
+ impl TryFrom<Bounded<u64, 4>> for MemoryType {
+ type Error = u64;
+ fn try_from(value: Bounded<u64, 4>) -> Result<Self, Self::Error> {
+ match value.get() {
+ 0 => Ok(MemoryType::Unmapped),
+ 1 => Ok(MemoryType::Normal),
+ 2 => Ok(MemoryType::Device),
+ 3 => Ok(MemoryType::Reserved),
+ _ => Err(value.get()),
+ }
+ }
+ }
+
+ impl From<MemoryType> for Bounded<u64, 4> {
+ fn from(mt: MemoryType) -> Bounded<u64, 4> {
+ Bounded::from_expr(mt as u64)
+ }
+ }
+
+ #[derive(Debug, Default, Clone, Copy, PartialEq)]
+ enum Priority {
+ #[default]
+ Low = 0,
+ Medium = 1,
+ High = 2,
+ Critical = 3,
+ }
+
+ impl From<Bounded<u16, 2>> for Priority {
+ fn from(value: Bounded<u16, 2>) -> Self {
+ match value & 0x3 {
+ 0 => Priority::Low,
+ 1 => Priority::Medium,
+ 2 => Priority::High,
+ _ => Priority::Critical,
+ }
+ }
+ }
+
+ impl From<Priority> for Bounded<u16, 2> {
+ fn from(p: Priority) -> Bounded<u16, 2> {
+ Bounded::from_expr(p as u16)
+ }
+ }
+
+ bitfield! {
+ struct TestPageTableEntry(u64) {
+ 0:0 present;
+ 1:1 writable;
+ 11:9 available;
+ 15:12 mem_type ?=> MemoryType;
+ 51:16 pfn;
+ 61:52 available2;
+ }
+ }
+
+ bitfield! {
+ struct TestControlRegister(u16) {
+ 0:0 enable;
+ 3:1 mode;
+ 5:4 priority => Priority;
+ 7:4 priority_nibble;
+ 15:8 channel;
+ }
+ }
+
+ bitfield! {
+ struct TestStatusRegister(u8) {
+ 0:0 ready;
+ 1:1 error;
+ 3:2 state;
+ 7:4 reserved;
+ 7:0 full_byte; // For entire register
+ }
+ }
+
+ #[test]
+ fn test_single_bits() {
+ let mut pte = TestPageTableEntry::default();
+
+ assert!(!pte.present().as_bool());
+ assert!(!pte.writable().as_bool());
+ assert_eq!(u64::from(pte), 0x0);
+
+ pte = pte.set_present(true);
+ assert!(pte.present().as_bool());
+ assert_eq!(u64::from(pte), 0x1);
+
+ pte = pte.set_writable(true);
+ assert!(pte.writable().as_bool());
+ assert_eq!(u64::from(pte), 0x3);
+
+ pte = pte.set_writable(false);
+ assert!(!pte.writable().as_bool());
+ assert_eq!(u64::from(pte), 0x1);
+
+ assert_eq!(pte.available(), 0);
+ pte = pte.set_available(Bounded::<u64, 3>::new::<0x5>());
+ assert_eq!(pte.available(), 0x5);
+ assert_eq!(u64::from(pte), 0xA01);
+ }
+
+ #[test]
+ fn test_range_fields() {
+ let mut pte = TestPageTableEntry::default();
+ assert_eq!(u64::from(pte), 0x0);
+
+ pte = pte.set_pfn(Bounded::<u64, 36>::new::<0x123456>());
+ assert_eq!(pte.pfn(), 0x123456);
+ assert_eq!(u64::from(pte), 0x1234560000);
+
+ pte = pte.set_available(Bounded::<u64, 3>::new::<0x7>());
+ assert_eq!(pte.available(), 0x7);
+ assert_eq!(u64::from(pte), 0x1234560E00);
+
+ pte = pte.set_available2(Bounded::<u64, 10>::new::<0x3FF>());
+ assert_eq!(pte.available2(), 0x3FF);
+ assert_eq!(u64::from(pte), 0x3FF0_0012_3456_0E00u64);
+
+ // Test TryFrom with ?=> for MemoryType
+ pte = pte.set_mem_type(MemoryType::Device);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Device));
+ assert_eq!(u64::from(pte), 0x3FF0_0012_3456_2E00u64);
+
+ pte = pte.set_mem_type(MemoryType::Normal);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Normal));
+ assert_eq!(u64::from(pte), 0x3FF0_0012_3456_1E00u64);
+
+ // Test all valid values for mem_type
+ pte = pte.set_mem_type(MemoryType::Reserved);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved));
+ assert_eq!(u64::from(pte), 0x3FF0_0012_3456_3E00u64);
+
+ // Test failure case using mem_type field which has 4 bits (0-15)
+ // MemoryType only handles 0-3, so values 4-15 should return Err
+ let mut raw = pte.into();
+ // Set bits 15:12 to 7 (invalid for MemoryType)
+ raw = (raw & !::kernel::bits::genmask_u64(12..=15)) | (0x7 << 12);
+ let invalid_pte = TestPageTableEntry(raw);
+ // Should return Err with the invalid value
+ assert_eq!(invalid_pte.mem_type(), Err(0x7));
+
+ // Test a valid value after testing invalid to ensure both cases work
+ // Set bits 15:12 to 2 (valid: Device)
+ raw = (raw & !::kernel::bits::genmask_u64(12..=15)) | (0x2 << 12);
+ let valid_pte = TestPageTableEntry(raw);
+ assert_eq!(valid_pte.mem_type(), Ok(MemoryType::Device));
+
+ const MAX_PFN: u64 = ::kernel::bits::genmask_u64(0..=35);
+ pte = pte.set_pfn(Bounded::<u64, 36>::new::<{ MAX_PFN }>());
+ assert_eq!(pte.pfn(), MAX_PFN);
+ }
+
+ #[test]
+ fn test_builder_pattern() {
+ let pte = TestPageTableEntry::default()
+ .set_present(true)
+ .set_writable(true)
+ .set_available(Bounded::<u64, 3>::new::<0x7>())
+ .set_pfn(Bounded::<u64, 36>::new::<0xABCDEF>())
+ .set_mem_type(MemoryType::Reserved)
+ .set_available2(Bounded::<u64, 10>::new::<0x3FF>());
+
+ assert!(pte.present().as_bool());
+ assert!(pte.writable().as_bool());
+ assert_eq!(pte.available(), 0x7);
+ assert_eq!(pte.pfn(), 0xABCDEF);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved));
+ assert_eq!(pte.available2(), 0x3FF);
+ }
+
+ #[test]
+ fn test_raw_operations() {
+ let raw_value = 0x3FF0000031233E03u64;
+
+ let pte = TestPageTableEntry(raw_value);
+ assert_eq!(u64::from(pte), raw_value);
+
+ assert!(pte.present().as_bool());
+ assert!(pte.writable().as_bool());
+ assert_eq!(pte.available(), 0x7);
+ assert_eq!(pte.pfn(), 0x3123);
+ assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved));
+ assert_eq!(pte.available2(), 0x3FF);
+
+ // Test using direct constructor syntax TestStruct(value)
+ let pte2 = TestPageTableEntry(raw_value);
+ assert_eq!(u64::from(pte2), raw_value);
+ }
+
+ #[test]
+ fn test_u16_bitfield() {
+ let mut ctrl = TestControlRegister::default();
+
+ assert!(!ctrl.enable().as_bool());
+ assert_eq!(ctrl.mode(), 0);
+ assert_eq!(ctrl.priority(), Priority::Low);
+ assert_eq!(ctrl.priority_nibble(), 0);
+ assert_eq!(ctrl.channel(), 0);
+
+ ctrl = ctrl.set_enable(true);
+ assert!(ctrl.enable().as_bool());
+
+ ctrl = ctrl.set_mode(Bounded::<u16, 3>::new::<0x5>());
+ assert_eq!(ctrl.mode(), 0x5);
+
+ // Test From conversion with =>
+ ctrl = ctrl.set_priority(Priority::High);
+ assert_eq!(ctrl.priority(), Priority::High);
+ assert_eq!(ctrl.priority_nibble(), 0x2); // High = 2 in bits 5:4
+
+ ctrl = ctrl.set_channel(0xAB);
+ assert_eq!(ctrl.channel(), 0xAB);
+
+ // Test overlapping fields
+ ctrl = ctrl.set_priority_nibble(Bounded::<u16, 4>::new::<0xF>());
+ assert_eq!(ctrl.priority_nibble(), 0xF);
+ assert_eq!(ctrl.priority(), Priority::Critical); // bits 5:4 = 0x3
+
+ let ctrl2 = TestControlRegister::default()
+ .set_enable(true)
+ .set_mode(Bounded::<u16, 3>::new::<0x3>())
+ .set_priority(Priority::Medium)
+ .set_channel(0x42);
+
+ assert!(ctrl2.enable().as_bool());
+ assert_eq!(ctrl2.mode(), 0x3);
+ assert_eq!(ctrl2.priority(), Priority::Medium);
+ assert_eq!(ctrl2.channel(), 0x42);
+
+ let raw_value: u16 = 0x4217;
+ let ctrl3 = TestControlRegister(raw_value);
+ assert_eq!(u16::from(ctrl3), raw_value);
+ assert!(ctrl3.enable().as_bool());
+ assert_eq!(ctrl3.priority(), Priority::Medium);
+ assert_eq!(ctrl3.priority_nibble(), 0x1);
+ assert_eq!(ctrl3.channel(), 0x42);
+ }
+
+ #[test]
+ fn test_u8_bitfield() {
+ let mut status = TestStatusRegister::default();
+
+ assert!(!status.ready().as_bool());
+ assert!(!status.error().as_bool());
+ assert_eq!(status.state(), 0);
+ assert_eq!(status.reserved(), 0);
+ assert_eq!(status.full_byte(), 0);
+
+ status = status.set_ready(true);
+ assert!(status.ready().as_bool());
+ assert_eq!(status.full_byte(), 0x01);
+
+ status = status.set_error(true);
+ assert!(status.error().as_bool());
+ assert_eq!(status.full_byte(), 0x03);
+
+ status = status.set_state(Bounded::<u8, 2>::new::<0x3>());
+ assert_eq!(status.state(), 0x3);
+ assert_eq!(status.full_byte(), 0x0F);
+
+ status = status.set_reserved(Bounded::<u8, 4>::new::<0xA>());
+ assert_eq!(status.reserved(), 0xA);
+ assert_eq!(status.full_byte(), 0xAF);
+
+ // Test overlapping field
+ status = status.set_full_byte(0x55);
+ assert_eq!(status.full_byte(), 0x55);
+ assert!(status.ready().as_bool());
+ assert!(!status.error().as_bool());
+ assert_eq!(status.state(), 0x1);
+ assert_eq!(status.reserved(), 0x5);
+
+ let status2 = TestStatusRegister::default()
+ .set_ready(true)
+ .set_state(Bounded::<u8, 2>::new::<0x2>())
+ .set_reserved(Bounded::<u8, 4>::new::<0x5>());
+
+ assert!(status2.ready().as_bool());
+ assert!(!status2.error().as_bool());
+ assert_eq!(status2.state(), 0x2);
+ assert_eq!(status2.reserved(), 0x5);
+ assert_eq!(status2.full_byte(), 0x59);
+
+ let raw_value: u8 = 0x59;
+ let status3 = TestStatusRegister(raw_value);
+ assert_eq!(u8::from(status3), raw_value);
+ assert!(status3.ready().as_bool());
+ assert!(!status3.error().as_bool());
+ assert_eq!(status3.state(), 0x2);
+ assert_eq!(status3.reserved(), 0x5);
+ assert_eq!(status3.full_byte(), 0x59);
+
+ let status4 = TestStatusRegister(0xFF);
+ assert!(status4.ready().as_bool());
+ assert!(status4.error().as_bool());
+ assert_eq!(status4.state(), 0x3);
+ assert_eq!(status4.reserved(), 0xF);
+ assert_eq!(status4.full_byte(), 0xFF);
+ }
+}
--
2.52.0
Powered by blists - more mailing lists