Skip to content

Commit ad756a1

Browse files
junjiemao1avikivity
authored andcommitted
KVM: VMX: Implement PCID/INVPCID for guests with EPT
This patch handles PCID/INVPCID for guests. Process-context identifiers (PCIDs) are a facility by which a logical processor may cache information for multiple linear-address spaces so that the processor may retain cached information when software switches to a different linear address space. Refer to section 4.10.1 in IA32 Intel Software Developer's Manual Volume 3A for details. For guests with EPT, the PCID feature is enabled and INVPCID behaves as running natively. For guests without EPT, the PCID feature is disabled and INVPCID triggers #UD. Signed-off-by: Junjie Mao <[email protected]> Signed-off-by: Avi Kivity <[email protected]>
1 parent fc73373 commit ad756a1

File tree

8 files changed

+77
-7
lines changed

8 files changed

+77
-7
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,13 @@
4848

4949
#define CR3_PAE_RESERVED_BITS ((X86_CR3_PWT | X86_CR3_PCD) - 1)
5050
#define CR3_NONPAE_RESERVED_BITS ((PAGE_SIZE-1) & ~(X86_CR3_PWT | X86_CR3_PCD))
51+
#define CR3_PCID_ENABLED_RESERVED_BITS 0xFFFFFF0000000000ULL
5152
#define CR3_L_MODE_RESERVED_BITS (CR3_NONPAE_RESERVED_BITS | \
5253
0xFFFFFF0000000000ULL)
5354
#define CR4_RESERVED_BITS \
5455
(~(unsigned long)(X86_CR4_VME | X86_CR4_PVI | X86_CR4_TSD | X86_CR4_DE\
5556
| X86_CR4_PSE | X86_CR4_PAE | X86_CR4_MCE \
56-
| X86_CR4_PGE | X86_CR4_PCE | X86_CR4_OSFXSR \
57+
| X86_CR4_PGE | X86_CR4_PCE | X86_CR4_OSFXSR | X86_CR4_PCIDE \
5758
| X86_CR4_OSXSAVE | X86_CR4_SMEP | X86_CR4_RDWRGSFS \
5859
| X86_CR4_OSXMMEXCPT | X86_CR4_VMXE))
5960

@@ -673,6 +674,7 @@ struct kvm_x86_ops {
673674
u64 (*get_mt_mask)(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio);
674675
int (*get_lpage_level)(void);
675676
bool (*rdtscp_supported)(void);
677+
bool (*invpcid_supported)(void);
676678
void (*adjust_tsc_offset)(struct kvm_vcpu *vcpu, s64 adjustment, bool host);
677679

678680
void (*set_tdp_cr3)(struct kvm_vcpu *vcpu, unsigned long cr3);

arch/x86/include/asm/processor-flags.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
*/
4545
#define X86_CR3_PWT 0x00000008 /* Page Write Through */
4646
#define X86_CR3_PCD 0x00000010 /* Page Cache Disable */
47+
#define X86_CR3_PCID_MASK 0x00000fff /* PCID Mask */
4748

4849
/*
4950
* Intel CPU features in CR4
@@ -61,6 +62,7 @@
6162
#define X86_CR4_OSXMMEXCPT 0x00000400 /* enable unmasked SSE exceptions */
6263
#define X86_CR4_VMXE 0x00002000 /* enable VMX virtualization */
6364
#define X86_CR4_RDWRGSFS 0x00010000 /* enable RDWRGSFS support */
65+
#define X86_CR4_PCIDE 0x00020000 /* enable PCID support */
6466
#define X86_CR4_OSXSAVE 0x00040000 /* enable xsave and xrestore */
6567
#define X86_CR4_SMEP 0x00100000 /* enable SMEP support */
6668

arch/x86/include/asm/vmx.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#define SECONDARY_EXEC_WBINVD_EXITING 0x00000040
6161
#define SECONDARY_EXEC_UNRESTRICTED_GUEST 0x00000080
6262
#define SECONDARY_EXEC_PAUSE_LOOP_EXITING 0x00000400
63+
#define SECONDARY_EXEC_ENABLE_INVPCID 0x00001000
6364

6465

6566
#define PIN_BASED_EXT_INTR_MASK 0x00000001
@@ -281,6 +282,7 @@ enum vmcs_field {
281282
#define EXIT_REASON_EPT_MISCONFIG 49
282283
#define EXIT_REASON_WBINVD 54
283284
#define EXIT_REASON_XSETBV 55
285+
#define EXIT_REASON_INVPCID 58
284286

285287
/*
286288
* Interruption-information format

arch/x86/kvm/cpuid.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ static int do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
201201
unsigned f_lm = 0;
202202
#endif
203203
unsigned f_rdtscp = kvm_x86_ops->rdtscp_supported() ? F(RDTSCP) : 0;
204+
unsigned f_invpcid = kvm_x86_ops->invpcid_supported() ? F(INVPCID) : 0;
204205

205206
/* cpuid 1.edx */
206207
const u32 kvm_supported_word0_x86_features =
@@ -228,7 +229,7 @@ static int do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
228229
0 /* DS-CPL, VMX, SMX, EST */ |
229230
0 /* TM2 */ | F(SSSE3) | 0 /* CNXT-ID */ | 0 /* Reserved */ |
230231
F(FMA) | F(CX16) | 0 /* xTPR Update, PDCM */ |
231-
0 /* Reserved, DCA */ | F(XMM4_1) |
232+
F(PCID) | 0 /* Reserved, DCA */ | F(XMM4_1) |
232233
F(XMM4_2) | F(X2APIC) | F(MOVBE) | F(POPCNT) |
233234
0 /* Reserved*/ | F(AES) | F(XSAVE) | 0 /* OSXSAVE */ | F(AVX) |
234235
F(F16C) | F(RDRAND);
@@ -248,7 +249,7 @@ static int do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
248249
/* cpuid 7.0.ebx */
249250
const u32 kvm_supported_word9_x86_features =
250251
F(FSGSBASE) | F(BMI1) | F(HLE) | F(AVX2) | F(SMEP) |
251-
F(BMI2) | F(ERMS) | F(RTM);
252+
F(BMI2) | F(ERMS) | f_invpcid | F(RTM);
252253

253254
/* all calls to cpuid_count() should be made on the same cpu */
254255
get_cpu();

arch/x86/kvm/cpuid.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,12 @@ static inline bool guest_cpuid_has_osvw(struct kvm_vcpu *vcpu)
5252
return best && (best->ecx & bit(X86_FEATURE_OSVW));
5353
}
5454

55+
static inline bool guest_cpuid_has_pcid(struct kvm_vcpu *vcpu)
56+
{
57+
struct kvm_cpuid_entry2 *best;
58+
59+
best = kvm_find_cpuid_entry(vcpu, 1, 0);
60+
return best && (best->ecx & bit(X86_FEATURE_PCID));
61+
}
62+
5563
#endif

arch/x86/kvm/svm.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4044,6 +4044,11 @@ static bool svm_rdtscp_supported(void)
40444044
return false;
40454045
}
40464046

