VMX EPT misconfigurtion handler
cover VMX EPT misconfigurtion handler
Quoted 1 line
From the Intel docs:
An EPT misconfiguration occurs when, in the course of translation a guest-physical address, the logical processor encounters an EPT paging-structure entry that contains an unsupported value.
Handle this event and print useful information for diagnostics.
PATCH 1/5 KVM: VMX: more MSR_IA32_VMX_EPT_VPID_CAP capability bits
Required for EPT misconfiguration handler.
arch/x86/include/asm/vmx.h +7 -0
@@ -351,9 +351,16 @@ enum vmcs_field {#define VMX_EPT_EXTENT_INDIVIDUAL_ADDR 0#define VMX_EPT_EXTENT_CONTEXT 1#define VMX_EPT_EXTENT_GLOBAL 2 + +#define VMX_EPT_EXECUTE_ONLY_BIT (1ull) +#define VMX_EPT_PAGE_WALK_4_BIT (1ull << 6) +#define VMX_EPTP_UC_BIT (1ull << 8) +#define VMX_EPTP_WB_BIT (1ull << 14) +#define VMX_EPT_2MB_PAGE_BIT (1ull << 16)#define VMX_EPT_EXTENT_INDIVIDUAL_BIT (1ull << 24)#define VMX_EPT_EXTENT_CONTEXT_BIT (1ull << 25)#define VMX_EPT_EXTENT_GLOBAL_BIT (1ull << 26) +#define VMX_EPT_DEFAULT_GAW 3#define VMX_EPT_MAX_GAW 0x4#define VMX_EPT_MT_EPTE_SHIFT 3
arch/x86/kvm/vmx.c +20 -0
@@ -258,6 +258,26 @@ static inline bool cpu_has_vmx_flexpriorcpu_has_vmx_virtualize_apic_accesses (); } +static inline bool cpu_has_vmx_ept_execute_only (void ) +{ +return !!(vmx_capability.ept &VMX_EPT_EXECUTE_ONLY_BIT ); +} + +static inline bool cpu_has_vmx_eptp_uncacheable (void ) +{ +return !!(vmx_capability.ept &VMX_EPTP_UC_BIT ); +} + +static inline bool cpu_has_vmx_eptp_writeback (void ) +{ +return !!(vmx_capability.ept &VMX_EPTP_WB_BIT ); +} + +static inline bool cpu_has_vmx_ept_2m_page (void ) +{ +return !!(vmx_capability.ept &VMX_EPT_2MB_PAGE_BIT ); +} +static inline int cpu_has_vmx_invept_individual_addr (void ) {return !!(vmx_capability.ept &VMX_EPT_EXTENT_INDIVIDUAL_BIT );
PATCH 2/5 KVM: MMU: make for_each_shadow_entry aware of largepages
In reply to Marcelo TosattiThis way there is no need to add explicit checks in every for_each_shadow_entry user.
arch/x86/kvm/mmu.c +5 -0
@@ -1273,6 +1273,11 @@ static bool shadow_walk_okay(struct kvm_ {if (iterator->level <PT_PAGE_TABLE_LEVEL )return false; + +if (iterator->level ==PT_PAGE_TABLE_LEVEL ) +if (is_large_pte (*iterator->sptep)) +return false; + iterator->index =SHADOW_PT_INDEX (iterator->addr, iterator->level); iterator->sptep = ((u64 *)__va (iterator->shadow_addr)) + iterator->index;return true;
Re: [patch 2/5] KVM: MMU: make for_each_shadow_entry aware of largepages
Quoted 10 lines · Marcelo Tosatti
This way there is no need to add explicit checks in every
for_each_shadow_entryuser.Signed-off-by Marcelo Tosatti <[email protected]>Index:
kvm/arch/x86/kvm/mmu.c
===================================================================
arch/x86/kvm/mmu.c static bool shadow_walk_okay(struct kvm_ · line 1281
Quoted 9 lines
arch/x86/kvm/mmu.c +4
-0@@ -1273,6 +1273,11 @@ static bool shadow_walk_okay(struct kvm_ {if (iterator->level <PT_PAGE_TABLE_LEVEL )return false; + +if (iterator->level ==PT_PAGE_TABLE_LEVEL ) +if (is_large_pte (*iterator->sptep)) +return false;
s/==/>/?
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [patch 2/5] KVM: MMU: make for_each_shadow_entry aware of largepages
Quoted 6 lines · Avi Kivity
Quoted 5 lines · Marcelo Tosatti
This way there is no need to add explicit checks in every
for_each_shadow_entryuser.Signed-off-by Marcelo Tosatti <[email protected]>
Quoted 16 lines
Quoted 5 lines
Index:
kvm/arch/x86/kvm/mmu.c
===================================================================arch/x86/kvm/mmu.c static bool shadow_walk_okay(struct kvm_ · line 1281
Quoted 9 lines
arch/x86/kvm/mmu.c +4
-0@@ -1273,6 +1273,11 @@ static bool shadow_walk_okay(struct kvm_ {if (iterator->level <PT_PAGE_TABLE_LEVEL )return false; + +if (iterator->level ==PT_PAGE_TABLE_LEVEL ) +if (is_large_pte (*iterator->sptep)) +return false;s/==/>/?
Ah, it's actually fine. But changing == to >= will make it 1GBpage-ready.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [patch 2/5] KVM: MMU: make for_each_shadow_entry aware of largepages
Quoted 24 lines · Avi Kivity
Quoted 6 lines · Avi Kivity
Quoted 5 lines · Marcelo Tosatti
This way there is no need to add explicit checks in every
for_each_shadow_entryuser.Signed-off-by Marcelo Tosatti <[email protected]>Quoted 16 lines
Quoted 5 lines
Index:
kvm/arch/x86/kvm/mmu.c
===================================================================arch/x86/kvm/mmu.c static bool shadow_walk_okay(struct kvm_ · line 1281
Quoted 9 lines
arch/x86/kvm/mmu.c +4
-0@@ -1273,6 +1273,11 @@ static bool shadow_walk_okay(struct kvm_ {if (iterator->level <PT_PAGE_TABLE_LEVEL )return false; + +if (iterator->level ==PT_PAGE_TABLE_LEVEL ) +if (is_large_pte (*iterator->sptep)) +return false;s/==/>/?
Quoted 1 line
Ah, it's actually fine. But changing == to >= will make it 1GBpage-ready.
Humpf, better check level explicitly before interpreting bit 7, so lets skip this for 1GB pages.
Re: [patch 2/5] KVM: MMU: make for_each_shadow_entry aware of largepages
Quoted 14 lines · Marcelo Tosatti
Quoted 14 lines
Quoted 14 lines
arch/x86/kvm/mmu.c static bool shadow_walk_okay(struct kvm_ · line 1282
Quoted 11 lines
arch/x86/kvm/mmu.c +4
-0@@ -1273,6 +1273,11 @@ static bool shadow_walk_okay(struct kvm_ {if (iterator->level <PT_PAGE_TABLE_LEVEL )return false; + +if (iterator->level ==PT_PAGE_TABLE_LEVEL ) +if (is_large_pte (*iterator->sptep)) +return false;s/==/>/?
Quoted 6 lines
Quoted 2 lines
Ah, it's actually fine. But changing == to >= will make it 1GBpage-ready.
Humpf, better check level explicitly before interpreting bit 7, so lets skip this for 1GB pages.
Okay. But I'm rewriting shadow_walk_* afterwards.
Signature · 1 line
error compiling committee.c: too many arguments to function
PATCH 3/5 KVM: MMU: add kvm_mmu_shadow_walk helper
In reply to Marcelo TosattiRequired by EPT misconfiguration handler.
arch/x86/kvm/mmu.c +20 -0
@@ -3013,6 +3013,26 @@ out:return r; } +void kvm_mmu_shadow_walk (struct kvm_vcpu *vcpu,u64 addr, +struct mmu_shadow_walk *walk) +{ +struct kvm_shadow_walk_iterator iterator; + +spin_lock (&vcpu->kvm->mmu_lock); +for_each_shadow_entry (vcpu, addr, iterator) { +int err; + + err = walk->fn (vcpu, iterator.sptep, iterator.level, walk); +if (err) +break ; + +if (!is_shadow_present_pte (*iterator.sptep)) +break ; + } +spin_unlock (&vcpu->kvm->mmu_lock); +} +EXPORT_SYMBOL (kvm_mmu_shadow_walk); +#ifdef AUDIT static const char *audit_msg;
arch/x86/kvm/mmu.h +8 -0
@@ -37,6 +37,14 @@#define PT32_ROOT_LEVEL 2#define PT32E_ROOT_LEVEL 3 +struct mmu_shadow_walk { +int (*fn) (struct kvm_vcpu *vcpu,u64 *sptep,int level, +struct mmu_shadow_walk *walk); +}; + +void kvm_mmu_shadow_walk (struct kvm_vcpu *vcpu,u64 addr, +struct mmu_shadow_walk *walk); +static inline void kvm_mmu_free_some_pages (struct kvm_vcpu *vcpu) {if (unlikely (vcpu->kvm->arch.n_free_mmu_pages <KVM_MIN_FREE_MMU_PAGES ))
Re: [patch 3/5] KVM: MMU: add kvm_mmu_shadow_walk helper
Quoted 23 lines · Marcelo Tosatti
Required by EPT misconfiguration handler.
Signed-off-by Marcelo Tosatti <[email protected]>Index:
kvm/arch/x86/kvm/mmu.c
===================================================================arch/x86/kvm/mmu.c +11
-0@@ -3013,6 +3013,26 @@ out:return r; } +void kvm_mmu_shadow_walk (struct kvm_vcpu *vcpu,u64 addr, +struct mmu_shadow_walk *walk) +{ +struct kvm_shadow_walk_iterator iterator; + +spin_lock (&vcpu->kvm->mmu_lock); +for_each_shadow_entry (vcpu, addr, iterator) { +int err; + + err = walk->fn (vcpu, iterator.sptep, iterator.level, walk); +if (err)
arch/x86/kvm/mmu.c out: · line 3036
Quoted 10 lines
arch/x86/kvm/mmu.c +9
-0+break ; + +if (!is_shadow_present_pte (*iterator.sptep)) +break ; + } +spin_unlock (&vcpu->kvm->mmu_lock); +} +EXPORT_SYMBOL (kvm_mmu_shadow_walk); +
Isn't it simpler to invoke for_each_shadow_entry(), instead of defining a callback and calling it?
We had those callbacks once, then switched to for_each.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [patch 3/5] KVM: MMU: add kvm_mmu_shadow_walk helper
Quoted 37 lines · Avi Kivity
Quoted 22 lines · Marcelo Tosatti
Required by EPT misconfiguration handler.
Signed-off-by Marcelo Tosatti <[email protected]>Index:
kvm/arch/x86/kvm/mmu.c
===================================================================arch/x86/kvm/mmu.c +10
-0@@ -3013,6 +3013,26 @@ out:return r; } +void kvm_mmu_shadow_walk(struct kvm_vcpu *vcpu, u64 addr, + struct mmu_shadow_walk *walk) +{ + struct kvm_shadow_walk_iterator iterator; + +spin_lock (&vcpu->kvm->mmu_lock); +for_each_shadow_entry (vcpu, addr, iterator) { +int err; + + err = walk->fn (vcpu, iterator.sptep, iterator.level, walk); +if (err)arch/x86/kvm/mmu.c out: · line 3035
Quoted 10 lines
arch/x86/kvm/mmu.c +9
-0+break ; + +if (!is_shadow_present_pte (*iterator.sptep)) +break ; + } +spin_unlock (&vcpu->kvm->mmu_lock); +} +EXPORT_SYMBOL (kvm_mmu_shadow_walk); +Isn't it simpler to invoke
for_each_shadow_entry(), instead of defining a callback and calling it?
Quoted 1 line
We had those callbacks once, then switched to
for_each.
The point is its exported to use in a external module (kvm-intel.ko), so you hide the details (such as locking) in the kvm_mmu_shadow_walk helper. Let me know how do you prefer this to be.
Re: [patch 3/5] KVM: MMU: add kvm_mmu_shadow_walk helper
Quoted 9 lines · Marcelo Tosatti
Quoted 4 lines
Isn't it simpler to invoke
for_each_shadow_entry(), instead of defining a callback and calling it?We had those callbacks once, then switched to
for_each.The point is its exported to use in a external module (kvm-intel.ko), so you hide the details (such as locking) in the
kvm_mmu_shadow_walkhelper. Let me know how do you prefer this to be.
Ah, you're right.
I don't think it's worthwhile to add all this just for debugging. You can add a function that dumps the spte chain as well as the features MSR, and we can decode it by hand when we see it. Disadvantage is more work for us when we hit the bug, but at least that function is reusable in other contexts.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [patch 3/5] KVM: MMU: add kvm_mmu_shadow_walk helper
Quoted 12 lines · Avi Kivity
Quoted 10 lines · Marcelo Tosatti
Quoted 5 lines
Isn't it simpler to invoke
for_each_shadow_entry(), instead of defining a callback and calling it?We had those callbacks once, then switched to
for_each.The point is its exported to use in a external module (kvm-intel.ko), so you hide the details (such as locking) in the
kvm_mmu_shadow_walkhelper. Let me know how do you prefer this to be.
Quoted 7 lines
Ah, you're right.
I don't think it's worthwhile to add all this just for debugging. You can add a function that dumps the spte chain as well as the features MSR, and we can decode it by hand when we see it. Disadvantage is more work for us when we hit the bug, but at least that function is reusable in other contexts.
The problem is if someone hits the bug who has no possibility of reporting the printks. Nice thing about the WARN_ON's there is you can look up kerneloops.org, match the line number to the kernel version, and narrow down what bits are wrong (you still need reporter to send contents of dmesg for full spte value).
Also the bit-by-bit validity checks (the inspect_spte function in vmx.c) can be used in the mmu audit code (thats the reason for print=0 in the callback parameters), so it is reusable in other contexes.
What you dislike is hardcoding the bits checked in C code? Don't worry about the level stuff, will be handled next.
Re: [patch 3/5] KVM: MMU: add kvm_mmu_shadow_walk helper
Quoted 11 lines · Marcelo Tosatti
Quoted 4 lines
Ah, you're right.
I don't think it's worthwhile to add all this just for debugging. You can add a function that dumps the spte chain as well as the features MSR, and we can decode it by hand when we see it. Disadvantage is more work for us when we hit the bug, but at least that function is reusable in other contexts.
The problem is if someone hits the bug who has no possibility of reporting the printks. Nice thing about the WARN_ON's there is you can look up kerneloops.org, match the line number to the kernel version, and narrow down what bits are wrong (you still need reporter to send contents of dmesg for full spte value).
Well, we can KERN_WARNING instead of KERN_DEBUG in that printout.
Quoted 7 lines
Also the bit-by-bit validity checks (the
inspect_sptefunction invmx.c) can be used in the mmu audit code (thats the reason for print=0 in the callback parameters), so it is reusable in other contexes.What you dislike is hardcoding the bits checked in C code? Don't worry about the level stuff, will be handled next.
I just don't want to introduce yet another function-callback pair for such a small thing. No objection to the code itself.
Printing out the spte hierarchy seems a good idea in other bug contexts, so at least that function is reusable.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [patch 3/5] KVM: MMU: add kvm_mmu_shadow_walk helper
Quoted 5 lines · Avi Kivity
I just don't want to introduce yet another function-callback pair for such a small thing. No objection to the code itself.
Printing out the spte hierarchy seems a good idea in other bug contexts, so at least that function is reusable.
Here's one that preserves all information but is simple to use:
kvm_mmu_get_spte_hierarchy(vcpu, gva, int *nr_sptes, u64 sptes[4])
So you can later do detailed checks on the sptes, but it doesn't expose yet another generic walker.
Signature · 1 line
Do not meddle in the internals of kernels, for they are subtle and quick to panic.
PATCH 1/5 KVM: VMX: more MSR_IA32_VMX_EPT_VPID_CAP capability bits
Required for EPT misconfiguration handler.
arch/x86/include/asm/vmx.h +7 -0
@@ -352,9 +352,16 @@ enum vmcs_field {#define VMX_EPT_EXTENT_INDIVIDUAL_ADDR 0#define VMX_EPT_EXTENT_CONTEXT 1#define VMX_EPT_EXTENT_GLOBAL 2 + +#define VMX_EPT_EXECUTE_ONLY_BIT (1ull) +#define VMX_EPT_PAGE_WALK_4_BIT (1ull << 6) +#define VMX_EPTP_UC_BIT (1ull << 8) +#define VMX_EPTP_WB_BIT (1ull << 14) +#define VMX_EPT_2MB_PAGE_BIT (1ull << 16)#define VMX_EPT_EXTENT_INDIVIDUAL_BIT (1ull << 24)#define VMX_EPT_EXTENT_CONTEXT_BIT (1ull << 25)#define VMX_EPT_EXTENT_GLOBAL_BIT (1ull << 26) +#define VMX_EPT_DEFAULT_GAW 3#define VMX_EPT_MAX_GAW 0x4#define VMX_EPT_MT_EPTE_SHIFT 3
arch/x86/kvm/vmx.c +20 -0
@@ -270,6 +270,26 @@ static inline bool cpu_has_vmx_flexpriorcpu_has_vmx_virtualize_apic_accesses (); } +static inline bool cpu_has_vmx_ept_execute_only (void ) +{ +return !!(vmx_capability.ept &VMX_EPT_EXECUTE_ONLY_BIT ); +} + +static inline bool cpu_has_vmx_eptp_uncacheable (void ) +{ +return !!(vmx_capability.ept &VMX_EPTP_UC_BIT ); +} + +static inline bool cpu_has_vmx_eptp_writeback (void ) +{ +return !!(vmx_capability.ept &VMX_EPTP_WB_BIT ); +} + +static inline bool cpu_has_vmx_ept_2m_page (void ) +{ +return !!(vmx_capability.ept &VMX_EPT_2MB_PAGE_BIT ); +} +static inline int cpu_has_vmx_invept_individual_addr (void ) {return !!(vmx_capability.ept &VMX_EPT_EXTENT_INDIVIDUAL_BIT );
PATCH 2/5 KVM: MMU: make for_each_shadow_entry aware of largepages
In reply to Avi KivityThis way there is no need to add explicit checks in every for_each_shadow_entry user.
arch/x86/kvm/mmu.c +5 -0
@@ -1273,6 +1273,11 @@ static bool shadow_walk_okay(struct kvm_ {if (iterator->level <PT_PAGE_TABLE_LEVEL )return false; + +if (iterator->level ==PT_PAGE_TABLE_LEVEL ) +if (is_large_pte (*iterator->sptep)) +return false; + iterator->index =SHADOW_PT_INDEX (iterator->addr, iterator->level); iterator->sptep = ((u64 *)__va (iterator->shadow_addr)) + iterator->index;return true;
PATCH 3/5 KVM: MMU: add kvm_mmu_get_spte_hierarchy helper
In reply to Avi KivityRequired by EPT misconfiguration handler.
arch/x86/kvm/mmu.c +18 -0
@@ -3013,6 +3013,24 @@ out:return r; } +int kvm_mmu_get_spte_hierarchy (struct kvm_vcpu *vcpu,u64 addr,u64 *sptes[4 ]) +{ +struct kvm_shadow_walk_iterator iterator; +int nr_sptes =0 ; + +spin_lock (&vcpu->kvm->mmu_lock); +for_each_shadow_entry (vcpu, addr, iterator) { + sptes[iterator.level-1 ] = iterator.sptep; + nr_sptes++; +if (!is_shadow_present_pte (*iterator.sptep)) +break ; + } +spin_unlock (&vcpu->kvm->mmu_lock); + +return nr_sptes; +} +EXPORT_SYMBOL_GPL (kvm_mmu_get_spte_hierarchy); +#ifdef AUDIT static const char *audit_msg;
arch/x86/kvm/mmu.h +2 -0
@@ -37,6 +37,8 @@#define PT32_ROOT_LEVEL 2#define PT32E_ROOT_LEVEL 3 +int kvm_mmu_get_spte_hierarchy (struct kvm_vcpu *vcpu,u64 addr,u64 *sptes[4 ]); +static inline void kvm_mmu_free_some_pages (struct kvm_vcpu *vcpu) {if (unlikely (vcpu->kvm->arch.n_free_mmu_pages <KVM_MIN_FREE_MMU_PAGES ))
Re: [patch 3/5] KVM: MMU: add kvm_mmu_get_spte_hierarchy helper
Quoted 11 lines · Marcelo Tosatti
Required by EPT misconfiguration handler.
Signed-off-by Marcelo Tosatti <[email protected]>Index:
kvm/arch/x86/kvm/mmu.c
===================================================================arch/x86/kvm/mmu.c +0
-0@@ -3013,6 +3013,24 @@ out:return r; }
arch/x86/kvm/mmu.c out: · line 3024
Quoted 10 lines
arch/x86/kvm/mmu.c +8
-0+int kvm_mmu_get_spte_hierarchy (struct kvm_vcpu *vcpu,u64 addr,u64 *sptes[4 ]) +{ +struct kvm_shadow_walk_iterator iterator; +int nr_sptes =0 ; + +spin_lock (&vcpu->kvm->mmu_lock); +for_each_shadow_entry (vcpu, addr, iterator) { + sptes[iterator.level-1 ] = iterator.sptep;
Returning a pointer...
arch/x86/kvm/mmu.c out: · line 3035
Quoted 6 lines
arch/x86/kvm/mmu.c +5
-0+ nr_sptes++; +if (!is_shadow_present_pte (*iterator.sptep)) +break ; + } +spin_unlock (&vcpu->kvm->mmu_lock);
... and unlocking the lock that protects it.
True, this is called in extreme cases, but I think you can dereference the pointer in the function just as easily.
Signature · 1 line
error compiling committee.c: too many arguments to function
cover VMX EPT misconfiguration handler v3
Addressing comments.
Avi Kivity
AppliedRe: [patch 0/5] VMX EPT misconfiguration handler v3
Quoted 4 lines · Marcelo Tosatti
Addressing comments.
Applied all, thanks.
Signature · 1 line
error compiling committee.c: too many arguments to function
PATCH 1/5 KVM: VMX: more MSR_IA32_VMX_EPT_VPID_CAP capability bits
In reply to Avi KivityRequired for EPT misconfiguration handler.
arch/x86/include/asm/vmx.h +7 -0
@@ -352,9 +352,16 @@ enum vmcs_field {#define VMX_EPT_EXTENT_INDIVIDUAL_ADDR 0#define VMX_EPT_EXTENT_CONTEXT 1#define VMX_EPT_EXTENT_GLOBAL 2 + +#define VMX_EPT_EXECUTE_ONLY_BIT (1ull) +#define VMX_EPT_PAGE_WALK_4_BIT (1ull << 6) +#define VMX_EPTP_UC_BIT (1ull << 8) +#define VMX_EPTP_WB_BIT (1ull << 14) +#define VMX_EPT_2MB_PAGE_BIT (1ull << 16)#define VMX_EPT_EXTENT_INDIVIDUAL_BIT (1ull << 24)#define VMX_EPT_EXTENT_CONTEXT_BIT (1ull << 25)#define VMX_EPT_EXTENT_GLOBAL_BIT (1ull << 26) +#define VMX_EPT_DEFAULT_GAW 3#define VMX_EPT_MAX_GAW 0x4#define VMX_EPT_MT_EPTE_SHIFT 3
arch/x86/kvm/vmx.c +20 -0
@@ -270,6 +270,26 @@ static inline bool cpu_has_vmx_flexpriorcpu_has_vmx_virtualize_apic_accesses (); } +static inline bool cpu_has_vmx_ept_execute_only (void ) +{ +return !!(vmx_capability.ept &VMX_EPT_EXECUTE_ONLY_BIT ); +} + +static inline bool cpu_has_vmx_eptp_uncacheable (void ) +{ +return !!(vmx_capability.ept &VMX_EPTP_UC_BIT ); +} + +static inline bool cpu_has_vmx_eptp_writeback (void ) +{ +return !!(vmx_capability.ept &VMX_EPTP_WB_BIT ); +} + +static inline bool cpu_has_vmx_ept_2m_page (void ) +{ +return !!(vmx_capability.ept &VMX_EPT_2MB_PAGE_BIT ); +} +static inline int cpu_has_vmx_invept_individual_addr (void ) {return !!(vmx_capability.ept &VMX_EPT_EXTENT_INDIVIDUAL_BIT );
PATCH 2/5 KVM: MMU: make for_each_shadow_entry aware of largepages
In reply to Avi KivityThis way there is no need to add explicit checks in every for_each_shadow_entry user.
arch/x86/kvm/mmu.c +5 -0
@@ -1273,6 +1273,11 @@ static bool shadow_walk_okay(struct kvm_ {if (iterator->level <PT_PAGE_TABLE_LEVEL )return false; + +if (iterator->level ==PT_PAGE_TABLE_LEVEL ) +if (is_large_pte (*iterator->sptep)) +return false; + iterator->index =SHADOW_PT_INDEX (iterator->addr, iterator->level); iterator->sptep = ((u64 *)__va (iterator->shadow_addr)) + iterator->index;return true;
PATCH 3/5 KVM: MMU: add kvm_mmu_get_spte_hierarchy helper
In reply to Avi KivityRequired by EPT misconfiguration handler.
arch/x86/kvm/mmu.c +18 -0
@@ -3013,6 +3013,24 @@ out:return r; } +int kvm_mmu_get_spte_hierarchy (struct kvm_vcpu *vcpu,u64 addr,u64 sptes[4 ]) +{ +struct kvm_shadow_walk_iterator iterator; +int nr_sptes =0 ; + +spin_lock (&vcpu->kvm->mmu_lock); +for_each_shadow_entry (vcpu, addr, iterator) { + sptes[iterator.level-1 ] = *iterator.sptep; + nr_sptes++; +if (!is_shadow_present_pte (*iterator.sptep)) +break ; + } +spin_unlock (&vcpu->kvm->mmu_lock); + +return nr_sptes; +} +EXPORT_SYMBOL_GPL (kvm_mmu_get_spte_hierarchy); +#ifdef AUDIT static const char *audit_msg;
arch/x86/kvm/mmu.h +2 -0
@@ -37,6 +37,8 @@#define PT32_ROOT_LEVEL 2#define PT32E_ROOT_LEVEL 3 +int kvm_mmu_get_spte_hierarchy (struct kvm_vcpu *vcpu,u64 addr,u64 sptes[4 ]); +static inline void kvm_mmu_free_some_pages (struct kvm_vcpu *vcpu) {if (unlikely (vcpu->kvm->arch.n_free_mmu_pages <KVM_MIN_FREE_MMU_PAGES ))
PATCH 4/5 KVM: VMX: EPT misconfiguration handler
In reply to Avi KivityHandler for EPT misconfiguration which checks for valid state in the shadow pagetables, printing the spte on each level.
The separate WARN_ONs are useful for kerneloops.org.
arch/x86/kvm/vmx.c +85 -1
@@ -3233,6 +3233,89 @@ static int handle_ept_violation(struct kreturn kvm_mmu_page_fault (vcpu, gpa &PAGE_MASK ,0 ); } +static u64 ept_rsvd_mask (u64 spte,int level) +{ +int i; +u64 mask =0 ; + +for (i =51 ; i > boot_cpu_data.x86_phys_bits; i--) + mask |= (1ULL << i); + +if (level >2 ) +/* bits 7:3 reserved */ + mask |=0xf8 ; +else if (level ==2 ) { +if (spte & (1ULL <<7 )) +/* 2MB ref, bits 20:12 reserved */ + mask |=0x1ff000 ; +else +/* bits 6:3 reserved */ + mask |=0x78 ; + } + +return mask; +} + +static void ept_misconfig_inspect_spte (struct kvm_vcpu *vcpu,u64 spte, +int level) +{ +printk (KERN_ERR "%s: spte 0x%llx level %d\n" , __func__, spte, level); + +/* 010b (write-only) */ +WARN_ON ((spte &0x7 ) ==0x2 ); + +/* 110b (write/execute) */ +WARN_ON ((spte &0x7 ) ==0x6 ); + +/* 100b (execute-only) and value not supported by logical processor */ +if (!cpu_has_vmx_ept_execute_only ()) +WARN_ON ((spte &0x7 ) ==0x4 ); + +/* not 000b */ +if ((spte &0x7 )) { +u64 rsvd_bits = spte &ept_rsvd_mask (spte, level); + +if (rsvd_bits !=0 ) { +printk (KERN_ERR "%s: rsvd_bits = 0x%llx\n" , + __func__, rsvd_bits); +WARN_ON (1 ); + } + +if (level ==1 || (level ==2 && (spte & (1ULL <<7 )))) { +u64 ept_mem_type = (spte &0x38 ) >>3 ; + +if (ept_mem_type ==2 || ept_mem_type ==3 || + ept_mem_type ==7 ) { +printk (KERN_ERR "%s: ept_mem_type=0x%llx\n" , + __func__, ept_mem_type); +WARN_ON (1 ); + } + } + } +} + +static int handle_ept_misconfig (struct kvm_vcpu *vcpu,struct kvm_run *kvm_run) +{ +u64 sptes[4 ]; +int nr_sptes, i; +gpa_t gpa; + + gpa =vmcs_read64 (GUEST_PHYSICAL_ADDRESS ); + +printk (KERN_ERR "EPT: Misconfiguration.\n" ); +printk (KERN_ERR "EPT: GPA: 0x%llx\n" , gpa); + + nr_sptes =kvm_mmu_get_spte_hierarchy (vcpu, gpa, sptes); + +for (i =PT64_ROOT_LEVEL ; i >PT64_ROOT_LEVEL - nr_sptes; --i) +ept_misconfig_inspect_spte (vcpu, sptes[i-1 ], i); + + kvm_run->exit_reason =KVM_EXIT_UNKNOWN ; + kvm_run->hw.hardware_exit_reason =EXIT_REASON_EPT_MISCONFIG ; + +return 0 ; +} +static int handle_nmi_window (struct kvm_vcpu *vcpu,struct kvm_run *kvm_run) {u32 cpu_based_vm_exec_control;
@@ -3303,8 +3386,9 @@ static int (*kvm_vmx_exit_handlers[])(st [EXIT_REASON_APIC_ACCESS] = handle_apic_access, [EXIT_REASON_WBINVD] = handle_wbinvd, [EXIT_REASON_TASK_SWITCH] = handle_task_switch, - [EXIT_REASON_EPT_VIOLATION] = handle_ept_violation, [EXIT_REASON_MCE_DURING_VMENTRY] = handle_machine_check, + [EXIT_REASON_EPT_VIOLATION] = handle_ept_violation, + [EXIT_REASON_EPT_MISCONFIG] = handle_ept_misconfig, }; static const int kvm_vmx_max_exit_handlers =
PATCH 5/5 KVM: VMX: conditionally disable 2M pages
In reply to Avi KivityDisable usage of 2M pages if VMX_EPT_2MB_PAGE_BIT (bit 16) is clear in MSR_IA32_VMX_EPT_VPID_CAP and EPT is enabled.
arch/x86/kvm/vmx.c +3 -0
@@ -1393,6 +1393,9 @@ static __init int hardware_setup(void)if (!cpu_has_vmx_tpr_shadow ()) kvm_x86_ops->update_cr8_intercept =NULL ; +if (enable_ept && !cpu_has_vmx_ept_2m_page ()) +kvm_disable_largepages (); +return alloc_kvm_area (); }
include/linux/kvm_host.h +1 -0
@@ -219,6 +219,7 @@ int kvm_arch_set_memory_region(struct kvstruct kvm_userspace_memory_region *mem, struct kvm_memory_slot old, int user_alloc); +void kvm_disable_largepages (void );void kvm_arch_flush_shadow (struct kvm *kvm);gfn_t unalias_gfn (struct kvm *kvm,gfn_t gfn);struct page *gfn_to_page (struct kvm *kvm,gfn_t gfn);
virt/kvm/kvm_main.c +12 -2
@@ -85,6 +85,8 @@ static long kvm_vcpu_ioctl(struct file *static bool kvm_rebooting; +static bool largepages_disabled = false; +#ifdef KVM_CAP_DEVICE_ASSIGNMENT static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev (struct list_head *head,int assigned_dev_id)
@@ -1171,9 +1173,11 @@ int __kvm_set_memory_region(struct kvm * ugfn = new.userspace_addr >>PAGE_SHIFT ;/* * If the gfn and userspace address are not aligned wrt each -* other, disable large page support for this slot +* other, or if explicitly asked to, disable large page +* support for this slot */ -if ((base_gfn ^ ugfn) & (KVM_PAGES_PER_HPAGE -1 )) +if ((base_gfn ^ ugfn) & (KVM_PAGES_PER_HPAGE -1 ) || + largepages_disabled)for (i =0 ; i < largepages; ++i) new.lpage_info[i].write_count =1 ; }
@@ -1286,6 +1290,12 @@ out:return r; } +void kvm_disable_largepages (void ) +{ + largepages_disabled = true; +} +EXPORT_SYMBOL_GPL (kvm_disable_largepages); +int is_error_page (struct page *page) {return page == bad_page;
cover VMX EPT misconfiguration handler v2
In reply to Avi KivityAddressing comments.
PATCH 4/5 KVM: VMX: EPT misconfiguration handler
In reply to Avi KivityHandler for EPT misconfiguration which checks for valid state in the shadow pagetables, printing the spte on each level.
The separate WARN_ONs are useful for kerneloops.org.
arch/x86/kvm/vmx.c +86 -1
@@ -3233,6 +3233,90 @@ static int handle_ept_violation(struct kreturn kvm_mmu_page_fault (vcpu, gpa &PAGE_MASK ,0 ); } +static u64 ept_rsvd_mask (u64 *sptep,int level) +{ +int i; +u64 mask =0 ; + +for (i =51 ; i > boot_cpu_data.x86_phys_bits; i--) + mask |= (1ULL << i); + +if (level >2 ) +/* bits 7:3 reserved */ + mask |=0xf8 ; +else if (level ==2 ) { +if (*sptep & (1ULL <<7 )) +/* 2MB ref, bits 20:12 reserved */ + mask |=0x1ff000 ; +else +/* bits 6:3 reserved */ + mask |=0x78 ; + } + +return mask; +} + +static void ept_misconfig_inspect_spte (struct kvm_vcpu *vcpu,u64 *sptep, +int level) +{ +printk (KERN_ERR "%s: sptep %p spte 0x%llx level %d\n" , + __func__, sptep, *sptep, level); + +/* 010b (write-only) */ +WARN_ON ((*sptep &0x7 ) ==0x2 ); + +/* 110b (write/execute) */ +WARN_ON ((*sptep &0x7 ) ==0x6 ); + +/* 100b (execute-only) and value not supported by logical processor */ +if (!cpu_has_vmx_ept_execute_only ()) +WARN_ON ((*sptep &0x7 ) ==0x4 ); + +/* not 000b */ +if ((*sptep &0x7 )) { +u64 rsvd_bits = *sptep &ept_rsvd_mask (sptep, level); + +if (rsvd_bits !=0 ) { +printk (KERN_ERR "%s: rsvd_bits = 0x%llx\n" , + __func__, rsvd_bits); +WARN_ON (1 ); + } + +if (level ==1 || (level ==2 && (*sptep & (1ULL <<7 )))) { +u64 ept_mem_type = (*sptep &0x38 ) >>3 ; + +if (ept_mem_type ==2 || ept_mem_type ==3 || + ept_mem_type ==7 ) { +printk (KERN_ERR "%s: ept_mem_type=0x%llx\n" , + __func__, ept_mem_type); +WARN_ON (1 ); + } + } + } +} + +static int handle_ept_misconfig (struct kvm_vcpu *vcpu,struct kvm_run *kvm_run) +{ +u64 *sptes[4 ]; +int nr_sptes, i; +gpa_t gpa; + + gpa =vmcs_read64 (GUEST_PHYSICAL_ADDRESS ); + +printk (KERN_ERR "EPT: Misconfiguration.\n" ); +printk (KERN_ERR "EPT: GPA: 0x%llx\n" , gpa); + + nr_sptes =kvm_mmu_get_spte_hierarchy (vcpu, gpa, sptes); + +for (i =PT64_ROOT_LEVEL ; i >PT64_ROOT_LEVEL - nr_sptes; --i) +ept_misconfig_inspect_spte (vcpu, sptes[i-1 ], i); + + kvm_run->exit_reason =KVM_EXIT_UNKNOWN ; + kvm_run->hw.hardware_exit_reason =EXIT_REASON_EPT_MISCONFIG ; + +return 0 ; +} +static int handle_nmi_window (struct kvm_vcpu *vcpu,struct kvm_run *kvm_run) {u32 cpu_based_vm_exec_control;
@@ -3303,8 +3387,9 @@ static int (*kvm_vmx_exit_handlers[])(st [EXIT_REASON_APIC_ACCESS] = handle_apic_access, [EXIT_REASON_WBINVD] = handle_wbinvd, [EXIT_REASON_TASK_SWITCH] = handle_task_switch, - [EXIT_REASON_EPT_VIOLATION] = handle_ept_violation, [EXIT_REASON_MCE_DURING_VMENTRY] = handle_machine_check, + [EXIT_REASON_EPT_VIOLATION] = handle_ept_violation, + [EXIT_REASON_EPT_MISCONFIG] = handle_ept_misconfig, }; static const int kvm_vmx_max_exit_handlers =
PATCH 5/5 KVM: VMX: conditionally disable 2M pages
In reply to Avi KivityDisable usage of 2M pages if VMX_EPT_2MB_PAGE_BIT (bit 16) is clear in MSR_IA32_VMX_EPT_VPID_CAP and EPT is enabled.
arch/x86/kvm/vmx.c +3 -0
@@ -1393,6 +1393,9 @@ static __init int hardware_setup(void)if (!cpu_has_vmx_tpr_shadow ()) kvm_x86_ops->update_cr8_intercept =NULL ; +if (enable_ept && !cpu_has_vmx_ept_2m_page ()) +kvm_disable_largepages (); +return alloc_kvm_area (); }
include/linux/kvm_host.h +1 -0
@@ -219,6 +219,7 @@ int kvm_arch_set_memory_region(struct kvstruct kvm_userspace_memory_region *mem, struct kvm_memory_slot old, int user_alloc); +void kvm_disable_largepages (void );void kvm_arch_flush_shadow (struct kvm *kvm);gfn_t unalias_gfn (struct kvm *kvm,gfn_t gfn);struct page *gfn_to_page (struct kvm *kvm,gfn_t gfn);
virt/kvm/kvm_main.c +12 -2
@@ -85,6 +85,8 @@ static long kvm_vcpu_ioctl(struct file *static bool kvm_rebooting; +static bool largepages_disabled = false; +#ifdef KVM_CAP_DEVICE_ASSIGNMENT static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev (struct list_head *head,int assigned_dev_id)
@@ -1171,9 +1173,11 @@ int __kvm_set_memory_region(struct kvm * ugfn = new.userspace_addr >>PAGE_SHIFT ;/* * If the gfn and userspace address are not aligned wrt each -* other, disable large page support for this slot +* other, or if explicitly asked to, disable large page +* support for this slot */ -if ((base_gfn ^ ugfn) & (KVM_PAGES_PER_HPAGE -1 )) +if ((base_gfn ^ ugfn) & (KVM_PAGES_PER_HPAGE -1 ) || + largepages_disabled)for (i =0 ; i < largepages; ++i) new.lpage_info[i].write_count =1 ; }
@@ -1286,6 +1290,12 @@ out:return r; } +void kvm_disable_largepages (void ) +{ + largepages_disabled = true; +} +EXPORT_SYMBOL_GPL (kvm_disable_largepages); +int is_error_page (struct page *page) {return page == bad_page;
PATCH 4/5 KVM: VMX: EPT misconfiguration handler
In reply to Marcelo TosattiHandler for EPT misconfiguration which checks for valid state in the shadow pagetables, printing the spte on each level.
The separate WARN_ONs are useful for kerneloops.org.
arch/x86/kvm/vmx.c +96 -1
@@ -3173,6 +3173,100 @@ static int handle_ept_violation(struct kreturn kvm_mmu_page_fault (vcpu, gpa &PAGE_MASK ,0 ); } +static u64 ept_rsvd_mask (u64 *sptep,int level) +{ +int i; +u64 mask =0 ; + +for (i =51 ; i > boot_cpu_data.x86_phys_bits; i--) + mask |= (1ULL << i); + +if (level >2 ) +/* bits 7:3 reserved */ + mask |=0xf8 ; +else if (level ==2 ) { +if (*sptep & (1ULL <<7 )) +/* 2MB ref, bits 20:12 reserved */ + mask |=0x1ff000 ; +else +/* bits 6:3 reserved */ + mask |=0x78 ; + } + +return mask; +} + +struct ept_inspect_spte { +int print; +struct mmu_shadow_walk walk; +}; + +static int ept_misconfig_inspect_spte (struct kvm_vcpu *vcpu,u64 *sptep, +int level, +struct mmu_shadow_walk *walk) +{ +struct ept_inspect_spte *iept =container_of (walk, + struct ept_inspect_spte, walk); + +if (iept->print) +printk (KERN_ERR "%s: sptep %p spte 0x%llx level %d\n" , + __func__, sptep, *sptep, level); + +/* 010b (write-only) */ +WARN_ON ((*sptep &0x7 ) ==0x2 ); + +/* 110b (write/execute) */ +WARN_ON ((*sptep &0x7 ) ==0x6 ); + +/* 100b (execute-only) and value not supported by logical processor */ +if (!cpu_has_vmx_ept_execute_only ()) +WARN_ON ((*sptep &0x7 ) ==0x4 ); + +/* not 000b */ +if ((*sptep &0x7 )) { +u64 rsvd_bits = *sptep &ept_rsvd_mask (sptep, level); + +if (rsvd_bits !=0 ) { +printk (KERN_ERR "%s: rsvd_bits = 0x%llx\n" , + __func__, rsvd_bits); +WARN_ON (1 ); + } + +if (level ==1 || (level ==2 && (*sptep & (1ULL <<7 )))) { +u64 ept_mem_type = (*sptep &0x38 ) >>3 ; + +if (ept_mem_type ==2 || ept_mem_type ==3 || + ept_mem_type ==7 ) { +printk (KERN_ERR "%s: ept_mem_type=0x%llx\n" , + __func__, ept_mem_type); +WARN_ON (1 ); + } + } + } + +return 0 ; +} + +static int handle_ept_misconfig (struct kvm_vcpu *vcpu,struct kvm_run *kvm_run) +{ +gpa_t gpa; +struct ept_inspect_spte iept = { .print =1 , + .walk.fn = + ept_misconfig_inspect_spte }; + + gpa =vmcs_read64 (GUEST_PHYSICAL_ADDRESS ); + +printk (KERN_ERR "EPT: Misconfiguration.\n" ); +printk (KERN_ERR "EPT: GPA: 0x%llx\n" , gpa); + +kvm_mmu_shadow_walk (vcpu, gpa, &iept.walk); + + kvm_run->exit_reason =KVM_EXIT_UNKNOWN ; + kvm_run->hw.hardware_exit_reason =EXIT_REASON_EPT_MISCONFIG ; + +return 0 ; +} +static int handle_nmi_window (struct kvm_vcpu *vcpu,struct kvm_run *kvm_run) {u32 cpu_based_vm_exec_control;
@@ -3243,8 +3337,9 @@ static int (*kvm_vmx_exit_handlers[])(st [EXIT_REASON_APIC_ACCESS] = handle_apic_access, [EXIT_REASON_WBINVD] = handle_wbinvd, [EXIT_REASON_TASK_SWITCH] = handle_task_switch, - [EXIT_REASON_EPT_VIOLATION] = handle_ept_violation, [EXIT_REASON_MCE_DURING_VMENTRY] = handle_machine_check, + [EXIT_REASON_EPT_VIOLATION] = handle_ept_violation, + [EXIT_REASON_EPT_MISCONFIG] = handle_ept_misconfig, }; static const int kvm_vmx_max_exit_handlers =
PATCH 5/5 KVM: VMX: conditionally disable 2M pages
In reply to Marcelo TosattiDisable usage of 2M pages if VMX_EPT_2MB_PAGE_BIT (bit 16) is clear in MSR_IA32_VMX_EPT_VPID_CAP and EPT is enabled.
arch/x86/kvm/vmx.c +3 -0
@@ -1369,6 +1369,9 @@ static __init int hardware_setup(void)if (!cpu_has_vmx_tpr_shadow ()) kvm_x86_ops->update_cr8_intercept =NULL ; +if (enable_ept && !cpu_has_vmx_ept_2m_page ()) +kvm_disable_largepages (); +return alloc_kvm_area (); }
include/linux/kvm_host.h +1 -0
@@ -203,6 +203,7 @@ int kvm_arch_set_memory_region(struct kvstruct kvm_userspace_memory_region *mem, struct kvm_memory_slot old, int user_alloc); +void kvm_disable_largepages (void );void kvm_arch_flush_shadow (struct kvm *kvm);gfn_t unalias_gfn (struct kvm *kvm,gfn_t gfn);struct page *gfn_to_page (struct kvm *kvm,gfn_t gfn);
virt/kvm/kvm_main.c +12 -0
@@ -85,6 +85,8 @@ static long kvm_vcpu_ioctl(struct file *static bool kvm_rebooting; +static bool largepages_disabled = false; +#ifdef KVM_CAP_DEVICE_ASSIGNMENT static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev (struct list_head *head,int assigned_dev_id)
@@ -1184,6 +1186,10 @@ int __kvm_set_memory_region(struct kvm *if ((base_gfn ^ ugfn) & (KVM_PAGES_PER_HPAGE -1 ))for (i =0 ; i < largepages; ++i) new.lpage_info[i].write_count =1 ; + +if (largepages_disabled) +for (i =0 ; i < largepages; ++i) + new.lpage_info[i].write_count =1 ; }/* Allocate page dirty bitmap if needed */
@@ -1294,6 +1300,12 @@ out:return r; } +void kvm_disable_largepages (void ) +{ + largepages_disabled = true; +} +EXPORT_SYMBOL_GPL (kvm_disable_largepages); +int is_error_page (struct page *page) {return page == bad_page;
Re: [patch 5/5] KVM: VMX: conditionally disable 2M pages
Quoted 17 lines · Marcelo Tosatti
Disable usage of 2M pages if
VMX_EPT_2MB_PAGE_BIT(bit 16) is clear inMSR_IA32_VMX_EPT_VPID_CAPand EPT is enabled.Index:
kvm/virt/kvm/kvm_main.c
===================================================================virt/kvm/kvm_main.c +2
-0@@ -85,6 +85,8 @@ static long kvm_vcpu_ioctl(struct file *static bool kvm_rebooting; +static bool largepages_disabled = false; +#ifdef KVM_CAP_DEVICE_ASSIGNMENT static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev (struct list_head *head,int assigned_dev_id)
virt/kvm/kvm_main.c int __kvm_set_memory_region(struct kvm * · line 1194
Quoted 9 lines
virt/kvm/kvm_main.c +4
-0@@ -1184,6 +1186,10 @@ int __kvm_set_memory_region(struct kvm *if ((base_gfn ^ ugfn) & (KVM_PAGES_PER_HPAGE -1 ))for (i =0 ; i < largepages; ++i) new.lpage_info[i].write_count =1 ; + +if (largepages_disabled) +for (i =0 ; i < largepages; ++i) + new.lpage_info[i].write_count =1 ; }
Please reuse the loop above, it's exactly the same (think of 1GB pages).
Signature · 1 line
error compiling committee.c: too many arguments to function
Quoted 7 lines · On Wednesday 10 June 2009 05:30:09 Marcelo Tosatti wrote:
Quoted 1 line
From the Intel docs:
An EPT misconfiguration occurs when, in the course of translation a guest-physical address, the logical processor encounters an EPT paging-structure entry that contains an unsupported value.
Handle this event and print useful information for diagnostics.
Looks fine to me, thanks!
Signature · 2 lines
regards Yang, Sheng