Skip to content

Commit 325678f

Browse files
npigginmpe
authored andcommitted
powerpc/64s: add a table of implicit soft-masked addresses
Commit 9d1988c ("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: 9d1988c ("powerpc/64: treat low kernel text as irqs soft-masked") Reported-by: Sachin Sant <[email protected]> Signed-off-by: Nicholas Piggin <[email protected]> Tested-by: Sachin Sant <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 9b69d48 commit 325678f

File tree

6 files changed

+106
-11
lines changed

6 files changed

+106
-11
lines changed

arch/powerpc/include/asm/interrupt.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575

7676
#ifdef CONFIG_PPC_BOOK3S_64
7777
extern char __end_soft_masked[];
78+
bool search_kernel_soft_mask_table(unsigned long addr);
7879
unsigned long search_kernel_restart_table(unsigned long addr);
7980

8081
DECLARE_STATIC_KEY_FALSE(interrupt_exit_not_reentrant);
@@ -87,7 +88,7 @@ static inline bool is_implicit_soft_masked(struct pt_regs *regs)
8788
if (regs->nip >= (unsigned long)__end_soft_masked)
8889
return false;
8990

90-
return true;
91+
return search_kernel_soft_mask_table(regs->nip);
9192
}
9293

9394
static inline void srr_regs_clobbered(void)

arch/powerpc/include/asm/ppc_asm.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,13 @@ END_FTR_SECTION_NESTED(CPU_FTR_CELL_TB_BUG, CPU_FTR_CELL_TB_BUG, 96)
762762
stringify_in_c(.long (_target) - . ;) \
763763
stringify_in_c(.previous)
764764

765+
#define SOFT_MASK_TABLE(_start, _end) \
766+
stringify_in_c(.section __soft_mask_table,"a";)\
767+
stringify_in_c(.balign 8;) \
768+
stringify_in_c(.llong (_start);) \
769+
stringify_in_c(.llong (_end);) \
770+
stringify_in_c(.previous)
771+
765772
#define RESTART_TABLE(_start, _end, _target) \
766773
stringify_in_c(.section __restart_table,"a";)\
767774
stringify_in_c(.balign 8;) \

arch/powerpc/kernel/exceptions-64s.S

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -428,21 +428,31 @@ DEFINE_FIXED_SYMBOL(\name\()_common_real)
428428

429429
/* If coming from user, skip soft-mask tests. */
430430
andi. r10,r12,MSR_PR
431-
bne 2f
431+
bne 3f
432432

433433
/*
434-
* Kernel code running below __end_soft_masked is implicitly
435-
* soft-masked
434+
* Kernel code running below __end_soft_masked may be
435+
* implicitly soft-masked if it is within the regions
436+
* in the soft mask table.
436437
*/
437438
LOAD_HANDLER(r10, __end_soft_masked)
438439
cmpld r11,r10
439-
440+
bge+ 1f
441+
442+
/* SEARCH_SOFT_MASK_TABLE clobbers r9,r10,r12 */
443+
mtctr r12
444+
stw r9,PACA_EXGEN+EX_CCR(r13)
445+
SEARCH_SOFT_MASK_TABLE
446+
cmpdi r12,0
447+
mfctr r12 /* Restore r12 to SRR1 */
448+
lwz r9,PACA_EXGEN+EX_CCR(r13)
449+
beq 1f /* Not in soft-mask table */
440450
li r10,IMASK
441-
blt- 1f
451+
b 2f /* In soft-mask table, always mask */
442452

443453
/* Test the soft mask state against our interrupt's bit */
444-
lbz r10,PACAIRQSOFTMASK(r13)
445-
1: andi. r10,r10,IMASK
454+
1: lbz r10,PACAIRQSOFTMASK(r13)
455+
2: andi. r10,r10,IMASK
446456
/* Associate vector numbers with bits in paca->irq_happened */
447457
.if IVEC == 0x500 || IVEC == 0xea0
448458
li r10,PACA_IRQ_EE
@@ -473,7 +483,7 @@ DEFINE_FIXED_SYMBOL(\name\()_common_real)
473483

