From: Xiongfeng Wang <wangxiongfeng2(a)huawei.com>
commit 1e86c400c84e9c8ecf286acedab825141805e94c openEuler-1.0.
When we panic in hardlockup, the secure timer interrupt remains activate
because firmware clear eoi after dispatch is completed. This will cause
arm_arch_timer interrupt failed to trigger in the second kernel.
This patch add a new SMC helper to clear eoi of a certain interrupt and
clear eoi of the secure timer before booting the second kernel.
Signed-off-by: Xiongfeng Wang <wangxiongfeng2(a)huawei.com>
Reviewed-by: Hanjun Guo <guohanjun(a)huawei.com>
Signed-off-by: Yang Yingliang <yangyingliang(a)huawei.com>
Signed-off-by: Xin Hao <haoxing990(a)gmail.com>
---
arch/arm64/kernel/machine_kexec.c | 10 ++++++++++
arch/arm64/kernel/watchdog_sdei.c | 6 ++++++
drivers/firmware/arm_sdei.c | 6 ++++++
include/linux/arm_sdei.h | 1 +
include/linux/nmi.h | 6 ++++++
include/uapi/linux/arm_sdei.h | 1 +
6 files changed, 30 insertions(+)
diff --git a/arch/arm64/kernel/machine_kexec.c b/arch/arm64/kernel/machine_kexec.c
index 922add8adb74..3c0e9383d7e0 100644
--- a/arch/arm64/kernel/machine_kexec.c
+++ b/arch/arm64/kernel/machine_kexec.c
@@ -13,6 +13,7 @@
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/kexec.h>
+#include <linux/nmi.h>
#include <linux/page-flags.h>
#include <linux/smp.h>
@@ -260,6 +261,15 @@ void machine_crash_shutdown(struct pt_regs *regs)
/* shutdown non-crashing cpus */
crash_smp_send_stop();
+ /*
+ * when we panic in hardlockup detected by sdei_watchdog, the secure
+ * timer interrupt remains activate here because firmware clear eoi
+ * after dispatch is completed. This will cause arm_arch_timer
+ * interrupt failed to trigger in the second kernel. So we clear eoi
+ * of the secure timer before booting the second kernel.
+ */
+ sdei_watchdog_clear_eoi();
+
/* for crashing cpu */
crash_save_cpu(regs, smp_processor_id());
machine_kexec_mask_interrupts();
diff --git a/arch/arm64/kernel/watchdog_sdei.c b/arch/arm64/kernel/watchdog_sdei.c
index e36c4d398893..99ab9bdfdee6 100644
--- a/arch/arm64/kernel/watchdog_sdei.c
+++ b/arch/arm64/kernel/watchdog_sdei.c
@@ -80,6 +80,12 @@ static int __init disable_sdei_nmi_watchdog_setup(char *str)
}
__setup("disable_sdei_nmi_watchdog", disable_sdei_nmi_watchdog_setup);
+void sdei_watchdog_clear_eoi(void)
+{
+ if (sdei_watchdog_registered)
+ sdei_api_clear_eoi(SDEI_NMI_WATCHDOG_HWIRQ);
+}
+
int __init watchdog_nmi_probe(void)
{
int ret;
diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index 64c5278b35bf..629464dd9514 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -201,6 +201,12 @@ int sdei_api_event_interrupt_bind(int hwirq)
return (int)event_number;
}
+int sdei_api_clear_eoi(int hwirq)
+{
+ return invoke_sdei_fn(SDEI_1_0_FN_SDEI_CLEAR_EOI, hwirq, 0, 0, 0, 0,
+ NULL);
+}
+
static int sdei_api_event_get_info(u32 event, u32 info, u64 *result)
{
return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_GET_INFO, event, info, 0,
diff --git a/include/linux/arm_sdei.h b/include/linux/arm_sdei.h
index befebeba84f1..de49a289c5f3 100644
--- a/include/linux/arm_sdei.h
+++ b/include/linux/arm_sdei.h
@@ -45,6 +45,7 @@ int sdei_event_disable(u32 event_num);
int sdei_api_event_interrupt_bind(int hwirq);
int sdei_api_event_disable(u32 event_num);
int sdei_api_event_enable(u32 event_num);
+int sdei_api_clear_eoi(int hwirq);
/* GHES register/unregister helpers */
int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb,
diff --git a/include/linux/nmi.h b/include/linux/nmi.h
index 4ffbc78a0dd4..79238eb5cbb0 100644
--- a/include/linux/nmi.h
+++ b/include/linux/nmi.h
@@ -218,4 +218,10 @@ extern int proc_watchdog_cpumask(struct ctl_table *, int,
#include <asm/nmi.h>
#endif
+#ifdef CONFIG_SDEI_WATCHDOG
+void sdei_watchdog_clear_eoi(void);
+#else
+static inline void sdei_watchdog_clear_eoi(void) { }
+#endif
+
#endif
diff --git a/include/uapi/linux/arm_sdei.h b/include/uapi/linux/arm_sdei.h
index af0630ba5437..9a810e7883ed 100644
--- a/include/uapi/linux/arm_sdei.h
+++ b/include/uapi/linux/arm_sdei.h
@@ -24,6 +24,7 @@
#define SDEI_1_0_FN_SDEI_INTERRUPT_RELEASE SDEI_1_0_FN(0x0E)
#define SDEI_1_0_FN_SDEI_PRIVATE_RESET SDEI_1_0_FN(0x11)
#define SDEI_1_0_FN_SDEI_SHARED_RESET SDEI_1_0_FN(0x12)
+#define SDEI_1_0_FN_SDEI_CLEAR_EOI SDEI_1_0_FN(0xD0)
#define SDEI_VERSION_MAJOR_SHIFT 48
#define SDEI_VERSION_MAJOR_MASK 0x7fff
--
2.31.0