Skip to content

Commit 1472775

Browse files
tlendackyrkrcmar
authored andcommitted
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 <[email protected]> Reviewed-by: Borislav Petkov <[email protected]> Signed-off-by: Brijesh Singh <[email protected]> Signed-off-by: Radim Krčmář <[email protected]>
1 parent ae0f549 commit 1472775

File tree

3 files changed

+29
-4
lines changed

3 files changed

+29
-4
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,22 @@ enum {
191191
#define PFERR_RSVD_BIT 3
192192
#define PFERR_FETCH_BIT 4
193193
#define PFERR_PK_BIT 5
194+
#define PFERR_GUEST_FINAL_BIT 32
195+
#define PFERR_GUEST_PAGE_BIT 33
194196

195197
#define PFERR_PRESENT_MASK (1U << PFERR_PRESENT_BIT)
196198
#define PFERR_WRITE_MASK (1U << PFERR_WRITE_BIT)
197199
#define PFERR_USER_MASK (1U << PFERR_USER_BIT)
198200
#define PFERR_RSVD_MASK (1U << PFERR_RSVD_BIT)
199201
#define PFERR_FETCH_MASK (1U << PFERR_FETCH_BIT)
200202
#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)
201210

202211
/* apic attention bits */
203212
#define KVM_APIC_CHECK_VAPIC 0
@@ -1203,7 +1212,7 @@ void kvm_vcpu_deactivate_apicv(struct kvm_vcpu *vcpu);
12031212

12041213
int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
12051214

1206-
int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code,
1215+
int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u64 error_code,
12071216
void *insn, int insn_len);
12081217
void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva);
12091218
void kvm_mmu_new_cr3(struct kvm_vcpu *vcpu);

arch/x86/kvm/mmu.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4501,7 +4501,7 @@ static void make_mmu_pages_available(struct kvm_vcpu *vcpu)
45014501
kvm_mmu_commit_zap_page(vcpu->kvm, &invalid_list);
45024502
}
45034503

4504-
int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u32 error_code,
4504+
int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code,
45054505
void *insn, int insn_len)
45064506
{
45074507
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,
45204520
return r;
45214521
}
45224522

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);
45244525
if (r < 0)
45254526
return r;
45264527
if (!r)
45274528
return 1;
45284529

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+
45294545
if (mmio_info_in_cache(vcpu, cr2, direct))
45304546
emulation_type = 0;
45314547
emulate:

arch/x86/kvm/svm.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2074,7 +2074,7 @@ static void svm_set_dr7(struct kvm_vcpu *vcpu, unsigned long value)
20742074
static int pf_interception(struct vcpu_svm *svm)
20752075
{
20762076
u64 fault_address = svm->vmcb->control.exit_info_2;
2077-
u32 error_code;
2077+
u64 error_code;
20782078
int r = 1;
20792079

20802080
switch (svm->apf_reason) {

0 commit comments

Comments
 (0)