Commit 3db8aa10 authored by Nicholas Piggin's avatar Nicholas Piggin Committed by Michael Ellerman
Browse files

powerpc/64e/interrupt: NMI save irq soft-mask state in C



64e non-maskable interrupts save the state of the irq soft-mask in
asm. This can be done in C in interrupt wrappers as 64s does.

I haven't been able to test this with qemu because it doesn't seem
to cause FSL bookE WDT interrupts.

This makes WatchdogException an NMI interrupt, which affects 32-bit
as well (okay, or create a new handler?)

Signed-off-by: default avatarNicholas Piggin <npiggin@gmail.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20210316104206.407354-6-npiggin@gmail.com
parent 0c2472de
Loading
Loading
Loading
Loading
+22 −10
Original line number Diff line number Diff line
@@ -149,18 +149,32 @@ static inline void interrupt_async_exit_prepare(struct pt_regs *regs, struct int

struct interrupt_nmi_state {
#ifdef CONFIG_PPC64
#ifdef CONFIG_PPC_BOOK3S_64
	u8 irq_soft_mask;
	u8 irq_happened;
#endif
	u8 ftrace_enabled;
#endif
};

static inline bool nmi_disables_ftrace(struct pt_regs *regs)
{
	/* Allow DEC and PMI to be traced when they are soft-NMI */
	if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) {
		if (TRAP(regs) == 0x900)
		       return false;
		if (TRAP(regs) == 0xf00)
		       return false;
	}
	if (IS_ENABLED(CONFIG_PPC_BOOK3E)) {
		if (TRAP(regs) == 0x260)
			return false;
	}

	return true;
}

