Browse Source

whpx: reshuffle common code

Some code can be shared between x86_64 and arm64 WHPX. Do so as much as reasonable.

Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>

Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
master
Mohamed Mediouni 2 months ago
committed by Peter Maydell
parent
commit
4610fee324
  1. 2
      MAINTAINERS
  2. 1
      accel/whpx/meson.build
  3. 558
      accel/whpx/whpx-common.c
  4. 20
      include/system/whpx-all.h
  5. 21
      include/system/whpx-common.h
  6. 551
      target/i386/whpx/whpx-all.c

2
MAINTAINERS

@ -569,6 +569,8 @@ F: target/i386/whpx/
F: accel/stubs/whpx-stub.c
F: include/system/whpx.h
F: include/system/whpx-accel-ops.h
F: include/system/whpx-common.h
F: include/system/whpx-internal.h
MSHV
M: Magnus Kulke <magnus.kulke@linux.microsoft.com>

1
accel/whpx/meson.build

@ -1,6 +1,7 @@
whpx_ss = ss.source_set()
whpx_ss.add(files(
'whpx-accel-ops.c',
'whpx-common.c'
))
specific_ss.add_all(when: 'CONFIG_WHPX', if_true: whpx_ss)

558
accel/whpx/whpx-common.c

@ -0,0 +1,558 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* QEMU Windows Hypervisor Platform accelerator (WHPX)
*
* Copyright Microsoft Corp. 2017
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "system/address-spaces.h"
#include "system/ioport.h"
#include "gdbstub/helpers.h"
#include "qemu/accel.h"
#include "accel/accel-ops.h"
#include "system/whpx.h"
#include "system/cpus.h"
#include "system/runstate.h"
#include "qemu/main-loop.h"
#include "hw/core/boards.h"
#include "hw/intc/ioapic.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qapi/qapi-types-common.h"
#include "qapi/qapi-visit-common.h"
#include "migration/blocker.h"
#include "accel/accel-cpu-target.h"
#include <winerror.h>
#include "system/whpx-internal.h"
#include "system/whpx-accel-ops.h"
#include "system/whpx-common.h"
#include "system/whpx-all.h"
#include <winhvplatform.h>
#include <winhvplatformdefs.h>
bool whpx_allowed;
static bool whp_dispatch_initialized;
static HMODULE hWinHvPlatform;
static HMODULE hWinHvEmulation;
struct whpx_state whpx_global;
struct WHPDispatch whp_dispatch;
/* Tries to find a breakpoint at the specified address. */
struct whpx_breakpoint *whpx_lookup_breakpoint_by_addr(uint64_t address)
{
struct whpx_state *whpx = &whpx_global;
int i;
if (whpx->breakpoints.breakpoints) {
for (i = 0; i < whpx->breakpoints.breakpoints->used; i++) {
if (address == whpx->breakpoints.breakpoints->data[i].address) {
return &whpx->breakpoints.breakpoints->data[i];
}
}
}
return NULL;
}
/*
* This function is called when the a VCPU is about to start and no other
* VCPUs have been started so far. Since the VCPU start order could be
* arbitrary, it doesn't have to be VCPU#0.
*
* It is used to commit the breakpoints into memory, and configure WHPX
* to intercept debug exceptions.
*
* Note that whpx_set_exception_exit_bitmap() cannot be called if one or
* more VCPUs are already running, so this is the best place to do it.
*/
int whpx_first_vcpu_starting(CPUState *cpu)
{
struct whpx_state *whpx = &whpx_global;
g_assert(bql_locked());
if (!QTAILQ_EMPTY(&cpu->breakpoints) ||
(whpx->breakpoints.breakpoints &&
whpx->breakpoints.breakpoints->used)) {
CPUBreakpoint *bp;
int i = 0;
bool update_pending = false;
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
if (i >= whpx->breakpoints.original_address_count ||
bp->pc != whpx->breakpoints.original_addresses[i]) {
update_pending = true;
}
i++;
}
if (i != whpx->breakpoints.original_address_count) {
update_pending = true;
}
if (update_pending) {
/*
* The CPU breakpoints have changed since the last call to
* whpx_translate_cpu_breakpoints(). WHPX breakpoints must
* now be recomputed.
*/
whpx_translate_cpu_breakpoints(&whpx->breakpoints, cpu, i);
}
/* Actually insert the breakpoints into the memory. */
whpx_apply_breakpoints(whpx->breakpoints.breakpoints, cpu, true);
}
HRESULT hr;
uint64_t exception_mask;
if (whpx->step_pending ||
(whpx->breakpoints.breakpoints &&
whpx->breakpoints.breakpoints->used)) {
/*
* We are either attempting to single-step one or more CPUs, or
* have one or more breakpoints enabled. Both require intercepting
* the WHvX64ExceptionTypeBreakpointTrap exception.
*/
exception_mask = 1UL << WHvX64ExceptionTypeDebugTrapOrFault;
} else {
/* Let the guest handle all exceptions. */
exception_mask = 0;
}
hr = whpx_set_exception_exit_bitmap(exception_mask);
if (!SUCCEEDED(hr)) {
error_report("WHPX: Failed to update exception exit mask,"
"hr=%08lx.", hr);
return 1;
}
return 0;
}
/*
* This function is called when the last VCPU has finished running.
* It is used to remove any previously set breakpoints from memory.
*/
int whpx_last_vcpu_stopping(CPUState *cpu)
{
whpx_apply_breakpoints(whpx_global.breakpoints.breakpoints, cpu, false);
return 0;
}
static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
{
if (!cpu->vcpu_dirty) {
whpx_get_registers(cpu);
cpu->vcpu_dirty = true;
}
}
static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu,
run_on_cpu_data arg)
{
whpx_set_registers(cpu, WHPX_SET_RESET_STATE);
cpu->vcpu_dirty = false;
}
static void do_whpx_cpu_synchronize_post_init(CPUState *cpu,
run_on_cpu_data arg)
{
whpx_set_registers(cpu, WHPX_SET_FULL_STATE);
cpu->vcpu_dirty = false;
}
static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu,
run_on_cpu_data arg)
{
cpu->vcpu_dirty = true;
}
/*
* CPU support.
*/
void whpx_cpu_synchronize_state(CPUState *cpu)
{
if (!cpu->vcpu_dirty) {
run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL);
}
}
void whpx_cpu_synchronize_post_reset(CPUState *cpu)
{
run_on_cpu(cpu, do_whpx_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
}
void whpx_cpu_synchronize_post_init(CPUState *cpu)
{
run_on_cpu(cpu, do_whpx_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
}
void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu)
{
run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
}
static void whpx_pre_resume_vm(AccelState *as, bool step_pending)
{
whpx_global.step_pending = step_pending;
}
/*
* Vcpu support.
*/
int whpx_vcpu_exec(CPUState *cpu)
{
int ret;
int fatal;
for (;;) {
if (cpu->exception_index >= EXCP_INTERRUPT) {
ret = cpu->exception_index;
cpu->exception_index = -1;
break;
}
fatal = whpx_vcpu_run(cpu);
if (fatal) {
error_report("WHPX: Failed to exec a virtual processor");
abort();
}
}
return ret;
}
void whpx_destroy_vcpu(CPUState *cpu)
{
struct whpx_state *whpx = &whpx_global;
whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index);
AccelCPUState *vcpu = cpu->accel;
whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
g_free(cpu->accel);
}
void whpx_vcpu_kick(CPUState *cpu)
{
struct whpx_state *whpx = &whpx_global;
whp_dispatch.WHvCancelRunVirtualProcessor(
whpx->partition, cpu->cpu_index, 0);
}
/*
* Memory support.
*/
static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size,
void *host_va, int add, int rom,
const char *name)
{
struct whpx_state *whpx = &whpx_global;
HRESULT hr;
/*
if (add) {
printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
(void*)start_pa, (void*)size, host_va,
(rom ? "ROM" : "RAM"), name);
} else {
printf("WHPX: DEL PA:%p Size:%p, Host:%p, '%s'\n",
(void*)start_pa, (void*)size, host_va, name);
}
*/
if (add) {
hr = whp_dispatch.WHvMapGpaRange(whpx->partition,
host_va,
start_pa,
size,
(WHvMapGpaRangeFlagRead |
WHvMapGpaRangeFlagExecute |
(rom ? 0 : WHvMapGpaRangeFlagWrite)));
} else {
hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition,
start_pa,
size);
}
if (FAILED(hr)) {
error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
" Host:%p, hr=%08lx",
(add ? "MAP" : "UNMAP"), name,
(void *)(uintptr_t)start_pa, (void *)size, host_va, hr);
}
}
static void whpx_process_section(MemoryRegionSection *section, int add)
{
MemoryRegion *mr = section->mr;
hwaddr start_pa = section->offset_within_address_space;
ram_addr_t size = int128_get64(section->size);
unsigned int delta;
uint64_t host_va;
if (!memory_region_is_ram(mr)) {
return;
}
delta = qemu_real_host_page_size() - (start_pa & ~qemu_real_host_page_mask());
delta &= ~qemu_real_host_page_mask();
if (delta > size) {
return;
}
start_pa += delta;
size -= delta;
size &= qemu_real_host_page_mask();
if (!size || (start_pa & ~qemu_real_host_page_mask())) {
return;
}
host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
+ section->offset_within_region + delta;
whpx_update_mapping(start_pa, size, (void *)(uintptr_t)host_va, add,
memory_region_is_rom(mr), mr->name);
}
static void whpx_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
memory_region_ref(section->mr);
whpx_process_section(section, 1);
}
static void whpx_region_del(MemoryListener *listener,
MemoryRegionSection *section)
{
whpx_process_section(section, 0);
memory_region_unref(section->mr);
}
static void whpx_transaction_begin(MemoryListener *listener)
{
}
static void whpx_transaction_commit(MemoryListener *listener)
{
}
static void whpx_log_sync(MemoryListener *listener,
MemoryRegionSection *section)
{
MemoryRegion *mr = section->mr;
if (!memory_region_is_ram(mr)) {
return;
}
memory_region_set_dirty(mr, 0, int128_get64(section->size));
}
static MemoryListener whpx_memory_listener = {
.name = "whpx",
.begin = whpx_transaction_begin,
.commit = whpx_transaction_commit,
.region_add = whpx_region_add,
.region_del = whpx_region_del,
.log_sync = whpx_log_sync,
.priority = MEMORY_LISTENER_PRIORITY_ACCEL,
};
void whpx_memory_init(void)
{
memory_listener_register(&whpx_memory_listener, &address_space_memory);
}
/*
* Load the functions from the given library, using the given handle. If a
* handle is provided, it is used, otherwise the library is opened. The
* handle will be updated on return with the opened one.
*/
static bool load_whp_dispatch_fns(HMODULE *handle,
WHPFunctionList function_list)
{
HMODULE hLib = *handle;
#define WINHV_PLATFORM_DLL "WinHvPlatform.dll"
#define WINHV_EMULATION_DLL "WinHvEmulation.dll"
#define WHP_LOAD_FIELD_OPTIONAL(return_type, function_name, signature) \
whp_dispatch.function_name = \
(function_name ## _t)GetProcAddress(hLib, #function_name); \
#define WHP_LOAD_FIELD(return_type, function_name, signature) \
whp_dispatch.function_name = \
(function_name ## _t)GetProcAddress(hLib, #function_name); \
if (!whp_dispatch.function_name) { \
error_report("Could not load function %s", #function_name); \
goto error; \
} \
#define WHP_LOAD_LIB(lib_name, handle_lib) \
if (!handle_lib) { \
handle_lib = LoadLibrary(lib_name); \
if (!handle_lib) { \
error_report("Could not load library %s.", lib_name); \
goto error; \
} \
} \
switch (function_list) {
case WINHV_PLATFORM_FNS_DEFAULT:
WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib)
LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD)
break;
case WINHV_EMULATION_FNS_DEFAULT:
WHP_LOAD_LIB(WINHV_EMULATION_DLL, hLib)
LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD)
break;
case WINHV_PLATFORM_FNS_SUPPLEMENTAL:
WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib)
LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_LOAD_FIELD_OPTIONAL)
break;
}
*handle = hLib;
return true;
error:
if (hLib) {
FreeLibrary(hLib);
}
return false;
}
static void whpx_set_kernel_irqchip(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
struct whpx_state *whpx = &whpx_global;
OnOffSplit mode;
if (!visit_type_OnOffSplit(v, name, &mode, errp)) {
return;
}
switch (mode) {
case ON_OFF_SPLIT_ON:
whpx->kernel_irqchip_allowed = true;
whpx->kernel_irqchip_required = true;
break;
case ON_OFF_SPLIT_OFF:
whpx->kernel_irqchip_allowed = false;
whpx->kernel_irqchip_required = false;
break;
case ON_OFF_SPLIT_SPLIT:
error_setg(errp, "WHPX: split irqchip currently not supported");
error_append_hint(errp,
"Try without kernel-irqchip or with kernel-irqchip=on|off");
break;
default:
/*
* The value was checked in visit_type_OnOffSplit() above. If
* we get here, then something is wrong in QEMU.
*/
abort();
}
}
static void whpx_cpu_accel_class_init(ObjectClass *oc, const void *data)
{
AccelCPUClass *acc = ACCEL_CPU_CLASS(oc);
acc->cpu_instance_init = whpx_cpu_instance_init;
}
static const TypeInfo whpx_cpu_accel_type = {
.name = ACCEL_CPU_NAME("whpx"),
.parent = TYPE_ACCEL_CPU,
.class_init = whpx_cpu_accel_class_init,
.abstract = true,
};
/*
* Partition support
*/
bool whpx_apic_in_platform(void)
{
return whpx_global.apic_in_platform;
}
static void whpx_accel_class_init(ObjectClass *oc, const void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "WHPX";
ac->init_machine = whpx_accel_init;
ac->pre_resume_vm = whpx_pre_resume_vm;
ac->allowed = &whpx_allowed;
object_class_property_add(oc, "kernel-irqchip", "on|off|split",
NULL, whpx_set_kernel_irqchip,
NULL, NULL);
object_class_property_set_description(oc, "kernel-irqchip",
"Configure WHPX in-kernel irqchip");
}
static void whpx_accel_instance_init(Object *obj)
{
struct whpx_state *whpx = &whpx_global;
memset(whpx, 0, sizeof(struct whpx_state));
/* Turn on kernel-irqchip, by default */
whpx->kernel_irqchip_allowed = true;
}
static const TypeInfo whpx_accel_type = {
.name = ACCEL_CLASS_NAME("whpx"),
.parent = TYPE_ACCEL,
.instance_init = whpx_accel_instance_init,
.class_init = whpx_accel_class_init,
};
static void whpx_type_init(void)
{
type_register_static(&whpx_accel_type);
type_register_static(&whpx_cpu_accel_type);
}
bool init_whp_dispatch(void)
{
if (whp_dispatch_initialized) {
return true;
}
if (!load_whp_dispatch_fns(&hWinHvPlatform, WINHV_PLATFORM_FNS_DEFAULT)) {
goto error;
}
if (!load_whp_dispatch_fns(&hWinHvEmulation, WINHV_EMULATION_FNS_DEFAULT)) {
goto error;
}
assert(load_whp_dispatch_fns(&hWinHvPlatform,
WINHV_PLATFORM_FNS_SUPPLEMENTAL));
whp_dispatch_initialized = true;
return true;
error:
if (hWinHvPlatform) {
FreeLibrary(hWinHvPlatform);
}
if (hWinHvEmulation) {
FreeLibrary(hWinHvEmulation);
}
return false;
}
type_init(whpx_type_init);

