From: “z00447436” zouqian4@huawei.com
Some use-cases, such as system management, require the ability to generate a non-maskable event to the OS to request the OS kernel to perform a diagnostic dump and reset the system. Arm Generic Diagnostic Dump and Reset device enables a maintainer to request OS to perform a diagnostic dump and reset a system via SDEI event or an interrupt. This patch implements SDEI path and discards the interrupted context before proceeding to the crash kernel.
D Scott Phillips (1): arm64: sdei: abort running SDEI handlers during crash
Ilkka Koskinen (3): ACPI: tables: Add AGDI to the list of known table signatures ACPI: AGDI: Add driver for Arm Generic Diagnostic Dump and Reset device ACPICA: iASL: Add suppport for AGDI table
Shuai Xue (1): ACPI: APEI: explicit init of HEST and GHES in apci_init()
arch/arm64/include/asm/sdei.h | 6 ++ arch/arm64/kernel/entry.S | 27 +++++++- arch/arm64/kernel/sdei.c | 3 + arch/arm64/kernel/smp.c | 8 +-- drivers/acpi/apei/ghes.c | 19 +++--- drivers/acpi/arm64/Kconfig | 10 +++ drivers/acpi/arm64/Makefile | 1 + drivers/acpi/arm64/agdi.c | 116 ++++++++++++++++++++++++++++++++++ drivers/acpi/bus.c | 4 ++ drivers/acpi/pci_root.c | 3 - drivers/acpi/tables.c | 2 +- drivers/firmware/Kconfig | 1 + drivers/firmware/arm_sdei.c | 32 ++++++---- include/acpi/actbl2.h | 20 ++++++ include/acpi/apei.h | 4 +- include/linux/acpi_agdi.h | 13 ++++ include/linux/arm_sdei.h | 4 ++ 17 files changed, 240 insertions(+), 33 deletions(-) create mode 100644 drivers/acpi/arm64/agdi.c create mode 100644 include/linux/acpi_agdi.h
From: Shuai Xue xueshuai@linux.alibaba.com
mainline inclusion from mainline-v5.18-rc1 commit dc4e8c07e9e2f69387579c49caca26ba239f7270 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8DQUX CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
apci_init()
From commit e147133a42cb ("ACPI / APEI: Make hest.c manage the estatus memory pool") was merged, ghes_init() relies on acpi_hest_init() to manage the estatus memory pool. On the other hand, ghes_init() relies on sdei_init() to detect the SDEI version and (un)register events. The dependencies are as follows:
ghes_init() => acpi_hest_init() => acpi_bus_init() => acpi_init() ghes_init() => sdei_init()
HEST is not PCI-specific and initcall ordering is implicit and not well-defined within a level.
Based on above, remove acpi_hest_init() from acpi_pci_root_init() and convert ghes_init() and sdei_init() from initcalls to explicit calls in the following order:
acpi_hest_init() ghes_init() sdei_init()
Signed-off-by: Shuai Xue xueshuai@linux.alibaba.com Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com --- drivers/acpi/apei/ghes.c | 19 ++++++++----------- drivers/acpi/bus.c | 2 ++ drivers/acpi/pci_root.c | 3 --- drivers/firmware/Kconfig | 1 + drivers/firmware/arm_sdei.c | 13 ++----------- include/acpi/apei.h | 4 +++- include/linux/arm_sdei.h | 2 ++ 7 files changed, 18 insertions(+), 26 deletions(-)
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 650a8feda..6535b444a 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -1473,33 +1473,35 @@ static struct platform_driver ghes_platform_driver = { .remove = ghes_remove, };
-static int __init ghes_init(void) +void __init ghes_init(void) { int rc;
+ sdei_init(); + if (acpi_disabled) - return -ENODEV; + return;
switch (hest_disable) { case HEST_NOT_FOUND: - return -ENODEV; + return; case HEST_DISABLED: pr_info(GHES_PFX "HEST is not enabled!\n"); - return -EINVAL; + return; default: break; }
if (ghes_disable) { pr_info(GHES_PFX "GHES is not enabled!\n"); - return -EINVAL; + return; }
ghes_nmi_init_cxt();
rc = platform_driver_register(&ghes_platform_driver); if (rc) - goto err; + return;
rc = apei_osc_setup(); if (rc == 0 && osc_sb_apei_support_acked) @@ -1510,9 +1512,4 @@ static int __init ghes_init(void) pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n"); else pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n"); - - return 0; -err: - return rc; } -device_initcall(ghes_init); diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 50a65ff16..d8086d49c 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1286,6 +1286,8 @@ static int __init acpi_init(void)
pci_mmcfg_late_init(); acpi_iort_init(); + acpi_hest_init(); + ghes_init(); acpi_scan_init(); acpi_ec_init(); acpi_debugfs_init(); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index c12b5fb3e..d972ea057 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -20,8 +20,6 @@ #include <linux/slab.h> #include <linux/dmi.h> #include <linux/platform_data/x86/apple.h> -#include <acpi/apei.h> /* for acpi_hest_init() */ - #include "internal.h"
#define ACPI_PCI_ROOT_CLASS "pci_bridge" @@ -950,7 +948,6 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
void __init acpi_pci_root_init(void) { - acpi_hest_init(); if (acpi_pci_disabled) return;
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index a34d4bfb2..a3996a6fc 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -72,6 +72,7 @@ config ARM_SCPI_POWER_DOMAIN config ARM_SDE_INTERFACE bool "ARM Software Delegated Exception Interface (SDEI)" depends on ARM64 + depends on ACPI_APEI_GHES help The Software Delegated Exception Interface (SDEI) is an ARM standard for registering callbacks from the platform firmware diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index 8e9d565c2..0459e1d44 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -1081,14 +1081,14 @@ static bool __init sdei_present_acpi(void) return true; }
-static int __init sdei_init(void) +void __init sdei_init(void) { struct platform_device *pdev; int ret;
ret = platform_driver_register(&sdei_driver); if (ret || !sdei_present_acpi()) - return ret; + return;
pdev = platform_device_register_simple(sdei_driver.driver.name, 0, NULL, 0); @@ -1098,17 +1098,8 @@ static int __init sdei_init(void) pr_info("Failed to register ACPI:SDEI platform device %d\n", ret); } - - return ret; }
-/* - * On an ACPI system SDEI needs to be ready before HEST:GHES tries to register - * its events. ACPI is initialised from a subsys_initcall(), GHES is initialised - * by device_initcall(). We want to be called in the middle. - */ -subsys_initcall_sync(sdei_init); - int sdei_event_handler(struct pt_regs *regs, struct sdei_registered_event *arg) { diff --git a/include/acpi/apei.h b/include/acpi/apei.h index 680f80960..a6ac2e8b7 100644 --- a/include/acpi/apei.h +++ b/include/acpi/apei.h @@ -27,14 +27,16 @@ extern int hest_disable; extern int erst_disable; #ifdef CONFIG_ACPI_APEI_GHES extern bool ghes_disable; +void __init ghes_init(void); #else #define ghes_disable 1 +static inline void ghes_init(void) { } #endif
#ifdef CONFIG_ACPI_APEI void __init acpi_hest_init(void); #else -static inline void acpi_hest_init(void) { return; } +static inline void acpi_hest_init(void) { } #endif
typedef int (*apei_hest_func_t)(struct acpi_hest_header *hest_hdr, void *data); diff --git a/include/linux/arm_sdei.h b/include/linux/arm_sdei.h index f3027342c..b1233196c 100644 --- a/include/linux/arm_sdei.h +++ b/include/linux/arm_sdei.h @@ -51,9 +51,11 @@ int sdei_unregister_ghes(struct ghes *ghes); /* For use by arch code when CPU hotplug notifiers are not appropriate. */ int sdei_mask_local_cpu(void); int sdei_unmask_local_cpu(void); +void __init sdei_init(void); #else static inline int sdei_mask_local_cpu(void) { return 0; } static inline int sdei_unmask_local_cpu(void) { return 0; } +static inline void sdei_init(void) { } #endif /* CONFIG_ARM_SDE_INTERFACE */
From: Ilkka Koskinen ilkka@os.amperecomputing.com
mainline inclusion from mainline-v5.18-rc1 commit e86801b0ff1c5c6d1f78232f7e3b52c0b0631560 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8DQUX CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
Add AGDI to the list of known ACPI table signatures to allow the kernel to recognize it when upgrading tables via initrd.
Signed-off-by: Ilkka Koskinen ilkka@os.amperecomputing.com Reviewed-by: Russell King (Oracle) rmk+kernel@armlinux.org.uk Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com --- drivers/acpi/tables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 5943ae4f7..3635c7be6 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -509,7 +509,7 @@ static const char table_sigs[][ACPI_NAMESEG_SIZE] __initconst = { ACPI_SIG_WDDT, ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT, ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, ACPI_SIG_IORT, ACPI_SIG_NFIT, ACPI_SIG_HMAT, ACPI_SIG_PPTT, - ACPI_SIG_NHLT }; + ACPI_SIG_NHLT, ACPI_SIG_AGDI };
#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)
From: Ilkka Koskinen ilkka@os.amperecomputing.com
mainline inclusion from mainline-v5.18-rc1 commit a2a591fb76e6f5461dfd04715b69c317e50c43a5 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8DQUX CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... ----------------------------------------------------------------------
ACPI for Arm Components 1.1 Platform Design Document v1.1 [0] specifices Arm Generic Diagnostic Device Interface (AGDI). It allows an admin to issue diagnostic dump and reset via an SDEI event or an interrupt. This patch implements SDEI path.
[0] https://developer.arm.com/documentation/den0093/latest/
Signed-off-by: Ilkka Koskinen ilkka@os.amperecomputing.com Reviewed-by: Russell King (Oracle) rmk+kernel@armlinux.org.uk Acked-by: Lorenzo Pieralisi lorenzo.pieralisi@arm.com Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com --- drivers/acpi/arm64/Kconfig | 10 ++++ drivers/acpi/arm64/Makefile | 1 + drivers/acpi/arm64/agdi.c | 116 ++++++++++++++++++++++++++++++++++++ drivers/acpi/bus.c | 2 + include/linux/acpi_agdi.h | 13 ++++ 5 files changed, 142 insertions(+) create mode 100644 drivers/acpi/arm64/agdi.c create mode 100644 include/linux/acpi_agdi.h
diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig index 664d2ca05..d6f98f89c 100644 --- a/drivers/acpi/arm64/Kconfig +++ b/drivers/acpi/arm64/Kconfig @@ -11,3 +11,13 @@ config ACPI_GTDT
config ACPI_MPAM bool + +config ACPI_AGDI + bool "Arm Generic Diagnostic Dump and Reset Device Interface" + depends on ARM_SDE_INTERFACE + help + Arm Generic Diagnostic Dump and Reset Device Interface (AGDI) is + a standard that enables issuing a non-maskable diagnostic dump and + reset command. + + If set, the kernel parses AGDI table and listens for the command. diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile index 2bae08207..7d3c52952 100644 --- a/drivers/acpi/arm64/Makefile +++ b/drivers/acpi/arm64/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_ACPI_AGDI) += agdi.o obj-$(CONFIG_ACPI_IORT) += iort.o obj-$(CONFIG_ACPI_GTDT) += gtdt.o obj-$(CONFIG_ACPI_MPAM) += mpam.o mpam_v2.o diff --git a/drivers/acpi/arm64/agdi.c b/drivers/acpi/arm64/agdi.c new file mode 100644 index 000000000..4df337d54 --- /dev/null +++ b/drivers/acpi/arm64/agdi.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * This file implements handling of + * Arm Generic Diagnostic Dump and Reset Interface table (AGDI) + * + * Copyright (c) 2022, Ampere Computing LLC + */ + +#define pr_fmt(fmt) "ACPI: AGDI: " fmt + +#include <linux/acpi.h> +#include <linux/arm_sdei.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> + +struct agdi_data { + int sdei_event; +}; + +static int agdi_sdei_handler(u32 sdei_event, struct pt_regs *regs, void *arg) +{ + nmi_panic(regs, "Arm Generic Diagnostic Dump and Reset SDEI event issued"); + return 0; +} + +static int agdi_sdei_probe(struct platform_device *pdev, + struct agdi_data *adata) +{ + int err; + + err = sdei_event_register(adata->sdei_event, agdi_sdei_handler, pdev); + if (err) { + dev_err(&pdev->dev, "Failed to register for SDEI event %d", + adata->sdei_event); + return err; + } + + err = sdei_event_enable(adata->sdei_event); + if (err) { + sdei_event_unregister(adata->sdei_event); + dev_err(&pdev->dev, "Failed to enable event %d\n", + adata->sdei_event); + return err; + } + + return 0; +} + +static int agdi_probe(struct platform_device *pdev) +{ + struct agdi_data *adata = dev_get_platdata(&pdev->dev); + + if (!adata) + return -EINVAL; + + return agdi_sdei_probe(pdev, adata); +} + +static int agdi_remove(struct platform_device *pdev) +{ + struct agdi_data *adata = dev_get_platdata(&pdev->dev); + int err, i; + + err = sdei_event_disable(adata->sdei_event); + if (err) + return err; + + for (i = 0; i < 3; i++) { + err = sdei_event_unregister(adata->sdei_event); + if (err != -EINPROGRESS) + break; + + schedule(); + } + + return err; +} + +static struct platform_driver agdi_driver = { + .driver = { + .name = "agdi", + }, + .probe = agdi_probe, + .remove = agdi_remove, +}; + +void __init acpi_agdi_init(void) +{ + struct acpi_table_agdi *agdi_table; + struct agdi_data pdata; + struct platform_device *pdev; + acpi_status status; + + status = acpi_get_table(ACPI_SIG_AGDI, 0, + (struct acpi_table_header **) &agdi_table); + if (ACPI_FAILURE(status)) + return; + + if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE) { + pr_warn("Interrupt signaling is not supported"); + goto err_put_table; + } + + pdata.sdei_event = agdi_table->sdei_event; + + pdev = platform_device_register_data(NULL, "agdi", 0, &pdata, sizeof(pdata)); + if (IS_ERR(pdev)) + goto err_put_table; + + if (platform_driver_register(&agdi_driver)) + platform_device_unregister(pdev); + +err_put_table: + acpi_put_table((struct acpi_table_header *)agdi_table); +} diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index d8086d49c..bb213ade9 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -24,6 +24,7 @@ #include <asm/mpspec.h> #include <linux/dmi.h> #endif +#include <linux/acpi_agdi.h> #include <linux/acpi_iort.h> #include <linux/pci.h> #include <acpi/apei.h> @@ -1295,6 +1296,7 @@ static int __init acpi_init(void) acpi_wakeup_device_init(); acpi_debugger_init(); acpi_setup_sb_notify_handler(); + acpi_agdi_init(); return 0; }
diff --git a/include/linux/acpi_agdi.h b/include/linux/acpi_agdi.h new file mode 100644 index 000000000..f477f0b45 --- /dev/null +++ b/include/linux/acpi_agdi.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __ACPI_AGDI_H__ +#define __ACPI_AGDI_H__ + +#include <linux/acpi.h> + +#ifdef CONFIG_ACPI_AGDI +void __init acpi_agdi_init(void); +#else +static inline void acpi_agdi_init(void) {} +#endif +#endif /* __ACPI_AGDI_H__ */
From: Ilkka Koskinen ilkka@os.amperecomputing.com
mainline inclusion from mainline-v5.17-rc1 commit 5579649e7eb756a4e3d5784b6958374e5bfc41de category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8DQUX CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
ACPICA commit cf36a6d658ca5aa8c329c2edfc3322c095ffd844
Add support for Arm Generic Diagnostic Dump and Reset Interface, which is described by "ACPI for Arm Components 1.1 Platform Design Document" ARM DEN0093.
Add the necessary types in the ACPICA header files and support for compiling and decompiling the table.
Link: https://github.com/acpica/acpica/commit/cf36a6d6 Signed-off-by: Ilkka Koskinen ilkka@os.amperecomputing.com Signed-off-by: Bob Moore robert.moore@intel.com Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com --- include/acpi/actbl2.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 2fe351437..aa4568c4f 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -24,6 +24,7 @@ * file. Useful because they make it more difficult to inadvertently type in * the wrong signature. */ +#define ACPI_SIG_AGDI "AGDI" /* Arm Generic Diagnostic Dump and Reset Device Interface */ #define ACPI_SIG_IORT "IORT" /* IO Remapping Table */ #define ACPI_SIG_IVRS "IVRS" /* I/O Virtualization Reporting Structure */ #define ACPI_SIG_LPIT "LPIT" /* Low Power Idle Table */ @@ -65,6 +66,25 @@ * See http://stackoverflow.com/a/1053662/41661 */
+/******************************************************************************* + * AGDI - Arm Generic Diagnostic Dump and Reset Device Interface + * + * Conforms to "ACPI for Arm Components 1.1, Platform Design Document" + * ARM DEN0093 v1.1 + * + ******************************************************************************/ +struct acpi_table_agdi { + struct acpi_table_header header; /* Common ACPI table header */ + u8 flags; + u8 reserved[3]; + u32 sdei_event; + u32 gsiv; +}; + +/* Mask for Flags field above */ + +#define ACPI_AGDI_SIGNALING_MODE (1) + /******************************************************************************* * * IORT - IO Remapping Table
From: D Scott Phillips scott@os.amperecomputing.com
mainline inclusion from mainline-v6.6-rc1 commit 5cd474e57368f0957c343bb21e309cf82826b1ef category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8DQUX CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
Interrupts are blocked in SDEI context, per the SDEI spec: "The client interrupts cannot preempt the event handler." If we crashed in the SDEI handler-running context (as with ACPI's AGDI) then we need to clean up the SDEI state before proceeding to the crash kernel so that the crash kernel can have working interrupts.
Track the active SDEI handler per-cpu so that we can COMPLETE_AND_RESUME the handler, discarding the interrupted context.
Fixes: f5df26961853 ("arm64: kernel: Add arch-specific SDEI entry code and CPU masking") Signed-off-by: D Scott Phillips scott@os.amperecomputing.com Cc: stable@vger.kernel.org Reviewed-by: James Morse james.morse@arm.com Tested-by: Mihai Carabas mihai.carabas@oracle.com Link: https://lore.kernel.org/r/20230627002939.2758-1-scott@os.amperecomputing.com Signed-off-by: Will Deacon will@kernel.org --- arch/arm64/include/asm/sdei.h | 6 ++++++ arch/arm64/kernel/entry.S | 27 +++++++++++++++++++++++++-- arch/arm64/kernel/sdei.c | 3 +++ arch/arm64/kernel/smp.c | 8 ++++---- drivers/firmware/arm_sdei.c | 19 +++++++++++++++++++ include/linux/arm_sdei.h | 2 ++ 6 files changed, 59 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/include/asm/sdei.h b/arch/arm64/include/asm/sdei.h index 63e0b92a5..5882c0e29 100644 --- a/arch/arm64/include/asm/sdei.h +++ b/arch/arm64/include/asm/sdei.h @@ -17,6 +17,9 @@
#include <asm/virt.h>
+DECLARE_PER_CPU(struct sdei_registered_event *, sdei_active_normal_event); +DECLARE_PER_CPU(struct sdei_registered_event *, sdei_active_critical_event); + extern unsigned long sdei_exit_mode;
/* Software Delegated Exception entry point from firmware*/ @@ -29,6 +32,9 @@ asmlinkage void __sdei_asm_entry_trampoline(unsigned long event_num, unsigned long pc, unsigned long pstate);
+/* Abort a running handler. Context is discarded. */ +void __sdei_handler_abort(void); + /* * The above entry point does the minimum to call C code. This function does * anything else, before calling the driver. diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 64145bfab..34c58ad95 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -1134,9 +1134,13 @@ SYM_CODE_START(__sdei_asm_handler)
mov x19, x1
-#if defined(CONFIG_VMAP_STACK) || defined(CONFIG_SHADOW_CALL_STACK) + /* Store the registered-event for crash_smp_send_stop() */ ldrb w4, [x19, #SDEI_EVENT_PRIORITY] -#endif + cbnz w4, 1f + adr_this_cpu dst=x5, sym=sdei_active_normal_event, tmp=x6 + b 2f +1: adr_this_cpu dst=x5, sym=sdei_active_critical_event, tmp=x6 +2: str x19, [x5]
#ifdef CONFIG_VMAP_STACK /* @@ -1201,6 +1205,14 @@ SYM_CODE_START(__sdei_asm_handler)
ldr_l x2, sdei_exit_mode
+ /* Clear the registered-event seen by crash_smp_send_stop() */ + ldrb w3, [x4, #SDEI_EVENT_PRIORITY] + cbnz w3, 1f + adr_this_cpu dst=x5, sym=sdei_active_normal_event, tmp=x6 + b 2f +1: adr_this_cpu dst=x5, sym=sdei_active_critical_event, tmp=x6 +2: str xzr, [x5] + alternative_if_not ARM64_UNMAP_KERNEL_AT_EL0 sdei_handler_exit exit_mode=x2 alternative_else_nop_endif @@ -1211,4 +1223,15 @@ alternative_else_nop_endif #endif SYM_CODE_END(__sdei_asm_handler) NOKPROBE(__sdei_asm_handler) + +SYM_CODE_START(__sdei_handler_abort) + mov_q x0, SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME + adr x1, 1f + ldr_l x2, sdei_exit_mode + sdei_handler_exit exit_mode=x2 + // exit the handler and jump to the next instruction. + // Exit will stomp x0-x17, PSTATE, ELR_ELx, and SPSR_ELx. +1: ret +SYM_CODE_END(__sdei_handler_abort) +NOKPROBE(__sdei_handler_abort) #endif /* CONFIG_ARM_SDE_INTERFACE */ diff --git a/arch/arm64/kernel/sdei.c b/arch/arm64/kernel/sdei.c index 2132bd953..6d51d873b 100644 --- a/arch/arm64/kernel/sdei.c +++ b/arch/arm64/kernel/sdei.c @@ -38,6 +38,9 @@ DEFINE_PER_CPU(unsigned long *, sdei_stack_normal_ptr); DEFINE_PER_CPU(unsigned long *, sdei_stack_critical_ptr); #endif
+DEFINE_PER_CPU(struct sdei_registered_event *, sdei_active_normal_event); +DEFINE_PER_CPU(struct sdei_registered_event *, sdei_active_critical_event); + static void _free_sdei_stack(unsigned long * __percpu *ptr, int cpu) { unsigned long *p; diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 08328ec02..23707812f 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -1138,10 +1138,8 @@ void crash_smp_send_stop(void) * If this cpu is the only one alive at this point in time, online or * not, there are no stop messages to be sent around, so just back out. */ - if (num_other_online_cpus() == 0) { - sdei_mask_local_cpu(); - return; - } + if (num_other_online_cpus() == 0) + goto skip_ipi;
cpumask_copy(&mask, cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), &mask); @@ -1160,7 +1158,9 @@ void crash_smp_send_stop(void) pr_warn("SMP: failed to stop secondary CPUs %*pbl\n", cpumask_pr_args(&mask));
+skip_ipi: sdei_mask_local_cpu(); + sdei_handler_abort(); }
bool smp_crash_stop_failed(void) diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index 0459e1d44..0f0629a94 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -1114,3 +1114,22 @@ int sdei_event_handler(struct pt_regs *regs, return err; } NOKPROBE_SYMBOL(sdei_event_handler); + +void sdei_handler_abort(void) +{ + /* + * If the crash happened in an SDEI event handler then we need to + * finish the handler with the firmware so that we can have working + * interrupts in the crash kernel. + */ + if (__this_cpu_read(sdei_active_critical_event)) { + pr_warn("still in SDEI critical event context, attempting to finish handler.\n"); + __sdei_handler_abort(); + __this_cpu_write(sdei_active_critical_event, NULL); + } + if (__this_cpu_read(sdei_active_normal_event)) { + pr_warn("still in SDEI normal event context, attempting to finish handler.\n"); + __sdei_handler_abort(); + __this_cpu_write(sdei_active_normal_event, NULL); + } +} diff --git a/include/linux/arm_sdei.h b/include/linux/arm_sdei.h index b1233196c..28e247dd5 100644 --- a/include/linux/arm_sdei.h +++ b/include/linux/arm_sdei.h @@ -52,10 +52,12 @@ int sdei_unregister_ghes(struct ghes *ghes); int sdei_mask_local_cpu(void); int sdei_unmask_local_cpu(void); void __init sdei_init(void); +void sdei_handler_abort(void); #else static inline int sdei_mask_local_cpu(void) { return 0; } static inline int sdei_unmask_local_cpu(void) { return 0; } static inline void sdei_init(void) { } +static inline void sdei_handler_abort(void) { } #endif /* CONFIG_ARM_SDE_INTERFACE */
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/2751 邮件列表地址:https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/Z...
FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://gitee.com/openeuler/kernel/pulls/2751 Mailing list address: https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/Z...