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>
This commit is contained in:
parent
24bfa6a9e0
commit
61a92f7031
|
@ -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 @@
|
||||||
__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)
|
||||||
* 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)
|
||||||
(*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)
|
||||||
|
|
||||||
/* 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)
|
||||||
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)
|
||||||
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)
|
||||||
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[];
|
||||||
#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;
|
||||||
|
|
Loading…
Reference in New Issue