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
                    int64_t opcode, uint64_t msr);
 
 /* misc runtime */
+void enable_machine_check(void);
 extern u64 __bswapdi2(u64);
 extern s64 __lshrdi3(s64, int);
 extern s64 __ashldi3(s64, int);
 
 INT_DEFINE_END(machine_check)
 
 EXC_REAL_BEGIN(machine_check, 0x200, 0x100)
+       EARLY_BOOT_FIXUP
        GEN_INT_ENTRY machine_check_early, virt=0
 EXC_REAL_END(machine_check, 0x200, 0x100)
 EXC_VIRT_NONE(0x4200, 0x100)
        bl      enable_machine_check
 END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
        addi    r3,r1,STACK_FRAME_OVERHEAD
+BEGIN_FTR_SECTION
+       bl      machine_check_early_boot
+END_FTR_SECTION(0, 1)     // nop out after boot
        bl      machine_check_early
        std     r3,RESULT(r1)   /* Save result */
        ld      r12,_MSR(r1)
 USE_TEXT_SECTION()
 
 /* MSR[RI] should be clear because this uses SRR[01] */
-enable_machine_check:
+_GLOBAL(enable_machine_check)
        mflr    r0
        bcl     20,31,$+4
 0:     mflr    r3
 
 #include <linux/of.h>
 #include <linux/of_fdt.h>
 
+#include <asm/asm-prototypes.h>
 #include <asm/kvm_guest.h>
 #include <asm/io.h>
 #include <asm/kdump.h>
 {
        /* The boot cpu is started */
        get_paca()->cpu_start = 1;
+#ifdef CONFIG_PPC_BOOK3S_64
+       /*
+        * Give the early boot machine check stack somewhere to use, use
+        * half of the init stack. This is a bit hacky but there should not be
+        * deep stack usage in early init so shouldn't overflow it or overwrite
+        * things.
+        */
+       get_paca()->mc_emergency_sp = (void *)&init_thread_union +
+               (THREAD_SIZE/2);
+#endif
        /* Allow percpu accesses to work until we setup percpu data */
        get_paca()->data_offset = 0;
        /* Mark interrupts soft and hard disabled in PACA */
 
        /* -------- printk is now safe to use ------- */
 
+       if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && (mfmsr() & MSR_HV))
+               enable_machine_check();
+
        /* Try new device tree based feature discovery ... */
        if (!dt_cpu_ftrs_init(__va(dt_ptr)))
                /* Otherwise use the old style CPU table */
 
 #include <asm/stacktrace.h>
 #include <asm/nmi.h>
 #include <asm/disassemble.h>
+#include <asm/udbg.h>
 
 #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC_CORE)
 int (*__debugger)(struct pt_regs *regs) __read_mostly;
 }
 
 #ifdef CONFIG_PPC_BOOK3S_64
+DEFINE_INTERRUPT_HANDLER_RAW(machine_check_early_boot)
+{
+       udbg_printf("Machine check (early boot)\n");
+       udbg_printf("SRR0=0x%016lx   SRR1=0x%016lx\n", regs->nip, regs->msr);
+       udbg_printf(" DAR=0x%016lx  DSISR=0x%08lx\n", regs->dar, regs->dsisr);
+       udbg_printf("  LR=0x%016lx     R1=0x%08lx\n", regs->link, regs->gpr[1]);
+       udbg_printf("------\n");
+       die("Machine check (early boot)", regs, SIGBUS);
+       for (;;)
+               ;
+       return 0;
+}
+
 DEFINE_INTERRUPT_HANDLER_ASYNC(machine_check_exception_async)
 {
        __machine_check_exception(regs);