- Functions to register a ghes entry with SSE - Add Handlers for low/high priority events - Call ghes common handler to handle an error event Signed-off-by: Himanshu Chauhan <hchauhan@xxxxxxxxxxxxxxxx> --- drivers/acpi/apei/ghes.c | 100 ++++++++++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 8a1029163799..59abb89d3547 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -17,6 +17,7 @@ * Author: Huang Ying <ying.huang@xxxxxxxxx> */ +#include <linux/riscv_sse.h> #include <linux/arm_sdei.h> #include <linux/kernel.h> #include <linux/moduleparam.h> @@ -97,6 +98,11 @@ #define FIX_APEI_GHES_SDEI_CRITICAL __end_of_fixed_addresses #endif +#ifndef CONFIG_RISCV_SSE +#define FIX_APEI_GHES_SSE_LOW_PRIORITY __end_of_fixed_addresses +#define FIX_APEI_GHES_SSE_HIGH_PRIORITY __end_of_fixed_addresses +#endif + static ATOMIC_NOTIFIER_HEAD(ghes_report_chain); static inline bool is_hest_type_generic_v2(struct ghes *ghes) @@ -1405,6 +1411,63 @@ static int apei_sdei_unregister_ghes(struct ghes *ghes) return sdei_unregister_ghes(ghes); } +#if defined(CONFIG_ACPI_APEI_SSE) +/* SSE Handlers */ +static int __ghes_sse_callback(struct ghes *ghes, + enum fixed_addresses fixmap_idx) +{ + if (!ghes_in_nmi_queue_one_entry(ghes, fixmap_idx)) { + irq_work_queue(&ghes_proc_irq_work); + + return 0; + } + + return -ENOENT; +} + +/* Low priority */ +static int ghes_sse_lo_callback(u32 event_num, void *arg, struct pt_regs *regs) +{ + static DEFINE_RAW_SPINLOCK(ghes_notify_lock_sse_lo); + struct ghes *ghes = arg; + int err; + + raw_spin_lock(&ghes_notify_lock_sse_lo); + err = __ghes_sse_callback(ghes, FIX_APEI_GHES_SSE_LOW_PRIORITY); + raw_spin_unlock(&ghes_notify_lock_sse_lo); + + return err; +} + +/* High priority */ +static int ghes_sse_hi_callback(u32 event_num, void *arg, struct pt_regs *regs) +{ + static DEFINE_RAW_SPINLOCK(ghes_notify_lock_sse_hi); + struct ghes *ghes = arg; + int err; + + raw_spin_lock(&ghes_notify_lock_sse_hi); + err = __ghes_sse_callback(ghes, FIX_APEI_GHES_SSE_HIGH_PRIORITY); + raw_spin_unlock(&ghes_notify_lock_sse_hi); + + return err; +} + +static int apei_sse_register_ghes(struct ghes *ghes) +{ + return sse_register_ghes(ghes, ghes_sse_lo_callback, + ghes_sse_hi_callback); +} + +static int apei_sse_unregister_ghes(struct ghes *ghes) +{ + return sse_unregister_ghes(ghes); +} +#else /* CONFIG_ACPI_APEI_SSE */ +static int apei_sse_register_ghes(struct ghes *ghes) { return -ENOTSUPP; } +static int apei_sse_unregister_ghes(struct ghes *ghes) { return -ENOTSUPP; } +#endif + static int ghes_probe(struct platform_device *ghes_dev) { struct acpi_hest_generic *generic; @@ -1451,6 +1514,15 @@ static int ghes_probe(struct platform_device *ghes_dev) pr_warn(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n", generic->header.source_id); goto err; + case ACPI_HEST_NOTIFY_SSE: + if (!IS_ENABLED(CONFIG_ACPI_APEI_SSE)) { + pr_warn(GHES_PFX "Generic hardware error source: %d " + "notified via SSE is not supported\n", + generic->header.source_id); + rc = -ENOTSUPP; + goto err; + } + break; default: pr_warn(FW_WARN GHES_PFX "Unknown notification type: %u for generic hardware error source: %d\n", generic->notify.type, generic->header.source_id); @@ -1514,6 +1586,18 @@ static int ghes_probe(struct platform_device *ghes_dev) if (rc) goto err; break; + + case ACPI_HEST_NOTIFY_SSE: + rc = apei_sse_register_ghes(ghes); + if (rc) { + pr_err(GHES_PFX "Failed to register for SSE notification" + " on vector %d\n", + generic->notify.vector); + goto err; + } + pr_err(GHES_PFX "Registered SSE notification on vector %d\n", + generic->notify.vector); + break; default: BUG(); } @@ -1543,7 +1627,6 @@ static int ghes_probe(struct platform_device *ghes_dev) static void ghes_remove(struct platform_device *ghes_dev) { - int rc; struct ghes *ghes; struct acpi_hest_generic *generic; @@ -1577,16 +1660,11 @@ static void ghes_remove(struct platform_device *ghes_dev) ghes_nmi_remove(ghes); break; case ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED: - rc = apei_sdei_unregister_ghes(ghes); - if (rc) { - /* - * Returning early results in a resource leak, but we're - * only here if stopping the hardware failed. - */ - dev_err(&ghes_dev->dev, "Failed to unregister ghes (%pe)\n", - ERR_PTR(rc)); - return; - } + apei_sdei_unregister_ghes(ghes); + break; + + case ACPI_HEST_NOTIFY_SSE: + apei_sse_unregister_ghes(ghes); break; default: BUG(); -- 2.43.0