Ivan T. Ivanov 931dd8
From: Julien Thierry <julien.thierry@arm.com>
Ivan T. Ivanov 931dd8
Date: Thu, 31 Jan 2019 14:58:39 +0000
Ivan T. Ivanov 931dd8
Subject: arm64: Fix HCR.TGE status for NMI contexts
Ivan T. Ivanov 931dd8
Git-commit: 5870970b9a828d8693aa6d15742573289d7dbcd0
Ivan T. Ivanov 931dd8
Patch-mainline: v5.1-rc1
Ivan T. Ivanov 931dd8
References: git-fixes
Ivan T. Ivanov 931dd8
Ivan T. Ivanov 931dd8
When using VHE, the host needs to clear HCR_EL2.TGE bit in order
Ivan T. Ivanov 931dd8
to interact with guest TLBs, switching from EL2&0 translation regime
Ivan T. Ivanov 931dd8
to EL1&0.
Ivan T. Ivanov 931dd8
Ivan T. Ivanov 931dd8
However, some non-maskable asynchronous event could happen while TGE is
Ivan T. Ivanov 931dd8
cleared like SDEI. Because of this address translation operations
Ivan T. Ivanov 931dd8
relying on EL2&0 translation regime could fail (tlb invalidation,
Ivan T. Ivanov 931dd8
userspace access, ...).
Ivan T. Ivanov 931dd8
Ivan T. Ivanov 931dd8
Fix this by properly setting HCR_EL2.TGE when entering NMI context and
Ivan T. Ivanov 931dd8
clear it if necessary when returning to the interrupted context.
Ivan T. Ivanov 931dd8
Ivan T. Ivanov 931dd8
Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Ivan T. Ivanov 931dd8
Suggested-by: Marc Zyngier <marc.zyngier@arm.com>
Ivan T. Ivanov 931dd8
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
Ivan T. Ivanov 931dd8
Reviewed-by: James Morse <james.morse@arm.com>
Ivan T. Ivanov 931dd8
Cc: Arnd Bergmann <arnd@arndb.de>
Ivan T. Ivanov 931dd8
Cc: Will Deacon <will.deacon@arm.com>
Ivan T. Ivanov 931dd8
Cc: Marc Zyngier <marc.zyngier@arm.com>
Ivan T. Ivanov 931dd8
Cc: James Morse <james.morse@arm.com>
Ivan T. Ivanov 931dd8
Cc: linux-arch@vger.kernel.org
Ivan T. Ivanov 931dd8
Cc: stable@vger.kernel.org
Ivan T. Ivanov 931dd8
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Ivan T. Ivanov 931dd8
Acked-by: Ivan T. Ivanov <iivanov@suse.de>
Ivan T. Ivanov 931dd8
---
Ivan T. Ivanov 931dd8
 arch/arm64/include/asm/hardirq.h |   31 +++++++++++++++++++++++++++++++
Ivan T. Ivanov 931dd8
 arch/arm64/kernel/irq.c          |    3 +++
Ivan T. Ivanov 931dd8
 include/linux/hardirq.h          |    7 +++++++
Ivan T. Ivanov 931dd8
 3 files changed, 41 insertions(+)
Ivan T. Ivanov 931dd8
Ivan T. Ivanov 931dd8
--- a/arch/arm64/include/asm/hardirq.h
Ivan T. Ivanov 931dd8
+++ b/arch/arm64/include/asm/hardirq.h
Ivan T. Ivanov 931dd8
@@ -17,8 +17,12 @@
Ivan T. Ivanov 931dd8
 #define __ASM_HARDIRQ_H
Ivan T. Ivanov 931dd8
 
Ivan T. Ivanov 931dd8
 #include <linux/cache.h>
Ivan T. Ivanov 931dd8
+#include <linux/percpu.h>
Ivan T. Ivanov 931dd8
 #include <linux/threads.h>
Ivan T. Ivanov 931dd8
+#include <asm/barrier.h>
Ivan T. Ivanov 931dd8
 #include <asm/irq.h>
Ivan T. Ivanov 931dd8
+#include <asm/kvm_arm.h>
Ivan T. Ivanov 931dd8
+#include <asm/sysreg.h>
Ivan T. Ivanov 931dd8
 
Ivan T. Ivanov 931dd8
 #define NR_IPI	7
Ivan T. Ivanov 931dd8
 
Ivan T. Ivanov 931dd8
@@ -37,6 +41,33 @@ u64 smp_irq_stat_cpu(unsigned int cpu);
Ivan T. Ivanov 931dd8
 
Ivan T. Ivanov 931dd8
 #define __ARCH_IRQ_EXIT_IRQS_DISABLED	1
Ivan T. Ivanov 931dd8
 
