aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTyler Baicar <tbaicar@codeaurora.org>2017-06-21 14:17:14 -0400
committerWill Deacon <will.deacon@arm.com>2017-06-22 13:22:05 -0400
commit621f48e40ee9b0100a802531069166d7d94796e0 (patch)
tree3411689c2307e6f82325354c85c8940cf9aec483
parente9279e83ad1f4b5af541a522a81888f828210b40 (diff)
arm/arm64: KVM: add guest SEA support
Currently external aborts are unsupported by the guest abort handling. Add handling for SEAs so that the host kernel reports SEAs which occur in the guest kernel. When an SEA occurs in the guest kernel, the guest exits and is routed to kvm_handle_guest_abort(). Prior to this patch, a print message of an unsupported FSC would be printed and nothing else would happen. With this patch, the code gets routed to the APEI handling of SEAs in the host kernel to report the SEA information. Signed-off-by: Tyler Baicar <tbaicar@codeaurora.org> Acked-by: Catalin Marinas <catalin.marinas@arm.com> Acked-by: Marc Zyngier <marc.zyngier@arm.com> Acked-by: Christoffer Dall <cdall@linaro.org> Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--arch/arm/include/asm/kvm_arm.h10
-rw-r--r--arch/arm/include/asm/system_misc.h5
-rw-r--r--arch/arm64/include/asm/kvm_arm.h10
-rw-r--r--arch/arm64/include/asm/system_misc.h2
-rw-r--r--arch/arm64/mm/fault.c22
-rw-r--r--drivers/acpi/apei/ghes.c17
-rw-r--r--include/acpi/ghes.h2
-rw-r--r--virt/kvm/arm/mmu.c36
8 files changed, 92 insertions, 12 deletions
diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h
index a3f0b3d50089..ebf020b02bc8 100644
--- a/arch/arm/include/asm/kvm_arm.h
+++ b/arch/arm/include/asm/kvm_arm.h
@@ -187,6 +187,16 @@
187#define FSC_FAULT (0x04) 187#define FSC_FAULT (0x04)
188#define FSC_ACCESS (0x08) 188#define FSC_ACCESS (0x08)
189#define FSC_PERM (0x0c) 189#define FSC_PERM (0x0c)
190#define FSC_SEA (0x10)
191#define FSC_SEA_TTW0 (0x14)
192#define FSC_SEA_TTW1 (0x15)
193#define FSC_SEA_TTW2 (0x16)
194#define FSC_SEA_TTW3 (0x17)
195#define FSC_SECC (0x18)
196#define FSC_SECC_TTW0 (0x1c)
197#define FSC_SECC_TTW1 (0x1d)
198#define FSC_SECC_TTW2 (0x1e)
199#define FSC_SECC_TTW3 (0x1f)
190 200
191/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */ 201/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
192#define HPFAR_MASK (~0xf) 202#define HPFAR_MASK (~0xf)
diff --git a/arch/arm/include/asm/system_misc.h b/arch/arm/include/asm/system_misc.h
index a3d61ad984af..8c4a89f5ce7d 100644
--- a/arch/arm/include/asm/system_misc.h
+++ b/arch/arm/include/asm/system_misc.h
@@ -22,6 +22,11 @@ extern void (*arm_pm_idle)(void);
22 22
23extern unsigned int user_debug; 23extern unsigned int user_debug;
24 24
25static inline int handle_guest_sea(phys_addr_t addr, unsigned int esr)
26{
27 return -1;
28}
29
25#endif /* !__ASSEMBLY__ */ 30#endif /* !__ASSEMBLY__ */
26 31
27#endif /* __ASM_ARM_SYSTEM_MISC_H */ 32#endif /* __ASM_ARM_SYSTEM_MISC_H */
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 6e99978e83bd..61d694c2eae5 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -204,6 +204,16 @@
204#define FSC_FAULT ESR_ELx_FSC_FAULT 204#define FSC_FAULT ESR_ELx_FSC_FAULT
205#define FSC_ACCESS ESR_ELx_FSC_ACCESS 205#define FSC_ACCESS ESR_ELx_FSC_ACCESS
206#define FSC_PERM ESR_ELx_FSC_PERM 206#define FSC_PERM ESR_ELx_FSC_PERM
207#define FSC_SEA ESR_ELx_FSC_EXTABT
208#define FSC_SEA_TTW0 (0x14)
209#define FSC_SEA_TTW1 (0x15)
210#define FSC_SEA_TTW2 (0x16)
211#define FSC_SEA_TTW3 (0x17)
212#define FSC_SECC (0x18)
213#define FSC_SECC_TTW0 (0x1c)
214#define FSC_SECC_TTW1 (0x1d)
215#define FSC_SECC_TTW2 (0x1e)
216#define FSC_SECC_TTW3 (0x1f)
207 217
208/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */ 218/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
209#define HPFAR_MASK (~UL(0xf)) 219#define HPFAR_MASK (~UL(0xf))
diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
index bc812435bc76..95aa4423247d 100644
--- a/arch/arm64/include/asm/system_misc.h
+++ b/arch/arm64/include/asm/system_misc.h
@@ -56,6 +56,8 @@ extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
56 __show_ratelimited; \ 56 __show_ratelimited; \
57}) 57})
58 58
59int handle_guest_sea(phys_addr_t addr, unsigned int esr);
60
59#endif /* __ASSEMBLY__ */ 61#endif /* __ASSEMBLY__ */
60 62
61#endif /* __ASM_SYSTEM_MISC_H */ 63#endif /* __ASM_SYSTEM_MISC_H */
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 07481af15fd7..a8c9f2db20ba 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -532,6 +532,7 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
532{ 532{
533 struct siginfo info; 533 struct siginfo info;
534 const struct fault_info *inf; 534 const struct fault_info *inf;
535 int ret = 0;
535 536
536 inf = esr_to_fault_info(esr); 537 inf = esr_to_fault_info(esr);
537 pr_err("Synchronous External Abort: %s (0x%08x) at 0x%016lx\n", 538 pr_err("Synchronous External Abort: %s (0x%08x) at 0x%016lx\n",
@@ -546,7 +547,7 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
546 if (interrupts_enabled(regs)) 547 if (interrupts_enabled(regs))
547 nmi_enter(); 548 nmi_enter();
548 549
549 ghes_notify_sea(); 550 ret = ghes_notify_sea();
550 551
551 if (interrupts_enabled(regs)) 552 if (interrupts_enabled(regs))
552 nmi_exit(); 553 nmi_exit();
@@ -561,7 +562,7 @@ static int do_sea(unsigned long addr, unsigned int esr, struct pt_regs *regs)
561 info.si_addr = (void __user *)addr; 562 info.si_addr = (void __user *)addr;
562 arm64_notify_die("", regs, &info, esr); 563 arm64_notify_die("", regs, &info, esr);
563 564
564 return 0; 565 return ret;
565} 566}
566 567
567static const struct fault_info fault_info[] = { 568static const struct fault_info fault_info[] = {
@@ -632,6 +633,23 @@ static const struct fault_info fault_info[] = {
632}; 633};
633 634
634/* 635/*
636 * Handle Synchronous External Aborts that occur in a guest kernel.
637 *
638 * The return value will be zero if the SEA was successfully handled
639 * and non-zero if there was an error processing the error or there was
640 * no error to process.
641 */
642int handle_guest_sea(phys_addr_t addr, unsigned int esr)
643{
644 int ret = -ENOENT;
645
646 if (IS_ENABLED(CONFIG_ACPI_APEI_SEA))
647 ret = ghes_notify_sea();
648
649 return ret;
650}
651
652/*
635 * Dispatch a data abort to the relevant handler. 653 * Dispatch a data abort to the relevant handler.
636 */ 654 */
637asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, 655asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 5073d035bb6e..bc717bdf50f1 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -816,17 +816,22 @@ static struct notifier_block ghes_notifier_sci = {
816#ifdef CONFIG_ACPI_APEI_SEA 816#ifdef CONFIG_ACPI_APEI_SEA
817static LIST_HEAD(ghes_sea); 817static LIST_HEAD(ghes_sea);
818 818
819void ghes_notify_sea(void) 819/*
820 * Return 0 only if one of the SEA error sources successfully reported an error
821 * record sent from the firmware.
822 */
823int ghes_notify_sea(void)
820{ 824{
821 struct ghes *ghes; 825 struct ghes *ghes;
826 int ret = -ENOENT;
822 827
823 /* 828 rcu_read_lock();
824 * synchronize_rcu() will wait for nmi_exit(), so no need to
825 * rcu_read_lock().
826 */
827 list_for_each_entry_rcu(ghes, &ghes_sea, list) { 829 list_for_each_entry_rcu(ghes, &ghes_sea, list) {
828 ghes_proc(ghes); 830 if (!ghes_proc(ghes))
831 ret = 0;
829 } 832 }
833 rcu_read_unlock();
834 return ret;
830} 835}
831 836
832static void ghes_sea_add(struct ghes *ghes) 837static void ghes_sea_add(struct ghes *ghes)
diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h
index 95305aeb13ff..9f26e01186ae 100644
--- a/include/acpi/ghes.h
+++ b/include/acpi/ghes.h
@@ -113,6 +113,6 @@ static inline void *acpi_hest_get_next(struct acpi_hest_generic_data *gdata)
113 return (void *)(gdata) + acpi_hest_get_record_size(gdata); 113 return (void *)(gdata) + acpi_hest_get_record_size(gdata);
114} 114}
115 115
116void ghes_notify_sea(void); 116int ghes_notify_sea(void);
117 117
118#endif /* GHES_H */ 118#endif /* GHES_H */
diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c
index a2d63247d1bb..bde0cdd60997 100644
--- a/virt/kvm/arm/mmu.c
+++ b/virt/kvm/arm/mmu.c
@@ -29,6 +29,7 @@
29#include <asm/kvm_asm.h> 29#include <asm/kvm_asm.h>
30#include <asm/kvm_emulate.h> 30#include <asm/kvm_emulate.h>
31#include <asm/virt.h> 31#include <asm/virt.h>
32#include <asm/system_misc.h>
32 33
33#include "trace.h" 34#include "trace.h"
34 35
@@ -1427,6 +1428,25 @@ out:
1427 kvm_set_pfn_accessed(pfn); 1428 kvm_set_pfn_accessed(pfn);
1428} 1429}
1429 1430
1431static bool is_abort_sea(unsigned long fault_status)
1432{
1433 switch (fault_status) {
1434 case FSC_SEA:
1435 case FSC_SEA_TTW0:
1436 case FSC_SEA_TTW1:
1437 case FSC_SEA_TTW2:
1438 case FSC_SEA_TTW3:
1439 case FSC_SECC:
1440 case FSC_SECC_TTW0:
1441 case FSC_SECC_TTW1:
1442 case FSC_SECC_TTW2:
1443 case FSC_SECC_TTW3:
1444 return true;
1445 default:
1446 return false;
1447 }
1448}
1449
1430/** 1450/**
1431 * kvm_handle_guest_abort - handles all 2nd stage aborts 1451 * kvm_handle_guest_abort - handles all 2nd stage aborts
1432 * @vcpu: the VCPU pointer 1452 * @vcpu: the VCPU pointer
@@ -1449,19 +1469,29 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
1449 gfn_t gfn; 1469 gfn_t gfn;
1450 int ret, idx; 1470 int ret, idx;
1451 1471
1472 fault_status = kvm_vcpu_trap_get_fault_type(vcpu);
1473
1474 fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
1475
1476 /*
1477 * The host kernel will handle the synchronous external abort. There
1478 * is no need to pass the error into the guest.
1479 */
1480 if (is_abort_sea(fault_status)) {
1481 if (!handle_guest_sea(fault_ipa, kvm_vcpu_get_hsr(vcpu)))
1482 return 1;
1483 }
1484
1452 is_iabt = kvm_vcpu_trap_is_iabt(vcpu); 1485 is_iabt = kvm_vcpu_trap_is_iabt(vcpu);
1453 if (unlikely(!is_iabt && kvm_vcpu_dabt_isextabt(vcpu))) { 1486 if (unlikely(!is_iabt && kvm_vcpu_dabt_isextabt(vcpu))) {
1454 kvm_inject_vabt(vcpu); 1487 kvm_inject_vabt(vcpu);
1455 return 1; 1488 return 1;
1456 } 1489 }
1457 1490
1458 fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
1459
1460 trace_kvm_guest_fault(*vcpu_pc(vcpu), kvm_vcpu_get_hsr(vcpu), 1491 trace_kvm_guest_fault(*vcpu_pc(vcpu), kvm_vcpu_get_hsr(vcpu),
1461 kvm_vcpu_get_hfar(vcpu), fault_ipa); 1492 kvm_vcpu_get_hfar(vcpu), fault_ipa);
1462 1493
1463 /* Check the stage-2 fault is trans. fault or write fault */ 1494 /* Check the stage-2 fault is trans. fault or write fault */
1464 fault_status = kvm_vcpu_trap_get_fault_type(vcpu);
1465 if (fault_status != FSC_FAULT && fault_status != FSC_PERM && 1495 if (fault_status != FSC_FAULT && fault_status != FSC_PERM &&
1466 fault_status != FSC_ACCESS) { 1496 fault_status != FSC_ACCESS) {
1467 kvm_err("Unsupported FSC: EC=%#x xFSC=%#lx ESR_EL2=%#lx\n", 1497 kvm_err("Unsupported FSC: EC=%#x xFSC=%#lx ESR_EL2=%#lx\n",