diff options
author | Nicholas Piggin <npiggin@gmail.com> | 2021-06-17 18:51:08 +0300 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2021-06-24 17:06:56 +0300 |
commit | f23699c93becd746295aaa506537882a46a62219 (patch) | |
tree | ea44289996524915c83e6876b39babbe6a640ff8 /arch/powerpc/lib/restart_table.c | |
parent | 63e40806eea984f770c992120bbfd71b589ea580 (diff) | |
download | linux-f23699c93becd746295aaa506537882a46a62219.tar.xz |
powerpc/64: allow alternate return locations for soft-masked interrupts
The exception table fixup adjusts a failed page fault's interrupt return
location if it was taken at an address specified in the exception table,
to a corresponding fixup handler address.
Introduce a variation of that idea which adds a fixup table for NMIs and
soft-masked asynchronous interrupts. This will be used to protect
certain critical sections that are sensitive to being clobbered by
interrupts coming in (due to using the same SPRs and/or irq soft-mask
state).
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210617155116.2167984-10-npiggin@gmail.com
Diffstat (limited to 'arch/powerpc/lib/restart_table.c')
-rw-r--r-- | arch/powerpc/lib/restart_table.c | 30 |
1 files changed, 30 insertions, 0 deletions
diff --git a/arch/powerpc/lib/restart_table.c b/arch/powerpc/lib/restart_table.c new file mode 100644 index 000000000000..7cd20757cc33 --- /dev/null +++ b/arch/powerpc/lib/restart_table.c @@ -0,0 +1,30 @@ +#include <asm/interrupt.h> +#include <asm/kprobes.h> + +struct restart_table_entry { + unsigned long start; + unsigned long end; + unsigned long fixup; +}; + +extern struct restart_table_entry __start___restart_table[]; +extern struct restart_table_entry __stop___restart_table[]; + +/* Given an address, look for it in the kernel exception table */ +unsigned long search_kernel_restart_table(unsigned long addr) +{ + struct restart_table_entry *rte = __start___restart_table; + + while (rte < __stop___restart_table) { + unsigned long start = rte->start; + unsigned long end = rte->end; + unsigned long fixup = rte->fixup; + + if (addr >= start && addr < end) + return fixup; + + rte++; + } + return 0; +} +NOKPROBE_SYMBOL(search_kernel_restart_table); |