Ivan T. Ivanov 931dd8
+struct nmi_ctx {
Ivan T. Ivanov 931dd8
+	u64 hcr;
Ivan T. Ivanov 931dd8
+};
Ivan T. Ivanov 931dd8
+
Ivan T. Ivanov 931dd8
+DECLARE_PER_CPU(struct nmi_ctx, nmi_contexts);
Ivan T. Ivanov 931dd8
+
Ivan T. Ivanov 931dd8
+#define arch_nmi_enter()							\
Ivan T. Ivanov 931dd8
+	do {									\
Ivan T. Ivanov 931dd8
+		if (is_kernel_in_hyp_mode()) {					\
Ivan T. Ivanov 931dd8
+			struct nmi_ctx *nmi_ctx = this_cpu_ptr(&nmi_contexts);	\
Ivan T. Ivanov 931dd8
+			nmi_ctx->hcr = read_sysreg(hcr_el2);			\
Ivan T. Ivanov 931dd8
+			if (!(nmi_ctx->hcr & HCR_TGE)) {			\
Ivan T. Ivanov 931dd8
+				write_sysreg(nmi_ctx->hcr | HCR_TGE, hcr_el2);	\
Ivan T. Ivanov 931dd8
+				isb();						\
Ivan T. Ivanov 931dd8
+			}							\
Ivan T. Ivanov 931dd8
+		}								\
Ivan T. Ivanov 931dd8
+	} while (0)
Ivan T. Ivanov 931dd8
+
Ivan T. Ivanov 931dd8
+#define arch_nmi_exit()								\
Ivan T. Ivanov 931dd8
+	do {									\
Ivan T. Ivanov 931dd8
+		if (is_kernel_in_hyp_mode()) {					\
Ivan T. Ivanov 931dd8
+			struct nmi_ctx *nmi_ctx = this_cpu_ptr(&nmi_contexts);	\
Ivan T. Ivanov 931dd8
+			if (!(nmi_ctx->hcr & HCR_TGE))				\
Ivan T. Ivanov 931dd8
+				write_sysreg(nmi_ctx->hcr, hcr_el2);		\
Ivan T. Ivanov 931dd8
+		}								\
Ivan T. Ivanov 931dd8
+	} while (0)
Ivan T. Ivanov 931dd8
+
Ivan T. Ivanov 931dd8
 static inline void ack_bad_irq(unsigned int irq)
Ivan T. Ivanov 931dd8
 {
Ivan T. Ivanov 931dd8
 	extern unsigned long irq_err_count;
Ivan T. Ivanov 931dd8
--- a/arch/arm64/kernel/irq.c
Ivan T. Ivanov 931dd8
+++ b/arch/arm64/kernel/irq.c
Ivan T. Ivanov 931dd8
@@ -33,6 +33,9 @@
Ivan T. Ivanov 931dd8
 
Ivan T. Ivanov 931dd8
 unsigned long irq_err_count;
Ivan T. Ivanov 931dd8
 
Ivan T. Ivanov 931dd8
+/* Only access this in an NMI enter/exit */
Ivan T. Ivanov 931dd8
+DEFINE_PER_CPU(struct nmi_ctx, nmi_contexts);
Ivan T. Ivanov 931dd8
+
Ivan T. Ivanov 931dd8
 DEFINE_PER_CPU(unsigned long *, irq_stack_ptr);
Ivan T. Ivanov 931dd8
 
Ivan T. Ivanov 931dd8
 int arch_show_interrupts(struct seq_file *p, int prec)
Ivan T. Ivanov 931dd8
--- a/include/linux/hardirq.h
Ivan T. Ivanov 931dd8
+++ b/include/linux/hardirq.h
Ivan T. Ivanov 931dd8
@@ -59,8 +59,14 @@ extern void irq_enter(void);
Ivan T. Ivanov 931dd8
  */
Ivan T. Ivanov 931dd8
 extern void irq_exit(void);
Ivan T. Ivanov 931dd8
 
Ivan T. Ivanov 931dd8
+#ifndef arch_nmi_enter
Ivan T. Ivanov 931dd8
+#define arch_nmi_enter()	do { } while (0)
Ivan T. Ivanov 931dd8
+#define arch_nmi_exit()		do { } while (0)
Ivan T. Ivanov 931dd8
+#endif
Ivan T. Ivanov 931dd8
+
Ivan T. Ivanov 931dd8
 #define nmi_enter()						\
Ivan T. Ivanov 931dd8
 	do {							\
Ivan T. Ivanov 931dd8
+		arch_nmi_enter();				\
Ivan T. Ivanov 931dd8
 		printk_nmi_enter();				\
Ivan T. Ivanov 931dd8
 		lockdep_off();					\
Ivan T. Ivanov 931dd8
 		ftrace_nmi_enter();				\
Ivan T. Ivanov 931dd8
@@ -79,6 +85,7 @@ extern void irq_exit(void);
Ivan T. Ivanov 931dd8
 		ftrace_nmi_exit();				\
Ivan T. Ivanov 931dd8
 		lockdep_on();					\
Ivan T. Ivanov 931dd8
 		printk_nmi_exit();				\
Ivan T. Ivanov 931dd8
+		arch_nmi_exit();				\
Ivan T. Ivanov 931dd8
 	} while (0)
Ivan T. Ivanov 931dd8
 
Ivan T. Ivanov 931dd8
 #endif /* LINUX_HARDIRQ_H */