@samitouri / QOSamiQemu / commits / 79b3cb1f01

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 *)&reg_values[1].Reg32,
2180 - (UINT32 *)&reg_values[4].Reg32, (UINT32 *)&reg_values[2].Reg32,
2181 - (UINT32 *)&reg_values[3].Reg32);
2182 - }
2171 + cpu_x86_cpuid(env, vcpu->exit_ctx.CpuidAccess.Rax,
2172 + vcpu->exit_ctx.CpuidAccess.Rcx,
2173 + (UINT32 *)&reg_values[1].Reg32,
2174 + (UINT32 *)&reg_values[4].Reg32, (UINT32 *)&reg_values[2].Reg32,
2175 + (UINT32 *)&reg_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"