Skip to main content

kernel/
bug.rs

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)]
14macro_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))]
54macro_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))]
84macro_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)))]
110macro_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)))]
125macro_rules! warn_flags {
126    ($file:expr, $flags:expr) => {
127        if false {
128            _ = $file;
129            _ = $flags;
130        }
131    };
132}
133
134#[doc(hidden)]
135pub 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]
141macro_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)]
161mod 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}