hv: cr: handle control registers related to PCID

1. This patch passes-through CR4.PCIDE to guest VM.

2. This patch handles the invlidation of TLB and the paging-structure caches.
   According to SDM Vol.3 4.10.4.1, the following instructions invalidate
   entries in the TLBs and the paging-structure caches:
   - INVLPG: this instruction is passed-through to guest, no extra handling needed.
   - INVPCID: this instruction is passed-trhough to guest, no extra handling needed.
   - CR0.PG from 1 to 0: already handled by current code, change of CR0.PG will do
     EPT flush.
   - MOV to CR3: hypervisor doesn't trap this instrcution, no extra handling needed.
   - CR4.PGE changed: already handled by current code, change of CR4.PGE will no EPT
     flush.
   - CR4.PCIDE from 1 to 0: this patch handles this case, will do EPT flush.
   - CR4.PAE changed: already handled by current code, change of CR4.PAE will do EPT
     flush.
   - CR4.SEMP from 1 to 0, already handled by current code, change of CR4.SEMP will
     do EPT flush.
   - Task switch: Task switch is not supported in VMX non-root mode.
   - VMX transitions: already handled by current code with the support of VPID.

3. This patch checks the validatiy of CR0, CR4 related to PCID feature.
   According to SDM Vol.3 4.10.1, CR.PCIDE can be 1 only in IA-32e mode.
   - MOV to CR4 causes a general-protection exception (#GP) if it would change CR4.PCIDE
     from 0 to 1 and either IA32_EFER.LMA = 0 or CR3[11:0] ≠ 000H
   - MOV to CR0 causes a general-protection exception if it would clear CR0.PG to 0
     while CR4.PCIDE = 1

Tracked-On: #4296
Signed-off-by: Binbin Wu <binbin.wu@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Binbin Wu 2019-12-23 14:26:13 +08:00 committed by wenlingz
parent 4ae350a091
commit 41a998fca3
1 changed files with 38 additions and 10 deletions

View File

@ -17,6 +17,7 @@
#include <vtd.h>
#include <vmexit.h>
#include <pgtable.h>
#include <cpufeatures.h>
#include <trace.h>
#include <logmsg.h>
@ -26,7 +27,7 @@
CR0_NE | CR0_ET | CR0_TS | CR0_EM | CR0_MP | CR0_PE)
/* CR4 bits hv want to trap to track status change */
#define CR4_TRAP_MASK (CR4_PSE | CR4_PAE | CR4_VMXE | CR4_PCIDE | CR4_SMEP | CR4_SMAP | CR4_PKE)
#define CR4_TRAP_MASK (CR4_PSE | CR4_PAE | CR4_VMXE | CR4_SMEP | CR4_SMAP | CR4_PKE)
#define CR4_RESERVED_MASK ~(CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_PSE | \
CR4_PAE | CR4_MCE | CR4_PGE | CR4_PCE | \
CR4_OSFXSR | CR4_PCIDE | CR4_OSXSAVE | \
@ -113,6 +114,14 @@ static bool is_cr0_write_valid(struct acrn_vcpu *vcpu, uint64_t cr0)
if (((cr0 & CR0_CD) == 0UL) && ((cr0 & CR0_NW) != 0UL)) {
ret = false;
}
/* SDM 4.10.1 "Process-Context Identifiers"
*
* MOV to CR0 causes a general-protection exception if it would
* clear CR0.PG to 0 while CR4.PCIDE = 1
*/
if (((cr0 & CR0_PG) == 0UL) && ((vcpu_get_cr4(vcpu) & CR4_PCIDE) != 0UL)) {
ret = false;
}
}
}
}
@ -252,14 +261,9 @@ static bool is_cr4_write_valid(struct acrn_vcpu *vcpu, uint64_t cr4)
if (((cr4 & CR4_VMXE) != 0UL) || ((cr4 & CR4_SMXE) != 0UL)) {
ret = false;
} else {
/* Do NOT support PCID in guest */
if ((cr4 & CR4_PCIDE) != 0UL) {
ret = false;
} else {
if (is_long_mode(vcpu)) {
if ((cr4 & CR4_PAE) == 0UL) {
ret = false;
}
if (is_long_mode(vcpu)) {
if ((cr4 & CR4_PAE) == 0UL) {
ret = false;
}
}
}
@ -294,7 +298,7 @@ static bool is_cr4_write_valid(struct acrn_vcpu *vcpu, uint64_t cr4)
* - OSXMMEXCPT (10) Flexible to guest
* - VMXE (13) Trapped to hide from guest
* - SMXE (14) must always be 0 => must lead to a VM exit
* - PCIDE (17) Trapped to hide from guest
* - PCIDE (17) Flexible tol guest
* - OSXSAVE (18) Flexible to guest
* - XSAVE (19) Flexible to guest
* We always keep align with physical cpu. So it's flexible to
@ -326,6 +330,30 @@ static void vmx_write_cr4(struct acrn_vcpu *vcpu, uint64_t cr4)
}
}
if ((err_found == false) && (((cr4 ^ old_cr4) & CR4_PCIDE) != 0UL)) {
/* MOV to CR4 causes a general-protection exception (#GP) if it would change
* CR4.PCIDE from 0 to 1 and either IA32_EFER.LMA = 0 or CR3[11:0] != 000H
*/
if ((cr4 & CR4_PCIDE) != 0UL) {
uint64_t guest_cr3 = exec_vmread(VMX_GUEST_CR3);
/* For now, HV passes-through PCID capability to guest, check X86_FEATURE_PCID of
* pcpu capabilities directly.
*/
if ((!pcpu_has_cap(X86_FEATURE_PCID)) || (!is_long_mode(vcpu)) ||
((guest_cr3 & 0xFFFUL) != 0UL)) {
pr_dbg("Failed to enable CR4.PCIE");
err_found = true;
vcpu_inject_gp(vcpu, 0U);
}
} else {
/* The instruction invalidates all TLB entries (including global entries) and
* all entries in all paging-structure caches (for all PCIDs) if it changes the
* value of the CR4.PCIDE from 1 to 0
*/
vcpu_make_request(vcpu, ACRN_REQUEST_EPT_FLUSH);
}
}
if (err_found == false) {
/* Clear forced off bits */
cr4_shadow = cr4 & ~CR4_MCE;