mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	Add warn_on macro, uses the BUG/WARN feature (lib/bug.c) via assembly for x86_64/arm64/riscv. The current Rust code simply wraps BUG() macro but doesn't provide the proper debug information. The BUG/WARN feature can only be used from assembly. This uses the assembly code exported by the C side via ARCH_WARN_ASM macro. To avoid duplicating the assembly code, this approach follows the same strategy as the static branch code: it generates the assembly code for Rust using the C preprocessor at compile time. Similarly, ARCH_WARN_REACHABLE is also used at compile time to generate the assembly code; objtool's reachable annotation code. It's used for only architectures that use objtool. For now, Loongarch and arm just use a wrapper for WARN macro. UML doesn't use the assembly BUG/WARN feature; just wrapping generic BUG/WARN functions implemented in C works. Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Link: https://lore.kernel.org/r/20250502094537.231725-5-fujita.tomonori@gmail.com [ Avoid evaluating the condition twice (a good idea in general, but it also matches the C side). Simplify with `as_char_ptr()` to avoid a cast. Cast to `ffi` integer types for `warn_slowpath_fmt`. Avoid cast for `null()`. - Miguel ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
		
			
				
	
	
		
			126 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
// 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 {
 | 
						|
    ($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 {
 | 
						|
    ($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 {
 | 
						|
    ($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 {
 | 
						|
    ($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 {
 | 
						|
    ($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;
 | 
						|
        if cond {
 | 
						|
            const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN);
 | 
						|
 | 
						|
            $crate::warn_flags!(WARN_ON_FLAGS);
 | 
						|
        }
 | 
						|
        cond
 | 
						|
    }};
 | 
						|
}
 |