KVM: arm64: nv: MMU fixes for 7.2
Subsystems (3)
Oliver Upton
maintainercover KVM: arm64: nv: MMU fixes for 7.2
I've accumulated a small pile of nested MMU fixes as part of a larger series, flushing those out as a separate thing that can land for 7.2. As is the trend these days, some of these bugs were found by sashiko review and others were found in the process of fixing the AI bug reports.
Oliver Upton (5):
KVM: arm64: Don't leak PFN when kvm_translate_vncr() races MMU
notifier
KVM: arm64: nv: Fully update VNCR fixmap state in kvm_translate_vncr()
KVM: arm64: nv: Inject SEA TTW when desc update can't write to GPA
KVM: arm64: Restart instruction upon race in __kvm_at_s12()
KVM: arm64: nv: Restart stage-1 walk if stage-2 desc update fails
arch/arm64/kvm/at.c | 14 ++++++++++++-- arch/arm64/kvm/nested.c | 38 ++++++++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 14 deletions(-)
base-commit: 5d6919055dec134de3c40167a490f33c74c12581
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH 1/5 KVM: arm64: Don't leak PFN when kvm_translate_vncr() races MMU notifier
In the case that kvm_translate_vncr() races with an MMU notifier the early return does not release a reference on the faulted in PFN. Add the necessary call to kvm_release_faultin_page() for the unused PFN.
--- arch/arm64/kvm/nested.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
arch/arm64/kvm/nested.c +3 -1
@@ -1326,8 +1326,10 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem) } scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { -if (mmu_invalidate_retry (vcpu->kvm, mmu_seq)) +if (mmu_invalidate_retry (vcpu->kvm, mmu_seq)) { +kvm_release_faultin_page (vcpu->kvm, page, true, false);return -EAGAIN ; + } vt->gva = va; vt->hpa = pfn <<PAGE_SHIFT ;
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH 2/5 KVM: arm64: nv: Fully update VNCR fixmap state in kvm_translate_vncr()
In reply to Oliver Uptonkvm_translate_vncr() first invalidates the pseudo-TLB entry and corresponding fixmap in anticipation of installing a new translation.
While the fixmap invalidation does clear the mapping from host stage-1, it does not clear the L1_VNCR_MAPPED flag. Depending on the state of the VNCR TLB at vcpu_put(), this could potentially precipitate a BUG_ON() if vt->cpu is reset.
Share a helper with kvm_vcpu_put_hw_mmu(), ensuring that KVM's view of the VNCR fixmap is in sync with the state of the VNCR TLB. Give it a slightly verbose name to make it obvious that it is meant to be used local to a CPU, unlike other VNCR TLB maintenance.
--- arch/arm64/kvm/nested.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-)
arch/arm64/kvm/nested.c +17 -10
@@ -797,18 +797,24 @@ void kvm_vcpu_load_hw_mmu(struct kvm_vcpu *vcpu) } } +static void this_cpu_reset_vncr_fixmap (struct kvm_vcpu *vcpu) +{ +if (!host_data_test_flag (L1_VNCR_MAPPED )) +return ; + +BUG_ON (vcpu->arch.vncr_tlb->cpu !=smp_processor_id ()); +BUG_ON (is_hyp_ctxt (vcpu)); + +clear_fixmap (vncr_fixmap (vcpu->arch.vncr_tlb->cpu)); + vcpu->arch.vncr_tlb->cpu =-1 ; +host_data_clear_flag (L1_VNCR_MAPPED ); +atomic_dec (&vcpu->kvm->arch.vncr_map_count); +} +void kvm_vcpu_put_hw_mmu (struct kvm_vcpu *vcpu) {/* Unconditionally drop the VNCR mapping if we have one */ -if (host_data_test_flag (L1_VNCR_MAPPED )) { -BUG_ON (vcpu->arch.vncr_tlb->cpu !=smp_processor_id ()); -BUG_ON (is_hyp_ctxt (vcpu)); - -clear_fixmap (vncr_fixmap (vcpu->arch.vncr_tlb->cpu)); - vcpu->arch.vncr_tlb->cpu =-1 ; -host_data_clear_flag (L1_VNCR_MAPPED ); -atomic_dec (&vcpu->kvm->arch.vncr_map_count); - } +this_cpu_reset_vncr_fixmap (vcpu);/* * Keep a reference on the associated stage-2 MMU if the vCPU is
@@ -1282,7 +1288,8 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)* We also prepare the next walk wilst we're at it. */ scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { -invalidate_vncr (vt); +this_cpu_reset_vncr_fixmap (vcpu); + vt->valid = false; vt->wi = (struct s1_walk_info ) { .regime =TR_EL20 ,
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH 3/5 KVM: arm64: nv: Inject SEA TTW when desc update can't write to GPA
In reply to Oliver UptonSimilar to the handling of descriptor reads, inject an SEA during TTW when the descriptor access fails for reasons other than a race, such as a read-only memslot or a bad HVA.
--- arch/arm64/kvm/at.c | 6 +++++- arch/arm64/kvm/nested.c | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-)
arch/arm64/kvm/at.c +5 -1
@@ -521,8 +521,12 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi, } ret =kvm_swap_s1_desc (vcpu, ipa, desc, new_desc, wi); -if (ret) +if (ret == -EAGAIN )return ret; +if (ret) { +fail_s1_walk (wr,ESR_ELx_FSC_SEA_TTW (level), false); +return ret; + } desc = new_desc; }
arch/arm64/kvm/nested.c +6 -1
@@ -352,8 +352,13 @@ static int walk_nested_s2_pgd(struct kvm_vcpu *vcpu, phys_addr_t ipa,if (new_desc != desc) { ret =swap_guest_s2_desc (vcpu, paddr, desc, new_desc, wi); -if (ret) +if (ret == -EAGAIN )return ret; +if (ret) { + out->esr =ESR_ELx_FSC_SEA_TTW (level); + out->desc = desc; +return 1 ; + } desc = new_desc; }
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH 4/5 KVM: arm64: Restart instruction upon race in __kvm_at_s12()
In reply to Oliver Upton__kvm_at_s*() are expected to return -EAGAIN if the page table walk raced with a concurrent update to a page table descriptor, which is interpreted as a signal to restart the trapping instruction.
While this mostly works, __kvm_at_s12() silently eats the return from __kvm_at_s1e01() and consumes an uninitialized PAR value. Propagate the nonzero return instead.
--- arch/arm64/kvm/at.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
arch/arm64/kvm/at.c +4 -1
@@ -1557,7 +1557,10 @@ int __kvm_at_s12(struct kvm_vcpu *vcpu, u32 op, u64 vaddr)return 0 ; } -__kvm_at_s1e01 (vcpu, op, vaddr); + ret =__kvm_at_s1e01 (vcpu, op, vaddr); +if (ret) +return ret; + par =vcpu_read_sys_reg (vcpu,PAR_EL1 );if (par &SYS_PAR_EL1_F )return 0 ;
Signature · 1 line
2.47.3
Oliver Upton
maintainerPATCH 5/5 KVM: arm64: nv: Restart stage-1 walk if stage-2 desc update fails
In reply to Oliver Uptonkvm_walk_nested_s2() returns -EAGAIN as an indication that an underlying descriptor update fails due to a race. The expectation is that the caller restart translation, yet walk_s1() actually synthesizes an abort.
Propagate the -EAGAIN return out of walk_s1(), relying on callers to restart the translation fetch.
--- arch/arm64/kvm/at.c | 3 +++ 1 file changed, 3 insertions(+)
arch/arm64/kvm/at.c +3 -0
@@ -423,6 +423,9 @@ static int walk_s1(struct kvm_vcpu *vcpu, struct s1_walk_info *wi,if (wi->s2) { ret =kvm_walk_nested_s2 (vcpu, ipa, &s2_trans); +if (ret == -EAGAIN ) +return ret; +if (ret) { fail_s1_walk(wr, (s2_trans.esr & ~ESR_ELx_FSC_LEVEL) | level,
Signature · 1 line
2.47.3