1 // SPDX-License-Identifier: GPL-2.0 2 3 // Copyright (C) 2024, 2025 FUJITA Tomonori <fujita.tomonori@gmail.com> 4 5 //! Support for BUG and WARN functionality. 6 //! 7 //! C header: [`include/asm-generic/bug.h`](srctree/include/asm-generic/bug.h) 8 9 #[macro_export] 10 #[doc(hidden)] 11 #[cfg(not(testlib))] 12 #[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))] 13 #[cfg(CONFIG_DEBUG_BUGVERBOSE)] 14 macro_rules! warn_flags { 15 ($file:expr, $flags:expr) => { 16 const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags; 17 const _FILE: &[u8] = $file.as_bytes(); 18 // Plus one for null-terminator. 19 static FILE: [u8; _FILE.len() + 1] = { 20 let mut bytes = [0; _FILE.len() + 1]; 21 let mut i = 0; 22 while i < _FILE.len() { 23 bytes[i] = _FILE[i]; 24 i += 1; 25 } 26 bytes 27 }; 28 29 // SAFETY: 30 // - `file`, `line`, `flags`, and `size` are all compile-time constants or 31 // symbols, preventing any invalid memory access. 32 // - The asm block has no side effects and does not modify any registers 33 // or memory. It is purely for embedding metadata into the ELF section. 34 unsafe { 35 $crate::asm!( 36 concat!( 37 "/* {size} */", 38 include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")), 39 include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_reachable_asm.rs"))); 40 file = sym FILE, 41 line = const line!(), 42 flags = const FLAGS, 43 size = const ::core::mem::size_of::<$crate::bindings::bug_entry>(), 44 ); 45 } 46 } 47 } 48 49 #[macro_export] 50 #[doc(hidden)] 51 #[cfg(not(testlib))] 52 #[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))] 53 #[cfg(not(CONFIG_DEBUG_BUGVERBOSE))] 54 macro_rules! warn_flags { 55 ($file:expr, $flags:expr) => { 56 const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags; 57 58 if false { 59 _ = $file; 60 } 61 62 // SAFETY: 63 // - `flags` and `size` are all compile-time constants, preventing 64 // any invalid memory access. 65 // - The asm block has no side effects and does not modify any registers 66 // or memory. It is purely for embedding metadata into the ELF section. 67 unsafe { 68 $crate::asm!( 69 concat!( 70 "/* {size} */", 71 include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")), 72 include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_reachable_asm.rs"))); 73 flags = const FLAGS, 74 size = const ::core::mem::size_of::<$crate::bindings::bug_entry>(), 75 ); 76 } 77 } 78 } 79 80 #[macro_export] 81 #[doc(hidden)] 82 #[cfg(not(testlib))] 83 #[cfg(all(CONFIG_BUG, CONFIG_UML))] 84 macro_rules! warn_flags { 85 ($file:expr, $flags:expr) => { 86 if false { 87 _ = $file; 88 _ = $flags; 89 } 90 91 // SAFETY: 92 // - The file name is a null-terminated string with `'static` lifetime. 93 // - `TAINT_WARN` is a valid taint number. 94 // - `fmt` can be NULL, and NULL is passed. 95 unsafe { 96 $crate::bindings::warn_slowpath_fmt( 97 $crate::str::CStrExt::as_char_ptr($crate::c_str!(::core::file!())), 98 line!() as $crate::ffi::c_int, 99 $crate::bindings::TAINT_WARN, 100 ::core::ptr::null(), 101 ); 102 } 103 }; 104 } 105 106 #[macro_export] 107 #[doc(hidden)] 108 #[cfg(not(testlib))] 109 #[cfg(all(CONFIG_BUG, any(CONFIG_LOONGARCH, CONFIG_ARM)))] 110 macro_rules! warn_flags { 111 ($file:expr, $flags:expr) => { 112 if false { 113 _ = $file; 114 _ = $flags; 115 } 116 117 // SAFETY: It is always safe to call `WARN_ON()`. 118 unsafe { $crate::bindings::WARN_ON(true) } 119 }; 120 } 121 122 #[macro_export] 123 #[doc(hidden)] 124 #[cfg(any(testlib, not(CONFIG_BUG)))] 125 macro_rules! warn_flags { 126 ($file:expr, $flags:expr) => { 127 if false { 128 _ = $file; 129 _ = $flags; 130 } 131 }; 132 } 133 134 #[doc(hidden)] 135 pub const fn bugflag_taint(value: u32) -> u32 { 136 value << 8 137 } 138 139 /// Report a warning if `cond` is true and return the condition's evaluation result. 140 #[macro_export] 141 macro_rules! warn_on { 142 ($cond:expr) => {{ 143 let cond = $cond; 144 145 #[cfg(CONFIG_DEBUG_BUGVERBOSE_DETAILED)] 146 const COND_STR: &str = concat!("[", stringify!($cond), "] ", file!()); 147 #[cfg(not(CONFIG_DEBUG_BUGVERBOSE_DETAILED))] 148 const COND_STR: &str = file!(); 149 150 if cond { 151 const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN); 152 153 $crate::warn_flags!(COND_STR, WARN_ON_FLAGS); 154 } 155 cond 156 }}; 157 } 158 159 #[cfg(CONFIG_RUST_BUG_KUNIT_TEST)] 160 #[macros::kunit_tests(rust_kernel_bug)] 161 mod tests { 162 // The counter is incremented by the kernel warning path, not by `warn_on!` 163 // itself. A count of one means the warning was really reported. 164 #[test] 165 fn test_warn_on() { 166 // SAFETY: `kunit_get_current_test()` is always safe to call (it has 167 // fallbacks for when no KUnit test is running). 168 let test = unsafe { bindings::kunit_get_current_test() }; 169 170 // SAFETY: This function runs only as a KUnit test case, so `test` is a 171 // valid pointer to the running test. 172 let handle = unsafe { bindings::kunit_start_suppress_warning(test) }; 173 174 assert!(!warn_on!(false)); 175 assert!(warn_on!(true)); 176 177 // SAFETY: `kunit_suppressed_warning_count()` accepts any value returned by 178 // `kunit_start_suppress_warning()`. 179 let suppressed_count = unsafe { bindings::kunit_suppressed_warning_count(handle) }; 180 181 // SAFETY: `test` is valid as above. `kunit_end_suppress_warning()` accepts any 182 // value returned by `kunit_start_suppress_warning()`. 183 unsafe { bindings::kunit_end_suppress_warning(test, handle) }; 184 185 assert_eq!(suppressed_count, 1); 186 } 187 } 188