mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-05-05 06:43:48 +00:00
powerpc/64s: add a table of implicit soft-masked addresses
Commit9d1988ca87
("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:9d1988ca87
("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
This commit is contained in:
parent
9b69d48c75
commit
325678fd05
6 changed files with 105 additions and 10 deletions
|
@ -75,6 +75,7 @@
|
||||||
|
|
||||||
#ifdef CONFIG_PPC_BOOK3S_64
|
#ifdef CONFIG_PPC_BOOK3S_64
|
||||||
extern char __end_soft_masked[];
|
extern char __end_soft_masked[];
|
||||||
|
bool search_kernel_soft_mask_table(unsigned long addr);
|
||||||
unsigned long search_kernel_restart_table(unsigned long addr);
|
unsigned long search_kernel_restart_table(unsigned long addr);
|
||||||
|
|
||||||
DECLARE_STATIC_KEY_FALSE(interrupt_exit_not_reentrant);
|
DECLARE_STATIC_KEY_FALSE(interrupt_exit_not_reentrant);
|
||||||
|
@ -87,7 +88,7 @@ static inline bool is_implicit_soft_masked(struct pt_regs *regs)
|
||||||
if (regs->nip >= (unsigned long)__end_soft_masked)
|
if (regs->nip >= (unsigned long)__end_soft_masked)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return search_kernel_soft_mask_table(regs->nip);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void srr_regs_clobbered(void)
|
static inline void srr_regs_clobbered(void)
|
||||||
|
|
|
@ -762,6 +762,13 @@ END_FTR_SECTION_NESTED(CPU_FTR_CELL_TB_BUG, CPU_FTR_CELL_TB_BUG, 96)
|
||||||
stringify_in_c(.long (_target) - . ;) \
|
stringify_in_c(.long (_target) - . ;) \
|
||||||
stringify_in_c(.previous)
|
stringify_in_c(.previous)
|
||||||
|
|
||||||
|
#define SOFT_MASK_TABLE(_start, _end) \
|
||||||
|
stringify_in_c(.section __soft_mask_table,"a";)\
|
||||||
|
stringify_in_c(.balign 8;) \
|
||||||
|
stringify_in_c(.llong (_start);) \
|
||||||
|
stringify_in_c(.llong (_end);) \
|
||||||
|
stringify_in_c(.previous)
|
||||||
|
|
||||||
#define RESTART_TABLE(_start, _end, _target) \
|
#define RESTART_TABLE(_start, _end, _target) \
|
||||||
stringify_in_c(.section __restart_table,"a";)\
|
stringify_in_c(.section __restart_table,"a";)\
|
||||||
stringify_in_c(.balign 8;) \
|
stringify_in_c(.balign 8;) \
|
||||||
|
|
|
@ -428,21 +428,31 @@ DEFINE_FIXED_SYMBOL(\name\()_common_real)
|
||||||
|
|
||||||
/* If coming from user, skip soft-mask tests. */
|
/* If coming from user, skip soft-mask tests. */
|
||||||
andi. r10,r12,MSR_PR
|
andi. r10,r12,MSR_PR
|
||||||
bne 2f
|
bne 3f
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Kernel code running below __end_soft_masked is implicitly
|
* Kernel code running below __end_soft_masked may be
|
||||||
* soft-masked
|
* implicitly soft-masked if it is within the regions
|
||||||
|
* in the soft mask table.
|
||||||
*/
|
*/
|
||||||
LOAD_HANDLER(r10, __end_soft_masked)
|
LOAD_HANDLER(r10, __end_soft_masked)
|
||||||
cmpld r11,r10
|
cmpld r11,r10
|
||||||
|
bge+ 1f
|
||||||
|
|
||||||
|
/* SEARCH_SOFT_MASK_TABLE clobbers r9,r10,r12 */
|
||||||
|
mtctr r12
|
||||||
|
stw r9,PACA_EXGEN+EX_CCR(r13)
|
||||||
|
SEARCH_SOFT_MASK_TABLE
|
||||||
|
cmpdi r12,0
|
||||||
|
mfctr r12 /* Restore r12 to SRR1 */
|
||||||
|
lwz r9,PACA_EXGEN+EX_CCR(r13)
|
||||||
|
beq 1f /* Not in soft-mask table */
|
||||||
li r10,IMASK
|
li r10,IMASK
|
||||||
blt- 1f
|
b 2f /* In soft-mask table, always mask */
|
||||||
|
|
||||||
/* Test the soft mask state against our interrupt's bit */
|
/* Test the soft mask state against our interrupt's bit */
|
||||||
lbz r10,PACAIRQSOFTMASK(r13)
|
1: lbz r10,PACAIRQSOFTMASK(r13)
|
||||||
1: andi. r10,r10,IMASK
|
2: andi. r10,r10,IMASK
|
||||||
/* Associate vector numbers with bits in paca->irq_happened */
|
/* Associate vector numbers with bits in paca->irq_happened */
|
||||||
.if IVEC == 0x500 || IVEC == 0xea0
|
.if IVEC == 0x500 || IVEC == 0xea0
|
||||||
li r10,PACA_IRQ_EE
|
li r10,PACA_IRQ_EE
|
||||||
|
@ -473,7 +483,7 @@ DEFINE_FIXED_SYMBOL(\name\()_common_real)
|
||||||
|
|
||||||
.if ISTACK
|
.if ISTACK
|
||||||
andi. r10,r12,MSR_PR /* See if coming from user */
|
andi. r10,r12,MSR_PR /* See if coming from user */
|
||||||
2: mr r10,r1 /* Save r1 */
|
3: mr r10,r1 /* Save r1 */
|
||||||
subi r1,r1,INT_FRAME_SIZE /* alloc frame on kernel stack */
|
subi r1,r1,INT_FRAME_SIZE /* alloc frame on kernel stack */
|
||||||
beq- 100f
|
beq- 100f
|
||||||
ld r1,PACAKSAVE(r13) /* kernel stack to use */
|
ld r1,PACAKSAVE(r13) /* kernel stack to use */
|
||||||
|
@ -624,6 +634,36 @@ END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
|
||||||
303:
|
303:
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
.macro SEARCH_SOFT_MASK_TABLE
|
||||||
|
#ifdef CONFIG_RELOCATABLE
|
||||||
|
mr r12,r2
|
||||||
|
ld r2,PACATOC(r13)
|
||||||
|
LOAD_REG_ADDR(r9, __start___soft_mask_table)
|
||||||
|
LOAD_REG_ADDR(r10, __stop___soft_mask_table)
|
||||||
|
mr r2,r12
|
||||||
|
#else
|
||||||
|
LOAD_REG_IMMEDIATE_SYM(r9, r12, __start___soft_mask_table)
|
||||||
|
LOAD_REG_IMMEDIATE_SYM(r10, r12, __stop___soft_mask_table)
|
||||||
|
#endif
|
||||||
|
300:
|
||||||
|
cmpd r9,r10
|
||||||
|
beq 302f
|
||||||
|
ld r12,0(r9)
|
||||||
|
cmpld r11,r12
|
||||||
|
blt 301f
|
||||||
|
ld r12,8(r9)
|
||||||
|
cmpld r11,r12
|
||||||
|
bge 301f
|
||||||
|
li r12,1
|
||||||
|
b 303f
|
||||||
|
301:
|
||||||
|
addi r9,r9,16
|
||||||
|
b 300b
|
||||||
|
302:
|
||||||
|
li r12,0
|
||||||
|
303:
|
||||||
|
.endm
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Restore all registers including H/SRR0/1 saved in a stack frame of a
|
* Restore all registers including H/SRR0/1 saved in a stack frame of a
|
||||||
* standard exception.
|
* standard exception.
|
||||||
|
@ -754,8 +794,8 @@ __start_interrupts:
|
||||||
* scv instructions enter the kernel without changing EE, RI, ME, or HV.
|
* scv instructions enter the kernel without changing EE, RI, ME, or HV.
|
||||||
* In particular, this means we can take a maskable interrupt at any point
|
* In particular, this means we can take a maskable interrupt at any point
|
||||||
* in the scv handler, which is unlike any other interrupt. This is solved
|
* in the scv handler, which is unlike any other interrupt. This is solved
|
||||||
* by treating the instruction addresses below __end_soft_masked as being
|
* by treating the instruction addresses in the handler as being soft-masked,
|
||||||
* soft-masked.
|
* by adding a SOFT_MASK_TABLE entry for them.
|
||||||
*
|
*
|
||||||
* AIL-0 mode scv exceptions go to 0x17000-0x17fff, but we set AIL-3 and
|
* AIL-0 mode scv exceptions go to 0x17000-0x17fff, but we set AIL-3 and
|
||||||
* ensure scv is never executed with relocation off, which means AIL-0
|
* ensure scv is never executed with relocation off, which means AIL-0
|
||||||
|
@ -772,6 +812,7 @@ __start_interrupts:
|
||||||
* syscall register convention is in Documentation/powerpc/syscall64-abi.rst
|
* syscall register convention is in Documentation/powerpc/syscall64-abi.rst
|
||||||
*/
|
*/
|
||||||
EXC_VIRT_BEGIN(system_call_vectored, 0x3000, 0x1000)
|
EXC_VIRT_BEGIN(system_call_vectored, 0x3000, 0x1000)
|
||||||
|
1:
|
||||||
/* SCV 0 */
|
/* SCV 0 */
|
||||||
mr r9,r13
|
mr r9,r13
|
||||||
GET_PACA(r13)
|
GET_PACA(r13)
|
||||||
|
@ -801,8 +842,11 @@ EXC_VIRT_BEGIN(system_call_vectored, 0x3000, 0x1000)
|
||||||
b system_call_vectored_sigill
|
b system_call_vectored_sigill
|
||||||
#endif
|
#endif
|
||||||
.endr
|
.endr
|
||||||
|
2:
|
||||||
EXC_VIRT_END(system_call_vectored, 0x3000, 0x1000)
|
EXC_VIRT_END(system_call_vectored, 0x3000, 0x1000)
|
||||||
|
|
||||||
|
SOFT_MASK_TABLE(1b, 2b) // Treat scv vectors as soft-masked, see comment above.
|
||||||
|
|
||||||
#ifdef CONFIG_RELOCATABLE
|
#ifdef CONFIG_RELOCATABLE
|
||||||
TRAMP_VIRT_BEGIN(system_call_vectored_tramp)
|
TRAMP_VIRT_BEGIN(system_call_vectored_tramp)
|
||||||
__LOAD_HANDLER(r10, system_call_vectored_common)
|
__LOAD_HANDLER(r10, system_call_vectored_common)
|
||||||
|
|
|
@ -207,7 +207,9 @@ syscall_vectored_\name\()_restart:
|
||||||
bl syscall_exit_restart
|
bl syscall_exit_restart
|
||||||
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
||||||
b .Lsyscall_vectored_\name\()_rst_start
|
b .Lsyscall_vectored_\name\()_rst_start
|
||||||
|
1:
|
||||||
|
|
||||||
|
SOFT_MASK_TABLE(.Lsyscall_vectored_\name\()_rst_start, 1b)
|
||||||
RESTART_TABLE(.Lsyscall_vectored_\name\()_rst_start, .Lsyscall_vectored_\name\()_rst_end, syscall_vectored_\name\()_restart)
|
RESTART_TABLE(.Lsyscall_vectored_\name\()_rst_start, .Lsyscall_vectored_\name\()_rst_end, syscall_vectored_\name\()_restart)
|
||||||
|
|
||||||
.endm
|
.endm
|
||||||
|
@ -410,7 +412,9 @@ syscall_restart:
|
||||||
bl syscall_exit_restart
|
bl syscall_exit_restart
|
||||||
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
||||||
b .Lsyscall_rst_start
|
b .Lsyscall_rst_start
|
||||||
|
1:
|
||||||
|
|
||||||
|
SOFT_MASK_TABLE(.Lsyscall_rst_start, 1b)
|
||||||
RESTART_TABLE(.Lsyscall_rst_start, .Lsyscall_rst_end, syscall_restart)
|
RESTART_TABLE(.Lsyscall_rst_start, .Lsyscall_rst_end, syscall_restart)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -607,7 +611,9 @@ interrupt_return_\srr\()_user_restart:
|
||||||
bl interrupt_exit_user_restart
|
bl interrupt_exit_user_restart
|
||||||
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
||||||
b .Linterrupt_return_\srr\()_user_rst_start
|
b .Linterrupt_return_\srr\()_user_rst_start
|
||||||
|
1:
|
||||||
|
|
||||||
|
SOFT_MASK_TABLE(.Linterrupt_return_\srr\()_user_rst_start, 1b)
|
||||||
RESTART_TABLE(.Linterrupt_return_\srr\()_user_rst_start, .Linterrupt_return_\srr\()_user_rst_end, interrupt_return_\srr\()_user_restart)
|
RESTART_TABLE(.Linterrupt_return_\srr\()_user_rst_start, .Linterrupt_return_\srr\()_user_rst_end, interrupt_return_\srr\()_user_restart)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -738,7 +744,9 @@ interrupt_return_\srr\()_kernel_restart:
|
||||||
bl interrupt_exit_kernel_restart
|
bl interrupt_exit_kernel_restart
|
||||||
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
|
||||||
b .Linterrupt_return_\srr\()_kernel_rst_start
|
b .Linterrupt_return_\srr\()_kernel_rst_start
|
||||||
|
1:
|
||||||
|
|
||||||
|
SOFT_MASK_TABLE(.Linterrupt_return_\srr\()_kernel_rst_start, 1b)
|
||||||
RESTART_TABLE(.Linterrupt_return_\srr\()_kernel_rst_start, .Linterrupt_return_\srr\()_kernel_rst_end, interrupt_return_\srr\()_kernel_restart)
|
RESTART_TABLE(.Linterrupt_return_\srr\()_kernel_rst_start, .Linterrupt_return_\srr\()_kernel_rst_end, interrupt_return_\srr\()_kernel_restart)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,14 @@
|
||||||
#define EMITS_PT_NOTE
|
#define EMITS_PT_NOTE
|
||||||
#define RO_EXCEPTION_TABLE_ALIGN 0
|
#define RO_EXCEPTION_TABLE_ALIGN 0
|
||||||
|
|
||||||
|
#define SOFT_MASK_TABLE(align) \
|
||||||
|
. = ALIGN(align); \
|
||||||
|
__soft_mask_table : AT(ADDR(__soft_mask_table) - LOAD_OFFSET) { \
|
||||||
|
__start___soft_mask_table = .; \
|
||||||
|
KEEP(*(__soft_mask_table)) \
|
||||||
|
__stop___soft_mask_table = .; \
|
||||||
|
}
|
||||||
|
|
||||||
#define RESTART_TABLE(align) \
|
#define RESTART_TABLE(align) \
|
||||||
. = ALIGN(align); \
|
. = ALIGN(align); \
|
||||||
__restart_table : AT(ADDR(__restart_table) - LOAD_OFFSET) { \
|
__restart_table : AT(ADDR(__restart_table) - LOAD_OFFSET) { \
|
||||||
|
@ -132,6 +140,7 @@ SECTIONS
|
||||||
RO_DATA(PAGE_SIZE)
|
RO_DATA(PAGE_SIZE)
|
||||||
|
|
||||||
#ifdef CONFIG_PPC64
|
#ifdef CONFIG_PPC64
|
||||||
|
SOFT_MASK_TABLE(8)
|
||||||
RESTART_TABLE(8)
|
RESTART_TABLE(8)
|
||||||
|
|
||||||
. = ALIGN(8);
|
. = ALIGN(8);
|
||||||
|
|
|
@ -1,15 +1,41 @@
|
||||||
#include <asm/interrupt.h>
|
#include <asm/interrupt.h>
|
||||||
#include <asm/kprobes.h>
|
#include <asm/kprobes.h>
|
||||||
|
|
||||||
|
struct soft_mask_table_entry {
|
||||||
|
unsigned long start;
|
||||||
|
unsigned long end;
|
||||||
|
};
|
||||||
|
|
||||||
struct restart_table_entry {
|
struct restart_table_entry {
|
||||||
unsigned long start;
|
unsigned long start;
|
||||||
unsigned long end;
|
unsigned long end;
|
||||||
unsigned long fixup;
|
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 __start___restart_table[];
|
||||||
extern struct restart_table_entry __stop___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 */
|
/* Given an address, look for it in the kernel exception table */
|
||||||
unsigned long search_kernel_restart_table(unsigned long addr)
|
unsigned long search_kernel_restart_table(unsigned long addr)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue