xref: /openbmc/linux/arch/arm64/include/asm/hardirq.h (revision a531b0c2)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Copyright (C) 2012 ARM Ltd.
4  */
5 #ifndef __ASM_HARDIRQ_H
6 #define __ASM_HARDIRQ_H
7 
8 #include <linux/cache.h>
9 #include <linux/percpu.h>
10 #include <linux/threads.h>
11 #include <asm/barrier.h>
12 #include <asm/irq.h>
13 #include <asm/kvm_arm.h>
14 #include <asm/sysreg.h>
15 
16 #define ack_bad_irq ack_bad_irq
17 #include <asm-generic/hardirq.h>
18 
19 #define __ARCH_IRQ_EXIT_IRQS_DISABLED	1
20 
21 struct nmi_ctx {
22 	u64 hcr;
23 	unsigned int cnt;
24 };
25 
26 DECLARE_PER_CPU(struct nmi_ctx, nmi_contexts);
27 
28 #define arch_nmi_enter()						\
29 do {									\
30 	struct nmi_ctx *___ctx;						\
31 	u64 ___hcr;							\
32 									\
33 	if (!is_kernel_in_hyp_mode())					\
34 		break;							\
35 									\
36 	___ctx = this_cpu_ptr(&nmi_contexts);				\
37 	if (___ctx->cnt) {						\
38 		___ctx->cnt++;						\
39 		break;							\
40 	}								\
41 									\
42 	___hcr = read_sysreg(hcr_el2);					\
43 	if (!(___hcr & HCR_TGE)) {					\
44 		write_sysreg(___hcr | HCR_TGE, hcr_el2);		\
45 		isb();							\
46 	}								\
47 	/*								\
48 	 * Make sure the sysreg write is performed before ___ctx->cnt	\
49 	 * is set to 1. NMIs that see cnt == 1 will rely on us.		\
50 	 */								\
51 	barrier();							\
52 	___ctx->cnt = 1;                                                \
53 	/*								\
54 	 * Make sure ___ctx->cnt is set before we save ___hcr. We	\
55 	 * don't want ___ctx->hcr to be overwritten.			\
56 	 */								\
57 	barrier();							\
58 	___ctx->hcr = ___hcr;						\
59 } while (0)
60 
61 #define arch_nmi_exit()							\
62 do {									\
63 	struct nmi_ctx *___ctx;						\
64 	u64 ___hcr;							\
65 									\
66 	if (!is_kernel_in_hyp_mode())					\
67 		break;							\
68 									\
69 	___ctx = this_cpu_ptr(&nmi_contexts);				\
70 	___hcr = ___ctx->hcr;						\
71 	/*								\
72 	 * Make sure we read ___ctx->hcr before we release		\
73 	 * ___ctx->cnt as it makes ___ctx->hcr updatable again.		\
74 	 */								\
75 	barrier();							\
76 	___ctx->cnt--;							\
77 	/*								\
78 	 * Make sure ___ctx->cnt release is visible before we		\
79 	 * restore the sysreg. Otherwise a new NMI occurring		\
80 	 * right after write_sysreg() can be fooled and think		\
81 	 * we secured things for it.					\
82 	 */								\
83 	barrier();							\
84 	if (!___ctx->cnt && !(___hcr & HCR_TGE))			\
85 		write_sysreg(___hcr, hcr_el2);				\
86 } while (0)
87 
88 static inline void ack_bad_irq(unsigned int irq)
89 {
90 	extern unsigned long irq_err_count;
91 	irq_err_count++;
92 }
93 
94 #endif /* __ASM_HARDIRQ_H */
95