Skip to content

Commit 26fbdf3

Browse files
committed
KVM: arm64: Don't translate FAR if invalid/unsafe
Don't re-walk the page tables if an SEA occurred during the faulting page table walk to avoid taking a fatal exception in the hyp. Additionally, check that FAR_EL2 is valid for SEAs not taken on PTW as the architecture doesn't guarantee it contains the fault VA. Finally, fix up the rest of the abort path by checking for SEAs early and bugging the VM if we get further along with an UNKNOWN fault IPA. Reviewed-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20250402201725.2963645-4-oliver.upton@linux.dev Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
1 parent 1cf3e12 commit 26fbdf3

File tree

6 files changed

+73
-18
lines changed

6 files changed

+73
-18
lines changed

arch/arm64/include/asm/esr.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,28 @@ static inline bool esr_fsc_is_addr_sz_fault(unsigned long esr)
482482
(esr == ESR_ELx_FSC_ADDRSZ_L(-1));
483483
}
484484

485+
static inline bool esr_fsc_is_sea_ttw(unsigned long esr)
486+
{
487+
esr = esr & ESR_ELx_FSC;
488+
489+
return (esr == ESR_ELx_FSC_SEA_TTW(3)) ||
490+
(esr == ESR_ELx_FSC_SEA_TTW(2)) ||
491+
(esr == ESR_ELx_FSC_SEA_TTW(1)) ||
492+
(esr == ESR_ELx_FSC_SEA_TTW(0)) ||
493+
(esr == ESR_ELx_FSC_SEA_TTW(-1));
494+
}
495+
496+
static inline bool esr_fsc_is_secc_ttw(unsigned long esr)
497+
{
498+
esr = esr & ESR_ELx_FSC;
499+
500+
return (esr == ESR_ELx_FSC_SECC_TTW(3)) ||
501+
(esr == ESR_ELx_FSC_SECC_TTW(2)) ||
502+
(esr == ESR_ELx_FSC_SECC_TTW(1)) ||
503+
(esr == ESR_ELx_FSC_SECC_TTW(0)) ||
504+
(esr == ESR_ELx_FSC_SECC_TTW(-1));
505+
}
506+
485507
/* Indicate whether ESR.EC==0x1A is for an ERETAx instruction */
486508
static inline bool esr_iss_is_eretax(unsigned long esr)
487509
{

arch/arm64/include/asm/kvm_emulate.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,9 @@ static __always_inline phys_addr_t kvm_vcpu_get_fault_ipa(const struct kvm_vcpu
307307
{
308308
u64 hpfar = vcpu->arch.fault.hpfar_el2;
309309

310+
if (unlikely(!(hpfar & HPFAR_EL2_NS)))
311+
return INVALID_GPA;
312+
310313
return FIELD_GET(HPFAR_EL2_FIPA, hpfar) << 12;
311314
}
312315

arch/arm64/include/asm/kvm_ras.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* Was this synchronous external abort a RAS notification?
1515
* Returns '0' for errors handled by some RAS subsystem, or -ENOENT.
1616
*/
17-
static inline int kvm_handle_guest_sea(phys_addr_t addr, u64 esr)
17+
static inline int kvm_handle_guest_sea(void)
1818
{
1919
/* apei_claim_sea(NULL) expects to mask interrupts itself */
2020
lockdep_assert_irqs_enabled();

arch/arm64/kvm/hyp/include/hyp/fault.h

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@
1212
#include <asm/kvm_hyp.h>
1313
#include <asm/kvm_mmu.h>
1414

15+
static inline bool __fault_safe_to_translate(u64 esr)
16+
{
17+
u64 fsc = esr & ESR_ELx_FSC;
18+
19+
if (esr_fsc_is_sea_ttw(esr) || esr_fsc_is_secc_ttw(esr))
20+
return false;
21+
22+
return !(fsc == ESR_ELx_FSC_EXTABT && (esr & ESR_ELx_FnV));
23+
}
24+
1525
static inline bool __translate_far_to_hpfar(u64 far, u64 *hpfar)
1626
{
1727
int ret;
@@ -71,17 +81,23 @@ static inline bool __hpfar_valid(u64 esr)
7181

7282
static inline bool __get_fault_info(u64 esr, struct kvm_vcpu_fault_info *fault)
7383
{
74-
u64 hpfar, far;
84+
u64 hpfar;
7585

76-
far = read_sysreg_el2(SYS_FAR);
86+
fault->far_el2 = read_sysreg_el2(SYS_FAR);
87+
fault->hpfar_el2 = 0;
7788

7889
if (__hpfar_valid(esr))
7990
hpfar = read_sysreg(hpfar_el2);
80-
else if (!__translate_far_to_hpfar(far, &hpfar))
91+
else if (unlikely(!__fault_safe_to_translate(esr)))
92+
return true;
93+
else if (!__translate_far_to_hpfar(fault->far_el2, &hpfar))
8194
return false;
8295

83-
fault->far_el2 = far;
84-
fault->hpfar_el2 = hpfar;
96+
/*
97+
* Hijack HPFAR_EL2.NS (RES0 in Non-secure) to indicate a valid
98+
* HPFAR value.
99+
*/
100+
fault->hpfar_el2 = hpfar | HPFAR_EL2_NS;
85101
return true;
86102
}
87103

arch/arm64/kvm/hyp/nvhe/mem_protect.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,14 @@ void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt)
578578
return;
579579
}
580580

581+
582+
/*
583+
* Yikes, we couldn't resolve the fault IPA. This should reinject an
584+
* abort into the host when we figure out how to do that.
585+
*/
586+
BUG_ON(!(fault.hpfar_el2 & HPFAR_EL2_NS));
581587
addr = FIELD_GET(HPFAR_EL2_FIPA, fault.hpfar_el2) << 12;
588+
582589
ret = host_stage2_idmap(addr);
583590
BUG_ON(ret && ret != -EAGAIN);
584591
}

arch/arm64/kvm/mmu.c

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,9 +1794,28 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
17941794
gfn_t gfn;
17951795
int ret, idx;
17961796

1797+
/* Synchronous External Abort? */
1798+
if (kvm_vcpu_abt_issea(vcpu)) {
1799+
/*
1800+
* For RAS the host kernel may handle this abort.
1801+
* There is no need to pass the error into the guest.
1802+
*/
1803+
if (kvm_handle_guest_sea())
1804+
kvm_inject_vabt(vcpu);
1805+
1806+
return 1;
1807+
}
1808+
17971809
esr = kvm_vcpu_get_esr(vcpu);
17981810

1811+
/*
1812+
* The fault IPA should be reliable at this point as we're not dealing
1813+
* with an SEA.
1814+
*/
17991815
ipa = fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
1816+
if (KVM_BUG_ON(ipa == INVALID_GPA, vcpu->kvm))
1817+
return -EFAULT;
1818+
18001819
is_iabt = kvm_vcpu_trap_is_iabt(vcpu);
18011820

18021821
if (esr_fsc_is_translation_fault(esr)) {
@@ -1818,18 +1837,6 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
18181837
}
18191838
}
18201839

1821-
/* Synchronous External Abort? */
1822-
if (kvm_vcpu_abt_issea(vcpu)) {
1823-
/*
1824-
* For RAS the host kernel may handle this abort.
1825-
* There is no need to pass the error into the guest.
1826-
*/
1827-
if (kvm_handle_guest_sea(fault_ipa, kvm_vcpu_get_esr(vcpu)))
1828-
kvm_inject_vabt(vcpu);
1829-
1830-
return 1;
1831-
}
1832-
18331840
trace_kvm_guest_fault(*vcpu_pc(vcpu), kvm_vcpu_get_esr(vcpu),
18341841
kvm_vcpu_get_hfar(vcpu), fault_ipa);
18351842

0 commit comments

Comments
 (0)