4047+
static bool svm_invpcid_supported(void)
4048+
{
4049+
return false;
4050+
}
4051+
40474052
static bool svm_has_wbinvd_exit(void)
40484053
{
40494054
return true;
@@ -4312,6 +4317,7 @@ static struct kvm_x86_ops svm_x86_ops = {
43124317
.cpuid_update = svm_cpuid_update,
43134318

43144319
.rdtscp_supported = svm_rdtscp_supported,
4320+
.invpcid_supported = svm_invpcid_supported,
43154321

43164322
.set_supported_cpuid = svm_set_supported_cpuid,
43174323

arch/x86/kvm/vmx.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,12 @@ static inline bool cpu_has_vmx_rdtscp(void)
861861
SECONDARY_EXEC_RDTSCP;
862862
}
863863

864+
static inline bool cpu_has_vmx_invpcid(void)
865+
{
866+
return vmcs_config.cpu_based_2nd_exec_ctrl &
867+
SECONDARY_EXEC_ENABLE_INVPCID;
868+
}
869+
864870
static inline bool cpu_has_virtual_nmis(void)
865871
{
866872
return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS;
@@ -1751,6 +1757,11 @@ static bool vmx_rdtscp_supported(void)
17511757
return cpu_has_vmx_rdtscp();
17521758
}
17531759

1760+
static bool vmx_invpcid_supported(void)
1761+
{
1762+
return cpu_has_vmx_invpcid() && enable_ept;
1763+
}
1764+
17541765
/*
17551766
* Swap MSR entry in host/guest MSR entry array.
17561767
*/
@@ -2470,7 +2481,8 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
24702481
SECONDARY_EXEC_ENABLE_EPT |
24712482
SECONDARY_EXEC_UNRESTRICTED_GUEST |
24722483
SECONDARY_EXEC_PAUSE_LOOP_EXITING |
2473-
SECONDARY_EXEC_RDTSCP;
2484+
SECONDARY_EXEC_RDTSCP |
2485+
SECONDARY_EXEC_ENABLE_INVPCID;
24742486
if (adjust_vmx_controls(min2, opt2,
24752487
MSR_IA32_VMX_PROCBASED_CTLS2,
24762488
&_cpu_based_2nd_exec_control) < 0)
@@ -3800,6 +3812,8 @@ static u32 vmx_secondary_exec_control(struct vcpu_vmx *vmx)
38003812
if (!enable_ept) {
38013813
exec_control &= ~SECONDARY_EXEC_ENABLE_EPT;
38023814
enable_unrestricted_guest = 0;
3815+
/* Enable INVPCID for non-ept guests may cause performance regression. */
3816+
exec_control &= ~SECONDARY_EXEC_ENABLE_INVPCID;
38033817
}
38043818
if (!enable_unrestricted_guest)
38053819
exec_control &= ~SECONDARY_EXEC_UNRESTRICTED_GUEST;
@@ -6550,6 +6564,23 @@ static void vmx_cpuid_update(struct kvm_vcpu *vcpu)
65506564
}
65516565
}
65526566
}
6567+
6568+
exec_control = vmcs_read32(SECONDARY_VM_EXEC_CONTROL);
6569+
/* Exposing INVPCID only when PCID is exposed */
6570+
best = kvm_find_cpuid_entry(vcpu, 0x7, 0);
6571+
if (vmx_invpcid_supported() &&
6572+
best && (best->ecx & bit(X86_FEATURE_INVPCID)) &&
6573+
guest_cpuid_has_pcid(vcpu)) {
6574+
exec_control |= SECONDARY_EXEC_ENABLE_INVPCID;
6575+
vmcs_write32(SECONDARY_VM_EXEC_CONTROL,
6576+
exec_control);
6577+
} else {
6578+
exec_control &= ~SECONDARY_EXEC_ENABLE_INVPCID;
6579+
vmcs_write32(SECONDARY_VM_EXEC_CONTROL,
6580+
exec_control);
6581+
if (best)
6582+
best->ecx &= ~bit(X86_FEATURE_INVPCID);
6583+
}
65536584
}
65546585

