162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2012 ARM Ltd.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#ifndef __ASM_HARDIRQ_H
662306a36Sopenharmony_ci#define __ASM_HARDIRQ_H
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/cache.h>
962306a36Sopenharmony_ci#include <linux/percpu.h>
1062306a36Sopenharmony_ci#include <linux/threads.h>
1162306a36Sopenharmony_ci#include <asm/barrier.h>
1262306a36Sopenharmony_ci#include <asm/irq.h>
1362306a36Sopenharmony_ci#include <asm/kvm_arm.h>
1462306a36Sopenharmony_ci#include <asm/sysreg.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define ack_bad_irq ack_bad_irq
1762306a36Sopenharmony_ci#include <asm-generic/hardirq.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define __ARCH_IRQ_EXIT_IRQS_DISABLED	1
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistruct nmi_ctx {
2262306a36Sopenharmony_ci	u64 hcr;
2362306a36Sopenharmony_ci	unsigned int cnt;
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ciDECLARE_PER_CPU(struct nmi_ctx, nmi_contexts);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define arch_nmi_enter()						\
2962306a36Sopenharmony_cido {									\
3062306a36Sopenharmony_ci	struct nmi_ctx *___ctx;						\
3162306a36Sopenharmony_ci	u64 ___hcr;							\
3262306a36Sopenharmony_ci									\
3362306a36Sopenharmony_ci	if (!is_kernel_in_hyp_mode())					\
3462306a36Sopenharmony_ci		break;							\
3562306a36Sopenharmony_ci									\
3662306a36Sopenharmony_ci	___ctx = this_cpu_ptr(&nmi_contexts);				\
3762306a36Sopenharmony_ci	if (___ctx->cnt) {						\
3862306a36Sopenharmony_ci		___ctx->cnt++;						\
3962306a36Sopenharmony_ci		break;							\
4062306a36Sopenharmony_ci	}								\
4162306a36Sopenharmony_ci									\
4262306a36Sopenharmony_ci	___hcr = read_sysreg(hcr_el2);					\
4362306a36Sopenharmony_ci	if (!(___hcr & HCR_TGE)) {					\
4462306a36Sopenharmony_ci		write_sysreg(___hcr | HCR_TGE, hcr_el2);		\
4562306a36Sopenharmony_ci		isb();							\
4662306a36Sopenharmony_ci	}								\
4762306a36Sopenharmony_ci	/*								\
4862306a36Sopenharmony_ci	 * Make sure the sysreg write is performed before ___ctx->cnt	\
4962306a36Sopenharmony_ci	 * is set to 1. NMIs that see cnt == 1 will rely on us.		\
5062306a36Sopenharmony_ci	 */								\
5162306a36Sopenharmony_ci	barrier();							\
5262306a36Sopenharmony_ci	___ctx->cnt = 1;                                                \
5362306a36Sopenharmony_ci	/*								\
5462306a36Sopenharmony_ci	 * Make sure ___ctx->cnt is set before we save ___hcr. We	\
5562306a36Sopenharmony_ci	 * don't want ___ctx->hcr to be overwritten.			\
5662306a36Sopenharmony_ci	 */								\
5762306a36Sopenharmony_ci	barrier();							\
5862306a36Sopenharmony_ci	___ctx->hcr = ___hcr;						\
5962306a36Sopenharmony_ci} while (0)
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define arch_nmi_exit()							\
6262306a36Sopenharmony_cido {									\
6362306a36Sopenharmony_ci	struct nmi_ctx *___ctx;						\
6462306a36Sopenharmony_ci	u64 ___hcr;							\
6562306a36Sopenharmony_ci									\
6662306a36Sopenharmony_ci	if (!is_kernel_in_hyp_mode())					\
6762306a36Sopenharmony_ci		break;							\
6862306a36Sopenharmony_ci									\
6962306a36Sopenharmony_ci	___ctx = this_cpu_ptr(&nmi_contexts);				\
7062306a36Sopenharmony_ci	___hcr = ___ctx->hcr;						\
7162306a36Sopenharmony_ci	/*								\
7262306a36Sopenharmony_ci	 * Make sure we read ___ctx->hcr before we release		\
7362306a36Sopenharmony_ci	 * ___ctx->cnt as it makes ___ctx->hcr updatable again.		\
7462306a36Sopenharmony_ci	 */								\
7562306a36Sopenharmony_ci	barrier();							\
7662306a36Sopenharmony_ci	___ctx->cnt--;							\
7762306a36Sopenharmony_ci	/*								\
7862306a36Sopenharmony_ci	 * Make sure ___ctx->cnt release is visible before we		\
7962306a36Sopenharmony_ci	 * restore the sysreg. Otherwise a new NMI occurring		\
8062306a36Sopenharmony_ci	 * right after write_sysreg() can be fooled and think		\
8162306a36Sopenharmony_ci	 * we secured things for it.					\
8262306a36Sopenharmony_ci	 */								\
8362306a36Sopenharmony_ci	barrier();							\
8462306a36Sopenharmony_ci	if (!___ctx->cnt && !(___hcr & HCR_TGE))			\
8562306a36Sopenharmony_ci		write_sysreg(___hcr, hcr_el2);				\
8662306a36Sopenharmony_ci} while (0)
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic inline void ack_bad_irq(unsigned int irq)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	extern unsigned long irq_err_count;
9162306a36Sopenharmony_ci	irq_err_count++;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#endif /* __ASM_HARDIRQ_H */
95