summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/s390/Kconfig6
-rw-r--r--arch/s390/include/asm/asm-prototypes.h1
-rw-r--r--arch/s390/include/asm/bug.h141
-rw-r--r--arch/s390/include/asm/ptrace.h5
-rw-r--r--arch/s390/kernel/entry.S11
-rw-r--r--arch/s390/kernel/traps.c46
6 files changed, 169 insertions, 41 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index cd856318ffe5..fcce8c5db703 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -69,6 +69,12 @@ config CC_HAS_ASM_AOR_FORMAT_FLAGS
Clang versions before 19.1.0 do not support A,
O, and R inline assembly format flags.
+config CC_HAS_ASM_IMMEDIATE_STRINGS
+ def_bool !(CC_IS_GCC && GCC_VERSION < 90000)
+ help
+ GCC versions before 9.0.0 cannot handle strings as immediate
+ input operands in inline assemblies.
+
config CC_HAS_STACKPROTECTOR_GLOBAL
def_bool $(cc-option, -mstack-protector-guard=global -mstack-protector-guard-record)
diff --git a/arch/s390/include/asm/asm-prototypes.h b/arch/s390/include/asm/asm-prototypes.h
index f662eb4b9246..7bd1801cf241 100644
--- a/arch/s390/include/asm/asm-prototypes.h
+++ b/arch/s390/include/asm/asm-prototypes.h
@@ -3,6 +3,7 @@
#include <linux/kvm_host.h>
#include <linux/ftrace.h>
+#include <asm/bug.h>
#include <asm/fpu.h>
#include <asm/nospec-branch.h>
#include <asm-generic/asm-prototypes.h>
diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h
index ee9221bb5d18..59017fd3d935 100644
--- a/arch/s390/include/asm/bug.h
+++ b/arch/s390/include/asm/bug.h
@@ -2,60 +2,127 @@
#ifndef _ASM_S390_BUG_H
#define _ASM_S390_BUG_H
-#include <linux/stringify.h>
+#include <linux/compiler.h>
+#include <linux/const.h>
-#ifdef CONFIG_BUG
+#define MONCODE_BUG _AC(0, U)
+#define MONCODE_BUG_ARG _AC(1, U)
-#ifndef CONFIG_DEBUG_BUGVERBOSE
-#define _BUGVERBOSE_LOCATION(file, line)
+#ifndef __ASSEMBLER__
+#if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS)
+
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+#define __BUG_ENTRY_VERBOSE(format, file, line) \
+ " .long " format " - . # bug_entry::format\n" \
+ " .long " file " - . # bug_entry::file\n" \
+ " .short " line " # bug_entry::line\n"
#else
-#define __BUGVERBOSE_LOCATION(file, line) \
- .pushsection .rodata.str, "aMS", @progbits, 1; \
- .align 2; \
- 10002: .ascii file "\0"; \
- .popsection; \
- \
- .long 10002b - .; \
- .short line;
-#define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line)
+#define __BUG_ENTRY_VERBOSE(format, file, line)
#endif
-#ifndef CONFIG_GENERIC_BUG
-#define __BUG_ENTRY(cond_str, flags)
+#ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED
+#define WARN_CONDITION_STR(cond_str) cond_str
#else
-#define __BUG_ENTRY(cond_str, flags) \
- .pushsection __bug_table, "aw"; \
- .align 4; \
- 10000: .long 10001f - .; \
- _BUGVERBOSE_LOCATION(WARN_CONDITION_STR(cond_str) __FILE__, __LINE__) \
- .short flags; \
- .popsection; \
- 10001:
+#define WARN_CONDITION_STR(cond_str) ""
#endif
-#define ASM_BUG_FLAGS(cond_str, flags) \
- __BUG_ENTRY(cond_str, flags) \
- mc 0,0
+#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 ASM_BUG() ASM_BUG_FLAGS("", 0)
+#define __BUG_ASM(cond_str, flags) \
+do { \
+ asm_inline volatile("\n" \
+ "0: mc %[monc](%%r0),0\n" \
+ __BUG_ENTRY("%[frmt]", "%[file]", "%[line]", \
+ "%[flgs]", "%[size]") \
+ : \
+ : [monc] "i" (MONCODE_BUG), \
+ [frmt] "i" (WARN_CONDITION_STR(cond_str)), \
+ [file] "i" (__FILE__), \
+ [line] "i" (__LINE__), \
+ [flgs] "i" (flags), \
+ [size] "i" (sizeof(struct bug_entry))); \
+} while (0)
-#define __BUG_FLAGS(cond_str, flags) \
- asm_inline volatile(__stringify(ASM_BUG_FLAGS(cond_str, flags)));
+#define BUG() \
+do { \
+ __BUG_ASM("", 0); \
+ unreachable(); \
+} while (0)
-#define __WARN_FLAGS(cond_str, flags) \
-do { \
- __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags)); \
+#define __WARN_FLAGS(cond_str, flags) \
+do { \
+ __BUG_ASM(cond_str, BUGFLAG_WARNING | (flags)); \
} while (0)
-#define BUG() \
-do { \
- __BUG_FLAGS("", 0); \
- unreachable(); \
+#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); \
+ /* prevent tail-call optimization */ \
+ asm(""); \
} while (0)
+#define __WARN_printf(taint, fmt, arg...) \
+ __WARN_print_arg(BUGFLAG_TAINT(taint), fmt, ## arg)
+
+#define WARN_ONCE(cond, format, arg...) \
+({ \
+ int __ret_warn_on = !!(cond); \
+ \
+ if (unlikely(__ret_warn_on)) { \
+ __WARN_print_arg(BUGFLAG_ONCE|BUGFLAG_TAINT(TAINT_WARN),\
+ format, ## arg); \
+ } \
+ __ret_warn_on; \
+})
+
#define HAVE_ARCH_BUG
+#define HAVE_ARCH_BUG_FORMAT
+#define HAVE_ARCH_BUG_FORMAT_ARGS
-#endif /* CONFIG_BUG */
+#endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */
+#endif /* __ASSEMBLER__ */
#include <asm-generic/bug.h>
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h
index e6ec0ccf3d73..aaceb1d9110a 100644
--- a/arch/s390/include/asm/ptrace.h
+++ b/arch/s390/include/asm/ptrace.h
@@ -120,7 +120,10 @@ struct pt_regs {
unsigned long gprs[NUM_GPRS];
};
};
- unsigned long orig_gpr2;
+ union {
+ unsigned long orig_gpr2;
+ unsigned long monitor_code;
+ };
union {
struct {
unsigned int int_code;
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index b7f1553d9ee5..4873fe9d891b 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,16 @@ SYM_FUNC_START(__switch_to_asm)
BR_EX %r14
SYM_FUNC_END(__switch_to_asm)
+#if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS)
+
+SYM_FUNC_START(__WARN_trap)
+ mc MONCODE_BUG_ARG(%r0),0
+ BR_EX %r14
+SYM_FUNC_END(__WARN_trap)
+EXPORT_SYMBOL(__WARN_trap)
+
+#endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */
+
#if IS_ENABLED(CONFIG_KVM)
/*
* __sie64a calling convention:
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index 19687dab32f7..1b5c6fc431cc 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,48 @@ static void space_switch_exception(struct pt_regs *regs)
do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
}
+#if defined(CONFIG_BUG) && defined(CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS)
+
+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;
+}
+
+#endif /* CONFIG_BUG && CONFIG_CC_HAS_ASM_IMMEDIATE_STRINGS */
+
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) {
+ regs->psw.addr = regs->gprs[14];
+ 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;
@@ -258,11 +296,12 @@ static void __init test_monitor_call(void)
if (!IS_ENABLED(CONFIG_BUG))
return;
asm_inline volatile(
- " mc 0,0\n"
+ " mc %[monc](%%r0),0\n"
"0: lhi %[val],0\n"
"1:\n"
EX_TABLE(0b, 1b)
- : [val] "+d" (val));
+ : [val] "+d" (val)
+ : [monc] "i" (MONCODE_BUG));
if (!val)
panic("Monitor call doesn't work!\n");
}
@@ -297,6 +336,7 @@ void noinstr __do_pgm_check(struct pt_regs *regs)
teid.val = lc->trans_exc_code;
regs->int_code = lc->pgm_int_code;
regs->int_parm_long = teid.val;
+ regs->monitor_code = lc->monitor_code;
/*
* In case of a guest fault, short-circuit the fault handler and return.
* This way the sie64a() function will return 0; fault address and