KVM: x86: improve reexecute_instruction
v3 cover KVM: x86: improve reexecute_instruction
Changlog:
- do not change pte access for mmio access
- a new bug is exposed that Dirty bit is not tracked if CR0.WP = 0
- cache something on page fault path and use them to detect unhandleable instruction, suggested by Marcelo
I will add the two testcase for unhandleable instruction after figure out a way to notify the unemulationable error to guest
PATCH v3 1/5 KVM: MMU: fix Dirty bit missed if CR0.WP = 0
If the write-fault access is from supervisor and CR0.WP is not set on the vcpu, kvm will fix it by adjusting pte access - it sets the W bit on pte and clears U bit. This is the chance that kvm can change pte access from readonly to writable
Unfortunately, the pte access is the access of 'direct' shadow page table, means direct sp.role.access = pte_access, then we will create a writable spte entry on the readonly shadow page table. It will cause Dirty bit is not tracked when two guest ptes point to the same large page. Note, it does not have other impact except Dirty bit since cr0.wp is encoded into sp.role
It can be fixed by adjusting pte access before establishing shadow page table. Also, after that, no mmu specified code exists in the common function and drop two parameters in set_spte
--- arch/x86/kvm/mmu.c | 47 ++++++++++++------------------------------- arch/x86/kvm/paging_tmpl.h | 30 +++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 39 deletions(-)
arch/x86/kvm/mmu.c +13 -34
@@ -2342,8 +2342,7 @@ static int mmu_need_write_protect(struct kvm_vcpu *vcpu, gfn_t gfn, }static int set_spte (struct kvm_vcpu *vcpu,u64 *sptep, -unsigned pte_access,int user_fault, -int write_fault,int level, +unsigned pte_access,int level,gfn_t gfn,pfn_t pfn,bool speculative,bool can_unsync,bool host_writable) {
@@ -2378,9 +2377,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, spte |= (u64 )pfn <<PAGE_SHIFT ; -if ((pte_access &ACC_WRITE_MASK ) - || (!vcpu->arch.mmu.direct_map && write_fault - && !is_write_protection (vcpu) && !user_fault)) { +if (pte_access &ACC_WRITE_MASK ) {/* * There are two cases:
@@ -2399,19 +2396,6 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, spte |=PT_WRITABLE_MASK |SPTE_MMU_WRITEABLE ; -if (!vcpu->arch.mmu.direct_map - && !(pte_access &ACC_WRITE_MASK )) { - spte &= ~PT_USER_MASK ; -/* -* If we converted a user page to a kernel page, -* so that the kernel can write to it when cr0.wp=0, -* then we should prevent the kernel from executing it -* if SMEP is enabled. -*/ -if (kvm_read_cr4_bits (vcpu,X86_CR4_SMEP )) - spte |=PT64_NX_MASK ; - } -/* * Optimization: for pte sync, if spte was writable the hash * lookup is unnecessary (and expensive). Write protection
@@ -2442,18 +2426,15 @@ done:static void mmu_set_spte (struct kvm_vcpu *vcpu,u64 *sptep,unsigned pt_access,unsigned pte_access, -int user_fault,int write_fault, -int *emulate,int level,gfn_t gfn, -pfn_t pfn,bool speculative, -bool host_writable) +int write_fault,int *emulate,int level,gfn_t gfn, +pfn_t pfn,bool speculative,bool host_writable) {int was_rmapped =0 ;int rmap_count; -pgprintk ("%s: spte %llx access %x write_fault %d" -" user_fault %d gfn %llx\n" , +pgprintk ("%s: spte %llx access %x write_fault %d gfn %llx\n" , __func__, *sptep, pt_access, - write_fault, user_fault, gfn); + write_fault, gfn);if (is_rmap_spte (*sptep)) {/*
@@ -2477,9 +2458,8 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, was_rmapped =1 ; } -if (set_spte (vcpu, sptep, pte_access, user_fault, write_fault, - level, gfn, pfn, speculative, true, - host_writable)) { +if (set_spte (vcpu, sptep, pte_access, level, gfn, pfn, speculative, + true, host_writable)) {if (write_fault) *emulate =1 ;kvm_mmu_flush_tlb (vcpu);
@@ -2571,10 +2551,9 @@ static int direct_pte_prefetch_many(struct kvm_vcpu *vcpu,return -1 ;for (i =0 ; i < ret; i++, gfn++, start++) -mmu_set_spte (vcpu, start,ACC_ALL , - access,0 ,0 ,NULL , - sp->role.level, gfn, -page_to_pfn (pages[i]), true, true); +mmu_set_spte (vcpu, start,ACC_ALL , access,0 ,NULL , + sp->role.level, gfn,page_to_pfn (pages[i]), + true, true);return 0 ; }
@@ -2636,8 +2615,8 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,unsigned pte_access =ACC_ALL ;mmu_set_spte (vcpu, iterator.sptep,ACC_ALL , pte_access, -0 , write, &emulate, - level, gfn, pfn, prefault, map_writable); + write, &emulate, level, gfn, pfn, + prefault, map_writable);direct_pte_prefetch (vcpu, iterator.sptep); ++vcpu->stat.pf_fixed;break ;
arch/x86/kvm/paging_tmpl.h +25 -5
@@ -330,7 +330,7 @@ FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,* we call mmu_set_spte() with host_writable = true because * pte_prefetch_gfn_to_pfn always gets a writable pfn. */ -mmu_set_spte (vcpu, spte, sp->role.access, pte_access,0 ,0 , +mmu_set_spte (vcpu, spte, sp->role.access, pte_access,0 ,NULL ,PT_PAGE_TABLE_LEVEL , gfn, pfn, true, true);return true;
@@ -405,7 +405,7 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw,*/ static int FNAME (fetch )(struct kvm_vcpu *vcpu,gva_t addr,struct guest_walker *gw, -int user_fault,int write_fault,int hlevel, +int write_fault,int hlevel,pfn_t pfn,bool map_writable,bool prefault) {struct kvm_mmu_page *sp =NULL ;
@@ -478,7 +478,7 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,clear_sp_write_flooding_count (it.sptep);mmu_set_spte (vcpu, it.sptep, access, gw->pte_access, - user_fault, write_fault, &emulate, it.level, + write_fault, &emulate, it.level, gw->gfn, pfn, prefault, map_writable);FNAME (pte_prefetch)(vcpu, gw, it.sptep);
@@ -564,6 +564,26 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, walker.gfn, pfn, walker.pte_access, &r))return r; +/* +* Do not change pte_access if the pfn is a mmio page, otherwise +* we will cache the incorrect access into mmio spte. +*/ +if (write_fault && !(walker.pte_access &ACC_WRITE_MASK ) && + !is_write_protection (vcpu) && !user_fault && + !is_noslot_pfn (pfn)) { + walker.pte_access |=ACC_WRITE_MASK ; + walker.pte_access &= ~ACC_USER_MASK ; + +/* +* If we converted a user page to a kernel page, +* so that the kernel can write to it when cr0.wp=0, +* then we should prevent the kernel from executing it +* if SMEP is enabled. +*/ +if (kvm_read_cr4_bits (vcpu,X86_CR4_SMEP )) + walker.pte_access &= ~ACC_EXEC_MASK ; + } +spin_lock (&vcpu->kvm->mmu_lock);if (mmu_notifier_retry (vcpu->kvm, mmu_seq)) goto out_unlock;
@@ -572,7 +592,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,kvm_mmu_free_some_pages (vcpu);if (!force_pt_level)transparent_hugepage_adjust (vcpu, &walker.gfn, &pfn, &level); - r =FNAME (fetch)(vcpu, addr, &walker, user_fault, write_fault, + r =FNAME (fetch)(vcpu, addr, &walker, write_fault, level, pfn, map_writable, prefault); ++vcpu->stat.pf_fixed;kvm_mmu_audit (vcpu,AUDIT_POST_PAGE_FAULT );
@@ -747,7 +767,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) host_writable = sp->spt[i] &SPTE_HOST_WRITEABLE ; -set_spte (vcpu, &sp->spt[i], pte_access,0 ,0 , +set_spte (vcpu, &sp->spt[i], pte_access,PT_PAGE_TABLE_LEVEL , gfn,spte_to_pfn (sp->spt[i]), true, false, host_writable);
Signature · 1 line
1.7.7.6
PATCH v3 2/5 KVM: MMU: fix infinite fault access retry
In reply to Xiao GuangrongWe have two issues in current code:
- if target gfn is used as its page table, guest will refault then kvm will use small page size to map it. We need two #PF to fix its shadow page table
- sometimes, say a exception is triggered during vm-exit caused by #PF (see handle_exception() in vmx.c), we remove all the shadow pages shadowed by the target gfn before go into page fault path, it will cause infinite loop:
delete shadow pages shadowed by the gfn -> try to use large page size to map the gfn -> retry the access ->...
To fix these, we can adjust page size early if the target gfn is used as page table
--- arch/x86/kvm/mmu.c | 13 ++++--------- arch/x86/kvm/paging_tmpl.h | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 10 deletions(-)
arch/x86/kvm/mmu.c +4 -9
@@ -2380,15 +2380,10 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,if (pte_access &ACC_WRITE_MASK ) {/* -* There are two cases: -* - the one is other vcpu creates new sp in the window -* between mapping_level() and acquiring mmu-lock. -* - the another case is the new sp is created by itself -* (page-fault path) when guest uses the target gfn as -* its page table. -* Both of these cases can be fixed by allowing guest to -* retry the access, it will refault, then we can establish -* the mapping by using small page. +* Other vcpu creates new sp in the window between +* mapping_level() and acquiring mmu-lock. We can +* allow guest to retry the access, the mapping can +* be fixed if guest refault. */ if (level >PT_PAGE_TABLE_LEVEL &&has_wrprotected_page (vcpu->kvm, gfn, level))
arch/x86/kvm/paging_tmpl.h +34 -1
@@ -491,6 +491,38 @@ out_gpte_changed:return 0 ; } +/* +* To see whether the mapped gfn can write its page table in the current +* mapping. +* +* It is the helper function of FNAME(page_fault). When guest uses large page +* size to map the writable gfn which is used as current page table, we should +* force kvm to use small page size to map it because new shadow page will be +* created when kvm establishes shadow page table that stop kvm using large +* page size. Do it early can avoid unnecessary #PF and emulation. +* +* Note: the PDPT page table is not checked for PAE-32 bit guest. It is ok +* since the PDPT is always shadowed, that means, we can not use large page +* size to map the gfn which is used as PDPT. +*/ +static bool +FNAME (is_self_change_mapping )(struct kvm_vcpu *vcpu, +struct guest_walker *walker,int user_fault) +{ +int level; +gfn_t mask = ~(KVM_PAGES_PER_HPAGE (walker->level) -1 ); + +if (!(walker->pte_access &ACC_WRITE_MASK || + (!is_write_protection (vcpu) && !user_fault))) +return false; + +for (level = walker->level; level <= walker->max_level; level++) +if (!((walker->gfn ^ walker->table_gfn[level -1 ]) & mask)) +return true; + +return false; +} +/* * Page fault handler. There are several causes for a page fault: * - there is no shadow pte for the guest pte
@@ -545,7 +577,8 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, }if (walker.level >=PT_DIRECTORY_LEVEL ) - force_pt_level =mapping_level_dirty_bitmap (vcpu, walker.gfn); + force_pt_level =mapping_level_dirty_bitmap (vcpu, walker.gfn) + ||FNAME (is_self_change_mapping)(vcpu, &walker, user_fault);else force_pt_level =1 ;if (!force_pt_level) {
Signature · 1 line
1.7.7.6
PATCH v3 3/5 KVM: x86: clean up reexecute_instruction
In reply to Xiao GuangrongLittle cleanup for reexecute_instruction, also use gpa_to_gfn in retry_instruction
--- arch/x86/kvm/x86.c | 13 ++++++------- 1 files changed, 6 insertions(+), 7 deletions(-)
arch/x86/kvm/x86.c +6 -7
@@ -4761,19 +4761,18 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gva_t gva)if (tdp_enabled)return false; + gpa =kvm_mmu_gva_to_gpa_read (vcpu, gva,NULL ); +if (gpa ==UNMAPPED_GVA ) +return true;/* let cpu generate fault */ +/* * if emulation was due to access to shadowed page table * and it failed try to unshadow page and re-enter the * guest to let CPU execute the instruction. */ -if (kvm_mmu_unprotect_page_virt (vcpu, gva)) +if (kvm_mmu_unprotect_page (vcpu->kvm,gpa_to_gfn (gpa)))return true; - gpa =kvm_mmu_gva_to_gpa_system (vcpu, gva,NULL ); - -if (gpa ==UNMAPPED_GVA ) -return true;/* let cpu generate fault */ -/* * Do not retry the unhandleable instruction if it faults on the * readonly host memory, otherwise it will goto a infinite loop:
@@ -4828,7 +4827,7 @@ static bool retry_instruction(struct x86_emulate_ctxt *ctxt,if (!vcpu->arch.mmu.direct_map) gpa =kvm_mmu_gva_to_gpa_write (vcpu, cr2,NULL ); -kvm_mmu_unprotect_page (vcpu->kvm, gpa >>PAGE_SHIFT ); +kvm_mmu_unprotect_page (vcpu->kvm,gpa_to_gfn (gpa));return true; }
Signature · 1 line
1.7.7.6
PATCH v3 4/5 KVM: x86: let reexecute_instruction work for tdp
In reply to Xiao GuangrongCurrently, reexecute_instruction refused to retry all instructions. If nested npt is used, the emulation may be caused by shadow page, it can be fixed by dropping the shadow page
--- arch/x86/kvm/x86.c | 19 +++++++++++++------ 1 files changed, 13 insertions(+), 6 deletions(-)
arch/x86/kvm/x86.c +13 -6
@@ -4753,17 +4753,24 @@ static int handle_emulation_failure(struct kvm_vcpu *vcpu)return r; } -static bool reexecute_instruction (struct kvm_vcpu *vcpu,gva_t gva) +static bool reexecute_instruction (struct kvm_vcpu *vcpu,unsigned long cr2) { -gpa_t gpa; +gpa_t gpa = cr2;pfn_t pfn; +unsigned int indirect_shadow_pages; + +spin_lock (&vcpu->kvm->mmu_lock); + indirect_shadow_pages = vcpu->kvm->arch.indirect_shadow_pages; +spin_unlock (&vcpu->kvm->mmu_lock); -if (tdp_enabled) +if (!indirect_shadow_pages)return false; - gpa =kvm_mmu_gva_to_gpa_read (vcpu, gva,NULL ); -if (gpa ==UNMAPPED_GVA ) -return true;/* let cpu generate fault */ +if (!vcpu->arch.mmu.direct_map) { + gpa =kvm_mmu_gva_to_gpa_read (vcpu, cr2,NULL ); +if (gpa ==UNMAPPED_GVA ) +return true;/* let cpu generate fault */ + }/* * if emulation was due to access to shadowed page table
Signature · 1 line
1.7.7.6
PATCH v3 5/5 KVM: x86: improve reexecute_instruction
In reply to Xiao GuangrongThe current reexecute_instruction can not well detect the failed instruction emulation. It allows guest to retry all the instructions except it accesses on error pfn
For example, some cases are nested-write-protect - if the page we want to write is used as PDE but it chains to itself. Under this case, we should stop the emulation and report the case to userspace
--- arch/x86/include/asm/kvm_host.h | 7 +++++ arch/x86/kvm/paging_tmpl.h | 23 +++++++++++----- arch/x86/kvm/x86.c | 58 +++++++++++++++++++++++++-------------- 3 files changed, 60 insertions(+), 28 deletions(-)
arch/x86/include/asm/kvm_host.h +7 -0
@@ -502,6 +502,13 @@ struct kvm_vcpu_arch {u64 msr_val;struct gfn_to_hva_cache data; } pv_eoi; + +/* +* Cache the access info when fix page fault then use +* them to detect unhandeable instruction. +*/ +gva_t fault_addr; +bool target_gfn_is_pt; };struct kvm_lpage_info {
arch/x86/kvm/paging_tmpl.h +16 -7
@@ -506,21 +506,27 @@ out_gpte_changed:* size to map the gfn which is used as PDPT. */ static bool -FNAME (is_self_change_mapping )(struct kvm_vcpu *vcpu, +FNAME (is_self_change_mapping )(struct kvm_vcpu *vcpu,gva_t addr,struct guest_walker *walker,int user_fault) {int level;gfn_t mask = ~(KVM_PAGES_PER_HPAGE (walker->level) -1 ); +bool self_changed = false;if (!(walker->pte_access &ACC_WRITE_MASK || (!is_write_protection (vcpu) && !user_fault)))return false; -for (level = walker->level; level <= walker->max_level; level++) -if (!((walker->gfn ^ walker->table_gfn[level -1 ]) & mask)) -return true; + vcpu->arch.fault_addr = addr; -return false; +for (level = walker->level; level <= walker->max_level; level++) { +gfn_t gfn = walker->gfn ^ walker->table_gfn[level -1 ]; + + self_changed |= !(gfn & mask); + vcpu->arch.target_gfn_is_pt |= !gfn; + } + +return self_changed; }/*
@@ -548,7 +554,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,int level =PT_PAGE_TABLE_LEVEL ;int force_pt_level;unsigned long mmu_seq; -bool map_writable; +bool map_writable, is_self_change_mapping;pgprintk ("%s: addr %lx err %x\n" , __func__, addr, error_code);
@@ -576,9 +582,12 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,return 0 ; } + is_self_change_mapping =FNAME (is_self_change_mapping)(vcpu, addr, + &walker, user_fault); +if (walker.level >=PT_DIRECTORY_LEVEL ) force_pt_level =mapping_level_dirty_bitmap (vcpu, walker.gfn) - ||FNAME (is_self_change_mapping)(vcpu, &walker, user_fault); + || is_self_change_mapping;else force_pt_level =1 ;if (!force_pt_level) {
arch/x86/kvm/x86.c +37 -21
79 lines not on this page · raw message
Signature · 1 line
1.7.7.6
Quoted 80 lines · Xiao Guangrong
The current
reexecute_instructioncan not well detect the failed instruction emulation. It allows guest to retry all the instructions except it accesses on error pfnFor example, some cases are nested-write-protect - if the page we want to write is used as PDE but it chains to itself. Under this case, we should stop the emulation and report the case to userspace
Signed-off-by Xiao Guangrong <[email protected]>--- arch/x86/include/asm/kvm_host.h | 7 +++++ arch/x86/kvm/paging_tmpl.h | 23 +++++++++++----- arch/x86/kvm/x86.c | 58 +++++++++++++++++++++++++-------------- 3 files changed, 60 insertions(+), 28 deletions(-)arch/x86/include/asm/kvm_host.h +7
-0@@ -502,6 +502,13 @@ struct kvm_vcpu_arch {u64 msr_val;struct gfn_to_hva_cache data; } pv_eoi; + +/* +* Cache the access info when fix page fault then use +* them to detect unhandeable instruction. +*/ +gva_t fault_addr; +bool target_gfn_is_pt; };struct kvm_lpage_info {arch/x86/kvm/paging_tmpl.h +12
-6@@ -506,21 +506,27 @@ out_gpte_changed:* size to map the gfn which is used as PDPT. */ static bool -FNAME (is_self_change_mapping )(struct kvm_vcpu *vcpu, +FNAME (is_self_change_mapping )(struct kvm_vcpu *vcpu,gva_t addr,struct guest_walker *walker,int user_fault) {int level;gfn_t mask = ~(KVM_PAGES_PER_HPAGE (walker->level) -1 ); +bool self_changed = false;if (!(walker->pte_access &ACC_WRITE_MASK || (!is_write_protection (vcpu) && !user_fault)))return false; -for (level = walker->level; level <= walker->max_level; level++) -if (!((walker->gfn ^ walker->table_gfn[level -1 ]) & mask)) -return true; + vcpu->arch.fault_addr = addr; -return false; +for (level = walker->level; level <= walker->max_level; level++) { +gfn_t gfn = walker->gfn ^ walker->table_gfn[level -1 ]; + + self_changed |= !(gfn & mask); + vcpu->arch.target_gfn_is_pt |= !gfn; + } + +return self_changed; }/* @@ -548,7 +554,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,int level =PT_PAGE_TABLE_LEVEL ;int force_pt_level;unsigned long mmu_seq; -bool map_writable; +bool map_writable, is_self_change_mapping;pgprintk ("%s: addr %lx err %x\n" , __func__, addr, error_code);
arch/x86/kvm/paging_tmpl.h static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, · line 587
Quoted 6 lines
arch/x86/kvm/paging_tmpl.h +3
-0@@ -576,9 +582,12 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,return 0 ; } + is_self_change_mapping =FNAME (is_self_change_mapping)(vcpu, addr, + &walker, user_fault); +
is_self_change_mapping() has a subtle side-effect by setting vcpu->arch.target_gfn_is_pt. From reading the page_fault() function you cannot guess why is_self_change_mapping() is not called inside "if (walker.level >= PT_DIRECTORY_LEVEL)" since this is the only place where its output is used. May be pass it pointer to target_gfn_is_pt as a parameter to make it clear that return value is not the only output of the function.
Quoted 32 lines
arch/x86/kvm/paging_tmpl.h +1
-1if (walker.level >=PT_DIRECTORY_LEVEL ) force_pt_level =mapping_level_dirty_bitmap (vcpu, walker.gfn) - ||FNAME (is_self_change_mapping)(vcpu, &walker, user_fault); + || is_self_change_mapping;else force_pt_level =1 ;if (!force_pt_level) {arch/x86/kvm/x86.c +5
-9@@ -4756,29 +4756,25 @@ static int handle_emulation_failure(struct kvm_vcpu *vcpu)static bool reexecute_instruction (struct kvm_vcpu *vcpu,unsigned long cr2) {gpa_t gpa = cr2; +gfn_t gfn;pfn_t pfn; -unsigned int indirect_shadow_pages; - -spin_lock (&vcpu->kvm->mmu_lock); - indirect_shadow_pages = vcpu->kvm->arch.indirect_shadow_pages; -spin_unlock (&vcpu->kvm->mmu_lock); - -if (!indirect_shadow_pages) -return false;if (!vcpu->arch.mmu.direct_map) { - gpa =kvm_mmu_gva_to_gpa_read (vcpu, cr2,NULL ); +/* +* Write permission should be allowed since only +* write access need to be emulated. +*/
arch/x86/kvm/x86.c static int handle_emulation_failure(struct kvm_vcpu *vcpu) · line 4775
Quoted 10 lines
arch/x86/kvm/x86.c +7
-1+ gpa =kvm_mmu_gva_to_gpa_write (vcpu, cr2,NULL ); + +/* +* If the mapping is invalid in guest, let cpu retry +* it to generate fault. +*/ if (gpa ==UNMAPPED_GVA ) -return true;/* let cpu generate fault */ +return true; }
Why not fold this change to if (!vcpu->arch.mmu.direct_map) into previous patch where it was introduced. This looks independent of what you are doing in this patch.
Quoted 37 lines
arch/x86/kvm/x86.c +19
-10-/* -* if emulation was due to access to shadowed page table -* and it failed try to unshadow page and re-enter the -* guest to let CPU execute the instruction. -*/ -if (kvm_mmu_unprotect_page (vcpu->kvm,gpa_to_gfn (gpa))) -return true; + gfn =gpa_to_gfn (gpa);/* * Do not retry the unhandleable instruction if it faults on the @@ -4786,13 +4782,33 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, unsigned long cr2)* retry instruction -> write #PF -> emulation fail -> retry * instruction -> ... */ - pfn =gfn_to_pfn (vcpu->kvm,gpa_to_gfn (gpa)); -if (!is_error_noslot_pfn (pfn)) { -kvm_release_pfn_clean (pfn); + pfn =gfn_to_pfn (vcpu->kvm, gfn); + +/* +* If the instruction failed on the error pfn, it can not be fixed, +* report the error to userspace. +*/ +if (is_error_noslot_pfn (pfn)) +return false; + +kvm_release_pfn_clean (pfn); + +/* The instructions are well-emulated on direct mmu. */ +if (vcpu->arch.mmu.direct_map) { +unsigned int indirect_shadow_pages; + +spin_lock (&vcpu->kvm->mmu_lock); + indirect_shadow_pages = vcpu->kvm->arch.indirect_shadow_pages; +spin_unlock (&vcpu->kvm->mmu_lock);
arch/x86/kvm/x86.c static bool reexecute_instruction(struct kvm_vcpu *vcpu, unsigned long cr2) · line 4811
Quoted 10 lines
arch/x86/kvm/x86.c +6
-1+ +if (indirect_shadow_pages) +kvm_mmu_unprotect_page (vcpu->kvm, gfn); +return true; } -return false; +kvm_mmu_unprotect_page (vcpu->kvm, gfn); +return !(vcpu->arch.fault_addr == cr2 && vcpu->arch.target_gfn_is_pt);
Do you store fault_addr only to avoid using stale target_gfn_is_pt? If yes why not reset target_gfn_is_pt to false at the beginning of a page fault and get rid of fault_addr?
Quoted 5 lines
arch/x86/kvm/x86.c +0
-0} static bool retry_instruction(struct x86_emulate_ctxt *ctxt,Signature · 1 line
1.7.7.6
--
Gleb.
Hi Gleb,
Thanks for your review and sorry for the delay reply since i was on my vacation.
Quoted 13 lines · Gleb Natapov
arch/x86/kvm/paging_tmpl.h static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, · line 587
Quoted 4 lines · Xiao Guangrong
arch/x86/kvm/paging_tmpl.h +3
-0+ is_self_change_mapping =FNAME (is_self_change_mapping)(vcpu, addr, + &walker, user_fault); +
is_self_change_mapping()has a subtle side-effect by settingvcpu->arch.target_gfn_is_pt. From reading thepage_fault()function you cannot guess whyis_self_change_mapping()is not called inside "if (walker.level >=PT_DIRECTORY_LEVEL)" since this is the only place where its output is used. May be pass it pointer totarget_gfn_is_ptas a parameter to make it clear that return value is not the only output of the function.
Yes, it is clearer, will do it in the next version.
Quoted 38 lines
Quoted 38 lines
arch/x86/kvm/paging_tmpl.h +1
-1if (walker.level >=PT_DIRECTORY_LEVEL ) force_pt_level =mapping_level_dirty_bitmap (vcpu, walker.gfn) - ||FNAME (is_self_change_mapping)(vcpu, &walker, user_fault); + || is_self_change_mapping;else force_pt_level =1 ;if (!force_pt_level) {arch/x86/kvm/x86.c +10
-9@@ -4756,29 +4756,25 @@ static int handle_emulation_failure(struct kvm_vcpu *vcpu)static bool reexecute_instruction (struct kvm_vcpu *vcpu,unsigned long cr2) {gpa_t gpa = cr2; +gfn_t gfn;pfn_t pfn; -unsigned int indirect_shadow_pages; - -spin_lock (&vcpu->kvm->mmu_lock); - indirect_shadow_pages = vcpu->kvm->arch.indirect_shadow_pages; -spin_unlock (&vcpu->kvm->mmu_lock); - -if (!indirect_shadow_pages) -return false;if (!vcpu->arch.mmu.direct_map) { - gpa =kvm_mmu_gva_to_gpa_read (vcpu, cr2,NULL ); +/* +* Write permission should be allowed since only +* write access need to be emulated. +*/ + gpa =kvm_mmu_gva_to_gpa_write (vcpu, cr2,NULL ); + +/* +* If the mapping is invalid in guest, let cpu retry +* it to generate fault.
Quoted 8 lines
arch/x86/kvm/x86.c static int handle_emulation_failure(struct kvm_vcpu *vcpu) · line 4775
Quoted 5 lines
arch/x86/kvm/x86.c +2
-1+*/ if (gpa ==UNMAPPED_GVA ) -return true;/* let cpu generate fault */ +return true; }Why not fold this change to if (!vcpu->arch.mmu.direct_map) into previous patch where it was introduced. This looks independent of what you are doing in this patch.
Fine to me.
Quoted 45 lines
Quoted 45 lines
arch/x86/kvm/x86.c +23
-10-/* -* if emulation was due to access to shadowed page table -* and it failed try to unshadow page and re-enter the -* guest to let CPU execute the instruction. -*/ -if (kvm_mmu_unprotect_page (vcpu->kvm,gpa_to_gfn (gpa))) -return true; + gfn =gpa_to_gfn (gpa);/* * Do not retry the unhandleable instruction if it faults on the @@ -4786,13 +4782,33 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, unsigned long cr2)* retry instruction -> write #PF -> emulation fail -> retry * instruction -> ... */ - pfn =gfn_to_pfn (vcpu->kvm,gpa_to_gfn (gpa)); -if (!is_error_noslot_pfn (pfn)) { -kvm_release_pfn_clean (pfn); + pfn =gfn_to_pfn (vcpu->kvm, gfn); + +/* +* If the instruction failed on the error pfn, it can not be fixed, +* report the error to userspace. +*/ +if (is_error_noslot_pfn (pfn)) +return false; + +kvm_release_pfn_clean (pfn); + +/* The instructions are well-emulated on direct mmu. */ +if (vcpu->arch.mmu.direct_map) { +unsigned int indirect_shadow_pages; + +spin_lock (&vcpu->kvm->mmu_lock); + indirect_shadow_pages = vcpu->kvm->arch.indirect_shadow_pages; +spin_unlock (&vcpu->kvm->mmu_lock); + +if (indirect_shadow_pages) +kvm_mmu_unprotect_page (vcpu->kvm, gfn); +return true; }
Quoted 6 lines
arch/x86/kvm/x86.c static bool reexecute_instruction(struct kvm_vcpu *vcpu, unsigned long cr2) · line 4811
Quoted 3 lines
arch/x86/kvm/x86.c +2
-1-return false; +kvm_mmu_unprotect_page (vcpu->kvm, gfn); +return !(vcpu->arch.fault_addr == cr2 && vcpu->arch.target_gfn_is_pt);Do you store
fault_addronly to avoid using staletarget_gfn_is_pt? If yes why not resettarget_gfn_is_ptto false at the beginning of a page fault and get rid offault_addr?
Good suggestion, will do. :)