mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:10 +00:00
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>
This commit is contained in:
committed by
Peter Maydell
parent
1f1f9fd88c
commit
4610fee324
@@ -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,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
Normal file
558
accel/whpx/whpx-common.c
Normal file
@@ -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
Normal file
20
include/system/whpx-all.h
Normal file
@@ -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
Normal file
21
include/system/whpx-common.h
Normal file
@@ -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
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user