"paca->soft_enabled" is used as a flag to mask some of interrupts. Currently supported flags values and their details: soft_enabled MSR[EE] 0 0 Disabled (PMI and HMI not masked) 1 1 Enabled "paca->soft_enabled" is initialized to 1 to make the interripts as enabled. arch_local_irq_disable() will toggle the value when interrupts needs to disbled. At this point, the interrupts are not actually disabled, instead, interrupt vector has code to check for the flag and mask it when it occurs. By "mask it", it update interrupt paca->irq_happened and return. arch_local_irq_restore() is called to re-enable interrupts, which checks and replays interrupts if any occured. Now, as mentioned, current logic doesnot mask "performance monitoring interrupts" and PMIs are implemented as NMI. But this patchset depends on local_irq_* for a successful local_* update. Meaning, mask all possible interrupts during local_* update and replay them after the update. So the idea here is to reserve the "paca->soft_enabled" logic. New values and details: soft_enabled MSR[EE] 1 0 Disabled (PMI and HMI not masked) 0 1 Enabled Reason for the this change is to create foundation for a third mask value "0x2" for "soft_enabled" to add support to mask PMIs. When ->soft_enabled is set to a value "3", PMI interrupts are mask and when set to a value of "1", PMI are not mask. With this patch also extends soft_enabled as interrupt disable mask. Current flags are renamed from IRQ_[EN?DIS}ABLED to IRQS_ENABLED and IRQS_DISABLED. Patch also fixes the ptrace call to force the user to see the softe value to be alway 1. Reason being, even though userspace has no business knowing about softe, it is part of pt_regs. Like-wise in signal context. Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
274 lines
6.1 KiB
C
274 lines
6.1 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 1999 Cort Dougan <cort@cs.nmt.edu>
|
|
*/
|
|
#ifndef _ASM_POWERPC_HW_IRQ_H
|
|
#define _ASM_POWERPC_HW_IRQ_H
|
|
|
|
#ifdef __KERNEL__
|
|
|
|
#include <linux/errno.h>
|
|
#include <linux/compiler.h>
|
|
#include <asm/ptrace.h>
|
|
#include <asm/processor.h>
|
|
|
|
#ifdef CONFIG_PPC64
|
|
|
|
/*
|
|
* PACA flags in paca->irq_happened.
|
|
*
|
|
* This bits are set when interrupts occur while soft-disabled
|
|
* and allow a proper replay. Additionally, PACA_IRQ_HARD_DIS
|
|
* is set whenever we manually hard disable.
|
|
*/
|
|
#define PACA_IRQ_HARD_DIS 0x01
|
|
#define PACA_IRQ_DBELL 0x02
|
|
#define PACA_IRQ_EE 0x04
|
|
#define PACA_IRQ_DEC 0x08 /* Or FIT */
|
|
#define PACA_IRQ_EE_EDGE 0x10 /* BookE only */
|
|
#define PACA_IRQ_HMI 0x20
|
|
|
|
/*
|
|
* flags for paca->soft_enabled
|
|
*/
|
|
#define IRQS_ENABLED 0
|
|
#define IRQS_DISABLED 1
|
|
|
|
#endif /* CONFIG_PPC64 */
|
|
|
|
#ifndef __ASSEMBLY__
|
|
|
|
extern void replay_system_reset(void);
|
|
extern void __replay_interrupt(unsigned int vector);
|
|
|
|
extern void timer_interrupt(struct pt_regs *);
|
|
extern void performance_monitor_exception(struct pt_regs *regs);
|
|
extern void WatchdogException(struct pt_regs *regs);
|
|
extern void unknown_exception(struct pt_regs *regs);
|
|
|
|
#ifdef CONFIG_PPC64
|
|
#include <asm/paca.h>
|
|
|
|
static inline notrace unsigned long soft_enabled_return(void)
|
|
{
|
|
unsigned long flags;
|
|
|
|
asm volatile(
|
|
"lbz %0,%1(13)"
|
|
: "=r" (flags)
|
|
: "i" (offsetof(struct paca_struct, soft_enabled)));
|
|
|
|
return flags;
|
|
}
|
|
|
|
/*
|
|
* The "memory" clobber acts as both a compiler barrier
|
|
* for the critical section and as a clobber because
|
|
* we changed paca->soft_enabled
|
|
*/
|
|
static inline notrace void soft_enabled_set(unsigned long enable)
|
|
{
|
|
#ifdef CONFIG_TRACE_IRQFLAGS
|
|
/*
|
|
* mask must always include LINUX bit if any are set, and
|
|
* interrupts don't get replayed until the Linux interrupt is
|
|
* unmasked. This could be changed to replay partial unmasks
|
|
* in future, which would allow Linux masks to nest inside
|
|
* other masks, among other things. For now, be very dumb and
|
|
* simple.
|
|
*/
|
|
WARN_ON(mask && !(mask & IRQS_DISABLED));
|
|
#endif
|
|
|
|
asm volatile(
|
|
"stb %0,%1(13)"
|
|
:
|
|
: "r" (enable),
|
|
"i" (offsetof(struct paca_struct, soft_enabled))
|
|
: "memory");
|
|
}
|
|
|
|
static inline notrace unsigned long soft_enabled_set_return(unsigned long mask)
|
|
{
|
|
unsigned long flags;
|
|
|
|
#ifdef CONFIG_TRACE_IRQFLAGS
|
|
WARN_ON(mask && !(mask & IRQS_DISABLED));
|
|
#endif
|
|
|
|
asm volatile(
|
|
"lbz %0,%1(13); stb %2,%1(13)"
|
|
: "=&r" (flags)
|
|
: "i" (offsetof(struct paca_struct, soft_enabled)),
|
|
"r" (mask)
|
|
: "memory");
|
|
|
|
return flags;
|
|
}
|
|
|
|
static inline unsigned long arch_local_save_flags(void)
|
|
{
|
|
return soft_enabled_return();
|
|
}
|
|
|
|
static inline void arch_local_irq_disable(void)
|
|
{
|
|
soft_enabled_set(IRQS_DISABLED);
|
|
}
|
|
|
|
extern void arch_local_irq_restore(unsigned long);
|
|
|
|
static inline void arch_local_irq_enable(void)
|
|
{
|
|
arch_local_irq_restore(IRQS_ENABLED);
|
|
}
|
|
|
|
static inline unsigned long arch_local_irq_save(void)
|
|
{
|
|
return soft_enabled_set_return(IRQS_DISABLED);
|
|
}
|
|
|
|
static inline bool arch_irqs_disabled_flags(unsigned long flags)
|
|
{
|
|
return flags & IRQS_DISABLED;
|
|
}
|
|
|
|
static inline bool arch_irqs_disabled(void)
|
|
{
|
|
return arch_irqs_disabled_flags(arch_local_save_flags());
|
|
}
|
|
|
|
#ifdef CONFIG_PPC_BOOK3E
|
|
#define __hard_irq_enable() asm volatile("wrteei 1" : : : "memory")
|
|
#define __hard_irq_disable() asm volatile("wrteei 0" : : : "memory")
|
|
#else
|
|
#define __hard_irq_enable() __mtmsrd(local_paca->kernel_msr | MSR_EE, 1)
|
|
#define __hard_irq_disable() __mtmsrd(local_paca->kernel_msr, 1)
|
|
#endif
|
|
|
|
#define hard_irq_disable() do { \
|
|
unsigned long flags; \
|
|
__hard_irq_disable(); \
|
|
flags = soft_enabled_set_return(IRQS_DISABLED);\
|
|
local_paca->irq_happened |= PACA_IRQ_HARD_DIS; \
|
|
if (!arch_irqs_disabled_flags(flags)) \
|
|
trace_hardirqs_off(); \
|
|
} while(0)
|
|
|
|
static inline bool lazy_irq_pending(void)
|
|
{
|
|
return !!(get_paca()->irq_happened & ~PACA_IRQ_HARD_DIS);
|
|
}
|
|
|
|
/*
|
|
* This is called by asynchronous interrupts to conditionally
|
|
* re-enable hard interrupts when soft-disabled after having
|
|
* cleared the source of the interrupt
|
|
*/
|
|
static inline void may_hard_irq_enable(void)
|
|
{
|
|
get_paca()->irq_happened &= ~PACA_IRQ_HARD_DIS;
|
|
if (!(get_paca()->irq_happened & PACA_IRQ_EE))
|
|
__hard_irq_enable();
|
|
}
|
|
|
|
static inline bool arch_irq_disabled_regs(struct pt_regs *regs)
|
|
{
|
|
return (regs->softe & IRQS_DISABLED);
|
|
}
|
|
|
|
extern bool prep_irq_for_idle(void);
|
|
extern bool prep_irq_for_idle_irqsoff(void);
|
|
extern void irq_set_pending_from_srr1(unsigned long srr1);
|
|
|
|
#define fini_irq_for_idle_irqsoff() trace_hardirqs_off();
|
|
|
|
extern void force_external_irq_replay(void);
|
|
|
|
#else /* CONFIG_PPC64 */
|
|
|
|
#define SET_MSR_EE(x) mtmsr(x)
|
|
|
|
static inline unsigned long arch_local_save_flags(void)
|
|
{
|
|
return mfmsr();
|
|
}
|
|
|
|
static inline void arch_local_irq_restore(unsigned long flags)
|
|
{
|
|
#if defined(CONFIG_BOOKE)
|
|
asm volatile("wrtee %0" : : "r" (flags) : "memory");
|
|
#else
|
|
mtmsr(flags);
|
|
#endif
|
|
}
|
|
|
|
static inline unsigned long arch_local_irq_save(void)
|
|
{
|
|
unsigned long flags = arch_local_save_flags();
|
|
#ifdef CONFIG_BOOKE
|
|
asm volatile("wrteei 0" : : : "memory");
|
|
#elif defined(CONFIG_PPC_8xx)
|
|
wrtspr(SPRN_EID);
|
|
#else
|
|
SET_MSR_EE(flags & ~MSR_EE);
|
|
#endif
|
|
return flags;
|
|
}
|
|
|
|
static inline void arch_local_irq_disable(void)
|
|
{
|
|
#ifdef CONFIG_BOOKE
|
|
asm volatile("wrteei 0" : : : "memory");
|
|
#elif defined(CONFIG_PPC_8xx)
|
|
wrtspr(SPRN_EID);
|
|
#else
|
|
arch_local_irq_save();
|
|
#endif
|
|
}
|
|
|
|
static inline void arch_local_irq_enable(void)
|
|
{
|
|
#ifdef CONFIG_BOOKE
|
|
asm volatile("wrteei 1" : : : "memory");
|
|
#elif defined(CONFIG_PPC_8xx)
|
|
wrtspr(SPRN_EIE);
|
|
#else
|
|
unsigned long msr = mfmsr();
|
|
SET_MSR_EE(msr | MSR_EE);
|
|
#endif
|
|
}
|
|
|
|
static inline bool arch_irqs_disabled_flags(unsigned long flags)
|
|
{
|
|
return (flags & MSR_EE) == 0;
|
|
}
|
|
|
|
static inline bool arch_irqs_disabled(void)
|
|
{
|
|
return arch_irqs_disabled_flags(arch_local_save_flags());
|
|
}
|
|
|
|
#define hard_irq_disable() arch_local_irq_disable()
|
|
|
|
static inline bool arch_irq_disabled_regs(struct pt_regs *regs)
|
|
{
|
|
return !(regs->msr & MSR_EE);
|
|
}
|
|
|
|
static inline void may_hard_irq_enable(void) { }
|
|
|
|
#endif /* CONFIG_PPC64 */
|
|
|
|
#define ARCH_IRQ_INIT_FLAGS IRQ_NOREQUEST
|
|
|
|
/*
|
|
* interrupt-retrigger: should we handle this via lost interrupts and IPIs
|
|
* or should we not care like we do now ? --BenH.
|
|
*/
|
|
struct irq_chip;
|
|
|
|
#endif /* __ASSEMBLY__ */
|
|
#endif /* __KERNEL__ */
|
|
#endif /* _ASM_POWERPC_HW_IRQ_H */
|