Skip to content

Commit 7edda08

Browse files
Tyler Baicarwildea01
authored andcommitted
acpi: apei: handle SEA notification type for ARMv8
ARM APEI extension proposal added SEA (Synchronous External Abort) notification type for ARMv8. Add a new GHES error source handling function for SEA. If an error source's notification type is SEA, then this function can be registered into the SEA exception handler. That way GHES will parse and report SEA exceptions when they occur. An SEA can interrupt code that had interrupts masked and is treated as an NMI. To aid this the page of address space for mapping APEI buffers while in_nmi() is always reserved, and ghes_ioremap_pfn_nmi() is changed to use the helper methods to find the prot_t to map with in the same way as ghes_ioremap_pfn_irq(). Signed-off-by: Tyler Baicar <[email protected]> CC: Jonathan (Zhixiong) Zhang <[email protected]> Reviewed-by: James Morse <[email protected]> Acked-by: Catalin Marinas <[email protected]> Signed-off-by: Will Deacon <[email protected]>
1 parent 32015c2 commit 7edda08

File tree

5 files changed

+105
-6
lines changed

5 files changed

+105
-6
lines changed

arch/arm64/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ config ARM64
1919
select ARCH_HAS_STRICT_KERNEL_RWX
2020
select ARCH_HAS_STRICT_MODULE_RWX
2121
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
22+
select ARCH_HAVE_NMI_SAFE_CMPXCHG if ACPI_APEI_SEA
2223
select ARCH_USE_CMPXCHG_LOCKREF
2324
select ARCH_SUPPORTS_ATOMIC_RMW
2425
select ARCH_SUPPORTS_NUMA_BALANCING
@@ -92,6 +93,7 @@ config ARM64
9293
select HAVE_IRQ_TIME_ACCOUNTING
9394
select HAVE_MEMBLOCK
9495
select HAVE_MEMBLOCK_NODE_MAP if NUMA
96+
select HAVE_NMI if ACPI_APEI_SEA
9597
select HAVE_PATA_PLATFORM
9698
select HAVE_PERF_EVENTS
9799
select HAVE_PERF_REGS

arch/arm64/mm/fault.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
#include <asm/pgtable.h>
4343
#include <asm/tlbflush.h>
4444

