diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2013-09-09 07:52:33 -0400 |
---|---|---|
committer | Gleb Natapov <gleb@redhat.com> | 2013-09-17 05:52:31 -0400 |
commit | ba6a3541545542721ce821d1e7e5ce35752e6fdf (patch) | |
tree | b53ff602454f383dc8148d38d7d870972d7866db /virt/kvm/kvm_main.c | |
parent | 3261107ebfd8f6bba57cfcdb89385779fd149a00 (diff) |
KVM: mmu: allow page tables to be in read-only slots
Page tables in a read-only memory slot will currently cause a triple
fault because the page walker uses gfn_to_hva and it fails on such a slot.
OVMF uses such a page table; however, real hardware seems to be fine with
that as long as the accessed/dirty bits are set. Save whether the slot
is readonly, and later check it when updating the accessed and dirty bits.
Reviewed-by: Xiao Guangrong <xiaoguangrong@linux.vnet.ibm.com>
Reviewed-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'virt/kvm/kvm_main.c')
-rw-r--r-- | virt/kvm/kvm_main.c | 14 |
1 files changed, 9 insertions, 5 deletions
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index bf040c4e02b3..979bff485fb0 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
@@ -1058,11 +1058,15 @@ unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn) | |||
1058 | EXPORT_SYMBOL_GPL(gfn_to_hva); | 1058 | EXPORT_SYMBOL_GPL(gfn_to_hva); |
1059 | 1059 | ||
1060 | /* | 1060 | /* |
1061 | * The hva returned by this function is only allowed to be read. | 1061 | * If writable is set to false, the hva returned by this function is only |
1062 | * It should pair with kvm_read_hva() or kvm_read_hva_atomic(). | 1062 | * allowed to be read. |
1063 | */ | 1063 | */ |
1064 | static unsigned long gfn_to_hva_read(struct kvm *kvm, gfn_t gfn) | 1064 | unsigned long gfn_to_hva_prot(struct kvm *kvm, gfn_t gfn, bool *writable) |
1065 | { | 1065 | { |
1066 | struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn); | ||
1067 | if (writable) | ||
1068 | *writable = !memslot_is_readonly(slot); | ||
1069 | |||
1066 | return __gfn_to_hva_many(gfn_to_memslot(kvm, gfn), gfn, NULL, false); | 1070 | return __gfn_to_hva_many(gfn_to_memslot(kvm, gfn), gfn, NULL, false); |
1067 | } | 1071 | } |
1068 | 1072 | ||
@@ -1430,7 +1434,7 @@ int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset, | |||
1430 | int r; | 1434 | int r; |
1431 | unsigned long addr; | 1435 | unsigned long addr; |
1432 | 1436 | ||
1433 | addr = gfn_to_hva_read(kvm, gfn); | 1437 | addr = gfn_to_hva_prot(kvm, gfn, NULL); |
1434 | if (kvm_is_error_hva(addr)) | 1438 | if (kvm_is_error_hva(addr)) |
1435 | return -EFAULT; | 1439 | return -EFAULT; |
1436 | r = kvm_read_hva(data, (void __user *)addr + offset, len); | 1440 | r = kvm_read_hva(data, (void __user *)addr + offset, len); |
@@ -1468,7 +1472,7 @@ int kvm_read_guest_atomic(struct kvm *kvm, gpa_t gpa, void *data, | |||
1468 | gfn_t gfn = gpa >> PAGE_SHIFT; | 1472 | gfn_t gfn = gpa >> PAGE_SHIFT; |
1469 | int offset = offset_in_page(gpa); | 1473 | int offset = offset_in_page(gpa); |
1470 | 1474 | ||
1471 | addr = gfn_to_hva_read(kvm, gfn); | 1475 | addr = gfn_to_hva_prot(kvm, gfn, NULL); |
1472 | if (kvm_is_error_hva(addr)) | 1476 | if (kvm_is_error_hva(addr)) |
1473 | return -EFAULT; | 1477 | return -EFAULT; |
1474 | pagefault_disable(); | 1478 | pagefault_disable(); |