Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

Nicholas Piggin and committed by
Michael Ellerman
325678fd 9b69d48c

+105 -10
+2 -1
arch/powerpc/include/asm/interrupt.h
··· 75 75 76 76 #ifdef CONFIG_PPC_BOOK3S_64 77 77 extern char __end_soft_masked[]; 78 + bool search_kernel_soft_mask_table(unsigned long addr); 78 79 unsigned long search_kernel_restart_table(unsigned long addr); 79 80 80 81 DECLARE_STATIC_KEY_FALSE(interrupt_exit_not_reentrant); ··· 88 87 if (regs->nip >= (unsigned long)__end_soft_masked) 89 88 return false; 90 89 91 - return true; 90 + return search_kernel_soft_mask_table(regs->nip); 92 91 } 93 92 94 93 static inline void srr_regs_clobbered(void)
+7
arch/powerpc/include/asm/ppc_asm.h
··· 762 762 stringify_in_c(.long (_target) - . ;) \ 763 763 stringify_in_c(.previous) 764 764 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 + 765 772 #define RESTART_TABLE(_start, _end, _target) \ 766 773 stringify_in_c(.section __restart_table,"a";)\ 767 774 stringify_in_c(.balign 8;) \
+53 -9
arch/powerpc/kernel/exceptions-64s.S
··· 428 428 429 429 /* If coming from user, skip soft-mask tests. */ 430 430 andi. r10,r12,MSR_PR 431 - bne 2f 431 + bne 3f 432 432 433 433 /* 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. 436 437 */ 437 438 LOAD_HANDLER(r10, __end_soft_masked) 438 439 cmpld r11,r10 440 + bge+ 1f 439 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 */ 440 450 li r10,IMASK 441 - blt- 1f 451 + b 2f /* In soft-mask table, always mask */ 442 452 443 453 /* 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 446 456 /* Associate vector numbers with bits in paca->irq_happened */ 447 457 .if IVEC == 0x500 || IVEC == 0xea0 448 458 li r10,PACA_IRQ_EE ··· 483 473 484 474 .if ISTACK 485 475 andi. r10,r12,MSR_PR /* See if coming from user */ 486 - 2: mr r10,r1 /* Save r1 */ 476 + 3: mr r10,r1 /* Save r1 */ 487 477 subi r1,r1,INT_FRAME_SIZE /* alloc frame on kernel stack */ 488 478 beq- 100f 489 479 ld r1,PACAKSAVE(r13) /* kernel stack to use */ ··· 634 624 303: 635 625 .endm 636 626 627 + .macro SEARCH_SOFT_MASK_TABLE 628 + #ifdef CONFIG_RELOCATABLE 629 + mr r12,r2 630 + ld r2,PACATOC(r13) 631 + LOAD_REG_ADDR(r9, __start___soft_mask_table) 632 + LOAD_REG_ADDR(r10, __stop___soft_mask_table) 633 + mr r2,r12 634 + #else 635 + LOAD_REG_IMMEDIATE_SYM(r9, r12, __start___soft_mask_table) 636 + LOAD_REG_IMMEDIATE_SYM(r10, r12, __stop___soft_mask_table) 637 + #endif 638 + 300: 639 + cmpd r9,r10 640 + beq 302f 641 + ld r12,0(r9) 642 + cmpld r11,r12 643 + blt 301f 644 + ld r12,8(r9) 645 + cmpld r11,r12 646 + bge 301f 647 + li r12,1 648 + b 303f 649 + 301: 650 + addi r9,r9,16 651 + b 300b 652 + 302: 653 + li r12,0 654 + 303: 655 + .endm 656 + 637 657 /* 638 658 * Restore all registers including H/SRR0/1 saved in a stack frame of a 639 659 * standard exception. ··· 794 754 * scv instructions enter the kernel without changing EE, RI, ME, or HV. 795 755 * In particular, this means we can take a maskable interrupt at any point 796 756 * in the scv handler, which is unlike any other interrupt. This is solved 797 - * by treating the instruction addresses below __end_soft_masked as being 798 - * soft-masked. 757 + * by treating the instruction addresses in the handler as being soft-masked, 758 + * by adding a SOFT_MASK_TABLE entry for them. 799 759 * 800 760 * AIL-0 mode scv exceptions go to 0x17000-0x17fff, but we set AIL-3 and 801 761 * ensure scv is never executed with relocation off, which means AIL-0 ··· 812 772 * syscall register convention is in Documentation/powerpc/syscall64-abi.rst 813 773 */ 814 774 EXC_VIRT_BEGIN(system_call_vectored, 0x3000, 0x1000) 775 + 1: 815 776 /* SCV 0 */ 816 777 mr r9,r13 817 778 GET_PACA(r13) ··· 842 801 b system_call_vectored_sigill 843 802 #endif 844 803 .endr 804 + 2: 845 805 EXC_VIRT_END(system_call_vectored, 0x3000, 0x1000) 806 + 807 + SOFT_MASK_TABLE(1b, 2b) // Treat scv vectors as soft-masked, see comment above. 846 808 847 809 #ifdef CONFIG_RELOCATABLE 848 810 TRAMP_VIRT_BEGIN(system_call_vectored_tramp)
+8
arch/powerpc/kernel/interrupt_64.S
··· 207 207 bl syscall_exit_restart 208 208 std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */ 209 209 b .Lsyscall_vectored_\name\()_rst_start 210 + 1: 210 211 212 + SOFT_MASK_TABLE(.Lsyscall_vectored_\name\()_rst_start, 1b) 211 213 RESTART_TABLE(.Lsyscall_vectored_\name\()_rst_start, .Lsyscall_vectored_\name\()_rst_end, syscall_vectored_\name\()_restart) 212 214 213 215 .endm ··· 412 410 bl syscall_exit_restart 413 411 std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */ 414 412 b .Lsyscall_rst_start 413 + 1: 415 414 415 + SOFT_MASK_TABLE(.Lsyscall_rst_start, 1b) 416 416 RESTART_TABLE(.Lsyscall_rst_start, .Lsyscall_rst_end, syscall_restart) 417 417 #endif 418 418 ··· 611 607 bl interrupt_exit_user_restart 612 608 std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */ 613 609 b .Linterrupt_return_\srr\()_user_rst_start 610 + 1: 614 611 612 + SOFT_MASK_TABLE(.Linterrupt_return_\srr\()_user_rst_start, 1b) 615 613 RESTART_TABLE(.Linterrupt_return_\srr\()_user_rst_start, .Linterrupt_return_\srr\()_user_rst_end, interrupt_return_\srr\()_user_restart) 616 614 #endif 617 615 ··· 744 738 bl interrupt_exit_kernel_restart 745 739 std r1,PACA_EXIT_SAVE_R1(r13) /* save r1 for restart */ 746 740 b .Linterrupt_return_\srr\()_kernel_rst_start 741 + 1: 747 742 743 + SOFT_MASK_TABLE(.Linterrupt_return_\srr\()_kernel_rst_start, 1b) 748 744 RESTART_TABLE(.Linterrupt_return_\srr\()_kernel_rst_start, .Linterrupt_return_\srr\()_kernel_rst_end, interrupt_return_\srr\()_kernel_restart) 749 745 #endif 750 746
+9
arch/powerpc/kernel/vmlinux.lds.S
··· 9 9 #define EMITS_PT_NOTE 10 10 #define RO_EXCEPTION_TABLE_ALIGN 0 11 11 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 + 12 20 #define RESTART_TABLE(align) \ 13 21 . = ALIGN(align); \ 14 22 __restart_table : AT(ADDR(__restart_table) - LOAD_OFFSET) { \ ··· 140 132 RO_DATA(PAGE_SIZE) 141 133 142 134 #ifdef CONFIG_PPC64 135 + SOFT_MASK_TABLE(8) 143 136 RESTART_TABLE(8) 144 137 145 138 . = ALIGN(8);
+26
arch/powerpc/lib/restart_table.c
··· 1 1 #include <asm/interrupt.h> 2 2 #include <asm/kprobes.h> 3 3 4 + struct soft_mask_table_entry { 5 + unsigned long start; 6 + unsigned long end; 7 + }; 8 + 4 9 struct restart_table_entry { 5 10 unsigned long start; 6 11 unsigned long end; 7 12 unsigned long fixup; 8 13 }; 9 14 15 + extern struct soft_mask_table_entry __start___soft_mask_table[]; 16 + extern struct soft_mask_table_entry __stop___soft_mask_table[]; 17 + 10 18 extern struct restart_table_entry __start___restart_table[]; 11 19 extern struct restart_table_entry __stop___restart_table[]; 20 + 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); 12 38 13 39 /* Given an address, look for it in the kernel exception table */ 14 40 unsigned long search_kernel_restart_table(unsigned long addr)