Skip to content

Commit

Permalink
Merge branch 'acpi-apei'
Browse files Browse the repository at this point in the history
* acpi-apei: (29 commits)
  efi: cper: Fix possible out-of-bounds access
  ACPI: APEI: Fix possible out-of-bounds access to BERT region
  MAINTAINERS: Add James Morse to the list of APEI reviewers
  ACPI / APEI: Add support for the SDEI GHES Notification type
  firmware: arm_sdei: Add ACPI GHES registration helper
  ACPI / APEI: Use separate fixmap pages for arm64 NMI-like notifications
  ACPI / APEI: Only use queued estatus entry during in_nmi_queue_one_entry()
  ACPI / APEI: Split ghes_read_estatus() to allow a peek at the CPER length
  ACPI / APEI: Make GHES estatus header validation more user friendly
  ACPI / APEI: Pass ghes and estatus separately to avoid a later copy
  ACPI / APEI: Let the notification helper specify the fixmap slot
  ACPI / APEI: Move locking to the notification helper
  arm64: KVM/mm: Move SEA handling behind a single 'claim' interface
  KVM: arm/arm64: Add kvm_ras.h to collect kvm specific RAS plumbing
  ACPI / APEI: Switch NOTIFY_SEA to use the estatus queue
  ACPI / APEI: Move NOTIFY_SEA between the estatus-queue and NOTIFY_NMI
  ACPI / APEI: Don't allow ghes_ack_error() to mask earlier errors
  ACPI / APEI: Generalise the estatus queue's notify code
  ACPI / APEI: Don't update struct ghes' flags in read/clear estatus
  ACPI / APEI: Remove spurious GHES_TO_CLEAR check
  ...
  • Loading branch information
Rafael J. Wysocki committed Mar 4, 2019
2 parents 511514f + 45b14a4 commit dcaed59
Show file tree
Hide file tree
Showing 21 changed files with 591 additions and 368 deletions.
1 change: 1 addition & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ ACPI APEI
M: "Rafael J. Wysocki" <rjw@rjwysocki.net>
M: Len Brown <lenb@kernel.org>
L: linux-acpi@vger.kernel.org
R: James Morse <james.morse@arm.com>
R: Tony Luck <tony.luck@intel.com>
R: Borislav Petkov <bp@alien8.de>
F: drivers/acpi/apei/
Expand Down
14 changes: 14 additions & 0 deletions arch/arm/include/asm/kvm_ras.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018 - Arm Ltd */

#ifndef __ARM_KVM_RAS_H__
#define __ARM_KVM_RAS_H__

#include <linux/types.h>

static inline int kvm_handle_guest_sea(phys_addr_t addr, unsigned int esr)
{
return -1;
}

#endif /* __ARM_KVM_RAS_H__ */
5 changes: 0 additions & 5 deletions arch/arm/include/asm/system_misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,6 @@ static inline void harden_branch_predictor(void)

extern unsigned int user_debug;

static inline int handle_guest_sea(phys_addr_t addr, unsigned int esr)
{
return -1;
}

#endif /* !__ASSEMBLY__ */

#endif /* __ASM_ARM_SYSTEM_MISC_H */
4 changes: 3 additions & 1 deletion arch/arm64/include/asm/acpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <asm/cputype.h>
#include <asm/io.h>
#include <asm/ptrace.h>
#include <asm/smp_plat.h>
#include <asm/tlbflush.h>

Expand Down Expand Up @@ -110,9 +111,10 @@ static inline u32 get_acpi_id_for_cpu(unsigned int cpu)

static inline void arch_fix_phys_package_id(int num, u32 slot) { }
void __init acpi_init_cpus(void);

int apei_claim_sea(struct pt_regs *regs);
#else
static inline void acpi_init_cpus(void) { }
static inline int apei_claim_sea(struct pt_regs *regs) { return -ENOENT; }
#endif /* CONFIG_ACPI */

#ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL
Expand Down
1 change: 1 addition & 0 deletions arch/arm64/include/asm/daifflags.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#define DAIF_PROCCTX 0
#define DAIF_PROCCTX_NOIRQ PSR_I_BIT
#define DAIF_ERRCTX (PSR_I_BIT | PSR_A_BIT)

/* mask/save/unmask/restore all exceptions, including interrupts. */
static inline void local_daif_mask(void)
Expand Down
6 changes: 5 additions & 1 deletion arch/arm64/include/asm/fixmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ enum fixed_addresses {
#ifdef CONFIG_ACPI_APEI_GHES
/* Used for GHES mapping from assorted contexts */
FIX_APEI_GHES_IRQ,
FIX_APEI_GHES_NMI,
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
Expand Down
25 changes: 25 additions & 0 deletions arch/arm64/include/asm/kvm_ras.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018 - Arm Ltd */

#ifndef __ARM64_KVM_RAS_H__
#define __ARM64_KVM_RAS_H__

#include <linux/acpi.h>
#include <linux/errno.h>
#include <linux/types.h>

#include <asm/acpi.h>

/*
* Was this synchronous external abort a RAS notification?
* Returns '0' for errors handled by some RAS subsystem, or -ENOENT.
*/
static inline int kvm_handle_guest_sea(phys_addr_t addr, unsigned int esr)
{
/* apei_claim_sea(NULL) expects to mask interrupts itself */
lockdep_assert_irqs_enabled();

return apei_claim_sea(NULL);
}

#endif /* __ARM64_KVM_RAS_H__ */
2 changes: 0 additions & 2 deletions arch/arm64/include/asm/system_misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ extern void __show_regs(struct pt_regs *);

extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);