20
include/system/whpx-all.h

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef SYSTEM_WHPX_ALL_H
#define SYSTEM_WHPX_ALL_H
/* Called by whpx-common */
int whpx_vcpu_run(CPUState *cpu);
void whpx_get_registers(CPUState *cpu);
void whpx_set_registers(CPUState *cpu, int level);
int whpx_accel_init(AccelState *as, MachineState *ms);
void whpx_cpu_instance_init(CPUState *cs);
HRESULT whpx_set_exception_exit_bitmap(UINT64 exceptions);
void whpx_apply_breakpoints(
struct whpx_breakpoint_collection *breakpoints,
CPUState *cpu,
bool resuming);
void whpx_translate_cpu_breakpoints(
struct whpx_breakpoints *breakpoints,
CPUState *cpu,
int cpu_breakpoint_count);
#endif

21
include/system/whpx-common.h

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef SYSTEM_WHPX_COMMON_H
#define SYSTEM_WHPX_COMMON_H
struct AccelCPUState {
WHV_EMULATOR_HANDLE emulator;
bool window_registered;
bool interruptable;
bool ready_for_pic_interrupt;
uint64_t tpr;
uint64_t apic_base;
bool interruption_pending;
/* Must be the last field as it may have a tail */
WHV_RUN_VP_EXIT_CONTEXT exit_ctx;
};
int whpx_first_vcpu_starting(CPUState *cpu);
int whpx_last_vcpu_stopping(CPUState *cpu);
void whpx_memory_init(void);
struct whpx_breakpoint *whpx_lookup_breakpoint_by_addr(uint64_t address);
#endif

