arm64: KVM/mm: Move SEA handling behind a single 'claim' interface
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()). Signed-off-by: James Morse <james.morse@arm.com> Acked-by: Marc Zyngier <marc.zyngier@arm.com> Tested-by: Tyler Baicar <tbaicar@codeaurora.org> Acked-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
0db5e02230
commit
d44f1b8dd7
5 changed files with 55 additions and 21 deletions
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include <asm/cputype.h>
|
#include <asm/cputype.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
#include <asm/ptrace.h>
|
||||||
#include <asm/smp_plat.h>
|
#include <asm/smp_plat.h>
|
||||||
#include <asm/tlbflush.h>
|
#include <asm/tlbflush.h>
|
||||||
|
|
||||||
|
@ -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) { }
|
static inline void arch_fix_phys_package_id(int num, u32 slot) { }
|
||||||
void __init acpi_init_cpus(void);
|
void __init acpi_init_cpus(void);
|
||||||
|
int apei_claim_sea(struct pt_regs *regs);
|
||||||
#else
|
#else
|
||||||
static inline void acpi_init_cpus(void) { }
|
static inline void acpi_init_cpus(void) { }
|
||||||
|
static inline int apei_claim_sea(struct pt_regs *regs) { return -ENOENT; }
|
||||||
#endif /* CONFIG_ACPI */
|
#endif /* CONFIG_ACPI */
|
||||||
|
|
||||||
#ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL
|
#ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#define DAIF_PROCCTX 0
|
#define DAIF_PROCCTX 0
|
||||||
#define DAIF_PROCCTX_NOIRQ PSR_I_BIT
|
#define DAIF_PROCCTX_NOIRQ PSR_I_BIT
|
||||||
|
#define DAIF_ERRCTX (PSR_I_BIT | PSR_A_BIT)
|
||||||
|
|
||||||
/* mask/save/unmask/restore all exceptions, including interrupts. */
|
/* mask/save/unmask/restore all exceptions, including interrupts. */
|
||||||
static inline void local_daif_mask(void)
|
static inline void local_daif_mask(void)
|
||||||
|
|
|
@ -4,8 +4,22 @@
|
||||||
#ifndef __ARM64_KVM_RAS_H__
|
#ifndef __ARM64_KVM_RAS_H__
|
||||||
#define __ARM64_KVM_RAS_H__
|
#define __ARM64_KVM_RAS_H__
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
#include <linux/types.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__ */
|
#endif /* __ARM64_KVM_RAS_H__ */
|
||||||
|
|
|
@ -27,8 +27,10 @@
|
||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
#include <linux/serial_core.h>
|
#include <linux/serial_core.h>
|
||||||
|
|
||||||
|
#include <acpi/ghes.h>
|
||||||
#include <asm/cputype.h>
|
#include <asm/cputype.h>
|
||||||
#include <asm/cpu_ops.h>
|
#include <asm/cpu_ops.h>
|
||||||
|
#include <asm/daifflags.h>
|
||||||
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
||||||
#include <asm/smp_plat.h>
|
#include <asm/smp_plat.h>
|
||||||
|
|
||||||
|
@ -256,3 +258,32 @@ pgprot_t __acpi_get_mem_attribute(phys_addr_t addr)
|
||||||
return __pgprot(PROT_NORMAL_NC);
|
return __pgprot(PROT_NORMAL_NC);
|
||||||
return __pgprot(PROT_DEVICE_nGnRnE);
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
#include <linux/extable.h>
|
#include <linux/extable.h>
|
||||||
#include <linux/signal.h>
|
#include <linux/signal.h>
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
#include <linux/preempt.h>
|
#include <linux/preempt.h>
|
||||||
#include <linux/hugetlb.h>
|
#include <linux/hugetlb.h>
|
||||||
|
|
||||||
|
#include <asm/acpi.h>
|
||||||
#include <asm/bug.h>
|
#include <asm/bug.h>
|
||||||
#include <asm/cmpxchg.h>
|
#include <asm/cmpxchg.h>
|
||||||
#include <asm/cpufeature.h>
|
#include <asm/cpufeature.h>
|
||||||
|
@ -47,8 +49,6 @@
|
||||||
#include <asm/tlbflush.h>
|
#include <asm/tlbflush.h>
|
||||||
#include <asm/traps.h>
|
#include <asm/traps.h>
|
||||||
|
|
||||||
#include <acpi/ghes.h>
|
|
||||||
|
|
||||||
struct fault_info {
|
struct fault_info {
|
||||||
int (*fn)(unsigned long addr, unsigned int esr,
|
int (*fn)(unsigned long addr, unsigned int esr,
|
||||||
struct pt_regs *regs);
|
struct pt_regs *regs);
|
||||||
|
@ -643,19 +643,10 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
|
||||||
inf = esr_to_fault_info(esr);
|
inf = esr_to_fault_info(esr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Synchronous aborts may interrupt code which had interrupts masked.
|
* Return value ignored as we rely on signal merging.
|
||||||
* Before calling out into the wider kernel tell the interested
|
* Future patches will make this more robust.
|
||||||
* subsystems.
|
|
||||||
*/
|
*/
|
||||||
if (IS_ENABLED(CONFIG_ACPI_APEI_SEA)) {
|
apei_claim_sea(regs);
|
||||||
if (interrupts_enabled(regs))
|
|
||||||
nmi_enter();
|
|
||||||
|
|
||||||
ghes_notify_sea();
|
|
||||||
|
|
||||||
if (interrupts_enabled(regs))
|
|
||||||
nmi_exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (esr & ESR_ELx_FnV)
|
if (esr & ESR_ELx_FnV)
|
||||||
siaddr = NULL;
|
siaddr = NULL;
|
||||||
|
@ -733,11 +724,6 @@ static const struct fault_info fault_info[] = {
|
||||||
{ do_bad, SIGKILL, SI_KERNEL, "unknown 63" },
|
{ do_bad, SIGKILL, SI_KERNEL, "unknown 63" },
|
||||||
};
|
};
|
||||||
|
|
||||||
int kvm_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,
|
asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
|
||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue