Skip to content

Commit 069a05e

Browse files
author
Marc Zyngier
committed
KVM: arm64: nv: Handle VNCR_EL2-triggered faults
As VNCR_EL2.BADDR contains a VA, it is bound to trigger faults. These faults can have multiple source: - We haven't mapped anything on the host: we need to compute the resulting translation, populate a TLB, and eventually map the corresponding page - The permissions are out of whack: we need to tell the guest about this state of affairs Note that the kernel doesn't support S1POE for itself yet, so the particular case of a VNCR page mapped with no permissions or with write-only permissions is not correctly handled yet. Reviewed-by: Oliver Upton <oliver.upton@linux.dev> Link: https://lore.kernel.org/r/20250514103501.2225951-10-maz@kernel.org Signed-off-by: Marc Zyngier <maz@kernel.org>
1 parent 6fb7573 commit 069a05e

File tree

5 files changed

+164
-0
lines changed

5 files changed

+164
-0
lines changed

arch/arm64/include/asm/esr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@
9999
#define ESR_ELx_AET_CE (UL(6) << ESR_ELx_AET_SHIFT)
100100

101101
/* Shared ISS field definitions for Data/Instruction aborts */
102+
#define ESR_ELx_VNCR_SHIFT (13)
103+
#define ESR_ELx_VNCR (UL(1) << ESR_ELx_VNCR_SHIFT)
102104
#define ESR_ELx_SET_SHIFT (11)
103105
#define ESR_ELx_SET_MASK (UL(3) << ESR_ELx_SET_SHIFT)
104106
#define ESR_ELx_FnV_SHIFT (10)

arch/arm64/include/asm/kvm_host.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#define KVM_REQ_RESYNC_PMU_EL0 KVM_ARCH_REQ(7)
5454
#define KVM_REQ_NESTED_S2_UNMAP KVM_ARCH_REQ(8)
5555
#define KVM_REQ_GUEST_HYP_IRQ_PENDING KVM_ARCH_REQ(9)
56+
#define KVM_REQ_MAP_L1_VNCR_EL2 KVM_ARCH_REQ(10)
5657

5758
#define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
5859
KVM_DIRTY_LOG_INITIALLY_SET)

arch/arm64/include/asm/kvm_nested.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,5 +335,6 @@ int __kvm_translate_va(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,
335335

336336
/* VNCR management */
337337
int kvm_vcpu_allocate_vncr_tlb(struct kvm_vcpu *vcpu);
338+
int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu);
338339

339340
#endif /* __ARM64_KVM_NESTED_H */

arch/arm64/kvm/handle_exit.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ static exit_handle_fn arm_exit_handlers[] = {
317317
[ESR_ELx_EC_ERET] = kvm_handle_eret,
318318
[ESR_ELx_EC_IABT_LOW] = kvm_handle_guest_abort,
319319
[ESR_ELx_EC_DABT_LOW] = kvm_handle_guest_abort,
320+
[ESR_ELx_EC_DABT_CUR] = kvm_handle_vncr_abort,
320321
[ESR_ELx_EC_SOFTSTP_LOW]= kvm_handle_guest_debug,
321322
[ESR_ELx_EC_WATCHPT_LOW]= kvm_handle_guest_debug,
322323
[ESR_ELx_EC_BREAKPT_LOW]= kvm_handle_guest_debug,

arch/arm64/kvm/nested.c

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,165 @@ int kvm_vcpu_allocate_vncr_tlb(struct kvm_vcpu *vcpu)
883883
return 0;
884884
}
885885