474484
.if ISTACK
475485
andi. r10,r12,MSR_PR /* See if coming from user */
476-
2: mr r10,r1 /* Save r1 */
486+
3: mr r10,r1 /* Save r1 */
477487
subi r1,r1,INT_FRAME_SIZE /* alloc frame on kernel stack */
478488
beq- 100f
479489
ld r1,PACAKSAVE(r13) /* kernel stack to use */
@@ -624,6 +634,36 @@ END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
624634
303:
625635
.endm
626636

637+
.macro SEARCH_SOFT_MASK_TABLE
638+
#ifdef CONFIG_RELOCATABLE
639+
mr r12,r2
640+
ld r2,PACATOC(r13)
641+
LOAD_REG_ADDR(r9, __start___soft_mask_table)
642+
LOAD_REG_ADDR(r10, __stop___soft_mask_table)
643+
mr r2,r12
644+
#else
645+
LOAD_REG_IMMEDIATE_SYM(r9, r12, __start___soft_mask_table)
646+
LOAD_REG_IMMEDIATE_SYM(r10, r12, __stop___soft_mask_table)
647+
#endif
648+
300:
649+
cmpd r9,r10
650+
beq 302f
651+
ld r12,0(r9)
652+
cmpld r11,r12
653+
blt 301f
654+
ld r12,8(r9)
655+
cmpld r11,r12
656+
bge 301f
657+
li r12,1
658+
b 303f
659+
301:
660+
addi r9,r9,16
661+
b 300b
662+
302:
663+
li r12,0
664+
303:
665+
.endm
666+
627667
/*
628668
* Restore all registers including H/SRR0/1 saved in a stack frame of a
629669
* standard exception.
@@ -754,8 +794,8 @@ __start_interrupts:
754794
* scv instructions enter the kernel without changing EE, RI, ME, or HV.
755795
* In particular, this means we can take a maskable interrupt at any point
756796
* in the scv handler, which is unlike any other interrupt. This is solved
757-
* by treating the instruction addresses below __end_soft_masked as being
758-
* soft-masked.
797+
* by treating the instruction addresses in the handler as being soft-masked,
798+
* by adding a SOFT_MASK_TABLE entry for them.
759799
*
760800
* AIL-0 mode scv exceptions go to 0x17000-0x17fff, but we set AIL-3 and
761801
* ensure scv is never executed with relocation off, which means AIL-0
@@ -772,6 +812,7 @@ __start_interrupts:
772812
* syscall register convention is in Documentation/powerpc/syscall64-abi.rst
773813
*/
774814
EXC_VIRT_BEGIN(system_call_vectored, 0x3000, 0x1000)
815+
1:
775816
/* SCV 0 */
776817
mr r9,r13
777818
GET_PACA(r13)
@@ -801,8 +842,11 @@ EXC_VIRT_BEGIN(system_call_vectored, 0x3000, 0x1000)
801842
b system_call_vectored_sigill
802843
#endif
803844
.endr
845+
2:
804846
EXC_VIRT_END(system_call_vectored, 0x3000, 0x1000)
805847

848+
SOFT_MASK_TABLE(1b, 2b) // Treat scv vectors as soft-masked, see comment above.
849+
806850
#ifdef CONFIG_RELOCATABLE
807851
TRAMP_VIRT_BEGIN(system_call_vectored_tramp)
808852
__LOAD_HANDLER(r10, system_call_vectored_common)

arch/powerpc/kernel/interrupt_64.S

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,9 @@ syscall_vectored_\name\()_restart:
207207
bl syscall_exit_restart
208208
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
209209
b .Lsyscall_vectored_\name\()_rst_start
210+
1:
210211

212+
SOFT_MASK_TABLE(.Lsyscall_vectored_\name\()_rst_start, 1b)
211213
RESTART_TABLE(.Lsyscall_vectored_\name\()_rst_start, .Lsyscall_vectored_\name\()_rst_end, syscall_vectored_\name\()_restart)
212214