int handle_guest_sea(phys_addr_t addr, unsigned int esr);

#endif /* __ASSEMBLY__ */

#endif /* __ASM_SYSTEM_MISC_H */
31 changes: 31 additions & 0 deletions arch/arm64/kernel/acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
#include <linux/smp.h>
#include <linux/serial_core.h>

#include <acpi/ghes.h>
#include <asm/cputype.h>
#include <asm/cpu_ops.h>
#include <asm/daifflags.h>
#include <asm/pgtable.h>
#include <asm/smp_plat.h>

Expand Down Expand Up @@ -256,3 +258,32 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr)
return __pgprot(PROT_NORMAL_NC);
return __pgprot(PROT_DEVICE_nGnRnE);
}

/*
* Claim Synchronous External Aborts as a firmware first notification.
*
* Used by KVM and the arch do_sea handler.
* @regs may be NULL when called from process context.
*/
int apei_claim_sea(struct pt_regs *regs)
{
int err = -ENOENT;
unsigned long current_flags;

if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES))
return err;

current_flags = arch_local_save_flags();

/*
* SEA can interrupt SError, mask it and describe this as an NMI so
* that APEI defers the handling.
*/
local_daif_restore(DAIF_ERRCTX);
nmi_enter();
err = ghes_notify_sea();
nmi_exit();
local_daif_restore(current_flags);

return err;
}
24 changes: 5 additions & 19 deletions arch/arm64/mm/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <linux/acpi.h>
#include <linux/extable.h>
#include <linux/signal.h>
#include <linux/mm.h>
Expand All @@ -33,6 +34,7 @@
#include <linux/preempt.h>
#include <linux/hugetlb.h>

#include <asm/acpi.h>
#include <asm/bug.h>
#include <asm/cmpxchg.h>
#include <asm/cpufeature.h>
Expand All @@ -47,8 +49,6 @@
#include <asm/tlbflush.h>
#include <asm/traps.h>

#include <acpi/ghes.h>

