]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
firmware: arm_sdei: Add ACPI GHES registration helper
authorJames Morse <james.morse@arm.com>
Tue, 29 Jan 2019 18:49:01 +0000 (18:49 +0000)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 11 Feb 2019 10:07:49 +0000 (11:07 +0100)
APEI's Generic Hardware Error Source structures do not describe
whether the SDEI event is shared or private, as this information is
discoverable via the API.

GHES needs to know whether an event is normal or critical to avoid
sharing locks or fixmap entries, but GHES shouldn't have to know about
the SDEI API.

Add a helper to register the GHES using the appropriate normal or
critical callback.

Signed-off-by: James Morse <james.morse@arm.com>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
arch/arm64/include/asm/fixmap.h
drivers/firmware/arm_sdei.c
include/linux/arm_sdei.h

index 966dd4bb23f27d784d1ffdf6d4dbda8613d212b3..f987b8a8f325e03623a7dc3502f7b39031aad504 100644 (file)
@@ -56,6 +56,10 @@ enum fixed_addresses {
        /* Used for GHES mapping from assorted contexts */
        FIX_APEI_GHES_IRQ,
        FIX_APEI_GHES_SEA,
+#ifdef CONFIG_ARM_SDE_INTERFACE
+       FIX_APEI_GHES_SDEI_NORMAL,
+       FIX_APEI_GHES_SDEI_CRITICAL,
+#endif
 #endif /* CONFIG_ACPI_APEI_GHES */
 
 #ifdef CONFIG_UNMAP_KERNEL_AT_EL0
index c64c7da738297453ee05f173c1a6c0d4c0fadbe9..e6376f985ef7b9e94556869dc8b311bf239a24df 100644 (file)
@@ -2,6 +2,7 @@
 // Copyright (C) 2017 Arm Ltd.
 #define pr_fmt(fmt) "sdei: " fmt
 
+#include <acpi/ghes.h>
 #include <linux/acpi.h>
 #include <linux/arm_sdei.h>
 #include <linux/arm-smccc.h>
@@ -887,6 +888,73 @@ static void sdei_smccc_hvc(unsigned long function_id,
        arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res);
 }
 
+int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb,
+                      sdei_event_callback *critical_cb)
+{
+       int err;
+       u64 result;
+       u32 event_num;
+       sdei_event_callback *cb;
+
+       if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES))
+               return -EOPNOTSUPP;
+
+       event_num = ghes->generic->notify.vector;
+       if (event_num == 0) {
+               /*
+                * Event 0 is reserved by the specification for
+                * SDEI_EVENT_SIGNAL.
+                */
+               return -EINVAL;
+       }
+
+       err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_PRIORITY,
+                                     &result);
+       if (err)
+               return err;
+
+       if (result == SDEI_EVENT_PRIORITY_CRITICAL)
+               cb = critical_cb;
+       else
+               cb = normal_cb;
+
+       err = sdei_event_register(event_num, cb, ghes);
+       if (!err)
+               err = sdei_event_enable(event_num);
+
+       return err;
+}
+
+int sdei_unregister_ghes(struct ghes *ghes)
+{
+       int i;
+       int err;
+       u32 event_num = ghes->generic->notify.vector;
+
+       might_sleep();
+
+       if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES))
+               return -EOPNOTSUPP;
+
+       /*
+        * The event may be running on another CPU. Disable it
+        * to stop new events, then try to unregister a few times.
+        */
+       err = sdei_event_disable(event_num);
+       if (err)
+               return err;
+
+       for (i = 0; i < 3; i++) {
+               err = sdei_event_unregister(event_num);
+               if (err != -EINPROGRESS)
+                       break;
+
+               schedule();
+       }
+
+       return err;
+}
+
 static int sdei_get_conduit(struct platform_device *pdev)
 {
        const char *method;
index 942afbd544b71af820290bf12106ba51cedff9a0..393899192906db3e7222173e004a4c212e0db4d9 100644 (file)
@@ -11,6 +11,7 @@ enum sdei_conduit_types {
        CONDUIT_HVC,
 };
 
+#include <acpi/ghes.h>
 #include <asm/sdei.h>
 
 /* Arch code should override this to set the entry point from firmware... */
@@ -39,6 +40,11 @@ int sdei_event_unregister(u32 event_num);
 int sdei_event_enable(u32 event_num);
 int sdei_event_disable(u32 event_num);
 
+/* GHES register/unregister helpers */
+int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb,
+                      sdei_event_callback *critical_cb);
+int sdei_unregister_ghes(struct ghes *ghes);
+
 #ifdef CONFIG_ARM_SDE_INTERFACE
 /* For use by arch code when CPU hotplug notifiers are not appropriate. */
 int sdei_mask_local_cpu(void);