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

Commit

Permalink
BACKPORT: arm64: KVM/mm: Move SEA handling behind a single 'claim' in…
Browse files Browse the repository at this point in the history
…terface

To split up APEIs in_nmi() path, the caller needs to always be
in_nmi(). Add a helper to do the work and claim the notification.

When KVM or the arch code takes an exception that might be a RAS
notification, it asks the APEI firmware-first code whether it wants
to claim the exception. A future kernel-first mechanism may be queried
afterwards, and claim the notification, otherwise we fall through
to the existing default behaviour.

The NOTIFY_SEA code was merged before considering multiple, possibly
interacting, NMI-like notifications and the need to consider kernel
first in the future. Make the 'claiming' behaviour explicit.

Restructuring the APEI code to allow multiple NMI-like notifications
means any notification that might interrupt interrupts-masked
code must always be wrapped in nmi_enter()/nmi_exit(). This will
allow APEI to use in_nmi() to use the right fixmap entries.

Mask SError over this window to prevent an asynchronous RAS error
arriving and tripping 'nmi_enter()'s BUG_ON(in_nmi()).

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@d44f1b8

Signed-off-by: James Morse <[email protected]>
Acked-by: Marc Zyngier <[email protected]>
Tested-by: Tyler Baicar <[email protected]>
Acked-by: Catalin Marinas <[email protected]>
Signed-off-by: Tyler Baicar <[email protected]>
  • Loading branch information
James Morse authored and tphan-ampere committed Apr 21, 2020
1 parent f78e808 commit ab77d4b
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 26 deletions.
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 @@ -102,9 +103,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
16 changes: 15 additions & 1 deletion arch/arm64/include/asm/kvm_ras.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,22 @@
#ifndef __ARM64_KVM_RAS_H__
#define __ARM64_KVM_RAS_H__

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

int kvm_handle_guest_sea(phys_addr_t addr, unsigned int esr);
#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__ */
31 changes: 31 additions & 0 deletions arch/arm64/kernel/acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,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 @@ -257,3 +259,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;
}
29 changes: 5 additions & 24 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 @@ -45,8 +47,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 @@ -631,19 +631,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();

clear_siginfo(&info);
info.si_signo = inf->sig;
Expand Down Expand Up @@ -725,16 +716,6 @@ static const struct fault_info fault_info[] = {
{ do_bad, SIGKILL, SI_KERNEL, "unknown 63" },
};

int kvm_handle_guest_sea(phys_addr_t addr, unsigned int esr)
{
int ret = -ENOENT;

if (IS_ENABLED(CONFIG_ACPI_APEI_SEA))
ret = ghes_notify_sea();

return ret;
}

asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
Expand Down

0 comments on commit ab77d4b

Please sign in to comment.