* [PATCH v5 0/2] powerpc: Add ARCH_WARN_ASM and KUnit tests for Rust WARN() support
@ 2026-09-15 9:04 Mukesh Kumar Chaurasiya (IBM)
2026-09-15 9:04 ` [PATCH V5 1/2] powerpc/bug: Add ARCH_WARN_ASM and refactor _EMIT_BUG_ENTRY for Rust support Mukesh Kumar Chaurasiya (IBM)
2026-09-15 9:04 ` [PATCH V5 2/2] rust: kernel: Add KUnit tests for powerpc ARCH_WARN_ASM bug table emission Mukesh Kumar Chaurasiya (IBM)
0 siblings, 2 replies; 7+ messages in thread
From: Mukesh Kumar Chaurasiya (IBM) @ 2026-09-15 9:04 UTC (permalink / raw)
To: maddy, mpe, npiggin, chleroy, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, daniel.almeida,
tamird, acourbot, work, pjw, palmer, aou, alex, nathan,
ndesaulniers, morbo, justinstitt, mkchauras, ynorov, david,
joelagnelf, fujita.tomonori, linkmauve, linuxppc-dev,
linux-kernel, rust-for-linux, linux-riscv, llvm
powerpc currently has no ARCH_WARN_ASM or ARCH_WARN_REACHABLE definitions,
which causes Rust kernel builds on powerpc to fail with a macro expansion
error when the generic Rust WARN() infrastructure tries to call
ARCH_WARN_ASM() through generated_arch_warn_asm.rs.S. Every other
architecture that supports Rust (x86, arm64, s390, riscv) already provides
these definitions. This series brings powerpc into line.
Patch 1 refactors _EMIT_BUG_ENTRY in arch/powerpc/include/asm/bug.h.
The old macro was a positional-operand template that hardcoded the 1b
label reference and embedded .org/.previous directives, making it
impossible to compose as a free string outside an asm operand context.
The refactored version takes explicit (label, file, line, flags) arguments
via string concatenation; .org/.previous are moved to each call site so
BUG_ENTRY() can continue to pass sizeof(struct bug_entry) as an asm
operand while ARCH_WARN_ASM supplies its own size string. With
_EMIT_BUG_ENTRY composable as a plain string, adding ARCH_WARN_ASM and
ARCH_WARN_REACHABLE is then straightforward.
Patch 2 adds five KUnit tests under CONFIG_RUST_BUG_POWERPC_KUNIT_TEST
(depends on PPC && GENERIC_BUG, covering both ppc32 and ppc64) that
verify the __bug_table entry emitted by ARCH_WARN_ASM is correctly
formed. find_bug() is called with the exact virtual address of the twi
instruction, captured at link time via a .dc.a relocation in a
global_asm! block. .dc.a emits a pointer-width word (4 bytes on ppc32,
8 bytes on ppc64); BUG_KUNIT_TRAP_ADDR is declared as usize on the Rust
side so the same test code is correct on both. The five tests check that
the entry is found, carries BUGFLAG_WARNING, records the right source
file and a non-zero line number, and that the trap address lies in
kernel text.
The global_asm! block is split into four cfg-gated variants covering all
combinations of CONFIG_PPC64 x CONFIG_DEBUG_BUGVERBOSE. The split on
CONFIG_DEBUG_BUGVERBOSE is necessary because when it is disabled,
_EMIT_BUG_ENTRY drops the file and line fields from the bug table entry,
leaving the {file} and {line} global_asm! operands unused — which is a
hard compile error in Rust. The split on CONFIG_PPC64 is necessary
because concat!() only accepts literals (not expressions), so .balign
4 vs .balign 8 must be selected statically.
Changelog:
V4 -> V5:
- Fixed a build error with DEBUG_BUGVERBOSE=n
- Added a label for bug entry
V4: https://lore.kernel.org/all/20260912065902.24017-1-mkchauras@gmail.com/
V3 -> V4:
- Fix Label with appending b at end
- Add KUnit test patch.
- Tested on ppc64le pseries:
pass:5 fail:0 skip:0.
- Tested on ppc32 QEMU mac99 G4:
pass:5 fail:0 skip:0.
- Tested on ppc64le QEMU pseries:
pass:5 fail:0 skip:0.
V3: https://lore.kernel.org/all/20260910100801.2159785-2-mkchauras@gmail.com
V2 -> V3:
- Add label argument in _EMIT_BUG_ENTRY
V2: https://lore.kernel.org/all/20260910071252.1950488-2-mkchauras@gmail.com
V1 -> V2:
- commit message now has error, fixes tag and closes tag
V1: https://lore.kernel.org/all/20260819084825.969116-1-mkchauras@gmail.com
Mukesh Kumar Chaurasiya (IBM) (2):
powerpc/bug: Add ARCH_WARN_ASM and refactor _EMIT_BUG_ENTRY for Rust
support
rust: kernel: Add KUnit tests for powerpc ARCH_WARN_ASM bug table
emission
arch/powerpc/include/asm/bug.h | 36 ++++---
rust/kernel/Kconfig.test | 13 +++
rust/kernel/bug.rs | 222 +++++++++++++++++++++++++++++++++
3 files changed, 251 insertions(+), 16 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH V5 1/2] powerpc/bug: Add ARCH_WARN_ASM and refactor _EMIT_BUG_ENTRY for Rust support
2026-09-15 9:04 [PATCH v5 0/2] powerpc: Add ARCH_WARN_ASM and KUnit tests for Rust WARN() support Mukesh Kumar Chaurasiya (IBM)
@ 2026-09-15 9:04 ` Mukesh Kumar Chaurasiya (IBM)
2026-09-15 9:04 ` [PATCH V5 2/2] rust: kernel: Add KUnit tests for powerpc ARCH_WARN_ASM bug table emission Mukesh Kumar Chaurasiya (IBM)
1 sibling, 0 replies; 7+ messages in thread
From: Mukesh Kumar Chaurasiya (IBM) @ 2026-09-15 9:04 UTC (permalink / raw)
To: maddy, mpe, npiggin, chleroy, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, daniel.almeida,
tamird, acourbot, work, pjw, palmer, aou, alex, nathan,
ndesaulniers, morbo, justinstitt, mkchauras, ynorov, david,
joelagnelf, fujita.tomonori, linkmauve, linuxppc-dev,
linux-kernel, rust-for-linux, linux-riscv, llvm
Cc: FUJITA Tomonori
The Rust kernel infrastructure generates inline asm for WARN() via
ARCH_WARN_ASM(file, line, flags, size), expanding it through a C
preprocessor pass (generated_arch_warn_asm.rs.S) to produce an
arch-specific asm template string for use in Rust's core::arch macros.
powerpc currently lacks ARCH_WARN_ASM and ARCH_WARN_REACHABLE, causing
Rust builds to fail on powerpc with
```
error: no rules expected `ARCH_WARN_ASM`
--> /home/linkmauve/dev/linux/wii/rust/kernel/generated_arch_warn_asm.rs:1:28
|
1 | ::kernel::concat_literals!(ARCH_WARN_ASM("{file}", "{line}", "{flags}", "{size}"))
| ^^^^^^^^^^^^^ no rules expected this token in macro call
|
::: ../rust/kernel/lib.rs:279:1
|
279 | macro_rules! concat_literals {
| ---------------------------- when calling this macro
|
= note: while trying to match sequence start
error: no rules expected `ARCH_WARN_REACHABLE`
--> /home/linkmauve/dev/linux/wii/rust/kernel/generated_arch_reachable_asm.rs:1:28
|
1 | ::kernel::concat_literals!(ARCH_WARN_REACHABLE)
| ^^^^^^^^^^^^^^^^^^^ no rules expected this token in macro call
|
::: ../rust/kernel/lib.rs:279:1
|
279 | macro_rules! concat_literals {
| ---------------------------- when calling this macro
|
= note: while trying to match sequence start
error: aborting due to 2 previous errors
```
To add ARCH_WARN_ASM, _EMIT_BUG_ENTRY first needs to be refactored.
The old definition was a bare macro with no parameters, relying on
positional asm operand references (%0-%3), hardcoding the backward
reference to local label 1b, and including .org/.previous directives
inline. That made it impossible to compose as a plain string outside of
an asm operand context, and left an invisible contract that callers must
always emit their trap at label 1:.
Refactor _EMIT_BUG_ENTRY to take explicit (bug_entry, trap, file, line, flags)
string arguments via string concatenation. This removes the dependency
on asm operand numbering and makes the labels an explicit argument,
so the caller's intent is visible at the call site and a future caller
using a different label cannot silently produce a wrong bug table entry.
Move the .org and .previous directives out of _EMIT_BUG_ENTRY and into
each call site, so BUG_ENTRY() can still pass sizeof(struct bug_entry)
as an asm operand while ARCH_WARN_ASM can supply its own size string
independently.
Add ARCH_WARN_REACHABLE as an empty define, matching the arm64
convention, indicating that no additional reachability annotation is
needed after a WARN on powerpc.
Reported-by: FUJITA Tomonori <tomo@flapping.org>
Closes: https://lore.kernel.org/all/anG67Q6Y59kDqh-c@desktop
Fixes: 73b741adb264 ("rust: Add PowerPC support")
Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
---
arch/powerpc/include/asm/bug.h | 46 ++++++++++++++++++----------------
1 file changed, 25 insertions(+), 21 deletions(-)
diff --git a/arch/powerpc/include/asm/bug.h b/arch/powerpc/include/asm/bug.h
index 0db48977c70c..bf31ee1e902a 100644
--- a/arch/powerpc/include/asm/bug.h
+++ b/arch/powerpc/include/asm/bug.h
@@ -32,34 +32,38 @@
#endif /* verbose */
#else /* !__ASSEMBLER__ */
-/* _EMIT_BUG_ENTRY expects args %0,%1,%2,%3 to be FILE, LINE, flags and
- sizeof(struct bug_entry), respectively */
#ifdef CONFIG_DEBUG_BUGVERBOSE
-#define _EMIT_BUG_ENTRY \
- ".section __bug_table,\"aw\"\n" \
- "2: .4byte 1b - .\n" \
- " .4byte %0 - .\n" \
- " .short %1, %2\n" \
- ".org 2b+%3\n" \
- ".previous\n"
+#define _EMIT_BUG_ENTRY(bug_entry, trap, file, line, flags) \
+ ".section __bug_table,\"aw\"\n" \
+ #bug_entry ": .4byte " #trap " - .\n" \
+ " .4byte " file " - .\n" \
+ " .short " line ", " flags "\n"
#else
-#define _EMIT_BUG_ENTRY \
- ".section __bug_table,\"aw\"\n" \
- "2: .4byte 1b - .\n" \
- " .short %2\n" \
- ".org 2b+%3\n" \
- ".previous\n"
+#define _EMIT_BUG_ENTRY(bug_entry, trap, file, line, flags) \
+ ".section __bug_table,\"aw\"\n" \
+ #bug_entry ": .4byte " #trap " - .\n" \
+ " .short " flags "\n"
#endif
-#define BUG_ENTRY(cond_str, insn, flags, ...) \
- __asm__ __volatile__( \
- "1: " insn "\n" \
- _EMIT_BUG_ENTRY \
+#define BUG_ENTRY(cond_str, insn, flags, ...) \
+ __asm__ __volatile__( \
+ "1: " insn "\n" \
+ _EMIT_BUG_ENTRY(2, 1b, "%0", "%1", "%2") \
+ ".org 2b+%3\n" \
+ ".previous\n" \
: : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \
- "i" (flags), \
- "i" (sizeof(struct bug_entry)), \
+ "i" (flags), \
+ "i" (sizeof(struct bug_entry)), \
##__VA_ARGS__)
+#define ARCH_WARN_ASM(file, line, flags, size) \
+ "1: twi 31, 0, 0\n" \
+ _EMIT_BUG_ENTRY(2, 1b, file, line, flags) \
+ ".org 2b+" size "\n" \
+ ".previous\n"
+
+#define ARCH_WARN_REACHABLE
+
/*
* BUG_ON() and WARN_ON() do their best to cooperate with compile-time
* optimisations. However depending on the complexity of the condition
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH V5 2/2] rust: kernel: Add KUnit tests for powerpc ARCH_WARN_ASM bug table emission
2026-09-15 9:04 [PATCH v5 0/2] powerpc: Add ARCH_WARN_ASM and KUnit tests for Rust WARN() support Mukesh Kumar Chaurasiya (IBM)
2026-09-15 9:04 ` [PATCH V5 1/2] powerpc/bug: Add ARCH_WARN_ASM and refactor _EMIT_BUG_ENTRY for Rust support Mukesh Kumar Chaurasiya (IBM)
@ 2026-09-15 9:04 ` Mukesh Kumar Chaurasiya (IBM)
2026-09-16 14:06 ` FUJITA Tomonori
2026-09-16 14:21 ` David Gow
1 sibling, 2 replies; 7+ messages in thread
From: Mukesh Kumar Chaurasiya (IBM) @ 2026-09-15 9:04 UTC (permalink / raw)
To: maddy, mpe, npiggin, chleroy, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, daniel.almeida,
tamird, acourbot, work, pjw, palmer, aou, alex, nathan,
ndesaulniers, morbo, justinstitt, mkchauras, ynorov, david,
joelagnelf, fujita.tomonori, linkmauve, linuxppc-dev,
linux-kernel, rust-for-linux, linux-riscv, llvm
Verify that the __bug_table entry emitted by ARCH_WARN_ASM has a correct
bug_addr displacement — i.e. the '1b' label reference in _EMIT_BUG_ENTRY
resolves to the trap instruction — by calling find_bug() with the exact
virtual address of the twi instruction, mirroring what the real powerpc
trap handler does.
The trap address is captured at link time via a .dc.a 1b relocation placed
in .data by the global_asm! block. global_asm! is used instead of asm!
because LLVM eliminates asm! blocks in dead branches; global_asm! is
file-scope and always emitted. BUG_KUNIT_TRAP_ADDR is defined as a .global
symbol directly on the .dc.a word so the linker relocation lands on it —
a Rust static initialized to zero would end up in BSS where relocations are
not applied.
.dc.a emits a pointer-width word (4 bytes on ppc32, 8 bytes on ppc64),
so BUG_KUNIT_TRAP_ADDR is declared as usize on the Rust side, making the
tests correct on both ppc32 and ppc64. The global_asm! block is split into
two cfg-gated variants (CONFIG_PPC64 / !CONFIG_PPC64) to select the right
.balign since concat!() only accepts literals.
Five tests are included in the rust_kernel_bug_powerpc suite:
bug_entry_found - find_bug() returns non-NULL for the trap address,
proving the bug_addr displacement is correct
bug_entry_is_warning - the emitted entry has BUGFLAG_WARNING set
bug_entry_file - bug_get_file_line() returns the correct source
file (requires CONFIG_DEBUG_BUGVERBOSE)
bug_entry_line - the recorded line number is non-zero, confirming
the {line} operand was substituted correctly
(requires CONFIG_DEBUG_BUGVERBOSE)
bug_entry_addr_is_in_text - kernel_text_address() confirms the trap address
lies in kernel text, not data or zero
The suite is named rust_kernel_bug_powerpc and the Kconfig option
CONFIG_RUST_BUG_POWERPC_KUNIT_TEST depends on PPC && GENERIC_BUG,
covering both ppc32 and ppc64.
Tested on ppc64le (ltcfujiaac-lp3, 7.3.0-rc1+): pass:5 fail:0 skip:0.
Tested on ppc32 Book3S (QEMU mac99 G4, chrp32_defconfig): pass:5 fail:0 skip:0.
Tested on ppc64le (QEMU pseries, pseries_le_defconfig): pass:5 fail:0 skip:0.
Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
---
rust/kernel/Kconfig.test | 13 +++
rust/kernel/bug.rs | 209 +++++++++++++++++++++++++++++++++++++++
2 files changed, 222 insertions(+)
diff --git a/rust/kernel/Kconfig.test b/rust/kernel/Kconfig.test
index e6a5c7a795f0..5a82f0812c89 100644
--- a/rust/kernel/Kconfig.test
+++ b/rust/kernel/Kconfig.test
@@ -83,4 +83,17 @@ config RUST_BITFIELD_KUNIT_TEST
If unsure, say N.
+config RUST_BUG_POWERPC_KUNIT_TEST
+ bool "KUnit tests for powerpc ARCH_WARN_ASM bug table emission" if !KUNIT_ALL_TESTS
+ depends on PPC && GENERIC_BUG
+ default KUNIT_ALL_TESTS
+ help
+ This option enables KUnit tests that verify ARCH_WARN_ASM emits a
+ correct __bug_table entry on powerpc (both ppc32 and ppc64): the
+ bug_addr displacement must resolve back to the trap instruction so
+ that find_bug() can locate the entry — exactly as the real trap
+ handler does.
+
+ If unsure, say N.
+
endif
diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs
index 3566f0234ca4..cd90bd511479 100644
--- a/rust/kernel/bug.rs
+++ b/rust/kernel/bug.rs
@@ -152,3 +152,212 @@ macro_rules! warn_on {
cond
}};
}
+
+// Test-only constants and file static referenced by the global_asm block below.
+//
+// global_asm! is file-scope and always emitted — LLVM cannot eliminate it,
+// unlike asm! inside a function which is subject to dead-code removal.
+//
+// BUG_KUNIT_TRAP_ADDR is declared as a .global symbol entirely inside the
+// global_asm! block so the .dc.a 1b relocation lands directly on it.
+// A Rust static initialized to zero would end up in BSS; the linker does
+// not apply relocations to BSS, so the address would stay zero at runtime.
+#[cfg(CONFIG_RUST_BUG_POWERPC_KUNIT_TEST)]
+mod test_statics {
+ use crate::bindings::{bug_entry, BUGFLAG_WARNING, TAINT_WARN};
+
+ pub(super) const FLAGS: u32 = BUGFLAG_WARNING | (TAINT_WARN << 8);
+ pub(super) const SIZE: usize = core::mem::size_of::<bug_entry>();
+
+ // LINE and BUG_KUNIT_FILE are only referenced by the {file}/{line}
+ // operands in global_asm!, which are only present when
+ // CONFIG_DEBUG_BUGVERBOSE is set (the non-verbose _EMIT_BUG_ENTRY
+ // drops the file/line fields from the bug table entry entirely).
+ #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
+ pub(super) const LINE: u32 = line!();
+
+ // Null-terminated source file name — the assembler references this symbol
+ // for the verbose file pointer in __bug_table, same as warn_flags!.
+ #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
+ const _FILE: &[u8] = file!().as_bytes();
+ #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
+ #[no_mangle]
+ pub(super) static BUG_KUNIT_FILE: [u8; _FILE.len() + 1] = {
+ let mut bytes = [0u8; _FILE.len() + 1];
+ let mut i = 0;
+ while i < _FILE.len() {
+ bytes[i] = _FILE[i];
+ i += 1;
+ }
+ bytes
+ };
+}
+
+// Emit ARCH_WARN_ASM at file scope and capture the trap address.
+//
+// BUG_KUNIT_TRAP_ADDR is defined as a .global symbol right on top of the
+// .dc.a 1b directive so the linker resolves the relocation directly into
+// that symbol's storage — no BSS, no zero-init problem.
+// .dc.a emits a pointer-width word (4 bytes on ppc32, 8 bytes on ppc64),
+// matching the usize declaration on the Rust side.
+#[cfg(all(CONFIG_RUST_BUG_POWERPC_KUNIT_TEST, CONFIG_PPC64, CONFIG_DEBUG_BUGVERBOSE))]
+::core::arch::global_asm!(
+ concat!(
+ include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
+ ".pushsection .data\n\t",
+ ".balign 8\n\t",
+ ".global BUG_KUNIT_TRAP_ADDR\n\t",
+ "BUG_KUNIT_TRAP_ADDR:\n\t",
+ ".dc.a 1b\n\t",
+ ".popsection\n",
+ ),
+ file = sym test_statics::BUG_KUNIT_FILE,
+ line = const test_statics::LINE,
+ flags = const test_statics::FLAGS,
+ size = const test_statics::SIZE,
+);
+
+#[cfg(all(CONFIG_RUST_BUG_POWERPC_KUNIT_TEST, CONFIG_PPC64, not(CONFIG_DEBUG_BUGVERBOSE)))]
+::core::arch::global_asm!(
+ concat!(
+ include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
+ ".pushsection .data\n\t",
+ ".balign 8\n\t",
+ ".global BUG_KUNIT_TRAP_ADDR\n\t",
+ "BUG_KUNIT_TRAP_ADDR:\n\t",
+ ".dc.a 1b\n\t",
+ ".popsection\n",
+ ),
+ flags = const test_statics::FLAGS,
+ size = const test_statics::SIZE,
+);
+
+#[cfg(all(CONFIG_RUST_BUG_POWERPC_KUNIT_TEST, not(CONFIG_PPC64), CONFIG_DEBUG_BUGVERBOSE))]
+::core::arch::global_asm!(
+ concat!(
+ include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
+ ".pushsection .data\n\t",
+ ".balign 4\n\t",
+ ".global BUG_KUNIT_TRAP_ADDR\n\t",
+ "BUG_KUNIT_TRAP_ADDR:\n\t",
+ ".dc.a 1b\n\t",
+ ".popsection\n",
+ ),
+ file = sym test_statics::BUG_KUNIT_FILE,
+ line = const test_statics::LINE,
+ flags = const test_statics::FLAGS,
+ size = const test_statics::SIZE,
+);
+
+#[cfg(all(CONFIG_RUST_BUG_POWERPC_KUNIT_TEST, not(CONFIG_PPC64), not(CONFIG_DEBUG_BUGVERBOSE)))]
+::core::arch::global_asm!(
+ concat!(
+ include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
+ ".pushsection .data\n\t",
+ ".balign 4\n\t",
+ ".global BUG_KUNIT_TRAP_ADDR\n\t",
+ "BUG_KUNIT_TRAP_ADDR:\n\t",
+ ".dc.a 1b\n\t",
+ ".popsection\n",
+ ),
+ flags = const test_statics::FLAGS,
+ size = const test_statics::SIZE,
+);
+
+#[cfg(CONFIG_RUST_BUG_POWERPC_KUNIT_TEST)]
+#[::kernel::macros::kunit_tests(rust_kernel_bug_powerpc)]
+mod tests {
+ use crate::bindings;
+
+ fn trap_addr() -> usize {
+ // BUG_KUNIT_TRAP_ADDR is a .global symbol defined in the global_asm!
+ // block above, placed in .data at the exact .dc.a 1b relocation word.
+ // The linker resolves it to the virtual address of the twi instruction
+ // before any Rust code runs, so reading it here is always safe.
+ extern "C" {
+ // .dc.a emits a pointer-width word: 4 bytes on ppc32, 8 on ppc64.
+ // usize matches the native pointer width on both.
+ static BUG_KUNIT_TRAP_ADDR: usize;
+ }
+ // SAFETY: read-only after link time, no concurrent mutation possible.
+ unsafe { BUG_KUNIT_TRAP_ADDR }
+ }
+
+ /// The `__bug_table` entry emitted by `ARCH_WARN_ASM` must be locatable
+ /// via `find_bug()` using the trap instruction's address. A NULL result
+ /// means the `1b` label reference in `_EMIT_BUG_ENTRY` resolved to the
+ /// wrong address and the real trap handler would not recognise the site.
+ #[test]
+ fn bug_entry_found() {
+ // Non-zero proves the .dc.a relocation was resolved by the linker.
+ assert!(trap_addr() != 0);
+
+ // SAFETY: find_bug() is always safe to call with any address; it
+ // simply walks __bug_table and returns NULL if nothing matches.
+ let entry = unsafe { bindings::find_bug(trap_addr()) };
+ // Non-NULL proves the bug_addr displacement in _EMIT_BUG_ENTRY is correct.
+ assert!(!entry.is_null());
+ }
+
+ /// The emitted entry must be flagged as a warning (not a hard BUG).
+ #[test]
+ fn bug_entry_is_warning() {
+ assert!(trap_addr() != 0);
+ let entry = unsafe { bindings::find_bug(trap_addr()) };
+ assert!(!entry.is_null());
+ // SAFETY: entry is non-null and points to a valid bug_entry.
+ let flags = unsafe { (*entry).flags } as u32;
+ assert!(flags & bindings::BUGFLAG_WARNING != 0);
+ }
+
+ /// With `CONFIG_DEBUG_BUGVERBOSE` the entry must record a non-null file
+ /// pointer pointing back into this source file.
+ #[test]
+ #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
+ fn bug_entry_file() {
+ use core::ffi::CStr;
+
+ assert!(trap_addr() != 0);
+ let entry = unsafe { bindings::find_bug(trap_addr()) };
+ assert!(!entry.is_null());
+
+ let mut file_ptr: *const core::ffi::c_char = core::ptr::null();
+ let mut line: u32 = 0;
+ // SAFETY: entry is non-null and valid; file_ptr and line are local
+ // variables passed as out-parameters.
+ unsafe { bindings::bug_get_file_line(entry, &mut file_ptr, &mut line) };
+
+ assert!(!file_ptr.is_null());
+ // SAFETY: file_ptr is a null-terminated C string from BUG_KUNIT_FILE.
+ let file_str = unsafe { CStr::from_ptr(file_ptr) }.to_str().unwrap_or("");
+ assert!(file_str.contains("bug"));
+ }
+
+ /// With `CONFIG_DEBUG_BUGVERBOSE` the recorded line number must be
+ /// non-zero (a zero line would mean the asm operand was not substituted).
+ #[test]
+ #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
+ fn bug_entry_line() {
+ assert!(trap_addr() != 0);
+ let entry = unsafe { bindings::find_bug(trap_addr()) };
+ assert!(!entry.is_null());
+
+ let mut file_ptr: *const core::ffi::c_char = core::ptr::null();
+ let mut line: u32 = 0;
+ // SAFETY: entry is non-null and valid.
+ unsafe { bindings::bug_get_file_line(entry, &mut file_ptr, &mut line) };
+
+ assert!(line != 0);
+ }
+
+ /// The trap address stored in `__bug_table` must lie within the kernel
+ /// text segment. If the label reference in `_EMIT_BUG_ENTRY` resolved
+ /// to data or zero, `kernel_text_address()` would return false.
+ #[test]
+ fn bug_entry_addr_is_in_text() {
+ assert!(trap_addr() != 0);
+ // SAFETY: kernel_text_address() is always safe to call with any addr.
+ let in_text = unsafe { bindings::kernel_text_address(trap_addr()) };
+ assert!(in_text != 0);
+ }
+}
--
2.55.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH V5 2/2] rust: kernel: Add KUnit tests for powerpc ARCH_WARN_ASM bug table emission
2026-09-15 9:04 ` [PATCH V5 2/2] rust: kernel: Add KUnit tests for powerpc ARCH_WARN_ASM bug table emission Mukesh Kumar Chaurasiya (IBM)
@ 2026-09-16 14:06 ` FUJITA Tomonori
2026-09-16 14:17 ` Mukesh Kumar Chaurasiya
2026-09-16 14:21 ` David Gow
1 sibling, 1 reply; 7+ messages in thread
From: FUJITA Tomonori @ 2026-09-16 14:06 UTC (permalink / raw)
To: mkchauras
Cc: maddy, mpe, npiggin, chleroy, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, daniel.almeida,
tamird, acourbot, work, pjw, palmer, aou, alex, nathan,
ndesaulniers, morbo, justinstitt, ynorov, david, joelagnelf,
fujita.tomonori, linkmauve, linuxppc-dev, linux-kernel,
rust-for-linux, linux-riscv, llvm
On Tue, 15 Sep 2026 14:34:53 +0530
"Mukesh Kumar Chaurasiya (IBM)" <mkchauras@gmail.com> wrote:
> Verify that the __bug_table entry emitted by ARCH_WARN_ASM has a correct
> bug_addr displacement ― i.e. the '1b' label reference in _EMIT_BUG_ENTRY
> resolves to the trap instruction ― by calling find_bug() with the exact
> virtual address of the twi instruction, mirroring what the real powerpc
> trap handler does.
>
> The trap address is captured at link time via a .dc.a 1b relocation placed
> in .data by the global_asm! block. global_asm! is used instead of asm!
> because LLVM eliminates asm! blocks in dead branches; global_asm! is
> file-scope and always emitted. BUG_KUNIT_TRAP_ADDR is defined as a .global
> symbol directly on the .dc.a word so the linker relocation lands on it ―
> a Rust static initialized to zero would end up in BSS where relocations are
> not applied.
>
> .dc.a emits a pointer-width word (4 bytes on ppc32, 8 bytes on ppc64),
> so BUG_KUNIT_TRAP_ADDR is declared as usize on the Rust side, making the
> tests correct on both ppc32 and ppc64. The global_asm! block is split into
> two cfg-gated variants (CONFIG_PPC64 / !CONFIG_PPC64) to select the right
> .balign since concat!() only accepts literals.
>
> Five tests are included in the rust_kernel_bug_powerpc suite:
>
> bug_entry_found - find_bug() returns non-NULL for the trap address,
> proving the bug_addr displacement is correct
> bug_entry_is_warning - the emitted entry has BUGFLAG_WARNING set
> bug_entry_file - bug_get_file_line() returns the correct source
> file (requires CONFIG_DEBUG_BUGVERBOSE)
> bug_entry_line - the recorded line number is non-zero, confirming
> the {line} operand was substituted correctly
> (requires CONFIG_DEBUG_BUGVERBOSE)
> bug_entry_addr_is_in_text - kernel_text_address() confirms the trap address
> lies in kernel text, not data or zero
>
> The suite is named rust_kernel_bug_powerpc and the Kconfig option
> CONFIG_RUST_BUG_POWERPC_KUNIT_TEST depends on PPC && GENERIC_BUG,
> covering both ppc32 and ppc64.
>
> Tested on ppc64le (ltcfujiaac-lp3, 7.3.0-rc1+): pass:5 fail:0 skip:0.
> Tested on ppc32 Book3S (QEMU mac99 G4, chrp32_defconfig): pass:5 fail:0 skip:0.
> Tested on ppc64le (QEMU pseries, pseries_le_defconfig): pass:5 fail:0 skip:0.
>
> Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
> ---
> rust/kernel/Kconfig.test | 13 +++
> rust/kernel/bug.rs | 209 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 222 insertions(+)
I think that testing warn_on!, in particular that the right address is
put in the bug entry, is a good idea.
I'd prefer such a test to work on every architecture. This one depends on
details of powerpc's ARCH_WARN_ASM, such as the '1b' label. KUnit has a
warning suppression API. A test can call warn_on! inside a suppression
block and check that one warning was counted. The counter is only
incremented after find_bug() found the entry, so a wrong address is caught
too.
I'll send a patch for that shortly.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH V5 2/2] rust: kernel: Add KUnit tests for powerpc ARCH_WARN_ASM bug table emission
2026-09-16 14:06 ` FUJITA Tomonori
@ 2026-09-16 14:17 ` Mukesh Kumar Chaurasiya
0 siblings, 0 replies; 7+ messages in thread
From: Mukesh Kumar Chaurasiya @ 2026-09-16 14:17 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: maddy, mpe, npiggin, chleroy, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, daniel.almeida,
tamird, acourbot, work, pjw, palmer, aou, alex, nathan,
ndesaulniers, morbo, justinstitt, ynorov, david, joelagnelf,
fujita.tomonori, linkmauve, linuxppc-dev, linux-kernel,
rust-for-linux, linux-riscv, llvm
On Wed, Sep 16, 2026 at 11:06:58PM +0900, FUJITA Tomonori wrote:
> On Tue, 15 Sep 2026 14:34:53 +0530
> "Mukesh Kumar Chaurasiya (IBM)" <mkchauras@gmail.com> wrote:
>
> > Verify that the __bug_table entry emitted by ARCH_WARN_ASM has a correct
> > bug_addr displacement ― i.e. the '1b' label reference in _EMIT_BUG_ENTRY
> > resolves to the trap instruction ― by calling find_bug() with the exact
> > virtual address of the twi instruction, mirroring what the real powerpc
> > trap handler does.
> >
> > The trap address is captured at link time via a .dc.a 1b relocation placed
> > in .data by the global_asm! block. global_asm! is used instead of asm!
> > because LLVM eliminates asm! blocks in dead branches; global_asm! is
> > file-scope and always emitted. BUG_KUNIT_TRAP_ADDR is defined as a .global
> > symbol directly on the .dc.a word so the linker relocation lands on it ―
> > a Rust static initialized to zero would end up in BSS where relocations are
> > not applied.
> >
> > .dc.a emits a pointer-width word (4 bytes on ppc32, 8 bytes on ppc64),
> > so BUG_KUNIT_TRAP_ADDR is declared as usize on the Rust side, making the
> > tests correct on both ppc32 and ppc64. The global_asm! block is split into
> > two cfg-gated variants (CONFIG_PPC64 / !CONFIG_PPC64) to select the right
> > .balign since concat!() only accepts literals.
> >
> > Five tests are included in the rust_kernel_bug_powerpc suite:
> >
> > bug_entry_found - find_bug() returns non-NULL for the trap address,
> > proving the bug_addr displacement is correct
> > bug_entry_is_warning - the emitted entry has BUGFLAG_WARNING set
> > bug_entry_file - bug_get_file_line() returns the correct source
> > file (requires CONFIG_DEBUG_BUGVERBOSE)
> > bug_entry_line - the recorded line number is non-zero, confirming
> > the {line} operand was substituted correctly
> > (requires CONFIG_DEBUG_BUGVERBOSE)
> > bug_entry_addr_is_in_text - kernel_text_address() confirms the trap address
> > lies in kernel text, not data or zero
> >
> > The suite is named rust_kernel_bug_powerpc and the Kconfig option
> > CONFIG_RUST_BUG_POWERPC_KUNIT_TEST depends on PPC && GENERIC_BUG,
> > covering both ppc32 and ppc64.
> >
> > Tested on ppc64le (ltcfujiaac-lp3, 7.3.0-rc1+): pass:5 fail:0 skip:0.
> > Tested on ppc32 Book3S (QEMU mac99 G4, chrp32_defconfig): pass:5 fail:0 skip:0.
> > Tested on ppc64le (QEMU pseries, pseries_le_defconfig): pass:5 fail:0 skip:0.
> >
> > Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
> > ---
> > rust/kernel/Kconfig.test | 13 +++
> > rust/kernel/bug.rs | 209 +++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 222 insertions(+)
>
> I think that testing warn_on!, in particular that the right address is
> put in the bug entry, is a good idea.
>
> I'd prefer such a test to work on every architecture. This one depends on
> details of powerpc's ARCH_WARN_ASM, such as the '1b' label. KUnit has a
> warning suppression API. A test can call warn_on! inside a suppression
> block and check that one warning was counted. The counter is only
> incremented after find_bug() found the entry, so a wrong address is caught
> too.
>
> I'll send a patch for that shortly.
yeah,
makes sense.
Regards,
Mukesh
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH V5 2/2] rust: kernel: Add KUnit tests for powerpc ARCH_WARN_ASM bug table emission
2026-09-15 9:04 ` [PATCH V5 2/2] rust: kernel: Add KUnit tests for powerpc ARCH_WARN_ASM bug table emission Mukesh Kumar Chaurasiya (IBM)
2026-09-16 14:06 ` FUJITA Tomonori
@ 2026-09-16 14:21 ` David Gow
2026-09-16 14:46 ` Mukesh Kumar Chaurasiya
1 sibling, 1 reply; 7+ messages in thread
From: David Gow @ 2026-09-16 14:21 UTC (permalink / raw)
To: Mukesh Kumar Chaurasiya (IBM),
maddy, mpe, npiggin, chleroy, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, daniel.almeida,
tamird, acourbot, work, pjw, palmer, aou, alex, nathan,
ndesaulniers, morbo, justinstitt, ynorov, joelagnelf,
fujita.tomonori, linkmauve, linuxppc-dev, linux-kernel,
rust-for-linux, linux-riscv, llvm
Le 15/09/2026 à 17:04, Mukesh Kumar Chaurasiya (IBM) a écrit :
> Verify that the __bug_table entry emitted by ARCH_WARN_ASM has a correct
> bug_addr displacement — i.e. the '1b' label reference in _EMIT_BUG_ENTRY
> resolves to the trap instruction — by calling find_bug() with the exact
> virtual address of the twi instruction, mirroring what the real powerpc
> trap handler does.
>
> The trap address is captured at link time via a .dc.a 1b relocation placed
> in .data by the global_asm! block. global_asm! is used instead of asm!
> because LLVM eliminates asm! blocks in dead branches; global_asm! is
> file-scope and always emitted. BUG_KUNIT_TRAP_ADDR is defined as a .global
> symbol directly on the .dc.a word so the linker relocation lands on it —
> a Rust static initialized to zero would end up in BSS where relocations are
> not applied.
>
> .dc.a emits a pointer-width word (4 bytes on ppc32, 8 bytes on ppc64),
> so BUG_KUNIT_TRAP_ADDR is declared as usize on the Rust side, making the
> tests correct on both ppc32 and ppc64. The global_asm! block is split into
> two cfg-gated variants (CONFIG_PPC64 / !CONFIG_PPC64) to select the right
> .balign since concat!() only accepts literals.
>
> Five tests are included in the rust_kernel_bug_powerpc suite:
>
> bug_entry_found - find_bug() returns non-NULL for the trap address,
> proving the bug_addr displacement is correct
> bug_entry_is_warning - the emitted entry has BUGFLAG_WARNING set
> bug_entry_file - bug_get_file_line() returns the correct source
> file (requires CONFIG_DEBUG_BUGVERBOSE)
> bug_entry_line - the recorded line number is non-zero, confirming
> the {line} operand was substituted correctly
> (requires CONFIG_DEBUG_BUGVERBOSE)
> bug_entry_addr_is_in_text - kernel_text_address() confirms the trap address
> lies in kernel text, not data or zero
>
> The suite is named rust_kernel_bug_powerpc and the Kconfig option
> CONFIG_RUST_BUG_POWERPC_KUNIT_TEST depends on PPC && GENERIC_BUG,
> covering both ppc32 and ppc64.
>
> Tested on ppc64le (ltcfujiaac-lp3, 7.3.0-rc1+): pass:5 fail:0 skip:0.
> Tested on ppc32 Book3S (QEMU mac99 G4, chrp32_defconfig): pass:5 fail:0 skip:0.
> Tested on ppc64le (QEMU pseries, pseries_le_defconfig): pass:5 fail:0 skip:0.
>
> Signed-off-by: Mukesh Kumar Chaurasiya (IBM) <mkchauras@gmail.com>
> ---
This works for me under QEMU, but I have a couple of small suggestions.
1. You should use the kernel CStr and kernel::ffi::c_* types, not the
ones from core. This properly matches the way the kernel uses
-funsigned-char. For any architectures where char is normally signed,
this would fail to build. Which brings me to…
2. Why is this specific to powerpc? Shouldn't it be possible to do this
in a way that works across more architectures? (Indeed, the current
implementation happens to pass on x86_64 with the above fix, though more
would be needed for other architectures, and there's probably a better
solution overall which doesn't hardcode a special bug address.)
So this still seems like it's something worth having, but I'd much
prefer it to not be architecture-specific if we can avoid it.
Cheers,
-- David
> rust/kernel/Kconfig.test | 13 +++
> rust/kernel/bug.rs | 209 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 222 insertions(+)
>
> diff --git a/rust/kernel/Kconfig.test b/rust/kernel/Kconfig.test
> index e6a5c7a795f0..5a82f0812c89 100644
> --- a/rust/kernel/Kconfig.test
> +++ b/rust/kernel/Kconfig.test
> @@ -83,4 +83,17 @@ config RUST_BITFIELD_KUNIT_TEST
>
> If unsure, say N.
>
> +config RUST_BUG_POWERPC_KUNIT_TEST
> + bool "KUnit tests for powerpc ARCH_WARN_ASM bug table emission" if !KUNIT_ALL_TESTS
> + depends on PPC && GENERIC_BUG
> + default KUNIT_ALL_TESTS
> + help
> + This option enables KUnit tests that verify ARCH_WARN_ASM emits a
> + correct __bug_table entry on powerpc (both ppc32 and ppc64): the
> + bug_addr displacement must resolve back to the trap instruction so
> + that find_bug() can locate the entry — exactly as the real trap
> + handler does.
> +
> + If unsure, say N.
> +
> endif
> diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs
> index 3566f0234ca4..cd90bd511479 100644
> --- a/rust/kernel/bug.rs
> +++ b/rust/kernel/bug.rs
> @@ -152,3 +152,212 @@ macro_rules! warn_on {
> cond
> }};
> }
> +
> +// Test-only constants and file static referenced by the global_asm block below.
> +//
> +// global_asm! is file-scope and always emitted — LLVM cannot eliminate it,
> +// unlike asm! inside a function which is subject to dead-code removal.
> +//
> +// BUG_KUNIT_TRAP_ADDR is declared as a .global symbol entirely inside the
> +// global_asm! block so the .dc.a 1b relocation lands directly on it.
> +// A Rust static initialized to zero would end up in BSS; the linker does
> +// not apply relocations to BSS, so the address would stay zero at runtime.
> +#[cfg(CONFIG_RUST_BUG_POWERPC_KUNIT_TEST)]
> +mod test_statics {
> + use crate::bindings::{bug_entry, BUGFLAG_WARNING, TAINT_WARN};
> +
> + pub(super) const FLAGS: u32 = BUGFLAG_WARNING | (TAINT_WARN << 8);
> + pub(super) const SIZE: usize = core::mem::size_of::<bug_entry>();
> +
> + // LINE and BUG_KUNIT_FILE are only referenced by the {file}/{line}
> + // operands in global_asm!, which are only present when
> + // CONFIG_DEBUG_BUGVERBOSE is set (the non-verbose _EMIT_BUG_ENTRY
> + // drops the file/line fields from the bug table entry entirely).
> + #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
> + pub(super) const LINE: u32 = line!();
> +
> + // Null-terminated source file name — the assembler references this symbol
> + // for the verbose file pointer in __bug_table, same as warn_flags!.
> + #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
> + const _FILE: &[u8] = file!().as_bytes();
> + #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
> + #[no_mangle]
> + pub(super) static BUG_KUNIT_FILE: [u8; _FILE.len() + 1] = {
> + let mut bytes = [0u8; _FILE.len() + 1];
> + let mut i = 0;
> + while i < _FILE.len() {
> + bytes[i] = _FILE[i];
> + i += 1;
> + }
> + bytes
> + };
> +}
> +
> +// Emit ARCH_WARN_ASM at file scope and capture the trap address.
> +//
> +// BUG_KUNIT_TRAP_ADDR is defined as a .global symbol right on top of the
> +// .dc.a 1b directive so the linker resolves the relocation directly into
> +// that symbol's storage — no BSS, no zero-init problem.
> +// .dc.a emits a pointer-width word (4 bytes on ppc32, 8 bytes on ppc64),
> +// matching the usize declaration on the Rust side.
> +#[cfg(all(CONFIG_RUST_BUG_POWERPC_KUNIT_TEST, CONFIG_PPC64, CONFIG_DEBUG_BUGVERBOSE))]
> +::core::arch::global_asm!(
> + concat!(
> + include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
> + ".pushsection .data\n\t",
> + ".balign 8\n\t",
> + ".global BUG_KUNIT_TRAP_ADDR\n\t",
> + "BUG_KUNIT_TRAP_ADDR:\n\t",
> + ".dc.a 1b\n\t",
> + ".popsection\n",
> + ),
> + file = sym test_statics::BUG_KUNIT_FILE,
> + line = const test_statics::LINE,
> + flags = const test_statics::FLAGS,
> + size = const test_statics::SIZE,
> +);
It'd be really nice to use the warn_flags!() macro here (or even all of
warn_on!() instead of hardcoding a separate implementation here. But
even if we can't (due, e.g., to the need for global_asm! vs asm!), it'd
be nice if this were more similar to the warn_flags!() implementation.
Not only would that reduce the likelihood of these deviating needlessly,
but it'd better support more architectures, too.
For example, the commented "{size}" would help on architectures where
size isn't needed.
> +
> +#[cfg(all(CONFIG_RUST_BUG_POWERPC_KUNIT_TEST, CONFIG_PPC64, not(CONFIG_DEBUG_BUGVERBOSE)))]
> +::core::arch::global_asm!(
> + concat!(
> + include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
> + ".pushsection .data\n\t",
> + ".balign 8\n\t",
> + ".global BUG_KUNIT_TRAP_ADDR\n\t",
> + "BUG_KUNIT_TRAP_ADDR:\n\t",
> + ".dc.a 1b\n\t",
> + ".popsection\n",
> + ),
> + flags = const test_statics::FLAGS,
> + size = const test_statics::SIZE,
> +);
> +
> +#[cfg(all(CONFIG_RUST_BUG_POWERPC_KUNIT_TEST, not(CONFIG_PPC64), CONFIG_DEBUG_BUGVERBOSE))]
> +::core::arch::global_asm!(
> + concat!(
> + include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
> + ".pushsection .data\n\t",
> + ".balign 4\n\t",
> + ".global BUG_KUNIT_TRAP_ADDR\n\t",
> + "BUG_KUNIT_TRAP_ADDR:\n\t",
> + ".dc.a 1b\n\t",
> + ".popsection\n",
> + ),
> + file = sym test_statics::BUG_KUNIT_FILE,
> + line = const test_statics::LINE,
> + flags = const test_statics::FLAGS,
> + size = const test_statics::SIZE,
> +);
> +
> +#[cfg(all(CONFIG_RUST_BUG_POWERPC_KUNIT_TEST, not(CONFIG_PPC64), not(CONFIG_DEBUG_BUGVERBOSE)))]
> +::core::arch::global_asm!(
> + concat!(
> + include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")),
> + ".pushsection .data\n\t",
> + ".balign 4\n\t",
> + ".global BUG_KUNIT_TRAP_ADDR\n\t",
> + "BUG_KUNIT_TRAP_ADDR:\n\t",
> + ".dc.a 1b\n\t",
> + ".popsection\n",
> + ),
> + flags = const test_statics::FLAGS,
> + size = const test_statics::SIZE,
> +);
> +
> +#[cfg(CONFIG_RUST_BUG_POWERPC_KUNIT_TEST)]
> +#[::kernel::macros::kunit_tests(rust_kernel_bug_powerpc)]
> +mod tests {
> + use crate::bindings;
> +
> + fn trap_addr() -> usize {
> + // BUG_KUNIT_TRAP_ADDR is a .global symbol defined in the global_asm!
> + // block above, placed in .data at the exact .dc.a 1b relocation word.
> + // The linker resolves it to the virtual address of the twi instruction
> + // before any Rust code runs, so reading it here is always safe.
> + extern "C" {
> + // .dc.a emits a pointer-width word: 4 bytes on ppc32, 8 on ppc64.
> + // usize matches the native pointer width on both.
> + static BUG_KUNIT_TRAP_ADDR: usize;
> + }
> + // SAFETY: read-only after link time, no concurrent mutation possible.
> + unsafe { BUG_KUNIT_TRAP_ADDR }
> + }
> +
> + /// The `__bug_table` entry emitted by `ARCH_WARN_ASM` must be locatable
> + /// via `find_bug()` using the trap instruction's address. A NULL result
> + /// means the `1b` label reference in `_EMIT_BUG_ENTRY` resolved to the
> + /// wrong address and the real trap handler would not recognise the site.
> + #[test]
> + fn bug_entry_found() {
> + // Non-zero proves the .dc.a relocation was resolved by the linker.
> + assert!(trap_addr() != 0);
> +
> + // SAFETY: find_bug() is always safe to call with any address; it
> + // simply walks __bug_table and returns NULL if nothing matches.
> + let entry = unsafe { bindings::find_bug(trap_addr()) };
> + // Non-NULL proves the bug_addr displacement in _EMIT_BUG_ENTRY is correct.
> + assert!(!entry.is_null());
> + }
> +
> + /// The emitted entry must be flagged as a warning (not a hard BUG).
> + #[test]
> + fn bug_entry_is_warning() {
> + assert!(trap_addr() != 0);
> + let entry = unsafe { bindings::find_bug(trap_addr()) };
> + assert!(!entry.is_null());
> + // SAFETY: entry is non-null and points to a valid bug_entry.
> + let flags = unsafe { (*entry).flags } as u32;
> + assert!(flags & bindings::BUGFLAG_WARNING != 0);
> + }
> +
> + /// With `CONFIG_DEBUG_BUGVERBOSE` the entry must record a non-null file
> + /// pointer pointing back into this source file.
> + #[test]
> + #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
> + fn bug_entry_file() {
> + use core::ffi::CStr;
Please don't use core::ffi::* in the kernel. The kernel provides its own
CStr and c_char implementations. Otherwise, this can lead to issues as
the kernel builds with -funsigned-char, but core::ffi will use the
architecture default.
> +
> + assert!(trap_addr() != 0);
> + let entry = unsafe { bindings::find_bug(trap_addr()) };
> + assert!(!entry.is_null());
> +
> + let mut file_ptr: *const core::ffi::c_char = core::ptr::null();
Again, core::ffi::c_char should be avoided in kernel code. Use
[kernel::]ffi:c_char
> + let mut line: u32 = 0;
> + // SAFETY: entry is non-null and valid; file_ptr and line are local
> + // variables passed as out-parameters.
> + unsafe { bindings::bug_get_file_line(entry, &mut file_ptr, &mut line) };
> +
> + assert!(!file_ptr.is_null());
> + // SAFETY: file_ptr is a null-terminated C string from BUG_KUNIT_FILE.
> + let file_str = unsafe { CStr::from_ptr(file_ptr) }.to_str().unwrap_or("");
> + assert!(file_str.contains("bug"));
> + }
> +
> + /// With `CONFIG_DEBUG_BUGVERBOSE` the recorded line number must be
> + /// non-zero (a zero line would mean the asm operand was not substituted).
> + #[test]
> + #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
> + fn bug_entry_line() {
> + assert!(trap_addr() != 0);
> + let entry = unsafe { bindings::find_bug(trap_addr()) };
> + assert!(!entry.is_null());
> +
> + let mut file_ptr: *const core::ffi::c_char = core::ptr::null();
And again.
> + let mut line: u32 = 0;
> + // SAFETY: entry is non-null and valid.
> + unsafe { bindings::bug_get_file_line(entry, &mut file_ptr, &mut line) };
> +
> + assert!(line != 0);
> + }
> +
> + /// The trap address stored in `__bug_table` must lie within the kernel
> + /// text segment. If the label reference in `_EMIT_BUG_ENTRY` resolved
> + /// to data or zero, `kernel_text_address()` would return false.
> + #[test]
> + fn bug_entry_addr_is_in_text() {
> + assert!(trap_addr() != 0);
> + // SAFETY: kernel_text_address() is always safe to call with any addr.
> + let in_text = unsafe { bindings::kernel_text_address(trap_addr()) };
> + assert!(in_text != 0);
> + }
> +}
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH V5 2/2] rust: kernel: Add KUnit tests for powerpc ARCH_WARN_ASM bug table emission
2026-09-16 14:21 ` David Gow
@ 2026-09-16 14:46 ` Mukesh Kumar Chaurasiya
0 siblings, 0 replies; 7+ messages in thread
From: Mukesh Kumar Chaurasiya @ 2026-09-16 14:46 UTC (permalink / raw)
To: David Gow
Cc: maddy, mpe, npiggin, chleroy, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, daniel.almeida,
tamird, acourbot, work, pjw, palmer, aou, alex, nathan,
ndesaulniers, morbo, justinstitt, ynorov, joelagnelf,
fujita.tomonori, linkmauve, linuxppc-dev, linux-kernel,
rust-for-linux, linux-riscv, llvm
[...]
> This works for me under QEMU, but I have a couple of small suggestions.
>
> 1. You should use the kernel CStr and kernel::ffi::c_* types, not the ones
> from core. This properly matches the way the kernel uses -funsigned-char.
> For any architectures where char is normally signed, this would fail to
> build. Which brings me to…
>
Thanks for pointing it out, i missed this.
> 2. Why is this specific to powerpc? Shouldn't it be possible to do this in a
> way that works across more architectures? (Indeed, the current
> implementation happens to pass on x86_64 with the above fix, though more
> would be needed for other architectures, and there's probably a better
> solution overall which doesn't hardcode a special bug address.)
>
> So this still seems like it's something worth having, but I'd much prefer it
> to not be architecture-specific if we can avoid it.
>
> Cheers,
> -- David
>
The dependency to support these on all archs as it is would be to probe
the implementation and figure out which will again cause it to have some
arch specific stuff. I couldn't find a clean way of having all that at
the same place without a lot of #cfg's so i just made it powerpc
specific.
Tomonori sent out a patch with a single test that's arch agnostic, but
for ppc I would still like to keep these if the maintainer agrees.
[...]
>
> It'd be really nice to use the warn_flags!() macro here (or even all of
> warn_on!() instead of hardcoding a separate implementation here. But even if
> we can't (due, e.g., to the need for global_asm! vs asm!), it'd be nice if
> this were more similar to the warn_flags!() implementation. Not only would
> that reduce the likelihood of these deviating needlessly, but it'd better
> support more architectures, too.
>
> For example, the commented "{size}" would help on architectures where size
> isn't needed.
>
Let me try this.
[...]
> > + /// With `CONFIG_DEBUG_BUGVERBOSE` the entry must record a non-null file
> > + /// pointer pointing back into this source file.
> > + #[test]
> > + #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
> > + fn bug_entry_file() {
> > + use core::ffi::CStr;
>
> Please don't use core::ffi::* in the kernel. The kernel provides its own
> CStr and c_char implementations. Otherwise, this can lead to issues as the
> kernel builds with -funsigned-char, but core::ffi will use the architecture
> default.
>
Sure.
> > +
> > + assert!(trap_addr() != 0);
> > + let entry = unsafe { bindings::find_bug(trap_addr()) };
> > + assert!(!entry.is_null());
> > +
> > + let mut file_ptr: *const core::ffi::c_char = core::ptr::null();
>
> Again, core::ffi::c_char should be avoided in kernel code. Use
> [kernel::]ffi:c_char
>
Sure.
[...]
> > + /// With `CONFIG_DEBUG_BUGVERBOSE` the recorded line number must be
> > + /// non-zero (a zero line would mean the asm operand was not substituted).
> > + #[test]
> > + #[cfg(CONFIG_DEBUG_BUGVERBOSE)]
> > + fn bug_entry_line() {
> > + assert!(trap_addr() != 0);
> > + let entry = unsafe { bindings::find_bug(trap_addr()) };
> > + assert!(!entry.is_null());
> > +
> > + let mut file_ptr: *const core::ffi::c_char = core::ptr::null();
>
> And again.
>
Sure
Regards,
Mukesh
[...]
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-09-16 14:46 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-15 9:04 [PATCH v5 0/2] powerpc: Add ARCH_WARN_ASM and KUnit tests for Rust WARN() support Mukesh Kumar Chaurasiya (IBM)
2026-09-15 9:04 ` [PATCH V5 1/2] powerpc/bug: Add ARCH_WARN_ASM and refactor _EMIT_BUG_ENTRY for Rust support Mukesh Kumar Chaurasiya (IBM)
2026-09-15 9:04 ` [PATCH V5 2/2] rust: kernel: Add KUnit tests for powerpc ARCH_WARN_ASM bug table emission Mukesh Kumar Chaurasiya (IBM)
2026-09-16 14:06 ` FUJITA Tomonori
2026-09-16 14:17 ` Mukesh Kumar Chaurasiya
2026-09-16 14:21 ` David Gow
2026-09-16 14:46 ` Mukesh Kumar Chaurasiya
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®