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 anotation code. It's
used for only architectures that use objtool.
For now, Loongarch just uses 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>
---
rust/Makefile | 8 ++
rust/helpers/bug.c | 5 +
rust/kernel/.gitignore | 2 +
rust/kernel/bug.rs | 113 ++++++++++++++++++
rust/kernel/generated_arch_reachable_asm.rs.S | 7 ++
rust/kernel/generated_arch_warn_asm.rs.S | 7 ++
rust/kernel/lib.rs | 1 +
7 files changed, 143 insertions(+)
create mode 100644 rust/kernel/bug.rs
create mode 100644 rust/kernel/generated_arch_reachable_asm.rs.S
create mode 100644 rust/kernel/generated_arch_warn_asm.rs.S
diff --git a/rust/Makefile b/rust/Makefile
index 8fcfd60447bc..a295b65c43f3 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -34,6 +34,9 @@ obj-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.o
obj-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.o
always-$(subst y,$(CONFIG_RUST),$(CONFIG_JUMP_LABEL)) += kernel/generated_arch_static_branch_asm.rs
+ifndef CONFIG_UML
+always-$(subst y,$(CONFIG_RUST),$(CONFIG_BUG)) += kernel/generated_arch_warn_asm.rs kernel/generated_arch_reachable_asm.rs
+endif
# Avoids running `$(RUSTC)` when it may not be available.
ifdef CONFIG_RUST
@@ -481,5 +484,10 @@ $(obj)/kernel.o: $(src)/kernel/lib.rs $(obj)/build_error.o \
ifdef CONFIG_JUMP_LABEL
$(obj)/kernel.o: $(obj)/kernel/generated_arch_static_branch_asm.rs
endif
+ifndef CONFIG_UML
+ifdef CONFIG_BUG
+$(obj)/kernel.o: $(obj)/kernel/generated_arch_warn_asm.rs $(obj)/kernel/generated_arch_reachable_asm.rs
+endif
+endif
endif # CONFIG_RUST
diff --git a/rust/helpers/bug.c b/rust/helpers/bug.c
index e2d13babc737..a62c96f507d1 100644
--- a/rust/helpers/bug.c
+++ b/rust/helpers/bug.c
@@ -6,3 +6,8 @@ __noreturn void rust_helper_BUG(void)
{
BUG();
}
+
+bool rust_helper_WARN_ON(bool cond)
+{
+ return WARN_ON(cond);
+}
diff --git a/rust/kernel/.gitignore b/rust/kernel/.gitignore
index 6ba39a178f30..f636ad95aaf3 100644
--- a/rust/kernel/.gitignore
+++ b/rust/kernel/.gitignore
@@ -1,3 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
/generated_arch_static_branch_asm.rs
+/generated_arch_warn_asm.rs
+/generated_arch_reachable_asm.rs
diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs
new file mode 100644
index 000000000000..a010033be5a2
--- /dev/null
+++ b/rust/kernel/bug.rs
@@ -0,0 +1,113 @@
+// 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)))]
+#[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: Just an FFI call.
+ 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)))]
+#[cfg(not(CONFIG_DEBUG_BUGVERBOSE))]
+macro_rules! warn_flags {
+ ($flags:expr) => {
+ const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags;
+ // SAFETY: Just an FFI call.
+ 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: Just an FFI call.
+ unsafe {
+ $crate::bindings::warn_slowpath_fmt(
+ $crate::c_str!(::core::file!()).as_ptr() as *const ::core::ffi::c_uchar,
+ line!() as i32,
+ $flags as u32,
+ ::core::ptr::null() as *const ::core::ffi::c_uchar,
+ );
+ }
+ };
+}
+
+#[macro_export]
+#[doc(hidden)]
+#[cfg(all(CONFIG_BUG, CONFIG_LOONGARCH))]
+macro_rules! warn_flags {
+ ($flags:expr) => {
+ // SAFETY: Just an FFI call.
+ 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) => {{
+ if $cond {
+ const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN);
+
+ $crate::warn_flags!(WARN_ON_FLAGS);
+ }
+ $cond
+ }};
+}
diff --git a/rust/kernel/generated_arch_reachable_asm.rs.S b/rust/kernel/generated_arch_reachable_asm.rs.S
new file mode 100644
index 000000000000..3886a9ad3a99
--- /dev/null
+++ b/rust/kernel/generated_arch_reachable_asm.rs.S
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/bug.h>
+
+// Cut here.
+
+::kernel::concat_literals!(ARCH_WARN_REACHABLE)
diff --git a/rust/kernel/generated_arch_warn_asm.rs.S b/rust/kernel/generated_arch_warn_asm.rs.S
new file mode 100644
index 000000000000..409eb4c2d3a1
--- /dev/null
+++ b/rust/kernel/generated_arch_warn_asm.rs.S
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/bug.h>
+
+// Cut here.
+
+::kernel::concat_literals!(ARCH_WARN_ASM("{file}", "{line}", "{flags}", "{size}"))
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 496ed32b0911..c0720846b249 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -38,6 +38,7 @@
pub mod alloc;
#[cfg(CONFIG_BLOCK)]
pub mod block;
+pub mod bug;
#[doc(hidden)]
pub mod build_assert;
pub mod cred;
--
2.43.0
On Wed, Mar 5, 2025 at 12:10 PM FUJITA Tomonori
<fujita.tomonori@gmail.com> wrote:
>
> 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 anotation code. It's
> used for only architectures that use objtool.
>
> For now, Loongarch just uses 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>
Overall looks reasonable. A few nits below:
> +#[macro_export]
> +#[doc(hidden)]
> +#[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH)))]
> +#[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: Just an FFI call.
> + 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>(),
The indentation here is odd. Shouldn't the arguments be indented one more?
> + );
> + }
> + }
> +}
> +
> +#[macro_export]
> +#[doc(hidden)]
> +#[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH)))]
> +#[cfg(not(CONFIG_DEBUG_BUGVERBOSE))]
> +macro_rules! warn_flags {
> + ($flags:expr) => {
> + const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags;
> + // SAFETY: Just an FFI call.
> + 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>(),
Same thing here.
> + );
> + }
> + }
> +}
> +
> +#[macro_export]
> +#[doc(hidden)]
> +#[cfg(all(CONFIG_BUG, CONFIG_UML))]
> +macro_rules! warn_flags {
> + ($flags:expr) => {
> + // SAFETY: Just an FFI call.
> + unsafe {
> + $crate::bindings::warn_slowpath_fmt(
> + $crate::c_str!(::core::file!()).as_ptr() as *const ::core::ffi::c_uchar,
> + line!() as i32,
> + $flags as u32,
> + ::core::ptr::null() as *const ::core::ffi::c_uchar,
Why unsigned char? I think this should be ::kernel::ffi::c_char to
utilize the custom integer type definitions in kernel rather than the
faulty ones in core.
(Maybe $crate rather than kernel)
Alice
On Tue, 8 Apr 2025 15:55:38 +0200
Alice Ryhl <aliceryhl@google.com> wrote:
> On Wed, Mar 5, 2025 at 12:10 PM FUJITA Tomonori
> <fujita.tomonori@gmail.com> wrote:
>>
>> 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 anotation code. It's
>> used for only architectures that use objtool.
>>
>> For now, Loongarch just uses 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>
>
> Overall looks reasonable. A few nits below:
Thanks!
>> +#[macro_export]
>> +#[doc(hidden)]
>> +#[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH)))]
>> +#[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: Just an FFI call.
>> + 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>(),
>
> The indentation here is odd. Shouldn't the arguments be indented one more?
Yeah, fixed.
>> +#[macro_export]
>> +#[doc(hidden)]
>> +#[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH)))]
>> +#[cfg(not(CONFIG_DEBUG_BUGVERBOSE))]
>> +macro_rules! warn_flags {
>> + ($flags:expr) => {
>> + const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags;
>> + // SAFETY: Just an FFI call.
>> + 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>(),
>
> Same thing here.
Fixed.
>> + );
>> + }
>> + }
>> +}
>> +
>> +#[macro_export]
>> +#[doc(hidden)]
>> +#[cfg(all(CONFIG_BUG, CONFIG_UML))]
>> +macro_rules! warn_flags {
>> + ($flags:expr) => {
>> + // SAFETY: Just an FFI call.
>> + unsafe {
>> + $crate::bindings::warn_slowpath_fmt(
>> + $crate::c_str!(::core::file!()).as_ptr() as *const ::core::ffi::c_uchar,
>> + line!() as i32,
>> + $flags as u32,
>> + ::core::ptr::null() as *const ::core::ffi::c_uchar,
>
> Why unsigned char? I think this should be ::kernel::ffi::c_char to
> utilize the custom integer type definitions in kernel rather than the
> faulty ones in core.
>
> (Maybe $crate rather than kernel)
Oops, replaced them with $crate::ffi::c_char
© 2016 - 2026 Red Hat, Inc.