static inline void interrupt_nmi_enter_prepare(struct pt_regs *regs, struct interrupt_nmi_state *state)
{
#ifdef CONFIG_PPC64
#ifdef CONFIG_PPC_BOOK3S_64
	state->irq_soft_mask = local_paca->irq_soft_mask;
	state->irq_happened = local_paca->irq_happened;

@@ -173,9 +187,8 @@ static inline void interrupt_nmi_enter_prepare(struct pt_regs *regs, struct inte
	local_paca->irq_happened |= PACA_IRQ_HARD_DIS;

	/* Don't do any per-CPU operations until interrupt state is fixed */
#endif
	/* Allow DEC and PMI to be traced when they are soft-NMI */
	if (TRAP(regs) != 0x900 && TRAP(regs) != 0xf00 && TRAP(regs) != 0x260) {

	if (nmi_disables_ftrace(regs)) {
		state->ftrace_enabled = this_cpu_get_ftrace_enabled();
		this_cpu_set_ftrace_enabled(0);
	}
@@ -204,16 +217,14 @@ static inline void interrupt_nmi_exit_prepare(struct pt_regs *regs, struct inter
	 */

#ifdef CONFIG_PPC64
	if (TRAP(regs) != 0x900 && TRAP(regs) != 0xf00 && TRAP(regs) != 0x260)
	if (nmi_disables_ftrace(regs))
		this_cpu_set_ftrace_enabled(state->ftrace_enabled);

#ifdef CONFIG_PPC_BOOK3S_64
	/* Check we didn't change the pending interrupt mask. */
	WARN_ON_ONCE((state->irq_happened | PACA_IRQ_HARD_DIS) != local_paca->irq_happened);
	local_paca->irq_happened = state->irq_happened;
	local_paca->irq_soft_mask = state->irq_soft_mask;
#endif
#endif
}

/*
@@ -426,6 +437,7 @@ DECLARE_INTERRUPT_HANDLER(SMIException);
DECLARE_INTERRUPT_HANDLER(handle_hmi_exception);
DECLARE_INTERRUPT_HANDLER(unknown_exception);
DECLARE_INTERRUPT_HANDLER_ASYNC(unknown_async_exception);
DECLARE_INTERRUPT_HANDLER_NMI(unknown_nmi_exception);
DECLARE_INTERRUPT_HANDLER(instruction_breakpoint_exception);
DECLARE_INTERRUPT_HANDLER(RunModeException);
DECLARE_INTERRUPT_HANDLER(single_step_exception);
@@ -449,7 +461,7 @@ DECLARE_INTERRUPT_HANDLER(altivec_assist_exception);
DECLARE_INTERRUPT_HANDLER(CacheLockingException);
DECLARE_INTERRUPT_HANDLER(SPEFloatingPointException);
DECLARE_INTERRUPT_HANDLER(SPEFloatingPointRoundException);
DECLARE_INTERRUPT_HANDLER(WatchdogException);
DECLARE_INTERRUPT_HANDLER_NMI(WatchdogException);
DECLARE_INTERRUPT_HANDLER(kernel_bad_stack);

/* slb.c */
+4 −32
Original line number Diff line number Diff line
@@ -63,9 +63,6 @@
	ld	reg, (SPECIAL_EXC_##name * 8 + SPECIAL_EXC_FRAME_OFFS)(r1)

special_reg_save:
	lbz	r9,PACAIRQHAPPENED(r13)
	RECONCILE_IRQ_STATE(r3,r4)

	/*
	 * We only need (or have stack space) to save this stuff if
	 * we interrupted the kernel.
@@ -119,15 +116,11 @@ BEGIN_FTR_SECTION
	mtspr	SPRN_MAS5,r10
	mtspr	SPRN_MAS8,r10
END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV)
	SPECIAL_EXC_STORE(r9,IRQHAPPENED)

	mfspr	r10,SPRN_DEAR
	SPECIAL_EXC_STORE(r10,DEAR)
	mfspr	r10,SPRN_ESR
	SPECIAL_EXC_STORE(r10,ESR)

	lbz	r10,PACAIRQSOFTMASK(r13)
	SPECIAL_EXC_STORE(r10,SOFTE)
	ld	r10,_NIP(r1)
	SPECIAL_EXC_STORE(r10,CSRR0)
	ld	r10,_MSR(r1)
@@ -194,27 +187,6 @@ BEGIN_FTR_SECTION
	mtspr	SPRN_MAS8,r10
END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV)

	lbz	r6,PACAIRQSOFTMASK(r13)
	ld	r5,SOFTE(r1)

	/* Interrupts had better not already be enabled... */
	tweqi	r6,IRQS_ENABLED

	andi.	r6,r5,IRQS_DISABLED
	bne	1f

	TRACE_ENABLE_INTS
	stb	r5,PACAIRQSOFTMASK(r13)
1:
	/*
	 * Restore PACAIRQHAPPENED rather than setting it based on
	 * the return MSR[EE], since we could have interrupted
	 * interrupt replay or other inconsistent transitory
	 * states that must remain that way.
	 */
	SPECIAL_EXC_LOAD(r10,IRQHAPPENED)
	stb	r10,PACAIRQHAPPENED(r13)

	SPECIAL_EXC_LOAD(r10,DEAR)
	mtspr	SPRN_DEAR,r10
	SPECIAL_EXC_LOAD(r10,ESR)
@@ -566,7 +538,7 @@ __end_interrupts:
	bl	special_reg_save
	CHECK_NAPPING();
	addi	r3,r1,STACK_FRAME_OVERHEAD
	bl	unknown_exception
	bl	unknown_nmi_exception
	b	ret_from_crit_except

/* Machine Check Interrupt */
@@ -702,7 +674,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
#ifdef CONFIG_BOOKE_WDT
	bl	WatchdogException
#else
	bl	unknown_exception
	bl	unknown_nmi_exception
#endif
	b	ret_from_crit_except

@@ -886,7 +858,7 @@ kernel_dbg_exc:
	bl	special_reg_save
	CHECK_NAPPING();
	addi	r3,r1,STACK_FRAME_OVERHEAD
	bl	unknown_exception
	bl	unknown_nmi_exception
	b	ret_from_crit_except

/*
@@ -910,7 +882,7 @@ kernel_dbg_exc:
	bl	special_reg_save
	CHECK_NAPPING();
	addi	r3,r1,STACK_FRAME_OVERHEAD
	bl	unknown_exception
	bl	unknown_nmi_exception
	b	ret_from_crit_except

/* Hypervisor call */
+12 −1
Original line number Diff line number Diff line
@@ -1078,6 +1078,16 @@ DEFINE_INTERRUPT_HANDLER_ASYNC(unknown_async_exception)
	_exception(SIGTRAP, regs, TRAP_UNK, 0);
}

DEFINE_INTERRUPT_HANDLER_NMI(unknown_nmi_exception)
{
	printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n",
	       regs->nip, regs->msr, regs->trap);

	_exception(SIGTRAP, regs, TRAP_UNK, 0);

	return 0;
}

DEFINE_INTERRUPT_HANDLER(instruction_breakpoint_exception)
{
	if (notify_die(DIE_IABR_MATCH, "iabr_match", regs, 5,
@@ -2181,10 +2191,11 @@ void __attribute__ ((weak)) WatchdogHandler(struct pt_regs *regs)
	return;
}

DEFINE_INTERRUPT_HANDLER(WatchdogException) /* XXX NMI? async? */
DEFINE_INTERRUPT_HANDLER_NMI(WatchdogException)
{
	printk (KERN_EMERG "PowerPC Book-E Watchdog Exception\n");
	WatchdogHandler(regs);
	return 0;
}
#endif