aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Lendacky <thomas.lendacky@amd.com>2016-11-23 12:01:38 -0500
committerRadim Krčmář <rkrcmar@redhat.com>2016-11-24 12:32:26 -0500
commit147277540bbc54119172481c8ef6d930cc9fbfc2 (patch)
tree6ebc574edba395060753311647ea571d1fe4c361
parentae0f5499511e5b1723792c848e44d661d0d4e22f (diff)
kvm: svm: Add support for additional SVM NPF error codes
AMD hardware adds two additional bits to aid in nested page fault handling. Bit 32 - NPF occurred while translating the guest's final physical address Bit 33 - NPF occurred while translating the guest page tables The guest page tables fault indicator can be used as an aid for nested virtualization. Using V0 for the host, V1 for the first level guest and V2 for the second level guest, when both V1 and V2 are using nested paging there are currently a number of unnecessary instruction emulations. When V2 is launched shadow paging is used in V1 for the nested tables of V2. As a result, KVM marks these pages as RO in the host nested page tables. When V2 exits and we resume V1, these pages are still marked RO. Every nested walk for a guest page table is treated as a user-level write access and this causes a lot of NPFs because the V1 page tables are marked RO in the V0 nested tables. While executing V1, when these NPFs occur KVM sees a write to a read-only page, emulates the V1 instruction and unprotects the page (marking it RW). This patch looks for cases where we get a NPF due to a guest page table walk where the page was marked RO. It immediately unprotects the page and resumes the guest, leading to far fewer instruction emulations when nested virtualization is used. Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com> Reviewed-by: Borislav Petkov <bp@suse.de> Signed-off-by: Brijesh Singh <brijesh.singh@amd.com> Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
-rw-r--r--arch/x86/include/asm/kvm_host.h11
-rw-r--r--arch/x86/kvm/mmu.c20
-rw-r--r--arch/x86/kvm/svm.c2
3 files changed, 29 insertions, 4 deletions
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index bdde80731f49..da07e175dac8 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -191,6 +191,8 @@ enum {
191#define PFERR_RSVD_BIT 3 191#define PFERR_RSVD_BIT 3
192#define PFERR_FETCH_BIT 4 192#define PFERR_FETCH_BIT 4
193#define PFERR_PK_BIT 5 193#define PFERR_PK_BIT 5
194#define PFERR_GUEST_FINAL_BIT 32
195#define PFERR_GUEST_PAGE_BIT 33
194 196
195#define PFERR_PRESENT_MASK (1U << PFERR_PRESENT_BIT) 197#define PFERR_PRESENT_MASK (1U << PFERR_PRESENT_BIT)
196#define PFERR_WRITE_MASK (1U << PFERR_WRITE_BIT) 198#define PFERR_WRITE_MASK (1U << PFERR_WRITE_BIT)
@@ -198,6 +200,13 @@ enum {
198#define PFERR_RSVD_MASK (1U << PFERR_RSVD_BIT) 200#define PFERR_RSVD_MASK (1U << PFERR_RSVD_BIT)
199#define PFERR_FETCH_MASK (1U << PFERR_FETCH_BIT) 201#define PFERR_FETCH_MASK (1U << PFERR_FETCH_BIT)
200#define PFERR_PK_MASK (1U << PFERR_PK_BIT) 202#define PFERR_PK_MASK (1U << PFERR_PK_BIT)
203#define PFERR_GUEST_FINAL_MASK (1ULL << PFERR_GUEST_FINAL_BIT)
204#define PFERR_GUEST_PAGE_MASK (1ULL << PFERR_GUEST_PAGE_BIT)
205
206#define PFERR_NESTED_GUEST_PAGE (PFERR_GUEST_PAGE_MASK | \
207 PFERR_USER_MASK | \
208 PFERR_WRITE_MASK | \
209 PFERR_PRESENT_MASK)
201 210
202/* apic attention bits */ 211/* apic attention bits */
203#define KVM_APIC_CHECK_VAPIC 0 212#define KVM_APIC_CHECK_VAPIC 0
@@ -1203,7 +1212,7 @@ void kvm_vcpu_deactivate_apicv(struct kvm_vcpu *vcpu);
1203 1212
1204int kvm_emulate_hypercall(struct kvm_vcpu *vcpu); 1213int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
1205 1214
1206int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code, 1215int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u64 error_code,
1207 void *insn, int insn_len); 1216 void *insn, int insn_len);
1208void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva); 1217void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva);
1209void kvm_mmu_new_cr3(struct kvm_vcpu *vcpu); 1218void kvm_mmu_new_cr3(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index 2a5ccec8b2a8..7012de4a1fed 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -4501,7 +4501,7 @@ static void make_mmu_pages_available(struct kvm_vcpu *vcpu)
4501 kvm_mmu_commit_zap_page(vcpu->kvm, &invalid_list); 4501 kvm_mmu_commit_zap_page(vcpu->kvm, &invalid_list);
4502} 4502}
4503 4503
4504int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u32 error_code, 4504int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code,
4505 void *insn, int insn_len) 4505 void *insn, int insn_len)
4506{ 4506{
4507 int r, emulation_type = EMULTYPE_RETRY; 4507 int r, emulation_type = EMULTYPE_RETRY;
@@ -4520,12 +4520,28 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u32 error_code,
4520 return r; 4520 return r;
4521 } 4521 }
4522 4522
4523 r = vcpu->arch.mmu.page_fault(vcpu, cr2, error_code, false); 4523 r = vcpu->arch.mmu.page_fault(vcpu, cr2, lower_32_bits(error_code),
4524 false);
4524 if (r < 0) 4525 if (r < 0)
4525 return r; 4526 return r;
4526 if (!r) 4527 if (!r)
4527 return 1; 4528 return 1;
4528 4529
4530 /*
4531 * Before emulating the instruction, check if the error code
4532 * was due to a RO violation while translating the guest page.
4533 * This can occur when using nested virtualization with nested
4534 * paging in both guests. If true, we simply unprotect the page
4535 * and resume the guest.
4536 *
4537 * Note: AMD only (since it supports the PFERR_GUEST_PAGE_MASK used
4538 * in PFERR_NEXT_GUEST_PAGE)
4539 */
4540 if (error_code == PFERR_NESTED_GUEST_PAGE) {
4541 kvm_mmu_unprotect_page(vcpu->kvm, gpa_to_gfn(cr2));
4542 return 1;
4543 }
4544
4529 if (mmio_info_in_cache(vcpu, cr2, direct)) 4545 if (mmio_info_in_cache(vcpu, cr2, direct))
4530 emulation_type = 0; 4546 emulation_type = 0;
4531emulate: 4547emulate:
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 8ca1eca5038d..4e462bb85723 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -2074,7 +2074,7 @@ static void svm_set_dr7(struct kvm_vcpu *vcpu, unsigned long value)
2074static int pf_interception(struct vcpu_svm *svm) 2074static int pf_interception(struct vcpu_svm *svm)
2075{ 2075{
2076 u64 fault_address = svm->vmcb->control.exit_info_2; 2076 u64 fault_address = svm->vmcb->control.exit_info_2;
2077 u32 error_code; 2077 u64 error_code;
2078 int r = 1; 2078 int r = 1;
2079 2079
2080 switch (svm->apf_reason) { 2080 switch (svm->apf_reason) {