diff options
author | Nicholas Piggin <npiggin@gmail.com> | 2021-06-30 09:46:16 +0200 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2021-06-30 14:21:20 +0200 |
commit | 325678fd052259e7c05ef29060a73c705ea90432 (patch) | |
tree | 4af33e651e345d979c3cdca9af3f9bccd7e10f38 /arch/powerpc/lib | |
parent | powerpc/64e: remove implicit soft-masking and interrupt exit restart logic (diff) | |
download | linux-325678fd052259e7c05ef29060a73c705ea90432.tar.xz linux-325678fd052259e7c05ef29060a73c705ea90432.zip |
powerpc/64s: add a table of implicit soft-masked addresses
Commit 9d1988ca87dd ("powerpc/64: treat low kernel text as irqs
soft-masked") ends up catching too much code, including ret_from_fork,
and parts of interrupt and syscall return that do not expect to be
interrupts to be soft-masked. If an interrupt gets marked pending,
and then the code proceeds out of the implicit soft-masked region it
will fail to deal with the pending interrupt.
Fix this by adding a new table of addresses which explicitly marks
the regions of code that are soft masked. This table is only checked
for interrupts that below __end_soft_masked, so most kernel interrupts
will not have the overhead of the table search.
Fixes: 9d1988ca87dd ("powerpc/64: treat low kernel text as irqs soft-masked")
Reported-by: Sachin Sant <sachinp@linux.vnet.ibm.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Tested-by: Sachin Sant <sachinp@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210630074621.2109197-5-npiggin@gmail.com
Diffstat (limited to 'arch/powerpc/lib')
-rw-r--r-- | arch/powerpc/lib/restart_table.c | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/arch/powerpc/lib/restart_table.c b/arch/powerpc/lib/restart_table.c index 7cd20757cc33..bccb662c1b7b 100644 --- a/arch/powerpc/lib/restart_table.c +++ b/arch/powerpc/lib/restart_table.c @@ -1,15 +1,41 @@ #include <asm/interrupt.h> #include <asm/kprobes.h> +struct soft_mask_table_entry { + unsigned long start; + unsigned long end; +}; + struct restart_table_entry { unsigned long start; unsigned long end; unsigned long fixup; }; +extern struct soft_mask_table_entry __start___soft_mask_table[]; +extern struct soft_mask_table_entry __stop___soft_mask_table[]; + extern struct restart_table_entry __start___restart_table[]; extern struct restart_table_entry __stop___restart_table[]; +/* Given an address, look for it in the soft mask table */ +bool search_kernel_soft_mask_table(unsigned long addr) +{ + struct soft_mask_table_entry *smte = __start___soft_mask_table; + + while (smte < __stop___soft_mask_table) { + unsigned long start = smte->start; + unsigned long end = smte->end; + + if (addr >= start && addr < end) + return true; + + smte++; + } + return false; +} +NOKPROBE_SYMBOL(search_kernel_soft_mask_table); + /* Given an address, look for it in the kernel exception table */ unsigned long search_kernel_restart_table(unsigned long addr) { |