Windows 10 doesn't have the API for this, so using this
only for Windows 11.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
include/system/whpx-internal.h | 9 ++++
target/i386/cpu.c | 25 ++++++++++++
target/i386/whpx/whpx-all.c | 75 ++++++++++++++++++++++++++++++++--
target/i386/whpx/whpx-i386.h | 4 ++
4 files changed, 109 insertions(+), 4 deletions(-)
create mode 100644 target/i386/whpx/whpx-i386.h
diff --git a/include/system/whpx-internal.h b/include/system/whpx-internal.h
index 8482901f71..5902124b63 100644
--- a/include/system/whpx-internal.h
+++ b/include/system/whpx-internal.h
@@ -73,6 +73,14 @@ void whpx_apic_get(APICCommonState *s);
X(HRESULT, WHvGetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, WHV_REGISTER_VALUE* RegisterValues)) \
X(HRESULT, WHvSetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, const WHV_REGISTER_VALUE* RegisterValues)) \
+#ifdef __x86_64__
+#define LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL_ARCH(X) \
+ X(HRESULT, WHvGetVirtualProcessorCpuidOutput, \
+ (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, UINT32 Eax, \
+ UINT32 Ecx, WHV_CPUID_OUTPUT *CpuidOutput))
+#else
+#define LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL_ARCH(X)
+#endif
/*
* These are supplemental functions that may not be present
* on all versions and are not critical for basic functionality.
@@ -89,6 +97,7 @@ void whpx_apic_get(APICCommonState *s);
UINT32 StateSize)) \
X(HRESULT, WHvResetPartition, \
(WHV_PARTITION_HANDLE Partition)) \
+ LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL_ARCH(X)
#define WHP_DEFINE_TYPE(return_type, function_name, signature) \
typedef return_type (WINAPI *function_name ## _t) signature;
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index c6fd1dc00e..0000093fa3 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -26,6 +26,8 @@
#include "tcg/helper-tcg.h"
#include "exec/translation-block.h"
#include "system/hvf.h"
+#include "system/whpx.h"
+#include "whpx/whpx-i386.h"
#include "hvf/hvf-i386.h"
#include "kvm/kvm_i386.h"
#include "kvm/tdx.h"
@@ -8087,6 +8089,17 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w)
r = hvf_get_supported_cpuid(wi->cpuid.eax,
wi->cpuid.ecx,
wi->cpuid.reg);
+ } else if (whpx_enabled()) {
+ if (wi->type != CPUID_FEATURE_WORD) {
+ return 0;
+ }
+ if (whpx_is_legacy_os()) {
+ r = wi->tcg_features;
+ } else {
+ r = whpx_get_supported_cpuid(wi->cpuid.eax,
+ wi->cpuid.ecx,
+ wi->cpuid.reg);
+ }
} else if (tcg_enabled() || qtest_enabled()) {
r = wi->tcg_features;
} else {
@@ -8168,6 +8181,18 @@ static void x86_cpu_get_supported_cpuid(uint32_t func, uint32_t index,
*ebx = hvf_get_supported_cpuid(func, index, R_EBX);
*ecx = hvf_get_supported_cpuid(func, index, R_ECX);
*edx = hvf_get_supported_cpuid(func, index, R_EDX);
+ } else if (whpx_enabled()) {
+ if (whpx_is_legacy_os()) {
+ *eax = 0;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ } else {
+ *eax = whpx_get_supported_cpuid(func, index, R_EAX);
+ *ebx = whpx_get_supported_cpuid(func, index, R_EBX);
+ *ecx = whpx_get_supported_cpuid(func, index, R_ECX);
+ *edx = whpx_get_supported_cpuid(func, index, R_EDX);
+ }
} else {
*eax = 0;
*ebx = 0;
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 4127440c0c..2d527b90dd 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -36,6 +36,7 @@
#include "system/whpx-accel-ops.h"
#include "system/whpx-all.h"
#include "system/whpx-common.h"
+#include "whpx-i386.h"
#include "emulate/x86_decode.h"
#include "emulate/x86_emu.h"
@@ -49,6 +50,8 @@
/* for kernel-irqchip=off */
#define HV_X64_MSR_APIC_FREQUENCY 0x40000023
+static bool is_modern_os = true;
+
static const WHV_REGISTER_NAME whpx_register_names[] = {
/* X64 General purpose registers */
@@ -1062,6 +1065,71 @@ static void whpx_init_emu(void)
init_emu(&whpx_x86_emul_ops);
}
+bool whpx_is_legacy_os(void)
+{
+ return !is_modern_os;
+}
+
+uint32_t whpx_get_supported_cpuid(uint32_t func, uint32_t idx, int reg)
+{
+ WHV_CPUID_OUTPUT output;
+ uint32_t eax, ebx, ecx, edx;
+ uint32_t cpu_index = 0;
+ bool temp_cpu = true;
+ HRESULT hr;
+
+ hr = whp_dispatch.WHvCreateVirtualProcessor(
+ whpx_global.partition, cpu_index, 0);
+
+ /* This means that the CPU already exists... */
+ if (FAILED(hr)) {
+ temp_cpu = false;
+ }
+
+ hr = whp_dispatch.WHvGetVirtualProcessorCpuidOutput(whpx_global.partition,
+ cpu_index, func, idx, &output);
+
+ if (FAILED(hr)) {
+ abort();
+ }
+
+ if (temp_cpu) {
+ hr = whp_dispatch.WHvDeleteVirtualProcessor(whpx_global.partition, cpu_index);
+ if (FAILED(hr)) {
+ abort();
+ }
+ }
+
+ eax = output.Eax;
+ ebx = output.Ebx;
+ ecx = output.Ecx;
+ edx = output.Edx;
+
+ /*
+ * We can emulate X2APIC even for the kernel-irqchip=off case.
+ * CPUID_EXT_HYPERVISOR and CPUID_HT should be considered present
+ * always, so report them as unconditionally supported here.
+ */
+ if (func == 1) {
+ ecx |= CPUID_EXT_X2APIC;
+ ecx |= CPUID_EXT_HYPERVISOR;
+ edx |= CPUID_HT;
+ }
+
+ switch (reg) {
+ case R_EAX:
+ return eax;
+ case R_EBX:
+ return ebx;
+ case R_ECX:
+ return ecx;
+ case R_EDX:
+ return edx;
+ default:
+ return 0;
+ }
+}
+
/*
* Controls whether we should intercept various exceptions on the guest,
* namely breakpoint/single-step events.
@@ -2235,7 +2303,6 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
WHV_CAPABILITY_FEATURES features = {0};
WHV_PROCESSOR_FEATURES_BANKS processor_features;
WHV_PROCESSOR_PERFMON_FEATURES perfmon_features;
- bool is_legacy_os = false;
UINT32 cpuidExitList[] = {1};
whpx = &whpx_global;
@@ -2395,7 +2462,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
if (FAILED(hr)) {
warn_report("WHPX: Failed to get performance "
"monitoring features, hr=%08lx", hr);
- is_legacy_os = true;
+ is_modern_os = false;
} else {
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
@@ -2435,7 +2502,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
synthetic_features.Bank0.DirectSyntheticTimers = 1;
}
- if (!is_legacy_os && whpx->hyperv_enlightenments_allowed) {
+ if (is_modern_os && whpx->hyperv_enlightenments_allowed) {
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
WHvPartitionPropertyCodeSyntheticProcessorFeaturesBanks,
@@ -2446,7 +2513,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
ret = -EINVAL;
goto error;
}
- } else if (is_legacy_os && whpx->hyperv_enlightenments_required) {
+ } else if (!is_modern_os && whpx->hyperv_enlightenments_required) {
error_report("Hyper-V enlightenments not available on legacy Windows");
ret = -EINVAL;
goto error;
diff --git a/target/i386/whpx/whpx-i386.h b/target/i386/whpx/whpx-i386.h
new file mode 100644
index 0000000000..6db9a75d39
--- /dev/null
+++ b/target/i386/whpx/whpx-i386.h
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+uint32_t whpx_get_supported_cpuid(uint32_t func, uint32_t idx, int reg);
+bool whpx_is_legacy_os(void);
--
2.50.1 (Apple Git-155)