diff options
author | Nicholas Piggin <npiggin@gmail.com> | 2016-10-14 01:47:31 -0400 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2016-11-13 19:11:51 -0500 |
commit | 61a92f703120daf7ed25e046275aa8a2d3085ad4 (patch) | |
tree | 0ded0103f9cc2e2a6fc53e0fcc5bb6c4c2329265 | |
parent | 24bfa6a9e0d4fe414dfc4ad06c93e10c4c37194e (diff) |
powerpc: Add support for relative exception tables
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>
-rw-r--r-- | arch/powerpc/include/asm/ppc_asm.h | 6 | ||||
-rw-r--r-- | arch/powerpc/include/asm/uaccess.h | 27 | ||||
-rw-r--r-- | arch/powerpc/kernel/kprobes.c | 2 | ||||
-rw-r--r-- | arch/powerpc/kernel/traps.c | 2 | ||||
-rw-r--r-- | arch/powerpc/mm/fault.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/holly.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c | 2 | ||||
-rw-r--r-- | arch/powerpc/sysdev/fsl_rio.c | 2 | ||||
-rw-r--r-- | tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c | 12 |
9 files changed, 34 insertions, 23 deletions
diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 6af8852d1f7f..bf9de5575ca9 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h | |||
@@ -785,9 +785,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601) | |||
785 | */ | 785 | */ |
786 | #define EX_TABLE(_fault, _target) \ | 786 | #define EX_TABLE(_fault, _target) \ |
787 | stringify_in_c(.section __ex_table,"a";)\ | 787 | stringify_in_c(.section __ex_table,"a";)\ |
788 | PPC_LONG_ALIGN stringify_in_c(;) \ | 788 | stringify_in_c(.balign 4;) \ |
789 | PPC_LONG stringify_in_c(_fault;) \ | 789 | stringify_in_c(.long (_fault) - . ;) \ |
790 | PPC_LONG stringify_in_c(_target;) \ | 790 | stringify_in_c(.long (_target) - . ;) \ |
791 | stringify_in_c(.previous) | 791 | stringify_in_c(.previous) |
792 | 792 | ||
793 | #endif /* _ASM_POWERPC_PPC_ASM_H */ | 793 | #endif /* _ASM_POWERPC_PPC_ASM_H */ |
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h index e0b724619c4a..a15d84d59356 100644 --- a/arch/powerpc/include/asm/uaccess.h +++ b/arch/powerpc/include/asm/uaccess.h | |||
@@ -64,23 +64,30 @@ | |||
64 | __access_ok((__force unsigned long)(addr), (size), get_fs())) | 64 | __access_ok((__force unsigned long)(addr), (size), get_fs())) |
65 | 65 | ||
66 | /* | 66 | /* |
67 | * The exception table consists of pairs of addresses: the first is the | 67 | * The exception table consists of pairs of relative addresses: the first is |
68 | * address of an instruction that is allowed to fault, and the second is | 68 | * the address of an instruction that is allowed to fault, and the second is |
69 | * the address at which the program should continue. No registers are | 69 | * the address at which the program should continue. No registers are |
70 | * modified, so it is entirely up to the continuation code to figure out | 70 | * modified, so it is entirely up to the continuation code to figure out what |
71 | * what to do. | 71 | * to do. |
72 | * | 72 | * |
73 | * All the routines below use bits of fixup code that are out of line | 73 | * All the routines below use bits of fixup code that are out of line with the |
74 | * with the main instruction path. This means when everything is well, | 74 | * main instruction path. This means when everything is well, we don't even |
75 | * we don't even have to jump over them. Further, they do not intrude | 75 | * have to jump over them. Further, they do not intrude on our cache or tlb |
76 | * on our cache or tlb entries. | 76 | * entries. |
77 | */ | 77 | */ |
78 | 78 | ||
79 | #define ARCH_HAS_RELATIVE_EXTABLE | ||
80 | |||
79 | struct exception_table_entry { | 81 | struct exception_table_entry { |
80 | unsigned long insn; | 82 | int insn; |
81 | unsigned long fixup; | 83 | int fixup; |
82 | }; | 84 | }; |
83 | 85 | ||
86 | static inline unsigned long extable_fixup(const struct exception_table_entry *x) | ||
87 | { | ||
88 | return (unsigned long)&x->fixup + x->fixup; | ||
89 | } | ||
90 | |||
84 | /* | 91 | /* |
85 | * These are the main single-value transfer routines. They automatically | 92 | * These are the main single-value transfer routines. They automatically |
86 | * use the right size if we just have the right pointer type. | 93 | * use the right size if we just have the right pointer type. |
diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index e785cc9e1ecd..9479d8e360cf 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c | |||
@@ -449,7 +449,7 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) | |||
449 | * zero, try to fix up. | 449 | * zero, try to fix up. |
450 | */ | 450 | */ |
451 | if ((entry = search_exception_tables(regs->nip)) != NULL) { | 451 | if ((entry = search_exception_tables(regs->nip)) != NULL) { |
452 | regs->nip = entry->fixup; | 452 | regs->nip = extable_fixup(entry); |
453 | return 1; | 453 | return 1; |
454 | } | 454 | } |
455 | 455 | ||
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 023a462725b5..32c468b8b548 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c | |||
@@ -365,7 +365,7 @@ static inline int check_io_access(struct pt_regs *regs) | |||
365 | (*nip & 0x100)? "OUT to": "IN from", | 365 | (*nip & 0x100)? "OUT to": "IN from", |
366 | regs->gpr[rb] - _IO_BASE, nip); | 366 | regs->gpr[rb] - _IO_BASE, nip); |
367 | regs->msr |= MSR_RI; | 367 | regs->msr |= MSR_RI; |
368 | regs->nip = entry->fixup; | 368 | regs->nip = extable_fixup(entry); |
369 | return 1; | 369 | return 1; |
370 | } | 370 | } |
371 | } | 371 | } |
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index d0b137d96df1..73932f4a386e 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c | |||
@@ -512,7 +512,7 @@ void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig) | |||
512 | 512 | ||
513 | /* Are we prepared to handle this fault? */ | 513 | /* Are we prepared to handle this fault? */ |
514 | if ((entry = search_exception_tables(regs->nip)) != NULL) { | 514 | if ((entry = search_exception_tables(regs->nip)) != NULL) { |
515 | regs->nip = entry->fixup; | 515 | regs->nip = extable_fixup(entry); |
516 | return; | 516 | return; |
517 | } | 517 | } |
518 | 518 | ||
diff --git a/arch/powerpc/platforms/embedded6xx/holly.c b/arch/powerpc/platforms/embedded6xx/holly.c index dfd310031549..0409714e8070 100644 --- a/arch/powerpc/platforms/embedded6xx/holly.c +++ b/arch/powerpc/platforms/embedded6xx/holly.c | |||
@@ -263,7 +263,7 @@ static int ppc750_machine_check_exception(struct pt_regs *regs) | |||
263 | if ((entry = search_exception_tables(regs->nip)) != NULL) { | 263 | if ((entry = search_exception_tables(regs->nip)) != NULL) { |
264 | tsi108_clear_pci_cfg_error(); | 264 | tsi108_clear_pci_cfg_error(); |
265 | regs->msr |= MSR_RI; | 265 | regs->msr |= MSR_RI; |
266 | regs->nip = entry->fixup; | 266 | regs->nip = extable_fixup(entry); |
267 | return 1; | 267 | return 1; |
268 | } | 268 | } |
269 | return 0; | 269 | return 0; |
diff --git a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c index f97bab8e37a2..9de100e22bf3 100644 --- a/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c +++ b/arch/powerpc/platforms/embedded6xx/mpc7448_hpc2.c | |||
@@ -174,7 +174,7 @@ static int mpc7448_machine_check_exception(struct pt_regs *regs) | |||
174 | if ((entry = search_exception_tables(regs->nip)) != NULL) { | 174 | if ((entry = search_exception_tables(regs->nip)) != NULL) { |
175 | tsi108_clear_pci_cfg_error(); | 175 | tsi108_clear_pci_cfg_error(); |
176 | regs->msr |= MSR_RI; | 176 | regs->msr |= MSR_RI; |
177 | regs->nip = entry->fixup; | 177 | regs->nip = extable_fixup(entry); |
178 | return 1; | 178 | return 1; |
179 | } | 179 | } |
180 | return 0; | 180 | return 0; |
diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c index 87fee0c8eb21..1c41c51f22cb 100644 --- a/arch/powerpc/sysdev/fsl_rio.c +++ b/arch/powerpc/sysdev/fsl_rio.c | |||
@@ -111,7 +111,7 @@ int fsl_rio_mcheck_exception(struct pt_regs *regs) | |||
111 | out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), | 111 | out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), |
112 | 0); | 112 | 0); |
113 | regs->msr |= MSR_RI; | 113 | regs->msr |= MSR_RI; |
114 | regs->nip = entry->fixup; | 114 | regs->nip = extable_fixup(entry); |
115 | return 1; | 115 | return 1; |
116 | } | 116 | } |
117 | } | 117 | } |
diff --git a/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c index cd7af4e1b65a..ed3239bbfae2 100644 --- a/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c +++ b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c | |||
@@ -73,19 +73,23 @@ extern char __stop___ex_table[]; | |||
73 | #error implement UCONTEXT_NIA | 73 | #error implement UCONTEXT_NIA |
74 | #endif | 74 | #endif |
75 | 75 | ||
76 | struct extbl_entry { | ||
77 | int insn; | ||
78 | int fixup; | ||
79 | }; | ||
76 | 80 | ||
77 | static void segv_handler(int signr, siginfo_t *info, void *ptr) | 81 | static void segv_handler(int signr, siginfo_t *info, void *ptr) |
78 | { | 82 | { |
79 | ucontext_t *uc = (ucontext_t *)ptr; | 83 | ucontext_t *uc = (ucontext_t *)ptr; |
80 | unsigned long addr = (unsigned long)info->si_addr; | 84 | unsigned long addr = (unsigned long)info->si_addr; |
81 | unsigned long *ip = &UCONTEXT_NIA(uc); | 85 | unsigned long *ip = &UCONTEXT_NIA(uc); |
82 | unsigned long *ex_p = (unsigned long *)__start___ex_table; | 86 | struct extbl_entry *entry = (struct extbl_entry *)__start___ex_table; |
83 | 87 | ||
84 | while (ex_p < (unsigned long *)__stop___ex_table) { | 88 | while (entry < (struct extbl_entry *)__stop___ex_table) { |
85 | unsigned long insn, fixup; | 89 | unsigned long insn, fixup; |
86 | 90 | ||
87 | insn = *ex_p++; | 91 | insn = (unsigned long)&entry->insn + entry->insn; |
88 | fixup = *ex_p++; | 92 | fixup = (unsigned long)&entry->fixup + entry->fixup; |
89 | 93 | ||
90 | if (insn == *ip) { | 94 | if (insn == *ip) { |
91 | *ip = fixup; | 95 | *ip = fixup; |