Commit f9f05395 authored by James Morse's avatar James Morse Committed by Rafael J. Wysocki

ACPI / APEI: Add support for the SDEI GHES Notification type

If the GHES notification type is SDEI, register the provided event
using the SDEI-GHES helper.

SDEI may be one of two types of event, normal and critical. Critical
events can interrupt normal events, so these must have separate
fixmap slots and locks in case both event types are in use.
Signed-off-by: default avatarJames Morse <james.morse@arm.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent f96935d3
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include <linux/arm_sdei.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -85,6 +86,15 @@ ...@@ -85,6 +86,15 @@
((struct acpi_hest_generic_status *) \ ((struct acpi_hest_generic_status *) \
((struct ghes_estatus_node *)(estatus_node) + 1)) ((struct ghes_estatus_node *)(estatus_node) + 1))
/*
* NMI-like notifications vary by architecture, before the compiler can prune
* unused static functions it needs a value for these enums.
*/
#ifndef CONFIG_ARM_SDE_INTERFACE
#define FIX_APEI_GHES_SDEI_NORMAL __end_of_fixed_addresses
#define FIX_APEI_GHES_SDEI_CRITICAL __end_of_fixed_addresses
#endif
static inline bool is_hest_type_generic_v2(struct ghes *ghes) static inline bool is_hest_type_generic_v2(struct ghes *ghes)
{ {
return ghes->generic->header.type == ACPI_HEST_TYPE_GENERIC_ERROR_V2; return ghes->generic->header.type == ACPI_HEST_TYPE_GENERIC_ERROR_V2;
...@@ -1040,6 +1050,63 @@ static void ghes_nmi_init_cxt(void) ...@@ -1040,6 +1050,63 @@ static void ghes_nmi_init_cxt(void)
init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq); init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq);
} }
static int __ghes_sdei_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;
}
static int ghes_sdei_normal_callback(u32 event_num, struct pt_regs *regs,
void *arg)
{
static DEFINE_RAW_SPINLOCK(ghes_notify_lock_sdei_normal);
struct ghes *ghes = arg;
int err;
raw_spin_lock(&ghes_notify_lock_sdei_normal);
err = __ghes_sdei_callback(ghes, FIX_APEI_GHES_SDEI_NORMAL);
raw_spin_unlock(&ghes_notify_lock_sdei_normal);
return err;
}
static int ghes_sdei_critical_callback(u32 event_num, struct pt_regs *regs,
void *arg)
{
static DEFINE_RAW_SPINLOCK(ghes_notify_lock_sdei_critical);
struct ghes *ghes = arg;
int err;
raw_spin_lock(&ghes_notify_lock_sdei_critical);
err = __ghes_sdei_callback(ghes, FIX_APEI_GHES_SDEI_CRITICAL);
raw_spin_unlock(&ghes_notify_lock_sdei_critical);
return err;
}
static int apei_sdei_register_ghes(struct ghes *ghes)
{
if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
return -EOPNOTSUPP;
return sdei_register_ghes(ghes, ghes_sdei_normal_callback,
ghes_sdei_critical_callback);
}
static int apei_sdei_unregister_ghes(struct ghes *ghes)
{
if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE))
return -EOPNOTSUPP;
return sdei_unregister_ghes(ghes);
}
static int ghes_probe(struct platform_device *ghes_dev) static int ghes_probe(struct platform_device *ghes_dev)
{ {
struct acpi_hest_generic *generic; struct acpi_hest_generic *generic;
...@@ -1075,6 +1142,13 @@ static int ghes_probe(struct platform_device *ghes_dev) ...@@ -1075,6 +1142,13 @@ static int ghes_probe(struct platform_device *ghes_dev)
goto err; goto err;
} }
break; break;
case ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED:
if (!IS_ENABLED(CONFIG_ARM_SDE_INTERFACE)) {
pr_warn(GHES_PFX "Generic hardware error source: %d notified via SDE Interface is not supported!\n",
generic->header.source_id);
goto err;
}
break;
case ACPI_HEST_NOTIFY_LOCAL: case ACPI_HEST_NOTIFY_LOCAL:
pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n", pr_warning(GHES_PFX "Generic hardware error source: %d notified via local interrupt is not supported!\n",
generic->header.source_id); generic->header.source_id);
...@@ -1138,6 +1212,11 @@ static int ghes_probe(struct platform_device *ghes_dev) ...@@ -1138,6 +1212,11 @@ static int ghes_probe(struct platform_device *ghes_dev)
case ACPI_HEST_NOTIFY_NMI: case ACPI_HEST_NOTIFY_NMI:
ghes_nmi_add(ghes); ghes_nmi_add(ghes);
break; break;
case ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED:
rc = apei_sdei_register_ghes(ghes);
if (rc)
goto err;
break;
default: default:
BUG(); BUG();
} }
...@@ -1163,6 +1242,7 @@ static int ghes_probe(struct platform_device *ghes_dev) ...@@ -1163,6 +1242,7 @@ static int ghes_probe(struct platform_device *ghes_dev)
static int ghes_remove(struct platform_device *ghes_dev) static int ghes_remove(struct platform_device *ghes_dev)
{ {
int rc;
struct ghes *ghes; struct ghes *ghes;
struct acpi_hest_generic *generic; struct acpi_hest_generic *generic;
...@@ -1195,6 +1275,11 @@ static int ghes_remove(struct platform_device *ghes_dev) ...@@ -1195,6 +1275,11 @@ static int ghes_remove(struct platform_device *ghes_dev)
case ACPI_HEST_NOTIFY_NMI: case ACPI_HEST_NOTIFY_NMI:
ghes_nmi_remove(ghes); ghes_nmi_remove(ghes);
break; break;
case ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED:
rc = apei_sdei_unregister_ghes(ghes);
if (rc)
return rc;
break;
default: default:
BUG(); BUG();
break; break;
......
...@@ -12,7 +12,10 @@ enum sdei_conduit_types { ...@@ -12,7 +12,10 @@ enum sdei_conduit_types {
}; };
#include <acpi/ghes.h> #include <acpi/ghes.h>
#ifdef CONFIG_ARM_SDE_INTERFACE
#include <asm/sdei.h> #include <asm/sdei.h>
#endif
/* Arch code should override this to set the entry point from firmware... */ /* Arch code should override this to set the entry point from firmware... */
#ifndef sdei_arch_get_entry_point #ifndef sdei_arch_get_entry_point
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment