xref: /linux/rust/kernel/bug.rs (revision f4cdf7ca9a1fdcca413157df19753f388a5a224e)
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         }
89 
90         // SAFETY: It is always safe to call `warn_slowpath_fmt()`
91         // with a valid null-terminated string.
92         unsafe {
93             $crate::bindings::warn_slowpath_fmt(
94                 $crate::str::CStrExt::as_char_ptr($crate::c_str!(::core::file!())),
95                 line!() as $crate::ffi::c_int,
96                 $flags as $crate::ffi::c_uint,
97                 ::core::ptr::null(),
98             );
99         }
100     };
101 }
102 
103 #[macro_export]
104 #[doc(hidden)]
105 #[cfg(not(testlib))]
106 #[cfg(all(CONFIG_BUG, any(CONFIG_LOONGARCH, CONFIG_ARM)))]
107 macro_rules! warn_flags {
108     ($file:expr, $flags:expr) => {
109         if false {
110             _ = $file;
111             _ = $flags;
112         }
113 
114         // SAFETY: It is always safe to call `WARN_ON()`.
115         unsafe { $crate::bindings::WARN_ON(true) }
116     };
117 }
118 
119 #[macro_export]
120 #[doc(hidden)]
121 #[cfg(any(testlib, not(CONFIG_BUG)))]
122 macro_rules! warn_flags {
123     ($file:expr, $flags:expr) => {
124         if false {
125             _ = $file;
126             _ = $flags;
127         }
128     };
129 }
130 
131 #[doc(hidden)]
132 pub const fn bugflag_taint(value: u32) -> u32 {
133     value << 8
134 }
135 
136 /// Report a warning if `cond` is true and return the condition's evaluation result.
137 #[macro_export]
138 macro_rules! warn_on {
139     ($cond:expr) => {{
140         let cond = $cond;
141 
142         #[cfg(CONFIG_DEBUG_BUGVERBOSE_DETAILED)]
143         const COND_STR: &str = concat!("[", stringify!($cond), "] ", file!());
144         #[cfg(not(CONFIG_DEBUG_BUGVERBOSE_DETAILED))]
145         const COND_STR: &str = file!();
146 
147         if cond {
148             const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN);
149 
150             $crate::warn_flags!(COND_STR, WARN_ON_FLAGS);
151         }
152         cond
153     }};
154 }
155