hv: remove xsave dependence

ACRN could run without XSAVE Capability. So remove XSAVE dependence to support
more (hardware or virtual) platforms.

Tracked-On: #6287
Signed-off-by: Fei Li <fei1.li@intel.com>
This commit is contained in:
Fei Li 2021-08-03 15:42:11 +08:00 committed by wenlingz
parent 84235bf07c
commit 20061b7c39
7 changed files with 119 additions and 85 deletions

View File

@ -538,36 +538,38 @@ static void init_pcpu_xsave(void)
uint64_t xcr0, xss; uint64_t xcr0, xss;
uint32_t eax, ecx, unused, xsave_area_size; uint32_t eax, ecx, unused, xsave_area_size;
CPU_CR_READ(cr4, &val64); if (pcpu_has_cap(X86_FEATURE_XSAVE)) {
val64 |= CR4_OSXSAVE; CPU_CR_READ(cr4, &val64);
CPU_CR_WRITE(cr4, val64); val64 |= CR4_OSXSAVE;
CPU_CR_WRITE(cr4, val64);
if (get_pcpu_id() == BSP_CPU_ID) { if (get_pcpu_id() == BSP_CPU_ID) {
cpuid_subleaf(CPUID_FEATURES, 0x0U, &unused, &unused, &ecx, &unused); cpuid_subleaf(CPUID_FEATURES, 0x0U, &unused, &unused, &ecx, &unused);
/* if set, update it */ /* if set, update it */
if ((ecx & CPUID_ECX_OSXSAVE) != 0U) { if ((ecx & CPUID_ECX_OSXSAVE) != 0U) {
cpu_info = get_pcpu_info(); cpu_info = get_pcpu_info();
cpu_info->cpuid_leaves[FEAT_1_ECX] |= CPUID_ECX_OSXSAVE; cpu_info->cpuid_leaves[FEAT_1_ECX] |= CPUID_ECX_OSXSAVE;
/* set xcr0 and xss with the componets bitmap get from cpuid */ /* set xcr0 and xss with the componets bitmap get from cpuid */
xcr0 = ((uint64_t)cpu_info->cpuid_leaves[FEAT_D_0_EDX] << 32U) xcr0 = ((uint64_t)cpu_info->cpuid_leaves[FEAT_D_0_EDX] << 32U)
+ cpu_info->cpuid_leaves[FEAT_D_0_EAX]; + cpu_info->cpuid_leaves[FEAT_D_0_EAX];
xss = ((uint64_t)cpu_info->cpuid_leaves[FEAT_D_1_EDX] << 32U) xss = ((uint64_t)cpu_info->cpuid_leaves[FEAT_D_1_EDX] << 32U)
+ cpu_info->cpuid_leaves[FEAT_D_1_ECX]; + cpu_info->cpuid_leaves[FEAT_D_1_ECX];
write_xcr(0, xcr0); write_xcr(0, xcr0);
msr_write(MSR_IA32_XSS, xss); msr_write(MSR_IA32_XSS, xss);
/* get xsave area size, containing all the state components /* get xsave area size, containing all the state components
* corresponding to bits currently set in XCR0 | IA32_XSS */ * corresponding to bits currently set in XCR0 | IA32_XSS */
cpuid_subleaf(CPUID_XSAVE_FEATURES, 1U, cpuid_subleaf(CPUID_XSAVE_FEATURES, 1U,
&eax, &eax,
&xsave_area_size, &xsave_area_size,
&ecx, &ecx,
&unused); &unused);
if (xsave_area_size > XSAVE_STATE_AREA_SIZE) { if (xsave_area_size > XSAVE_STATE_AREA_SIZE) {
panic("XSAVE area (%d bytes) exceeds the pre-allocated 4K region\n", panic("XSAVE area (%d bytes) exceeds the pre-allocated 4K region\n",
xsave_area_size); xsave_area_size);
}
} }
} }
} }

View File

