Skip to content

Commit 7f50274

Browse files
joelagnelojeda
authored andcommitted
rust: bitfield: Add KUnit tests for bitfield
Add KUnit tests to make sure the macro is working correctly. The unit tests are put behind the new `RUST_BITFIELD_KUNIT_TEST` Kconfig option. Acked-by: Danilo Krummrich <dakr@kernel.org> Reviewed-by: Eliot Courtney <ecourtney@nvidia.com> Signed-off-by: Joel Fernandes <joelagnelf@nvidia.com> [acourbot: - Use a consistent test axis where each test focuses on a single thing. - Rename members to generic name including range for readability. - Add test exercising `try_with`. - Add test checking that unallocated bits are left untouched. ] Co-developed-by: Alexandre Courbot <acourbot@nvidia.com> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Yury Norov <ynorov@nvidia.com> Link: https://patch.msgid.link/20260606-bitfield-v5-2-b92188820914@nvidia.com [ Prefixed test suite name with `rust_` as mentioned. Markdown-formatted a few comments with Markdown. - Miguel ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
1 parent b7b8b4c commit 7f50274

2 files changed

Lines changed: 324 additions & 0 deletions

File tree

‎rust/kernel/Kconfig.test‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,14 @@ config RUST_ATOMICS_KUNIT_TEST
7373

7474
If unsure, say N.
7575

76+
config RUST_BITFIELD_KUNIT_TEST
77+
bool "KUnit tests for the Rust `bitfield!` macro" if !KUNIT_ALL_TESTS
78+
default KUNIT_ALL_TESTS
79+
help
80+
This option enables KUnit tests for the Rust `bitfield!` macro.
81+
These are only for development and testing, not for regular
82+
kernel use cases.
83+
84+
If unsure, say N.
85+
7686
endif