213215
.endm
@@ -410,7 +412,9 @@ syscall_restart:
410412
bl syscall_exit_restart
411413
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
412414
b .Lsyscall_rst_start
415+
1:
413416

417+
SOFT_MASK_TABLE(.Lsyscall_rst_start, 1b)
414418
RESTART_TABLE(.Lsyscall_rst_start, .Lsyscall_rst_end, syscall_restart)
415419
#endif
416420

@@ -607,7 +611,9 @@ interrupt_return_\srr\()_user_restart:
607611
bl interrupt_exit_user_restart
608612
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
609613
b .Linterrupt_return_\srr\()_user_rst_start
614+
1:
610615

616+
SOFT_MASK_TABLE(.Linterrupt_return_\srr\()_user_rst_start, 1b)
611617
RESTART_TABLE(.Linterrupt_return_\srr\()_user_rst_start, .Linterrupt_return_\srr\()_user_rst_end, interrupt_return_\srr\()_user_restart)
612618
#endif
613619

@@ -738,7 +744,9 @@ interrupt_return_\srr\()_kernel_restart:
738744
bl interrupt_exit_kernel_restart
739745
std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */
740746
b .Linterrupt_return_\srr\()_kernel_rst_start
747+
1:
741748

749+
SOFT_MASK_TABLE(.Linterrupt_return_\srr\()_kernel_rst_start, 1b)
742750
RESTART_TABLE(.Linterrupt_return_\srr\()_kernel_rst_start, .Linterrupt_return_\srr\()_kernel_rst_end, interrupt_return_\srr\()_kernel_restart)
743751
#endif
744752

arch/powerpc/kernel/vmlinux.lds.S

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
#define EMITS_PT_NOTE
1010
#define RO_EXCEPTION_TABLE_ALIGN 0
1111

12+
#define SOFT_MASK_TABLE(align) \
13+
. = ALIGN(align); \
14+
__soft_mask_table : AT(ADDR(__soft_mask_table) - LOAD_OFFSET) { \
15+
__start___soft_mask_table = .; \
16+
KEEP(*(__soft_mask_table)) \
17+
__stop___soft_mask_table = .; \
18+
}
19+
1220
#define RESTART_TABLE(align) \
1321
. = ALIGN(align); \
1422
__restart_table : AT(ADDR(__restart_table) - LOAD_OFFSET) { \
@@ -132,6 +140,7 @@ SECTIONS
132140
RO_DATA(PAGE_SIZE)
133141

134142
#ifdef CONFIG_PPC64
143+
SOFT_MASK_TABLE(8)
135144
RESTART_TABLE(8)
136145

137146
. = ALIGN(8);

arch/powerpc/lib/restart_table.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,41 @@
11
#include <asm/interrupt.h>
22
#include <asm/kprobes.h>
33

4+
struct soft_mask_table_entry {
5+
unsigned long start;
6+
unsigned long end;
7+
};
8+
49
struct restart_table_entry {
510
unsigned long start;
611
unsigned long end;
712
unsigned long fixup;
813
};
914

15+
extern struct soft_mask_table_entry __start___soft_mask_table[];
16+
extern struct soft_mask_table_entry __stop___soft_mask_table[];
17+
1018
extern struct restart_table_entry __start___restart_table[];
1119
extern struct restart_table_entry __stop___restart_table[];
1220

21+
/* Given an address, look for it in the soft mask table */
22+
bool search_kernel_soft_mask_table(unsigned long addr)
23+
{
24+
struct soft_mask_table_entry *smte = __start___soft_mask_table;
25+
26+
while (smte < __stop___soft_mask_table) {
27+
unsigned long start = smte->start;
28+
unsigned long end = smte->end;
29+
30+
if (addr >= start && addr < end)
31+
return true;
32+
33+
smte++;
34+
}
35+
return false;
36+
}
37+
NOKPROBE_SYMBOL(search_kernel_soft_mask_table);
38+
1339
/* Given an address, look for it in the kernel exception table */
1440
unsigned long search_kernel_restart_table(unsigned long addr)
1541
{

0 commit comments

Comments
 (0)