This is the s390 variant of commit 5b472b6e5bd9 ("x86_64/bug: Implement
__WARN_printf()"). See the x86 commit for the general idea; there are only
implementation details which are different.
With the new exception based __WARN_printf() implementation the generated
code for a simple WARN() is simplified.
For example:
void foo(int a) { WARN(a, "bar"); }
Before this change the generated code looks like this:
0000000000000210 <foo>:
210: c0 04 00 00 00 00 jgnop 210 <foo>
216: ec 26 00 06 00 7c cgijne %r2,0,222 <foo+0x12>
21c: c0 f4 00 00 00 00 jg 21c <foo+0xc>
21e: R_390_PC32DBL __s390_indirect_jump_r14+0x2
222: eb ef f0 88 00 24 stmg %r14,%r15,136(%r15)
228: b9 04 00 ef lgr %r14,%r15
22c: e3 f0 ff e8 ff 71 lay %r15,-24(%r15)
232: e3 e0 f0 98 00 24 stg %r14,152(%r15)
238: c0 20 00 00 00 00 larl %r2,238 <foo+0x28>
23a: R_390_PC32DBL .LC48+0x2
23e: c0 e5 00 00 00 00 brasl %r14,23e <foo+0x2e>
240: R_390_PLT32DBL __warn_printk+0x2
244: af 00 00 00 mc 0,0
248: eb ef f0 a0 00 04 lmg %r14,%r15,160(%r15)
24e: c0 f4 00 00 00 00 jg 24e <foo+0x3e>
250: R_390_PC32DBL __s390_indirect_jump_r14+0x2
With this change the generated code looks like this:
0000000000000210 <foo>:
210: c0 04 00 00 00 00 jgnop 210 <foo>
216: ec 26 00 06 00 7c cgijne %r2,0,222 <foo+0x12>
21c: c0 f4 00 00 00 00 jg 21c <foo+0xc>
21e: R_390_PC32DBL __s390_indirect_jump_r14+0x2
222: c0 20 00 00 00 00 larl %r2,222 <foobar+0x12>
224: R_390_PC32DBL __bug_table+0x2
228: c0 f4 00 00 00 00 jg 228 <foobar+0x18>
22a: R_390_PLT32DBL __WARN_trap+0x2
Downside is that the call trace now starts at __WARN_trap():
------------[ cut here ]------------
bar
WARNING: arch/s390/kernel/setup.c:1017 at 0x0, CPU#0: swapper/0/0
...
Krnl PSW : 0704c00180000000 000003ffe0f6a3b4 (__WARN_trap+0x4/0x10)
...
Krnl Code: 000003ffe0f6a3ac: 0707 bcr 0,%r7
000003ffe0f6a3ae: 0707 bcr 0,%r7
*000003ffe0f6a3b0: af000001 mc 1,0
>000003ffe0f6a3b4: 07fe bcr 15,%r14
000003ffe0f6a3b6: 47000700 bc 0,1792
000003ffe0f6a3ba: 0707 bcr 0,%r7
000003ffe0f6a3bc: 0707 bcr 0,%r7
000003ffe0f6a3be: 0707 bcr 0,%r7
Call Trace:
[<000003ffe0f6a3b4>] __WARN_trap+0x4/0x10
([<000003ffe185a54c>] start_kernel+0x53c/0x5d8)
[<000003ffe010002e>] startup_continue+0x2e/0x40
Which isn't too helpful. This can be addressed by just skipping __WARN_trap(),
which will be addressed in a later patch.
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
---
arch/s390/include/asm/bug.h | 62 +++++++++++++++++++++++++++++++++----
arch/s390/kernel/entry.S | 7 +++++
arch/s390/kernel/traps.c | 34 +++++++++++++++++++-
3 files changed, 96 insertions(+), 7 deletions(-)
diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h
index 1e1dece0eee4..f3aca691bae2 100644
--- a/arch/s390/include/asm/bug.h
+++ b/arch/s390/include/asm/bug.h
@@ -6,6 +6,7 @@
#include <linux/const.h>
#define MONCODE_BUG _AC(0, U)
+#define MONCODE_BUG_ARG _AC(1, U)
#ifndef __ASSEMBLER__
#ifdef CONFIG_BUG
@@ -25,16 +26,20 @@
#define WARN_CONDITION_STR(cond_str) ""
#endif
+#define __BUG_ENTRY(format, file, line, flags, size) \
+ " .section __bug_table,\"aw\"\n" \
+ "1: .long 0b - . # bug_entry::bug_addr\n" \
+ __BUG_ENTRY_VERBOSE(format, file, line) \
+ " .short "flags" # bug_entry::flags\n" \
+ " .org 1b+"size"\n" \
+ " .previous"
+
#define __BUG_ASM(cond_str, flags) \
do { \
asm_inline volatile("\n" \
"0: mc %[monc](%%r0),0\n" \
- " .section __bug_table,\"aw\"\n" \
- "1: .long 0b - . # bug_entry::bug_addr\n" \
- __BUG_ENTRY_VERBOSE("%[frmt]", "%[file]", "%[line]") \
- " .short %[flgs] # bug_entry::flags\n" \
- " .org 1b+%[size]\n" \
- " .previous" \
+ __BUG_ENTRY("%[frmt]", "%[file]", "%[line]", \
+ "%[flgs]", "%[size]") \
: \
: [monc] "i" (MONCODE_BUG), \
[frmt] "i" (WARN_CONDITION_STR(cond_str)), \
@@ -55,8 +60,53 @@ do { \
__BUG_ASM(cond_str, BUGFLAG_WARNING | (flags)); \
} while (0)
+#define __WARN_bug_entry(flags, format) \
+({ \
+ struct bug_entry *bug; \
+ \
+ asm_inline volatile("\n" \
+ "0: larl %[bug],1f\n" \
+ __BUG_ENTRY("%[frmt]", "%[file]", "%[line]", \
+ "%[flgs]", "%[size]") \
+ : [bug] "=d" (bug) \
+ : [frmt] "i" (format), \
+ [file] "i" (__FILE__), \
+ [line] "i" (__LINE__), \
+ [flgs] "i" (flags), \
+ [size] "i" (sizeof(struct bug_entry))); \
+ bug; \
+})
+
+/*
+ * Variable Argument List (va_list) as defined in ELF Application
+ * Binary Interface s390x Supplement documentation.
+ */
+struct arch_va_list {
+ long __gpr;
+ long __fpr;
+ void *__overflow_arg_area;
+ void *__reg_save_area;
+};
+
+struct bug_entry;
+struct pt_regs;
+
+void *__warn_args(struct arch_va_list *args, struct pt_regs *regs);
+void __WARN_trap(struct bug_entry *bug, ...);
+
+#define __WARN_print_arg(flags, format, arg...) \
+do { \
+ int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS; \
+ \
+ __WARN_trap(__WARN_bug_entry(__flags, format), ## arg); \
+} while (0)
+
+#define __WARN_printf(taint, fmt, arg...) \
+ __WARN_print_arg(BUGFLAG_TAINT(taint), fmt, ## arg)
+
#define HAVE_ARCH_BUG
#define HAVE_ARCH_BUG_FORMAT
+#define HAVE_ARCH_BUG_FORMAT_ARGS
#endif /* CONFIG_BUG */
#endif /* __ASSEMBLER__ */
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index b7f1553d9ee5..23ff05746aa6 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -23,6 +23,7 @@
#include <asm/unistd.h>
#include <asm/page.h>
#include <asm/sigp.h>
+#include <asm/bug.h>
#include <asm/irq.h>
#include <asm/fpu-insn.h>
#include <asm/setup.h>
@@ -173,6 +174,12 @@ SYM_FUNC_START(__switch_to_asm)
BR_EX %r14
SYM_FUNC_END(__switch_to_asm)
+SYM_FUNC_START(__WARN_trap)
+ mc MONCODE_BUG_ARG(%r0),0
+ BR_EX %r14
+SYM_FUNC_END(__WARN_trap)
+EXPORT_SYMBOL(__WARN_trap)
+
#if IS_ENABLED(CONFIG_KVM)
/*
* __sie64a calling convention:
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index b2d6d7cc3b17..8aca5858b403 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -23,6 +23,7 @@
#include <linux/cpu.h>
#include <linux/entry-common.h>
#include <linux/kmsan.h>
+#include <linux/bug.h>
#include <asm/asm-extable.h>
#include <asm/irqflags.h>
#include <asm/ptrace.h>
@@ -220,11 +221,42 @@ static void space_switch_exception(struct pt_regs *regs)
do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
}
+void *__warn_args(struct arch_va_list *args, struct pt_regs *regs)
+{
+ struct stack_frame *stack_frame;
+
+ /*
+ * Generate va_list from pt_regs. See ELF Application Binary Interface
+ * s390x Supplement documentation for details.
+ *
+ * - __overflow_arg_area needs to point to the parameter area, which
+ * is right above the standard stack frame (160 bytes)
+ *
+ * - __reg_save_area needs to point to a register save area where
+ * general registers (%r2 - %r6) can be found at offset 16. Which
+ * means that the gprs save area of pt_regs can be used
+ *
+ * - __gpr must be set to one, since the first parameter has been
+ * processed (pointer to bug_entry)
+ */
+ stack_frame = (struct stack_frame *)regs->gprs[15];
+ args->__overflow_arg_area = stack_frame + 1;
+ args->__reg_save_area = regs->gprs;
+ args->__gpr = 1;
+ return args;
+}
+
static void monitor_event_exception(struct pt_regs *regs)
{
+ enum bug_trap_type btt;
+
if (user_mode(regs))
return;
- switch (report_bug(regs->psw.addr - (regs->int_code >> 16), regs)) {
+ if (regs->monitor_code == MONCODE_BUG_ARG)
+ btt = report_bug_entry((struct bug_entry *)regs->gprs[2], regs);
+ else
+ btt = report_bug(regs->psw.addr - (regs->int_code >> 16), regs);
+ switch (btt) {
case BUG_TRAP_TYPE_NONE:
fixup_exception(regs);
break;
--
2.51.0
Hi Heiko,
kernel test robot noticed the following build errors:
[auto build test ERROR on 70075e3d0ca0b72cc983d03f7cd9796e43492980]
url: https://github.com/intel-lab-lkp/linux/commits/Heiko-Carstens/kbuild-Require-gcc-9-for-s390/20251209-202647
base: 70075e3d0ca0b72cc983d03f7cd9796e43492980
patch link: https://lore.kernel.org/r/20251209121701.1856271-7-hca%40linux.ibm.com
patch subject: [PATCH 6/9] s390/bug: Implement __WARN_printf()
config: s390-randconfig-r062-20251210 (https://download.01.org/0day-ci/archive/20251210/202512102243.DTSqDkfQ-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 14.3.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251210/202512102243.DTSqDkfQ-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202512102243.DTSqDkfQ-lkp@intel.com/
All errors (new ones prefixed by >>):
arch/s390/kernel/traps.c:224:26: warning: 'struct arch_va_list' declared inside parameter list will not be visible outside of this definition or declaration
224 | void *__warn_args(struct arch_va_list *args, struct pt_regs *regs)
| ^~~~~~~~~~~~
arch/s390/kernel/traps.c:224:7: warning: no previous prototype for '__warn_args' [-Wmissing-prototypes]
224 | void *__warn_args(struct arch_va_list *args, struct pt_regs *regs)
| ^~~~~~~~~~~
arch/s390/kernel/traps.c: In function '__warn_args':
>> arch/s390/kernel/traps.c:243:13: error: invalid use of undefined type 'struct arch_va_list'
243 | args->__overflow_arg_area = stack_frame + 1;
| ^~
arch/s390/kernel/traps.c:244:13: error: invalid use of undefined type 'struct arch_va_list'
244 | args->__reg_save_area = regs->gprs;
| ^~
arch/s390/kernel/traps.c:245:13: error: invalid use of undefined type 'struct arch_va_list'
245 | args->__gpr = 1;
| ^~
vim +243 arch/s390/kernel/traps.c
223
> 224 void *__warn_args(struct arch_va_list *args, struct pt_regs *regs)
225 {
226 struct stack_frame *stack_frame;
227
228 /*
229 * Generate va_list from pt_regs. See ELF Application Binary Interface
230 * s390x Supplement documentation for details.
231 *
232 * - __overflow_arg_area needs to point to the parameter area, which
233 * is right above the standard stack frame (160 bytes)
234 *
235 * - __reg_save_area needs to point to a register save area where
236 * general registers (%r2 - %r6) can be found at offset 16. Which
237 * means that the gprs save area of pt_regs can be used
238 *
239 * - __gpr must be set to one, since the first parameter has been
240 * processed (pointer to bug_entry)
241 */
242 stack_frame = (struct stack_frame *)regs->gprs[15];
> 243 args->__overflow_arg_area = stack_frame + 1;
244 args->__reg_save_area = regs->gprs;
245 args->__gpr = 1;
246 return args;
247 }
248
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Heiko,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 70075e3d0ca0b72cc983d03f7cd9796e43492980]
url: https://github.com/intel-lab-lkp/linux/commits/Heiko-Carstens/kbuild-Require-gcc-9-for-s390/20251209-202647
base: 70075e3d0ca0b72cc983d03f7cd9796e43492980
patch link: https://lore.kernel.org/r/20251209121701.1856271-7-hca%40linux.ibm.com
patch subject: [PATCH 6/9] s390/bug: Implement __WARN_printf()
config: s390-randconfig-r062-20251210 (https://download.01.org/0day-ci/archive/20251210/202512102049.3FCpsgLh-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 14.3.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251210/202512102049.3FCpsgLh-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202512102049.3FCpsgLh-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> arch/s390/kernel/traps.c:224:26: warning: 'struct arch_va_list' declared inside parameter list will not be visible outside of this definition or declaration
224 | void *__warn_args(struct arch_va_list *args, struct pt_regs *regs)
| ^~~~~~~~~~~~
>> arch/s390/kernel/traps.c:224:7: warning: no previous prototype for '__warn_args' [-Wmissing-prototypes]
224 | void *__warn_args(struct arch_va_list *args, struct pt_regs *regs)
| ^~~~~~~~~~~
arch/s390/kernel/traps.c: In function '__warn_args':
arch/s390/kernel/traps.c:243:13: error: invalid use of undefined type 'struct arch_va_list'
243 | args->__overflow_arg_area = stack_frame + 1;
| ^~
arch/s390/kernel/traps.c:244:13: error: invalid use of undefined type 'struct arch_va_list'
244 | args->__reg_save_area = regs->gprs;
| ^~
arch/s390/kernel/traps.c:245:13: error: invalid use of undefined type 'struct arch_va_list'
245 | args->__gpr = 1;
| ^~
vim +224 arch/s390/kernel/traps.c
223
> 224 void *__warn_args(struct arch_va_list *args, struct pt_regs *regs)
225 {
226 struct stack_frame *stack_frame;
227
228 /*
229 * Generate va_list from pt_regs. See ELF Application Binary Interface
230 * s390x Supplement documentation for details.
231 *
232 * - __overflow_arg_area needs to point to the parameter area, which
233 * is right above the standard stack frame (160 bytes)
234 *
235 * - __reg_save_area needs to point to a register save area where
236 * general registers (%r2 - %r6) can be found at offset 16. Which
237 * means that the gprs save area of pt_regs can be used
238 *
239 * - __gpr must be set to one, since the first parameter has been
240 * processed (pointer to bug_entry)
241 */
242 stack_frame = (struct stack_frame *)regs->gprs[15];
243 args->__overflow_arg_area = stack_frame + 1;
244 args->__reg_save_area = regs->gprs;
245 args->__gpr = 1;
246 return args;
247 }
248
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
On Tue, Dec 09, 2025 at 01:16:58PM +0100, Heiko Carstens wrote:
> +#define __WARN_print_arg(flags, format, arg...) \
> +do { \
> + int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS; \
> + \
> + __WARN_trap(__WARN_bug_entry(__flags, format), ## arg); \
> +} while (0)
So on x86 I had to add:
asm("");
after the __WARN_trap() call above, to inhibit tail call optimization,
because:
> +void *__warn_args(struct arch_va_list *args, struct pt_regs *regs)
> +{
> + struct stack_frame *stack_frame;
> +
> + /*
> + * Generate va_list from pt_regs. See ELF Application Binary Interface
> + * s390x Supplement documentation for details.
> + *
> + * - __overflow_arg_area needs to point to the parameter area, which
> + * is right above the standard stack frame (160 bytes)
> + *
> + * - __reg_save_area needs to point to a register save area where
> + * general registers (%r2 - %r6) can be found at offset 16. Which
> + * means that the gprs save area of pt_regs can be used
> + *
> + * - __gpr must be set to one, since the first parameter has been
> + * processed (pointer to bug_entry)
> + */
> + stack_frame = (struct stack_frame *)regs->gprs[15];
> + args->__overflow_arg_area = stack_frame + 1;
> + args->__reg_save_area = regs->gprs;
> + args->__gpr = 1;
> + return args;
> +}
that would affect the stack layout here. You don't suffer this because
you have a link register like setup?
On Tue, Dec 09, 2025 at 01:35:40PM +0100, Peter Zijlstra wrote:
> On Tue, Dec 09, 2025 at 01:16:58PM +0100, Heiko Carstens wrote:
> > +#define __WARN_print_arg(flags, format, arg...) \
> > +do { \
> > + int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS; \
> > + \
> > + __WARN_trap(__WARN_bug_entry(__flags, format), ## arg); \
> > +} while (0)
>
> So on x86 I had to add:
>
> asm("");
>
> after the __WARN_trap() call above, to inhibit tail call optimization,
> because:
>
> > +void *__warn_args(struct arch_va_list *args, struct pt_regs *regs)
> > +{
> > + struct stack_frame *stack_frame;
...
> > + stack_frame = (struct stack_frame *)regs->gprs[15];
> > + args->__overflow_arg_area = stack_frame + 1;
> > + args->__reg_save_area = regs->gprs;
> > + args->__gpr = 1;
> > + return args;
> > +}
>
> that would affect the stack layout here. You don't suffer this because
> you have a link register like setup?
Yes, in case of tail call optimization everything which needs to be known to
setup va_list is passed in registers. __overflow_arg_area will then point to
garbage, but it doesn't matter since it is unused for such cases.
© 2016 - 2025 Red Hat, Inc.