]> git.baikalelectronics.ru Git - kernel.git/commitdiff
powerpc/bug: Provide better flexibility to WARN_ON/__WARN_FLAGS() with asm goto
authorChristophe Leroy <christophe.leroy@csgroup.eu>
Tue, 13 Apr 2021 16:38:10 +0000 (16:38 +0000)
committerMichael Ellerman <mpe@ellerman.id.au>
Sun, 15 Aug 2021 03:49:24 +0000 (13:49 +1000)
Using asm goto in __WARN_FLAGS() and WARN_ON() allows more
flexibility to GCC.

For that add an entry to the exception table so that
program_check_exception() knowns where to resume execution
after a WARNING.

Here are two exemples. The first one is done on PPC32 (which
benefits from the previous patch), the second is on PPC64.

unsigned long test(struct pt_regs *regs)
{
int ret;

WARN_ON(regs->msr & MSR_PR);

return regs->gpr[3];
}

unsigned long test9w(unsigned long a, unsigned long b)
{
if (WARN_ON(!b))
return 0;
return a / b;
}

Before the patch:

000003a8 <test>:
 3a8: 81 23 00 84  lwz     r9,132(r3)
 3ac: 71 29 40 00  andi.   r9,r9,16384
 3b0: 40 82 00 0c  bne     3bc <test+0x14>
 3b4: 80 63 00 0c  lwz     r3,12(r3)
 3b8: 4e 80 00 20  blr

 3bc: 0f e0 00 00  twui    r0,0
 3c0: 80 63 00 0c  lwz     r3,12(r3)
 3c4: 4e 80 00 20  blr

0000000000000bf0 <.test9w>:
 bf0: 7c 89 00 74  cntlzd  r9,r4
 bf4: 79 29 d1 82  rldicl  r9,r9,58,6
 bf8: 0b 09 00 00  tdnei   r9,0
 bfc: 2c 24 00 00  cmpdi   r4,0
 c00: 41 82 00 0c  beq     c0c <.test9w+0x1c>
 c04: 7c 63 23 92  divdu   r3,r3,r4
 c08: 4e 80 00 20  blr

 c0c: 38 60 00 00  li      r3,0
 c10: 4e 80 00 20  blr

After the patch:

000003a8 <test>:
 3a8: 81 23 00 84  lwz     r9,132(r3)
 3ac: 71 29 40 00  andi.   r9,r9,16384
 3b0: 40 82 00 0c  bne     3bc <test+0x14>
 3b4: 80 63 00 0c  lwz     r3,12(r3)
 3b8: 4e 80 00 20  blr

 3bc: 0f e0 00 00  twui    r0,0

0000000000000c50 <.test9w>:
 c50: 7c 89 00 74  cntlzd  r9,r4
 c54: 79 29 d1 82  rldicl  r9,r9,58,6
 c58: 0b 09 00 00  tdnei   r9,0
 c5c: 7c 63 23 92  divdu   r3,r3,r4
 c60: 4e 80 00 20  blr

 c70: 38 60 00 00  li      r3,0
 c74: 4e 80 00 20  blr

In the first exemple, we see GCC doesn't need to duplicate what
happens after the trap.

In the second exemple, we see that GCC doesn't need to emit a test
and a branch in the likely path in addition to the trap.

We've got some WARN_ON() in .softirqentry.text section so it needs
to be added in the OTHER_TEXT_SECTIONS in modpost.c

Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/389962b1b702e3c78d169e59bcfac56282889173.1618331882.git.christophe.leroy@csgroup.eu
arch/powerpc/include/asm/book3s/64/kup.h
arch/powerpc/include/asm/bug.h
arch/powerpc/include/asm/extable.h
arch/powerpc/include/asm/ppc_asm.h
arch/powerpc/kernel/entry_64.S
arch/powerpc/kernel/misc_32.S
arch/powerpc/kernel/traps.c
scripts/mod/modpost.c
tools/testing/selftests/powerpc/primitives/asm/extable.h [new symlink]

index a1cc73a88710e2dc5e1618eb2d4a67aef8e9b189..170339969b7cc0cedd6742752b36eaf3ce006f3a 100644 (file)
@@ -90,7 +90,7 @@
        /* Prevent access to userspace using any key values */
        LOAD_REG_IMMEDIATE(\gpr2, AMR_KUAP_BLOCKED)
 999:   tdne    \gpr1, \gpr2
-       EMIT_BUG_ENTRY 999b, __FILE__, __LINE__, (BUGFLAG_WARNING | BUGFLAG_ONCE)
+       EMIT_WARN_ENTRY 999b, __FILE__, __LINE__, (BUGFLAG_WARNING | BUGFLAG_ONCE)
        END_MMU_FTR_SECTION_NESTED_IFSET(MMU_FTR_BOOK3S_KUAP, 67)
 #endif
 .endm