struct fault_info {
int (*fn)(unsigned long addr, unsigned int esr,
struct pt_regs *regs);
Expand Down Expand Up @@ -643,19 +643,10 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
inf = esr_to_fault_info(esr);

/*
* Synchronous aborts may interrupt code which had interrupts masked.
* Before calling out into the wider kernel tell the interested
* subsystems.
* Return value ignored as we rely on signal merging.
* Future patches will make this more robust.
*/
if (IS_ENABLED(CONFIG_ACPI_APEI_SEA)) {
if (interrupts_enabled(regs))
nmi_enter();

ghes_notify_sea();

if (interrupts_enabled(regs))
nmi_exit();
}
apei_claim_sea(regs);

if (esr & ESR_ELx_FnV)
siaddr = NULL;
Expand Down Expand Up @@ -733,11 +724,6 @@ static const struct fault_info fault_info[] = {
{ do_bad, SIGKILL, SI_KERNEL, "unknown 63" },
};

int handle_guest_sea(phys_addr_t addr, unsigned int esr)
{
return ghes_notify_sea();
}

asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
Expand Down
12 changes: 1 addition & 11 deletions drivers/acpi/apei/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,9 @@ config ACPI_APEI_PCIEAER
Turn on this option to enable the corresponding support.

config ACPI_APEI_SEA
bool "APEI Synchronous External Abort logging/recovering support"
bool
depends on ARM64 && ACPI_APEI_GHES
default y
help
This option should be enabled if the system supports
firmware first handling of SEA (Synchronous External Abort).
SEA happens with certain faults of data abort or instruction
abort synchronous exceptions on ARMv8 systems. If a system
supports firmware first handling of SEA, the platform analyzes
and handles hardware error notifications from SEA, 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"
Expand Down
23 changes: 10 additions & 13 deletions drivers/acpi/apei/bert.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,23 @@ static void __init bert_print_all(struct acpi_bert_region *region,
int remain = region_len;
u32 estatus_len;

if (!estatus->block_status)
return;

while (remain > sizeof(struct acpi_bert_region)) {
if (cper_estatus_check(estatus)) {
pr_err(FW_BUG "Invalid error record.\n");
return;
}

while (remain >= sizeof(struct acpi_bert_region)) {
estatus_len = cper_estatus_len(estatus);
if (remain < estatus_len) {
pr_err(FW_BUG "Truncated status block (length: %u).\n",
estatus_len);
return;
}

/* No more error records. */
if (!estatus->block_status)
return;

if (cper_estatus_check(estatus)) {
pr_err(FW_BUG "Invalid error record.\n");
return;
}

pr_info_once("Error records from previous boot:\n");

cper_estatus_print(KERN_INFO HW_ERR, estatus);
Expand All @@ -70,10 +71,6 @@ static void __init bert_print_all(struct acpi_bert_region *region,
estatus->block_status = 0;

estatus = (void *)estatus + estatus_len;
/* No more error records. */
if (!estatus->block_status)
return;

remain -= estatus_len;
}
}
Expand Down
15 changes: 7 additions & 8 deletions drivers/acpi/apei/einj.c
Original file line number Diff line number Diff line change
Expand Up @@ -644,8 +644,8 @@ static int error_type_set(void *data, u64 val)
return 0;
}

DEFINE_SIMPLE_ATTRIBUTE(error_type_fops, error_type_get,
error_type_set, "0x%llx\n");
DEFINE_DEBUGFS_ATTRIBUTE(error_type_fops, error_type_get, error_type_set,
"0x%llx\n");

static int error_inject_set(void *data, u64 val)
{
Expand All @@ -656,8 +656,7 @@ static int error_inject_set(void *data, u64 val)
error_param3, error_param4);
}

DEFINE_SIMPLE_ATTRIBUTE(error_inject_fops, NULL,
error_inject_set, "%llu\n");
DEFINE_DEBUGFS_ATTRIBUTE(error_inject_fops, NULL, error_inject_set, "%llu\n");

static int einj_check_table(struct acpi_table_einj *einj_tab)
{
Expand Down Expand Up @@ -709,10 +708,10 @@ static int __init einj_init(void)

debugfs_create_file("available_error_type", S_IRUSR, einj_debug_dir,
NULL, &available_error_type_fops);
debugfs_create_file("error_type", S_IRUSR | S_IWUSR, einj_debug_dir,
NULL, &error_type_fops);
debugfs_create_file("error_inject", S_IWUSR, einj_debug_dir,
NULL, &error_inject_fops);
debugfs_create_file_unsafe("error_type", 0600, einj_debug_dir,
NULL, &error_type_fops);
debugfs_create_file_unsafe("error_inject", 0200, einj_debug_dir,
NULL, &error_inject_fops);

apei_resources_init(&einj_resources);
einj_exec_ctx_init(&ctx);
Expand Down
27 changes: 12 additions & 15 deletions drivers/acpi/apei/erst.c
Original file line number Diff line number Diff line change
Expand Up @@ -938,17 +938,17 @@ static struct pstore_info erst_info = {
};

#define CPER_CREATOR_PSTORE \
UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \
0x64, 0x90, 0xb8, 0x9d)
GUID_INIT(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \
0x64, 0x90, 0xb8, 0x9d)
#define CPER_SECTION_TYPE_DMESG \
UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \
0x94, 0x19, 0xeb, 0x12)
GUID_INIT(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \
0x94, 0x19, 0xeb, 0x12)
#define CPER_SECTION_TYPE_DMESG_Z \
UUID_LE(0x4f118707, 0x04dd, 0x4055, 0xb5, 0xdd, 0x95, 0x6d, \
0x34, 0xdd, 0xfa, 0xc6)
GUID_INIT(0x4f118707, 0x04dd, 0x4055, 0xb5, 0xdd, 0x95, 0x6d, \
0x34, 0xdd, 0xfa, 0xc6)
#define CPER_SECTION_TYPE_MCE \
UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \
0x04, 0x4a, 0x38, 0xfc)
GUID_INIT(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \
0x04, 0x4a, 0x38, 0xfc)

struct cper_pstore_record {
struct cper_record_header hdr;
Expand Down Expand Up @@ -1012,7 +1012,7 @@ static ssize_t erst_reader(struct pstore_record *record)
rc = -EIO;
goto out;
}
if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0)
if (!guid_equal(&rcd->hdr.creator_id, &CPER_CREATOR_PSTORE))
goto skip;

record->buf = kmalloc(len, GFP_KERNEL);
Expand All @@ -1024,15 +1024,12 @@ static ssize_t erst_reader(struct pstore_record *record)
record->id = record_id;
record->compressed = false;
record->ecc_notice_size = 0;
if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_DMESG_Z) == 0) {
if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_DMESG_Z)) {
record->type = PSTORE_TYPE_DMESG;
record->compressed = true;
} else if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_DMESG) == 0)
} else if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_DMESG))
record->type = PSTORE_TYPE_DMESG;
else if (uuid_le_cmp(rcd->sec_hdr.section_type,
CPER_SECTION_TYPE_MCE) == 0)
else if (guid_equal(&rcd->sec_hdr.section_type, &CPER_SECTION_TYPE_MCE))
record->type = PSTORE_TYPE_MCE;
else
record->type = PSTORE_TYPE_MAX;
Expand Down
Loading

0 comments on commit dcaed59

Please sign in to comment.