whpx: i386: introduce proper cpuid support
Unlike the implementation in QEMU 10.2, this one works. It's not optimal though as it doesn't use the Hyper-V support for this. Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Link: https://lore.kernel.org/r/20260422214225.2242-8-mohamed@unpredictable.fr Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Mohamed Mediouni committed
Apr 22, 2026 at 23:41 UTC
79b3cb1f0145d154bb5f5656535174f6eb3ac67a
2 files changed
+122
-29
target/i386/whpx/whpx-all.c
+121
-15
@@ -2168,18 +2168,11 @@ int whpx_vcpu_run(CPUState *cpu)
2168
vcpu->exit_ctx.VpContext.Rip +
2169
vcpu->exit_ctx.VpContext.InstructionLength;
2170
2171
- if (whpx_is_legacy_os()) {
2172
- reg_values[1].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
2173
- reg_values[2].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRcx;
2174
- reg_values[3].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
2175
- reg_values[4].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
2176
- } else {
2177
- cpu_x86_cpuid(env, vcpu->exit_ctx.CpuidAccess.Rax,
2178
- vcpu->exit_ctx.CpuidAccess.Rcx,
2179
- (UINT32 *)®_values[1].Reg32,
2180
- (UINT32 *)®_values[4].Reg32, (UINT32 *)®_values[2].Reg32,
2181
- (UINT32 *)®_values[3].Reg32);
2182
- }
2171
+ cpu_x86_cpuid(env, vcpu->exit_ctx.CpuidAccess.Rax,
2172
+ vcpu->exit_ctx.CpuidAccess.Rcx,
2173
+ (UINT32 *)®_values[1].Reg32,
2174
+ (UINT32 *)®_values[4].Reg32, (UINT32 *)®_values[2].Reg32,
2175
+ (UINT32 *)®_values[3].Reg32);
2176
2177
if (!whpx->hyperv_enlightenments_enabled) {
2178
switch (vcpu->exit_ctx.CpuidAccess.Rax) {
@@ -2220,6 +2213,68 @@ int whpx_vcpu_run(CPUState *cpu)
2213
}
2214
break;
2215
}
2216
+ } else {
2217
+ switch (vcpu->exit_ctx.CpuidAccess.Rax) {
2218
+ case 0x40000000:
2219
+ case 0x40000001:
2220
+ case 0x40000010:
2221
+ reg_values[1].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
2222
+ reg_values[2].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRcx;
2223
+ reg_values[3].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
2224
+ reg_values[4].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
2225
+ break;
2226
+ }
2227
+ }
2228
+
2229
+ if (vcpu->exit_ctx.CpuidAccess.Rax == 0x1) {
2230
+ if (cpu_has_x2apic_feature(env)) {
2231
+ reg_values[2].Reg64 |= CPUID_EXT_X2APIC;
2232
+ } else {
2233
+ reg_values[2].Reg32 &= ~CPUID_EXT_X2APIC;
2234
+ }
2235
+ }
2236
+
2237
+ /* Dynamic depending on XCR0 and XSS, so query DefaultResult */
2238
+ if (vcpu->exit_ctx.CpuidAccess.Rax == 0x07
2239
+ && vcpu->exit_ctx.CpuidAccess.Rcx == 0) {
2240
+ if (vcpu->exit_ctx.CpuidAccess.DefaultResultRdx
2241
+ & CPUID_7_0_EDX_CET_IBT) {
2242
+ reg_values[3].Reg32 |= CPUID_7_0_EDX_CET_IBT;
2243
+ } else {
2244
+ reg_values[3].Reg32 &= ~CPUID_7_0_EDX_CET_IBT;
2245
+ }
2246
+
2247
+ if (vcpu->exit_ctx.CpuidAccess.DefaultResultRcx
2248
+ & CPUID_7_0_ECX_CET_SHSTK) {
2249
+ reg_values[2].Reg32 |= CPUID_7_0_ECX_CET_SHSTK;
2250
+ } else {
2251
+ reg_values[2].Reg32 &= ~CPUID_7_0_ECX_CET_SHSTK;
2252
+ }
2253
+
2254
+ if (vcpu->exit_ctx.CpuidAccess.DefaultResultRcx
2255
+ & CPUID_7_0_ECX_OSPKE) {
2256
+ reg_values[2].Reg32 |= CPUID_7_0_ECX_OSPKE;
2257
+ } else {
2258
+ reg_values[2].Reg32 &= ~CPUID_7_0_ECX_OSPKE;
2259
+ }
2260
+ }
2261
+
2262
+ /* CPUID[0xD,{1,2}].EBX are dynamic depending on guest features. */
2263
+ if (vcpu->exit_ctx.CpuidAccess.Rax == 0xd) {
2264
+ if (vcpu->exit_ctx.CpuidAccess.Rcx == 1
2265
+ || vcpu->exit_ctx.CpuidAccess.Rcx == 2) {
2266
+ reg_values[4].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
2267
+ }
2268
+ }
2269
+
2270
+ /* OSXSAVE is dynamic. Do this instead of syncing CR4 */
2271
+ if (vcpu->exit_ctx.CpuidAccess.Rax == 1) {
2272
+ if (vcpu->exit_ctx.CpuidAccess.DefaultResultRcx
2273
+ & CPUID_EXT_OSXSAVE) {
2274
+ reg_values[2].Reg32 |= CPUID_EXT_OSXSAVE;
2275
+ } else {
2276
+ reg_values[2].Reg32 &= ~CPUID_EXT_OSXSAVE;
2277
+ }
2278
}
2279
2280
hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
@@ -2409,6 +2464,45 @@ error:
2464
return ret;
2465
}
2466
2467
+static void whpx_cpu_xsave_init(void)
2468
+{
2469
+ static bool first = true;
2470
+ int i;
2471
+
2472
+ if (!first) {
2473
+ return;
2474
+ }
2475
+ first = false;
2476
+
2477
+ /* x87 and SSE states are in the legacy region of the XSAVE area. */
2478
+ x86_ext_save_areas[XSTATE_FP_BIT].offset = 0;
2479
+ x86_ext_save_areas[XSTATE_SSE_BIT].offset = 0;
2480
+
2481
+ for (i = XSTATE_SSE_BIT + 1; i < XSAVE_STATE_AREA_COUNT; i++) {
2482
+ ExtSaveArea *esa = &x86_ext_save_areas[i];
2483
+
2484
+ if (esa->size) {
2485
+ int sz = whpx_get_supported_cpuid(0xd, i, R_EAX);
2486
+ if (sz != 0) {
2487
+ assert(esa->size == sz);
2488
+ esa->offset = whpx_get_supported_cpuid(0xd, i, R_EBX);
2489
+ }
2490
+ }
2491
+ }
2492
+}
2493
+
2494
+static void whpx_cpu_max_instance_init(X86CPU *cpu)
2495
+{
2496
+ CPUX86State *env = &cpu->env;
2497
+
2498
+ env->cpuid_min_level =
2499
+ whpx_get_supported_cpuid(0x0, 0, R_EAX);
2500
+ env->cpuid_min_xlevel =
2501
+ whpx_get_supported_cpuid(0x80000000, 0, R_EAX);
2502
+ env->cpuid_min_xlevel2 =
2503
+ whpx_get_supported_cpuid(0xC0000000, 0, R_EAX);
2504
+}
2505
+
2506
static PropValue whpx_default_props[] = {
2507
{ "x2apic", "on" },
2508
{ NULL, NULL },
@@ -2418,9 +2512,18 @@ static PropValue whpx_default_props[] = {
2512
void whpx_cpu_instance_init(CPUState *cs)
2513
{
2514
X86CPU *cpu = X86_CPU(cs);
2515
+ X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu);
2516
2517
host_cpu_instance_init(cpu);
2518
x86_cpu_apply_props(cpu, whpx_default_props);
2519
+
2520
+ if (xcc->max_features) {
2521
+ whpx_cpu_max_instance_init(cpu);
2522
+ }
2523
+
2524
+ if (whpx_has_xsave()) {
2525
+ whpx_cpu_xsave_init();
2526
+ }
2527
}
2528
2529
/*
@@ -2438,8 +2541,11 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
2541
WHV_CAPABILITY_FEATURES features = {0};
2542
WHV_PROCESSOR_FEATURES_BANKS processor_features;
2543
WHV_PROCESSOR_PERFMON_FEATURES perfmon_features;
2441
- UINT32 cpuidExitList[] = {1};
2442
- UINT32 cpuidExitList_nohyperv[] = {1, 0x40000000, 0x40000001, 0x40000010};
2544
+
2545
+ UINT32 cpuidExitList[] = {0x0, 0x1, 0x6, 0x7, 0xb, 0xd, 0x14, 0x24, 0x29, 0x1E,
2546
+ 0x40000000, 0x40000001, 0x40000010, 0x80000000, 0x80000001,
2547
+ 0x80000002, 0x80000003, 0x80000004, 0x80000007, 0x80000008,
2548
+ 0x8000000A, 0x80000021, 0x80000022, 0xC0000000, 0xC0000001};
2549
2550
whpx = &whpx_global;
2551
@@ -2698,7 +2804,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
2804
hr = whp_dispatch.WHvSetPartitionProperty(
2805
whpx->partition,
2806
WHvPartitionPropertyCodeCpuidExitList,
2701
- whpx->hyperv_enlightenments_enabled ? cpuidExitList : cpuidExitList_nohyperv,
2807
+ cpuidExitList,
2808
RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
2809
2810
if (FAILED(hr)) {
target/i386/whpx/whpx-cpu-legacy.c
+1
-14
@@ -4,20 +4,7 @@
4
* Copyright (c) 2003 Fabrice Bellard
5
* Copyright (c) 2017 Google Inc.
6
*
7
- * This program is free software; you can redistribute it and/or
8
- * modify it under the terms of the GNU Lesser General Public
9
- * License as published by the Free Software Foundation; either
10
- * version 2.1 of the License, or (at your option) any later version.
11
- *
12
- * This program is distributed in the hope that it will be useful,
13
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
- * Lesser General Public License for more details.
16
- *
17
- * You should have received a copy of the GNU Lesser General Public
18
- * License along with this program; if not, see <http://www.gnu.org/licenses/>.
19
- *
20
- * cpuid
7
+ * SPDX-License-Identifier: LGPL-2.1-or-later
8
*/
9
10
#include "qemu/osdep.h"