@ -415,6 +415,26 @@ static inline bool pcpu_has_vmx_unrestricted_guest_cap(void)
return ((msr_read(MSR_IA32_VMX_MISC) & MSR_IA32_MISC_UNRESTRICTED_GUEST) != 0UL); return ((msr_read(MSR_IA32_VMX_MISC) & MSR_IA32_MISC_UNRESTRICTED_GUEST) != 0UL);
} }
static bool is_valid_xsave_combination(void)
{
uint64_t value64 = msr_read(MSR_IA32_VMX_PROCBASED_CTLS2);
uint32_t high = (uint32_t)(value64 >> 32U); /* allowed 1-settings */
bool ret;
/* Now we only assume the platform must support XSAVE on CPU side and XSVE_XRSTR on VMX side or not,
* in this case, we could pass through CR4.OSXSAVE bit.
*/
if (pcpu_has_cap(X86_FEATURE_XSAVE)) {
ret = pcpu_has_cap(X86_FEATURE_XSAVES) && pcpu_has_cap(X86_FEATURE_COMPACTION_EXT) &&
((high & VMX_PROCBASED_CTLS2_XSVE_XRSTR) != 0U);
} else {
ret = !pcpu_has_cap(X86_FEATURE_XSAVES) && !pcpu_has_cap(X86_FEATURE_COMPACTION_EXT) &&
((high & VMX_PROCBASED_CTLS2_XSVE_XRSTR) == 0U);
}
return ret;
}
static int32_t check_vmx_mmu_cap(void) static int32_t check_vmx_mmu_cap(void)
{ {
int32_t ret = 0; int32_t ret = 0;
@ -519,18 +539,15 @@ int32_t detect_hardware_support(void)
} else if (!pcpu_has_cap(X86_FEATURE_POPCNT)) { } else if (!pcpu_has_cap(X86_FEATURE_POPCNT)) {
printf("%s, popcnt instruction not supported\n", __func__); printf("%s, popcnt instruction not supported\n", __func__);
ret = -ENODEV; ret = -ENODEV;
} else if (!pcpu_has_cap(X86_FEATURE_XSAVES)) {
printf("%s, XSAVES not supported\n", __func__);
ret = -ENODEV;
} else if (!pcpu_has_cap(X86_FEATURE_SSE)) { } else if (!pcpu_has_cap(X86_FEATURE_SSE)) {
printf("%s, SSE not supported\n", __func__); printf("%s, SSE not supported\n", __func__);
ret = -ENODEV; ret = -ENODEV;
} else if (!pcpu_has_cap(X86_FEATURE_COMPACTION_EXT)) {
printf("%s, Compaction extensions in XSAVE is not supported\n", __func__);
ret = -ENODEV;
} else if (!pcpu_has_cap(X86_FEATURE_RDRAND)) { } else if (!pcpu_has_cap(X86_FEATURE_RDRAND)) {
printf("%s, RDRAND is not supported\n", __func__); printf("%s, RDRAND is not supported\n", __func__);
ret = -ENODEV; ret = -ENODEV;
} else if (!is_valid_xsave_combination()) {
printf("%s, check XSAVE combined Capability failed\n", __func__);
ret = -ENODEV;
} else { } else {
ret = check_vmx_mmu_cap(); ret = check_vmx_mmu_cap();
} }

View File