65556586
static void vmx_set_supported_cpuid(u32 func, struct kvm_cpuid_entry2 *entry)
@@ -7284,6 +7315,7 @@ static struct kvm_x86_ops vmx_x86_ops = {
72847315
.cpuid_update = vmx_cpuid_update,
72857316

72867317
.rdtscp_supported = vmx_rdtscp_supported,
7318+
.invpcid_supported = vmx_invpcid_supported,
72877319

72887320
.set_supported_cpuid = vmx_set_supported_cpuid,
72897321

arch/x86/kvm/x86.c

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,9 @@ int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
528528
return 1;
529529
}
530530

531+
if (!(cr0 & X86_CR0_PG) && kvm_read_cr4_bits(vcpu, X86_CR4_PCIDE))
532+
return 1;
533+
531534
kvm_x86_ops->set_cr0(vcpu, cr0);
532535

533536
if ((cr0 ^ old_cr0) & X86_CR0_PG) {
@@ -604,10 +607,20 @@ int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
604607
kvm_read_cr3(vcpu)))
605608
return 1;
606609

610+
if ((cr4 & X86_CR4_PCIDE) && !(old_cr4 & X86_CR4_PCIDE)) {
611+
if (!guest_cpuid_has_pcid(vcpu))
612+
return 1;
613+
614+
/* PCID can not be enabled when cr3[11:0]!=000H or EFER.LMA=0 */
615+
if ((kvm_read_cr3(vcpu) & X86_CR3_PCID_MASK) || !is_long_mode(vcpu))
616+
return 1;
617+
}
618+
607619
if (kvm_x86_ops->set_cr4(vcpu, cr4))
608620
return 1;
609621

610-
if ((cr4 ^ old_cr4) & pdptr_bits)
622+
if (((cr4 ^ old_cr4) & pdptr_bits) ||
623+
(!(cr4 & X86_CR4_PCIDE) && (old_cr4 & X86_CR4_PCIDE)))
611624
kvm_mmu_reset_context(vcpu);
612625

613626
if ((cr4 ^ old_cr4) & X86_CR4_OSXSAVE)
@@ -626,8 +639,12 @@ int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
626639
}
627640

628641
if (is_long_mode(vcpu)) {
629-
if (cr3 & CR3_L_MODE_RESERVED_BITS)
630-
return 1;
642+
if (kvm_read_cr4(vcpu) & X86_CR4_PCIDE) {
643+
if (cr3 & CR3_PCID_ENABLED_RESERVED_BITS)
644+
return 1;
645+
} else
646+
if (cr3 & CR3_L_MODE_RESERVED_BITS)
647+
return 1;
631648
} else {
632649
if (is_pae(vcpu)) {
633650
if (cr3 & CR3_PAE_RESERVED_BITS)

0 commit comments

Comments
 (0)