From: Magnus Kulke <[email protected]>
We cannot statically set the responses for CPUID[0xD,{1,2}].EBX, b/c
those are dynamic, dependent on which features the guest enables.
Hence we mask EBX when registering answers for those subleaves at the
hypervisor, which will result in the hypervisor providing us answers,
considering XCR0 and XSS.
The reported size now reflects the field masks properly (without the
mask they were 576 and 10728, which is wrong):
$ cpuid -l 0xd -s 0
CPU 0:
XSAVE features (0xd/0):
XCR0 valid bit field mask = 0x00000000000600e7
...
bytes required by fields in XCR0 = 0x00002b00 (11008)
bytes required by XSAVE/XRSTOR area = 0x00002b00 (11008)
$ cpuid -l 0xd -s 1
CPU 0:
XSAVE features (0xd/1):
...
SAVE area size in bytes = 0x000029c0 (10688)
IA32_XSS lower 32 bits valid bit field mask = 0x00001800
IA32_XSS upper 32 bits valid bit field mask = 0x00000000
Signed-off-by: Magnus Kulke <[email protected]>
Reviewed-by: Doru Blânzeanu <[email protected]>
Link:
https://lore.kernel.org/r/[email protected]
Signed-off-by: Paolo Bonzini <[email protected]>
---
target/i386/mshv/mshv-cpu.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index 66733390c3e..e85311af2af 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -528,6 +528,7 @@ static void collect_cpuid_entries(const CPUState *cpu,
GList **cpuid_entries)
static int register_intercept_result_cpuid_entry(const CPUState *cpu,
uint8_t subleaf_specific,
uint8_t always_override,
+ uint32_t ebx_mask,
struct hv_cpuid_entry *entry)
{
int ret;
@@ -543,11 +544,12 @@ static int register_intercept_result_cpuid_entry(const
CPUState *cpu,
/*
* Masks specify which bits to override. Set to 0xFFFFFFFF to
* override all bits with the values from the QEMU CPU model.
+ * A mask of 0 lets the hypervisor supply its own value.
*/
.result.eax = entry->eax,
.result.eax_mask = 0xFFFFFFFF,
.result.ebx = entry->ebx,
- .result.ebx_mask = 0xFFFFFFFF,
+ .result.ebx_mask = ebx_mask,
.result.ecx = entry->ecx,
.result.ecx_mask = 0xFFFFFFFF,
.result.edx = entry->edx,
@@ -582,6 +584,7 @@ static int register_intercept_result_cpuid(const CPUState
*cpu,
int ret = 0, entry_ret;
struct hv_cpuid_entry *entry;
uint8_t subleaf_specific, always_override;
+ uint32_t ebx_mask;
for (size_t i = 0; i < cpuid->nent; i++) {
entry = &cpuid->entries[i];
@@ -589,6 +592,7 @@ static int register_intercept_result_cpuid(const CPUState
*cpu,
/* set defaults */
subleaf_specific = 0;
always_override = 1;
+ ebx_mask = 0xFFFFFFFF;
/*
* Intel
@@ -628,8 +632,22 @@ static int register_intercept_result_cpuid(const CPUState
*cpu,
always_override = 1;
}
- entry_ret = register_intercept_result_cpuid_entry(cpu,
subleaf_specific,
+ /*
+ * CPUID[0xD,0].EBX and CPUID[0xD,1].EBX report the XSAVE area
+ * size based on features currently enabled in XCR0/XSS. These
+ * values are dynamic and must not be overridden with static
+ * results from the QEMU CPU model. Setting ebx_mask to 0 lets
+ * the hypervisor supply EBX based on the guest's actual state.
+ */
+ if (entry->function == 0x0d &&
+ (entry->index == 0 || entry->index == 1)) {
+ ebx_mask = 0;
+ }
+
+ entry_ret = register_intercept_result_cpuid_entry(cpu,
+ subleaf_specific,
always_override,
+ ebx_mask,
entry);
if ((entry_ret < 0) && (ret == 0)) {
ret = entry_ret;
@@ -1688,6 +1706,7 @@ uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t
idx, int reg)
if (func == 0x01 && reg == R_ECX) {
ret &= ~CPUID_EXT_VMX;
}
+
return ret;
}
--
2.54.0