551
target/i386/whpx/whpx-all.c

@ -33,6 +33,8 @@
#include "system/whpx-internal.h"
#include "system/whpx-accel-ops.h"
#include "system/whpx-all.h"
#include "system/whpx-common.h"
#include <winhvplatform.h>
#include <winhvemulation.h>
@ -232,28 +234,9 @@ typedef enum WhpxStepMode {
WHPX_STEP_EXCLUSIVE,
} WhpxStepMode;
struct AccelCPUState {
WHV_EMULATOR_HANDLE emulator;
bool window_registered;
bool interruptable;
bool ready_for_pic_interrupt;
uint64_t tpr;
uint64_t apic_base;
bool interruption_pending;
/* Must be the last field as it may have a tail */
WHV_RUN_VP_EXIT_CONTEXT exit_ctx;
};
bool whpx_allowed;
static bool whp_dispatch_initialized;
static HMODULE hWinHvPlatform, hWinHvEmulation;
static uint32_t max_vcpu_index;
static WHV_PROCESSOR_XSAVE_FEATURES whpx_xsave_cap;
struct whpx_state whpx_global;
struct WHPDispatch whp_dispatch;
static bool whpx_has_xsave(void)
{
return whpx_xsave_cap.XsaveSupport;
@ -379,7 +362,7 @@ static uint64_t whpx_cr8_to_apic_tpr(uint64_t cr8)
return cr8 << 4;
}
static void whpx_set_registers(CPUState *cpu, int level)
void whpx_set_registers(CPUState *cpu, int level)
{
struct whpx_state *whpx = &whpx_global;
AccelCPUState *vcpu = cpu->accel;
@ -594,7 +577,7 @@ static void whpx_get_xcrs(CPUState *cpu)
cpu_env(cpu)->xcr0 = xcr0.Reg64;
}
static void whpx_get_registers(CPUState *cpu)
void whpx_get_registers(CPUState *cpu)
{
struct whpx_state *whpx = &whpx_global;
AccelCPUState *vcpu = cpu->accel;
@ -934,7 +917,7 @@ static int whpx_handle_portio(CPUState *cpu,
* The 'exceptions' argument accepts a bitmask, e.g:
* (1 << WHvX64ExceptionTypeDebugTrapOrFault) | (...)
*/
static HRESULT whpx_set_exception_exit_bitmap(UINT64 exceptions)
HRESULT whpx_set_exception_exit_bitmap(UINT64 exceptions)
{
struct whpx_state *whpx = &whpx_global;
WHV_PARTITION_PROPERTY prop = { 0, };
@ -1084,23 +1067,6 @@ static HRESULT whpx_vcpu_configure_single_stepping(CPUState *cpu,
return S_OK;
}
/* Tries to find a breakpoint at the specified address. */
static struct whpx_breakpoint *whpx_lookup_breakpoint_by_addr(uint64_t address)
{
struct whpx_state *whpx = &whpx_global;
int i;
if (whpx->breakpoints.breakpoints) {
for (i = 0; i < whpx->breakpoints.breakpoints->used; i++) {
if (address == whpx->breakpoints.breakpoints->data[i].address) {
return &whpx->breakpoints.breakpoints->data[i];
}
}
}
return NULL;
}
/*
* Linux uses int3 (0xCC) during startup (see int3_selftest()) and for
* debugging user-mode applications. Since the WHPX API does not offer
@ -1136,7 +1102,7 @@ static const uint8_t whpx_breakpoint_instruction = 0xF1;
* memory, but doesn't actually do it. The memory accessing is done in
* whpx_apply_breakpoints().
*/
static void whpx_translate_cpu_breakpoints(
void whpx_translate_cpu_breakpoints(
struct whpx_breakpoints *breakpoints,
CPUState *cpu,
int cpu_breakpoint_count)
@ -1230,7 +1196,7 @@ static void whpx_translate_cpu_breakpoints(
* Passing resuming=true will try to set all previously unset breakpoints.
* Passing resuming=false will remove all inserted ones.
*/
static void whpx_apply_breakpoints(
void whpx_apply_breakpoints(
struct whpx_breakpoint_collection *breakpoints,
CPUState *cpu,
bool resuming)
@ -1306,93 +1272,6 @@ static void whpx_apply_breakpoints(
}
}
/*
* This function is called when the a VCPU is about to start and no other
* VCPUs have been started so far. Since the VCPU start order could be
* arbitrary, it doesn't have to be VCPU#0.
*
* It is used to commit the breakpoints into memory, and configure WHPX
* to intercept debug exceptions.
*
* Note that whpx_set_exception_exit_bitmap() cannot be called if one or
* more VCPUs are already running, so this is the best place to do it.
*/
static int whpx_first_vcpu_starting(CPUState *cpu)
{
struct whpx_state *whpx = &whpx_global;
HRESULT hr;
g_assert(bql_locked());
if (!QTAILQ_EMPTY(&cpu->breakpoints) ||
(whpx->breakpoints.breakpoints &&
whpx->breakpoints.breakpoints->used)) {
CPUBreakpoint *bp;
int i = 0;
bool update_pending = false;
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
if (i >= whpx->breakpoints.original_address_count ||
bp->pc != whpx->breakpoints.original_addresses[i]) {
update_pending = true;
}
i++;
}
if (i != whpx->breakpoints.original_address_count) {
update_pending = true;
}
if (update_pending) {
/*
* The CPU breakpoints have changed since the last call to
* whpx_translate_cpu_breakpoints(). WHPX breakpoints must
* now be recomputed.
*/
whpx_translate_cpu_breakpoints(&whpx->breakpoints, cpu, i);
}
/* Actually insert the breakpoints into the memory. */
whpx_apply_breakpoints(whpx->breakpoints.breakpoints, cpu, true);
}
uint64_t exception_mask;
if (whpx->step_pending ||
(whpx->breakpoints.breakpoints &&
whpx->breakpoints.breakpoints->used)) {
/*
* We are either attempting to single-step one or more CPUs, or
* have one or more breakpoints enabled. Both require intercepting
* the WHvX64ExceptionTypeBreakpointTrap exception.
*/
exception_mask = 1UL << WHvX64ExceptionTypeDebugTrapOrFault;
} else {
/* Let the guest handle all exceptions. */
exception_mask = 0;
}
hr = whpx_set_exception_exit_bitmap(exception_mask);
if (!SUCCEEDED(hr)) {
error_report("WHPX: Failed to update exception exit mask,"
"hr=%08lx.", hr);
return 1;
}
return 0;
}
/*
* This function is called when the last VCPU has finished running.
* It is used to remove any previously set breakpoints from memory.
*/
static int whpx_last_vcpu_stopping(CPUState *cpu)
{
whpx_apply_breakpoints(whpx_global.breakpoints.breakpoints, cpu, false);
return 0;
}
/* Returns the address of the next instruction that is about to be executed. */
static vaddr whpx_vcpu_get_pc(CPUState *cpu, bool exit_context_valid)
{
@ -1634,7 +1513,7 @@ static void whpx_vcpu_process_async_events(CPUState *cpu)
}
}
static int whpx_vcpu_run(CPUState *cpu)
int whpx_vcpu_run(CPUState *cpu)
{
HRESULT hr;
struct whpx_state *whpx = &whpx_global;
@ -2057,65 +1936,6 @@ static int whpx_vcpu_run(CPUState *cpu)
return ret < 0;
}
static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
{
if (!cpu->vcpu_dirty) {
whpx_get_registers(cpu);
cpu->vcpu_dirty = true;
}
}
static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu,
run_on_cpu_data arg)
{
whpx_set_registers(cpu, WHPX_SET_RESET_STATE);
cpu->vcpu_dirty = false;
}
static void do_whpx_cpu_synchronize_post_init(CPUState *cpu,
run_on_cpu_data arg)
{
whpx_set_registers(cpu, WHPX_SET_FULL_STATE);
cpu->vcpu_dirty = false;
}
static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu,
run_on_cpu_data arg)
{
cpu->vcpu_dirty = true;
}
/*
* CPU support.
*/
void whpx_cpu_synchronize_state(CPUState *cpu)
{
if (!cpu->vcpu_dirty) {
run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL);
}
}
void whpx_cpu_synchronize_post_reset(CPUState *cpu)
{
run_on_cpu(cpu, do_whpx_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
}
void whpx_cpu_synchronize_post_init(CPUState *cpu)
{
run_on_cpu(cpu, do_whpx_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
}
void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu)
{
run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
}
static void whpx_pre_resume_vm(AccelState *as, bool step_pending)
{
whpx_global.step_pending = step_pending;
}
/*
* Vcpu support.
*/
@ -2244,295 +2064,18 @@ error:
return ret;
}
int whpx_vcpu_exec(CPUState *cpu)
{
int ret;
int fatal;
for (;;) {
if (cpu->exception_index >= EXCP_INTERRUPT) {
ret = cpu->exception_index;
cpu->exception_index = -1;
break;
}
fatal = whpx_vcpu_run(cpu);
if (fatal) {
error_report("WHPX: Failed to exec a virtual processor");
abort();
}
}
return ret;
}
void whpx_destroy_vcpu(CPUState *cpu)
{
struct whpx_state *whpx = &whpx_global;
AccelCPUState *vcpu = cpu->accel;
whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index);
whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
g_free(cpu->accel);
}
void whpx_vcpu_kick(CPUState *cpu)
{
struct whpx_state *whpx = &whpx_global;
whp_dispatch.WHvCancelRunVirtualProcessor(
whpx->partition, cpu->cpu_index, 0);
}
/*
* Memory support.
*/
static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size,
void *host_va, int add, int rom,
const char *name)
{
struct whpx_state *whpx = &whpx_global;
HRESULT hr;
/*
if (add) {
printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
(void*)start_pa, (void*)size, host_va,
(rom ? "ROM" : "RAM"), name);
} else {
printf("WHPX: DEL PA:%p Size:%p, Host:%p, '%s'\n",
(void*)start_pa, (void*)size, host_va, name);
}
*/
if (add) {
hr = whp_dispatch.WHvMapGpaRange(whpx->partition,
host_va,
start_pa,
size,
(WHvMapGpaRangeFlagRead |
WHvMapGpaRangeFlagExecute |
(rom ? 0 : WHvMapGpaRangeFlagWrite)));
} else {
hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition,
start_pa,
size);
}
if (FAILED(hr)) {
error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
" Host:%p, hr=%08lx",
(add ? "MAP" : "UNMAP"), name,
(void *)(uintptr_t)start_pa, (void *)size, host_va, hr);
}
}
static void whpx_process_section(MemoryRegionSection *section, int add)
{
MemoryRegion *mr = section->mr;
hwaddr start_pa = section->offset_within_address_space;
ram_addr_t size = int128_get64(section->size);
unsigned int delta;
uint64_t host_va;
if (!memory_region_is_ram(mr)) {
return;
}
delta = qemu_real_host_page_size() - (start_pa & ~qemu_real_host_page_mask());
delta &= ~qemu_real_host_page_mask();
if (delta > size) {
return;
}
start_pa += delta;
size -= delta;
size &= qemu_real_host_page_mask();
if (!size || (start_pa & ~qemu_real_host_page_mask())) {
return;
}
host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
+ section->offset_within_region + delta;
whpx_update_mapping(start_pa, size, (void *)(uintptr_t)host_va, add,
memory_region_is_rom(mr), mr->name);
}
static void whpx_region_add(MemoryListener *listener,
MemoryRegionSection *section)
{
memory_region_ref(section->mr);
whpx_process_section(section, 1);
}
static void whpx_region_del(MemoryListener *listener,
MemoryRegionSection *section)
{
whpx_process_section(section, 0);
memory_region_unref(section->mr);
}
static void whpx_transaction_begin(MemoryListener *listener)
{
}
static void whpx_transaction_commit(MemoryListener *listener)
{
}
static void whpx_log_sync(MemoryListener *listener,
MemoryRegionSection *section)
{
MemoryRegion *mr = section->mr;
if (!memory_region_is_ram(mr)) {
return;
}
memory_region_set_dirty(mr, 0, int128_get64(section->size));
}
static MemoryListener whpx_memory_listener = {
.name = "whpx",
.begin = whpx_transaction_begin,
.commit = whpx_transaction_commit,
.region_add = whpx_region_add,
.region_del = whpx_region_del,
.log_sync = whpx_log_sync,
.priority = MEMORY_LISTENER_PRIORITY_ACCEL,
};
static void whpx_memory_init(void)
{
memory_listener_register(&whpx_memory_listener, &address_space_memory);
}
/*
* Load the functions from the given library, using the given handle. If a
* handle is provided, it is used, otherwise the library is opened. The
* handle will be updated on return with the opened one.
*/
static bool load_whp_dispatch_fns(HMODULE *handle,
WHPFunctionList function_list)
{
HMODULE hLib = *handle;
#define WINHV_PLATFORM_DLL "WinHvPlatform.dll"
#define WINHV_EMULATION_DLL "WinHvEmulation.dll"
#define WHP_LOAD_FIELD_OPTIONAL(return_type, function_name, signature) \
whp_dispatch.function_name = \
(function_name ## _t)GetProcAddress(hLib, #function_name); \
#define WHP_LOAD_FIELD(return_type, function_name, signature) \
whp_dispatch.function_name = \
(function_name ## _t)GetProcAddress(hLib, #function_name); \
if (!whp_dispatch.function_name) { \
error_report("Could not load function %s", #function_name); \
goto error; \
} \
#define WHP_LOAD_LIB(lib_name, handle_lib) \
if (!handle_lib) { \
handle_lib = LoadLibrary(lib_name); \
if (!handle_lib) { \
error_report("Could not load library %s.", lib_name); \
goto error; \
} \
} \
switch (function_list) {
case WINHV_PLATFORM_FNS_DEFAULT:
WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib)
LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD)
break;
case WINHV_EMULATION_FNS_DEFAULT:
WHP_LOAD_LIB(WINHV_EMULATION_DLL, hLib)
LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD)
break;
case WINHV_PLATFORM_FNS_SUPPLEMENTAL:
WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib)
LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_LOAD_FIELD_OPTIONAL)
break;
}
*handle = hLib;
return true;
error:
if (hLib) {
FreeLibrary(hLib);
}
return false;
}
static void whpx_set_kernel_irqchip(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
struct whpx_state *whpx = &whpx_global;
OnOffSplit mode;
if (!visit_type_OnOffSplit(v, name, &mode, errp)) {
return;
}
switch (mode) {
case ON_OFF_SPLIT_ON:
whpx->kernel_irqchip_allowed = true;
whpx->kernel_irqchip_required = true;
break;
case ON_OFF_SPLIT_OFF:
whpx->kernel_irqchip_allowed = false;
whpx->kernel_irqchip_required = false;
break;
case ON_OFF_SPLIT_SPLIT:
error_setg(errp, "WHPX: split irqchip currently not supported");
error_append_hint(errp,
"Try without kernel-irqchip or with kernel-irqchip=on|off");
break;
default:
/*
* The value was checked in visit_type_OnOffSplit() above. If
* we get here, then something is wrong in QEMU.
*/
abort();
}
}
static void whpx_cpu_instance_init(CPUState *cs)
void whpx_cpu_instance_init(CPUState *cs)
{
X86CPU *cpu = X86_CPU(cs);
host_cpu_instance_init(cpu);
}
static void whpx_cpu_accel_class_init(ObjectClass *oc, const void *data)
{
AccelCPUClass *acc = ACCEL_CPU_CLASS(oc);
acc->cpu_instance_init = whpx_cpu_instance_init;
}
static const TypeInfo whpx_cpu_accel_type = {
.name = ACCEL_CPU_NAME("whpx"),
.parent = TYPE_ACCEL_CPU,
.class_init = whpx_cpu_accel_class_init,
.abstract = true,
};
/*
* Partition support
*/
static int whpx_accel_init(AccelState *as, MachineState *ms)
int whpx_accel_init(AccelState *as, MachineState *ms)
{
struct whpx_state *whpx;
int ret;
@ -2715,77 +2258,3 @@ error:
return ret;
}
bool whpx_apic_in_platform(void) {
return whpx_global.apic_in_platform;
}
static void whpx_accel_class_init(ObjectClass *oc, const void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "WHPX";
ac->init_machine = whpx_accel_init;
ac->pre_resume_vm = whpx_pre_resume_vm;
ac->allowed = &whpx_allowed;
object_class_property_add(oc, "kernel-irqchip", "on|off|split",
NULL, whpx_set_kernel_irqchip,
NULL, NULL);
object_class_property_set_description(oc, "kernel-irqchip",
"Configure WHPX in-kernel irqchip");
}
static void whpx_accel_instance_init(Object *obj)
{
struct whpx_state *whpx = &whpx_global;
memset(whpx, 0, sizeof(struct whpx_state));
/* Turn on kernel-irqchip, by default */
whpx->kernel_irqchip_allowed = true;
}
static const TypeInfo whpx_accel_type = {
.name = ACCEL_CLASS_NAME("whpx"),
.parent = TYPE_ACCEL,
.instance_init = whpx_accel_instance_init,
.class_init = whpx_accel_class_init,
};
static void whpx_type_init(void)
{
type_register_static(&whpx_accel_type);
type_register_static(&whpx_cpu_accel_type);
}
bool init_whp_dispatch(void)
{
if (whp_dispatch_initialized) {
return true;
}
if (!load_whp_dispatch_fns(&hWinHvPlatform, WINHV_PLATFORM_FNS_DEFAULT)) {
goto error;
}
if (!load_whp_dispatch_fns(&hWinHvEmulation, WINHV_EMULATION_FNS_DEFAULT)) {
goto error;
}
assert(load_whp_dispatch_fns(&hWinHvPlatform,
WINHV_PLATFORM_FNS_SUPPLEMENTAL));
whp_dispatch_initialized = true;
return true;
error:
if (hWinHvPlatform) {
FreeLibrary(hWinHvPlatform);
}
if (hWinHvEmulation) {
FreeLibrary(hWinHvEmulation);
}
return false;
}
type_init(whpx_type_init);

Loading…
Cancel
Save