xref: /linux/rust/kernel/bug.rs (revision 5c94956f2bfb87025c1eaded6c51d5841d38d333)
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