886+
static u64 read_vncr_el2(struct kvm_vcpu *vcpu)
887+
{
888+
return (u64)sign_extend64(__vcpu_sys_reg(vcpu, VNCR_EL2), 48);
889+
}
890+
891+
static int kvm_translate_vncr(struct kvm_vcpu *vcpu)
892+
{
893+
bool write_fault, writable;
894+
unsigned long mmu_seq;
895+
struct vncr_tlb *vt;
896+
struct page *page;
897+
u64 va, pfn, gfn;
898+
int ret;
899+
900+
vt = vcpu->arch.vncr_tlb;
901+
902+
vt->wi = (struct s1_walk_info) {
903+
.regime = TR_EL20,
904+
.as_el0 = false,
905+
.pan = false,
906+
};
907+
vt->wr = (struct s1_walk_result){};
908+
vt->valid = false;
909+
910+
guard(srcu)(&vcpu->kvm->srcu);
911+
912+
va = read_vncr_el2(vcpu);
913+
914+
ret = __kvm_translate_va(vcpu, &vt->wi, &vt->wr, va);
915+
if (ret)
916+
return ret;
917+
918+
write_fault = kvm_is_write_fault(vcpu);
919+
920+
mmu_seq = vcpu->kvm->mmu_invalidate_seq;
921+
smp_rmb();
922+
923+
gfn = vt->wr.pa >> PAGE_SHIFT;
924+
pfn = kvm_faultin_pfn(vcpu, gfn, write_fault, &writable, &page);
925+
if (is_error_noslot_pfn(pfn) || (write_fault && !writable))
926+
return -EFAULT;
927+
928+
scoped_guard(write_lock, &vcpu->kvm->mmu_lock) {
929+
if (mmu_invalidate_retry(vcpu->kvm, mmu_seq))
930+
return -EAGAIN;
931+
932+
vt->gva = va;
933+
vt->hpa = pfn << PAGE_SHIFT;
934+
vt->valid = true;
935+
vt->cpu = -1;
936+
937+
kvm_make_request(KVM_REQ_MAP_L1_VNCR_EL2, vcpu);
938+
}
939+
940+
kvm_release_faultin_page(vcpu->kvm, page, false, vt->wr.pw);
941+
if (vt->wr.pw)
942+
mark_page_dirty(vcpu->kvm, gfn);
943+
944+
return 0;
945+
}
946+
947+
static void inject_vncr_perm(struct kvm_vcpu *vcpu)
948+
{
949+
struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
950+
u64 esr = kvm_vcpu_get_esr(vcpu);
951+
952+
/* Adjust the fault level to reflect that of the guest's */
953+
esr &= ~ESR_ELx_FSC;
954+
esr |= FIELD_PREP(ESR_ELx_FSC,
955+
ESR_ELx_FSC_PERM_L(vt->wr.level));
956+
957+
kvm_inject_nested_sync(vcpu, esr);
958+
}
959+
960+
static bool kvm_vncr_tlb_lookup(struct kvm_vcpu *vcpu)
961+
{
962+
struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
963+
964+
lockdep_assert_held_read(&vcpu->kvm->mmu_lock);
965+
966+
if (!vt->valid)
967+
return false;
968+
969+
if (read_vncr_el2(vcpu) != vt->gva)
970+
return false;
971+
972+
if (vt->wr.nG) {
973+
u64 tcr = vcpu_read_sys_reg(vcpu, TCR_EL2);
974+
u64 ttbr = ((tcr & TCR_A1) ?
975+
vcpu_read_sys_reg(vcpu, TTBR1_EL2) :
976+
vcpu_read_sys_reg(vcpu, TTBR0_EL2));
977+
u16 asid;
978+
979+
asid = FIELD_GET(TTBR_ASID_MASK, ttbr);
980+
if (!kvm_has_feat_enum(vcpu->kvm, ID_AA64MMFR0_EL1, ASIDBITS, 16) ||
981+
!(tcr & TCR_ASID16))
982+
asid &= GENMASK(7, 0);
983+
984+
return asid != vt->wr.asid;
985+
}
986+
987+
return true;
988+
}
989+
990+
int kvm_handle_vncr_abort(struct kvm_vcpu *vcpu)
991+
{
992+
struct vncr_tlb *vt = vcpu->arch.vncr_tlb;
993+
u64 esr = kvm_vcpu_get_esr(vcpu);
994+
995+
BUG_ON(!(esr & ESR_ELx_VNCR_SHIFT));
996+
997+
if (esr_fsc_is_permission_fault(esr)) {
998+
inject_vncr_perm(vcpu);
999+
} else if (esr_fsc_is_translation_fault(esr)) {
1000+
bool valid;
1001+
int ret;
1002+
1003+
scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
1004+
valid = kvm_vncr_tlb_lookup(vcpu);
1005+
1006+
if (!valid)
1007+
ret = kvm_translate_vncr(vcpu);
1008+
else
1009+
ret = -EPERM;
1010+
1011+
switch (ret) {
1012+
case -EAGAIN:
1013+
case -ENOMEM:
1014+
/* Let's try again... */
1015+
break;
1016+
case -EFAULT:
1017+
case -EINVAL:
1018+
case -ENOENT:
1019+
case -EACCES:
1020+
/*
1021+
* Translation failed, inject the corresponding
1022+
* exception back to EL2.
1023+
*/
1024+
BUG_ON(!vt->wr.failed);
1025+
1026+
esr &= ~ESR_ELx_FSC;
1027+
esr |= FIELD_PREP(ESR_ELx_FSC, vt->wr.fst);
1028+
1029+
kvm_inject_nested_sync(vcpu, esr);
1030+
break;
1031+
case -EPERM:
1032+
/* Hack to deal with POE until we get kernel support */
1033+
inject_vncr_perm(vcpu);
1034+
break;
1035+
case 0:
1036+
break;
1037+
}
1038+
} else {
1039+
WARN_ONCE(1, "Unhandled VNCR abort, ESR=%llx\n", esr);
1040+
}
1041+
1042+
return 1;
1043+
}
1044+
8861045
/*
8871046
* Our emulated CPU doesn't support all the possible features. For the
8881047
* sake of simplicity (and probably mental sanity), wipe out a number

0 commit comments

Comments
 (0)