Skip to content
This repository has been archived by the owner on Jul 16, 2024. It is now read-only.

Commit

Permalink
BACKPORT: ACPI / APEI: Let the notification helper specify the fixmap…
Browse files Browse the repository at this point in the history
… slot

ghes_copy_tofrom_phys() uses a different fixmap slot depending on in_nmi().
This doesn't work when there are multiple NMI-like notifications, that
could interrupt each other.

As with the locking, move the chosen fixmap_idx to the notification helper.
This only matters for NMI-like notifications, anything calling
ghes_proc() can use the IRQ fixmap slot as its already holding an irqsave
spinlock.

This lets us collapse the ghes_ioremap_pfn_*() helpers.

This patch is needed because Quicksilver firmware-first error handling
uses the SDEI notification type for communication between trusted
firmware and the OS. This adds needed NMI and SDEI functionality so
that the SDEI path in the kernel through APEI acts as an NMI and is
properly wired up to the APEI interfaces.

Backported from: torvalds/linux@b484079

Signed-off-by: James Morse <[email protected]>
Reviewed-by: Borislav Petkov <[email protected]>
Signed-off-by: Tyler Baicar <[email protected]>
  • Loading branch information
James Morse authored and tphan-ampere committed Apr 21, 2020
1 parent 2ff3da1 commit 71b03af
Showing 1 changed file with 39 additions and 53 deletions.
92 changes: 39 additions & 53 deletions drivers/acpi/apei/ghes.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <linux/llist.h>
#include <linux/genalloc.h>
#include <linux/pci.h>
#include <linux/pfn.h>
#include <linux/aer.h>
#include <linux/nmi.h>
#include <linux/sched/clock.h>
Expand Down Expand Up @@ -127,38 +128,24 @@ static atomic_t ghes_estatus_cache_alloced;

static int ghes_panic_timeout __read_mostly = 30;

static void __iomem *ghes_ioremap_pfn_nmi(u64 pfn)
static void __iomem *ghes_map(u64 pfn, enum fixed_addresses fixmap_idx)
{
phys_addr_t paddr;
pgprot_t prot;

paddr = pfn << PAGE_SHIFT;
paddr = PFN_PHYS(pfn);
prot = arch_apei_get_mem_attribute(paddr);
__set_fixmap(FIX_APEI_GHES_NMI, paddr, prot);
__set_fixmap(fixmap_idx, paddr, prot);

return (void __iomem *) fix_to_virt(FIX_APEI_GHES_NMI);
return (void __iomem *) __fix_to_virt(fixmap_idx);
}

static void __iomem *ghes_ioremap_pfn_irq(u64 pfn)
static void ghes_unmap(void __iomem *vaddr, enum fixed_addresses fixmap_idx)
{
phys_addr_t paddr;
pgprot_t prot;

paddr = pfn << PAGE_SHIFT;
prot = arch_apei_get_mem_attribute(paddr);
__set_fixmap(FIX_APEI_GHES_IRQ, paddr, prot);
int _idx = virt_to_fix((unsigned long)vaddr);

return (void __iomem *) fix_to_virt(FIX_APEI_GHES_IRQ);
}

static void ghes_iounmap_nmi(void)
{
clear_fixmap(FIX_APEI_GHES_NMI);
}

static void ghes_iounmap_irq(void)
{
clear_fixmap(FIX_APEI_GHES_IRQ);
WARN_ON_ONCE(fixmap_idx != _idx);
clear_fixmap(fixmap_idx);
}

int ghes_estatus_pool_init(int num_ghes)
Expand Down Expand Up @@ -283,20 +270,16 @@ static inline int ghes_severity(int severity)
}

static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
int from_phys)
int from_phys,
enum fixed_addresses fixmap_idx)
{
void __iomem *vaddr;
int in_nmi = in_nmi();
u64 offset;
u32 trunk;

while (len > 0) {
offset = paddr - (paddr & PAGE_MASK);
if (in_nmi) {
vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT);
} else {
vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT);
}
vaddr = ghes_map(PHYS_PFN(paddr), fixmap_idx);
trunk = PAGE_SIZE - offset;
trunk = min(trunk, len);
if (from_phys)
Expand All @@ -306,15 +289,13 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
len -= trunk;
paddr += trunk;
buffer += trunk;
if (in_nmi) {
ghes_iounmap_nmi();
} else {
ghes_iounmap_irq();
}
ghes_unmap(vaddr, fixmap_idx);
}
}

static int ghes_read_estatus(struct ghes *ghes, u64 *buf_paddr)
static int ghes_read_estatus(struct ghes *ghes, u64 *buf_paddr,
enum fixed_addresses fixmap_idx)

{
struct acpi_hest_generic *g = ghes->generic;
u32 len;
Expand All @@ -332,7 +313,7 @@ static int ghes_read_estatus(struct ghes *ghes, u64 *buf_paddr)
return -ENOENT;

ghes_copy_tofrom_phys(ghes->estatus, *buf_paddr,
sizeof(*ghes->estatus), 1);
sizeof(*ghes->estatus), 1, fixmap_idx);
if (!ghes->estatus->block_status) {
*buf_paddr = 0;
return -ENOENT;
Expand All @@ -348,7 +329,7 @@ static int ghes_read_estatus(struct ghes *ghes, u64 *buf_paddr)
goto err_read_block;
ghes_copy_tofrom_phys(ghes->estatus + 1,
*buf_paddr + sizeof(*ghes->estatus),
len - sizeof(*ghes->estatus), 1);
len - sizeof(*ghes->estatus), 1, fixmap_idx);
if (cper_estatus_check(ghes->estatus))
goto err_read_block;
rc = 0;
Expand All @@ -361,15 +342,17 @@ static int ghes_read_estatus(struct ghes *ghes, u64 *buf_paddr)
return rc;
}