index d844de5adfcbc75301e99502c82d058504d8ce47..1ee0f22313ee13a08b55bf5e4aa5c51998d155c4 100644 (file)
@@ -4,6 +4,7 @@
 #ifdef __KERNEL__
 
 #include <asm/asm-compat.h>
+#include <asm/extable.h>
 
 #ifdef CONFIG_BUG
 
 .endm
 #endif /* verbose */
 
+.macro EMIT_WARN_ENTRY addr,file,line,flags
+       EX_TABLE(\addr,\addr+4)
+       EMIT_BUG_ENTRY \addr,\file,\line,\flags
+.endm
+
 #else /* !__ASSEMBLY__ */
 /* _EMIT_BUG_ENTRY expects args %0,%1,%2,%3 to be FILE, LINE, flags and
    sizeof(struct bug_entry), respectively */
                  "i" (sizeof(struct bug_entry)),       \
                  ##__VA_ARGS__)
 
+#define WARN_ENTRY(insn, flags, label, ...)            \
+       asm_volatile_goto(                              \
+               "1:     " insn "\n"                     \
+               EX_TABLE(1b, %l[label])                 \
+               _EMIT_BUG_ENTRY                         \
+               : : "i" (__FILE__), "i" (__LINE__),     \
+                 "i" (flags),                          \
+                 "i" (sizeof(struct bug_entry)),       \
+                 ##__VA_ARGS__ : : label)
+
 /*
  * BUG_ON() and WARN_ON() do their best to cooperate with compile-time
  * optimisations. However depending on the complexity of the condition
 } while (0)
 #define HAVE_ARCH_BUG
 
-#define __WARN_FLAGS(flags) BUG_ENTRY("twi 31, 0, 0", BUGFLAG_WARNING | (flags))
+#define __WARN_FLAGS(flags) do {                               \
+       __label__ __label_warn_on;                              \
+                                                               \
+       WARN_ENTRY("twi 31, 0, 0", BUGFLAG_WARNING | (flags), __label_warn_on); \
+       unreachable();                                          \
+                                                               \
+__label_warn_on:                                               \
+       break;                                                  \
+} while (0)
 
 #ifdef CONFIG_PPC64
 #define BUG_ON(x) do {                                         \
 } while (0)
 
 #define WARN_ON(x) ({                                          \
-       int __ret_warn_on = !!(x);                              \
-       if (__builtin_constant_p(__ret_warn_on)) {              \
-               if (__ret_warn_on)                              \
+       bool __ret_warn_on = false;                             \
+       do {                                                    \
+               if (__builtin_constant_p((x))) {                \
+                       if (!(x))                               \
+                               break;                          \
                        __WARN();                               \
-       } else {                                                \
-               BUG_ENTRY(PPC_TLNEI " %4, 0",                   \
-                         BUGFLAG_WARNING | BUGFLAG_TAINT(TAINT_WARN),  \
-                         "r" (__ret_warn_on)); \
-       }                                                       \
+                       __ret_warn_on = true;                   \
+               } else {                                        \
+                       __label__ __label_warn_on;              \
+                                                               \
+                       WARN_ENTRY(PPC_TLNEI " %4, 0",          \
+                                  BUGFLAG_WARNING | BUGFLAG_TAINT(TAINT_WARN), \
+                                  __label_warn_on, "r" (x));   \
+                       break;                                  \
+__label_warn_on:                                               \
+                       __ret_warn_on = true;                   \
+               }                                               \
+       } while (0);                                            \
        unlikely(__ret_warn_on);                                \
 })
 
 #ifdef __ASSEMBLY__
 .macro EMIT_BUG_ENTRY addr,file,line,flags
 .endm
+.macro EMIT_WARN_ENTRY addr,file,line,flags
+.endm
 #else /* !__ASSEMBLY__ */
 #define _EMIT_BUG_ENTRY
+#define _EMIT_WARN_ENTRY
 #endif
 #endif /* CONFIG_BUG */
 
index eb91b2d2935ad76faea5d75d7cbd863208efa656..26ce2e5c0fa8e9b5c8d59028d153522bd07f0f36 100644 (file)
@@ -17,6 +17,8 @@
 
 #define ARCH_HAS_RELATIVE_EXTABLE
 
+#ifndef __ASSEMBLY__
+
 struct exception_table_entry {
        int insn;
        int fixup;
@@ -28,3 +30,15 @@ static inline unsigned long extable_fixup(const struct exception_table_entry *x)
 }
 
 #endif
+
+/*
+ * Helper macro for exception table entries
+ */
+#define EX_TABLE(_fault, _target)              \
+       stringify_in_c(.section __ex_table,"a";)\
+       stringify_in_c(.balign 4;)              \
+       stringify_in_c(.long (_fault) - . ;)    \
+       stringify_in_c(.long (_target) - . ;)   \
+       stringify_in_c(.previous)
+
+#endif
index 116c1519728a74fd83db6fc99c69a9b97ccfb522..ffe712307e1189f62f52af803ab9125bcf13cf19 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/ppc-opcode.h>
 #include <asm/firmware.h>
 #include <asm/feature-fixups.h>
+#include <asm/extable.h>
 
 #ifdef __ASSEMBLY__
 
@@ -752,16 +753,6 @@ END_FTR_SECTION_NESTED(CPU_FTR_CELL_TB_BUG, CPU_FTR_CELL_TB_BUG, 96)
 
 #endif /*  __ASSEMBLY__ */
 
-/*
- * Helper macro for exception table entries
- */
-#define EX_TABLE(_fault, _target)              \
-       stringify_in_c(.section __ex_table,"a";)\
-       stringify_in_c(.balign 4;)              \
-       stringify_in_c(.long (_fault) - . ;)    \
-       stringify_in_c(.long (_target) - . ;)   \
-       stringify_in_c(.previous)
-
 #define SOFT_MASK_TABLE(_start, _end)          \
        stringify_in_c(.section __soft_mask_table,"a";)\
        stringify_in_c(.balign 8;)              \
index 15720f8661a1b2db4ef7ce9fb120829d4955824d..70cff7b49e172bb9e5339102c8e3aac0b8853e26 100644 (file)
@@ -309,7 +309,7 @@ _GLOBAL(enter_rtas)
         */
        lbz     r0,PACAIRQSOFTMASK(r13)
 1:     tdeqi   r0,IRQS_ENABLED
-       EMIT_BUG_ENTRY 1b,__FILE__,__LINE__,BUGFLAG_WARNING
+       EMIT_WARN_ENTRY 1b,__FILE__,__LINE__,BUGFLAG_WARNING
 #endif
 
        /* Hard-disable interrupts */
index 39ab1541959274091de14f15c276d5a71b7be018..d8645efff902400966257c5a00d3c512f9517019 100644 (file)
@@ -237,7 +237,7 @@ _GLOBAL(copy_page)
        addi    r3,r3,-4
 
 0:     twnei   r5, 0   /* WARN if r3 is not cache aligned */
-       EMIT_BUG_ENTRY 0b,__FILE__,__LINE__, BUGFLAG_WARNING
+       EMIT_WARN_ENTRY 0b,__FILE__,__LINE__, BUGFLAG_WARNING
 
        addi    r4,r4,-4
 
index c8f648727d36da444104618cf9884f2ac79a5063..51d4f5faf42571578d694a6b2404a22f631edb40 100644 (file)
@@ -1477,8 +1477,13 @@ static void do_program_check(struct pt_regs *regs)
 
                if (!(regs->msr & MSR_PR) &&  /* not user-mode */
                    report_bug(bugaddr, regs) == BUG_TRAP_TYPE_WARN) {
-                       regs_add_return_ip(regs, 4);
-                       return;
+                       const struct exception_table_entry *entry;
+
+                       entry = search_exception_tables(bugaddr);
+                       if (entry) {
+                               regs_set_return_ip(regs, extable_fixup(entry) + regs->nip - bugaddr);
+                               return;
+                       }
                }
                _exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
                return;
index 270a7df898e2bf868e4987f8779b54f7444a1ce6..1209e1786af7c033bf7d1aeb9127c6401889d95f 100644 (file)
@@ -931,7 +931,7 @@ static void check_section(const char *modname, struct elf_info *elf,
                ".kprobes.text", ".cpuidle.text", ".noinstr.text"
 #define OTHER_TEXT_SECTIONS ".ref.text", ".head.text", ".spinlock.text", \
                ".fixup", ".entry.text", ".exception.text", ".text.*", \
-               ".coldtext"
+               ".coldtext", ".softirqentry.text"
 
 #define INIT_SECTIONS      ".init.*"
 #define MEM_INIT_SECTIONS  ".meminit.*"
diff --git a/tools/testing/selftests/powerpc/primitives/asm/extable.h b/tools/testing/selftests/powerpc/primitives/asm/extable.h
new file mode 120000 (symlink)
index 0000000..6385f05
--- /dev/null
@@ -0,0 +1 @@
+../../../../../../arch/powerpc/include/asm/extable.h
\ No newline at end of file