@@ -431,6 +431,7 @@ END_FTR_SECTION_NESTED(ftr,ftr,943)
#define SOFTEN_VALUE_0xe80 PACA_IRQ_DBELL
#define SOFTEN_VALUE_0xe60 PACA_IRQ_HMI
#define SOFTEN_VALUE_0xea0 PACA_IRQ_EE
+#define SOFTEN_VALUE_0xf00 PACA_IRQ_PMI
#define __SOFTEN_TEST(h, vec, bitmask) \
lbz r10,PACASOFTIRQEN(r13); \
@@ -495,6 +496,10 @@ END_FTR_SECTION_NESTED(ftr,ftr,943)
_MASKABLE_RELON_EXCEPTION_PSERIES(vec, label, \
EXC_STD, SOFTEN_NOTEST_PR, bitmask)
+#define MASKABLE_RELON_EXCEPTION_PSERIES_OOL(vec, label, bitmask) \
+ MASKABLE_EXCEPTION_PROLOG_1(PACA_EXGEN, SOFTEN_NOTEST_PR, vec, bitmask);\
+ EXCEPTION_PROLOG_PSERIES_1(label, EXC_STD);
+
#define MASKABLE_RELON_EXCEPTION_HV(loc, vec, label, bitmask) \
_MASKABLE_RELON_EXCEPTION_PSERIES(vec, label, \
EXC_HV, SOFTEN_NOTEST_HV, bitmask)
@@ -26,12 +26,15 @@
#define PACA_IRQ_DEC 0x08 /* Or FIT */
#define PACA_IRQ_EE_EDGE 0x10 /* BookE only */
#define PACA_IRQ_HMI 0x20
+#define PACA_IRQ_PMI 0x40
/*
* flags for paca->soft_enabled
*/
#define IRQ_DISABLE_MASK_NONE 0
#define IRQ_DISABLE_MASK_LINUX 1
+#define IRQ_DISABLE_MASK_PMU 2
+#define IRQ_DISABLE_MASK_ALL 3
#endif /* CONFIG_PPC64 */
@@ -132,7 +135,7 @@ static inline bool arch_irqs_disabled(void)
u8 _was_enabled; \
__hard_irq_disable(); \
_was_enabled = local_paca->soft_enabled; \
- local_paca->soft_enabled = IRQ_DISABLE_MASK_LINUX;\
+ local_paca->soft_enabled = IRQ_DISABLE_MASK_ALL;\
local_paca->irq_happened |= PACA_IRQ_HARD_DIS; \
if (!(_was_enabled & IRQ_DISABLE_MASK_LINUX)) \
trace_hardirqs_off(); \
@@ -931,6 +931,11 @@ restore_check_irq_replay:
addi r3,r1,STACK_FRAME_OVERHEAD;
bl do_IRQ
b ret_from_except
+1: cmpwi cr0,r3,0xf00
+ bne 1f
+ addi r3,r1,STACK_FRAME_OVERHEAD;
+ bl performance_monitor_exception
+ b ret_from_except
1: cmpwi cr0,r3,0xe60
bne 1f
addi r3,r1,STACK_FRAME_OVERHEAD;
@@ -1041,8 +1041,8 @@ EXC_REAL_NONE(0xec0, 0xf00)
EXC_VIRT_NONE(0x4ec0, 0x4f00)
-EXC_REAL_OOL(performance_monitor, 0xf00, 0xf20)
-EXC_VIRT_OOL(performance_monitor, 0x4f00, 0x4f20, 0xf00)
+EXC_REAL_OOL_MASKABLE(performance_monitor, 0xf00, 0xf20, IRQ_DISABLE_MASK_PMU)
+EXC_VIRT_OOL_MASKABLE(performance_monitor, 0x4f00, 0x4f20, 0xf00, IRQ_DISABLE_MASK_PMU)
TRAMP_KVM(PACA_EXGEN, 0xf00)
EXC_COMMON_ASYNC(performance_monitor_common, 0xf00, performance_monitor_exception)
@@ -1580,6 +1580,8 @@ _GLOBAL(__replay_interrupt)
beq decrementer_common
cmpwi r3,0x500
beq hardware_interrupt_common
+ cmpwi r3,0xf00
+ beq performance_monitor_common
BEGIN_FTR_SECTION
cmpwi r3,0xe80
beq h_doorbell_common
@@ -169,6 +169,27 @@ notrace unsigned int __check_irq_replay(void)
if ((happened & PACA_IRQ_DEC) || decrementer_check_overflow())
return 0x900;
+ /*
+ * In masked_handler() for PMI, we disable MSR[EE] and return.
+ * Replay it here.
+ *
+ * After this point, PMIs could still be disabled in certain
+ * scenarios like this one.
+ *
+ * local_irq_disable();
+ * powerpc_irq_pmu_save();
+ * powerpc_irq_pmu_restore();
+ * local_irq_restore();
+ *
+ * Even though powerpc_irq_pmu_restore() would have replayed the PMIs
+ * if any, we have still not enabled EE and this will happen only at
+ * complition of last *_restore in this nested cases. And PMIs will
+ * once again start firing only when we have MSR[EE] enabled.
+ */
+ local_paca->irq_happened &= ~PACA_IRQ_PMI;
+ if (happened & PACA_IRQ_PMI)
+ return 0xf00;
+
/* Finally check if an external interrupt happened */
local_paca->irq_happened &= ~PACA_IRQ_EE;
if (happened & PACA_IRQ_EE)
@@ -208,7 +229,9 @@ notrace void arch_local_irq_restore(unsigned long en)
/* Write the new soft-enabled value */
soft_enabled_set(en);
- if (en == IRQ_DISABLE_MASK_LINUX)
+
+ /* any bits still disabled */
+ if (en)
return;
/*
* From this point onward, we can take interrupts, preempt,