@ -314,13 +314,18 @@ static void init_xsave(struct acrn_vcpu *vcpu)
struct ext_context *ectx = &(vcpu->arch.contexts[vcpu->arch.cur_context].ext_ctx); struct ext_context *ectx = &(vcpu->arch.contexts[vcpu->arch.cur_context].ext_ctx);
struct xsave_area *area = &ectx->xs_area; struct xsave_area *area = &ectx->xs_area;
ectx->xcr0 = XSAVE_FPU; /* if the HW has this cap, we need to prepare the buffer for potential save/restore.
(void)memset((void *)area, 0U, XSAVE_STATE_AREA_SIZE); * Guest may or may not enable XSAVE -- it doesn't matter.
/* xsaves only support compacted format, so set it in xcomp_bv[63],
* keep the reset area in header area as zero.
*/ */
ectx->xs_area.xsave_hdr.hdr.xcomp_bv |= XSAVE_COMPACTED_FORMAT; if (pcpu_has_cap(X86_FEATURE_XSAVE)) {
ectx->xcr0 = XSAVE_FPU;
(void)memset((void *)area, 0U, XSAVE_STATE_AREA_SIZE);
/* xsaves only support compacted format, so set it in xcomp_bv[63],
* keep the reset area in header area as zero.
*/
ectx->xs_area.xsave_hdr.hdr.xcomp_bv |= XSAVE_COMPACTED_FORMAT;
}
} }
void set_vcpu_regs(struct acrn_vcpu *vcpu, struct acrn_regs *vcpu_regs) void set_vcpu_regs(struct acrn_vcpu *vcpu, struct acrn_regs *vcpu_regs)
@ -827,19 +832,23 @@ void zombie_vcpu(struct acrn_vcpu *vcpu, enum vcpu_state new_state)
} }
} }
void save_xsave_area(__unused struct acrn_vcpu *vcpu, struct ext_context *ectx) void save_xsave_area(struct acrn_vcpu *vcpu, struct ext_context *ectx)
{ {
ectx->xcr0 = read_xcr(0); if (vcpu->arch.xsave_enabled) {
write_xcr(0, ectx->xcr0 | XSAVE_SSE); ectx->xcr0 = read_xcr(0);
xsaves(&ectx->xs_area, UINT64_MAX); write_xcr(0, ectx->xcr0 | XSAVE_SSE);
xsaves(&ectx->xs_area, UINT64_MAX);
}
} }
void rstore_xsave_area(const struct acrn_vcpu *vcpu, const struct ext_context *ectx) void rstore_xsave_area(const struct acrn_vcpu *vcpu, const struct ext_context *ectx)
{ {
write_xcr(0, ectx->xcr0 | XSAVE_SSE); if (vcpu->arch.xsave_enabled) {
msr_write(MSR_IA32_XSS, vcpu_get_guest_msr(vcpu, MSR_IA32_XSS)); write_xcr(0, ectx->xcr0 | XSAVE_SSE);
xrstors(&ectx->xs_area, UINT64_MAX); msr_write(MSR_IA32_XSS, vcpu_get_guest_msr(vcpu, MSR_IA32_XSS));
write_xcr(0, ectx->xcr0); xrstors(&ectx->xs_area, UINT64_MAX);
write_xcr(0, ectx->xcr0);
}
} }
/* TODO: /* TODO:

View File

@ -321,7 +321,7 @@ static void init_exec_ctrl(struct acrn_vcpu *vcpu)
*/ */
value32 = check_vmx_ctrl(MSR_IA32_VMX_PROCBASED_CTLS2, value32 = check_vmx_ctrl(MSR_IA32_VMX_PROCBASED_CTLS2,
VMX_PROCBASED_CTLS2_VAPIC | VMX_PROCBASED_CTLS2_EPT |VMX_PROCBASED_CTLS2_VPID | VMX_PROCBASED_CTLS2_VAPIC | VMX_PROCBASED_CTLS2_EPT |VMX_PROCBASED_CTLS2_VPID |
VMX_PROCBASED_CTLS2_RDTSCP | VMX_PROCBASED_CTLS2_UNRESTRICT | VMX_PROCBASED_CTLS2_RDTSCP | VMX_PROCBASED_CTLS2_UNRESTRICT | VMX_PROCBASED_CTLS2_XSVE_XRSTR |
VMX_PROCBASED_CTLS2_PAUSE_LOOP | VMX_PROCBASED_CTLS2_UWAIT_PAUSE); VMX_PROCBASED_CTLS2_PAUSE_LOOP | VMX_PROCBASED_CTLS2_UWAIT_PAUSE);
/* SDM Vol3, 25.3, setting "enable INVPCID" VM-execution to 1 with "INVLPG exiting" disabled, /* SDM Vol3, 25.3, setting "enable INVPCID" VM-execution to 1 with "INVLPG exiting" disabled,
@ -348,9 +348,11 @@ static void init_exec_ctrl(struct acrn_vcpu *vcpu)
exec_vmwrite32(VMX_TPR_THRESHOLD, 0U); exec_vmwrite32(VMX_TPR_THRESHOLD, 0U);
} }
if (pcpu_has_cap(X86_FEATURE_OSXSAVE)) { if ((value32 & VMX_PROCBASED_CTLS2_XSVE_XRSTR) != 0U) {
exec_vmwrite64(VMX_XSS_EXITING_BITMAP_FULL, 0UL); exec_vmwrite64(VMX_XSS_EXITING_BITMAP_FULL, 0UL);
value32 |= VMX_PROCBASED_CTLS2_XSVE_XRSTR; vcpu->arch.xsave_enabled = true;
} else {
value32 &= ~VMX_PROCBASED_CTLS2_XSVE_XRSTR;
} }
value32 |= VMX_PROCBASED_CTLS2_WBINVD; value32 |= VMX_PROCBASED_CTLS2_WBINVD;

View File

@ -392,47 +392,46 @@ int32_t cpuid_vmexit_handler(struct acrn_vcpu *vcpu)
*/ */
static int32_t xsetbv_vmexit_handler(struct acrn_vcpu *vcpu) static int32_t xsetbv_vmexit_handler(struct acrn_vcpu *vcpu)
{ {
int32_t idx; int32_t idx, ret = -1; /* ret < 0 call vcpu_inject_gp(vcpu, 0U) */
uint32_t cpl;
uint64_t val64; uint64_t val64;
int32_t ret = 0;
idx = vcpu->arch.cur_context; if (vcpu->arch.xsave_enabled && ((vcpu_get_cr4(vcpu) && CR4_OSXSAVE) != 0UL)) {
if (idx >= NR_WORLD) { idx = vcpu->arch.cur_context;
ret = -1; /* get current privilege level */
} else { cpl = exec_vmread32(VMX_GUEST_CS_ATTR);
/* to access XCR0,'ecx' should be 0 */ cpl = (cpl >> 5U) & 3U;
if ((vcpu_get_gpreg(vcpu, CPU_REG_RCX) & 0xffffffffUL) != 0UL) {
vcpu_inject_gp(vcpu, 0U);
} else {
val64 = (vcpu_get_gpreg(vcpu, CPU_REG_RAX) & 0xffffffffUL) |
(vcpu_get_gpreg(vcpu, CPU_REG_RDX) << 32U);
/* bit 0(x87 state) of XCR0 can't be cleared */ if ((idx < NR_WORLD) && (cpl == 0U)) {
if ((val64 & 0x01UL) == 0UL) { /* to access XCR0,'ecx' should be 0 */
vcpu_inject_gp(vcpu, 0U); if ((vcpu_get_gpreg(vcpu, CPU_REG_RCX) & 0xffffffffUL) == 0UL) {
} else if ((val64 & XCR0_RESERVED_BITS) != 0UL) { val64 = (vcpu_get_gpreg(vcpu, CPU_REG_RAX) & 0xffffffffUL) |
vcpu_inject_gp(vcpu, 0U); (vcpu_get_gpreg(vcpu, CPU_REG_RDX) << 32U);
} else {
/* /* bit 0(x87 state) of XCR0 can't be cleared */
* XCR0[2:1] (SSE state & AVX state) can't not be if (((val64 & 0x01UL) != 0UL) && ((val64 & XCR0_RESERVED_BITS) == 0UL)) {
* set to 10b as it is necessary to set both bits
* to use AVX instructions.
*/
if ((val64 & (XCR0_SSE | XCR0_AVX)) == XCR0_AVX) {
vcpu_inject_gp(vcpu, 0U);
} else {
/* /*
* SDM Vol.1 13-4, XCR0[4:3] are associated with MPX state, * XCR0[2:1] (SSE state & AVX state) can't not be
* Guest should not set these two bits without MPX support. * set to 10b as it is necessary to set both bits
* to use AVX instructions.
*/ */
if ((val64 & (XCR0_BNDREGS | XCR0_BNDCSR)) != 0UL) { if ((val64 & (XCR0_SSE | XCR0_AVX)) != XCR0_AVX) {
vcpu_inject_gp(vcpu, 0U); /*
} else { * SDM Vol.1 13-4, XCR0[4:3] are associated with MPX state,
write_xcr(0, val64); * Guest should not set these two bits without MPX support.
*/
if ((val64 & (XCR0_BNDREGS | XCR0_BNDCSR)) == 0UL) {
write_xcr(0, val64);
ret = 0;
}
} }
} }
} }
} }
} else {
/* CPUID.01H:ECX.XSAVE[bit 26] = 0 */
vcpu_inject_ud(vcpu);
ret = 0;
} }
return ret; return ret;

View File

@ -903,11 +903,15 @@ int32_t wrmsr_vmexit_handler(struct acrn_vcpu *vcpu)
} }
case MSR_IA32_XSS: case MSR_IA32_XSS:
{ {
if ((v & ~(MSR_IA32_XSS_PT | MSR_IA32_XSS_HDC)) != 0UL) { if (vcpu->arch.xsave_enabled) {
err = -EACCES; if ((v & ~(MSR_IA32_XSS_PT | MSR_IA32_XSS_HDC)) != 0UL) {
err = -EACCES;
} else {
vcpu_set_guest_msr(vcpu, MSR_IA32_XSS, v);
msr_write(msr, v);
}
} else { } else {
vcpu_set_guest_msr(vcpu, MSR_IA32_XSS, v); err = -EACCES;
msr_write(msr, v);
} }
break; break;
} }

View File

@ -253,6 +253,7 @@ struct acrn_vcpu_arch {
uint8_t lapic_mask; uint8_t lapic_mask;
bool irq_window_enabled; bool irq_window_enabled;
bool emulating_lock; bool emulating_lock;
bool xsave_enabled;
uint32_t nrexits; uint32_t nrexits;
/* VCPU context state information */ /* VCPU context state information */