KVM: x86: propagate exception from permission checks on the nested page fault
authorPaolo Bonzini <pbonzini@redhat.com>
Tue, 2 Sep 2014 11:23:06 +0000 (13:23 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Fri, 5 Sep 2014 10:01:13 +0000 (12:01 +0200)
Currently, if a permission error happens during the translation of
the final GPA to HPA, walk_addr_generic returns 0 but does not fill
in walker->fault.  To avoid this, add an x86_exception* argument
to the translate_gpa function, and let it fill in walker->fault.
The nested_page_fault field will be true, since the walk_mmu is the
nested_mmu and translate_gpu instead operates on the "outer" (NPT)
instance.

Reported-by: Valentine Sinitsyn <valentine.sinitsyn@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/mmu.c
arch/x86/kvm/paging_tmpl.h
arch/x86/kvm/x86.c

index c9896518e54dac93eb458e8ac985f50745d5e1b7..2dbde3b7446c0c2e0f0912b1c9a3423cacbf85bf 100644 (file)
@@ -262,7 +262,8 @@ struct kvm_mmu {
                                  struct x86_exception *fault);
        gpa_t (*gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t gva, u32 access,
                            struct x86_exception *exception);
-       gpa_t (*translate_gpa)(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access);
+       gpa_t (*translate_gpa)(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access,
+                              struct x86_exception *exception);
        int (*sync_page)(struct kvm_vcpu *vcpu,
                         struct kvm_mmu_page *sp);
        void (*invlpg)(struct kvm_vcpu *vcpu, gva_t gva);
@@ -923,7 +924,8 @@ void __kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu);
 int kvm_mmu_load(struct kvm_vcpu *vcpu);
 void kvm_mmu_unload(struct kvm_vcpu *vcpu);
 void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu);
-gpa_t translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access);
+gpa_t translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access,
+                          struct x86_exception *exception);
 gpa_t kvm_mmu_gva_to_gpa_read(struct kvm_vcpu *vcpu, gva_t gva,
                              struct x86_exception *exception);
 gpa_t kvm_mmu_gva_to_gpa_fetch(struct kvm_vcpu *vcpu, gva_t gva,
@@ -943,7 +945,8 @@ void kvm_mmu_new_cr3(struct kvm_vcpu *vcpu);
 void kvm_enable_tdp(void);
 void kvm_disable_tdp(void);
 
-static inline gpa_t translate_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access)
+static inline gpa_t translate_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access,
+                                 struct x86_exception *exception)
 {
        return gpa;
 }
index 5b93a597e0c81536e7851f483e6f91754076224d..76398fe15df2d2e33ec9335aef3eba2e73c0315a 100644 (file)
@@ -3200,7 +3200,7 @@ static gpa_t nonpaging_gva_to_gpa_nested(struct kvm_vcpu *vcpu, gva_t vaddr,
 {
        if (exception)
                exception->error_code = 0;
-       return vcpu->arch.nested_mmu.translate_gpa(vcpu, vaddr, access);
+       return vcpu->arch.nested_mmu.translate_gpa(vcpu, vaddr, access, exception);
 }
 
 static bool quickly_check_mmio_pf(struct kvm_vcpu *vcpu, u64 addr, bool direct)
index df1a044d92de125928b45f00d49deb3e7d50591a..0ab6c65a28215c227c2a666aaea7d0e0b353ebc2 100644 (file)
@@ -321,7 +321,8 @@ retry_walk:
                walker->pte_gpa[walker->level - 1] = pte_gpa;
 
                real_gfn = mmu->translate_gpa(vcpu, gfn_to_gpa(table_gfn),
-                                             PFERR_USER_MASK|PFERR_WRITE_MASK);
+                                             PFERR_USER_MASK|PFERR_WRITE_MASK,
+                                             &walker->fault);
 
                /*
                 * FIXME: This can happen if emulation (for of an INS/OUTS
@@ -334,7 +335,7 @@ retry_walk:
                 * fields.
                 */
                if (unlikely(real_gfn == UNMAPPED_GVA))
-                       goto error;
+                       return 0;
 
                real_gfn = gpa_to_gfn(real_gfn);
 
@@ -376,7 +377,7 @@ retry_walk:
        if (PTTYPE == 32 && walker->level == PT_DIRECTORY_LEVEL && is_cpuid_PSE36())
                gfn += pse36_gfn_delta(pte);
 
-       real_gpa = mmu->translate_gpa(vcpu, gfn_to_gpa(gfn), access);
+       real_gpa = mmu->translate_gpa(vcpu, gfn_to_gpa(gfn), access, &walker->fault);
        if (real_gpa == UNMAPPED_GVA)
                return 0;
 
index 3541946719028097654ba44c7c69cdda59405805..7b25aa2725f8ba72c1752ef2befd1f0e195eecc9 100644 (file)
@@ -459,11 +459,12 @@ int kvm_read_guest_page_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
                            gfn_t ngfn, void *data, int offset, int len,
                            u32 access)
 {
+       struct x86_exception exception;
        gfn_t real_gfn;
        gpa_t ngpa;
 
        ngpa     = gfn_to_gpa(ngfn);
-       real_gfn = mmu->translate_gpa(vcpu, ngpa, access);
+       real_gfn = mmu->translate_gpa(vcpu, ngpa, access, &exception);
        if (real_gfn == UNMAPPED_GVA)
                return -EFAULT;
 
@@ -4065,16 +4066,16 @@ void kvm_get_segment(struct kvm_vcpu *vcpu,
        kvm_x86_ops->get_segment(vcpu, var, seg);
 }
 
-gpa_t translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access)
+gpa_t translate_nested_gpa(struct kvm_vcpu *vcpu, gpa_t gpa, u32 access,
+                          struct x86_exception *exception)
 {
        gpa_t t_gpa;
-       struct x86_exception exception;
 
        BUG_ON(!mmu_is_nested(vcpu));
 
        /* NPT walks are always user-walks */
        access |= PFERR_USER_MASK;
-       t_gpa  = vcpu->arch.mmu.gva_to_gpa(vcpu, gpa, access, &exception);
+       t_gpa  = vcpu->arch.mmu.gva_to_gpa(vcpu, gpa, access, exception);
 
        return t_gpa;
 }