@@ -585,7 +585,7 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
if (interrupts_enabled(regs))
nmi_enter();
- ret = ghes_notify_sea();
+ ret = ghes_notify_abort(ACPI_HEST_NOTIFY_SEA);
if (interrupts_enabled(regs))
nmi_exit();
@@ -682,7 +682,7 @@ int handle_guest_sea(phys_addr_t addr, unsigned int esr)
int ret = -ENOENT;
if (IS_ENABLED(CONFIG_ACPI_APEI_SEA))
- ret = ghes_notify_sea();
+ ret = ghes_notify_abort(ACPI_HEST_NOTIFY_SEA);
return ret;
}
@@ -54,6 +54,21 @@ config ACPI_APEI_SEA
option allows the OS to look for such hardware error record, and
take appropriate action.
+config ACPI_APEI_SEI
+ bool "APEI Asynchronous SError Interrupt logging/recovering support"
+ depends on ARM64 && ACPI_APEI_GHES
+ default y
+ help
+ This option should be enabled if the system supports
+ firmware first handling of SEI (asynchronous SError interrupt).
+
+ SEI happens with asynchronous external abort for errors on device
+ memory reads on ARMv8 systems. If a system supports firmware first
+ handling of SEI, the platform analyzes and handles hardware error
+ notifications from SEI, and it may then form a HW error record for
+ the OS to parse and handle. This option allows the OS to look for
+ such hardware error record, and take appropriate action.
+
config ACPI_APEI_MEMORY_FAILURE
bool "APEI memory error recovering support"
depends on ACPI_APEI && MEMORY_FAILURE
@@ -815,33 +815,57 @@ static struct notifier_block ghes_notifier_hed = {
#ifdef CONFIG_ACPI_APEI_SEA
static LIST_HEAD(ghes_sea);
+#endif
+
+#ifdef CONFIG_ACPI_APEI_SEI
+static LIST_HEAD(ghes_sei);
+#endif
+#if defined(CONFIG_ACPI_APEI_SEA) || defined(CONFIG_ACPI_APEI_SEI)
/*
- * Return 0 only if one of the SEA error sources successfully reported an error
- * record sent from the firmware.
+ * Return 0 only if one of the SEA or SEI error sources successfully
+ * reported an error record sent from the firmware.
*/
-int ghes_notify_sea(void)
+int ghes_notify_abort(u8 type)
{
struct ghes *ghes;
+ struct list_head *head = NULL;
int ret = -ENOENT;
- rcu_read_lock();
- list_for_each_entry_rcu(ghes, &ghes_sea, list) {
- if (!ghes_proc(ghes))
- ret = 0;
+ if (type == ACPI_HEST_NOTIFY_SEA)
+ head = &ghes_sea;
+ else if (type == ACPI_HEST_NOTIFY_SEI)
+ head = &ghes_sei;
+
+ if (head) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(ghes, head, list) {
+ if (!ghes_proc(ghes))
+ ret = 0;
+ }
+ rcu_read_unlock();
}
- rcu_read_unlock();
return ret;
}
-static void ghes_sea_add(struct ghes *ghes)
+static void ghes_abort_add(struct ghes *ghes)
{
- mutex_lock(&ghes_list_mutex);
- list_add_rcu(&ghes->list, &ghes_sea);
- mutex_unlock(&ghes_list_mutex);
+ struct list_head *head = NULL;
+ u8 notify_type = ghes->generic->notify.type;
+
+ if (notify_type == ACPI_HEST_NOTIFY_SEA)
+ head = &ghes_sea;
+ else if (notify_type == ACPI_HEST_NOTIFY_SEI)
+ head = &ghes_sei;
+
+ if (head) {
+ mutex_lock(&ghes_list_mutex);
+ list_add_rcu(&ghes->list, head);
+ mutex_unlock(&ghes_list_mutex);
+ }
}
-static void ghes_sea_remove(struct ghes *ghes)
+static void ghes_abort_remove(struct ghes *ghes)
{
mutex_lock(&ghes_list_mutex);
list_del_rcu(&ghes->list);
@@ -1084,6 +1108,13 @@ static int ghes_probe(struct platform_device *ghes_dev)
goto err;
}
break;
+ case ACPI_HEST_NOTIFY_SEI:
+ if (!IS_ENABLED(CONFIG_ACPI_APEI_SEI)) {
+ pr_warn(GHES_PFX "Generic hardware error source: %d notified via SEI is not supported!\n",
+ generic->header.source_id);
+ goto err;
+ }
+ break;
case ACPI_HEST_NOTIFY_NMI:
if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_NMI)) {
pr_warn(GHES_PFX "Generic hardware error source: %d notified via NMI interrupt is not supported!\n",
@@ -1153,7 +1184,8 @@ static int ghes_probe(struct platform_device *ghes_dev)
break;
case ACPI_HEST_NOTIFY_SEA:
- ghes_sea_add(ghes);
+ case ACPI_HEST_NOTIFY_SEI:
+ ghes_abort_add(ghes);
break;
case ACPI_HEST_NOTIFY_NMI:
ghes_nmi_add(ghes);
@@ -1206,7 +1238,8 @@ static int ghes_remove(struct platform_device *ghes_dev)
break;
case ACPI_HEST_NOTIFY_SEA:
- ghes_sea_remove(ghes);
+ case ACPI_HEST_NOTIFY_SEI:
+ ghes_abort_remove(ghes);
break;
case ACPI_HEST_NOTIFY_NMI:
ghes_nmi_remove(ghes);
@@ -118,6 +118,6 @@ static inline void *acpi_hest_get_next(struct acpi_hest_generic_data *gdata)
(void *)section - (void *)(estatus + 1) < estatus->data_length; \
section = acpi_hest_get_next(section))
-int ghes_notify_sea(void);
+int ghes_notify_abort(u8 type);
#endif /* GHES_H */