LoongArch: Use generic BUG() handler
authorYouling Tang <tangyouling@loongson.cn>
Wed, 12 Oct 2022 08:36:19 +0000 (16:36 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Wed, 12 Oct 2022 08:36:19 +0000 (16:36 +0800)
Inspired by commit 9fb7410f955("arm64/BUG: Use BRK instruction for
generic BUG traps"), do similar for LoongArch to use generic BUG()
handler.

This patch uses the BREAK software breakpoint instruction to generate
a trap instead, similarly to most other arches, with the generic BUG
code generating the dmesg boilerplate.

This allows bug metadata to be moved to a separate table and reduces
the amount of inline code at BUG() and WARN() sites. This also avoids
clobbering any registers before they can be dumped.

To mitigate the size of the bug table further, this patch makes use of
the existing infrastructure for encoding addresses within the bug table
as 32-bit relative pointers instead of absolute pointers.

(Note: this limits the max kernel size to 2GB.)

Before patch:
[ 3018.338013] lkdtm: Performing direct entry BUG
[ 3018.342445] Kernel bug detected[#5]:
[ 3018.345992] CPU: 2 PID: 865 Comm: cat Tainted: G D 6.0.0-rc6+ #35

After patch:
[  125.585985] lkdtm: Performing direct entry BUG
[  125.590433] ------------[ cut here ]------------
[  125.595020] kernel BUG at drivers/misc/lkdtm/bugs.c:78!
[  125.600211] Oops - BUG[#1]:
[  125.602980] CPU: 3 PID: 410 Comm: cat Not tainted 6.0.0-rc6+ #36

Out-of-line file/line data information obtained compared to before.

Signed-off-by: Youling Tang <tangyouling@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/Kconfig
arch/loongarch/include/asm/bug.h
arch/loongarch/kernel/head.S
arch/loongarch/kernel/traps.c

index 223edbb8fe8476b459251e65441b442afdd73444..cc3ba53242b6396fff44d138908f529267eb1851 100644 (file)
@@ -142,6 +142,14 @@ config CPU_HAS_PREFETCH
        bool
        default y
 
+config GENERIC_BUG
+       def_bool y
+       depends on BUG
+
+config GENERIC_BUG_RELATIVE_POINTERS
+       def_bool y
+       depends on GENERIC_BUG
+
 config GENERIC_CALIBRATE_DELAY
        def_bool y
 
index bda49108a76d0bba56a93b3139b3d7706298b238..d4ca3ba2541885c0a7d28229b1adacadad9ec0a5 100644 (file)
@@ -2,21 +2,59 @@
 #ifndef __ASM_BUG_H
 #define __ASM_BUG_H
 
-#include <linux/compiler.h>
+#include <asm/break.h>
+#include <linux/stringify.h>
+
+#ifndef CONFIG_DEBUG_BUGVERBOSE
+#define _BUGVERBOSE_LOCATION(file, line)
+#else
+#define __BUGVERBOSE_LOCATION(file, line)                      \
+               .pushsection .rodata.str, "aMS", @progbits, 1;  \
+       10002:  .string file;                                   \
+               .popsection;                                    \
+                                                               \
+               .long 10002b - .;                               \
+               .short line;
+#define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line)
+#endif
 
-#ifdef CONFIG_BUG
+#ifndef CONFIG_GENERIC_BUG
+#define __BUG_ENTRY(flags)
+#else
+#define __BUG_ENTRY(flags)                                     \
+               .pushsection __bug_table, "aw";                 \
+               .align 2;                                       \
+       10000:  .long 10001f - .;                               \
+               _BUGVERBOSE_LOCATION(__FILE__, __LINE__)        \
+               .short flags;                                   \
+               .popsection;                                    \
+       10001:
+#endif
 
-#include <asm/break.h>
+#define ASM_BUG_FLAGS(flags)                                   \
+       __BUG_ENTRY(flags)                                      \
+       break           BRK_BUG
 
-static inline void __noreturn BUG(void)
-{
-       __asm__ __volatile__("break %0" : : "i" (BRK_BUG));
-       unreachable();
-}
+#define ASM_BUG()      ASM_BUG_FLAGS(0)
 
-#define HAVE_ARCH_BUG
+#define __BUG_FLAGS(flags)                                     \
+       asm_inline volatile (__stringify(ASM_BUG_FLAGS(flags)));
 
-#endif
+#define __WARN_FLAGS(flags)                                    \
+do {                                                           \
+       instrumentation_begin();                                \
+       __BUG_FLAGS(BUGFLAG_WARNING|(flags));                   \
+       instrumentation_end();                                  \
+} while (0)
+
+#define BUG()                                                  \
+do {                                                           \
+       instrumentation_begin();                                \
+       __BUG_FLAGS(0);                                         \
+       unreachable();                                          \
+} while (0)
+
+#define HAVE_ARCH_BUG
 
 #include <asm-generic/bug.h>
 
index 0c67c24ce0878be8ba5483911634795244325693..d32128f1d3c495b8d6e8a2ab1c0364706c6b3950 100644 (file)
@@ -8,6 +8,7 @@
 #include <asm/addrspace.h>
 #include <asm/asm.h>
 #include <asm/asmmacro.h>
+#include <asm/bug.h>
 #include <asm/regdef.h>
 #include <asm/loongarch.h>
 #include <asm/stackframe.h>
@@ -85,6 +86,7 @@ SYM_CODE_START(kernel_entry)                  # kernel entry point
        PTR_ADDI        sp, sp, -4 * SZREG      # init stack pointer
 
        bl              start_kernel
+       ASM_BUG()
 
 SYM_CODE_END(kernel_entry)
 
@@ -116,6 +118,8 @@ SYM_CODE_START(smpboot_entry)
        ld.d            tp, t0, CPU_BOOT_TINFO
 
        bl              start_secondary
+       ASM_BUG()
+
 SYM_CODE_END(smpboot_entry)
 
 #endif /* CONFIG_SMP */
index a5e8bd5d79484486d9bd5d0fdd8e583f11f0053b..66c2849b26e5c48ece37636a116f3bf0c13d61bd 100644 (file)
@@ -374,6 +374,29 @@ asmlinkage void noinstr do_ale(struct pt_regs *regs)
        irqentry_exit(regs, state);
 }
 
+#ifdef CONFIG_GENERIC_BUG
+int is_valid_bugaddr(unsigned long addr)
+{
+       return 1;
+}
+#endif /* CONFIG_GENERIC_BUG */
+
+static void bug_handler(struct pt_regs *regs)
+{
+       switch (report_bug(regs->csr_era, regs)) {
+       case BUG_TRAP_TYPE_BUG:
+       case BUG_TRAP_TYPE_NONE:
+               die_if_kernel("Oops - BUG", regs);
+               force_sig(SIGTRAP);
+               break;
+
+       case BUG_TRAP_TYPE_WARN:
+               /* Skip the BUG instruction and continue */
+               regs->csr_era += LOONGARCH_INSN_SIZE;
+               break;
+       }
+}
+
 asmlinkage void noinstr do_bp(struct pt_regs *regs)
 {
        bool user = user_mode(regs);
@@ -427,8 +450,7 @@ asmlinkage void noinstr do_bp(struct pt_regs *regs)
 
        switch (bcode) {
        case BRK_BUG:
-               die_if_kernel("Kernel bug detected", regs);
-               force_sig(SIGTRAP);
+               bug_handler(regs);
                break;
        case BRK_DIVZERO:
                die_if_kernel("Break instruction in kernel code", regs);