45+
#include <acpi/ghes.h>
46+
4547
struct fault_info {
4648
int (*fn)(unsigned long addr, unsigned int esr,
4749
struct pt_regs *regs);
@@ -535,6 +537,21 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
535537
pr_err("Synchronous External Abort: %s (0x%08x) at 0x%016lx\n",
536538
inf->name, esr, addr);
537539

540+
/*
541+
* Synchronous aborts may interrupt code which had interrupts masked.
542+
* Before calling out into the wider kernel tell the interested
543+
* subsystems.
544+
*/
545+
if (IS_ENABLED(CONFIG_ACPI_APEI_SEA)) {
546+
if (interrupts_enabled(regs))
547+
nmi_enter();
548+
549+
ghes_notify_sea();
550+
551+
if (interrupts_enabled(regs))
552+
nmi_exit();
553+
}
554+
538555
info.si_signo = SIGBUS;
539556
info.si_errno = 0;
540557
info.si_code = 0;

drivers/acpi/apei/Kconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@ config ACPI_APEI_PCIEAER
3939
PCIe AER errors may be reported via APEI firmware first mode.
4040
Turn on this option to enable the corresponding support.
4141

42+
config ACPI_APEI_SEA
43+
bool "APEI Synchronous External Abort logging/recovering support"
44+
depends on ARM64 && ACPI_APEI_GHES
45+
default y
46+
help
47+
This option should be enabled if the system supports
48+
firmware first handling of SEA (Synchronous External Abort).
49+
SEA happens with certain faults of data abort or instruction
50+
abort synchronous exceptions on ARMv8 systems. If a system
51+
supports firmware first handling of SEA, the platform analyzes
52+
and handles hardware error notifications from SEA, and it may then
53+
form a HW error record for the OS to parse and handle. This
54+
option allows the OS to look for such hardware error record, and
55+
take appropriate action.
56+
4257
config ACPI_APEI_MEMORY_FAILURE
4358
bool "APEI memory error recovering support"
4459
depends on ACPI_APEI && MEMORY_FAILURE

drivers/acpi/apei/ghes.c

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,7 @@ static DEFINE_MUTEX(ghes_list_mutex);
116116
* Two virtual pages are used, one for IRQ/PROCESS context, the other for
117117
* NMI context (optionally).
118118
*/
119-
#ifdef CONFIG_HAVE_ACPI_APEI_NMI
120119
#define GHES_IOREMAP_PAGES 2
121-
#else
122-
#define GHES_IOREMAP_PAGES 1
123-
#endif
124120
#define GHES_IOREMAP_IRQ_PAGE(base) (base)
125121
#define GHES_IOREMAP_NMI_PAGE(base) ((base) + PAGE_SIZE)
126122

@@ -159,10 +155,14 @@ static void ghes_ioremap_exit(void)
159155
static void __iomem *ghes_ioremap_pfn_nmi(u64 pfn)
160156
{
161157
unsigned long vaddr;
158+
phys_addr_t paddr;
159+
pgprot_t prot;
162160

163161
vaddr = (unsigned long)GHES_IOREMAP_NMI_PAGE(ghes_ioremap_area->addr);
164-
ioremap_page_range(vaddr, vaddr + PAGE_SIZE,
165-
pfn << PAGE_SHIFT, PAGE_KERNEL);
162+
163+
paddr = pfn << PAGE_SHIFT;
164+
prot = arch_apei_get_mem_attribute(paddr);
165+
ioremap_page_range(vaddr, vaddr + PAGE_SIZE, paddr, prot);
166166

167167
return (void __iomem *)vaddr;
168168
}
@@ -774,6 +774,50 @@ static struct notifier_block ghes_notifier_sci = {
774774
.notifier_call = ghes_notify_sci,
775775
};
776776

777+
#ifdef CONFIG_ACPI_APEI_SEA
778+
static LIST_HEAD(ghes_sea);
779+
780+
void ghes_notify_sea(void)
781+
{
782+
struct ghes *ghes;
783+
784+
/*
785+
* synchronize_rcu() will wait for nmi_exit(), so no need to
786+
* rcu_read_lock().
787+
*/
788+
list_for_each_entry_rcu(ghes, &ghes_sea, list) {
789+
ghes_proc(ghes);
790+
}
791+
}
792+
793+
static void ghes_sea_add(struct ghes *ghes)
794+
{
795+
mutex_lock(&ghes_list_mutex);
796+
list_add_rcu(&ghes->list, &ghes_sea);
797+
mutex_unlock(&ghes_list_mutex);
798+
}
799+
800+
static void ghes_sea_remove(struct ghes *ghes)
801+
{
802+
mutex_lock(&ghes_list_mutex);
803+
list_del_rcu(&ghes->list);
804+
mutex_unlock(&ghes_list_mutex);
805+
synchronize_rcu();
806+
}
807+
#else /* CONFIG_ACPI_APEI_SEA */
808+
static inline void ghes_sea_add(struct ghes *ghes)
809+
{
810+
pr_err(GHES_PFX "ID: %d, trying to add SEA notification which is not supported\n",
811+
ghes->generic->header.source_id);
812+
}
813+
814+
static inline void ghes_sea_remove(struct ghes *ghes)
815+
{
816+
pr_err(GHES_PFX "ID: %d, trying to remove SEA notification which is not supported\n",
817+
ghes->generic->header.source_id);
818+
}
819+
#endif /* CONFIG_ACPI_APEI_SEA */
820+
777821
#ifdef CONFIG_HAVE_ACPI_APEI_NMI
778822
/*
779823
* printk is not safe in NMI context. So in NMI handler, we allocate
@@ -1019,6 +1063,14 @@ static int ghes_probe(struct platform_device *ghes_dev)
10191063
case ACPI_HEST_NOTIFY_EXTERNAL:
10201064
case ACPI_HEST_NOTIFY_SCI:
10211065
break;
1066+
case ACPI_HEST_NOTIFY_SEA:
1067+
if (!IS_ENABLED(CONFIG_ACPI_APEI_SEA)) {
1068+
pr_warn(GHES_PFX "Generic hardware error source: %d notified via SEA is not supported\n",
1069+
generic->header.source_id);
1070+
rc = -ENOTSUPP;
1071+
goto err;
1072+
}
1073+
break;
10221074
case ACPI_HEST_NOTIFY_NMI:
10231075
if (!IS_ENABLED(CONFIG_HAVE_ACPI_APEI_NMI)) {
10241076
pr_warn(GHES_PFX "Generic hardware error source: %d notified via NMI interrupt is not supported!\n",
@@ -1083,6 +1135,9 @@ static int ghes_probe(struct platform_device *ghes_dev)
10831135
list_add_rcu(&ghes->list, &ghes_sci);
10841136
mutex_unlock(&ghes_list_mutex);
10851137
break;
1138+
case ACPI_HEST_NOTIFY_SEA:
1139+
ghes_sea_add(ghes);
1140+
break;
10861141
case ACPI_HEST_NOTIFY_NMI:
10871142
ghes_nmi_add(ghes);
10881143
break;
@@ -1126,6 +1181,9 @@ static int ghes_remove(struct platform_device *ghes_dev)
11261181
mutex_unlock(&ghes_list_mutex);
11271182
synchronize_rcu();
11281183
break;
1184+
case ACPI_HEST_NOTIFY_SEA:
1185+
ghes_sea_remove(ghes);
1186+
break;
11291187
case ACPI_HEST_NOTIFY_NMI:
11301188
ghes_nmi_remove(ghes);
11311189
break;

include/acpi/ghes.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#ifndef GHES_H
2+
#define GHES_H
3+
14
#include <acpi/apei.h>
25
#include <acpi/hed.h>
36

@@ -109,3 +112,7 @@ static inline void *acpi_hest_get_next(struct acpi_hest_generic_data *gdata)
109112
{
110113
return (void *)(gdata) + acpi_hest_get_record_size(gdata);
111114
}
115+
116+
void ghes_notify_sea(void);
117+
118+
#endif /* GHES_H */

0 commit comments

Comments
 (0)