KVM: MMU: Fix hugepage pdes mapping same physical address with different access
The kvm mmu keeps a shadow page for hugepage pdes; if several such pdes map the same physical address, they share the same shadow page. This is a fairly common case (kernel mappings on i386 nonpae Linux, for example). However, if the two pdes map the same memory but with different permissions, kvm will happily use the cached shadow page. If the access through the more permissive pde will occur after the access to the strict pde, an endless pagefault loop will be generated and the guest will make no progress. Fix by making the access permissions part of the cache lookup key. The fix allows Xen pae to boot on kvm and run guest domains. Thanks to Jeremy Fitzhardinge for reporting the bug and testing the fix. Signed-off-by: Avi Kivity <avi@qumranet.com>
This commit is contained in:
@@ -247,6 +247,7 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
|
||||
u64 shadow_pte;
|
||||
int metaphysical;
|
||||
gfn_t table_gfn;
|
||||
unsigned hugepage_access = 0;
|
||||
|
||||
if (is_present_pte(*shadow_ent) || is_io_pte(*shadow_ent)) {
|
||||
if (level == PT_PAGE_TABLE_LEVEL)
|
||||
@@ -276,6 +277,9 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
|
||||
if (level - 1 == PT_PAGE_TABLE_LEVEL
|
||||
&& walker->level == PT_DIRECTORY_LEVEL) {
|
||||
metaphysical = 1;
|
||||
hugepage_access = *guest_ent;
|
||||
hugepage_access &= PT_USER_MASK | PT_WRITABLE_MASK;
|
||||
hugepage_access >>= PT_WRITABLE_SHIFT;
|
||||
table_gfn = (*guest_ent & PT_BASE_ADDR_MASK)
|
||||
>> PAGE_SHIFT;
|
||||
} else {
|
||||
@@ -283,7 +287,8 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
|
||||
table_gfn = walker->table_gfn[level - 2];
|
||||
}
|
||||
shadow_page = kvm_mmu_get_page(vcpu, table_gfn, addr, level-1,
|
||||
metaphysical, shadow_ent);
|
||||
metaphysical, hugepage_access,
|
||||
shadow_ent);
|
||||
shadow_addr = shadow_page->page_hpa;
|
||||
shadow_pte = shadow_addr | PT_PRESENT_MASK | PT_ACCESSED_MASK
|
||||
| PT_WRITABLE_MASK | PT_USER_MASK;
|
||||
|
Reference in New Issue
Block a user