Skip to content

Commit 5870970

Browse files
Julien Thierryctmarinas
authored andcommitted
arm64: Fix HCR.TGE status for NMI contexts
When using VHE, the host needs to clear HCR_EL2.TGE bit in order to interact with guest TLBs, switching from EL2&0 translation regime to EL1&0. However, some non-maskable asynchronous event could happen while TGE is cleared like SDEI. Because of this address translation operations relying on EL2&0 translation regime could fail (tlb invalidation, userspace access, ...). Fix this by properly setting HCR_EL2.TGE when entering NMI context and clear it if necessary when returning to the interrupted context. Signed-off-by: Julien Thierry <[email protected]> Suggested-by: Marc Zyngier <[email protected]> Reviewed-by: Marc Zyngier <[email protected]> Reviewed-by: James Morse <[email protected]> Cc: Arnd Bergmann <[email protected]> Cc: Will Deacon <[email protected]> Cc: Marc Zyngier <[email protected]> Cc: James Morse <[email protected]> Cc: [email protected] Cc: [email protected] Signed-off-by: Catalin Marinas <[email protected]>
1 parent e26a433 commit 5870970

File tree

3 files changed

+41
-0
lines changed

3 files changed

+41
-0
lines changed

arch/arm64/include/asm/hardirq.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@
1717
#define __ASM_HARDIRQ_H
1818

1919
#include <linux/cache.h>
20+
#include <linux/percpu.h>
2021
#include <linux/threads.h>
22+
#include <asm/barrier.h>
2123
#include <asm/irq.h>
24+
#include <asm/kvm_arm.h>
25+
#include <asm/sysreg.h>
2226

2327
#define NR_IPI 7
2428

@@ -37,6 +41,33 @@ u64 smp_irq_stat_cpu(unsigned int cpu);
3741

3842
#define __ARCH_IRQ_EXIT_IRQS_DISABLED 1
3943

44+
struct nmi_ctx {
45+
u64 hcr;
46+
};
47+
48+
DECLARE_PER_CPU(struct nmi_ctx, nmi_contexts);
49+
50+
#define arch_nmi_enter() \
51+
do { \
52+
if (is_kernel_in_hyp_mode()) { \
53+
struct nmi_ctx *nmi_ctx = this_cpu_ptr(&nmi_contexts); \
54+
nmi_ctx->hcr = read_sysreg(hcr_el2); \
55+
if (!(nmi_ctx->hcr & HCR_TGE)) { \
56+
write_sysreg(nmi_ctx->hcr | HCR_TGE, hcr_el2); \
57+
isb(); \
58+
} \
59+
} \
60+
} while (0)
61+
62+
#define arch_nmi_exit() \
63+
do { \
64+
if (is_kernel_in_hyp_mode()) { \
65+
struct nmi_ctx *nmi_ctx = this_cpu_ptr(&nmi_contexts); \
66+
if (!(nmi_ctx->hcr & HCR_TGE)) \
67+
write_sysreg(nmi_ctx->hcr, hcr_el2); \
68+
} \
69+
} while (0)
70+
4071
static inline void ack_bad_irq(unsigned int irq)
4172
{
4273
extern unsigned long irq_err_count;

arch/arm64/kernel/irq.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333

3434
unsigned long irq_err_count;
3535

36+
/* Only access this in an NMI enter/exit */
37+
DEFINE_PER_CPU(struct nmi_ctx, nmi_contexts);
38+
3639
DEFINE_PER_CPU(unsigned long *, irq_stack_ptr);
3740

3841
int arch_show_interrupts(struct seq_file *p, int prec)

include/linux/hardirq.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,14 @@ extern void irq_enter(void);
6060
*/
6161
extern void irq_exit(void);
6262

63+
#ifndef arch_nmi_enter
64+
#define arch_nmi_enter() do { } while (0)
65+
#define arch_nmi_exit() do { } while (0)
66+
#endif
67+
6368
#define nmi_enter() \
6469
do { \
70+
arch_nmi_enter(); \
6571
printk_nmi_enter(); \
6672
lockdep_off(); \
6773
ftrace_nmi_enter(); \
@@ -80,6 +86,7 @@ extern void irq_exit(void);
8086
ftrace_nmi_exit(); \
8187
lockdep_on(); \
8288
printk_nmi_exit(); \
89+
arch_nmi_exit(); \
8390
} while (0)
8491

8592
#endif /* LINUX_HARDIRQ_H */

0 commit comments

Comments
 (0)