static void ghes_clear_estatus(struct ghes *ghes, u64 buf_paddr)
static void ghes_clear_estatus(struct ghes *ghes, u64 buf_paddr,
enum fixed_addresses fixmap_idx)
{
ghes->estatus->block_status = 0;

if (!buf_paddr)
return;

ghes_copy_tofrom_phys(ghes->estatus, buf_paddr,
sizeof(ghes->estatus->block_status), 0);
sizeof(ghes->estatus->block_status), 0,
fixmap_idx);

/*
* GHESv2 type HEST entries introduce support for error acknowledgment,
Expand Down Expand Up @@ -668,11 +651,12 @@ static void ghes_estatus_cache_add(
rcu_read_unlock();
}

static void __ghes_panic(struct ghes *ghes, u64 buf_paddr)
static void __ghes_panic(struct ghes *ghes, u64 buf_paddr,
enum fixed_addresses fixmap_idx)
{
__ghes_print_estatus(KERN_EMERG, ghes->generic, ghes->estatus);

ghes_clear_estatus(ghes, buf_paddr);
ghes_clear_estatus(ghes, buf_paddr, fixmap_idx);

/* reboot to log the error! */
if (!panic_timeout)
Expand All @@ -685,12 +669,12 @@ static int ghes_proc(struct ghes *ghes)
u64 buf_paddr;
int rc;

rc = ghes_read_estatus(ghes, &buf_paddr);
rc = ghes_read_estatus(ghes, &buf_paddr, FIX_APEI_GHES_IRQ);
if (rc)
goto out;

if (ghes_severity(ghes->estatus->error_severity) >= GHES_SEV_PANIC) {
__ghes_panic(ghes, buf_paddr);
__ghes_panic(ghes, buf_paddr, FIX_APEI_GHES_IRQ);
}

if (!ghes_estatus_cached(ghes->estatus)) {
Expand All @@ -700,7 +684,7 @@ static int ghes_proc(struct ghes *ghes)
ghes_do_proc(ghes, ghes->estatus);

out:
ghes_clear_estatus(ghes, buf_paddr);
ghes_clear_estatus(ghes, buf_paddr, FIX_APEI_GHES_IRQ);

return rc;
}
Expand Down Expand Up @@ -866,36 +850,38 @@ static void __process_error(struct ghes *ghes)
#endif
}

static int ghes_in_nmi_queue_one_entry(struct ghes *ghes)
static int ghes_in_nmi_queue_one_entry(struct ghes *ghes,
enum fixed_addresses fixmap_idx)
{
u64 buf_paddr;
int sev;

if (ghes_read_estatus(ghes, &buf_paddr)) {
ghes_clear_estatus(ghes, buf_paddr);
if (ghes_read_estatus(ghes, &buf_paddr, fixmap_idx)) {
ghes_clear_estatus(ghes, buf_paddr, fixmap_idx);
return -ENOENT;
}

sev = ghes_severity(ghes->estatus->error_severity);
if (sev >= GHES_SEV_PANIC) {
ghes_print_queued_estatus();
__ghes_panic(ghes, buf_paddr);
__ghes_panic(ghes, buf_paddr, fixmap_idx);
}

__process_error(ghes);
ghes_clear_estatus(ghes, buf_paddr);
ghes_clear_estatus(ghes, buf_paddr, fixmap_idx);

return 0;
}

static int ghes_in_nmi_spool_from_list(struct list_head *rcu_list)
static int ghes_in_nmi_spool_from_list(struct list_head *rcu_list,
enum fixed_addresses fixmap_idx)
{
int err, ret = -ENOENT;
struct ghes *ghes;

rcu_read_lock();
list_for_each_entry_rcu(ghes, rcu_list, list) {
err = ghes_in_nmi_queue_one_entry(ghes);
err = ghes_in_nmi_queue_one_entry(ghes, fixmap_idx);
if (!err)
ret = 0;
}
Expand All @@ -920,7 +906,7 @@ int ghes_notify_sea(void)
int rv;

raw_spin_lock(&ghes_notify_lock_sea);
rv = ghes_in_nmi_spool_from_list(&ghes_sea);
rv = ghes_in_nmi_spool_from_list(&ghes_sea, FIX_APEI_GHES_NMI);
raw_spin_unlock(&ghes_notify_lock_sea);

return rv;
Expand Down Expand Up @@ -963,7 +949,7 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
return ret;

raw_spin_lock(&ghes_notify_lock_nmi);
err = ghes_in_nmi_spool_from_list(&ghes_nmi);
err = ghes_in_nmi_spool_from_list(&ghes_nmi, FIX_APEI_GHES_NMI);
if (!err)
ret = NMI_HANDLED;
raw_spin_unlock(&ghes_notify_lock_nmi);
Expand Down

0 comments on commit 71b03af

Please sign in to comment.