Skip to content

Commit bb1fcc7

Browse files
Sean Christophersonbonzini
authored andcommitted
KVM: nVMX: Allow L1 to use 5-level page walks for nested EPT
Add support for 5-level nested EPT, and advertise said support in the EPT capabilities MSR. KVM's MMU can already handle 5-level legacy page tables, there's no reason to force an L1 VMM to use shadow paging if it wants to employ 5-level page tables. Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 8053f92 commit bb1fcc7

File tree

5 files changed

+37
-12
lines changed

5 files changed

+37
-12
lines changed

arch/x86/include/asm/vmx.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,18 @@ enum vmcs_field {
500500
VMX_EPT_EXECUTABLE_MASK)
501501
#define VMX_EPT_MT_MASK (7ull << VMX_EPT_MT_EPTE_SHIFT)
502502

503+
static inline u8 vmx_eptp_page_walk_level(u64 eptp)
504+
{
505+
u64 encoded_level = eptp & VMX_EPTP_PWL_MASK;
506+
507+
if (encoded_level == VMX_EPTP_PWL_5)
508+
return 5;
509+
510+
/* @eptp must be pre-validated by the caller. */
511+
WARN_ON_ONCE(encoded_level != VMX_EPTP_PWL_4);
512+
return 4;
513+
}
514+
503515
/* The mask to use to trigger an EPT Misconfiguration in order to track MMIO */
504516
#define VMX_EPT_MISCONFIG_WX_VALUE (VMX_EPT_WRITABLE_MASK | \
505517
VMX_EPT_EXECUTABLE_MASK)

arch/x86/kvm/mmu/mmu.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5008,14 +5008,14 @@ EXPORT_SYMBOL_GPL(kvm_init_shadow_mmu);
50085008

50095009
static union kvm_mmu_role
50105010
kvm_calc_shadow_ept_root_page_role(struct kvm_vcpu *vcpu, bool accessed_dirty,
5011-
bool execonly)
5011+
bool execonly, u8 level)
50125012
{
50135013
union kvm_mmu_role role = {0};
50145014

50155015
/* SMM flag is inherited from root_mmu */
50165016
role.base.smm = vcpu->arch.root_mmu.mmu_role.base.smm;
50175017

5018-
role.base.level = PT64_ROOT_4LEVEL;
5018+
role.base.level = level;
50195019
role.base.gpte_is_8_bytes = true;
50205020
role.base.direct = false;
50215021
role.base.ad_disabled = !accessed_dirty;
@@ -5039,16 +5039,17 @@ void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly,
50395039
bool accessed_dirty, gpa_t new_eptp)
50405040
{
50415041
struct kvm_mmu *context = vcpu->arch.mmu;
5042+
u8 level = vmx_eptp_page_walk_level(new_eptp);
50425043
union kvm_mmu_role new_role =
50435044
kvm_calc_shadow_ept_root_page_role(vcpu, accessed_dirty,
5044-
execonly);
5045+
execonly, level);
50455046

50465047
__kvm_mmu_new_cr3(vcpu, new_eptp, new_role.base, false);
50475048

50485049
if (new_role.as_u64 == context->mmu_role.as_u64)
50495050
return;
50505051

5051-
context->shadow_root_level = PT64_ROOT_4LEVEL;
5052+
context->shadow_root_level = level;
50525053

50535054
context->nx = true;
50545055
context->ept_ad = accessed_dirty;
@@ -5057,7 +5058,7 @@ void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly,
50575058
context->sync_page = ept_sync_page;
50585059
context->invlpg = ept_invlpg;
50595060
context->update_pte = ept_update_pte;
5060-
context->root_level = PT64_ROOT_4LEVEL;
5061+
context->root_level = level;
50615062
context->direct_map = false;
50625063
context->mmu_role.as_u64 = new_role.as_u64;
50635064

arch/x86/kvm/mmu/paging_tmpl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
#define PT_GUEST_ACCESSED_SHIFT 8
6767
#define PT_HAVE_ACCESSED_DIRTY(mmu) ((mmu)->ept_ad)
6868
#define CMPXCHG cmpxchg64
69-
#define PT_MAX_FULL_LEVELS 4
69+
#define PT_MAX_FULL_LEVELS PT64_ROOT_MAX_LEVEL
7070
#else
7171
#error Invalid PTTYPE value
7272
#endif

arch/x86/kvm/vmx/nested.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,9 +2582,19 @@ static bool valid_ept_address(struct kvm_vcpu *vcpu, u64 address)
25822582
return false;
25832583
}
25842584

2585-
/* only 4 levels page-walk length are valid */
2586-
if (CC((address & VMX_EPTP_PWL_MASK) != VMX_EPTP_PWL_4))
2585+
/* Page-walk levels validity. */
2586+
switch (address & VMX_EPTP_PWL_MASK) {
2587+
case VMX_EPTP_PWL_5:
2588+
if (CC(!(vmx->nested.msrs.ept_caps & VMX_EPT_PAGE_WALK_5_BIT)))
2589+
return false;
2590+
break;
2591+
case VMX_EPTP_PWL_4:
2592+
if (CC(!(vmx->nested.msrs.ept_caps & VMX_EPT_PAGE_WALK_4_BIT)))
2593+
return false;
2594+
break;
2595+
default:
25872596
return false;
2597+
}
25882598

25892599
/* Reserved bits should not be set */
25902600
if (CC(address >> maxphyaddr || ((address >> 7) & 0x1f)))
@@ -6119,8 +6129,11 @@ void nested_vmx_setup_ctls_msrs(struct nested_vmx_msrs *msrs, u32 ept_caps)
61196129
/* nested EPT: emulate EPT also to L1 */
61206130
msrs->secondary_ctls_high |=
61216131
SECONDARY_EXEC_ENABLE_EPT;
6122-
msrs->ept_caps = VMX_EPT_PAGE_WALK_4_BIT |
6123-
VMX_EPTP_WB_BIT | VMX_EPT_INVEPT_BIT;
6132+
msrs->ept_caps =
6133+
VMX_EPT_PAGE_WALK_4_BIT |
6134+
VMX_EPT_PAGE_WALK_5_BIT |
6135+
VMX_EPTP_WB_BIT |
6136+
VMX_EPT_INVEPT_BIT;
61246137
if (cpu_has_vmx_ept_execute_only())
61256138
msrs->ept_caps |=
61266139
VMX_EPT_EXECUTE_ONLY_BIT;

arch/x86/kvm/vmx/vmx.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2985,9 +2985,8 @@ void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
29852985

29862986
static int get_ept_level(struct kvm_vcpu *vcpu)
29872987
{
2988-
/* Nested EPT currently only supports 4-level walks. */
29892988
if (is_guest_mode(vcpu) && nested_cpu_has_ept(get_vmcs12(vcpu)))
2990-
return 4;
2989+
return vmx_eptp_page_walk_level(nested_ept_get_cr3(vcpu));
29912990
if (cpu_has_vmx_ept_5levels() && (cpuid_maxphyaddr(vcpu) > 48))
29922991
return 5;
29932992
return 4;

0 commit comments

Comments
 (0)