Commit 61a92f70 authored by Nicholas Piggin's avatar Nicholas Piggin Committed by Michael Ellerman

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: default avatarNicholas Piggin <npiggin@gmail.com>
[mpe: Minor asm fixups and bits to keep the selftests working]
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 24bfa6a9
...@@ -785,9 +785,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601) ...@@ -785,9 +785,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601)
*/ */
#define EX_TABLE(_fault, _target) \ #define EX_TABLE(_fault, _target) \
stringify_in_c(.section __ex_table,"a";)\ stringify_in_c(.section __ex_table,"a";)\
PPC_LONG_ALIGN stringify_in_c(;) \ stringify_in_c(.balign 4;) \
PPC_LONG stringify_in_c(_fault;) \ stringify_in_c(.long (_fault) - . ;) \
PPC_LONG stringify_in_c(_target;) \ stringify_in_c(.long (_target) - . ;) \
stringify_in_c(.previous) stringify_in_c(.previous)
#endif /* _ASM_POWERPC_PPC_ASM_H */ #endif /* _ASM_POWERPC_PPC_ASM_H */
...@@ -64,23 +64,30 @@ ...@@ -64,23 +64,30 @@
__access_ok((__force unsigned long)(addr), (size), get_fs())) __access_ok((__force unsigned long)(addr), (size), get_fs()))
/* /*
* The exception table consists of pairs of addresses: the first is the * The exception table consists of pairs of relative addresses: the first is
* address of an instruction that is allowed to fault, and the second 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 * the address at which the program should continue. No registers are
* modified, so it is entirely up to the continuation code to figure out * modified, so it is entirely up to the continuation code to figure out what
* what to do. * to do.
* *
* All the routines below use bits of fixup code that are out of line * All the routines below use bits of fixup code that are out of line with the
* with the main instruction path. This means when everything is well, * main instruction path. This means when everything is well, we don't even
* we don't even have to jump over them. Further, they do not intrude * have to jump over them. Further, they do not intrude on our cache or tlb
* on our cache or tlb entries. * entries.
*/ */
#define ARCH_HAS_RELATIVE_EXTABLE
struct exception_table_entry { struct exception_table_entry {
unsigned long insn; int insn;
unsigned long fixup; 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 * These are the main single-value transfer routines. They automatically
* use the right size if we just have the right pointer type. * use the right size if we just have the right pointer type.
......
...@@ -449,7 +449,7 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) ...@@ -449,7 +449,7 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
* zero, try to fix up. * zero, try to fix up.
*/ */
if ((entry = search_exception_tables(regs->nip)) != NULL) { if ((entry = search_exception_tables(regs->nip)) != NULL) {
regs->nip = entry->fixup; regs->nip = extable_fixup(entry);
return 1; return 1;
} }
......
...@@ -365,7 +365,7 @@ static inline int check_io_access(struct pt_regs *regs) ...@@ -365,7 +365,7 @@ static inline int check_io_access(struct pt_regs *regs)
(*nip & 0x100)? "OUT to": "IN from", (*nip & 0x100)? "OUT to": "IN from",
regs->gpr[rb] - _IO_BASE, nip); regs->gpr[rb] - _IO_BASE, nip);
regs->msr |= MSR_RI; regs->msr |= MSR_RI;
regs->nip = entry->fixup; regs->nip = extable_fixup(entry);
return 1; return 1;
} }
} }
......
...@@ -512,7 +512,7 @@ void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig) ...@@ -512,7 +512,7 @@ void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
/* Are we prepared to handle this fault? */ /* Are we prepared to handle this fault? */
if ((entry = search_exception_tables(regs->nip)) != NULL) { if ((entry = search_exception_tables(regs->nip)) != NULL) {
regs->nip = entry->fixup; regs->nip = extable_fixup(entry);
return; return;
} }
......
...@@ -263,7 +263,7 @@ static int ppc750_machine_check_exception(struct pt_regs *regs) ...@@ -263,7 +263,7 @@ static int ppc750_machine_check_exception(struct pt_regs *regs)
if ((entry = search_exception_tables(regs->nip)) != NULL) { if ((entry = search_exception_tables(regs->nip)) != NULL) {
tsi108_clear_pci_cfg_error(); tsi108_clear_pci_cfg_error();
regs->msr |= MSR_RI; regs->msr |= MSR_RI;
regs->nip = entry->fixup; regs->nip = extable_fixup(entry);
return 1; return 1;
} }
return 0; return 0;
......
...@@ -174,7 +174,7 @@ static int mpc7448_machine_check_exception(struct pt_regs *regs) ...@@ -174,7 +174,7 @@ static int mpc7448_machine_check_exception(struct pt_regs *regs)
if ((entry = search_exception_tables(regs->nip)) != NULL) { if ((entry = search_exception_tables(regs->nip)) != NULL) {
tsi108_clear_pci_cfg_error(); tsi108_clear_pci_cfg_error();
regs->msr |= MSR_RI; regs->msr |= MSR_RI;
regs->nip = entry->fixup; regs->nip = extable_fixup(entry);
return 1; return 1;
} }
return 0; return 0;
......
...@@ -111,7 +111,7 @@ int fsl_rio_mcheck_exception(struct pt_regs *regs) ...@@ -111,7 +111,7 @@ int fsl_rio_mcheck_exception(struct pt_regs *regs)
out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR), out_be32((u32 *)(rio_regs_win + RIO_LTLEDCSR),
0); 0);
regs->msr |= MSR_RI; regs->msr |= MSR_RI;
regs->nip = entry->fixup; regs->nip = extable_fixup(entry);
return 1; return 1;
} }
} }
......
...@@ -73,19 +73,23 @@ extern char __stop___ex_table[]; ...@@ -73,19 +73,23 @@ extern char __stop___ex_table[];
#error implement UCONTEXT_NIA #error implement UCONTEXT_NIA
#endif #endif
struct extbl_entry {
int insn;
int fixup;
};
static void segv_handler(int signr, siginfo_t *info, void *ptr) static void segv_handler(int signr, siginfo_t *info, void *ptr)
{ {
ucontext_t *uc = (ucontext_t *)ptr; ucontext_t *uc = (ucontext_t *)ptr;
unsigned long addr = (unsigned long)info->si_addr; unsigned long addr = (unsigned long)info->si_addr;
unsigned long *ip = &UCONTEXT_NIA(uc); 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; unsigned long insn, fixup;
insn = *ex_p++; insn = (unsigned long)&entry->insn + entry->insn;
fixup = *ex_p++; fixup = (unsigned long)&entry->fixup + entry->fixup;
if (insn == *ip) { if (insn == *ip) {
*ip = fixup; *ip = fixup;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment