[PATCH v4 08/10] riscv: Introduce HEST SSE notification handlers

Anup Patel anup at brainfault.org
Sun Aug 16 22:02:21 PDT 2026


On Wed, May 13, 2026 at 2:18 PM Himanshu Chauhan
<himanshu.chauhan at oss.qualcomm.com> wrote:
>
> Add config option to enable SSE in APEI. When it is enabled, functions
> to register/unregister a ghes entry with SSE are avilable along with
> low and high priority event handers. If a SSE notification type is
> determined, a ghes common handler to handle an error event is registered.
>
> Signed-off-by: Himanshu Chauhan <himanshu.chauhan at oss.qualcomm.com>
> ---
>  drivers/acpi/apei/Kconfig |  5 ++
>  drivers/acpi/apei/ghes.c  | 99 ++++++++++++++++++++++++++++++++++-----
>  2 files changed, 93 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
> index 428458c623f0..895a843d0e36 100644
> --- a/drivers/acpi/apei/Kconfig
> +++ b/drivers/acpi/apei/Kconfig
> @@ -46,6 +46,11 @@ config ACPI_APEI_SEA
>         depends on ARM64 && ACPI_APEI_GHES
>         default y
>
> +config ACPI_APEI_SSE
> +       bool
> +       depends on RISCV && RISCV_SBI_SSE && ACPI_APEI_GHES
> +       default y
> +
>  config ACPI_APEI_MEMORY_FAILURE
>         bool "APEI memory error recovering support"
>         depends on ACPI_APEI && MEMORY_FAILURE
> diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
> index 8edc2c8db1bb..1baa8ddd10ea 100644
> --- a/drivers/acpi/apei/ghes.c
> +++ b/drivers/acpi/apei/ghes.c
> @@ -17,6 +17,8 @@
>   *   Author: Huang Ying <ying.huang at intel.com>
>   */
>
> +#include <linux/err.h>
> +#include <linux/riscv_sbi_sse.h>
>  #include <linux/arm_sdei.h>
>  #include <linux/kernel.h>
>  #include <linux/moduleparam.h>
> @@ -100,6 +102,11 @@
>  #define FIX_APEI_GHES_SDEI_CRITICAL    __end_of_fixed_addresses
>  #endif
>
> +#ifndef CONFIG_RISCV_SBI_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)
> @@ -1662,6 +1669,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 -EOPNOTSUPP; }
> +static int apei_sse_unregister_ghes(struct ghes *ghes) { return -EOPNOTSUPP; }
> +#endif
> +
>  static int ghes_probe(struct platform_device *ghes_dev)
>  {
>         struct acpi_hest_generic *generic;
> @@ -1708,6 +1772,14 @@ 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 = -EOPNOTSUPP;
> +                       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);
> @@ -1775,6 +1847,17 @@ 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);

This needs to be pr_info() instead of pr_err().

> +               break;
>         default:
>                 BUG();
>         }
> @@ -1804,7 +1887,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;
>
> @@ -1838,16 +1920,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
>
>

Regards,
Anup



More information about the linux-riscv mailing list