summaryrefslogtreecommitdiff
path: root/rust/kernel/bug.rs
blob: ed943960f851644bd3235e37860bdbb7d5f76969 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// SPDX-License-Identifier: GPL-2.0

// Copyright (C) 2024, 2025 FUJITA Tomonori <fujita.tomonori@gmail.com>

//! Support for BUG and WARN functionality.
//!
//! C header: [`include/asm-generic/bug.h`](srctree/include/asm-generic/bug.h)

#[macro_export]
#[doc(hidden)]
#[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))]
#[cfg(CONFIG_DEBUG_BUGVERBOSE)]
macro_rules! warn_flags {
    ($file:expr, $flags:expr) => {
        const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags;
        const _FILE: &[u8] = $file.as_bytes();
        // Plus one for null-terminator.
        static FILE: [u8; _FILE.len() + 1] = {
            let mut bytes = [0; _FILE.len() + 1];
            let mut i = 0;
            while i < _FILE.len() {
                bytes[i] = _FILE[i];
                i += 1;
            }
            bytes
        };

        // SAFETY:
        // - `file`, `line`, `flags`, and `size` are all compile-time constants or
        // symbols, preventing any invalid memory access.
        // - The asm block has no side effects and does not modify any registers
        // or memory. It is purely for embedding metadata into the ELF section.
        unsafe {
            $crate::asm!(
                concat!(
                    "/* {size} */",
                    include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
                    include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_reachable_asm.rs")));
                file = sym FILE,
                line = const line!(),
                flags = const FLAGS,
                size = const ::core::mem::size_of::<$crate::bindings::bug_entry>(),
            );
        }
    }
}

#[macro_export]
#[doc(hidden)]
#[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))]
#[cfg(not(CONFIG_DEBUG_BUGVERBOSE))]
macro_rules! warn_flags {
    ($file:expr, $flags:expr) => {
        const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags;

        // SAFETY:
        // - `flags` and `size` are all compile-time constants, preventing
        // any invalid memory access.
        // - The asm block has no side effects and does not modify any registers
        // or memory. It is purely for embedding metadata into the ELF section.
        unsafe {
            $crate::asm!(
                concat!(
                    "/* {size} */",
                    include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
                    include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_reachable_asm.rs")));
                flags = const FLAGS,
                size = const ::core::mem::size_of::<$crate::bindings::bug_entry>(),
            );
        }
    }
}

#[macro_export]
#[doc(hidden)]
#[cfg(all(CONFIG_BUG, CONFIG_UML))]
macro_rules! warn_flags {
    ($file:expr, $flags:expr) => {
        // SAFETY: It is always safe to call `warn_slowpath_fmt()`
        // with a valid null-terminated string.
        unsafe {
            $crate::bindings::warn_slowpath_fmt(
                $crate::c_str!(::core::file!()).as_char_ptr(),
                line!() as $crate::ffi::c_int,
                $flags as $crate::ffi::c_uint,
                ::core::ptr::null(),
            );
        }
    };
}

#[macro_export]
#[doc(hidden)]
#[cfg(all(CONFIG_BUG, any(CONFIG_LOONGARCH, CONFIG_ARM)))]
macro_rules! warn_flags {
    ($file:expr, $flags:expr) => {
        // SAFETY: It is always safe to call `WARN_ON()`.
        unsafe { $crate::bindings::WARN_ON(true) }
    };
}

#[macro_export]
#[doc(hidden)]
#[cfg(not(CONFIG_BUG))]
macro_rules! warn_flags {
    ($file:expr, $flags:expr) => {};
}

#[doc(hidden)]
pub const fn bugflag_taint(value: u32) -> u32 {
    value << 8
}

/// Report a warning if `cond` is true and return the condition's evaluation result.
#[macro_export]
macro_rules! warn_on {
    ($cond:expr) => {{
        let cond = $cond;

        #[cfg(CONFIG_DEBUG_BUGVERBOSE_DETAILED)]
        const _COND_STR: &str = concat!("[", stringify!($cond), "] ", file!());
        #[cfg(not(CONFIG_DEBUG_BUGVERBOSE_DETAILED))]
        const _COND_STR: &str = file!();

        if cond {
            const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN);

            $crate::warn_flags!(_COND_STR, WARN_ON_FLAGS);
        }
        cond
    }};
}