Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1595429 > unrolled thread
| Started by | Paolo Bonzini <pbonzini@redhat.com> |
|---|---|
| First post | 2017-03-08 19:20 +0100 |
| Last post | 2017-03-08 20:10 +0100 |
| Articles | 11 — 3 participants |
Back to article view | Back to linux.kernel
[PATCH 0/6] KVM: nVMX: nested EPT improvements and A/D bits, RDRAND and RDSEED exits Paolo Bonzini <pbonzini@redhat.com> - 2017-03-08 19:20 +0100
[PATCH 6/6] KVM: nVMX: support RDRAND and RDSEED exiting Paolo Bonzini <pbonzini@redhat.com> - 2017-03-08 19:20 +0100
Re: [PATCH 6/6] KVM: nVMX: support RDRAND and RDSEED exiting Jim Mattson <jmattson@google.com> - 2017-03-13 19:30 +0100
Re: [PATCH 6/6] KVM: nVMX: support RDRAND and RDSEED exiting Paolo Bonzini <pbonzini@redhat.com> - 2017-03-14 11:00 +0100
[PATCH 2/6] KVM: VMX: cleanup check for invalid EPT violation Paolo Bonzini <pbonzini@redhat.com> - 2017-03-08 19:40 +0100
Re: [PATCH 2/6] KVM: VMX: cleanup check for invalid EPT violation David Hildenbrand <david@redhat.com> - 2017-03-09 12:00 +0100
Re: [PATCH 2/6] KVM: VMX: cleanup check for invalid EPT violation David Hildenbrand <david@redhat.com> - 2017-03-09 12:00 +0100
[PATCH 1/6] KVM: nVMX: we support 1GB EPT pages Paolo Bonzini <pbonzini@redhat.com> - 2017-03-08 19:40 +0100
Re: [PATCH 1/6] KVM: nVMX: we support 1GB EPT pages David Hildenbrand <david@redhat.com> - 2017-03-09 11:40 +0100
[PATCH 4/6] kvm: nVMX: support EPT accessed/dirty bits Paolo Bonzini <pbonzini@redhat.com> - 2017-03-08 19:40 +0100
[PATCH 3/6] kvm: x86: MMU support for EPT accessed/dirty bits Paolo Bonzini <pbonzini@redhat.com> - 2017-03-08 20:10 +0100
| From | Paolo Bonzini <pbonzini@redhat.com> |
|---|---|
| Date | 2017-03-08 19:20 +0100 |
| Subject | [PATCH 0/6] KVM: nVMX: nested EPT improvements and A/D bits, RDRAND and RDSEED exits |
| Message-ID | <tiOlP-1Pw-3@gated-at.bofh.it> |
Patches 1-4 implement nested EPT A/D bits and GB pages. As a side effect, this fixes one vmx.flat failure on machines with EPT A/D bits. It should be possible to implement PML on top of this with host support for A/D bits only. Patches 5-6 implement nested RDRAND and RDSEED exiting. Tested relatively lightly, but I'm throwing this out early. Paolo Paolo Bonzini (6): KVM: nVMX: we support 1GB EPT pages KVM: VMX: cleanup check for invalid EPT violation kvm: x86: MMU support for EPT accessed/dirty bits kvm: nVMX: support EPT accessed/dirty bits KVM: VMX: add missing exit reasons KVM: nVMX: support RDRAND and RDSEED exiting arch/x86/include/asm/kvm_host.h | 5 ++-- arch/x86/include/asm/vmx.h | 2 ++ arch/x86/include/uapi/asm/vmx.h | 25 +++++++++++++------ arch/x86/kvm/mmu.c | 4 ++- arch/x86/kvm/mmu.h | 3 ++- arch/x86/kvm/paging_tmpl.h | 54 +++++++++++++++++++++++------------------ arch/x86/kvm/vmx.c | 47 +++++++++++++++++++++++++++-------- 7 files changed, 95 insertions(+), 45 deletions(-) -- 1.8.3.1
[toc] | [next] | [standalone]
| From | Paolo Bonzini <pbonzini@redhat.com> |
|---|---|
| Date | 2017-03-08 19:20 +0100 |
| Subject | [PATCH 6/6] KVM: nVMX: support RDRAND and RDSEED exiting |
| Message-ID | <tiOlP-1Pw-11@gated-at.bofh.it> |
| In reply to | #1595429 |
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- arch/x86/include/asm/vmx.h | 2 ++ arch/x86/kvm/vmx.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index cc54b7026567..b2b6e5b1782b 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -70,8 +70,10 @@ #define SECONDARY_EXEC_APIC_REGISTER_VIRT 0x00000100 #define SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY 0x00000200 #define SECONDARY_EXEC_PAUSE_LOOP_EXITING 0x00000400 +#define SECONDARY_EXEC_RDRAND 0x00000800 #define SECONDARY_EXEC_ENABLE_INVPCID 0x00001000 #define SECONDARY_EXEC_SHADOW_VMCS 0x00004000 +#define SECONDARY_EXEC_RDSEED 0x00010000 #define SECONDARY_EXEC_ENABLE_PML 0x00020000 #define SECONDARY_EXEC_XSAVES 0x00100000 #define SECONDARY_EXEC_TSC_SCALING 0x02000000 diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index a3395e23cf5a..23b304fc72ec 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2749,6 +2749,7 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx) vmx->nested.nested_vmx_secondary_ctls_high); vmx->nested.nested_vmx_secondary_ctls_low = 0; vmx->nested.nested_vmx_secondary_ctls_high &= + SECONDARY_EXEC_RDRAND | SECONDARY_EXEC_RDSEED | SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | SECONDARY_EXEC_RDTSCP | SECONDARY_EXEC_DESC | @@ -8132,6 +8133,10 @@ static bool nested_vmx_exit_handled(struct kvm_vcpu *vcpu) return nested_cpu_has(vmcs12, CPU_BASED_INVLPG_EXITING); case EXIT_REASON_RDPMC: return nested_cpu_has(vmcs12, CPU_BASED_RDPMC_EXITING); + case EXIT_REASON_RDRAND: + return nested_cpu_has2(vmcs12, SECONDARY_EXEC_RDRAND); + case EXIT_REASON_RDSEED: + return nested_cpu_has2(vmcs12, SECONDARY_EXEC_RDSEED); case EXIT_REASON_RDTSC: case EXIT_REASON_RDTSCP: return nested_cpu_has(vmcs12, CPU_BASED_RDTSC_EXITING); case EXIT_REASON_VMCALL: case EXIT_REASON_VMCLEAR: -- 1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | Jim Mattson <jmattson@google.com> |
|---|---|
| Date | 2017-03-13 19:30 +0100 |
| Subject | Re: [PATCH 6/6] KVM: nVMX: support RDRAND and RDSEED exiting |
| Message-ID | <tkCTh-4s7-39@gated-at.bofh.it> |
| In reply to | #1595430 |
I would expect that any reasonable CPU will support "RDRAND exiting" iff it supports RDRAND (i.e. CPUID.01H:ECX.RDRAND[bit 30]). Similarly, any reasonable CPU will support "RDSEED exiting" iff it supports RDSEED (i.e. CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18]). Shouldn't there be some code in vmx_cpuid_update to adjust the vmx->nested.nested_vmx_secondary_ctls_high bits for "RDRAND exiting" and "RDSEED exiting," as there already is for "enable RDTSCP"? On Wed, Mar 8, 2017 at 10:03 AM, Paolo Bonzini <pbonzini@redhat.com> wrote: > Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> > --- > arch/x86/include/asm/vmx.h | 2 ++ > arch/x86/kvm/vmx.c | 5 +++++ > 2 files changed, 7 insertions(+) > > diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h > index cc54b7026567..b2b6e5b1782b 100644 > --- a/arch/x86/include/asm/vmx.h > +++ b/arch/x86/include/asm/vmx.h > @@ -70,8 +70,10 @@ > #define SECONDARY_EXEC_APIC_REGISTER_VIRT 0x00000100 > #define SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY 0x00000200 > #define SECONDARY_EXEC_PAUSE_LOOP_EXITING 0x00000400 > +#define SECONDARY_EXEC_RDRAND 0x00000800 > #define SECONDARY_EXEC_ENABLE_INVPCID 0x00001000 > #define SECONDARY_EXEC_SHADOW_VMCS 0x00004000 > +#define SECONDARY_EXEC_RDSEED 0x00010000 > #define SECONDARY_EXEC_ENABLE_PML 0x00020000 > #define SECONDARY_EXEC_XSAVES 0x00100000 > #define SECONDARY_EXEC_TSC_SCALING 0x02000000 > diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c > index a3395e23cf5a..23b304fc72ec 100644 > --- a/arch/x86/kvm/vmx.c > +++ b/arch/x86/kvm/vmx.c > @@ -2749,6 +2749,7 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx) > vmx->nested.nested_vmx_secondary_ctls_high); > vmx->nested.nested_vmx_secondary_ctls_low = 0; > vmx->nested.nested_vmx_secondary_ctls_high &= > + SECONDARY_EXEC_RDRAND | SECONDARY_EXEC_RDSEED | > SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | > SECONDARY_EXEC_RDTSCP | > SECONDARY_EXEC_DESC | > @@ -8132,6 +8133,10 @@ static bool nested_vmx_exit_handled(struct kvm_vcpu *vcpu) > return nested_cpu_has(vmcs12, CPU_BASED_INVLPG_EXITING); > case EXIT_REASON_RDPMC: > return nested_cpu_has(vmcs12, CPU_BASED_RDPMC_EXITING); > + case EXIT_REASON_RDRAND: > + return nested_cpu_has2(vmcs12, SECONDARY_EXEC_RDRAND); > + case EXIT_REASON_RDSEED: > + return nested_cpu_has2(vmcs12, SECONDARY_EXEC_RDSEED); > case EXIT_REASON_RDTSC: case EXIT_REASON_RDTSCP: > return nested_cpu_has(vmcs12, CPU_BASED_RDTSC_EXITING); > case EXIT_REASON_VMCALL: case EXIT_REASON_VMCLEAR: > -- > 1.8.3.1 >
[toc] | [prev] | [next] | [standalone]
| From | Paolo Bonzini <pbonzini@redhat.com> |
|---|---|
| Date | 2017-03-14 11:00 +0100 |
| Subject | Re: [PATCH 6/6] KVM: nVMX: support RDRAND and RDSEED exiting |
| Message-ID | <tkRpg-6Fd-9@gated-at.bofh.it> |
| In reply to | #1599675 |
On 13/03/2017 19:27, Jim Mattson wrote: > I would expect that any reasonable CPU will support "RDRAND exiting" > iff it supports RDRAND (i.e. CPUID.01H:ECX.RDRAND[bit 30]). > Similarly, any reasonable CPU will support "RDSEED exiting" iff it > supports RDSEED (i.e. CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18]). > > Shouldn't there be some code in vmx_cpuid_update to adjust the > vmx->nested.nested_vmx_secondary_ctls_high bits for "RDRAND exiting" > and "RDSEED exiting," as there already is for "enable RDTSCP"? Enable RDTSCP is a bit different in that the instruction itself is disabled rather than the VMEXIT, still it would probably be a good idea. One thing to be careful about is any conflicts with David Matlack's work to customize the MSRs for processor models, since there's no prescribed ordering of KVM_SET_CPUID2 and KVM_SET_MSR. Maybe nested_vmx_setup_ctls_msrs can save the "default" nested_*_ctls_*; then vmx_restore_control_msr can refer to those in order to check the written value against the supported bits. Paolo
[toc] | [prev] | [next] | [standalone]
| From | Paolo Bonzini <pbonzini@redhat.com> |
|---|---|
| Date | 2017-03-08 19:40 +0100 |
| Subject | [PATCH 2/6] KVM: VMX: cleanup check for invalid EPT violation |
| Message-ID | <tiOFb-1Wd-7@gated-at.bofh.it> |
| In reply to | #1595429 |
handle_ept_violation is checking for "guest-linear-address invalid" +
"paging-structure walk", which is a sign of a bug in KVM. However,
_all_ EPT violations without a valid guest linear address are paging
structure walks, because those EPT violations happen when loading the
guest PDPTEs. So simplify the check to only look at bit 7 of the
exit qualification.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
arch/x86/kvm/vmx.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 89b74d9bc357..cb9034601c7b 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -6232,12 +6232,10 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu)
unsigned long exit_qualification;
gpa_t gpa;
u32 error_code;
- int gla_validity;
exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
- gla_validity = (exit_qualification >> 7) & 0x3;
- if (gla_validity == 0x2) {
+ if ((exit_qualification & 0x80) == 0) {
printk(KERN_ERR "EPT: Handling EPT violation failed!\n");
printk(KERN_ERR "EPT: GPA: 0x%lx, GVA: 0x%lx\n",
(long unsigned int)vmcs_read64(GUEST_PHYSICAL_ADDRESS),
--
1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | David Hildenbrand <david@redhat.com> |
|---|---|
| Date | 2017-03-09 12:00 +0100 |
| Subject | Re: [PATCH 2/6] KVM: VMX: cleanup check for invalid EPT violation |
| Message-ID | <tj3Xz-43h-5@gated-at.bofh.it> |
| In reply to | #1595434 |
Am 08.03.2017 um 19:03 schrieb Paolo Bonzini:
> handle_ept_violation is checking for "guest-linear-address invalid" +
> "paging-structure walk", which is a sign of a bug in KVM. However,
> _all_ EPT violations without a valid guest linear address are paging
> structure walks, because those EPT violations happen when loading the
> guest PDPTEs. So simplify the check to only look at bit 7 of the
> exit qualification.
Do we have any define for this magic bit 7?
#EPT_EXITQ_GLA_VALID 0x80
>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> arch/x86/kvm/vmx.c | 4 +---
> 1 file changed, 1 insertion(+), 3 deletions(-)
>
> diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
> index 89b74d9bc357..cb9034601c7b 100644
> --- a/arch/x86/kvm/vmx.c
> +++ b/arch/x86/kvm/vmx.c
> @@ -6232,12 +6232,10 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu)
> unsigned long exit_qualification;
> gpa_t gpa;
> u32 error_code;
> - int gla_validity;
>
> exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
>
> - gla_validity = (exit_qualification >> 7) & 0x3;
> - if (gla_validity == 0x2) {
> + if ((exit_qualification & 0x80) == 0) {
> printk(KERN_ERR "EPT: Handling EPT violation failed!\n");
> printk(KERN_ERR "EPT: GPA: 0x%lx, GVA: 0x%lx\n",
> (long unsigned int)vmcs_read64(GUEST_PHYSICAL_ADDRESS),
>
--
Thanks,
David
[toc] | [prev] | [next] | [standalone]
| From | David Hildenbrand <david@redhat.com> |
|---|---|
| Date | 2017-03-09 12:00 +0100 |
| Subject | Re: [PATCH 2/6] KVM: VMX: cleanup check for invalid EPT violation |
| Message-ID | <tj3XC-43h-35@gated-at.bofh.it> |
| In reply to | #1595909 |
Am 09.03.2017 um 11:43 schrieb David Hildenbrand: > Am 08.03.2017 um 19:03 schrieb Paolo Bonzini: >> handle_ept_violation is checking for "guest-linear-address invalid" + >> "paging-structure walk", which is a sign of a bug in KVM. However, >> _all_ EPT violations without a valid guest linear address are paging >> structure walks, because those EPT violations happen when loading the >> guest PDPTEs. So simplify the check to only look at bit 7 of the >> exit qualification. > > Do we have any define for this magic bit 7? > > #EPT_EXITQ_GLA_VALID 0x80 > Introducing #define EPT_VIOLATION_GLA_VALID_BIT #define EPT_VIOLATION_GLA_VALID would make sense. -- Thanks, David
[toc] | [prev] | [next] | [standalone]
| From | Paolo Bonzini <pbonzini@redhat.com> |
|---|---|
| Date | 2017-03-08 19:40 +0100 |
| Subject | [PATCH 1/6] KVM: nVMX: we support 1GB EPT pages |
| Message-ID | <tiOFc-1Wd-15@gated-at.bofh.it> |
| In reply to | #1595429 |
Large pages at the PDPE level can be emulated by the MMU, so the bit can be set unconditionally in the EPT capabilities MSR. The same is true of 2MB EPT pages, though all Intel processors with EPT in practice support those. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- arch/x86/kvm/vmx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 283aa8601833..89b74d9bc357 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2764,14 +2764,14 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx) vmx->nested.nested_vmx_secondary_ctls_high |= SECONDARY_EXEC_ENABLE_EPT; vmx->nested.nested_vmx_ept_caps = VMX_EPT_PAGE_WALK_4_BIT | - VMX_EPTP_WB_BIT | VMX_EPT_2MB_PAGE_BIT | - VMX_EPT_INVEPT_BIT; + VMX_EPTP_WB_BIT | VMX_EPT_INVEPT_BIT; if (cpu_has_vmx_ept_execute_only()) vmx->nested.nested_vmx_ept_caps |= VMX_EPT_EXECUTE_ONLY_BIT; vmx->nested.nested_vmx_ept_caps &= vmx_capability.ept; vmx->nested.nested_vmx_ept_caps |= VMX_EPT_EXTENT_GLOBAL_BIT | - VMX_EPT_EXTENT_CONTEXT_BIT; + VMX_EPT_EXTENT_CONTEXT_BIT | VMX_EPT_2MB_PAGE_BIT | + VMX_EPT_1GB_PAGE_BIT; } else vmx->nested.nested_vmx_ept_caps = 0; -- 1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | David Hildenbrand <david@redhat.com> |
|---|---|
| Date | 2017-03-09 11:40 +0100 |
| Subject | Re: [PATCH 1/6] KVM: nVMX: we support 1GB EPT pages |
| Message-ID | <tj3Ee-3Ue-17@gated-at.bofh.it> |
| In reply to | #1595435 |
Am 08.03.2017 um 19:03 schrieb Paolo Bonzini: > Large pages at the PDPE level can be emulated by the MMU, so the bit > can be set unconditionally in the EPT capabilities MSR. The same is > true of 2MB EPT pages, though all Intel processors with EPT in practice > support those. > > Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> > --- > arch/x86/kvm/vmx.c | 6 +++--- > 1 file changed, 3 insertions(+), 3 deletions(-) > > diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c > index 283aa8601833..89b74d9bc357 100644 > --- a/arch/x86/kvm/vmx.c > +++ b/arch/x86/kvm/vmx.c > @@ -2764,14 +2764,14 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx) > vmx->nested.nested_vmx_secondary_ctls_high |= > SECONDARY_EXEC_ENABLE_EPT; > vmx->nested.nested_vmx_ept_caps = VMX_EPT_PAGE_WALK_4_BIT | > - VMX_EPTP_WB_BIT | VMX_EPT_2MB_PAGE_BIT | > - VMX_EPT_INVEPT_BIT; > + VMX_EPTP_WB_BIT | VMX_EPT_INVEPT_BIT; > if (cpu_has_vmx_ept_execute_only()) > vmx->nested.nested_vmx_ept_caps |= > VMX_EPT_EXECUTE_ONLY_BIT; > vmx->nested.nested_vmx_ept_caps &= vmx_capability.ept; > vmx->nested.nested_vmx_ept_caps |= VMX_EPT_EXTENT_GLOBAL_BIT | > - VMX_EPT_EXTENT_CONTEXT_BIT; > + VMX_EPT_EXTENT_CONTEXT_BIT | VMX_EPT_2MB_PAGE_BIT | > + VMX_EPT_1GB_PAGE_BIT; > } else > vmx->nested.nested_vmx_ept_caps = 0; > > Reviewed-by: David Hildenbrand <david@redhat.com> -- Thanks, David
[toc] | [prev] | [next] | [standalone]
| From | Paolo Bonzini <pbonzini@redhat.com> |
|---|---|
| Date | 2017-03-08 19:40 +0100 |
| Subject | [PATCH 4/6] kvm: nVMX: support EPT accessed/dirty bits |
| Message-ID | <tiOFb-1Wd-9@gated-at.bofh.it> |
| In reply to | #1595429 |
Now use bit 6 of EPTP to optionally enable A/D bits for EPTP. Another
thing to change is that, when EPT accessed and dirty bits are not in use,
VMX treats accesses to guest paging structures as data reads. When they
are in use (bit 6 of EPTP is set), they are treated as writes and the
corresponding EPT dirty bit is set. The MMU didn't know this detail,
so this patch adds it.
We also have to fix up the exit qualification. It may be wrong because
KVM sets bit 6 but the guest might not.
L1 emulates EPT A/D bits using write permissions, so in principle it may
be possible for EPT A/D bits to be used by L1 even though not available
in hardware. The problem is that guest page-table walks will be treated
as reads rather than writes, so they would not cause an EPT violation.
Therefore, this patch requires host support for the feature.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
arch/x86/include/asm/kvm_host.h | 5 +++--
arch/x86/kvm/mmu.c | 4 +++-
arch/x86/kvm/mmu.h | 3 ++-
arch/x86/kvm/paging_tmpl.h | 33 ++++++++++++++++-----------------
arch/x86/kvm/vmx.c | 32 ++++++++++++++++++++++++++++----
5 files changed, 52 insertions(+), 25 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 74ef58c8ff53..7dbb8d622683 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -343,9 +343,10 @@ struct kvm_mmu {
void (*update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
u64 *spte, const void *pte);
hpa_t root_hpa;
- int root_level;
- int shadow_root_level;
union kvm_mmu_page_role base_role;
+ u8 root_level;
+ u8 shadow_root_level;
+ u8 ept_ad;
bool direct_map;
/*
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index ac7810513d0e..558676538fca 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -4340,7 +4340,8 @@ void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu)
}
EXPORT_SYMBOL_GPL(kvm_init_shadow_mmu);
-void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly)
+void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly,
+ bool accessed_dirty)
{
struct kvm_mmu *context = &vcpu->arch.mmu;
@@ -4349,6 +4350,7 @@ void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly)
context->shadow_root_level = kvm_x86_ops->get_tdp_level();
context->nx = true;
+ context->ept_ad = accessed_dirty;
context->page_fault = ept_page_fault;
context->gva_to_gpa = ept_gva_to_gpa;
context->sync_page = ept_sync_page;
diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h
index ddc56e91f2e4..d8ccb32f7308 100644
--- a/arch/x86/kvm/mmu.h
+++ b/arch/x86/kvm/mmu.h
@@ -74,7 +74,8 @@ enum {
int handle_mmio_page_fault(struct kvm_vcpu *vcpu, u64 addr, bool direct);
void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu);
-void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly);
+void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly,
+ bool accessed_dirty);
static inline unsigned int kvm_mmu_available_pages(struct kvm *kvm)
{
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
index 3e20f7b33892..8bf829703a00 100644
--- a/arch/x86/kvm/paging_tmpl.h
+++ b/arch/x86/kvm/paging_tmpl.h
@@ -23,13 +23,6 @@
* so the code in this file is compiled twice, once per pte size.
*/
-/*
- * This is used to catch non optimized PT_GUEST_(DIRTY|ACCESS)_SHIFT macro
- * uses for EPT without A/D paging type.
- */
-extern u64 __pure __using_nonexistent_pte_bit(void)
- __compiletime_error("wrong use of PT_GUEST_(DIRTY|ACCESS)_SHIFT");
-
#if PTTYPE == 64
#define pt_element_t u64
#define guest_walker guest_walker64
@@ -39,8 +32,6 @@ extern u64 __pure __using_nonexistent_pte_bit(void)
#define PT_LVL_OFFSET_MASK(lvl) PT64_LVL_OFFSET_MASK(lvl)
#define PT_INDEX(addr, level) PT64_INDEX(addr, level)
#define PT_LEVEL_BITS PT64_LEVEL_BITS
- #define PT_GUEST_ACCESSED_MASK PT_ACCESSED_MASK
- #define PT_GUEST_DIRTY_MASK PT_DIRTY_MASK
#define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT
#define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT
#define PT_HAVE_ACCESSED_DIRTY(mmu) true
@@ -61,8 +52,6 @@ extern u64 __pure __using_nonexistent_pte_bit(void)
#define PT_INDEX(addr, level) PT32_INDEX(addr, level)
#define PT_LEVEL_BITS PT32_LEVEL_BITS
#define PT_MAX_FULL_LEVELS 2
- #define PT_GUEST_ACCESSED_MASK PT_ACCESSED_MASK
- #define PT_GUEST_DIRTY_MASK PT_DIRTY_MASK
#define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT
#define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT
#define PT_HAVE_ACCESSED_DIRTY(mmu) true
@@ -76,17 +65,18 @@ extern u64 __pure __using_nonexistent_pte_bit(void)
#define PT_LVL_OFFSET_MASK(lvl) PT64_LVL_OFFSET_MASK(lvl)
#define PT_INDEX(addr, level) PT64_INDEX(addr, level)
#define PT_LEVEL_BITS PT64_LEVEL_BITS
- #define PT_GUEST_ACCESSED_MASK 0
- #define PT_GUEST_DIRTY_MASK 0
- #define PT_GUEST_DIRTY_SHIFT __using_nonexistent_pte_bit()
- #define PT_GUEST_ACCESSED_SHIFT __using_nonexistent_pte_bit()
- #define PT_HAVE_ACCESSED_DIRTY(mmu) false
+ #define PT_GUEST_DIRTY_SHIFT 9
+ #define PT_GUEST_ACCESSED_SHIFT 8
+ #define PT_HAVE_ACCESSED_DIRTY(mmu) ((mmu)->ept_ad)
#define CMPXCHG cmpxchg64
#define PT_MAX_FULL_LEVELS 4
#else
#error Invalid PTTYPE value
#endif
+#define PT_GUEST_DIRTY_MASK (1 << PT_GUEST_DIRTY_SHIFT)
+#define PT_GUEST_ACCESSED_MASK (1 << PT_GUEST_ACCESSED_SHIFT)
+
#define gpte_to_gfn_lvl FNAME(gpte_to_gfn_lvl)
#define gpte_to_gfn(pte) gpte_to_gfn_lvl((pte), PT_PAGE_TABLE_LEVEL)
@@ -290,6 +280,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
pt_element_t __user *uninitialized_var(ptep_user);
gfn_t table_gfn;
unsigned index, pt_access, pte_access, accessed_dirty, pte_pkey;
+ unsigned nested_access;
gpa_t pte_gpa;
bool have_ad;
int offset;
@@ -319,6 +310,14 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
ASSERT(!(is_long_mode(vcpu) && !is_pae(vcpu)));
accessed_dirty = have_ad ? PT_GUEST_ACCESSED_MASK : 0;
+
+ /*
+ * FIXME: on Intel processors, loads of the PDPTE registers for PAE paging
+ * by the MOV to CR instruction are treated as reads and do not cause the
+ * processor to set the dirty flag in tany EPT paging-structure entry.
+ */
+ nested_access = (have_ad ? PFERR_WRITE_MASK : 0) | PFERR_USER_MASK;
+
pt_access = pte_access = ACC_ALL;
++walker->level;
@@ -338,7 +337,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
walker->pte_gpa[walker->level - 1] = pte_gpa;
real_gfn = mmu->translate_gpa(vcpu, gfn_to_gpa(table_gfn),
- PFERR_USER_MASK|PFERR_WRITE_MASK,
+ nested_access,
&walker->fault);
/*
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index cb9034601c7b..a3395e23cf5a 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -2772,6 +2772,8 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx)
vmx->nested.nested_vmx_ept_caps |= VMX_EPT_EXTENT_GLOBAL_BIT |
VMX_EPT_EXTENT_CONTEXT_BIT | VMX_EPT_2MB_PAGE_BIT |
VMX_EPT_1GB_PAGE_BIT;
+ if (enable_ept_ad_bits)
+ vmx->nested.nested_vmx_ept_caps |= VMX_EPT_AD_BIT;
} else
vmx->nested.nested_vmx_ept_caps = 0;
@@ -6247,6 +6249,17 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu)
return 0;
}
+ if (is_guest_mode(vcpu) && !(exit_qualification & 0x100)) {
+ /*
+ * Fix up exit_qualification according to whether guest
+ * page table accesses are reads or writes.
+ */
+ u64 eptp = nested_ept_get_cr3(vcpu);
+ exit_qualification &= ~EPT_VIOLATION_ACC_WRITE;
+ if (eptp & VMX_EPT_AD_ENABLE_BIT)
+ exit_qualification |= EPT_VIOLATION_ACC_WRITE;
+ }
+
/*
* EPT violation happened while executing iret from NMI,
* "blocked by NMI" bit has to be set before next VM entry.
@@ -9490,17 +9503,25 @@ static unsigned long nested_ept_get_cr3(struct kvm_vcpu *vcpu)
return get_vmcs12(vcpu)->ept_pointer;
}
-static void nested_ept_init_mmu_context(struct kvm_vcpu *vcpu)
+static int nested_ept_init_mmu_context(struct kvm_vcpu *vcpu)
{
+ u64 eptp;
+
WARN_ON(mmu_is_nested(vcpu));
+ eptp = nested_ept_get_cr3(vcpu);
+ if ((eptp & VMX_EPT_AD_ENABLE_BIT) && !enable_ept_ad_bits)
+ return 1;
+
kvm_init_shadow_ept_mmu(vcpu,
to_vmx(vcpu)->nested.nested_vmx_ept_caps &
- VMX_EPT_EXECUTE_ONLY_BIT);
+ VMX_EPT_EXECUTE_ONLY_BIT,
+ eptp & VMX_EPT_AD_ENABLE_BIT);
vcpu->arch.mmu.set_cr3 = vmx_set_cr3;
vcpu->arch.mmu.get_cr3 = nested_ept_get_cr3;
vcpu->arch.mmu.inject_page_fault = nested_ept_inject_page_fault;
vcpu->arch.walk_mmu = &vcpu->arch.nested_mmu;
+ return 0;
}
static void nested_ept_uninit_mmu_context(struct kvm_vcpu *vcpu)
@@ -10267,7 +10289,9 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
if (nested_cpu_has_ept(vmcs12)) {
kvm_mmu_unload(vcpu);
- nested_ept_init_mmu_context(vcpu);
+ if (nested_ept_init_mmu_context(vcpu)) {
+ *entry_failure_code = ENTRY_FAIL_DEFAULT;
+ return 1;
+ }
}
/*
--
1.8.3.1
[toc] | [prev] | [next] | [standalone]
| From | Paolo Bonzini <pbonzini@redhat.com> |
|---|---|
| Date | 2017-03-08 20:10 +0100 |
| Subject | [PATCH 3/6] kvm: x86: MMU support for EPT accessed/dirty bits |
| Message-ID | <tiP8d-2nO-13@gated-at.bofh.it> |
| In reply to | #1595429 |
This prepares the MMU paging code for EPT accessed and dirty bits,
which can be enabled optionally at runtime. Code that updates the
accessed and dirty bits will need a pointer to the struct kvm_mmu.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
arch/x86/kvm/paging_tmpl.h | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
index a01105485315..3e20f7b33892 100644
--- a/arch/x86/kvm/paging_tmpl.h
+++ b/arch/x86/kvm/paging_tmpl.h
@@ -43,6 +43,7 @@ extern u64 __pure __using_nonexistent_pte_bit(void)
#define PT_GUEST_DIRTY_MASK PT_DIRTY_MASK
#define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT
#define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT
+ #define PT_HAVE_ACCESSED_DIRTY(mmu) true
#ifdef CONFIG_X86_64
#define PT_MAX_FULL_LEVELS 4
#define CMPXCHG cmpxchg
@@ -64,6 +65,7 @@ extern u64 __pure __using_nonexistent_pte_bit(void)
#define PT_GUEST_DIRTY_MASK PT_DIRTY_MASK
#define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT
#define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT
+ #define PT_HAVE_ACCESSED_DIRTY(mmu) true
#define CMPXCHG cmpxchg
#elif PTTYPE == PTTYPE_EPT
#define pt_element_t u64
@@ -78,6 +80,7 @@ extern u64 __pure __using_nonexistent_pte_bit(void)
#define PT_GUEST_DIRTY_MASK 0
#define PT_GUEST_DIRTY_SHIFT __using_nonexistent_pte_bit()
#define PT_GUEST_ACCESSED_SHIFT __using_nonexistent_pte_bit()
+ #define PT_HAVE_ACCESSED_DIRTY(mmu) false
#define CMPXCHG cmpxchg64
#define PT_MAX_FULL_LEVELS 4
#else
@@ -111,12 +114,13 @@ static gfn_t gpte_to_gfn_lvl(pt_element_t gpte, int lvl)
return (gpte & PT_LVL_ADDR_MASK(lvl)) >> PAGE_SHIFT;
}
-static inline void FNAME(protect_clean_gpte)(unsigned *access, unsigned gpte)
+static inline void FNAME(protect_clean_gpte)(struct kvm_mmu *mmu, unsigned *access,
+ unsigned gpte)
{
unsigned mask;
/* dirty bit is not supported, so no need to track it */
- if (!PT_GUEST_DIRTY_MASK)
+ if (!PT_HAVE_ACCESSED_DIRTY(mmu))
return;
BUILD_BUG_ON(PT_WRITABLE_MASK != ACC_WRITE_MASK);
@@ -171,7 +175,7 @@ static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu,
goto no_present;
/* if accessed bit is not supported prefetch non accessed gpte */
- if (PT_GUEST_ACCESSED_MASK && !(gpte & PT_GUEST_ACCESSED_MASK))
+ if (PT_HAVE_ACCESSED_DIRTY(&vcpu->arch.mmu) && !(gpte & PT_GUEST_ACCESSED_MASK))
goto no_present;
return false;
@@ -217,7 +221,7 @@ static int FNAME(update_accessed_dirty_bits)(struct kvm_vcpu *vcpu,
int ret;
/* dirty/accessed bits are not supported, so no need to update them */
- if (!PT_GUEST_DIRTY_MASK)
+ if (!PT_HAVE_ACCESSED_DIRTY(mmu))
return 0;
for (level = walker->max_level; level >= walker->level; --level) {
@@ -287,6 +291,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
gfn_t table_gfn;
unsigned index, pt_access, pte_access, accessed_dirty, pte_pkey;
gpa_t pte_gpa;
+ bool have_ad;
int offset;
const int write_fault = access & PFERR_WRITE_MASK;
const int user_fault = access & PFERR_USER_MASK;
@@ -299,6 +304,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
retry_walk:
walker->level = mmu->root_level;
pte = mmu->get_cr3(vcpu);
+ have_ad = PT_HAVE_ACCESSED_DIRTY(mmu);
#if PTTYPE == 64
if (walker->level == PT32E_ROOT_LEVEL) {
@@ -312,7 +318,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
walker->max_level = walker->level;
ASSERT(!(is_long_mode(vcpu) && !is_pae(vcpu)));
- accessed_dirty = PT_GUEST_ACCESSED_MASK;
+ accessed_dirty = have_ad ? PT_GUEST_ACCESSED_MASK : 0;
pt_access = pte_access = ACC_ALL;
++walker->level;
@@ -394,7 +400,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker,
walker->gfn = real_gpa >> PAGE_SHIFT;
if (!write_fault)
- FNAME(protect_clean_gpte)(&pte_access, pte);
+ FNAME(protect_clean_gpte)(mmu, &pte_access, pte);
else
/*
* On a write fault, fold the dirty bit into accessed_dirty.
@@ -485,7 +491,7 @@ static int FNAME(walk_addr_nested)(struct guest_walker *walker,
gfn = gpte_to_gfn(gpte);
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
- FNAME(protect_clean_gpte)(&pte_access, gpte);
+ FNAME(protect_clean_gpte)(&vcpu->arch.mmu, &pte_access, gpte);
pfn = pte_prefetch_gfn_to_pfn(vcpu, gfn,
no_dirty_log && (pte_access & ACC_WRITE_MASK));
if (is_error_pfn(pfn))
@@ -979,7 +985,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
gfn = gpte_to_gfn(gpte);
pte_access = sp->role.access;
pte_access &= FNAME(gpte_access)(vcpu, gpte);
- FNAME(protect_clean_gpte)(&pte_access, gpte);
+ FNAME(protect_clean_gpte)(&vcpu->arch.mmu, &pte_access, gpte);
if (sync_mmio_spte(vcpu, &sp->spt[i], gfn, pte_access,
&nr_present))
@@ -1025,3 +1031,4 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
#undef PT_GUEST_DIRTY_MASK
#undef PT_GUEST_DIRTY_SHIFT
#undef PT_GUEST_ACCESSED_SHIFT
+#undef PT_HAVE_ACCESSED_DIRTY
--
1.8.3.1
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web