aboutsummaryrefslogtreecommitdiff
path: root/arch/arm64/include/asm
diff options
context:
space:
mode:
authorDaniel Thompson <daniel.thompson@linaro.org>2015-03-17 16:29:22 +0000
committerDaniel Thompson <daniel.thompson@linaro.org>2017-02-27 14:31:42 +0000
commit2ec7980c302cf037b4987e6e0c39fdfb06b0326b (patch)
tree8d6df3c05620c6d79861d507f87a846c876e5eb4 /arch/arm64/include/asm
parent0ed2673d629f5f695c8e003bb3c6a360a46105f2 (diff)
arm64: irqflags: Use ICC sysregs to implement IRQ masking
Currently irqflags is implemented using the PSR's I bit. It is possible to implement irqflags by using the co-processor interface to the GIC. Using the co-processor interface makes it feasible to simulate NMIs using GIC interrupt prioritization. This patch changes the irqflags macros to modify, save and restore ICC_PMR_EL1. This has a substantial knock on effect for the rest of the kernel. There are three reasons for this: 1. The state of the ICC_PMR_EL1_G_BIT becomes part of the CPU context and must be saved and restored during traps. To simplify the additional context management the ICC_PMR_EL1_G_BIT is converted into a fake (reserved) bit within the PSR (PSR_G_BIT). Naturally this approach will need to be changed if future ARM architecture extensions make use of this bit. 2. The hardware automatically masks the I bit (at boot, during traps, etc). When the I bit is set by hardware we must add code to switch from I bit masking and PMR masking. 3. Some instructions, noteably wfi, require that the PMR not be used for interrupt masking. Before calling these instructions we must switch from PMR masking to I bit masking. Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
Diffstat (limited to 'arch/arm64/include/asm')
-rw-r--r--arch/arm64/include/asm/assembler.h74
-rw-r--r--arch/arm64/include/asm/irqflags.h89
-rw-r--r--arch/arm64/include/asm/ptrace.h10
3 files changed, 167 insertions, 6 deletions
diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
index 3579988b23f9..cce73427207b 100644
--- a/arch/arm64/include/asm/assembler.h
+++ b/arch/arm64/include/asm/assembler.h
@@ -23,6 +23,9 @@
#ifndef __ASM_ASSEMBLER_H
#define __ASM_ASSEMBLER_H
+#include <linux/irqchip/arm-gic-v3.h>
+#include <asm/asm-offsets.h>
+#include <asm/pgtable-hwdef.h>
#include <asm/ptrace.h>
#include <asm/thread_info.h>
@@ -39,26 +42,79 @@
.endm
/*
+ * Enable and disable pseudo NMI.
+ */
+ .macro disable_nmi
+#ifdef CONFIG_USE_ICC_SYSREGS_FOR_IRQFLAGS
+ msr daifset, #2
+#endif
+ .endm
+
+ .macro enable_nmi
+#ifdef CONFIG_USE_ICC_SYSREGS_FOR_IRQFLAGS
+ msr daifclr, #2
+#endif
+ .endm
+
+/*
+ * Save/disable and restore pseudo NMI.
+ */
+ .macro save_and_disable_nmis, olddaif
+#ifdef CONFIG_USE_ICC_SYSREGS_FOR_IRQFLAGS
+ mrs \olddaif, daif /* Get flags */
+ disable_nmi
+#endif
+ .endm
+
+ .macro restore_nmis, olddaif
+#ifdef CONFIG_USE_ICC_SYSREGS_FOR_IRQFLAGS
+ msr daif, \olddaif
+#endif
+ .endm
+
+/*
* Enable and disable interrupts.
*/
- .macro disable_irq
+ .macro disable_irq, tmp
+#ifdef CONFIG_USE_ICC_SYSREGS_FOR_IRQFLAGS
+ mov \tmp, #ICC_PMR_EL1_MASKED
+ msr_s ICC_PMR_EL1, \tmp
+ isb
+#else
msr daifset, #2
+#endif
.endm
- .macro enable_irq
+ .macro enable_irq, tmp
+#ifdef CONFIG_USE_ICC_SYSREGS_FOR_IRQFLAGS
+ enable_nmi
+ mov \tmp, #ICC_PMR_EL1_UNMASKED
+ msr_s ICC_PMR_EL1, \tmp
+ isb
+#else
msr daifclr, #2
+#endif
.endm
/*
* Save/disable and restore interrupts.
*/
- .macro save_and_disable_irqs, olddaif
- mrs \olddaif, daif
- disable_irq
+ .macro save_and_disable_irqs, olddaif, tmp
+#ifdef CONFIG_USE_ICC_SYSREGS_FOR_IRQFLAGS
+ mrs_s \olddaif, ICC_PMR_EL1 /* Get PMR */
+#else
+ mrs \olddaif, daif /* Get flags */
+#endif
+ disable_irq \tmp
.endm
.macro restore_irqs, olddaif
+#ifdef CONFIG_USE_ICC_SYSREGS_FOR_IRQFLAGS
+ msr_s ICC_PMR_EL1, \olddaif /* Write to PMR */
+ isb
+#else
msr daif, \olddaif
+#endif
.endm
/*
@@ -90,13 +146,19 @@
9990:
.endm
+
/*
* Enable both debug exceptions and interrupts. This is likely to be
* faster than two daifclr operations, since writes to this register
* are self-synchronising.
*/
- .macro enable_dbg_and_irq
+ .macro enable_dbg_and_irq, tmp
+#ifdef CONFIG_USE_ICC_SYSREGS_FOR_IRQFLAGS
+ enable_dbg
+ enable_irq \tmp
+#else
msr daifclr, #(8 | 2)
+#endif
.endm
/*
diff --git a/arch/arm64/include/asm/irqflags.h b/arch/arm64/include/asm/irqflags.h
index df7477af6389..7b6866022f82 100644
--- a/arch/arm64/include/asm/irqflags.h
+++ b/arch/arm64/include/asm/irqflags.h
@@ -20,6 +20,8 @@
#include <asm/ptrace.h>
+#ifndef CONFIG_USE_ICC_SYSREGS_FOR_IRQFLAGS
+
/*
* CPU interrupt mask handling.
*/
@@ -84,6 +86,93 @@ static inline int arch_irqs_disabled_flags(unsigned long flags)
return flags & PSR_I_BIT;
}
+#else /* CONFIG_IRQFLAGS_GIC_MASKING */
+
+#include <linux/irqchip/arm-gic-v3.h>
+
+/*
+ * CPU interrupt mask handling.
+ */
+static inline unsigned long arch_local_irq_save(void)
+{
+ unsigned long flags, masked = ICC_PMR_EL1_MASKED;
+
+ asm volatile(
+ "// arch_local_irq_save\n"
+ "mrs_s %0, " __stringify(ICC_PMR_EL1) "\n"
+ "msr_s " __stringify(ICC_PMR_EL1) ",%1\n"
+ "isb\n"
+ : "=&r" (flags)
+ : "r" (masked)
+ : "memory");
+
+ return flags;
+}
+
+static inline void arch_local_irq_enable(void)
+{
+ unsigned long unmasked = ICC_PMR_EL1_UNMASKED;
+
+ asm volatile(
+ "// arch_local_irq_enable\n"
+ "msr_s " __stringify(ICC_PMR_EL1) ",%0\n"
+ "isb\n"
+ :
+ : "r" (unmasked)
+ : "memory");
+}
+
+static inline void arch_local_irq_disable(void)
+{
+ unsigned long masked = ICC_PMR_EL1_MASKED;
+
+ asm volatile(
+ "// arch_local_irq_disable\n"
+ "msr_s " __stringify(ICC_PMR_EL1) ",%0\n"
+ "isb\n"
+ :
+ : "r" (masked)
+ : "memory");
+}
+
+/*
+ * Save the current interrupt enable state.
+ */
+static inline unsigned long arch_local_save_flags(void)
+{
+ unsigned long flags;
+
+ asm volatile(
+ "// arch_local_save_flags\n"
+ "mrs_s %0, " __stringify(ICC_PMR_EL1) "\n"
+ : "=r" (flags)
+ :
+ : "memory");
+
+ return flags;
+}
+
+/*
+ * restore saved IRQ state
+ */
+static inline void arch_local_irq_restore(unsigned long flags)
+{
+ asm volatile(
+ "// arch_local_irq_restore\n"
+ "msr_s " __stringify(ICC_PMR_EL1) ",%0\n"
+ "isb\n"
+ :
+ : "r" (flags)
+ : "memory");
+}
+
+static inline int arch_irqs_disabled_flags(unsigned long flags)
+{
+ return !(flags & ICC_PMR_EL1_G_BIT);
+}
+
+#endif /* CONFIG_IRQFLAGS_GIC_MASKING */
+
#define local_fiq_enable() asm("msr daifclr, #1" : : : "memory")
#define local_fiq_disable() asm("msr daifset, #1" : : : "memory")
diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h
index d6dd9fdbc3be..f4f93caeed02 100644
--- a/arch/arm64/include/asm/ptrace.h
+++ b/arch/arm64/include/asm/ptrace.h
@@ -25,6 +25,16 @@
#define CurrentEL_EL1 (1 << 2)
#define CurrentEL_EL2 (2 << 2)
+/* PMR values used to mask/unmask interrupts */
+#define ICC_PMR_EL1_G_SHIFT 6
+#define ICC_PMR_EL1_G_BIT (1 << ICC_PMR_EL1_G_SHIFT)
+#define ICC_PMR_EL1_UNMASKED 0xf0
+#define ICC_PMR_EL1_MASKED (ICC_PMR_EL1_UNMASKED ^ ICC_PMR_EL1_G_BIT)
+
+#define PSR_G_SHIFT 22
+#define PSR_G_PMR_G_SHIFT (PSR_G_SHIFT - ICC_PMR_EL1_G_SHIFT)
+#define PSR_I_PMR_G_SHIFT (7 - ICC_PMR_EL1_G_SHIFT)
+
/* AArch32-specific ptrace requests */
#define COMPAT_PTRACE_GETREGS 12
#define COMPAT_PTRACE_SETREGS 13