powerpc: Add support for relative exception tables
authorNicholas Piggin <npiggin@gmail.com>
Fri, 14 Oct 2016 05:47:31 +0000 (16:47 +1100)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 14 Nov 2016 00:11:51 +0000 (11:11 +1100)
This halves the exception table size on 64-bit builds, and it allows
build-time sorting of exception tables to work on relocated kernels.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
[mpe: Minor asm fixups and bits to keep the selftests working]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/ppc_asm.h
arch/powerpc/include/asm/uaccess.h
arch/powerpc/kernel/kprobes.c
arch/powerpc/kernel/traps.c
arch/powerpc/mm/fault.c
arch/powerpc/platforms/embedded6xx/holly.c
arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c
arch/powerpc/sysdev/fsl_rio.c
tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c

index 6af8852d1f7f11dece3ac62ad735a1e935f6b56a..bf9de5575ca97f667be74c5e9abf7a3084d62ab6 100644 (file)
@@ -785,9 +785,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601)
  */
 #define EX_TABLE(_fault, _target)              \
        stringify_in_c(.section __ex_table,"a";)\
-       PPC_LONG_ALIGN stringify_in_c(;)        \
-       PPC_LONG stringify_in_c(_fault;)        \
-       PPC_LONG stringify_in_c(_target;)       \
+       stringify_in_c(.balign 4;)              \
+       stringify_in_c(.long (_fault) - . ;)    \
+       stringify_in_c(.long (_target) - . ;)   \
        stringify_in_c(.previous)
 
 #endif /* _ASM_POWERPC_PPC_ASM_H */
index e0b724619c4a87e8250138f309c180155dd5c532..a15d84d5935613f2c180ca764d9651746c216b44 100644 (file)
         __access_ok((__force unsigned long)(addr), (size), get_fs()))
 
 /*
- * The exception table consists of pairs of addresses: the first is the
- * address of an instruction that is allowed to fault, and the second is
+ * The exception table consists of pairs of relative addresses: the first is
+ * the address of an instruction that is allowed to fault, and the second is
  * the address at which the program should continue.  No registers are
- * modified, so it is entirely up to the continuation code to figure out
- * what to do.
+ * modified, so it is entirely up to the continuation code to figure out what
+ * to do.
  *
- * All the routines below use bits of fixup code that are out of line
- * with the main instruction path.  This means when everything is well,
- * we don't even have to jump over them.  Further, they do not intrude
- * on our cache or tlb entries.
+ * All the routines below use bits of fixup code that are out of line with the
+ * main instruction path.  This means when everything is well, we don't even
+ * have to jump over them.  Further, they do not intrude on our cache or tlb
+ * entries.
  */
 
+#define ARCH_HAS_RELATIVE_EXTABLE
+
 struct exception_table_entry {
-       unsigned long insn;
-       unsigned long fixup;
+       int insn;
+       int fixup;
 };
 
+static inline unsigned long extable_fixup(const struct exception_table_entry *x)
+{
+       return (unsigned long)&x->fixup + x->fixup;
+}
+
 /*
  * These are the main single-value transfer routines.  They automatically
  * use the right size if we just have the right pointer type.
index e785cc9e1ecd8bb0e442168412278bdbcf70afdd..9479d8e360cfe9b6ba8d677072898dec25e7b5a5 100644 (file)
@@ -449,7 +449,7 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
                 * zero, try to fix up.
                 */
                if ((entry = search_exception_tables(regs->nip)) != NULL) {
-                       regs->nip = entry->fixup;
+                       regs->nip = extable_fixup(entry);
                        return 1;
                }
 
index 023a462725b5dccf39ff51521118986f824afdbe..32c468b8b5480ee514b8ef5d7486213be3f19c79 100644 (file)
@@ -365,7 +365,7 @@ static inline int check_io_access(struct pt_regs *regs)
                               (*nip & 0x100)? "OUT to": "IN from",
                               regs->gpr[rb] - _IO_BASE, nip);
                        regs->msr |= MSR_RI;
-                       regs->nip = entry->fixup;
+                       regs->nip = extable_fixup(entry);
                        return 1;
                }
        }
index d0b137d96df19e0e6f1b1258555f9ff359cd3fbd..73932f4a386e75082e622cd60049779c2d33150a 100644 (file)
@@ -512,7 +512,7 @@ void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
 
        /* Are we prepared to handle this fault?  */
        if ((entry = search_exception_tables(regs->nip)) != NULL) {
-               regs->nip = entry->fixup;
+               regs->nip = extable_fixup(entry);
                return;
        }
 
index dfd310031549d9007252a0416688a0605ebccd4d..0409714e8070e802352936321f4bfc27492dff62 100644 (file)
@@ -263,7 +263,7 @@ static int ppc750_machine_check_exception(struct pt_regs *regs)
        if ((entry = search_exception_tables(regs->nip)) != NULL) {
                tsi108_clear_pci_cfg_error();
                regs->msr |= MSR_RI;
-               regs->nip = entry->fixup;
+               regs->nip = extable_fixup(entry);
                return 1;
        }
        return 0;
index f97bab8e37a2688e4d3ef46fcf6f8150d1cf0255..9de100e22bf30050c72ccc0c686b3e82b8269842 100644 (file)
@@ -174,7 +174,7 @@ static int mpc7448_machine_check_exception(struct pt_regs *regs)
        if ((entry = search_exception_tables(regs->nip)) != NULL) {
                tsi108_clear_pci_cfg_error();
                regs->msr |= MSR_RI;
-               regs->nip = entry->fixup;
+               regs->nip = extable_fixup(entry);
                return 1;
        }
        return 0;
index 87fee0c8eb212906b50702ba98a792ddcf1b39cb..1c41c51f22cb596876788930962c53fda3b664b9 100644 (file)
@@ -111,7 +111,7 @@ int fsl_rio_mcheck_exception(struct pt_regs *regs)
                        out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR),
                                 0);
                        regs->msr |= MSR_RI;
-                       regs->nip = entry->fixup;
+                       regs->nip = extable_fixup(entry);
                        return 1;
                }
        }
index cd7af4e1b65a3ce3087649348846f539174b3542..ed3239bbfae2db96e9884cd8776d179b0eccf4fa 100644 (file)
@@ -73,19 +73,23 @@ extern char __stop___ex_table[];
 #error implement UCONTEXT_NIA
 #endif
 
+struct extbl_entry {
+       int insn;
+       int fixup;
+};
 
 static void segv_handler(int signr, siginfo_t *info, void *ptr)
 {
        ucontext_t *uc = (ucontext_t *)ptr;
        unsigned long addr = (unsigned long)info->si_addr;
        unsigned long *ip = &UCONTEXT_NIA(uc);
-       unsigned long *ex_p = (unsigned long *)__start___ex_table;
+       struct extbl_entry *entry = (struct extbl_entry *)__start___ex_table;
 
-       while (ex_p < (unsigned long *)__stop___ex_table) {
+       while (entry < (struct extbl_entry *)__stop___ex_table) {
                unsigned long insn, fixup;
 
-               insn = *ex_p++;
-               fixup = *ex_p++;
+               insn  = (unsigned long)&entry->insn + entry->insn;
+               fixup = (unsigned long)&entry->fixup + entry->fixup;
 
                if (insn == *ip) {
                        *ip = fixup;