[PATCH 35/45] KVM: x86/mmu: Propagate errors from nested GPA translation
From: Paolo Bonzini
Date: Mon Nov 02 2026 - 13:43:06 EST
The nested translation callbacks currently discard errors returned
by gva_to_gpa(). Adjust translate_nested_gpa()'s calling convention
similar to what was done to gva_to_gpa(), so that errors can be
propagated from page walk as well as from users that directly care
about L2 GPAs.
Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx>
---
arch/x86/include/asm/kvm_host.h | 8 ++++----
arch/x86/kvm/hyperv.c | 6 ++++--
arch/x86/kvm/mmu.h | 18 ++++++++++--------
arch/x86/kvm/mmu/mmu.c | 5 ++---
arch/x86/kvm/mmu/paging_tmpl.h | 12 ++++++------
arch/x86/kvm/regs.c | 8 +++++---
arch/x86/kvm/svm/nested.c | 18 +++++++++---------
arch/x86/kvm/vmx/nested.c | 18 +++++++++---------
arch/x86/kvm/x86.c | 15 +++++++++++----
9 files changed, 60 insertions(+), 48 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 99f9c9269f2f..7fbcf60e56cf 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1780,10 +1780,10 @@ struct kvm_x86_nested_ops {
struct kvm_nested_state *kvm_state);
bool (*get_nested_state_pages)(struct kvm_vcpu *vcpu);
int (*write_log_dirty)(struct kvm_vcpu *vcpu, gpa_t l2_gpa);
- gpa_t (*translate_nested_gpa)(struct kvm_vcpu *vcpu, gpa_t gpa,
- u64 access,
- struct x86_exception *exception,
- u64 pte_access);
+ int (*translate_nested_gpa)(struct kvm_vcpu *vcpu, gpa_t gpa,
+ u64 access, gpa_t *translated_gpa,
+ struct x86_exception *exception,
+ u64 pte_access);
int (*enable_evmcs)(struct kvm_vcpu *vcpu,
uint16_t *vmcs_version);
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index ee5189815a06..090955541518 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -2119,8 +2119,10 @@ static s64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
/* Slow direct hypercalls from L2 provide a nested GPA. */
if (!hc->fast) {
- hc->ingpa = kvm_translate_gpa(vcpu, &vcpu->arch.gva_walk, hc->ingpa,
- PFERR_GUEST_FINAL_MASK, NULL, 0);
+ r = kvm_translate_gpa(vcpu, &vcpu->arch.gva_walk, hc->ingpa,
+ PFERR_GUEST_FINAL_MASK, &hc->ingpa, NULL, 0);
+ if (r)
+ return r;
if (unlikely(hc->ingpa == INVALID_GPA))
return HV_STATUS_INVALID_HYPERCALL_INPUT;
}
diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h
index 224f4c1a957e..40dcbcbae173 100644
--- a/arch/x86/kvm/mmu.h
+++ b/arch/x86/kvm/mmu.h
@@ -377,15 +377,17 @@ static inline bool mmu_is_nested(struct kvm_vcpu *vcpu)
return vcpu->arch.mmu == &vcpu->arch.guest_mmu;
}
-static inline gpa_t kvm_translate_gpa(struct kvm_vcpu *vcpu,
- struct kvm_pagewalk *w,
- gpa_t gpa, u64 access,
- struct x86_exception *exception,
- u64 pte_access)
+static inline int kvm_translate_gpa(struct kvm_vcpu *vcpu,
+ struct kvm_pagewalk *w,
+ gpa_t gpa, u64 access, gpa_t *translated_gpa,
+ struct x86_exception *exception,
+ u64 pte_access)
{
- if (!mmu_is_nested(vcpu) || w == &vcpu->arch.ngpa_walk)
- return gpa;
- return kvm_nested_call(translate_nested_gpa)(vcpu, gpa, access,
+ if (!mmu_is_nested(vcpu) || w == &vcpu->arch.ngpa_walk) {
+ *translated_gpa = gpa;
+ return 0;
+ }
+ return kvm_nested_call(translate_nested_gpa)(vcpu, gpa, access, translated_gpa,
exception, pte_access);
}
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index c827f2e1f3d1..9567c109306b 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -4474,9 +4474,8 @@ static int nonpaging_gva_to_gpa(struct kvm_vcpu *vcpu, struct kvm_pagewalk *w,
* user-mode address if CR0.PG=0. Therefore *include* ACC_USER_MASK in
* the last argument to kvm_translate_gpa (which NPT does not use).
*/
- *gpa = kvm_translate_gpa(vcpu, w, vaddr, access | PFERR_GUEST_FINAL_MASK,
- exception, ACC_ALL);
- return 0;
+ return kvm_translate_gpa(vcpu, w, vaddr, access | PFERR_GUEST_FINAL_MASK,
+ gpa, exception, ACC_ALL);
}
static bool mmio_info_in_cache(struct kvm_vcpu *vcpu, u64 addr, bool direct)
diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h
index 7775ba789258..a76ec22ce3f2 100644
--- a/arch/x86/kvm/mmu/paging_tmpl.h
+++ b/arch/x86/kvm/mmu/paging_tmpl.h
@@ -398,9 +398,9 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
walker->table_gfn[walker->level - 1] = table_gfn;
walker->pte_gpa[walker->level - 1] = pte_gpa;
- real_gpa = kvm_translate_gpa(vcpu, w, gfn_to_gpa(table_gfn),
- nested_access | PFERR_GUEST_PAGE_MASK,
- &walker->fault, 0);
+ kvm_translate_gpa(vcpu, w, gfn_to_gpa(table_gfn),
+ nested_access | PFERR_GUEST_PAGE_MASK,
+ &real_gpa, &walker->fault, 0);
if (unlikely(real_gpa == INVALID_GPA))
return 0;
@@ -458,9 +458,9 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
gfn += pse36_gfn_delta(pte);
#endif
- real_gpa = kvm_translate_gpa(vcpu, w, gfn_to_gpa(gfn),
- access | PFERR_GUEST_FINAL_MASK,
- &walker->fault, walker->pte_access);
+ kvm_translate_gpa(vcpu, w, gfn_to_gpa(gfn),
+ access | PFERR_GUEST_FINAL_MASK, &real_gpa,
+ &walker->fault, walker->pte_access);
if (real_gpa == INVALID_GPA)
return 0;
diff --git a/arch/x86/kvm/regs.c b/arch/x86/kvm/regs.c
index 3621f1a47d37..3969b769f1c1 100644
--- a/arch/x86/kvm/regs.c
+++ b/arch/x86/kvm/regs.c
@@ -165,9 +165,11 @@ int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3)
* If the MMU is nested, CR3 holds an L2 GPA and needs to be translated
* to an L1 GPA.
*/
- real_gpa = kvm_translate_gpa(vcpu, w, gfn_to_gpa(pdpt_gfn),
- PFERR_USER_MASK | PFERR_WRITE_MASK |
- PFERR_GUEST_PAGE_MASK, NULL, 0);
+ ret = kvm_translate_gpa(vcpu, w, gfn_to_gpa(pdpt_gfn),
+ PFERR_USER_MASK | PFERR_WRITE_MASK | PFERR_GUEST_PAGE_MASK,
+ &real_gpa, NULL, 0);
+ if (ret)
+ return ret;
if (real_gpa == INVALID_GPA)
return 0;
diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index 5d0a2f658365..73f1f40aad47 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -2148,24 +2148,24 @@ static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu)
return true;
}
-static gpa_t svm_translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa,
- u64 access,
- struct x86_exception *exception,
- u64 pte_access)
+static int svm_translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa,
+ u64 access, gpa_t *translated_gpa,
+ struct x86_exception *exception,
+ u64 pte_access)
{
struct vcpu_svm *svm = to_svm(vcpu);
struct kvm_pagewalk *w = &vcpu->arch.ngpa_walk;
- gpa_t result;
- if (WARN_ON_ONCE(!mmu_is_nested(vcpu)))
- return gpa;
+ if (WARN_ON_ONCE(!mmu_is_nested(vcpu))) {
+ *translated_gpa = gpa;
+ return 0;
+ }
/* Non-GMET walks are always user-walks */
if (!(svm->nested.ctl.misc_ctl & SVM_MISC_ENABLE_GMET))
access |= PFERR_USER_MASK;
- w->gva_to_gpa(vcpu, w, gpa, access, &result, exception);
- return result;
+ return w->gva_to_gpa(vcpu, w, gpa, access, translated_gpa, exception);
}
struct kvm_x86_nested_ops svm_nested_ops __initdata = {
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index a666c2908889..f6994a95523a 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -7477,16 +7477,17 @@ __init int nested_vmx_hardware_setup(int (*exit_handlers[])(struct kvm_vcpu *))
}
-static gpa_t vmx_translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa,
- u64 access,
- struct x86_exception *exception,
- u64 pte_access)
+static int vmx_translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa,
+ u64 access, gpa_t *translated_gpa,
+ struct x86_exception *exception,
+ u64 pte_access)
{
struct kvm_pagewalk *w = &vcpu->arch.ngpa_walk;
- gpa_t result;
- if (WARN_ON_ONCE(!mmu_is_nested(vcpu)))
- return gpa;
+ if (WARN_ON_ONCE(!mmu_is_nested(vcpu))) {
+ *translated_gpa = gpa;
+ return 0;
+ }
/*
* MBEC differentiates based on the effective U/S bit of
@@ -7496,8 +7497,7 @@ static gpa_t vmx_translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa,
if ((pte_access & ACC_USER_MASK) && (access & PFERR_GUEST_FINAL_MASK))
access |= PFERR_USER_MASK;
- w->gva_to_gpa(vcpu, w, gpa, access, &result, exception);
- return result;
+ return w->gva_to_gpa(vcpu, w, gpa, access, translated_gpa, exception);
}
struct kvm_x86_nested_ops vmx_nested_ops __initdata = {
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 53400fda9f44..59b38484a0d4 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -7315,10 +7315,17 @@ static int kvm_pv_clock_pairing(struct kvm_vcpu *vcpu, gpa_t paddr,
*ret = -KVM_EFAULT;
while (offset < sizeof(clock_pairing)) {
- gpa_t gpa = kvm_translate_gpa(vcpu, &vcpu->arch.gva_walk, paddr + offset,
- PFERR_WRITE_MASK | PFERR_GUEST_FINAL_MASK, NULL, 0);
- size_t len = min_t(size_t, sizeof(clock_pairing) - offset,
- PAGE_SIZE - offset_in_page(gpa));
+ gpa_t gpa;
+ size_t len;
+ int r;
+
+ r = kvm_translate_gpa(vcpu, &vcpu->arch.gva_walk, paddr + offset,
+ PFERR_WRITE_MASK | PFERR_GUEST_FINAL_MASK,
+ &gpa, NULL, 0);
+ if (r)
+ return r;
+ len = min_t(size_t, sizeof(clock_pairing) - offset,
+ PAGE_SIZE - offset_in_page(gpa));
if (gpa == INVALID_GPA)
return 0;
--
2.52.0