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

powerpc/64s: early boot machine check handler

Use the early boot interrupt fixup in the machine check handler to allow
the machine check handler to run before interrupt endian is set up.
Branch to an early boot handler that just does a basic crash, which
allows it to run before ppc_md is set up. MSR[ME] is enabled on the boot
CPU earlier, and the machine check stack is temporarily set to the
middle of the init task stack.

This allows machine checks (e.g., due to invalid data access in real
mode) to print something useful earlier in boot (as soon as udbg is set
up, if CONFIG_PPC_EARLY_DEBUG=y).

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20220926055620.2676869-3-npiggin@gmail.com

authored by

Nicholas Piggin and committed by
Michael Ellerman
2f5182cf bf75a325

+34 -1
+1
arch/powerpc/include/asm/asm-prototypes.h
··· 36 36 int64_t opcode, uint64_t msr); 37 37 38 38 /* misc runtime */ 39 + void enable_machine_check(void); 39 40 extern u64 __bswapdi2(u64); 40 41 extern s64 __lshrdi3(s64, int); 41 42 extern s64 __ashldi3(s64, int);
+5 -1
arch/powerpc/kernel/exceptions-64s.S
··· 1134 1134 INT_DEFINE_END(machine_check) 1135 1135 1136 1136 EXC_REAL_BEGIN(machine_check, 0x200, 0x100) 1137 + EARLY_BOOT_FIXUP 1137 1138 GEN_INT_ENTRY machine_check_early, virt=0 1138 1139 EXC_REAL_END(machine_check, 0x200, 0x100) 1139 1140 EXC_VIRT_NONE(0x4200, 0x100) ··· 1199 1198 bl enable_machine_check 1200 1199 END_FTR_SECTION_IFSET(CPU_FTR_HVMODE) 1201 1200 addi r3,r1,STACK_FRAME_OVERHEAD 1201 + BEGIN_FTR_SECTION 1202 + bl machine_check_early_boot 1203 + END_FTR_SECTION(0, 1) // nop out after boot 1202 1204 bl machine_check_early 1203 1205 std r3,RESULT(r1) /* Save result */ 1204 1206 ld r12,_MSR(r1) ··· 3100 3096 USE_TEXT_SECTION() 3101 3097 3102 3098 /* MSR[RI] should be clear because this uses SRR[01] */ 3103 - enable_machine_check: 3099 + _GLOBAL(enable_machine_check) 3104 3100 mflr r0 3105 3101 bcl 20,31,$+4 3106 3102 0: mflr r3
+14
arch/powerpc/kernel/setup_64.c
··· 34 34 #include <linux/of.h> 35 35 #include <linux/of_fdt.h> 36 36 37 + #include <asm/asm-prototypes.h> 37 38 #include <asm/kvm_guest.h> 38 39 #include <asm/io.h> 39 40 #include <asm/kdump.h> ··· 181 180 { 182 181 /* The boot cpu is started */ 183 182 get_paca()->cpu_start = 1; 183 + #ifdef CONFIG_PPC_BOOK3S_64 184 + /* 185 + * Give the early boot machine check stack somewhere to use, use 186 + * half of the init stack. This is a bit hacky but there should not be 187 + * deep stack usage in early init so shouldn't overflow it or overwrite 188 + * things. 189 + */ 190 + get_paca()->mc_emergency_sp = (void *)&init_thread_union + 191 + (THREAD_SIZE/2); 192 + #endif 184 193 /* Allow percpu accesses to work until we setup percpu data */ 185 194 get_paca()->data_offset = 0; 186 195 /* Mark interrupts soft and hard disabled in PACA */ ··· 367 356 fixup_boot_paca(); 368 357 369 358 /* -------- printk is now safe to use ------- */ 359 + 360 + if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && (mfmsr() & MSR_HV)) 361 + enable_machine_check(); 370 362 371 363 /* Try new device tree based feature discovery ... */ 372 364 if (!dt_cpu_ftrs_init(__va(dt_ptr)))
+14
arch/powerpc/kernel/traps.c
··· 68 68 #include <asm/stacktrace.h> 69 69 #include <asm/nmi.h> 70 70 #include <asm/disassemble.h> 71 + #include <asm/udbg.h> 71 72 72 73 #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC_CORE) 73 74 int (*__debugger)(struct pt_regs *regs) __read_mostly; ··· 851 850 } 852 851 853 852 #ifdef CONFIG_PPC_BOOK3S_64 853 + DEFINE_INTERRUPT_HANDLER_RAW(machine_check_early_boot) 854 + { 855 + udbg_printf("Machine check (early boot)\n"); 856 + udbg_printf("SRR0=0x%016lx SRR1=0x%016lx\n", regs->nip, regs->msr); 857 + udbg_printf(" DAR=0x%016lx DSISR=0x%08lx\n", regs->dar, regs->dsisr); 858 + udbg_printf(" LR=0x%016lx R1=0x%08lx\n", regs->link, regs->gpr[1]); 859 + udbg_printf("------\n"); 860 + die("Machine check (early boot)", regs, SIGBUS); 861 + for (;;) 862 + ; 863 + return 0; 864 + } 865 + 854 866 DEFINE_INTERRUPT_HANDLER_ASYNC(machine_check_exception_async) 855 867 { 856 868 __machine_check_exception(regs);