target/i386/whp-dispatch.h | 3 +- target/i386/whpx-all.c | 94 ++++++++++++++++++++++++++++++++++---- 2 files changed, 86 insertions(+), 11 deletions(-)
Newer versions of WHPX provide the capability to query the tsc
and apic frequency. Expose these through the vmware cpuid leaf.
This patch doesnt support setting the tsc frequency; that will
come as a separate fix.
Signed-off-by: Sunil Muthuswamy <sunilmut@microsoft.com>
---
target/i386/whp-dispatch.h | 3 +-
target/i386/whpx-all.c | 94 ++++++++++++++++++++++++++++++++++----
2 files changed, 86 insertions(+), 11 deletions(-)
diff --git a/target/i386/whp-dispatch.h b/target/i386/whp-dispatch.h
index e4695c349f..b18aba20ed 100644
--- a/target/i386/whp-dispatch.h
+++ b/target/i386/whp-dispatch.h
@@ -2,10 +2,11 @@
#define WHP_DISPATCH_H
#include <windows.h>
-
#include <WinHvPlatform.h>
#include <WinHvEmulation.h>
+#define WHV_E_UNKNOWN_CAPABILITY 0x80370300L
+
#define LIST_WINHVPLATFORM_FUNCTIONS(X) \
X(HRESULT, WHvGetCapability, (WHV_CAPABILITY_CODE CapabilityCode, VOID* CapabilityBuffer, UINT32 CapabilityBufferSizeInBytes, UINT32* WrittenSizeInBytes)) \
X(HRESULT, WHvCreatePartition, (WHV_PARTITION_HANDLE* Partition)) \
diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c
index c78baac6df..da4c135925 100644
--- a/target/i386/whpx-all.c
+++ b/target/i386/whpx-all.c
@@ -27,6 +27,8 @@
#include <WinHvPlatform.h>
#include <WinHvEmulation.h>
+#define HYPERV_APIC_BUS_FREQUENCY (200000000ULL)
+
struct whpx_state {
uint64_t mem_quota;
WHV_PARTITION_HANDLE partition;
@@ -1061,6 +1063,18 @@ static int whpx_vcpu_run(CPUState *cpu)
cpu_x86_cpuid(env, cpuid_fn, 0, (UINT32 *)&rax, (UINT32 *)&rbx,
(UINT32 *)&rcx, (UINT32 *)&rdx);
switch (cpuid_fn) {
+ case 0x40000000:
+ /* Expose the vmware cpu frequency cpuid leaf */
+ rax = 0x40000010;
+ rbx = rcx = rdx = 0;
+ break;
+
+ case 0x40000010:
+ rax = env->tsc_khz;
+ rbx = env->apic_bus_freq / 1000; /* Hz to KHz */
+ rcx = rdx = 0;
+ break;
+
case 0x80000001:
/* Remove any support of OSVW */
rcx &= ~CPUID_EXT3_OSVW;
@@ -1193,6 +1207,10 @@ int whpx_init_vcpu(CPUState *cpu)
struct whpx_state *whpx = &whpx_global;
struct whpx_vcpu *vcpu;
Error *local_error = NULL;
+ struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ UINT64 freq = 0;
+ int ret;
/* Add migration blockers for all unsupported features of the
* Windows Hypervisor Platform
@@ -1207,7 +1225,8 @@ int whpx_init_vcpu(CPUState *cpu)
error_report_err(local_error);
migrate_del_blocker(whpx_migration_blocker);
error_free(whpx_migration_blocker);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
}
@@ -1215,7 +1234,8 @@ int whpx_init_vcpu(CPUState *cpu)
if (!vcpu) {
error_report("WHPX: Failed to allocte VCPU context.");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto error;
}
hr = whp_dispatch.WHvEmulatorCreateEmulator(
@@ -1224,8 +1244,8 @@ int whpx_init_vcpu(CPUState *cpu)
if (FAILED(hr)) {
error_report("WHPX: Failed to setup instruction completion support,"
" hr=%08lx", hr);
- g_free(vcpu);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
hr = whp_dispatch.WHvCreateVirtualProcessor(
@@ -1234,17 +1254,72 @@ int whpx_init_vcpu(CPUState *cpu)
error_report("WHPX: Failed to create a virtual processor,"
" hr=%08lx", hr);
whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
- g_free(vcpu);
- return -EINVAL;
+ ret = -EINVAL;
+ goto error;
}
- vcpu->interruptable = true;
+ /*
+ * vcpu's TSC frequency is either specified by user, or use the value
+ * provided by Hyper-V if the former is not present. In the latter case, we
+ * query it from Hyper-V and record in env->tsc_khz, so that vcpu's TSC
+ * frequency can be migrated later via this field.
+ */
+ if (!env->tsc_khz) {
+ hr = whp_dispatch.WHvGetCapability(
+ WHvCapabilityCodeProcessorClockFrequency, &freq, sizeof(freq),
+ NULL);
+ if (hr != WHV_E_UNKNOWN_CAPABILITY) {
+ if (FAILED(hr)) {
+ printf("WHPX: Failed to query tsc frequency, hr=0x%08lx\n", hr);
+ } else {
+ env->tsc_khz = freq / 1000; /* Hz to KHz */
+ }
+ }
+ }
+ env->apic_bus_freq = HYPERV_APIC_BUS_FREQUENCY;
+ hr = whp_dispatch.WHvGetCapability(
+ WHvCapabilityCodeInterruptClockFrequency, &freq, sizeof(freq), NULL);
+ if (hr != WHV_E_UNKNOWN_CAPABILITY) {
+ if (FAILED(hr)) {
+ printf("WHPX: Failed to query apic bus frequency hr=0x%08lx\n", hr);
+ } else {
+ env->apic_bus_freq = freq;
+ }
+ }
+
+ /*
+ * If the vmware cpuid frequency leaf option is set, and we have a valid
+ * tsc value, trap the corresponding cpuid's.
+ */
+ if (x86_cpu->vmware_cpuid_freq && env->tsc_khz) {
+ UINT32 cpuidExitList[] = {1, 0x80000001, 0x40000000, 0x40000010};
+
+ hr = whp_dispatch.WHvSetPartitionProperty(
+ whpx->partition,
+ WHvPartitionPropertyCodeCpuidExitList,
+ cpuidExitList,
+ RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
+
+ if (FAILED(hr)) {
+ error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
+ hr);
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+
+ vcpu->interruptable = true;
cpu->vcpu_dirty = true;
cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu;
qemu_add_vm_change_state_handler(whpx_cpu_update_state, cpu->env_ptr);
return 0;
+
+error:
+ g_free(vcpu);
+
+ return ret;
}
int whpx_vcpu_exec(CPUState *cpu)
@@ -1493,6 +1568,7 @@ static int whpx_accel_init(MachineState *ms)
WHV_CAPABILITY whpx_cap;
UINT32 whpx_cap_size;
WHV_PARTITION_PROPERTY prop;
+ UINT32 cpuidExitList[] = {1, 0x80000001};
whpx = &whpx_global;
@@ -1551,7 +1627,6 @@ static int whpx_accel_init(MachineState *ms)
goto error;
}
- UINT32 cpuidExitList[] = {1, 0x80000001};
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
WHvPartitionPropertyCodeCpuidExitList,
@@ -1579,14 +1654,13 @@ static int whpx_accel_init(MachineState *ms)
printf("Windows Hypervisor Platform accelerator is operational\n");
return 0;
- error:
+error:
if (NULL != whpx->partition) {
whp_dispatch.WHvDeletePartition(whpx->partition);
whpx->partition = NULL;
}
-
return ret;
}
--
2.25.1
Can I please get some eyes on this? > -----Original Message----- > From: Sunil Muthuswamy > Sent: Thursday, July 30, 2020 3:11 PM > To: Paolo Bonzini <pbonzini@redhat.com>; Richard Henderson <rth@twiddle.net>; Eduardo Habkost <ehabkost@redhat.com> > Cc: qemu-devel@nongnu.org; Stefan Weil <sw@weilnetz.de> > Subject: [PATCH] WHPX: vmware cpuid leaf for tsc and apic frequency > > Newer versions of WHPX provide the capability to query the tsc > and apic frequency. Expose these through the vmware cpuid leaf. > This patch doesnt support setting the tsc frequency; that will > come as a separate fix. > > Signed-off-by: Sunil Muthuswamy <sunilmut@microsoft.com> > --- > target/i386/whp-dispatch.h | 3 +- > target/i386/whpx-all.c | 94 ++++++++++++++++++++++++++++++++++---- > 2 files changed, 86 insertions(+), 11 deletions(-) > > diff --git a/target/i386/whp-dispatch.h b/target/i386/whp-dispatch.h > index e4695c349f..b18aba20ed 100644 > --- a/target/i386/whp-dispatch.h > +++ b/target/i386/whp-dispatch.h > @@ -2,10 +2,11 @@ > #define WHP_DISPATCH_H > > #include <windows.h> > - > #include <WinHvPlatform.h> > #include <WinHvEmulation.h> > > +#define WHV_E_UNKNOWN_CAPABILITY 0x80370300L > + > #define LIST_WINHVPLATFORM_FUNCTIONS(X) \ > X(HRESULT, WHvGetCapability, (WHV_CAPABILITY_CODE CapabilityCode, VOID* CapabilityBuffer, UINT32 > CapabilityBufferSizeInBytes, UINT32* WrittenSizeInBytes)) \ > X(HRESULT, WHvCreatePartition, (WHV_PARTITION_HANDLE* Partition)) \ > diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c > index c78baac6df..da4c135925 100644 > --- a/target/i386/whpx-all.c > +++ b/target/i386/whpx-all.c > @@ -27,6 +27,8 @@ > #include <WinHvPlatform.h> > #include <WinHvEmulation.h> > > +#define HYPERV_APIC_BUS_FREQUENCY (200000000ULL) > + > struct whpx_state { > uint64_t mem_quota; > WHV_PARTITION_HANDLE partition; > @@ -1061,6 +1063,18 @@ static int whpx_vcpu_run(CPUState *cpu) > cpu_x86_cpuid(env, cpuid_fn, 0, (UINT32 *)&rax, (UINT32 *)&rbx, > (UINT32 *)&rcx, (UINT32 *)&rdx); > switch (cpuid_fn) { > + case 0x40000000: > + /* Expose the vmware cpu frequency cpuid leaf */ > + rax = 0x40000010; > + rbx = rcx = rdx = 0; > + break; > + > + case 0x40000010: > + rax = env->tsc_khz; > + rbx = env->apic_bus_freq / 1000; /* Hz to KHz */ > + rcx = rdx = 0; > + break; > + > case 0x80000001: > /* Remove any support of OSVW */ > rcx &= ~CPUID_EXT3_OSVW; > @@ -1193,6 +1207,10 @@ int whpx_init_vcpu(CPUState *cpu) > struct whpx_state *whpx = &whpx_global; > struct whpx_vcpu *vcpu; > Error *local_error = NULL; > + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); > + X86CPU *x86_cpu = X86_CPU(cpu); > + UINT64 freq = 0; > + int ret; > > /* Add migration blockers for all unsupported features of the > * Windows Hypervisor Platform > @@ -1207,7 +1225,8 @@ int whpx_init_vcpu(CPUState *cpu) > error_report_err(local_error); > migrate_del_blocker(whpx_migration_blocker); > error_free(whpx_migration_blocker); > - return -EINVAL; > + ret = -EINVAL; > + goto error; > } > } > > @@ -1215,7 +1234,8 @@ int whpx_init_vcpu(CPUState *cpu) > > if (!vcpu) { > error_report("WHPX: Failed to allocte VCPU context."); > - return -ENOMEM; > + ret = -ENOMEM; > + goto error; > } > > hr = whp_dispatch.WHvEmulatorCreateEmulator( > @@ -1224,8 +1244,8 @@ int whpx_init_vcpu(CPUState *cpu) > if (FAILED(hr)) { > error_report("WHPX: Failed to setup instruction completion support," > " hr=%08lx", hr); > - g_free(vcpu); > - return -EINVAL; > + ret = -EINVAL; > + goto error; > } > > hr = whp_dispatch.WHvCreateVirtualProcessor( > @@ -1234,17 +1254,72 @@ int whpx_init_vcpu(CPUState *cpu) > error_report("WHPX: Failed to create a virtual processor," > " hr=%08lx", hr); > whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator); > - g_free(vcpu); > - return -EINVAL; > + ret = -EINVAL; > + goto error; > } > > - vcpu->interruptable = true; > + /* > + * vcpu's TSC frequency is either specified by user, or use the value > + * provided by Hyper-V if the former is not present. In the latter case, we > + * query it from Hyper-V and record in env->tsc_khz, so that vcpu's TSC > + * frequency can be migrated later via this field. > + */ > + if (!env->tsc_khz) { > + hr = whp_dispatch.WHvGetCapability( > + WHvCapabilityCodeProcessorClockFrequency, &freq, sizeof(freq), > + NULL); > + if (hr != WHV_E_UNKNOWN_CAPABILITY) { > + if (FAILED(hr)) { > + printf("WHPX: Failed to query tsc frequency, hr=0x%08lx\n", hr); > + } else { > + env->tsc_khz = freq / 1000; /* Hz to KHz */ > + } > + } > + } > > + env->apic_bus_freq = HYPERV_APIC_BUS_FREQUENCY; > + hr = whp_dispatch.WHvGetCapability( > + WHvCapabilityCodeInterruptClockFrequency, &freq, sizeof(freq), NULL); > + if (hr != WHV_E_UNKNOWN_CAPABILITY) { > + if (FAILED(hr)) { > + printf("WHPX: Failed to query apic bus frequency hr=0x%08lx\n", hr); > + } else { > + env->apic_bus_freq = freq; > + } > + } > + > + /* > + * If the vmware cpuid frequency leaf option is set, and we have a valid > + * tsc value, trap the corresponding cpuid's. > + */ > + if (x86_cpu->vmware_cpuid_freq && env->tsc_khz) { > + UINT32 cpuidExitList[] = {1, 0x80000001, 0x40000000, 0x40000010}; > + > + hr = whp_dispatch.WHvSetPartitionProperty( > + whpx->partition, > + WHvPartitionPropertyCodeCpuidExitList, > + cpuidExitList, > + RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32)); > + > + if (FAILED(hr)) { > + error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx", > + hr); > + ret = -EINVAL; > + goto error; > + } > + } > + > + vcpu->interruptable = true; > cpu->vcpu_dirty = true; > cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu; > qemu_add_vm_change_state_handler(whpx_cpu_update_state, cpu->env_ptr); > > return 0; > + > +error: > + g_free(vcpu); > + > + return ret; > } > > int whpx_vcpu_exec(CPUState *cpu) > @@ -1493,6 +1568,7 @@ static int whpx_accel_init(MachineState *ms) > WHV_CAPABILITY whpx_cap; > UINT32 whpx_cap_size; > WHV_PARTITION_PROPERTY prop; > + UINT32 cpuidExitList[] = {1, 0x80000001}; > > whpx = &whpx_global; > > @@ -1551,7 +1627,6 @@ static int whpx_accel_init(MachineState *ms) > goto error; > } > > - UINT32 cpuidExitList[] = {1, 0x80000001}; > hr = whp_dispatch.WHvSetPartitionProperty( > whpx->partition, > WHvPartitionPropertyCodeCpuidExitList, > @@ -1579,14 +1654,13 @@ static int whpx_accel_init(MachineState *ms) > printf("Windows Hypervisor Platform accelerator is operational\n"); > return 0; > > - error: > +error: > > if (NULL != whpx->partition) { > whp_dispatch.WHvDeletePartition(whpx->partition); > whpx->partition = NULL; > } > > - > return ret; > } > > -- > 2.25.1
On 01/09/20 01:32, Sunil Muthuswamy wrote: > Can I please get some eyes on this? Yes, sorry about the bad timing (5.1 freeze + Meson conversion + parental leave). Paolo >> -----Original Message----- >> From: Sunil Muthuswamy >> Sent: Thursday, July 30, 2020 3:11 PM >> To: Paolo Bonzini <pbonzini@redhat.com>; Richard Henderson <rth@twiddle.net>; Eduardo Habkost <ehabkost@redhat.com> >> Cc: qemu-devel@nongnu.org; Stefan Weil <sw@weilnetz.de> >> Subject: [PATCH] WHPX: vmware cpuid leaf for tsc and apic frequency >> >> Newer versions of WHPX provide the capability to query the tsc >> and apic frequency. Expose these through the vmware cpuid leaf. >> This patch doesnt support setting the tsc frequency; that will >> come as a separate fix. >> >> Signed-off-by: Sunil Muthuswamy <sunilmut@microsoft.com> >> --- >> target/i386/whp-dispatch.h | 3 +- >> target/i386/whpx-all.c | 94 ++++++++++++++++++++++++++++++++++---- >> 2 files changed, 86 insertions(+), 11 deletions(-) >> >> diff --git a/target/i386/whp-dispatch.h b/target/i386/whp-dispatch.h >> index e4695c349f..b18aba20ed 100644 >> --- a/target/i386/whp-dispatch.h >> +++ b/target/i386/whp-dispatch.h >> @@ -2,10 +2,11 @@ >> #define WHP_DISPATCH_H >> >> #include <windows.h> >> - >> #include <WinHvPlatform.h> >> #include <WinHvEmulation.h> >> >> +#define WHV_E_UNKNOWN_CAPABILITY 0x80370300L >> + >> #define LIST_WINHVPLATFORM_FUNCTIONS(X) \ >> X(HRESULT, WHvGetCapability, (WHV_CAPABILITY_CODE CapabilityCode, VOID* CapabilityBuffer, UINT32 >> CapabilityBufferSizeInBytes, UINT32* WrittenSizeInBytes)) \ >> X(HRESULT, WHvCreatePartition, (WHV_PARTITION_HANDLE* Partition)) \ >> diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c >> index c78baac6df..da4c135925 100644 >> --- a/target/i386/whpx-all.c >> +++ b/target/i386/whpx-all.c >> @@ -27,6 +27,8 @@ >> #include <WinHvPlatform.h> >> #include <WinHvEmulation.h> >> >> +#define HYPERV_APIC_BUS_FREQUENCY (200000000ULL) >> + >> struct whpx_state { >> uint64_t mem_quota; >> WHV_PARTITION_HANDLE partition; >> @@ -1061,6 +1063,18 @@ static int whpx_vcpu_run(CPUState *cpu) >> cpu_x86_cpuid(env, cpuid_fn, 0, (UINT32 *)&rax, (UINT32 *)&rbx, >> (UINT32 *)&rcx, (UINT32 *)&rdx); >> switch (cpuid_fn) { >> + case 0x40000000: >> + /* Expose the vmware cpu frequency cpuid leaf */ >> + rax = 0x40000010; >> + rbx = rcx = rdx = 0; >> + break; >> + >> + case 0x40000010: >> + rax = env->tsc_khz; >> + rbx = env->apic_bus_freq / 1000; /* Hz to KHz */ >> + rcx = rdx = 0; >> + break; >> + >> case 0x80000001: >> /* Remove any support of OSVW */ >> rcx &= ~CPUID_EXT3_OSVW; >> @@ -1193,6 +1207,10 @@ int whpx_init_vcpu(CPUState *cpu) >> struct whpx_state *whpx = &whpx_global; >> struct whpx_vcpu *vcpu; >> Error *local_error = NULL; >> + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); >> + X86CPU *x86_cpu = X86_CPU(cpu); >> + UINT64 freq = 0; >> + int ret; >> >> /* Add migration blockers for all unsupported features of the >> * Windows Hypervisor Platform >> @@ -1207,7 +1225,8 @@ int whpx_init_vcpu(CPUState *cpu) >> error_report_err(local_error); >> migrate_del_blocker(whpx_migration_blocker); >> error_free(whpx_migration_blocker); >> - return -EINVAL; >> + ret = -EINVAL; >> + goto error; >> } >> } >> >> @@ -1215,7 +1234,8 @@ int whpx_init_vcpu(CPUState *cpu) >> >> if (!vcpu) { >> error_report("WHPX: Failed to allocte VCPU context."); >> - return -ENOMEM; >> + ret = -ENOMEM; >> + goto error; >> } >> >> hr = whp_dispatch.WHvEmulatorCreateEmulator( >> @@ -1224,8 +1244,8 @@ int whpx_init_vcpu(CPUState *cpu) >> if (FAILED(hr)) { >> error_report("WHPX: Failed to setup instruction completion support," >> " hr=%08lx", hr); >> - g_free(vcpu); >> - return -EINVAL; >> + ret = -EINVAL; >> + goto error; >> } >> >> hr = whp_dispatch.WHvCreateVirtualProcessor( >> @@ -1234,17 +1254,72 @@ int whpx_init_vcpu(CPUState *cpu) >> error_report("WHPX: Failed to create a virtual processor," >> " hr=%08lx", hr); >> whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator); >> - g_free(vcpu); >> - return -EINVAL; >> + ret = -EINVAL; >> + goto error; >> } >> >> - vcpu->interruptable = true; >> + /* >> + * vcpu's TSC frequency is either specified by user, or use the value >> + * provided by Hyper-V if the former is not present. In the latter case, we >> + * query it from Hyper-V and record in env->tsc_khz, so that vcpu's TSC >> + * frequency can be migrated later via this field. >> + */ >> + if (!env->tsc_khz) { >> + hr = whp_dispatch.WHvGetCapability( >> + WHvCapabilityCodeProcessorClockFrequency, &freq, sizeof(freq), >> + NULL); >> + if (hr != WHV_E_UNKNOWN_CAPABILITY) { >> + if (FAILED(hr)) { >> + printf("WHPX: Failed to query tsc frequency, hr=0x%08lx\n", hr); >> + } else { >> + env->tsc_khz = freq / 1000; /* Hz to KHz */ >> + } >> + } >> + } >> >> + env->apic_bus_freq = HYPERV_APIC_BUS_FREQUENCY; >> + hr = whp_dispatch.WHvGetCapability( >> + WHvCapabilityCodeInterruptClockFrequency, &freq, sizeof(freq), NULL); >> + if (hr != WHV_E_UNKNOWN_CAPABILITY) { >> + if (FAILED(hr)) { >> + printf("WHPX: Failed to query apic bus frequency hr=0x%08lx\n", hr); >> + } else { >> + env->apic_bus_freq = freq; >> + } >> + } >> + >> + /* >> + * If the vmware cpuid frequency leaf option is set, and we have a valid >> + * tsc value, trap the corresponding cpuid's. >> + */ >> + if (x86_cpu->vmware_cpuid_freq && env->tsc_khz) { >> + UINT32 cpuidExitList[] = {1, 0x80000001, 0x40000000, 0x40000010}; >> + >> + hr = whp_dispatch.WHvSetPartitionProperty( >> + whpx->partition, >> + WHvPartitionPropertyCodeCpuidExitList, >> + cpuidExitList, >> + RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32)); >> + >> + if (FAILED(hr)) { >> + error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx", >> + hr); >> + ret = -EINVAL; >> + goto error; >> + } >> + } >> + >> + vcpu->interruptable = true; >> cpu->vcpu_dirty = true; >> cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu; >> qemu_add_vm_change_state_handler(whpx_cpu_update_state, cpu->env_ptr); >> >> return 0; >> + >> +error: >> + g_free(vcpu); >> + >> + return ret; >> } >> >> int whpx_vcpu_exec(CPUState *cpu) >> @@ -1493,6 +1568,7 @@ static int whpx_accel_init(MachineState *ms) >> WHV_CAPABILITY whpx_cap; >> UINT32 whpx_cap_size; >> WHV_PARTITION_PROPERTY prop; >> + UINT32 cpuidExitList[] = {1, 0x80000001}; >> >> whpx = &whpx_global; >> >> @@ -1551,7 +1627,6 @@ static int whpx_accel_init(MachineState *ms) >> goto error; >> } >> >> - UINT32 cpuidExitList[] = {1, 0x80000001}; >> hr = whp_dispatch.WHvSetPartitionProperty( >> whpx->partition, >> WHvPartitionPropertyCodeCpuidExitList, >> @@ -1579,14 +1654,13 @@ static int whpx_accel_init(MachineState *ms) >> printf("Windows Hypervisor Platform accelerator is operational\n"); >> return 0; >> >> - error: >> +error: >> >> if (NULL != whpx->partition) { >> whp_dispatch.WHvDeletePartition(whpx->partition); >> whpx->partition = NULL; >> } >> >> - >> return ret; >> } >> >> -- >> 2.25.1 >
On 31/07/20 00:11, Sunil Muthuswamy wrote: > Newer versions of WHPX provide the capability to query the tsc > and apic frequency. Expose these through the vmware cpuid leaf. > This patch doesnt support setting the tsc frequency; that will > come as a separate fix. > > Signed-off-by: Sunil Muthuswamy <sunilmut@microsoft.com> > --- > target/i386/whp-dispatch.h | 3 +- > target/i386/whpx-all.c | 94 ++++++++++++++++++++++++++++++++++---- > 2 files changed, 86 insertions(+), 11 deletions(-) > > diff --git a/target/i386/whp-dispatch.h b/target/i386/whp-dispatch.h > index e4695c349f..b18aba20ed 100644 > --- a/target/i386/whp-dispatch.h > +++ b/target/i386/whp-dispatch.h > @@ -2,10 +2,11 @@ > #define WHP_DISPATCH_H > > #include <windows.h> > - > #include <WinHvPlatform.h> > #include <WinHvEmulation.h> > > +#define WHV_E_UNKNOWN_CAPABILITY 0x80370300L > + > #define LIST_WINHVPLATFORM_FUNCTIONS(X) \ > X(HRESULT, WHvGetCapability, (WHV_CAPABILITY_CODE CapabilityCode, VOID* CapabilityBuffer, UINT32 CapabilityBufferSizeInBytes, UINT32* WrittenSizeInBytes)) \ > X(HRESULT, WHvCreatePartition, (WHV_PARTITION_HANDLE* Partition)) \ > diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c > index c78baac6df..da4c135925 100644 > --- a/target/i386/whpx-all.c > +++ b/target/i386/whpx-all.c > @@ -27,6 +27,8 @@ > #include <WinHvPlatform.h> > #include <WinHvEmulation.h> > > +#define HYPERV_APIC_BUS_FREQUENCY (200000000ULL) > + > struct whpx_state { > uint64_t mem_quota; > WHV_PARTITION_HANDLE partition; > @@ -1061,6 +1063,18 @@ static int whpx_vcpu_run(CPUState *cpu) > cpu_x86_cpuid(env, cpuid_fn, 0, (UINT32 *)&rax, (UINT32 *)&rbx, > (UINT32 *)&rcx, (UINT32 *)&rdx); > switch (cpuid_fn) { > + case 0x40000000: > + /* Expose the vmware cpu frequency cpuid leaf */ > + rax = 0x40000010; > + rbx = rcx = rdx = 0; > + break; > + > + case 0x40000010: > + rax = env->tsc_khz; > + rbx = env->apic_bus_freq / 1000; /* Hz to KHz */ > + rcx = rdx = 0; > + break; > + > case 0x80000001: > /* Remove any support of OSVW */ > rcx &= ~CPUID_EXT3_OSVW; > @@ -1193,6 +1207,10 @@ int whpx_init_vcpu(CPUState *cpu) > struct whpx_state *whpx = &whpx_global; > struct whpx_vcpu *vcpu; > Error *local_error = NULL; > + struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); > + X86CPU *x86_cpu = X86_CPU(cpu); > + UINT64 freq = 0; > + int ret; > > /* Add migration blockers for all unsupported features of the > * Windows Hypervisor Platform > @@ -1207,7 +1225,8 @@ int whpx_init_vcpu(CPUState *cpu) > error_report_err(local_error); > migrate_del_blocker(whpx_migration_blocker); > error_free(whpx_migration_blocker); > - return -EINVAL; > + ret = -EINVAL; > + goto error; > } > } > > @@ -1215,7 +1234,8 @@ int whpx_init_vcpu(CPUState *cpu) > > if (!vcpu) { > error_report("WHPX: Failed to allocte VCPU context."); > - return -ENOMEM; > + ret = -ENOMEM; > + goto error; > } > > hr = whp_dispatch.WHvEmulatorCreateEmulator( > @@ -1224,8 +1244,8 @@ int whpx_init_vcpu(CPUState *cpu) > if (FAILED(hr)) { > error_report("WHPX: Failed to setup instruction completion support," > " hr=%08lx", hr); > - g_free(vcpu); > - return -EINVAL; > + ret = -EINVAL; > + goto error; > } > > hr = whp_dispatch.WHvCreateVirtualProcessor( > @@ -1234,17 +1254,72 @@ int whpx_init_vcpu(CPUState *cpu) > error_report("WHPX: Failed to create a virtual processor," > " hr=%08lx", hr); > whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator); > - g_free(vcpu); > - return -EINVAL; > + ret = -EINVAL; > + goto error; > } > > - vcpu->interruptable = true; > + /* > + * vcpu's TSC frequency is either specified by user, or use the value > + * provided by Hyper-V if the former is not present. In the latter case, we > + * query it from Hyper-V and record in env->tsc_khz, so that vcpu's TSC > + * frequency can be migrated later via this field. > + */ > + if (!env->tsc_khz) { > + hr = whp_dispatch.WHvGetCapability( > + WHvCapabilityCodeProcessorClockFrequency, &freq, sizeof(freq), > + NULL); > + if (hr != WHV_E_UNKNOWN_CAPABILITY) { > + if (FAILED(hr)) { > + printf("WHPX: Failed to query tsc frequency, hr=0x%08lx\n", hr); > + } else { > + env->tsc_khz = freq / 1000; /* Hz to KHz */ > + } > + } > + } > > + env->apic_bus_freq = HYPERV_APIC_BUS_FREQUENCY; > + hr = whp_dispatch.WHvGetCapability( > + WHvCapabilityCodeInterruptClockFrequency, &freq, sizeof(freq), NULL); > + if (hr != WHV_E_UNKNOWN_CAPABILITY) { > + if (FAILED(hr)) { > + printf("WHPX: Failed to query apic bus frequency hr=0x%08lx\n", hr); > + } else { > + env->apic_bus_freq = freq; > + } > + } > + > + /* > + * If the vmware cpuid frequency leaf option is set, and we have a valid > + * tsc value, trap the corresponding cpuid's. > + */ > + if (x86_cpu->vmware_cpuid_freq && env->tsc_khz) { > + UINT32 cpuidExitList[] = {1, 0x80000001, 0x40000000, 0x40000010}; > + > + hr = whp_dispatch.WHvSetPartitionProperty( > + whpx->partition, > + WHvPartitionPropertyCodeCpuidExitList, > + cpuidExitList, > + RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32)); > + > + if (FAILED(hr)) { > + error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx", > + hr); > + ret = -EINVAL; > + goto error; > + } > + } > + > + vcpu->interruptable = true; > cpu->vcpu_dirty = true; > cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu; > qemu_add_vm_change_state_handler(whpx_cpu_update_state, cpu->env_ptr); > > return 0; > + > +error: > + g_free(vcpu); > + > + return ret; > } > > int whpx_vcpu_exec(CPUState *cpu) > @@ -1493,6 +1568,7 @@ static int whpx_accel_init(MachineState *ms) > WHV_CAPABILITY whpx_cap; > UINT32 whpx_cap_size; > WHV_PARTITION_PROPERTY prop; > + UINT32 cpuidExitList[] = {1, 0x80000001}; > > whpx = &whpx_global; > > @@ -1551,7 +1627,6 @@ static int whpx_accel_init(MachineState *ms) > goto error; > } > > - UINT32 cpuidExitList[] = {1, 0x80000001}; > hr = whp_dispatch.WHvSetPartitionProperty( > whpx->partition, > WHvPartitionPropertyCodeCpuidExitList, > @@ -1579,14 +1654,13 @@ static int whpx_accel_init(MachineState *ms) > printf("Windows Hypervisor Platform accelerator is operational\n"); > return 0; > > - error: > +error: > > if (NULL != whpx->partition) { > whp_dispatch.WHvDeletePartition(whpx->partition); > whpx->partition = NULL; > } > > - > return ret; > } > > Queued, thanks. Paolo
© 2016 - 2024 Red Hat, Inc.