‎rust/kernel/bitfield.rs‎

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,3 +546,317 @@ macro_rules! bitfield {
546546
}
547547
};
548548
}
549+
550+
#[cfg(CONFIG_RUST_BITFIELD_KUNIT_TEST)]
551+
#[::kernel::macros::kunit_tests(rust_kernel_bitfield)]
552+
mod tests {
553+
use core::convert::TryFrom;
554+
555+
use pin_init::Zeroable;
556+
557+
use kernel::num::Bounded;
558+
559+
// Enum types for testing `=>` and `?=>` conversions.
560+
561+
#[derive(Debug, Clone, Copy, PartialEq)]
562+
enum MemoryType {
563+
Unmapped = 0,
564+
Normal = 1,
565+
Device = 2,
566+
Reserved = 3,
567+
}
568+
569+
impl TryFrom<Bounded<u64, 4>> for MemoryType {
570+
type Error = u64;
571+
fn try_from(value: Bounded<u64, 4>) -> Result<Self, Self::Error> {
572+
match value.get() {
573+
0 => Ok(MemoryType::Unmapped),
574+
1 => Ok(MemoryType::Normal),
575+
2 => Ok(MemoryType::Device),
576+
3 => Ok(MemoryType::Reserved),
577+
_ => Err(value.get()),
578+
}
579+
}
580+
}
581+
582+
impl From<MemoryType> for Bounded<u64, 4> {
583+
fn from(mt: MemoryType) -> Bounded<u64, 4> {
584+
Bounded::from_expr(mt as u64)
585+
}
586+
}
587+
588+
#[derive(Debug, Clone, Copy, PartialEq)]
589+
enum Priority {
590+
Low = 0,
591+
Medium = 1,
592+
High = 2,
593+
Critical = 3,
594+
}
595+
596+
impl From<Bounded<u16, 2>> for Priority {
597+
fn from(value: Bounded<u16, 2>) -> Self {
598+
match value & 0x3 {
599+
0 => Priority::Low,
600+
1 => Priority::Medium,
601+
2 => Priority::High,
602+
_ => Priority::Critical,
603+
}
604+
}
605+
}
606+
607+
impl From<Priority> for Bounded<u16, 2> {
608+
fn from(p: Priority) -> Bounded<u16, 2> {
609+
Bounded::from_expr(p as u16)
610+
}
611+
}
612+
613+
bitfield! {
614+
struct TestU64(u64) {
615+
63:63 field_63;
616+
61:52 field_61_52;
617+
51:16 field_51_16;
618+
15:12 field_15_12 ?=> MemoryType;
619+
11:9 field_11_9;
620+
1:1 field_1;
621+
0:0 field_0;
622+
}
623+
}
624+
625+
bitfield! {
626+
struct TestU16(u16) {
627+
15:8 field_15_8;
628+
7:4 field_7_4; // Partial overlap with `field_5_4`.
629+
5:4 field_5_4 => Priority;
630+
3:1 field_3_1;
631+
0:0 field_0;
632+
}
633+
}
634+
635+
bitfield! {
636+
struct TestU8(u8) {
637+
7:0 field_7_0; // Full byte overlap.
638+
7:4 field_7_4;
639+
3:2 field_3_2;
640+
1:1 field_1;
641+
0:0 field_0;
642+
}
643+
}
644+
645+
// Single and multi-bit fields basic access.
646+
#[test]
647+
fn test_basic_access() {
648+
// `TestU64`.
649+
let mut val = TestU64::zeroed();
650+
assert_eq!(val.into_raw(), 0x0);
651+
652+
val = val.with_field_0(true);
653+
assert!(val.field_0().into_bool());
654+
assert_eq!(val.into_raw(), 0x1);
655+
656+
val = val.with_field_1(true);
657+
assert!(val.field_1().into_bool());
658+
val = val.with_field_1(false);
659+
assert!(!val.field_1().into_bool());
660+
assert_eq!(val.into_raw(), 0x1);
661+
662+
val = val.with_const_field_11_9::<0x5>();
663+
assert_eq!(val.field_11_9(), 0x5);
664+
assert_eq!(val.into_raw(), 0xA01);
665+
666+
val = val.with_const_field_51_16::<0x123456>();
667+
assert_eq!(val.field_51_16(), 0x123456);
668+
assert_eq!(val.into_raw(), 0x0012_3456_0A01);
669+
670+
const MAX_FIELD_51_16: u64 = ::kernel::bits::genmask_u64(0..=35);
671+
val = val.with_const_field_51_16::<{ MAX_FIELD_51_16 }>();
672+
assert_eq!(val.field_51_16(), MAX_FIELD_51_16);
673+
674+
val = val.with_const_field_61_52::<0x3FF>();
675+
assert_eq!(val.field_61_52(), 0x3FF);
676+
677+
val = val.with_field_63(true);
678+
assert!(val.field_63().into_bool());
679+
680+
// `TestU16`.
681+
let mut val = TestU16::zeroed();
682+
assert_eq!(val.into_raw(), 0x0);
683+
684+
val = val.with_field_0(true);
685+
assert!(val.field_0().into_bool());
686+
assert_eq!(val.into_raw(), 0x1);
687+
688+
val = val.with_const_field_3_1::<0x5>();
689+
assert_eq!(val.field_3_1(), 0x5);
690+
assert_eq!(val.into_raw(), 0xB);
691+
692+
val = val.with_const_field_7_4::<0xA>();
693+
assert_eq!(val.field_7_4(), 0xA);
694+
assert_eq!(val.into_raw(), 0xAB);
695+
696+
val = val.with_const_field_15_8::<0x42>();
697+
assert_eq!(val.field_15_8(), 0x42);
698+
assert_eq!(val.into_raw(), 0x42AB);
699+
700+
// `TestU8`.
701+
let mut val = TestU8::zeroed();
702+
assert_eq!(val.into_raw(), 0x0);
703+
704+
val = val.with_field_0(true);
705+
assert!(val.field_0().into_bool());
706+
assert_eq!(val.into_raw(), 0x1);
707+
708+
val = val.with_field_1(true);
709+
assert!(val.field_1().into_bool());
710+
assert_eq!(val.into_raw(), 0x3);
711+
712+
val = val.with_const_field_3_2::<0x3>();
713+
assert_eq!(val.field_3_2(), 0x3);
714+
assert_eq!(val.into_raw(), 0xF);
715+
716+
val = val.with_const_field_7_4::<0xA>();
717+
assert_eq!(val.field_7_4(), 0xA);
718+
assert_eq!(val.into_raw(), 0xAF);
719+
}
720+
721+
// `=>` infallible conversion.
722+
#[test]
723+
fn test_infallible_conversion() {
724+
let mut val = TestU16::zeroed();
725+
726+
val = val.with_field_5_4(Priority::Low);
727+
assert_eq!(val.field_5_4(), Priority::Low);
728+
assert_eq!(val.into_raw() & 0x30, 0x00);
729+
730+
val = val.with_field_5_4(Priority::Medium);
731+
assert_eq!(val.field_5_4(), Priority::Medium);
732+
assert_eq!(val.into_raw() & 0x30, 0x10);
733+
734+
val = val.with_field_5_4(Priority::High);
735+
assert_eq!(val.field_5_4(), Priority::High);
736+
assert_eq!(val.into_raw() & 0x30, 0x20);
737+
738+
val = val.with_field_5_4(Priority::Critical);
739+
assert_eq!(val.field_5_4(), Priority::Critical);
740+
assert_eq!(val.into_raw() & 0x30, 0x30);
741+
}
742+
743+
// `?=>` fallible conversion.
744+
#[test]
745+
fn test_fallible_conversion() {
746+
let mut val = TestU64::zeroed();
747+
748+
val = val.with_field_15_12(MemoryType::Unmapped);
749+
assert_eq!(val.field_15_12(), Ok(MemoryType::Unmapped));
750+
val = val.with_field_15_12(MemoryType::Normal);
751+
assert_eq!(val.field_15_12(), Ok(MemoryType::Normal));
752+
val = val.with_field_15_12(MemoryType::Device);
753+
assert_eq!(val.field_15_12(), Ok(MemoryType::Device));
754+
val = val.with_field_15_12(MemoryType::Reserved);
755+
assert_eq!(val.field_15_12(), Ok(MemoryType::Reserved));
756+
757+
// `field_15_12` is 4 bits wide (0-15); `MemoryType` only covers 0-3, so 4-15 return `Err`.
758+
let raw = (val.into_raw() & !::kernel::bits::genmask_u64(12..=15)) | (0x7 << 12);
759+
assert_eq!(TestU64::from_raw(raw).field_15_12(), Err(0x7));
760+
}
761+
762+
// Test that setting an overlapping field affects the overlapped one as expected.
763+
#[test]
764+
fn test_overlapping_fields() {
765+
let mut val = TestU16::zeroed();
766+
767+
val = val.with_field_5_4(Priority::High); // High == 2 == 0b10.
768+
assert_eq!(val.field_5_4(), Priority::High);
769+
assert_eq!(val.field_7_4(), 0x2); // Bits 7:6 == 0, bits 5:4 == 0b10.
770+
771+
val = val.with_const_field_7_4::<0xF>();
772+
assert_eq!(val.field_7_4(), 0xF);
773+
assert_eq!(val.field_5_4(), Priority::Critical); // Bits 5:4 == 0b11.
774+
775+
// `field_7_0` should encompass all other fields.
776+
let mut val = TestU8::zeroed()
777+
.with_field_0(true)
778+
.with_field_1(true)
779+
.with_const_field_3_2::<0x3>()
780+
.with_const_field_7_4::<0xA>();
781+
assert_eq!(val.into_raw(), 0xAF);
782+
783+
val = val.with_field_7_0(0x55);
784+
assert_eq!(val.field_7_0(), 0x55);
785+
assert!(val.field_0().into_bool());
786+
assert!(!val.field_1().into_bool());
787+
assert_eq!(val.field_3_2(), 0x1);
788+
assert_eq!(val.field_7_4(), 0x5);
789+
}
790+
791+
// Checks that bits not mapped to any field are left untouched.
792+
#[test]
793+
fn test_unallocated_bits() {
794+
let gap_bits = (1u64 << 62) | 0x1FC;
795+
796+
let set_all_fields = |val: TestU64| {
797+
val.with_field_63(true)
798+
.with_const_field_61_52::<0x155>()
799+
.with_const_field_51_16::<0x123456>()
800+
.with_field_15_12(MemoryType::Device)
801+
.with_const_field_11_9::<0x5>()
802+
.with_field_1(true)
803+
.with_field_0(true)
804+
};
805+
806+
// Gap bits to 0.
807+
let val = set_all_fields(TestU64::from_raw(0));
808+
assert_eq!(val.into_raw() & gap_bits, 0);
809+
810+
// Gap bits to 1.
811+
let val = set_all_fields(TestU64::from_raw(gap_bits));
812+
assert_eq!(val.into_raw() & gap_bits, gap_bits);
813+
}
814+
815+
#[test]
816+
fn test_try_with() {
817+
let val = TestU64::zeroed().try_with_field_51_16(0x123456).unwrap();
818+
assert_eq!(val.field_51_16(), 0x123456);
819+
820+
let err = TestU64::zeroed().try_with_field_51_16(u64::MAX);
821+
assert_eq!(err, Err(::kernel::error::code::EOVERFLOW));
822+
823+
let val = TestU64::zeroed()
824+
.try_with_field_51_16(0xABCDEF)
825+
.and_then(|p| p.try_with_field_0(1))
826+
.unwrap();
827+
assert_eq!(val.field_51_16(), 0xABCDEF);
828+
assert!(val.field_0().into_bool());
829+
}
830+
831+
// `from_raw`/`into_raw` and `From`/`Into` round-trips.
832+
#[test]
833+
fn test_raw() {
834+
let raw: u64 = 0xBFF0_0000_3123_3E03;
835+
let val = TestU64::from_raw(raw);
836+
assert_eq!(u64::from(val), raw);
837+
assert!(val.field_0().into_bool());
838+
assert!(val.field_1().into_bool());
839+
assert_eq!(val.field_11_9(), 0x7);
840+
assert_eq!(val.field_51_16(), 0x3123);
841+
assert_eq!(val.field_15_12(), Ok(MemoryType::Reserved));
842+
assert_eq!(val.field_61_52(), 0x3FF);
843+
assert!(val.field_63().into_bool());
844+
845+
let raw: u16 = 0x42AB;
846+
let val = TestU16::from_raw(raw);
847+
assert_eq!(u16::from(val), raw);
848+
assert!(val.field_0().into_bool());
849+
assert_eq!(val.field_3_1(), 0x5);
850+
assert_eq!(val.field_7_4(), 0xA);
851+
assert_eq!(val.field_15_8(), 0x42);
852+
853+
let raw: u8 = 0xAF;
854+
let val = TestU8::from_raw(raw);
855+
assert_eq!(u8::from(val), raw);
856+
assert!(val.field_0().into_bool());
857+
assert!(val.field_1().into_bool());
858+
assert_eq!(val.field_3_2(), 0x3);
859+
assert_eq!(val.field_7_4(), 0xA);
860+
assert_eq!(val.field_7_0(), 0xAF);
861+
}
862+
}

0 commit comments

Comments
 (0)