* RTC fixes (Artem)
* icount fixes (Artem) * rr fixes (Pavel, myself) * hotplug cleanup (Igor) * SCSI fixes (myself) * 4.20-rc1 KVM header update (myself) * coalesced PIO support (Peng Hao) * HVF fixes (Roman B.) * Hyper-V refactoring (Roman K.) * Support for Hyper-V IPI (Vitaly) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQEcBAABAgAGBQJbycRuAAoJEL/70l94x66DGL4H/00Gu/+0dNlpxt6hYVaJ30jX vFCsZoglBJ060M8m0C9roTF7zdIgI/X0oxJKWNaxqCDD0GSL5oM1AfG0DCsEBq6X ApHYfBOh6mMWuB2qzV9QkK0b2u7+g9J8pQQYfZlU+QNtmUUmbzBxV4h7oqOoedJZ nTJrkYzBg88bLDXUAuFrnMhaktqzPvyhdD36vUX5Kc9Hk9R3krtEenc/XKfEJg+o n1DX9QeAWgi3MdhkhXSaNSnAu2k2+/qJDmOPk1r63ft5ZfaUKOaVecU06ioiEmrc KJd6EYeRvh2eIpbOCGSEVDrieGVBOPvqYg0ryWroxSveoPqJZh5ys9MdIjD+8zg= =4XhC -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging * RTC fixes (Artem) * icount fixes (Artem) * rr fixes (Pavel, myself) * hotplug cleanup (Igor) * SCSI fixes (myself) * 4.20-rc1 KVM header update (myself) * coalesced PIO support (Peng Hao) * HVF fixes (Roman B.) * Hyper-V refactoring (Roman K.) * Support for Hyper-V IPI (Vitaly) # gpg: Signature made Fri 19 Oct 2018 12:47:58 BST # gpg: using RSA key BFFBD25F78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini/tags/for-upstream: (47 commits) replay: pass raw icount value to replay_save_clock target/i386: kvm: just return after migrate_add_blocker failed hyperv_testdev: add SynIC message and event testmodes hyperv: process POST_MESSAGE hypercall hyperv: add support for KVM_HYPERV_EVENTFD hyperv: process SIGNAL_EVENT hypercall hyperv: add synic event flag signaling hyperv: add synic message delivery hyperv: make overlay pages for SynIC hyperv: only add SynIC in compatible configurations hyperv: qom-ify SynIC hyperv:synic: split capability testing and setting i386: add hyperv-stub for CONFIG_HYPERV=n default-configs: collect CONFIG_HYPERV* in hyperv.mak hyperv: factor out arch-independent API into hw/hyperv hyperv: make hyperv_vp_index inline hyperv: split hyperv-proto.h into x86 and arch-independent parts hyperv: rename kvm_hv_sint_route_set_sint hyperv: make HvSintRoute reference-counted hyperv: address HvSintRoute by X86CPU pointer ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
b312532fd0
|
@ -79,6 +79,7 @@ struct KVMState
|
||||||
int fd;
|
int fd;
|
||||||
int vmfd;
|
int vmfd;
|
||||||
int coalesced_mmio;
|
int coalesced_mmio;
|
||||||
|
int coalesced_pio;
|
||||||
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
|
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
|
||||||
bool coalesced_flush_in_progress;
|
bool coalesced_flush_in_progress;
|
||||||
int vcpu_events;
|
int vcpu_events;
|
||||||
|
@ -560,6 +561,45 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void kvm_coalesce_pio_add(MemoryListener *listener,
|
||||||
|
MemoryRegionSection *section,
|
||||||
|
hwaddr start, hwaddr size)
|
||||||
|
{
|
||||||
|
KVMState *s = kvm_state;
|
||||||
|
|
||||||
|
if (s->coalesced_pio) {
|
||||||
|
struct kvm_coalesced_mmio_zone zone;
|
||||||
|
|
||||||
|
zone.addr = start;
|
||||||
|
zone.size = size;
|
||||||
|
zone.pio = 1;
|
||||||
|
|
||||||
|
(void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kvm_coalesce_pio_del(MemoryListener *listener,
|
||||||
|
MemoryRegionSection *section,
|
||||||
|
hwaddr start, hwaddr size)
|
||||||
|
{
|
||||||
|
KVMState *s = kvm_state;
|
||||||
|
|
||||||
|
if (s->coalesced_pio) {
|
||||||
|
struct kvm_coalesced_mmio_zone zone;
|
||||||
|
|
||||||
|
zone.addr = start;
|
||||||
|
zone.size = size;
|
||||||
|
zone.pio = 1;
|
||||||
|
|
||||||
|
(void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MemoryListener kvm_coalesced_pio_listener = {
|
||||||
|
.coalesced_io_add = kvm_coalesce_pio_add,
|
||||||
|
.coalesced_io_del = kvm_coalesce_pio_del,
|
||||||
|
};
|
||||||
|
|
||||||
int kvm_check_extension(KVMState *s, unsigned int extension)
|
int kvm_check_extension(KVMState *s, unsigned int extension)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -1616,6 +1656,8 @@ static int kvm_init(MachineState *ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
|
s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
|
||||||
|
s->coalesced_pio = s->coalesced_mmio &&
|
||||||
|
kvm_check_extension(s, KVM_CAP_COALESCED_PIO);
|
||||||
|
|
||||||
#ifdef KVM_CAP_VCPU_EVENTS
|
#ifdef KVM_CAP_VCPU_EVENTS
|
||||||
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
|
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
|
||||||
|
@ -1686,13 +1728,15 @@ static int kvm_init(MachineState *ms)
|
||||||
s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
|
s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
|
||||||
s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
|
s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
|
||||||
}
|
}
|
||||||
s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region;
|
s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region;
|
||||||
s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region;
|
s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region;
|
||||||
|
|
||||||
kvm_memory_listener_register(s, &s->memory_listener,
|
kvm_memory_listener_register(s, &s->memory_listener,
|
||||||
&address_space_memory, 0);
|
&address_space_memory, 0);
|
||||||
memory_listener_register(&kvm_io_listener,
|
memory_listener_register(&kvm_io_listener,
|
||||||
&address_space_io);
|
&address_space_io);
|
||||||
|
memory_listener_register(&kvm_coalesced_pio_listener,
|
||||||
|
&address_space_io);
|
||||||
|
|
||||||
s->many_ioeventfds = kvm_check_many_ioeventfds();
|
s->many_ioeventfds = kvm_check_many_ioeventfds();
|
||||||
|
|
||||||
|
@ -1778,7 +1822,13 @@ void kvm_flush_coalesced_mmio_buffer(void)
|
||||||
|
|
||||||
ent = &ring->coalesced_mmio[ring->first];
|
ent = &ring->coalesced_mmio[ring->first];
|
||||||
|
|
||||||
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
|
if (ent->pio == 1) {
|
||||||
|
address_space_rw(&address_space_io, ent->phys_addr,
|
||||||
|
MEMTXATTRS_UNSPECIFIED, ent->data,
|
||||||
|
ent->len, true);
|
||||||
|
} else {
|
||||||
|
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
|
||||||
|
}
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
|
ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
|
||||||
}
|
}
|
||||||
|
|
4
cpus.c
4
cpus.c
|
@ -509,8 +509,8 @@ static void icount_warp_rt(void)
|
||||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||||
&timers_state.vm_clock_lock);
|
&timers_state.vm_clock_lock);
|
||||||
if (runstate_is_running()) {
|
if (runstate_is_running()) {
|
||||||
int64_t clock = REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT,
|
int64_t clock = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT,
|
||||||
cpu_get_clock_locked());
|
cpu_get_clock_locked());
|
||||||
int64_t warp_delta;
|
int64_t warp_delta;
|
||||||
|
|
||||||
warp_delta = clock - timers_state.vm_clock_warp_start;
|
warp_delta = clock - timers_state.vm_clock_warp_start;
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
CONFIG_HYPERV=$(CONFIG_KVM)
|
||||||
|
CONFIG_HYPERV_TESTDEV=y
|
|
@ -3,6 +3,7 @@
|
||||||
include pci.mak
|
include pci.mak
|
||||||
include sound.mak
|
include sound.mak
|
||||||
include usb.mak
|
include usb.mak
|
||||||
|
include hyperv.mak
|
||||||
CONFIG_QXL=$(CONFIG_SPICE)
|
CONFIG_QXL=$(CONFIG_SPICE)
|
||||||
CONFIG_VGA_ISA=y
|
CONFIG_VGA_ISA=y
|
||||||
CONFIG_VGA_CIRRUS=y
|
CONFIG_VGA_CIRRUS=y
|
||||||
|
@ -58,7 +59,6 @@ CONFIG_XIO3130=y
|
||||||
CONFIG_IOH3420=y
|
CONFIG_IOH3420=y
|
||||||
CONFIG_I82801B11=y
|
CONFIG_I82801B11=y
|
||||||
CONFIG_SMBIOS=y
|
CONFIG_SMBIOS=y
|
||||||
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
|
|
||||||
CONFIG_PXB=y
|
CONFIG_PXB=y
|
||||||
CONFIG_ACPI_VMGENID=y
|
CONFIG_ACPI_VMGENID=y
|
||||||
CONFIG_FW_CFG_DMA=y
|
CONFIG_FW_CFG_DMA=y
|
||||||
|
|
|
@ -9,6 +9,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += cpu/
|
||||||
devices-dirs-$(CONFIG_SOFTMMU) += display/
|
devices-dirs-$(CONFIG_SOFTMMU) += display/
|
||||||
devices-dirs-$(CONFIG_SOFTMMU) += dma/
|
devices-dirs-$(CONFIG_SOFTMMU) += dma/
|
||||||
devices-dirs-$(CONFIG_SOFTMMU) += gpio/
|
devices-dirs-$(CONFIG_SOFTMMU) += gpio/
|
||||||
|
devices-dirs-$(CONFIG_HYPERV) += hyperv/
|
||||||
devices-dirs-$(CONFIG_SOFTMMU) += i2c/
|
devices-dirs-$(CONFIG_SOFTMMU) += i2c/
|
||||||
devices-dirs-$(CONFIG_SOFTMMU) += ide/
|
devices-dirs-$(CONFIG_SOFTMMU) += ide/
|
||||||
devices-dirs-$(CONFIG_SOFTMMU) += input/
|
devices-dirs-$(CONFIG_SOFTMMU) += input/
|
||||||
|
|
|
@ -585,10 +585,13 @@ static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size)
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ES1370_REG_ADC_FRAMECNT:
|
||||||
|
d += 2;
|
||||||
|
goto framecnt;
|
||||||
case ES1370_REG_DAC1_FRAMECNT:
|
case ES1370_REG_DAC1_FRAMECNT:
|
||||||
case ES1370_REG_DAC2_FRAMECNT:
|
case ES1370_REG_DAC2_FRAMECNT:
|
||||||
case ES1370_REG_ADC_FRAMECNT:
|
|
||||||
d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3;
|
d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3;
|
||||||
|
framecnt:
|
||||||
val = d->frame_cnt;
|
val = d->frame_cnt;
|
||||||
#ifdef DEBUG_ES1370
|
#ifdef DEBUG_ES1370
|
||||||
{
|
{
|
||||||
|
@ -602,10 +605,13 @@ static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size)
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ES1370_REG_ADC_FRAMEADR:
|
||||||
|
d += 2;
|
||||||
|
goto frameadr;
|
||||||
case ES1370_REG_DAC1_FRAMEADR:
|
case ES1370_REG_DAC1_FRAMEADR:
|
||||||
case ES1370_REG_DAC2_FRAMEADR:
|
case ES1370_REG_DAC2_FRAMEADR:
|
||||||
case ES1370_REG_ADC_FRAMEADR:
|
|
||||||
d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3;
|
d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3;
|
||||||
|
frameadr:
|
||||||
val = d->frame_addr;
|
val = d->frame_addr;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -35,16 +35,6 @@ void hotplug_handler_plug(HotplugHandler *plug_handler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void hotplug_handler_post_plug(HotplugHandler *plug_handler,
|
|
||||||
DeviceState *plugged_dev)
|
|
||||||
{
|
|
||||||
HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler);
|
|
||||||
|
|
||||||
if (hdc->post_plug) {
|
|
||||||
hdc->post_plug(plug_handler, plugged_dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void hotplug_handler_unplug_request(HotplugHandler *plug_handler,
|
void hotplug_handler_unplug_request(HotplugHandler *plug_handler,
|
||||||
DeviceState *plugged_dev,
|
DeviceState *plugged_dev,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
|
|
|
@ -832,14 +832,6 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
|
||||||
|
|
||||||
DEVICE_LISTENER_CALL(realize, Forward, dev);
|
DEVICE_LISTENER_CALL(realize, Forward, dev);
|
||||||
|
|
||||||
if (hotplug_ctrl) {
|
|
||||||
hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (local_err != NULL) {
|
|
||||||
goto post_realize_fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* always free/re-initialize here since the value cannot be cleaned up
|
* always free/re-initialize here since the value cannot be cleaned up
|
||||||
* in device_unrealize due to its usage later on in the unplug path
|
* in device_unrealize due to its usage later on in the unplug path
|
||||||
|
@ -869,8 +861,12 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
|
||||||
dev->pending_deleted_event = false;
|
dev->pending_deleted_event = false;
|
||||||
|
|
||||||
if (hotplug_ctrl) {
|
if (hotplug_ctrl) {
|
||||||
hotplug_handler_post_plug(hotplug_ctrl, dev);
|
hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
|
||||||
}
|
if (local_err != NULL) {
|
||||||
|
goto child_realize_fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else if (!value && dev->realized) {
|
} else if (!value && dev->realized) {
|
||||||
Error **local_errp = NULL;
|
Error **local_errp = NULL;
|
||||||
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
|
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
obj-y += hyperv.o
|
||||||
|
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
|
|
@ -0,0 +1,654 @@
|
||||||
|
/*
|
||||||
|
* Hyper-V guest/hypervisor interaction
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015-2018 Virtuozzo International GmbH.
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/main-loop.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
#include "sysemu/kvm.h"
|
||||||
|
#include "qemu/bitops.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/queue.h"
|
||||||
|
#include "qemu/rcu.h"
|
||||||
|
#include "qemu/rcu_queue.h"
|
||||||
|
#include "hw/hyperv/hyperv.h"
|
||||||
|
|
||||||
|
typedef struct SynICState {
|
||||||
|
DeviceState parent_obj;
|
||||||
|
|
||||||
|
CPUState *cs;
|
||||||
|
|
||||||
|
bool enabled;
|
||||||
|
hwaddr msg_page_addr;
|
||||||
|
hwaddr event_page_addr;
|
||||||
|
MemoryRegion msg_page_mr;
|
||||||
|
MemoryRegion event_page_mr;
|
||||||
|
struct hyperv_message_page *msg_page;
|
||||||
|
struct hyperv_event_flags_page *event_page;
|
||||||
|
} SynICState;
|
||||||
|
|
||||||
|
#define TYPE_SYNIC "hyperv-synic"
|
||||||
|
#define SYNIC(obj) OBJECT_CHECK(SynICState, (obj), TYPE_SYNIC)
|
||||||
|
|
||||||
|
static SynICState *get_synic(CPUState *cs)
|
||||||
|
{
|
||||||
|
return SYNIC(object_resolve_path_component(OBJECT(cs), "synic"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void synic_update(SynICState *synic, bool enable,
|
||||||
|
hwaddr msg_page_addr, hwaddr event_page_addr)
|
||||||
|
{
|
||||||
|
|
||||||
|
synic->enabled = enable;
|
||||||
|
if (synic->msg_page_addr != msg_page_addr) {
|
||||||
|
if (synic->msg_page_addr) {
|
||||||
|
memory_region_del_subregion(get_system_memory(),
|
||||||
|
&synic->msg_page_mr);
|
||||||
|
}
|
||||||
|
if (msg_page_addr) {
|
||||||
|
memory_region_add_subregion(get_system_memory(), msg_page_addr,
|
||||||
|
&synic->msg_page_mr);
|
||||||
|
}
|
||||||
|
synic->msg_page_addr = msg_page_addr;
|
||||||
|
}
|
||||||
|
if (synic->event_page_addr != event_page_addr) {
|
||||||
|
if (synic->event_page_addr) {
|
||||||
|
memory_region_del_subregion(get_system_memory(),
|
||||||
|
&synic->event_page_mr);
|
||||||
|
}
|
||||||
|
if (event_page_addr) {
|
||||||
|
memory_region_add_subregion(get_system_memory(), event_page_addr,
|
||||||
|
&synic->event_page_mr);
|
||||||
|
}
|
||||||
|
synic->event_page_addr = event_page_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hyperv_synic_update(CPUState *cs, bool enable,
|
||||||
|
hwaddr msg_page_addr, hwaddr event_page_addr)
|
||||||
|
{
|
||||||
|
SynICState *synic = get_synic(cs);
|
||||||
|
|
||||||
|
if (!synic) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
synic_update(synic, enable, msg_page_addr, event_page_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void synic_realize(DeviceState *dev, Error **errp)
|
||||||
|
{
|
||||||
|
Object *obj = OBJECT(dev);
|
||||||
|
SynICState *synic = SYNIC(dev);
|
||||||
|
char *msgp_name, *eventp_name;
|
||||||
|
uint32_t vp_index;
|
||||||
|
|
||||||
|
/* memory region names have to be globally unique */
|
||||||
|
vp_index = hyperv_vp_index(synic->cs);
|
||||||
|
msgp_name = g_strdup_printf("synic-%u-msg-page", vp_index);
|
||||||
|
eventp_name = g_strdup_printf("synic-%u-event-page", vp_index);
|
||||||
|
|
||||||
|
memory_region_init_ram(&synic->msg_page_mr, obj, msgp_name,
|
||||||
|
sizeof(*synic->msg_page), &error_abort);
|
||||||
|
memory_region_init_ram(&synic->event_page_mr, obj, eventp_name,
|
||||||
|
sizeof(*synic->event_page), &error_abort);
|
||||||
|
synic->msg_page = memory_region_get_ram_ptr(&synic->msg_page_mr);
|
||||||
|
synic->event_page = memory_region_get_ram_ptr(&synic->event_page_mr);
|
||||||
|
|
||||||
|
g_free(msgp_name);
|
||||||
|
g_free(eventp_name);
|
||||||
|
}
|
||||||
|
static void synic_reset(DeviceState *dev)
|
||||||
|
{
|
||||||
|
SynICState *synic = SYNIC(dev);
|
||||||
|
memset(synic->msg_page, 0, sizeof(*synic->msg_page));
|
||||||
|
memset(synic->event_page, 0, sizeof(*synic->event_page));
|
||||||
|
synic_update(synic, false, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void synic_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
dc->realize = synic_realize;
|
||||||
|
dc->reset = synic_reset;
|
||||||
|
dc->user_creatable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hyperv_synic_add(CPUState *cs)
|
||||||
|
{
|
||||||
|
Object *obj;
|
||||||
|
SynICState *synic;
|
||||||
|
|
||||||
|
obj = object_new(TYPE_SYNIC);
|
||||||
|
synic = SYNIC(obj);
|
||||||
|
synic->cs = cs;
|
||||||
|
object_property_add_child(OBJECT(cs), "synic", obj, &error_abort);
|
||||||
|
object_unref(obj);
|
||||||
|
object_property_set_bool(obj, true, "realized", &error_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hyperv_synic_reset(CPUState *cs)
|
||||||
|
{
|
||||||
|
device_reset(DEVICE(get_synic(cs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo synic_type_info = {
|
||||||
|
.name = TYPE_SYNIC,
|
||||||
|
.parent = TYPE_DEVICE,
|
||||||
|
.instance_size = sizeof(SynICState),
|
||||||
|
.class_init = synic_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void synic_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&synic_type_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(synic_register_types)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KVM has its own message producers (SynIC timers). To guarantee
|
||||||
|
* serialization with both KVM vcpu and the guest cpu, the messages are first
|
||||||
|
* staged in an intermediate area and then posted to the SynIC message page in
|
||||||
|
* the vcpu thread.
|
||||||
|
*/
|
||||||
|
typedef struct HvSintStagedMessage {
|
||||||
|
/* message content staged by hyperv_post_msg */
|
||||||
|
struct hyperv_message msg;
|
||||||
|
/* callback + data (r/o) to complete the processing in a BH */
|
||||||
|
HvSintMsgCb cb;
|
||||||
|
void *cb_data;
|
||||||
|
/* message posting status filled by cpu_post_msg */
|
||||||
|
int status;
|
||||||
|
/* passing the buck: */
|
||||||
|
enum {
|
||||||
|
/* initial state */
|
||||||
|
HV_STAGED_MSG_FREE,
|
||||||
|
/*
|
||||||
|
* hyperv_post_msg (e.g. in main loop) grabs the staged area (FREE ->
|
||||||
|
* BUSY), copies msg, and schedules cpu_post_msg on the assigned cpu
|
||||||
|
*/
|
||||||
|
HV_STAGED_MSG_BUSY,
|
||||||
|
/*
|
||||||
|
* cpu_post_msg (vcpu thread) tries to copy staged msg to msg slot,
|
||||||
|
* notify the guest, records the status, marks the posting done (BUSY
|
||||||
|
* -> POSTED), and schedules sint_msg_bh BH
|
||||||
|
*/
|
||||||
|
HV_STAGED_MSG_POSTED,
|
||||||
|
/*
|
||||||
|
* sint_msg_bh (BH) verifies that the posting is done, runs the
|
||||||
|
* callback, and starts over (POSTED -> FREE)
|
||||||
|
*/
|
||||||
|
} state;
|
||||||
|
} HvSintStagedMessage;
|
||||||
|
|
||||||
|
struct HvSintRoute {
|
||||||
|
uint32_t sint;
|
||||||
|
SynICState *synic;
|
||||||
|
int gsi;
|
||||||
|
EventNotifier sint_set_notifier;
|
||||||
|
EventNotifier sint_ack_notifier;
|
||||||
|
|
||||||
|
HvSintStagedMessage *staged_msg;
|
||||||
|
|
||||||
|
unsigned refcount;
|
||||||
|
};
|
||||||
|
|
||||||
|
static CPUState *hyperv_find_vcpu(uint32_t vp_index)
|
||||||
|
{
|
||||||
|
CPUState *cs = qemu_get_cpu(vp_index);
|
||||||
|
assert(hyperv_vp_index(cs) == vp_index);
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BH to complete the processing of a staged message.
|
||||||
|
*/
|
||||||
|
static void sint_msg_bh(void *opaque)
|
||||||
|
{
|
||||||
|
HvSintRoute *sint_route = opaque;
|
||||||
|
HvSintStagedMessage *staged_msg = sint_route->staged_msg;
|
||||||
|
|
||||||
|
if (atomic_read(&staged_msg->state) != HV_STAGED_MSG_POSTED) {
|
||||||
|
/* status nor ready yet (spurious ack from guest?), ignore */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
staged_msg->cb(staged_msg->cb_data, staged_msg->status);
|
||||||
|
staged_msg->status = 0;
|
||||||
|
|
||||||
|
/* staged message processing finished, ready to start over */
|
||||||
|
atomic_set(&staged_msg->state, HV_STAGED_MSG_FREE);
|
||||||
|
/* drop the reference taken in hyperv_post_msg */
|
||||||
|
hyperv_sint_route_unref(sint_route);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Worker to transfer the message from the staging area into the SynIC message
|
||||||
|
* page in vcpu context.
|
||||||
|
*/
|
||||||
|
static void cpu_post_msg(CPUState *cs, run_on_cpu_data data)
|
||||||
|
{
|
||||||
|
HvSintRoute *sint_route = data.host_ptr;
|
||||||
|
HvSintStagedMessage *staged_msg = sint_route->staged_msg;
|
||||||
|
SynICState *synic = sint_route->synic;
|
||||||
|
struct hyperv_message *dst_msg;
|
||||||
|
bool wait_for_sint_ack = false;
|
||||||
|
|
||||||
|
assert(staged_msg->state == HV_STAGED_MSG_BUSY);
|
||||||
|
|
||||||
|
if (!synic->enabled || !synic->msg_page_addr) {
|
||||||
|
staged_msg->status = -ENXIO;
|
||||||
|
goto posted;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst_msg = &synic->msg_page->slot[sint_route->sint];
|
||||||
|
|
||||||
|
if (dst_msg->header.message_type != HV_MESSAGE_NONE) {
|
||||||
|
dst_msg->header.message_flags |= HV_MESSAGE_FLAG_PENDING;
|
||||||
|
staged_msg->status = -EAGAIN;
|
||||||
|
wait_for_sint_ack = true;
|
||||||
|
} else {
|
||||||
|
memcpy(dst_msg, &staged_msg->msg, sizeof(*dst_msg));
|
||||||
|
staged_msg->status = hyperv_sint_route_set_sint(sint_route);
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_region_set_dirty(&synic->msg_page_mr, 0, sizeof(*synic->msg_page));
|
||||||
|
|
||||||
|
posted:
|
||||||
|
atomic_set(&staged_msg->state, HV_STAGED_MSG_POSTED);
|
||||||
|
/*
|
||||||
|
* Notify the msg originator of the progress made; if the slot was busy we
|
||||||
|
* set msg_pending flag in it so it will be the guest who will do EOM and
|
||||||
|
* trigger the notification from KVM via sint_ack_notifier
|
||||||
|
*/
|
||||||
|
if (!wait_for_sint_ack) {
|
||||||
|
aio_bh_schedule_oneshot(qemu_get_aio_context(), sint_msg_bh,
|
||||||
|
sint_route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Post a Hyper-V message to the staging area, for delivery to guest in the
|
||||||
|
* vcpu thread.
|
||||||
|
*/
|
||||||
|
int hyperv_post_msg(HvSintRoute *sint_route, struct hyperv_message *src_msg)
|
||||||
|
{
|
||||||
|
HvSintStagedMessage *staged_msg = sint_route->staged_msg;
|
||||||
|
|
||||||
|
assert(staged_msg);
|
||||||
|
|
||||||
|
/* grab the staging area */
|
||||||
|
if (atomic_cmpxchg(&staged_msg->state, HV_STAGED_MSG_FREE,
|
||||||
|
HV_STAGED_MSG_BUSY) != HV_STAGED_MSG_FREE) {
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&staged_msg->msg, src_msg, sizeof(*src_msg));
|
||||||
|
|
||||||
|
/* hold a reference on sint_route until the callback is finished */
|
||||||
|
hyperv_sint_route_ref(sint_route);
|
||||||
|
|
||||||
|
/* schedule message posting attempt in vcpu thread */
|
||||||
|
async_run_on_cpu(sint_route->synic->cs, cpu_post_msg,
|
||||||
|
RUN_ON_CPU_HOST_PTR(sint_route));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sint_ack_handler(EventNotifier *notifier)
|
||||||
|
{
|
||||||
|
HvSintRoute *sint_route = container_of(notifier, HvSintRoute,
|
||||||
|
sint_ack_notifier);
|
||||||
|
event_notifier_test_and_clear(notifier);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the guest consumed the previous message so complete the current one with
|
||||||
|
* -EAGAIN and let the msg originator retry
|
||||||
|
*/
|
||||||
|
aio_bh_schedule_oneshot(qemu_get_aio_context(), sint_msg_bh, sint_route);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set given event flag for a given sint on a given vcpu, and signal the sint.
|
||||||
|
*/
|
||||||
|
int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
SynICState *synic = sint_route->synic;
|
||||||
|
unsigned long *flags, set_mask;
|
||||||
|
unsigned set_idx;
|
||||||
|
|
||||||
|
if (eventno > HV_EVENT_FLAGS_COUNT) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (!synic->enabled || !synic->event_page_addr) {
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_idx = BIT_WORD(eventno);
|
||||||
|
set_mask = BIT_MASK(eventno);
|
||||||
|
flags = synic->event_page->slot[sint_route->sint].flags;
|
||||||
|
|
||||||
|
if ((atomic_fetch_or(&flags[set_idx], set_mask) & set_mask) != set_mask) {
|
||||||
|
memory_region_set_dirty(&synic->event_page_mr, 0,
|
||||||
|
sizeof(*synic->event_page));
|
||||||
|
ret = hyperv_sint_route_set_sint(sint_route);
|
||||||
|
} else {
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint,
|
||||||
|
HvSintMsgCb cb, void *cb_data)
|
||||||
|
{
|
||||||
|
HvSintRoute *sint_route;
|
||||||
|
EventNotifier *ack_notifier;
|
||||||
|
int r, gsi;
|
||||||
|
CPUState *cs;
|
||||||
|
SynICState *synic;
|
||||||
|
|
||||||
|
cs = hyperv_find_vcpu(vp_index);
|
||||||
|
if (!cs) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
synic = get_synic(cs);
|
||||||
|
if (!synic) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sint_route = g_new0(HvSintRoute, 1);
|
||||||
|
r = event_notifier_init(&sint_route->sint_set_notifier, false);
|
||||||
|
if (r) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ack_notifier = cb ? &sint_route->sint_ack_notifier : NULL;
|
||||||
|
if (ack_notifier) {
|
||||||
|
sint_route->staged_msg = g_new0(HvSintStagedMessage, 1);
|
||||||
|
sint_route->staged_msg->cb = cb;
|
||||||
|
sint_route->staged_msg->cb_data = cb_data;
|
||||||
|
|
||||||
|
r = event_notifier_init(ack_notifier, false);
|
||||||
|
if (r) {
|
||||||
|
goto err_sint_set_notifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_notifier_set_handler(ack_notifier, sint_ack_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint);
|
||||||
|
if (gsi < 0) {
|
||||||
|
goto err_gsi;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state,
|
||||||
|
&sint_route->sint_set_notifier,
|
||||||
|
ack_notifier, gsi);
|
||||||
|
if (r) {
|
||||||
|
goto err_irqfd;
|
||||||
|
}
|
||||||
|
sint_route->gsi = gsi;
|
||||||
|
sint_route->synic = synic;
|
||||||
|
sint_route->sint = sint;
|
||||||
|
sint_route->refcount = 1;
|
||||||
|
|
||||||
|
return sint_route;
|
||||||
|
|
||||||
|
err_irqfd:
|
||||||
|
kvm_irqchip_release_virq(kvm_state, gsi);
|
||||||
|
err_gsi:
|
||||||
|
if (ack_notifier) {
|
||||||
|
event_notifier_set_handler(ack_notifier, NULL);
|
||||||
|
event_notifier_cleanup(ack_notifier);
|
||||||
|
g_free(sint_route->staged_msg);
|
||||||
|
}
|
||||||
|
err_sint_set_notifier:
|
||||||
|
event_notifier_cleanup(&sint_route->sint_set_notifier);
|
||||||
|
err:
|
||||||
|
g_free(sint_route);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hyperv_sint_route_ref(HvSintRoute *sint_route)
|
||||||
|
{
|
||||||
|
sint_route->refcount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hyperv_sint_route_unref(HvSintRoute *sint_route)
|
||||||
|
{
|
||||||
|
if (!sint_route) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(sint_route->refcount > 0);
|
||||||
|
|
||||||
|
if (--sint_route->refcount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state,
|
||||||
|
&sint_route->sint_set_notifier,
|
||||||
|
sint_route->gsi);
|
||||||
|
kvm_irqchip_release_virq(kvm_state, sint_route->gsi);
|
||||||
|
if (sint_route->staged_msg) {
|
||||||
|
event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL);
|
||||||
|
event_notifier_cleanup(&sint_route->sint_ack_notifier);
|
||||||
|
g_free(sint_route->staged_msg);
|
||||||
|
}
|
||||||
|
event_notifier_cleanup(&sint_route->sint_set_notifier);
|
||||||
|
g_free(sint_route);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hyperv_sint_route_set_sint(HvSintRoute *sint_route)
|
||||||
|
{
|
||||||
|
return event_notifier_set(&sint_route->sint_set_notifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct MsgHandler {
|
||||||
|
struct rcu_head rcu;
|
||||||
|
QLIST_ENTRY(MsgHandler) link;
|
||||||
|
uint32_t conn_id;
|
||||||
|
HvMsgHandler handler;
|
||||||
|
void *data;
|
||||||
|
} MsgHandler;
|
||||||
|
|
||||||
|
typedef struct EventFlagHandler {
|
||||||
|
struct rcu_head rcu;
|
||||||
|
QLIST_ENTRY(EventFlagHandler) link;
|
||||||
|
uint32_t conn_id;
|
||||||
|
EventNotifier *notifier;
|
||||||
|
} EventFlagHandler;
|
||||||
|
|
||||||
|
static QLIST_HEAD(, MsgHandler) msg_handlers;
|
||||||
|
static QLIST_HEAD(, EventFlagHandler) event_flag_handlers;
|
||||||
|
static QemuMutex handlers_mutex;
|
||||||
|
|
||||||
|
static void __attribute__((constructor)) hv_init(void)
|
||||||
|
{
|
||||||
|
QLIST_INIT(&msg_handlers);
|
||||||
|
QLIST_INIT(&event_flag_handlers);
|
||||||
|
qemu_mutex_init(&handlers_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hyperv_set_msg_handler(uint32_t conn_id, HvMsgHandler handler, void *data)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
MsgHandler *mh;
|
||||||
|
|
||||||
|
qemu_mutex_lock(&handlers_mutex);
|
||||||
|
QLIST_FOREACH(mh, &msg_handlers, link) {
|
||||||
|
if (mh->conn_id == conn_id) {
|
||||||
|
if (handler) {
|
||||||
|
ret = -EEXIST;
|
||||||
|
} else {
|
||||||
|
QLIST_REMOVE_RCU(mh, link);
|
||||||
|
g_free_rcu(mh, rcu);
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
mh = g_new(MsgHandler, 1);
|
||||||
|
mh->conn_id = conn_id;
|
||||||
|
mh->handler = handler;
|
||||||
|
mh->data = data;
|
||||||
|
QLIST_INSERT_HEAD_RCU(&msg_handlers, mh, link);
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
ret = -ENOENT;
|
||||||
|
}
|
||||||
|
unlock:
|
||||||
|
qemu_mutex_unlock(&handlers_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t hyperv_hcall_post_message(uint64_t param, bool fast)
|
||||||
|
{
|
||||||
|
uint16_t ret;
|
||||||
|
hwaddr len;
|
||||||
|
struct hyperv_post_message_input *msg;
|
||||||
|
MsgHandler *mh;
|
||||||
|
|
||||||
|
if (fast) {
|
||||||
|
return HV_STATUS_INVALID_HYPERCALL_CODE;
|
||||||
|
}
|
||||||
|
if (param & (__alignof__(*msg) - 1)) {
|
||||||
|
return HV_STATUS_INVALID_ALIGNMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = sizeof(*msg);
|
||||||
|
msg = cpu_physical_memory_map(param, &len, 0);
|
||||||
|
if (len < sizeof(*msg)) {
|
||||||
|
ret = HV_STATUS_INSUFFICIENT_MEMORY;
|
||||||
|
goto unmap;
|
||||||
|
}
|
||||||
|
if (msg->payload_size > sizeof(msg->payload)) {
|
||||||
|
ret = HV_STATUS_INVALID_HYPERCALL_INPUT;
|
||||||
|
goto unmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = HV_STATUS_INVALID_CONNECTION_ID;
|
||||||
|
rcu_read_lock();
|
||||||
|
QLIST_FOREACH_RCU(mh, &msg_handlers, link) {
|
||||||
|
if (mh->conn_id == (msg->connection_id & HV_CONNECTION_ID_MASK)) {
|
||||||
|
ret = mh->handler(msg, mh->data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
unmap:
|
||||||
|
cpu_physical_memory_unmap(msg, len, 0, 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
EventFlagHandler *handler;
|
||||||
|
|
||||||
|
qemu_mutex_lock(&handlers_mutex);
|
||||||
|
QLIST_FOREACH(handler, &event_flag_handlers, link) {
|
||||||
|
if (handler->conn_id == conn_id) {
|
||||||
|
if (notifier) {
|
||||||
|
ret = -EEXIST;
|
||||||
|
} else {
|
||||||
|
QLIST_REMOVE_RCU(handler, link);
|
||||||
|
g_free_rcu(handler, rcu);
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notifier) {
|
||||||
|
handler = g_new(EventFlagHandler, 1);
|
||||||
|
handler->conn_id = conn_id;
|
||||||
|
handler->notifier = notifier;
|
||||||
|
QLIST_INSERT_HEAD_RCU(&event_flag_handlers, handler, link);
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
ret = -ENOENT;
|
||||||
|
}
|
||||||
|
unlock:
|
||||||
|
qemu_mutex_unlock(&handlers_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool process_event_flags_userspace;
|
||||||
|
|
||||||
|
int hyperv_set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier)
|
||||||
|
{
|
||||||
|
if (!process_event_flags_userspace &&
|
||||||
|
!kvm_check_extension(kvm_state, KVM_CAP_HYPERV_EVENTFD)) {
|
||||||
|
process_event_flags_userspace = true;
|
||||||
|
|
||||||
|
warn_report("Hyper-V event signaling is not supported by this kernel; "
|
||||||
|
"using slower userspace hypercall processing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process_event_flags_userspace) {
|
||||||
|
struct kvm_hyperv_eventfd hvevfd = {
|
||||||
|
.conn_id = conn_id,
|
||||||
|
.fd = notifier ? event_notifier_get_fd(notifier) : -1,
|
||||||
|
.flags = notifier ? 0 : KVM_HYPERV_EVENTFD_DEASSIGN,
|
||||||
|
};
|
||||||
|
|
||||||
|
return kvm_vm_ioctl(kvm_state, KVM_HYPERV_EVENTFD, &hvevfd);
|
||||||
|
}
|
||||||
|
return set_event_flag_handler(conn_id, notifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast)
|
||||||
|
{
|
||||||
|
uint16_t ret;
|
||||||
|
EventFlagHandler *handler;
|
||||||
|
|
||||||
|
if (unlikely(!fast)) {
|
||||||
|
hwaddr addr = param;
|
||||||
|
|
||||||
|
if (addr & (__alignof__(addr) - 1)) {
|
||||||
|
return HV_STATUS_INVALID_ALIGNMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
param = ldq_phys(&address_space_memory, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Per spec, bits 32-47 contain the extra "flag number". However, we
|
||||||
|
* have no use for it, and in all known usecases it is zero, so just
|
||||||
|
* report lookup failure if it isn't.
|
||||||
|
*/
|
||||||
|
if (param & 0xffff00000000ULL) {
|
||||||
|
return HV_STATUS_INVALID_PORT_ID;
|
||||||
|
}
|
||||||
|
/* remaining bits are reserved-zero */
|
||||||
|
if (param & ~HV_CONNECTION_ID_MASK) {
|
||||||
|
return HV_STATUS_INVALID_HYPERCALL_INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = HV_STATUS_INVALID_CONNECTION_ID;
|
||||||
|
rcu_read_lock();
|
||||||
|
QLIST_FOREACH_RCU(handler, &event_flag_handlers, link) {
|
||||||
|
if (handler->conn_id == param) {
|
||||||
|
event_notifier_set(handler->notifier);
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,327 @@
|
||||||
|
/*
|
||||||
|
* QEMU KVM Hyper-V test device to support Hyper-V kvm-unit-tests
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com>
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Andrey Smetanin <asmetanin@virtuozzo.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/main-loop.h"
|
||||||
|
#include "qemu/queue.h"
|
||||||
|
#include "hw/qdev.h"
|
||||||
|
#include "hw/isa/isa.h"
|
||||||
|
#include "hw/hyperv/hyperv.h"
|
||||||
|
|
||||||
|
typedef struct TestSintRoute {
|
||||||
|
QLIST_ENTRY(TestSintRoute) le;
|
||||||
|
uint8_t vp_index;
|
||||||
|
uint8_t sint;
|
||||||
|
HvSintRoute *sint_route;
|
||||||
|
} TestSintRoute;
|
||||||
|
|
||||||
|
typedef struct TestMsgConn {
|
||||||
|
QLIST_ENTRY(TestMsgConn) le;
|
||||||
|
uint8_t conn_id;
|
||||||
|
HvSintRoute *sint_route;
|
||||||
|
struct hyperv_message msg;
|
||||||
|
} TestMsgConn;
|
||||||
|
|
||||||
|
typedef struct TestEvtConn {
|
||||||
|
QLIST_ENTRY(TestEvtConn) le;
|
||||||
|
uint8_t conn_id;
|
||||||
|
HvSintRoute *sint_route;
|
||||||
|
EventNotifier notifier;
|
||||||
|
} TestEvtConn;
|
||||||
|
|
||||||
|
struct HypervTestDev {
|
||||||
|
ISADevice parent_obj;
|
||||||
|
MemoryRegion sint_control;
|
||||||
|
QLIST_HEAD(, TestSintRoute) sint_routes;
|
||||||
|
QLIST_HEAD(, TestMsgConn) msg_conns;
|
||||||
|
QLIST_HEAD(, TestEvtConn) evt_conns;
|
||||||
|
};
|
||||||
|
typedef struct HypervTestDev HypervTestDev;
|
||||||
|
|
||||||
|
#define TYPE_HYPERV_TEST_DEV "hyperv-testdev"
|
||||||
|
#define HYPERV_TEST_DEV(obj) \
|
||||||
|
OBJECT_CHECK(HypervTestDev, (obj), TYPE_HYPERV_TEST_DEV)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
|
||||||
|
HV_TEST_DEV_SINT_ROUTE_DESTROY,
|
||||||
|
HV_TEST_DEV_SINT_ROUTE_SET_SINT,
|
||||||
|
HV_TEST_DEV_MSG_CONN_CREATE,
|
||||||
|
HV_TEST_DEV_MSG_CONN_DESTROY,
|
||||||
|
HV_TEST_DEV_EVT_CONN_CREATE,
|
||||||
|
HV_TEST_DEV_EVT_CONN_DESTROY,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void sint_route_create(HypervTestDev *dev,
|
||||||
|
uint8_t vp_index, uint8_t sint)
|
||||||
|
{
|
||||||
|
TestSintRoute *sint_route;
|
||||||
|
|
||||||
|
sint_route = g_new0(TestSintRoute, 1);
|
||||||
|
assert(sint_route);
|
||||||
|
|
||||||
|
sint_route->vp_index = vp_index;
|
||||||
|
sint_route->sint = sint;
|
||||||
|
|
||||||
|
sint_route->sint_route = hyperv_sint_route_new(vp_index, sint, NULL, NULL);
|
||||||
|
assert(sint_route->sint_route);
|
||||||
|
|
||||||
|
QLIST_INSERT_HEAD(&dev->sint_routes, sint_route, le);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TestSintRoute *sint_route_find(HypervTestDev *dev,
|
||||||
|
uint8_t vp_index, uint8_t sint)
|
||||||
|
{
|
||||||
|
TestSintRoute *sint_route;
|
||||||
|
|
||||||
|
QLIST_FOREACH(sint_route, &dev->sint_routes, le) {
|
||||||
|
if (sint_route->vp_index == vp_index && sint_route->sint == sint) {
|
||||||
|
return sint_route;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sint_route_destroy(HypervTestDev *dev,
|
||||||
|
uint8_t vp_index, uint8_t sint)
|
||||||
|
{
|
||||||
|
TestSintRoute *sint_route;
|
||||||
|
|
||||||
|
sint_route = sint_route_find(dev, vp_index, sint);
|
||||||
|
QLIST_REMOVE(sint_route, le);
|
||||||
|
hyperv_sint_route_unref(sint_route->sint_route);
|
||||||
|
g_free(sint_route);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sint_route_set_sint(HypervTestDev *dev,
|
||||||
|
uint8_t vp_index, uint8_t sint)
|
||||||
|
{
|
||||||
|
TestSintRoute *sint_route;
|
||||||
|
|
||||||
|
sint_route = sint_route_find(dev, vp_index, sint);
|
||||||
|
|
||||||
|
hyperv_sint_route_set_sint(sint_route->sint_route);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void msg_retry(void *opaque)
|
||||||
|
{
|
||||||
|
TestMsgConn *conn = opaque;
|
||||||
|
assert(!hyperv_post_msg(conn->sint_route, &conn->msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void msg_cb(void *data, int status)
|
||||||
|
{
|
||||||
|
TestMsgConn *conn = data;
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(status == -EAGAIN);
|
||||||
|
|
||||||
|
aio_bh_schedule_oneshot(qemu_get_aio_context(), msg_retry, conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t msg_handler(const struct hyperv_post_message_input *msg,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
TestMsgConn *conn = data;
|
||||||
|
|
||||||
|
/* post the same message we've got */
|
||||||
|
conn->msg.header.message_type = msg->message_type;
|
||||||
|
assert(msg->payload_size < sizeof(conn->msg.payload));
|
||||||
|
conn->msg.header.payload_size = msg->payload_size;
|
||||||
|
memcpy(&conn->msg.payload, msg->payload, msg->payload_size);
|
||||||
|
|
||||||
|
ret = hyperv_post_msg(conn->sint_route, &conn->msg);
|
||||||
|
|
||||||
|
switch (ret) {
|
||||||
|
case 0:
|
||||||
|
return HV_STATUS_SUCCESS;
|
||||||
|
case -EAGAIN:
|
||||||
|
return HV_STATUS_INSUFFICIENT_BUFFERS;
|
||||||
|
default:
|
||||||
|
return HV_STATUS_INVALID_HYPERCALL_INPUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void msg_conn_create(HypervTestDev *dev, uint8_t vp_index,
|
||||||
|
uint8_t sint, uint8_t conn_id)
|
||||||
|
{
|
||||||
|
TestMsgConn *conn;
|
||||||
|
|
||||||
|
conn = g_new0(TestMsgConn, 1);
|
||||||
|
assert(conn);
|
||||||
|
|
||||||
|
conn->conn_id = conn_id;
|
||||||
|
|
||||||
|
conn->sint_route = hyperv_sint_route_new(vp_index, sint, msg_cb, conn);
|
||||||
|
assert(conn->sint_route);
|
||||||
|
|
||||||
|
assert(!hyperv_set_msg_handler(conn->conn_id, msg_handler, conn));
|
||||||
|
|
||||||
|
QLIST_INSERT_HEAD(&dev->msg_conns, conn, le);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void msg_conn_destroy(HypervTestDev *dev, uint8_t conn_id)
|
||||||
|
{
|
||||||
|
TestMsgConn *conn;
|
||||||
|
|
||||||
|
QLIST_FOREACH(conn, &dev->msg_conns, le) {
|
||||||
|
if (conn->conn_id == conn_id) {
|
||||||
|
QLIST_REMOVE(conn, le);
|
||||||
|
hyperv_set_msg_handler(conn->conn_id, NULL, NULL);
|
||||||
|
hyperv_sint_route_unref(conn->sint_route);
|
||||||
|
g_free(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void evt_conn_handler(EventNotifier *notifier)
|
||||||
|
{
|
||||||
|
TestEvtConn *conn = container_of(notifier, TestEvtConn, notifier);
|
||||||
|
|
||||||
|
event_notifier_test_and_clear(notifier);
|
||||||
|
|
||||||
|
/* signal the same event flag we've got */
|
||||||
|
assert(!hyperv_set_event_flag(conn->sint_route, conn->conn_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void evt_conn_create(HypervTestDev *dev, uint8_t vp_index,
|
||||||
|
uint8_t sint, uint8_t conn_id)
|
||||||
|
{
|
||||||
|
TestEvtConn *conn;
|
||||||
|
|
||||||
|
conn = g_new0(TestEvtConn, 1);
|
||||||
|
assert(conn);
|
||||||
|
|
||||||
|
conn->conn_id = conn_id;
|
||||||
|
|
||||||
|
conn->sint_route = hyperv_sint_route_new(vp_index, sint, NULL, NULL);
|
||||||
|
assert(conn->sint_route);
|
||||||
|
|
||||||
|
assert(!event_notifier_init(&conn->notifier, false));
|
||||||
|
|
||||||
|
event_notifier_set_handler(&conn->notifier, evt_conn_handler);
|
||||||
|
|
||||||
|
assert(!hyperv_set_event_flag_handler(conn_id, &conn->notifier));
|
||||||
|
|
||||||
|
QLIST_INSERT_HEAD(&dev->evt_conns, conn, le);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void evt_conn_destroy(HypervTestDev *dev, uint8_t conn_id)
|
||||||
|
{
|
||||||
|
TestEvtConn *conn;
|
||||||
|
|
||||||
|
QLIST_FOREACH(conn, &dev->evt_conns, le) {
|
||||||
|
if (conn->conn_id == conn_id) {
|
||||||
|
QLIST_REMOVE(conn, le);
|
||||||
|
hyperv_set_event_flag_handler(conn->conn_id, NULL);
|
||||||
|
event_notifier_set_handler(&conn->notifier, NULL);
|
||||||
|
event_notifier_cleanup(&conn->notifier);
|
||||||
|
hyperv_sint_route_unref(conn->sint_route);
|
||||||
|
g_free(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t hv_test_dev_read(void *opaque, hwaddr addr, unsigned size)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hv_test_dev_write(void *opaque, hwaddr addr, uint64_t data,
|
||||||
|
uint32_t len)
|
||||||
|
{
|
||||||
|
HypervTestDev *dev = HYPERV_TEST_DEV(opaque);
|
||||||
|
uint8_t sint = data & 0xFF;
|
||||||
|
uint8_t vp_index = (data >> 8ULL) & 0xFF;
|
||||||
|
uint8_t ctl = (data >> 16ULL) & 0xFF;
|
||||||
|
uint8_t conn_id = (data >> 24ULL) & 0xFF;
|
||||||
|
|
||||||
|
switch (ctl) {
|
||||||
|
case HV_TEST_DEV_SINT_ROUTE_CREATE:
|
||||||
|
sint_route_create(dev, vp_index, sint);
|
||||||
|
break;
|
||||||
|
case HV_TEST_DEV_SINT_ROUTE_DESTROY:
|
||||||
|
sint_route_destroy(dev, vp_index, sint);
|
||||||
|
break;
|
||||||
|
case HV_TEST_DEV_SINT_ROUTE_SET_SINT:
|
||||||
|
sint_route_set_sint(dev, vp_index, sint);
|
||||||
|
break;
|
||||||
|
case HV_TEST_DEV_MSG_CONN_CREATE:
|
||||||
|
msg_conn_create(dev, vp_index, sint, conn_id);
|
||||||
|
break;
|
||||||
|
case HV_TEST_DEV_MSG_CONN_DESTROY:
|
||||||
|
msg_conn_destroy(dev, conn_id);
|
||||||
|
break;
|
||||||
|
case HV_TEST_DEV_EVT_CONN_CREATE:
|
||||||
|
evt_conn_create(dev, vp_index, sint, conn_id);
|
||||||
|
break;
|
||||||
|
case HV_TEST_DEV_EVT_CONN_DESTROY:
|
||||||
|
evt_conn_destroy(dev, conn_id);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps synic_test_sint_ops = {
|
||||||
|
.read = hv_test_dev_read,
|
||||||
|
.write = hv_test_dev_write,
|
||||||
|
.valid.min_access_size = 4,
|
||||||
|
.valid.max_access_size = 4,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void hv_test_dev_realizefn(DeviceState *d, Error **errp)
|
||||||
|
{
|
||||||
|
ISADevice *isa = ISA_DEVICE(d);
|
||||||
|
HypervTestDev *dev = HYPERV_TEST_DEV(d);
|
||||||
|
MemoryRegion *io = isa_address_space_io(isa);
|
||||||
|
|
||||||
|
QLIST_INIT(&dev->sint_routes);
|
||||||
|
QLIST_INIT(&dev->msg_conns);
|
||||||
|
QLIST_INIT(&dev->evt_conns);
|
||||||
|
memory_region_init_io(&dev->sint_control, OBJECT(dev),
|
||||||
|
&synic_test_sint_ops, dev,
|
||||||
|
"hyperv-testdev-ctl", 4);
|
||||||
|
memory_region_add_subregion(io, 0x3000, &dev->sint_control);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hv_test_dev_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||||
|
dc->realize = hv_test_dev_realizefn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo hv_test_dev_info = {
|
||||||
|
.name = TYPE_HYPERV_TEST_DEV,
|
||||||
|
.parent = TYPE_ISA_DEVICE,
|
||||||
|
.instance_size = sizeof(HypervTestDev),
|
||||||
|
.class_init = hv_test_dev_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void hv_test_dev_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&hv_test_dev_info);
|
||||||
|
}
|
||||||
|
type_init(hv_test_dev_register_types);
|
|
@ -71,7 +71,6 @@ obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o
|
||||||
obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o
|
obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o
|
||||||
|
|
||||||
obj-$(CONFIG_PVPANIC) += pvpanic.o
|
obj-$(CONFIG_PVPANIC) += pvpanic.o
|
||||||
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
|
|
||||||
obj-$(CONFIG_AUX) += auxbus.o
|
obj-$(CONFIG_AUX) += auxbus.o
|
||||||
obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o
|
obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o
|
||||||
obj-$(CONFIG_MSF2) += msf2-sysreg.o
|
obj-$(CONFIG_MSF2) += msf2-sysreg.o
|
||||||
|
|
|
@ -342,7 +342,7 @@ static void *edu_fact_thread(void *opaque)
|
||||||
|
|
||||||
static void pci_edu_realize(PCIDevice *pdev, Error **errp)
|
static void pci_edu_realize(PCIDevice *pdev, Error **errp)
|
||||||
{
|
{
|
||||||
EduState *edu = DO_UPCAST(EduState, pdev, pdev);
|
EduState *edu = EDU(pdev);
|
||||||
uint8_t *pci_conf = pdev->config;
|
uint8_t *pci_conf = pdev->config;
|
||||||
|
|
||||||
pci_config_set_interrupt_pin(pci_conf, 1);
|
pci_config_set_interrupt_pin(pci_conf, 1);
|
||||||
|
@ -365,7 +365,7 @@ static void pci_edu_realize(PCIDevice *pdev, Error **errp)
|
||||||
|
|
||||||
static void pci_edu_uninit(PCIDevice *pdev)
|
static void pci_edu_uninit(PCIDevice *pdev)
|
||||||
{
|
{
|
||||||
EduState *edu = DO_UPCAST(EduState, pdev, pdev);
|
EduState *edu = EDU(pdev);
|
||||||
|
|
||||||
qemu_mutex_lock(&edu->thr_mutex);
|
qemu_mutex_lock(&edu->thr_mutex);
|
||||||
edu->stopping = true;
|
edu->stopping = true;
|
||||||
|
|
|
@ -1,174 +0,0 @@
|
||||||
/*
|
|
||||||
* QEMU KVM Hyper-V test device to support Hyper-V kvm-unit-tests
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com>
|
|
||||||
*
|
|
||||||
* Authors:
|
|
||||||
* Andrey Smetanin <asmetanin@virtuozzo.com>
|
|
||||||
*
|
|
||||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
||||||
* See the COPYING file in the top-level directory.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
|
||||||
#include <linux/kvm.h>
|
|
||||||
#include "hw/hw.h"
|
|
||||||
#include "hw/qdev.h"
|
|
||||||
#include "hw/isa/isa.h"
|
|
||||||
#include "sysemu/kvm.h"
|
|
||||||
#include "target/i386/hyperv.h"
|
|
||||||
#include "kvm_i386.h"
|
|
||||||
|
|
||||||
#define HV_TEST_DEV_MAX_SINT_ROUTES 64
|
|
||||||
|
|
||||||
struct HypervTestDev {
|
|
||||||
ISADevice parent_obj;
|
|
||||||
MemoryRegion sint_control;
|
|
||||||
HvSintRoute *sint_route[HV_TEST_DEV_MAX_SINT_ROUTES];
|
|
||||||
};
|
|
||||||
typedef struct HypervTestDev HypervTestDev;
|
|
||||||
|
|
||||||
#define TYPE_HYPERV_TEST_DEV "hyperv-testdev"
|
|
||||||
#define HYPERV_TEST_DEV(obj) \
|
|
||||||
OBJECT_CHECK(HypervTestDev, (obj), TYPE_HYPERV_TEST_DEV)
|
|
||||||
|
|
||||||
enum {
|
|
||||||
HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
|
|
||||||
HV_TEST_DEV_SINT_ROUTE_DESTROY,
|
|
||||||
HV_TEST_DEV_SINT_ROUTE_SET_SINT
|
|
||||||
};
|
|
||||||
|
|
||||||
static int alloc_sint_route_index(HypervTestDev *dev)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
|
|
||||||
if (dev->sint_route[i] == NULL) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_sint_route_index(HypervTestDev *dev, int i)
|
|
||||||
{
|
|
||||||
assert(i >= 0 && i < ARRAY_SIZE(dev->sint_route));
|
|
||||||
dev->sint_route[i] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int find_sint_route_index(HypervTestDev *dev, uint32_t vp_index,
|
|
||||||
uint32_t sint)
|
|
||||||
{
|
|
||||||
HvSintRoute *sint_route;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
|
|
||||||
sint_route = dev->sint_route[i];
|
|
||||||
if (sint_route && sint_route->vp_index == vp_index &&
|
|
||||||
sint_route->sint == sint) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl,
|
|
||||||
uint32_t vp_index, uint32_t sint)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
HvSintRoute *sint_route;
|
|
||||||
|
|
||||||
switch (ctl) {
|
|
||||||
case HV_TEST_DEV_SINT_ROUTE_CREATE:
|
|
||||||
i = alloc_sint_route_index(dev);
|
|
||||||
assert(i >= 0);
|
|
||||||
sint_route = kvm_hv_sint_route_create(vp_index, sint, NULL);
|
|
||||||
assert(sint_route);
|
|
||||||
dev->sint_route[i] = sint_route;
|
|
||||||
break;
|
|
||||||
case HV_TEST_DEV_SINT_ROUTE_DESTROY:
|
|
||||||
i = find_sint_route_index(dev, vp_index, sint);
|
|
||||||
assert(i >= 0);
|
|
||||||
sint_route = dev->sint_route[i];
|
|
||||||
kvm_hv_sint_route_destroy(sint_route);
|
|
||||||
free_sint_route_index(dev, i);
|
|
||||||
break;
|
|
||||||
case HV_TEST_DEV_SINT_ROUTE_SET_SINT:
|
|
||||||
i = find_sint_route_index(dev, vp_index, sint);
|
|
||||||
assert(i >= 0);
|
|
||||||
sint_route = dev->sint_route[i];
|
|
||||||
kvm_hv_sint_route_set_sint(sint_route);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t hv_test_dev_read(void *opaque, hwaddr addr, unsigned size)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hv_test_dev_write(void *opaque, hwaddr addr, uint64_t data,
|
|
||||||
uint32_t len)
|
|
||||||
{
|
|
||||||
HypervTestDev *dev = HYPERV_TEST_DEV(opaque);
|
|
||||||
uint8_t ctl;
|
|
||||||
|
|
||||||
ctl = (data >> 16ULL) & 0xFF;
|
|
||||||
switch (ctl) {
|
|
||||||
case HV_TEST_DEV_SINT_ROUTE_CREATE:
|
|
||||||
case HV_TEST_DEV_SINT_ROUTE_DESTROY:
|
|
||||||
case HV_TEST_DEV_SINT_ROUTE_SET_SINT: {
|
|
||||||
uint8_t sint = data & 0xFF;
|
|
||||||
uint8_t vp_index = (data >> 8ULL) & 0xFF;
|
|
||||||
hv_synic_test_dev_control(dev, ctl, vp_index, sint);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const MemoryRegionOps synic_test_sint_ops = {
|
|
||||||
.read = hv_test_dev_read,
|
|
||||||
.write = hv_test_dev_write,
|
|
||||||
.valid.min_access_size = 4,
|
|
||||||
.valid.max_access_size = 4,
|
|
||||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void hv_test_dev_realizefn(DeviceState *d, Error **errp)
|
|
||||||
{
|
|
||||||
ISADevice *isa = ISA_DEVICE(d);
|
|
||||||
HypervTestDev *dev = HYPERV_TEST_DEV(d);
|
|
||||||
MemoryRegion *io = isa_address_space_io(isa);
|
|
||||||
|
|
||||||
memset(dev->sint_route, 0, sizeof(dev->sint_route));
|
|
||||||
memory_region_init_io(&dev->sint_control, OBJECT(dev),
|
|
||||||
&synic_test_sint_ops, dev,
|
|
||||||
"hyperv-testdev-ctl", 4);
|
|
||||||
memory_region_add_subregion(io, 0x3000, &dev->sint_control);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hv_test_dev_class_init(ObjectClass *klass, void *data)
|
|
||||||
{
|
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
||||||
|
|
||||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
|
||||||
dc->realize = hv_test_dev_realizefn;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const TypeInfo hv_test_dev_info = {
|
|
||||||
.name = TYPE_HYPERV_TEST_DEV,
|
|
||||||
.parent = TYPE_ISA_DEVICE,
|
|
||||||
.instance_size = sizeof(HypervTestDev),
|
|
||||||
.class_init = hv_test_dev_class_init,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void hv_test_dev_register_types(void)
|
|
||||||
{
|
|
||||||
type_register_static(&hv_test_dev_info);
|
|
||||||
}
|
|
||||||
type_init(hv_test_dev_register_types);
|
|
|
@ -327,6 +327,10 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp)
|
||||||
|
|
||||||
sysbus_add_io(sbd, 0xcfc, &s->data_mem);
|
sysbus_add_io(sbd, 0xcfc, &s->data_mem);
|
||||||
sysbus_init_ioports(sbd, 0xcfc, 4);
|
sysbus_init_ioports(sbd, 0xcfc, 4);
|
||||||
|
|
||||||
|
/* register i440fx 0xcf8 port as coalesced pio */
|
||||||
|
memory_region_set_flush_coalesced(&s->data_mem);
|
||||||
|
memory_region_add_coalescing(&s->conf_mem, 0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i440fx_realize(PCIDevice *dev, Error **errp)
|
static void i440fx_realize(PCIDevice *dev, Error **errp)
|
||||||
|
|
|
@ -51,6 +51,10 @@ static void q35_host_realize(DeviceState *dev, Error **errp)
|
||||||
sysbus_add_io(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, &pci->data_mem);
|
sysbus_add_io(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, &pci->data_mem);
|
||||||
sysbus_init_ioports(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, 4);
|
sysbus_init_ioports(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, 4);
|
||||||
|
|
||||||
|
/* register q35 0xcf8 port as coalesced pio */
|
||||||
|
memory_region_set_flush_coalesced(&pci->data_mem);
|
||||||
|
memory_region_add_coalescing(&pci->conf_mem, 0, 4);
|
||||||
|
|
||||||
pci->bus = pci_root_bus_new(DEVICE(s), "pcie.0",
|
pci->bus = pci_root_bus_new(DEVICE(s), "pcie.0",
|
||||||
s->mch.pci_address_space,
|
s->mch.pci_address_space,
|
||||||
s->mch.address_space_io,
|
s->mch.address_space_io,
|
||||||
|
|
|
@ -441,9 +441,18 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed)
|
||||||
}
|
}
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case 0:
|
case 0:
|
||||||
/* The command has run, no need to fake sense. */
|
/* A passthrough command has run and has produced sense data; check
|
||||||
|
* whether the error has to be handled by the guest or should rather
|
||||||
|
* pause the host.
|
||||||
|
*/
|
||||||
assert(r->status && *r->status);
|
assert(r->status && *r->status);
|
||||||
scsi_req_complete(&r->req, *r->status);
|
error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense));
|
||||||
|
if (error == ECANCELED || error == EAGAIN || error == ENOTCONN ||
|
||||||
|
error == 0) {
|
||||||
|
/* These errors are handled by guest. */
|
||||||
|
scsi_req_complete(&r->req, *r->status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ENOMEDIUM:
|
case ENOMEDIUM:
|
||||||
scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
|
scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
|
||||||
|
@ -462,23 +471,17 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!error) {
|
|
||||||
assert(r->status && *r->status);
|
|
||||||
error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense));
|
|
||||||
|
|
||||||
if (error == ECANCELED || error == EAGAIN || error == ENOTCONN ||
|
|
||||||
error == 0) {
|
|
||||||
/* These errors are handled by guest. */
|
|
||||||
scsi_req_complete(&r->req, *r->status);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blk_error_action(s->qdev.conf.blk, action, is_read, error);
|
blk_error_action(s->qdev.conf.blk, action, is_read, error);
|
||||||
|
if (action == BLOCK_ERROR_ACTION_IGNORE) {
|
||||||
|
scsi_req_complete(&r->req, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (action == BLOCK_ERROR_ACTION_STOP) {
|
if (action == BLOCK_ERROR_ACTION_STOP) {
|
||||||
scsi_req_retry(&r->req);
|
scsi_req_retry(&r->req);
|
||||||
}
|
}
|
||||||
return action != BLOCK_ERROR_ACTION_IGNORE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)
|
static void scsi_write_complete_noio(SCSIDiskReq *r, int ret)
|
||||||
|
|
|
@ -797,16 +797,8 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
||||||
virtio_scsi_acquire(s);
|
virtio_scsi_acquire(s);
|
||||||
blk_set_aio_context(sd->conf.blk, s->ctx);
|
blk_set_aio_context(sd->conf.blk, s->ctx);
|
||||||
virtio_scsi_release(s);
|
virtio_scsi_release(s);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Announce the new device after it has been plugged */
|
}
|
||||||
static void virtio_scsi_post_hotplug(HotplugHandler *hotplug_dev,
|
|
||||||
DeviceState *dev)
|
|
||||||
{
|
|
||||||
VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev);
|
|
||||||
VirtIOSCSI *s = VIRTIO_SCSI(vdev);
|
|
||||||
SCSIDevice *sd = SCSI_DEVICE(dev);
|
|
||||||
|
|
||||||
if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
|
if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) {
|
||||||
virtio_scsi_acquire(s);
|
virtio_scsi_acquire(s);
|
||||||
|
@ -976,7 +968,6 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data)
|
||||||
vdc->start_ioeventfd = virtio_scsi_dataplane_start;
|
vdc->start_ioeventfd = virtio_scsi_dataplane_start;
|
||||||
vdc->stop_ioeventfd = virtio_scsi_dataplane_stop;
|
vdc->stop_ioeventfd = virtio_scsi_dataplane_stop;
|
||||||
hc->plug = virtio_scsi_hotplug;
|
hc->plug = virtio_scsi_hotplug;
|
||||||
hc->post_plug = virtio_scsi_post_hotplug;
|
|
||||||
hc->unplug = virtio_scsi_hotunplug;
|
hc->unplug = virtio_scsi_hotunplug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "qapi/qapi-commands-misc.h"
|
#include "qapi/qapi-commands-misc.h"
|
||||||
#include "qapi/qapi-events-misc.h"
|
#include "qapi/qapi-events-misc.h"
|
||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
|
||||||
#ifdef TARGET_I386
|
#ifdef TARGET_I386
|
||||||
#include "hw/i386/apic.h"
|
#include "hw/i386/apic.h"
|
||||||
|
@ -70,6 +71,7 @@ typedef struct RTCState {
|
||||||
ISADevice parent_obj;
|
ISADevice parent_obj;
|
||||||
|
|
||||||
MemoryRegion io;
|
MemoryRegion io;
|
||||||
|
MemoryRegion coalesced_io;
|
||||||
uint8_t cmos_data[128];
|
uint8_t cmos_data[128];
|
||||||
uint8_t cmos_index;
|
uint8_t cmos_index;
|
||||||
int32_t base_year;
|
int32_t base_year;
|
||||||
|
@ -990,6 +992,13 @@ static void rtc_realizefn(DeviceState *dev, Error **errp)
|
||||||
memory_region_init_io(&s->io, OBJECT(s), &cmos_ops, s, "rtc", 2);
|
memory_region_init_io(&s->io, OBJECT(s), &cmos_ops, s, "rtc", 2);
|
||||||
isa_register_ioport(isadev, &s->io, base);
|
isa_register_ioport(isadev, &s->io, base);
|
||||||
|
|
||||||
|
/* register rtc 0x70 port for coalesced_pio */
|
||||||
|
memory_region_set_flush_coalesced(&s->io);
|
||||||
|
memory_region_init_io(&s->coalesced_io, OBJECT(s), &cmos_ops,
|
||||||
|
s, "rtc-index", 1);
|
||||||
|
memory_region_add_subregion(&s->io, 0, &s->coalesced_io);
|
||||||
|
memory_region_add_coalescing(&s->coalesced_io, 0, 1);
|
||||||
|
|
||||||
qdev_set_legacy_instance_id(dev, base, 3);
|
qdev_set_legacy_instance_id(dev, base, 3);
|
||||||
qemu_register_reset(rtc_reset, s);
|
qemu_register_reset(rtc_reset, s);
|
||||||
|
|
||||||
|
|
|
@ -387,6 +387,32 @@ struct LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp);
|
||||||
/* Return the LinuxAioState bound to this AioContext */
|
/* Return the LinuxAioState bound to this AioContext */
|
||||||
struct LinuxAioState *aio_get_linux_aio(AioContext *ctx);
|
struct LinuxAioState *aio_get_linux_aio(AioContext *ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aio_timer_new_with_attrs:
|
||||||
|
* @ctx: the aio context
|
||||||
|
* @type: the clock type
|
||||||
|
* @scale: the scale
|
||||||
|
* @attributes: 0, or one to multiple OR'ed QEMU_TIMER_ATTR_<id> values
|
||||||
|
* to assign
|
||||||
|
* @cb: the callback to call on timer expiry
|
||||||
|
* @opaque: the opaque pointer to pass to the callback
|
||||||
|
*
|
||||||
|
* Allocate a new timer (with attributes) attached to the context @ctx.
|
||||||
|
* The function is responsible for memory allocation.
|
||||||
|
*
|
||||||
|
* The preferred interface is aio_timer_init or aio_timer_init_with_attrs.
|
||||||
|
* Use that unless you really need dynamic memory allocation.
|
||||||
|
*
|
||||||
|
* Returns: a pointer to the new timer
|
||||||
|
*/
|
||||||
|
static inline QEMUTimer *aio_timer_new_with_attrs(AioContext *ctx,
|
||||||
|
QEMUClockType type,
|
||||||
|
int scale, int attributes,
|
||||||
|
QEMUTimerCB *cb, void *opaque)
|
||||||
|
{
|
||||||
|
return timer_new_full(&ctx->tlg, type, scale, attributes, cb, opaque);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* aio_timer_new:
|
* aio_timer_new:
|
||||||
* @ctx: the aio context
|
* @ctx: the aio context
|
||||||
|
@ -396,10 +422,7 @@ struct LinuxAioState *aio_get_linux_aio(AioContext *ctx);
|
||||||
* @opaque: the opaque pointer to pass to the callback
|
* @opaque: the opaque pointer to pass to the callback
|
||||||
*
|
*
|
||||||
* Allocate a new timer attached to the context @ctx.
|
* Allocate a new timer attached to the context @ctx.
|
||||||
* The function is responsible for memory allocation.
|
* See aio_timer_new_with_attrs for details.
|
||||||
*
|
|
||||||
* The preferred interface is aio_timer_init. Use that
|
|
||||||
* unless you really need dynamic memory allocation.
|
|
||||||
*
|
*
|
||||||
* Returns: a pointer to the new timer
|
* Returns: a pointer to the new timer
|
||||||
*/
|
*/
|
||||||
|
@ -407,7 +430,29 @@ static inline QEMUTimer *aio_timer_new(AioContext *ctx, QEMUClockType type,
|
||||||
int scale,
|
int scale,
|
||||||
QEMUTimerCB *cb, void *opaque)
|
QEMUTimerCB *cb, void *opaque)
|
||||||
{
|
{
|
||||||
return timer_new_tl(ctx->tlg.tl[type], scale, cb, opaque);
|
return timer_new_full(&ctx->tlg, type, scale, 0, cb, opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aio_timer_init_with_attrs:
|
||||||
|
* @ctx: the aio context
|
||||||
|
* @ts: the timer
|
||||||
|
* @type: the clock type
|
||||||
|
* @scale: the scale
|
||||||
|
* @attributes: 0, or one to multiple OR'ed QEMU_TIMER_ATTR_<id> values
|
||||||
|
* to assign
|
||||||
|
* @cb: the callback to call on timer expiry
|
||||||
|
* @opaque: the opaque pointer to pass to the callback
|
||||||
|
*
|
||||||
|
* Initialise a new timer (with attributes) attached to the context @ctx.
|
||||||
|
* The caller is responsible for memory allocation.
|
||||||
|
*/
|
||||||
|
static inline void aio_timer_init_with_attrs(AioContext *ctx,
|
||||||
|
QEMUTimer *ts, QEMUClockType type,
|
||||||
|
int scale, int attributes,
|
||||||
|
QEMUTimerCB *cb, void *opaque)
|
||||||
|
{
|
||||||
|
timer_init_full(ts, &ctx->tlg, type, scale, attributes, cb, opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -420,14 +465,14 @@ static inline QEMUTimer *aio_timer_new(AioContext *ctx, QEMUClockType type,
|
||||||
* @opaque: the opaque pointer to pass to the callback
|
* @opaque: the opaque pointer to pass to the callback
|
||||||
*
|
*
|
||||||
* Initialise a new timer attached to the context @ctx.
|
* Initialise a new timer attached to the context @ctx.
|
||||||
* The caller is responsible for memory allocation.
|
* See aio_timer_init_with_attrs for details.
|
||||||
*/
|
*/
|
||||||
static inline void aio_timer_init(AioContext *ctx,
|
static inline void aio_timer_init(AioContext *ctx,
|
||||||
QEMUTimer *ts, QEMUClockType type,
|
QEMUTimer *ts, QEMUClockType type,
|
||||||
int scale,
|
int scale,
|
||||||
QEMUTimerCB *cb, void *opaque)
|
QEMUTimerCB *cb, void *opaque)
|
||||||
{
|
{
|
||||||
timer_init_tl(ts, ctx->tlg.tl[type], scale, cb, opaque);
|
timer_init_full(ts, &ctx->tlg, type, scale, 0, cb, opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -419,9 +419,9 @@ struct MemoryListener {
|
||||||
bool match_data, uint64_t data, EventNotifier *e);
|
bool match_data, uint64_t data, EventNotifier *e);
|
||||||
void (*eventfd_del)(MemoryListener *listener, MemoryRegionSection *section,
|
void (*eventfd_del)(MemoryListener *listener, MemoryRegionSection *section,
|
||||||
bool match_data, uint64_t data, EventNotifier *e);
|
bool match_data, uint64_t data, EventNotifier *e);
|
||||||
void (*coalesced_mmio_add)(MemoryListener *listener, MemoryRegionSection *section,
|
void (*coalesced_io_add)(MemoryListener *listener, MemoryRegionSection *section,
|
||||||
hwaddr addr, hwaddr len);
|
hwaddr addr, hwaddr len);
|
||||||
void (*coalesced_mmio_del)(MemoryListener *listener, MemoryRegionSection *section,
|
void (*coalesced_io_del)(MemoryListener *listener, MemoryRegionSection *section,
|
||||||
hwaddr addr, hwaddr len);
|
hwaddr addr, hwaddr len);
|
||||||
/* Lower = earlier (during add), later (during del) */
|
/* Lower = earlier (during add), later (during del) */
|
||||||
unsigned priority;
|
unsigned priority;
|
||||||
|
|
|
@ -47,8 +47,6 @@ typedef void (*hotplug_fn)(HotplugHandler *plug_handler,
|
||||||
* @parent: Opaque parent interface.
|
* @parent: Opaque parent interface.
|
||||||
* @pre_plug: pre plug callback called at start of device.realize(true)
|
* @pre_plug: pre plug callback called at start of device.realize(true)
|
||||||
* @plug: plug callback called at end of device.realize(true).
|
* @plug: plug callback called at end of device.realize(true).
|
||||||
* @post_plug: post plug callback called after device.realize(true) and device
|
|
||||||
* reset
|
|
||||||
* @unplug_request: unplug request callback.
|
* @unplug_request: unplug request callback.
|
||||||
* Used as a means to initiate device unplug for devices that
|
* Used as a means to initiate device unplug for devices that
|
||||||
* require asynchronous unplug handling.
|
* require asynchronous unplug handling.
|
||||||
|
@ -63,7 +61,6 @@ typedef struct HotplugHandlerClass {
|
||||||
/* <public> */
|
/* <public> */
|
||||||
hotplug_fn pre_plug;
|
hotplug_fn pre_plug;
|
||||||
hotplug_fn plug;
|
hotplug_fn plug;
|
||||||
void (*post_plug)(HotplugHandler *plug_handler, DeviceState *plugged_dev);
|
|
||||||
hotplug_fn unplug_request;
|
hotplug_fn unplug_request;
|
||||||
hotplug_fn unplug;
|
hotplug_fn unplug;
|
||||||
} HotplugHandlerClass;
|
} HotplugHandlerClass;
|
||||||
|
@ -86,14 +83,6 @@ void hotplug_handler_pre_plug(HotplugHandler *plug_handler,
|
||||||
DeviceState *plugged_dev,
|
DeviceState *plugged_dev,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
/**
|
|
||||||
* hotplug_handler_post_plug:
|
|
||||||
*
|
|
||||||
* Call #HotplugHandlerClass.post_plug callback of @plug_handler.
|
|
||||||
*/
|
|
||||||
void hotplug_handler_post_plug(HotplugHandler *plug_handler,
|
|
||||||
DeviceState *plugged_dev);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hotplug_handler_unplug_request:
|
* hotplug_handler_unplug_request:
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* Definitions for Hyper-V guest/hypervisor interaction
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017-2018 Virtuozzo International GmbH.
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HW_HYPERV_HYPERV_PROTO_H
|
||||||
|
#define HW_HYPERV_HYPERV_PROTO_H
|
||||||
|
|
||||||
|
#include "qemu/bitmap.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hypercall status code
|
||||||
|
*/
|
||||||
|
#define HV_STATUS_SUCCESS 0
|
||||||
|
#define HV_STATUS_INVALID_HYPERCALL_CODE 2
|
||||||
|
#define HV_STATUS_INVALID_HYPERCALL_INPUT 3
|
||||||
|
#define HV_STATUS_INVALID_ALIGNMENT 4
|
||||||
|
#define HV_STATUS_INVALID_PARAMETER 5
|
||||||
|
#define HV_STATUS_INSUFFICIENT_MEMORY 11
|
||||||
|
#define HV_STATUS_INVALID_PORT_ID 17
|
||||||
|
#define HV_STATUS_INVALID_CONNECTION_ID 18
|
||||||
|
#define HV_STATUS_INSUFFICIENT_BUFFERS 19
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hypercall numbers
|
||||||
|
*/
|
||||||
|
#define HV_POST_MESSAGE 0x005c
|
||||||
|
#define HV_SIGNAL_EVENT 0x005d
|
||||||
|
#define HV_HYPERCALL_FAST (1u << 16)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Message size
|
||||||
|
*/
|
||||||
|
#define HV_MESSAGE_PAYLOAD_SIZE 240
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Message types
|
||||||
|
*/
|
||||||
|
#define HV_MESSAGE_NONE 0x00000000
|
||||||
|
#define HV_MESSAGE_VMBUS 0x00000001
|
||||||
|
#define HV_MESSAGE_UNMAPPED_GPA 0x80000000
|
||||||
|
#define HV_MESSAGE_GPA_INTERCEPT 0x80000001
|
||||||
|
#define HV_MESSAGE_TIMER_EXPIRED 0x80000010
|
||||||
|
#define HV_MESSAGE_INVALID_VP_REGISTER_VALUE 0x80000020
|
||||||
|
#define HV_MESSAGE_UNRECOVERABLE_EXCEPTION 0x80000021
|
||||||
|
#define HV_MESSAGE_UNSUPPORTED_FEATURE 0x80000022
|
||||||
|
#define HV_MESSAGE_EVENTLOG_BUFFERCOMPLETE 0x80000040
|
||||||
|
#define HV_MESSAGE_X64_IOPORT_INTERCEPT 0x80010000
|
||||||
|
#define HV_MESSAGE_X64_MSR_INTERCEPT 0x80010001
|
||||||
|
#define HV_MESSAGE_X64_CPUID_INTERCEPT 0x80010002
|
||||||
|
#define HV_MESSAGE_X64_EXCEPTION_INTERCEPT 0x80010003
|
||||||
|
#define HV_MESSAGE_X64_APIC_EOI 0x80010004
|
||||||
|
#define HV_MESSAGE_X64_LEGACY_FP_ERROR 0x80010005
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Message flags
|
||||||
|
*/
|
||||||
|
#define HV_MESSAGE_FLAG_PENDING 0x1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Number of synthetic interrupts
|
||||||
|
*/
|
||||||
|
#define HV_SINT_COUNT 16
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Event flags number per SINT
|
||||||
|
*/
|
||||||
|
#define HV_EVENT_FLAGS_COUNT (256 * 8)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connection id valid bits
|
||||||
|
*/
|
||||||
|
#define HV_CONNECTION_ID_MASK 0x00ffffff
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Input structure for POST_MESSAGE hypercall
|
||||||
|
*/
|
||||||
|
struct hyperv_post_message_input {
|
||||||
|
uint32_t connection_id;
|
||||||
|
uint32_t _reserved;
|
||||||
|
uint32_t message_type;
|
||||||
|
uint32_t payload_size;
|
||||||
|
uint8_t payload[HV_MESSAGE_PAYLOAD_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Input structure for SIGNAL_EVENT hypercall
|
||||||
|
*/
|
||||||
|
struct hyperv_signal_event_input {
|
||||||
|
uint32_t connection_id;
|
||||||
|
uint16_t flag_number;
|
||||||
|
uint16_t _reserved_zero;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SynIC message structures
|
||||||
|
*/
|
||||||
|
struct hyperv_message_header {
|
||||||
|
uint32_t message_type;
|
||||||
|
uint8_t payload_size;
|
||||||
|
uint8_t message_flags; /* HV_MESSAGE_FLAG_XX */
|
||||||
|
uint8_t _reserved[2];
|
||||||
|
uint64_t sender;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hyperv_message {
|
||||||
|
struct hyperv_message_header header;
|
||||||
|
uint8_t payload[HV_MESSAGE_PAYLOAD_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hyperv_message_page {
|
||||||
|
struct hyperv_message slot[HV_SINT_COUNT];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SynIC event flags structures
|
||||||
|
*/
|
||||||
|
struct hyperv_event_flags {
|
||||||
|
DECLARE_BITMAP(flags, HV_EVENT_FLAGS_COUNT);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hyperv_event_flags_page {
|
||||||
|
struct hyperv_event_flags slot[HV_SINT_COUNT];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Hyper-V guest/hypervisor interaction
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015-2018 Virtuozzo International GmbH.
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HW_HYPERV_HYPERV_H
|
||||||
|
#define HW_HYPERV_HYPERV_H
|
||||||
|
|
||||||
|
#include "cpu-qom.h"
|
||||||
|
#include "hw/hyperv/hyperv-proto.h"
|
||||||
|
|
||||||
|
typedef struct HvSintRoute HvSintRoute;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback executed in a bottom-half when the status of posting the message
|
||||||
|
* becomes known, before unblocking the connection for further messages
|
||||||
|
*/
|
||||||
|
typedef void (*HvSintMsgCb)(void *data, int status);
|
||||||
|
|
||||||
|
HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint,
|
||||||
|
HvSintMsgCb cb, void *cb_data);
|
||||||
|
void hyperv_sint_route_ref(HvSintRoute *sint_route);
|
||||||
|
void hyperv_sint_route_unref(HvSintRoute *sint_route);
|
||||||
|
|
||||||
|
int hyperv_sint_route_set_sint(HvSintRoute *sint_route);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Submit a message to be posted in vcpu context. If the submission succeeds,
|
||||||
|
* the status of posting the message is reported via the callback associated
|
||||||
|
* with the @sint_route; until then no more messages are accepted.
|
||||||
|
*/
|
||||||
|
int hyperv_post_msg(HvSintRoute *sint_route, struct hyperv_message *msg);
|
||||||
|
/*
|
||||||
|
* Set event flag @eventno, and signal the SINT if the flag has changed.
|
||||||
|
*/
|
||||||
|
int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handler for messages arriving from the guest via HV_POST_MESSAGE hypercall.
|
||||||
|
* Executed in vcpu context.
|
||||||
|
*/
|
||||||
|
typedef uint16_t (*HvMsgHandler)(const struct hyperv_post_message_input *msg,
|
||||||
|
void *data);
|
||||||
|
/*
|
||||||
|
* Associate @handler with the message connection @conn_id, such that @handler
|
||||||
|
* is called with @data when the guest executes HV_POST_MESSAGE hypercall on
|
||||||
|
* @conn_id. If @handler is NULL clear the association.
|
||||||
|
*/
|
||||||
|
int hyperv_set_msg_handler(uint32_t conn_id, HvMsgHandler handler, void *data);
|
||||||
|
/*
|
||||||
|
* Associate @notifier with the event connection @conn_id, such that @notifier
|
||||||
|
* is signaled when the guest executes HV_SIGNAL_EVENT hypercall on @conn_id.
|
||||||
|
* If @notifier is NULL clear the association.
|
||||||
|
*/
|
||||||
|
int hyperv_set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process HV_POST_MESSAGE hypercall: parse the data in the guest memory as
|
||||||
|
* specified in @param, and call the HvMsgHandler associated with the
|
||||||
|
* connection on the message contained therein.
|
||||||
|
*/
|
||||||
|
uint16_t hyperv_hcall_post_message(uint64_t param, bool fast);
|
||||||
|
/*
|
||||||
|
* Process HV_SIGNAL_EVENT hypercall: signal the EventNotifier associated with
|
||||||
|
* the connection as specified in @param.
|
||||||
|
*/
|
||||||
|
uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast);
|
||||||
|
|
||||||
|
static inline uint32_t hyperv_vp_index(CPUState *cs)
|
||||||
|
{
|
||||||
|
return cs->cpu_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hyperv_synic_add(CPUState *cs);
|
||||||
|
void hyperv_synic_reset(CPUState *cs);
|
||||||
|
void hyperv_synic_update(CPUState *cs, bool enable,
|
||||||
|
hwaddr msg_page_addr, hwaddr event_page_addr);
|
||||||
|
|
||||||
|
#endif
|
|
@ -294,6 +294,14 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t);
|
||||||
int e820_get_num_entries(void);
|
int e820_get_num_entries(void);
|
||||||
bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
|
bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
|
||||||
|
|
||||||
|
#define PC_COMPAT_3_0 \
|
||||||
|
HW_COMPAT_3_0 \
|
||||||
|
{\
|
||||||
|
.driver = TYPE_X86_CPU,\
|
||||||
|
.property = "x-hv-synic-kvm-only",\
|
||||||
|
.value = "on",\
|
||||||
|
}
|
||||||
|
|
||||||
#define PC_COMPAT_2_12 \
|
#define PC_COMPAT_2_12 \
|
||||||
HW_COMPAT_2_12 \
|
HW_COMPAT_2_12 \
|
||||||
{\
|
{\
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define QEMU_TIMER_H
|
#define QEMU_TIMER_H
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
|
#include "qemu/bitops.h"
|
||||||
#include "qemu/notify.h"
|
#include "qemu/notify.h"
|
||||||
#include "qemu/host-utils.h"
|
#include "qemu/host-utils.h"
|
||||||
|
|
||||||
|
@ -42,14 +43,6 @@
|
||||||
* In icount mode, this clock counts nanoseconds while the virtual
|
* In icount mode, this clock counts nanoseconds while the virtual
|
||||||
* machine is running. It is used to increase @QEMU_CLOCK_VIRTUAL
|
* machine is running. It is used to increase @QEMU_CLOCK_VIRTUAL
|
||||||
* while the CPUs are sleeping and thus not executing instructions.
|
* while the CPUs are sleeping and thus not executing instructions.
|
||||||
*
|
|
||||||
* @QEMU_CLOCK_VIRTUAL_EXT: virtual clock for external subsystems
|
|
||||||
*
|
|
||||||
* The virtual clock only runs during the emulation. It stops
|
|
||||||
* when the virtual machine is stopped. The timers for this clock
|
|
||||||
* do not recorded in rr mode, therefore this clock could be used
|
|
||||||
* for the subsystems that operate outside the guest core.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -57,10 +50,27 @@ typedef enum {
|
||||||
QEMU_CLOCK_VIRTUAL = 1,
|
QEMU_CLOCK_VIRTUAL = 1,
|
||||||
QEMU_CLOCK_HOST = 2,
|
QEMU_CLOCK_HOST = 2,
|
||||||
QEMU_CLOCK_VIRTUAL_RT = 3,
|
QEMU_CLOCK_VIRTUAL_RT = 3,
|
||||||
QEMU_CLOCK_VIRTUAL_EXT = 4,
|
|
||||||
QEMU_CLOCK_MAX
|
QEMU_CLOCK_MAX
|
||||||
} QEMUClockType;
|
} QEMUClockType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QEMU Timer attributes:
|
||||||
|
*
|
||||||
|
* An individual timer may be given one or multiple attributes when initialized.
|
||||||
|
* Each attribute corresponds to one bit. Attributes modify the processing
|
||||||
|
* of timers when they fire.
|
||||||
|
*
|
||||||
|
* The following attributes are available:
|
||||||
|
*
|
||||||
|
* QEMU_TIMER_ATTR_EXTERNAL: drives external subsystem
|
||||||
|
*
|
||||||
|
* Timers with this attribute do not recorded in rr mode, therefore it could be
|
||||||
|
* used for the subsystems that operate outside the guest core. Applicable only
|
||||||
|
* with virtual clock type.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define QEMU_TIMER_ATTR_EXTERNAL BIT(0)
|
||||||
|
|
||||||
typedef struct QEMUTimerList QEMUTimerList;
|
typedef struct QEMUTimerList QEMUTimerList;
|
||||||
|
|
||||||
struct QEMUTimerListGroup {
|
struct QEMUTimerListGroup {
|
||||||
|
@ -76,6 +86,7 @@ struct QEMUTimer {
|
||||||
QEMUTimerCB *cb;
|
QEMUTimerCB *cb;
|
||||||
void *opaque;
|
void *opaque;
|
||||||
QEMUTimer *next;
|
QEMUTimer *next;
|
||||||
|
int attributes;
|
||||||
int scale;
|
int scale;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -427,22 +438,27 @@ int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* timer_init_tl:
|
* timer_init_full:
|
||||||
* @ts: the timer to be initialised
|
* @ts: the timer to be initialised
|
||||||
* @timer_list: the timer list to attach the timer to
|
* @timer_list_group: (optional) the timer list group to attach the timer to
|
||||||
|
* @type: the clock type to use
|
||||||
* @scale: the scale value for the timer
|
* @scale: the scale value for the timer
|
||||||
|
* @attributes: 0, or one or more OR'ed QEMU_TIMER_ATTR_<id> values
|
||||||
* @cb: the callback to be called when the timer expires
|
* @cb: the callback to be called when the timer expires
|
||||||
* @opaque: the opaque pointer to be passed to the callback
|
* @opaque: the opaque pointer to be passed to the callback
|
||||||
*
|
*
|
||||||
* Initialise a new timer and associate it with @timer_list.
|
* Initialise a timer with the given scale and attributes,
|
||||||
|
* and associate it with timer list for given clock @type in @timer_list_group
|
||||||
|
* (or default timer list group, if NULL).
|
||||||
* The caller is responsible for allocating the memory.
|
* The caller is responsible for allocating the memory.
|
||||||
*
|
*
|
||||||
* You need not call an explicit deinit call. Simply make
|
* You need not call an explicit deinit call. Simply make
|
||||||
* sure it is not on a list with timer_del.
|
* sure it is not on a list with timer_del.
|
||||||
*/
|
*/
|
||||||
void timer_init_tl(QEMUTimer *ts,
|
void timer_init_full(QEMUTimer *ts,
|
||||||
QEMUTimerList *timer_list, int scale,
|
QEMUTimerListGroup *timer_list_group, QEMUClockType type,
|
||||||
QEMUTimerCB *cb, void *opaque);
|
int scale, int attributes,
|
||||||
|
QEMUTimerCB *cb, void *opaque);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* timer_init:
|
* timer_init:
|
||||||
|
@ -454,14 +470,12 @@ void timer_init_tl(QEMUTimer *ts,
|
||||||
*
|
*
|
||||||
* Initialize a timer with the given scale on the default timer list
|
* Initialize a timer with the given scale on the default timer list
|
||||||
* associated with the clock.
|
* associated with the clock.
|
||||||
*
|
* See timer_init_full for details.
|
||||||
* You need not call an explicit deinit call. Simply make
|
|
||||||
* sure it is not on a list with timer_del.
|
|
||||||
*/
|
*/
|
||||||
static inline void timer_init(QEMUTimer *ts, QEMUClockType type, int scale,
|
static inline void timer_init(QEMUTimer *ts, QEMUClockType type, int scale,
|
||||||
QEMUTimerCB *cb, void *opaque)
|
QEMUTimerCB *cb, void *opaque)
|
||||||
{
|
{
|
||||||
timer_init_tl(ts, main_loop_tlg.tl[type], scale, cb, opaque);
|
timer_init_full(ts, NULL, type, scale, 0, cb, opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -473,9 +487,7 @@ static inline void timer_init(QEMUTimer *ts, QEMUClockType type, int scale,
|
||||||
*
|
*
|
||||||
* Initialize a timer with nanosecond scale on the default timer list
|
* Initialize a timer with nanosecond scale on the default timer list
|
||||||
* associated with the clock.
|
* associated with the clock.
|
||||||
*
|
* See timer_init_full for details.
|
||||||
* You need not call an explicit deinit call. Simply make
|
|
||||||
* sure it is not on a list with timer_del.
|
|
||||||
*/
|
*/
|
||||||
static inline void timer_init_ns(QEMUTimer *ts, QEMUClockType type,
|
static inline void timer_init_ns(QEMUTimer *ts, QEMUClockType type,
|
||||||
QEMUTimerCB *cb, void *opaque)
|
QEMUTimerCB *cb, void *opaque)
|
||||||
|
@ -492,9 +504,7 @@ static inline void timer_init_ns(QEMUTimer *ts, QEMUClockType type,
|
||||||
*
|
*
|
||||||
* Initialize a timer with microsecond scale on the default timer list
|
* Initialize a timer with microsecond scale on the default timer list
|
||||||
* associated with the clock.
|
* associated with the clock.
|
||||||
*
|
* See timer_init_full for details.
|
||||||
* You need not call an explicit deinit call. Simply make
|
|
||||||
* sure it is not on a list with timer_del.
|
|
||||||
*/
|
*/
|
||||||
static inline void timer_init_us(QEMUTimer *ts, QEMUClockType type,
|
static inline void timer_init_us(QEMUTimer *ts, QEMUClockType type,
|
||||||
QEMUTimerCB *cb, void *opaque)
|
QEMUTimerCB *cb, void *opaque)
|
||||||
|
@ -511,9 +521,7 @@ static inline void timer_init_us(QEMUTimer *ts, QEMUClockType type,
|
||||||
*
|
*
|
||||||
* Initialize a timer with millisecond scale on the default timer list
|
* Initialize a timer with millisecond scale on the default timer list
|
||||||
* associated with the clock.
|
* associated with the clock.
|
||||||
*
|
* See timer_init_full for details.
|
||||||
* You need not call an explicit deinit call. Simply make
|
|
||||||
* sure it is not on a list with timer_del.
|
|
||||||
*/
|
*/
|
||||||
static inline void timer_init_ms(QEMUTimer *ts, QEMUClockType type,
|
static inline void timer_init_ms(QEMUTimer *ts, QEMUClockType type,
|
||||||
QEMUTimerCB *cb, void *opaque)
|
QEMUTimerCB *cb, void *opaque)
|
||||||
|
@ -522,27 +530,37 @@ static inline void timer_init_ms(QEMUTimer *ts, QEMUClockType type,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* timer_new_tl:
|
* timer_new_full:
|
||||||
* @timer_list: the timer list to attach the timer to
|
* @timer_list_group: (optional) the timer list group to attach the timer to
|
||||||
|
* @type: the clock type to use
|
||||||
* @scale: the scale value for the timer
|
* @scale: the scale value for the timer
|
||||||
|
* @attributes: 0, or one or more OR'ed QEMU_TIMER_ATTR_<id> values
|
||||||
* @cb: the callback to be called when the timer expires
|
* @cb: the callback to be called when the timer expires
|
||||||
* @opaque: the opaque pointer to be passed to the callback
|
* @opaque: the opaque pointer to be passed to the callback
|
||||||
*
|
*
|
||||||
* Create a new timer and associate it with @timer_list.
|
* Create a new timer with the given scale and attributes,
|
||||||
|
* and associate it with timer list for given clock @type in @timer_list_group
|
||||||
|
* (or default timer list group, if NULL).
|
||||||
* The memory is allocated by the function.
|
* The memory is allocated by the function.
|
||||||
*
|
*
|
||||||
* This is not the preferred interface unless you know you
|
* This is not the preferred interface unless you know you
|
||||||
* are going to call timer_free. Use timer_init instead.
|
* are going to call timer_free. Use timer_init or timer_init_full instead.
|
||||||
|
*
|
||||||
|
* The default timer list has one special feature: in icount mode,
|
||||||
|
* %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is
|
||||||
|
* not true of other timer lists, which are typically associated
|
||||||
|
* with an AioContext---each of them runs its timer callbacks in its own
|
||||||
|
* AioContext thread.
|
||||||
*
|
*
|
||||||
* Returns: a pointer to the timer
|
* Returns: a pointer to the timer
|
||||||
*/
|
*/
|
||||||
static inline QEMUTimer *timer_new_tl(QEMUTimerList *timer_list,
|
static inline QEMUTimer *timer_new_full(QEMUTimerListGroup *timer_list_group,
|
||||||
int scale,
|
QEMUClockType type,
|
||||||
QEMUTimerCB *cb,
|
int scale, int attributes,
|
||||||
void *opaque)
|
QEMUTimerCB *cb, void *opaque)
|
||||||
{
|
{
|
||||||
QEMUTimer *ts = g_malloc0(sizeof(QEMUTimer));
|
QEMUTimer *ts = g_malloc0(sizeof(QEMUTimer));
|
||||||
timer_init_tl(ts, timer_list, scale, cb, opaque);
|
timer_init_full(ts, timer_list_group, type, scale, attributes, cb, opaque);
|
||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,21 +571,16 @@ static inline QEMUTimer *timer_new_tl(QEMUTimerList *timer_list,
|
||||||
* @cb: the callback to be called when the timer expires
|
* @cb: the callback to be called when the timer expires
|
||||||
* @opaque: the opaque pointer to be passed to the callback
|
* @opaque: the opaque pointer to be passed to the callback
|
||||||
*
|
*
|
||||||
* Create a new timer and associate it with the default
|
* Create a new timer with the given scale,
|
||||||
* timer list for the clock type @type.
|
* and associate it with the default timer list for the clock type @type.
|
||||||
*
|
* See timer_new_full for details.
|
||||||
* The default timer list has one special feature: in icount mode,
|
|
||||||
* %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is
|
|
||||||
* not true of other timer lists, which are typically associated
|
|
||||||
* with an AioContext---each of them runs its timer callbacks in its own
|
|
||||||
* AioContext thread.
|
|
||||||
*
|
*
|
||||||
* Returns: a pointer to the timer
|
* Returns: a pointer to the timer
|
||||||
*/
|
*/
|
||||||
static inline QEMUTimer *timer_new(QEMUClockType type, int scale,
|
static inline QEMUTimer *timer_new(QEMUClockType type, int scale,
|
||||||
QEMUTimerCB *cb, void *opaque)
|
QEMUTimerCB *cb, void *opaque)
|
||||||
{
|
{
|
||||||
return timer_new_tl(main_loop_tlg.tl[type], scale, cb, opaque);
|
return timer_new_full(NULL, type, scale, 0, cb, opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -578,12 +591,7 @@ static inline QEMUTimer *timer_new(QEMUClockType type, int scale,
|
||||||
*
|
*
|
||||||
* Create a new timer with nanosecond scale on the default timer list
|
* Create a new timer with nanosecond scale on the default timer list
|
||||||
* associated with the clock.
|
* associated with the clock.
|
||||||
*
|
* See timer_new_full for details.
|
||||||
* The default timer list has one special feature: in icount mode,
|
|
||||||
* %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is
|
|
||||||
* not true of other timer lists, which are typically associated
|
|
||||||
* with an AioContext---each of them runs its timer callbacks in its own
|
|
||||||
* AioContext thread.
|
|
||||||
*
|
*
|
||||||
* Returns: a pointer to the newly created timer
|
* Returns: a pointer to the newly created timer
|
||||||
*/
|
*/
|
||||||
|
@ -599,14 +607,9 @@ static inline QEMUTimer *timer_new_ns(QEMUClockType type, QEMUTimerCB *cb,
|
||||||
* @cb: the callback to call when the timer expires
|
* @cb: the callback to call when the timer expires
|
||||||
* @opaque: the opaque pointer to pass to the callback
|
* @opaque: the opaque pointer to pass to the callback
|
||||||
*
|
*
|
||||||
* The default timer list has one special feature: in icount mode,
|
|
||||||
* %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is
|
|
||||||
* not true of other timer lists, which are typically associated
|
|
||||||
* with an AioContext---each of them runs its timer callbacks in its own
|
|
||||||
* AioContext thread.
|
|
||||||
*
|
|
||||||
* Create a new timer with microsecond scale on the default timer list
|
* Create a new timer with microsecond scale on the default timer list
|
||||||
* associated with the clock.
|
* associated with the clock.
|
||||||
|
* See timer_new_full for details.
|
||||||
*
|
*
|
||||||
* Returns: a pointer to the newly created timer
|
* Returns: a pointer to the newly created timer
|
||||||
*/
|
*/
|
||||||
|
@ -622,14 +625,9 @@ static inline QEMUTimer *timer_new_us(QEMUClockType type, QEMUTimerCB *cb,
|
||||||
* @cb: the callback to call when the timer expires
|
* @cb: the callback to call when the timer expires
|
||||||
* @opaque: the opaque pointer to pass to the callback
|
* @opaque: the opaque pointer to pass to the callback
|
||||||
*
|
*
|
||||||
* The default timer list has one special feature: in icount mode,
|
|
||||||
* %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is
|
|
||||||
* not true of other timer lists, which are typically associated
|
|
||||||
* with an AioContext---each of them runs its timer callbacks in its own
|
|
||||||
* AioContext thread.
|
|
||||||
*
|
|
||||||
* Create a new timer with millisecond scale on the default timer list
|
* Create a new timer with millisecond scale on the default timer list
|
||||||
* associated with the clock.
|
* associated with the clock.
|
||||||
|
* See timer_new_full for details.
|
||||||
*
|
*
|
||||||
* Returns: a pointer to the newly created timer
|
* Returns: a pointer to the newly created timer
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include "exec/memory.h"
|
#include "exec/memory.h"
|
||||||
#include "sysemu/accel.h"
|
#include "sysemu/accel.h"
|
||||||
|
|
||||||
extern int hvf_disabled;
|
extern bool hvf_allowed;
|
||||||
#ifdef CONFIG_HVF
|
#ifdef CONFIG_HVF
|
||||||
#include <Hypervisor/hv.h>
|
#include <Hypervisor/hv.h>
|
||||||
#include <Hypervisor/hv_vmx.h>
|
#include <Hypervisor/hv_vmx.h>
|
||||||
|
@ -26,7 +26,7 @@ extern int hvf_disabled;
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx,
|
uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx,
|
||||||
int reg);
|
int reg);
|
||||||
#define hvf_enabled() !hvf_disabled
|
#define hvf_enabled() (hvf_allowed)
|
||||||
#else
|
#else
|
||||||
#define hvf_enabled() 0
|
#define hvf_enabled() 0
|
||||||
#define hvf_get_supported_cpuid(func, idx, reg) 0
|
#define hvf_get_supported_cpuid(func, idx, reg) 0
|
||||||
|
|
|
@ -100,14 +100,20 @@ bool replay_has_interrupt(void);
|
||||||
/* Processing clocks and other time sources */
|
/* Processing clocks and other time sources */
|
||||||
|
|
||||||
/*! Save the specified clock */
|
/*! Save the specified clock */
|
||||||
int64_t replay_save_clock(ReplayClockKind kind, int64_t clock);
|
int64_t replay_save_clock(ReplayClockKind kind, int64_t clock,
|
||||||
|
int64_t raw_icount);
|
||||||
/*! Read the specified clock from the log or return cached data */
|
/*! Read the specified clock from the log or return cached data */
|
||||||
int64_t replay_read_clock(ReplayClockKind kind);
|
int64_t replay_read_clock(ReplayClockKind kind);
|
||||||
/*! Saves or reads the clock depending on the current replay mode. */
|
/*! Saves or reads the clock depending on the current replay mode. */
|
||||||
#define REPLAY_CLOCK(clock, value) \
|
#define REPLAY_CLOCK(clock, value) \
|
||||||
(replay_mode == REPLAY_MODE_PLAY ? replay_read_clock((clock)) \
|
(replay_mode == REPLAY_MODE_PLAY ? replay_read_clock((clock)) \
|
||||||
: replay_mode == REPLAY_MODE_RECORD \
|
: replay_mode == REPLAY_MODE_RECORD \
|
||||||
? replay_save_clock((clock), (value)) \
|
? replay_save_clock((clock), (value), cpu_get_icount_raw()) \
|
||||||
|
: (value))
|
||||||
|
#define REPLAY_CLOCK_LOCKED(clock, value) \
|
||||||
|
(replay_mode == REPLAY_MODE_PLAY ? replay_read_clock((clock)) \
|
||||||
|
: replay_mode == REPLAY_MODE_RECORD \
|
||||||
|
? replay_save_clock((clock), (value), cpu_get_icount_raw_locked()) \
|
||||||
: (value))
|
: (value))
|
||||||
|
|
||||||
/* Events */
|
/* Events */
|
||||||
|
|
|
@ -634,6 +634,7 @@ struct kvm_ppc_cpu_char {
|
||||||
|
|
||||||
#define KVM_REG_PPC_DEC_EXPIRY (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbe)
|
#define KVM_REG_PPC_DEC_EXPIRY (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbe)
|
||||||
#define KVM_REG_PPC_ONLINE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xbf)
|
#define KVM_REG_PPC_ONLINE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xbf)
|
||||||
|
#define KVM_REG_PPC_PTCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc0)
|
||||||
|
|
||||||
/* Transactional Memory checkpointed state:
|
/* Transactional Memory checkpointed state:
|
||||||
* This is all GPRs, all VSX regs and a subset of SPRs
|
* This is all GPRs, all VSX regs and a subset of SPRs
|
||||||
|
|
|
@ -288,6 +288,7 @@ struct kvm_reinject_control {
|
||||||
#define KVM_VCPUEVENT_VALID_SIPI_VECTOR 0x00000002
|
#define KVM_VCPUEVENT_VALID_SIPI_VECTOR 0x00000002
|
||||||
#define KVM_VCPUEVENT_VALID_SHADOW 0x00000004
|
#define KVM_VCPUEVENT_VALID_SHADOW 0x00000004
|
||||||
#define KVM_VCPUEVENT_VALID_SMM 0x00000008
|
#define KVM_VCPUEVENT_VALID_SMM 0x00000008
|
||||||
|
#define KVM_VCPUEVENT_VALID_PAYLOAD 0x00000010
|
||||||
|
|
||||||
/* Interrupt shadow states */
|
/* Interrupt shadow states */
|
||||||
#define KVM_X86_SHADOW_INT_MOV_SS 0x01
|
#define KVM_X86_SHADOW_INT_MOV_SS 0x01
|
||||||
|
@ -299,7 +300,7 @@ struct kvm_vcpu_events {
|
||||||
__u8 injected;
|
__u8 injected;
|
||||||
__u8 nr;
|
__u8 nr;
|
||||||
__u8 has_error_code;
|
__u8 has_error_code;
|
||||||
__u8 pad;
|
__u8 pending;
|
||||||
__u32 error_code;
|
__u32 error_code;
|
||||||
} exception;
|
} exception;
|
||||||
struct {
|
struct {
|
||||||
|
@ -322,7 +323,9 @@ struct kvm_vcpu_events {
|
||||||
__u8 smm_inside_nmi;
|
__u8 smm_inside_nmi;
|
||||||
__u8 latched_init;
|
__u8 latched_init;
|
||||||
} smi;
|
} smi;
|
||||||
__u32 reserved[9];
|
__u8 reserved[27];
|
||||||
|
__u8 exception_has_payload;
|
||||||
|
__u64 exception_payload;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* for KVM_GET/SET_DEBUGREGS */
|
/* for KVM_GET/SET_DEBUGREGS */
|
||||||
|
@ -381,6 +384,7 @@ struct kvm_sync_regs {
|
||||||
|
|
||||||
#define KVM_STATE_NESTED_GUEST_MODE 0x00000001
|
#define KVM_STATE_NESTED_GUEST_MODE 0x00000001
|
||||||
#define KVM_STATE_NESTED_RUN_PENDING 0x00000002
|
#define KVM_STATE_NESTED_RUN_PENDING 0x00000002
|
||||||
|
#define KVM_STATE_NESTED_EVMCS 0x00000004
|
||||||
|
|
||||||
#define KVM_STATE_NESTED_SMM_GUEST_MODE 0x00000001
|
#define KVM_STATE_NESTED_SMM_GUEST_MODE 0x00000001
|
||||||
#define KVM_STATE_NESTED_SMM_VMXON 0x00000002
|
#define KVM_STATE_NESTED_SMM_VMXON 0x00000002
|
||||||
|
|
|
@ -420,13 +420,19 @@ struct kvm_run {
|
||||||
struct kvm_coalesced_mmio_zone {
|
struct kvm_coalesced_mmio_zone {
|
||||||
__u64 addr;
|
__u64 addr;
|
||||||
__u32 size;
|
__u32 size;
|
||||||
__u32 pad;
|
union {
|
||||||
|
__u32 pad;
|
||||||
|
__u32 pio;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct kvm_coalesced_mmio {
|
struct kvm_coalesced_mmio {
|
||||||
__u64 phys_addr;
|
__u64 phys_addr;
|
||||||
__u32 len;
|
__u32 len;
|
||||||
__u32 pad;
|
union {
|
||||||
|
__u32 pad;
|
||||||
|
__u32 pio;
|
||||||
|
};
|
||||||
__u8 data[8];
|
__u8 data[8];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -719,6 +725,7 @@ struct kvm_ppc_one_seg_page_size {
|
||||||
|
|
||||||
#define KVM_PPC_PAGE_SIZES_REAL 0x00000001
|
#define KVM_PPC_PAGE_SIZES_REAL 0x00000001
|
||||||
#define KVM_PPC_1T_SEGMENTS 0x00000002
|
#define KVM_PPC_1T_SEGMENTS 0x00000002
|
||||||
|
#define KVM_PPC_NO_HASH 0x00000004
|
||||||
|
|
||||||
struct kvm_ppc_smmu_info {
|
struct kvm_ppc_smmu_info {
|
||||||
__u64 flags;
|
__u64 flags;
|
||||||
|
@ -953,6 +960,11 @@ struct kvm_ppc_resize_hpt {
|
||||||
#define KVM_CAP_NESTED_STATE 157
|
#define KVM_CAP_NESTED_STATE 157
|
||||||
#define KVM_CAP_ARM_INJECT_SERROR_ESR 158
|
#define KVM_CAP_ARM_INJECT_SERROR_ESR 158
|
||||||
#define KVM_CAP_MSR_PLATFORM_INFO 159
|
#define KVM_CAP_MSR_PLATFORM_INFO 159
|
||||||
|
#define KVM_CAP_PPC_NESTED_HV 160
|
||||||
|
#define KVM_CAP_HYPERV_SEND_IPI 161
|
||||||
|
#define KVM_CAP_COALESCED_PIO 162
|
||||||
|
#define KVM_CAP_HYPERV_ENLIGHTENED_VMCS 163
|
||||||
|
#define KVM_CAP_EXCEPTION_PAYLOAD 164
|
||||||
|
|
||||||
#ifdef KVM_CAP_IRQ_ROUTING
|
#ifdef KVM_CAP_IRQ_ROUTING
|
||||||
|
|
||||||
|
|
4
memory.c
4
memory.c
|
@ -2129,7 +2129,7 @@ static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpa
|
||||||
.size = fr->addr.size,
|
.size = fr->addr.size,
|
||||||
};
|
};
|
||||||
|
|
||||||
MEMORY_LISTENER_CALL(as, coalesced_mmio_del, Reverse, §ion,
|
MEMORY_LISTENER_CALL(as, coalesced_io_del, Reverse, §ion,
|
||||||
int128_get64(fr->addr.start),
|
int128_get64(fr->addr.start),
|
||||||
int128_get64(fr->addr.size));
|
int128_get64(fr->addr.size));
|
||||||
QTAILQ_FOREACH(cmr, &mr->coalesced, link) {
|
QTAILQ_FOREACH(cmr, &mr->coalesced, link) {
|
||||||
|
@ -2140,7 +2140,7 @@ static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpa
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
tmp = addrrange_intersection(tmp, fr->addr);
|
tmp = addrrange_intersection(tmp, fr->addr);
|
||||||
MEMORY_LISTENER_CALL(as, coalesced_mmio_add, Forward, §ion,
|
MEMORY_LISTENER_CALL(as, coalesced_io_add, Forward, §ion,
|
||||||
int128_get64(tmp.start),
|
int128_get64(tmp.start),
|
||||||
int128_get64(tmp.size));
|
int128_get64(tmp.size));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3070,7 +3070,8 @@
|
||||||
# Emitted when the guest changes the RTC time.
|
# Emitted when the guest changes the RTC time.
|
||||||
#
|
#
|
||||||
# @offset: offset between base RTC clock (as specified by -rtc base), and
|
# @offset: offset between base RTC clock (as specified by -rtc base), and
|
||||||
# new RTC clock value
|
# new RTC clock value. Note that value will be different depending
|
||||||
|
# on clock chosen to drive RTC (specified by -rtc clock).
|
||||||
#
|
#
|
||||||
# Note: This event is rate-limited.
|
# Note: This event is rate-limited.
|
||||||
#
|
#
|
||||||
|
|
|
@ -3458,25 +3458,29 @@ HXCOMM Silently ignored for compatibility
|
||||||
DEF("clock", HAS_ARG, QEMU_OPTION_clock, "", QEMU_ARCH_ALL)
|
DEF("clock", HAS_ARG, QEMU_OPTION_clock, "", QEMU_ARCH_ALL)
|
||||||
|
|
||||||
DEF("rtc", HAS_ARG, QEMU_OPTION_rtc, \
|
DEF("rtc", HAS_ARG, QEMU_OPTION_rtc, \
|
||||||
"-rtc [base=utc|localtime|date][,clock=host|rt|vm][,driftfix=none|slew]\n" \
|
"-rtc [base=utc|localtime|<datetime>][,clock=host|rt|vm][,driftfix=none|slew]\n" \
|
||||||
" set the RTC base and clock, enable drift fix for clock ticks (x86 only)\n",
|
" set the RTC base and clock, enable drift fix for clock ticks (x86 only)\n",
|
||||||
QEMU_ARCH_ALL)
|
QEMU_ARCH_ALL)
|
||||||
|
|
||||||
STEXI
|
STEXI
|
||||||
|
|
||||||
@item -rtc [base=utc|localtime|@var{date}][,clock=host|vm][,driftfix=none|slew]
|
@item -rtc [base=utc|localtime|@var{datetime}][,clock=host|rt|vm][,driftfix=none|slew]
|
||||||
@findex -rtc
|
@findex -rtc
|
||||||
Specify @option{base} as @code{utc} or @code{localtime} to let the RTC start at the current
|
Specify @option{base} as @code{utc} or @code{localtime} to let the RTC start at the current
|
||||||
UTC or local time, respectively. @code{localtime} is required for correct date in
|
UTC or local time, respectively. @code{localtime} is required for correct date in
|
||||||
MS-DOS or Windows. To start at a specific point in time, provide @var{date} in the
|
MS-DOS or Windows. To start at a specific point in time, provide @var{datetime} in the
|
||||||
format @code{2006-06-17T16:01:21} or @code{2006-06-17}. The default base is UTC.
|
format @code{2006-06-17T16:01:21} or @code{2006-06-17}. The default base is UTC.
|
||||||
|
|
||||||
By default the RTC is driven by the host system time. This allows using of the
|
By default the RTC is driven by the host system time. This allows using of the
|
||||||
RTC as accurate reference clock inside the guest, specifically if the host
|
RTC as accurate reference clock inside the guest, specifically if the host
|
||||||
time is smoothly following an accurate external reference clock, e.g. via NTP.
|
time is smoothly following an accurate external reference clock, e.g. via NTP.
|
||||||
If you want to isolate the guest time from the host, you can set @option{clock}
|
If you want to isolate the guest time from the host, you can set @option{clock}
|
||||||
to @code{rt} instead. To even prevent it from progressing during suspension,
|
to @code{rt} instead, which provides a host monotonic clock if host support it.
|
||||||
you can set it to @code{vm}.
|
To even prevent the RTC from progressing during suspension, you can set @option{clock}
|
||||||
|
to @code{vm} (virtual clock). @samp{clock=vm} is recommended especially in
|
||||||
|
icount mode in order to preserve determinism; however, note that in icount mode
|
||||||
|
the speed of the virtual clock is variable and can in general differ from the
|
||||||
|
host clock.
|
||||||
|
|
||||||
Enable @option{driftfix} (i386 targets only) if you experience time drift problems,
|
Enable @option{driftfix} (i386 targets only) if you experience time drift problems,
|
||||||
specifically with Windows' ACPI HAL. This option will try to figure out how
|
specifically with Windows' ACPI HAL. This option will try to figure out how
|
||||||
|
|
|
@ -190,6 +190,7 @@ void replay_save_events(int checkpoint)
|
||||||
{
|
{
|
||||||
g_assert(replay_mutex_locked());
|
g_assert(replay_mutex_locked());
|
||||||
g_assert(checkpoint != CHECKPOINT_CLOCK_WARP_START);
|
g_assert(checkpoint != CHECKPOINT_CLOCK_WARP_START);
|
||||||
|
g_assert(checkpoint != CHECKPOINT_CLOCK_VIRTUAL);
|
||||||
while (!QTAILQ_EMPTY(&events_list)) {
|
while (!QTAILQ_EMPTY(&events_list)) {
|
||||||
Event *event = QTAILQ_FIRST(&events_list);
|
Event *event = QTAILQ_FIRST(&events_list);
|
||||||
replay_save_event(event, checkpoint);
|
replay_save_event(event, checkpoint);
|
||||||
|
|
|
@ -217,20 +217,25 @@ void replay_mutex_unlock(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void replay_advance_current_step(uint64_t current_step)
|
||||||
|
{
|
||||||
|
int diff = (int)(replay_get_current_step() - replay_state.current_step);
|
||||||
|
|
||||||
|
/* Time can only go forward */
|
||||||
|
assert(diff >= 0);
|
||||||
|
|
||||||
|
if (diff > 0) {
|
||||||
|
replay_put_event(EVENT_INSTRUCTION);
|
||||||
|
replay_put_dword(diff);
|
||||||
|
replay_state.current_step += diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*! Saves cached instructions. */
|
/*! Saves cached instructions. */
|
||||||
void replay_save_instructions(void)
|
void replay_save_instructions(void)
|
||||||
{
|
{
|
||||||
if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
|
if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
|
||||||
g_assert(replay_mutex_locked());
|
g_assert(replay_mutex_locked());
|
||||||
int diff = (int)(replay_get_current_step() - replay_state.current_step);
|
replay_advance_current_step(replay_get_current_step());
|
||||||
|
|
||||||
/* Time can only go forward */
|
|
||||||
assert(diff >= 0);
|
|
||||||
|
|
||||||
if (diff > 0) {
|
|
||||||
replay_put_event(EVENT_INSTRUCTION);
|
|
||||||
replay_put_dword(diff);
|
|
||||||
replay_state.current_step += diff;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,6 +122,8 @@ void replay_finish_event(void);
|
||||||
data_kind variable. */
|
data_kind variable. */
|
||||||
void replay_fetch_data_kind(void);
|
void replay_fetch_data_kind(void);
|
||||||
|
|
||||||
|
/*! Advance replay_state.current_step to the specified value. */
|
||||||
|
void replay_advance_current_step(uint64_t current_step);
|
||||||
/*! Saves queued events (like instructions and sound). */
|
/*! Saves queued events (like instructions and sound). */
|
||||||
void replay_save_instructions(void);
|
void replay_save_instructions(void);
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,15 @@
|
||||||
#include "replay-internal.h"
|
#include "replay-internal.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
|
|
||||||
int64_t replay_save_clock(ReplayClockKind kind, int64_t clock)
|
int64_t replay_save_clock(ReplayClockKind kind, int64_t clock, int64_t raw_icount)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (replay_file) {
|
if (replay_file) {
|
||||||
g_assert(replay_mutex_locked());
|
g_assert(replay_mutex_locked());
|
||||||
|
|
||||||
replay_save_instructions();
|
/* Due to the caller's locking requirements we get the icount from it
|
||||||
|
* instead of using replay_save_instructions().
|
||||||
|
*/
|
||||||
|
replay_advance_current_step(raw_icount);
|
||||||
replay_put_event(EVENT_CLOCK + kind);
|
replay_put_event(EVENT_CLOCK + kind);
|
||||||
replay_put_qword(clock);
|
replay_put_qword(clock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,7 +214,14 @@ bool replay_checkpoint(ReplayCheckpoint checkpoint)
|
||||||
/* This checkpoint belongs to several threads.
|
/* This checkpoint belongs to several threads.
|
||||||
Processing events from different threads is
|
Processing events from different threads is
|
||||||
non-deterministic */
|
non-deterministic */
|
||||||
if (checkpoint != CHECKPOINT_CLOCK_WARP_START) {
|
if (checkpoint != CHECKPOINT_CLOCK_WARP_START
|
||||||
|
/* FIXME: this is temporary fix, other checkpoints
|
||||||
|
may also be invoked from the different threads someday.
|
||||||
|
Asynchronous event processing should be refactored
|
||||||
|
to create additional replay event kind which is
|
||||||
|
nailed to the one of the threads and which processes
|
||||||
|
the event queue. */
|
||||||
|
&& checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
|
||||||
replay_save_events(checkpoint);
|
replay_save_events(checkpoint);
|
||||||
}
|
}
|
||||||
res = true;
|
res = true;
|
||||||
|
|
|
@ -17,7 +17,7 @@ static void ra_timer_handler(void *opaque)
|
||||||
{
|
{
|
||||||
Slirp *slirp = opaque;
|
Slirp *slirp = opaque;
|
||||||
timer_mod(slirp->ra_timer,
|
timer_mod(slirp->ra_timer,
|
||||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT) + NDP_Interval);
|
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
|
||||||
ndp_send_ra(slirp);
|
ndp_send_ra(slirp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,10 +27,11 @@ void icmp6_init(Slirp *slirp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
slirp->ra_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_EXT,
|
slirp->ra_timer = timer_new_full(NULL, QEMU_CLOCK_VIRTUAL,
|
||||||
ra_timer_handler, slirp);
|
SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
|
||||||
|
ra_timer_handler, slirp);
|
||||||
timer_mod(slirp->ra_timer,
|
timer_mod(slirp->ra_timer,
|
||||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT) + NDP_Interval);
|
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
void icmp6_cleanup(Slirp *slirp)
|
void icmp6_cleanup(Slirp *slirp)
|
||||||
|
|
|
@ -11,6 +11,11 @@ int64_t cpu_get_icount(void)
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t cpu_get_icount_raw(void)
|
||||||
|
{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
void qemu_timer_notify_cb(void *opaque, QEMUClockType type)
|
void qemu_timer_notify_cb(void *opaque, QEMUClockType type)
|
||||||
{
|
{
|
||||||
qemu_notify_event();
|
qemu_notify_event();
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
ReplayMode replay_mode;
|
ReplayMode replay_mode;
|
||||||
|
|
||||||
int64_t replay_save_clock(unsigned int kind, int64_t clock)
|
int64_t replay_save_clock(unsigned int kind, int64_t clock, int64_t raw_icount)
|
||||||
{
|
{
|
||||||
abort();
|
abort();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -3,17 +3,20 @@ obj-$(CONFIG_TCG) += translate.o
|
||||||
obj-$(CONFIG_TCG) += bpt_helper.o cc_helper.o excp_helper.o fpu_helper.o
|
obj-$(CONFIG_TCG) += bpt_helper.o cc_helper.o excp_helper.o fpu_helper.o
|
||||||
obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o mpx_helper.o
|
obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o mpx_helper.o
|
||||||
obj-$(CONFIG_TCG) += seg_helper.o smm_helper.o svm_helper.o
|
obj-$(CONFIG_TCG) += seg_helper.o smm_helper.o svm_helper.o
|
||||||
obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o monitor.o
|
ifeq ($(CONFIG_SOFTMMU),y)
|
||||||
obj-$(CONFIG_KVM) += kvm.o hyperv.o
|
obj-y += machine.o arch_memory_mapping.o arch_dump.o monitor.o
|
||||||
obj-$(CONFIG_SEV) += sev.o
|
obj-$(CONFIG_KVM) += kvm.o
|
||||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
||||||
obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
|
obj-$(CONFIG_HYPERV) += hyperv.o
|
||||||
# HAX support
|
obj-$(call lnot,$(CONFIG_HYPERV)) += hyperv-stub.o
|
||||||
ifdef CONFIG_WIN32
|
ifeq ($(CONFIG_WIN32),y)
|
||||||
obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-windows.o
|
obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-windows.o
|
||||||
endif
|
endif
|
||||||
ifdef CONFIG_DARWIN
|
ifeq ($(CONFIG_DARWIN),y)
|
||||||
obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-darwin.o
|
obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-darwin.o
|
||||||
obj-$(CONFIG_HVF) += hvf/
|
obj-$(CONFIG_HVF) += hvf/
|
||||||
endif
|
endif
|
||||||
obj-$(CONFIG_WHPX) += whpx-all.o
|
obj-$(CONFIG_WHPX) += whpx-all.o
|
||||||
|
endif
|
||||||
|
obj-$(CONFIG_SEV) += sev.o
|
||||||
|
obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o
|
||||||
|
|
|
@ -5564,6 +5564,7 @@ static Property x86_cpu_properties[] = {
|
||||||
DEFINE_PROP_BOOL("hv-frequencies", X86CPU, hyperv_frequencies, false),
|
DEFINE_PROP_BOOL("hv-frequencies", X86CPU, hyperv_frequencies, false),
|
||||||
DEFINE_PROP_BOOL("hv-reenlightenment", X86CPU, hyperv_reenlightenment, false),
|
DEFINE_PROP_BOOL("hv-reenlightenment", X86CPU, hyperv_reenlightenment, false),
|
||||||
DEFINE_PROP_BOOL("hv-tlbflush", X86CPU, hyperv_tlbflush, false),
|
DEFINE_PROP_BOOL("hv-tlbflush", X86CPU, hyperv_tlbflush, false),
|
||||||
|
DEFINE_PROP_BOOL("hv-ipi", X86CPU, hyperv_ipi, false),
|
||||||
DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true),
|
DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true),
|
||||||
DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
|
DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
|
||||||
DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
|
DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
|
||||||
|
@ -5606,6 +5607,8 @@ static Property x86_cpu_properties[] = {
|
||||||
* to the specific Windows version being used."
|
* to the specific Windows version being used."
|
||||||
*/
|
*/
|
||||||
DEFINE_PROP_INT32("x-hv-max-vps", X86CPU, hv_max_vps, -1),
|
DEFINE_PROP_INT32("x-hv-max-vps", X86CPU, hv_max_vps, -1),
|
||||||
|
DEFINE_PROP_BOOL("x-hv-synic-kvm-only", X86CPU, hyperv_synic_kvm_only,
|
||||||
|
false),
|
||||||
DEFINE_PROP_END_OF_LIST()
|
DEFINE_PROP_END_OF_LIST()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1378,10 +1378,12 @@ struct X86CPU {
|
||||||
bool hyperv_vpindex;
|
bool hyperv_vpindex;
|
||||||
bool hyperv_runtime;
|
bool hyperv_runtime;
|
||||||
bool hyperv_synic;
|
bool hyperv_synic;
|
||||||
|
bool hyperv_synic_kvm_only;
|
||||||
bool hyperv_stimer;
|
bool hyperv_stimer;
|
||||||
bool hyperv_frequencies;
|
bool hyperv_frequencies;
|
||||||
bool hyperv_reenlightenment;
|
bool hyperv_reenlightenment;
|
||||||
bool hyperv_tlbflush;
|
bool hyperv_tlbflush;
|
||||||
|
bool hyperv_ipi;
|
||||||
bool check_cpuid;
|
bool check_cpuid;
|
||||||
bool enforce_cpuid;
|
bool enforce_cpuid;
|
||||||
bool expose_kvm;
|
bool expose_kvm;
|
||||||
|
|
|
@ -73,7 +73,6 @@
|
||||||
#include "target/i386/cpu.h"
|
#include "target/i386/cpu.h"
|
||||||
|
|
||||||
HVFState *hvf_state;
|
HVFState *hvf_state;
|
||||||
int hvf_disabled = 1;
|
|
||||||
|
|
||||||
static void assert_hvf_ok(hv_return_t ret)
|
static void assert_hvf_ok(hv_return_t ret)
|
||||||
{
|
{
|
||||||
|
@ -604,11 +603,6 @@ int hvf_init_vcpu(CPUState *cpu)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hvf_disable(int shouldDisable)
|
|
||||||
{
|
|
||||||
hvf_disabled = shouldDisable;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hvf_store_events(CPUState *cpu, uint32_t ins_len, uint64_t idtvec_info)
|
static void hvf_store_events(CPUState *cpu, uint32_t ins_len, uint64_t idtvec_info)
|
||||||
{
|
{
|
||||||
X86CPU *x86_cpu = X86_CPU(cpu);
|
X86CPU *x86_cpu = X86_CPU(cpu);
|
||||||
|
@ -934,7 +928,7 @@ int hvf_vcpu_exec(CPUState *cpu)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hvf_allowed;
|
bool hvf_allowed;
|
||||||
|
|
||||||
static int hvf_accel_init(MachineState *ms)
|
static int hvf_accel_init(MachineState *ms)
|
||||||
{
|
{
|
||||||
|
@ -942,7 +936,6 @@ static int hvf_accel_init(MachineState *ms)
|
||||||
hv_return_t ret;
|
hv_return_t ret;
|
||||||
HVFState *s;
|
HVFState *s;
|
||||||
|
|
||||||
hvf_disable(0);
|
|
||||||
ret = hv_vm_create(HV_VM_DEFAULT);
|
ret = hv_vm_create(HV_VM_DEFAULT);
|
||||||
assert_hvf_ok(ret);
|
assert_hvf_ok(ret);
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,8 @@ static void decode_modrm_reg(CPUX86State *env, struct x86_decode *decode,
|
||||||
{
|
{
|
||||||
op->type = X86_VAR_REG;
|
op->type = X86_VAR_REG;
|
||||||
op->reg = decode->modrm.reg;
|
op->reg = decode->modrm.reg;
|
||||||
op->ptr = get_reg_ref(env, op->reg, decode->rex.r, decode->operand_size);
|
op->ptr = get_reg_ref(env, op->reg, decode->rex.rex, decode->rex.r,
|
||||||
|
decode->operand_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void decode_rax(CPUX86State *env, struct x86_decode *decode,
|
static void decode_rax(CPUX86State *env, struct x86_decode *decode,
|
||||||
|
@ -121,7 +122,8 @@ static void decode_rax(CPUX86State *env, struct x86_decode *decode,
|
||||||
{
|
{
|
||||||
op->type = X86_VAR_REG;
|
op->type = X86_VAR_REG;
|
||||||
op->reg = R_EAX;
|
op->reg = R_EAX;
|
||||||
op->ptr = get_reg_ref(env, op->reg, 0, decode->operand_size);
|
op->ptr = get_reg_ref(env, op->reg, decode->rex.rex, 0,
|
||||||
|
decode->operand_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void decode_immediate(CPUX86State *env, struct x86_decode *decode,
|
static inline void decode_immediate(CPUX86State *env, struct x86_decode *decode,
|
||||||
|
@ -263,16 +265,16 @@ static void decode_incgroup(CPUX86State *env, struct x86_decode *decode)
|
||||||
{
|
{
|
||||||
decode->op[0].type = X86_VAR_REG;
|
decode->op[0].type = X86_VAR_REG;
|
||||||
decode->op[0].reg = decode->opcode[0] - 0x40;
|
decode->op[0].reg = decode->opcode[0] - 0x40;
|
||||||
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
|
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
|
||||||
decode->operand_size);
|
decode->rex.b, decode->operand_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void decode_decgroup(CPUX86State *env, struct x86_decode *decode)
|
static void decode_decgroup(CPUX86State *env, struct x86_decode *decode)
|
||||||
{
|
{
|
||||||
decode->op[0].type = X86_VAR_REG;
|
decode->op[0].type = X86_VAR_REG;
|
||||||
decode->op[0].reg = decode->opcode[0] - 0x48;
|
decode->op[0].reg = decode->opcode[0] - 0x48;
|
||||||
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
|
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
|
||||||
decode->operand_size);
|
decode->rex.b, decode->operand_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void decode_incgroup2(CPUX86State *env, struct x86_decode *decode)
|
static void decode_incgroup2(CPUX86State *env, struct x86_decode *decode)
|
||||||
|
@ -288,16 +290,16 @@ static void decode_pushgroup(CPUX86State *env, struct x86_decode *decode)
|
||||||
{
|
{
|
||||||
decode->op[0].type = X86_VAR_REG;
|
decode->op[0].type = X86_VAR_REG;
|
||||||
decode->op[0].reg = decode->opcode[0] - 0x50;
|
decode->op[0].reg = decode->opcode[0] - 0x50;
|
||||||
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
|
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
|
||||||
decode->operand_size);
|
decode->rex.b, decode->operand_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void decode_popgroup(CPUX86State *env, struct x86_decode *decode)
|
static void decode_popgroup(CPUX86State *env, struct x86_decode *decode)
|
||||||
{
|
{
|
||||||
decode->op[0].type = X86_VAR_REG;
|
decode->op[0].type = X86_VAR_REG;
|
||||||
decode->op[0].reg = decode->opcode[0] - 0x58;
|
decode->op[0].reg = decode->opcode[0] - 0x58;
|
||||||
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
|
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
|
||||||
decode->operand_size);
|
decode->rex.b, decode->operand_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void decode_jxx(CPUX86State *env, struct x86_decode *decode)
|
static void decode_jxx(CPUX86State *env, struct x86_decode *decode)
|
||||||
|
@ -378,16 +380,16 @@ static void decode_xchgroup(CPUX86State *env, struct x86_decode *decode)
|
||||||
{
|
{
|
||||||
decode->op[0].type = X86_VAR_REG;
|
decode->op[0].type = X86_VAR_REG;
|
||||||
decode->op[0].reg = decode->opcode[0] - 0x90;
|
decode->op[0].reg = decode->opcode[0] - 0x90;
|
||||||
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
|
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
|
||||||
decode->operand_size);
|
decode->rex.b, decode->operand_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void decode_movgroup(CPUX86State *env, struct x86_decode *decode)
|
static void decode_movgroup(CPUX86State *env, struct x86_decode *decode)
|
||||||
{
|
{
|
||||||
decode->op[0].type = X86_VAR_REG;
|
decode->op[0].type = X86_VAR_REG;
|
||||||
decode->op[0].reg = decode->opcode[0] - 0xb8;
|
decode->op[0].reg = decode->opcode[0] - 0xb8;
|
||||||
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
|
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
|
||||||
decode->operand_size);
|
decode->rex.b, decode->operand_size);
|
||||||
decode_immediate(env, decode, &decode->op[1], decode->operand_size);
|
decode_immediate(env, decode, &decode->op[1], decode->operand_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,8 +404,8 @@ static void decode_movgroup8(CPUX86State *env, struct x86_decode *decode)
|
||||||
{
|
{
|
||||||
decode->op[0].type = X86_VAR_REG;
|
decode->op[0].type = X86_VAR_REG;
|
||||||
decode->op[0].reg = decode->opcode[0] - 0xb0;
|
decode->op[0].reg = decode->opcode[0] - 0xb0;
|
||||||
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
|
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
|
||||||
decode->operand_size);
|
decode->rex.b, decode->operand_size);
|
||||||
decode_immediate(env, decode, &decode->op[1], decode->operand_size);
|
decode_immediate(env, decode, &decode->op[1], decode->operand_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,7 +414,8 @@ static void decode_rcx(CPUX86State *env, struct x86_decode *decode,
|
||||||
{
|
{
|
||||||
op->type = X86_VAR_REG;
|
op->type = X86_VAR_REG;
|
||||||
op->reg = R_ECX;
|
op->reg = R_ECX;
|
||||||
op->ptr = get_reg_ref(env, op->reg, decode->rex.b, decode->operand_size);
|
op->ptr = get_reg_ref(env, op->reg, decode->rex.rex, decode->rex.b,
|
||||||
|
decode->operand_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct decode_tbl {
|
struct decode_tbl {
|
||||||
|
@ -639,8 +642,8 @@ static void decode_bswap(CPUX86State *env, struct x86_decode *decode)
|
||||||
{
|
{
|
||||||
decode->op[0].type = X86_VAR_REG;
|
decode->op[0].type = X86_VAR_REG;
|
||||||
decode->op[0].reg = decode->opcode[1] - 0xc8;
|
decode->op[0].reg = decode->opcode[1] - 0xc8;
|
||||||
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b,
|
decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex,
|
||||||
decode->operand_size);
|
decode->rex.b, decode->operand_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void decode_d9_4(CPUX86State *env, struct x86_decode *decode)
|
static void decode_d9_4(CPUX86State *env, struct x86_decode *decode)
|
||||||
|
@ -1686,7 +1689,8 @@ calc_addr:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
target_ulong get_reg_ref(CPUX86State *env, int reg, int is_extended, int size)
|
target_ulong get_reg_ref(CPUX86State *env, int reg, int rex, int is_extended,
|
||||||
|
int size)
|
||||||
{
|
{
|
||||||
target_ulong ptr = 0;
|
target_ulong ptr = 0;
|
||||||
int which = 0;
|
int which = 0;
|
||||||
|
@ -1698,7 +1702,7 @@ target_ulong get_reg_ref(CPUX86State *env, int reg, int is_extended, int size)
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
if (is_extended || reg < 4) {
|
if (is_extended || reg < 4 || rex) {
|
||||||
which = 1;
|
which = 1;
|
||||||
ptr = (target_ulong)&RL(env, reg);
|
ptr = (target_ulong)&RL(env, reg);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1714,10 +1718,11 @@ target_ulong get_reg_ref(CPUX86State *env, int reg, int is_extended, int size)
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
target_ulong get_reg_val(CPUX86State *env, int reg, int is_extended, int size)
|
target_ulong get_reg_val(CPUX86State *env, int reg, int rex, int is_extended,
|
||||||
|
int size)
|
||||||
{
|
{
|
||||||
target_ulong val = 0;
|
target_ulong val = 0;
|
||||||
memcpy(&val, (void *)get_reg_ref(env, reg, is_extended, size), size);
|
memcpy(&val, (void *)get_reg_ref(env, reg, rex, is_extended, size), size);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1739,7 +1744,8 @@ static target_ulong get_sib_val(CPUX86State *env, struct x86_decode *decode,
|
||||||
if (base_reg == R_ESP || base_reg == R_EBP) {
|
if (base_reg == R_ESP || base_reg == R_EBP) {
|
||||||
*sel = R_SS;
|
*sel = R_SS;
|
||||||
}
|
}
|
||||||
base = get_reg_val(env, decode->sib.base, decode->rex.b, addr_size);
|
base = get_reg_val(env, decode->sib.base, decode->rex.rex,
|
||||||
|
decode->rex.b, addr_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decode->rex.x) {
|
if (decode->rex.x) {
|
||||||
|
@ -1747,7 +1753,8 @@ static target_ulong get_sib_val(CPUX86State *env, struct x86_decode *decode,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index_reg != R_ESP) {
|
if (index_reg != R_ESP) {
|
||||||
scaled_index = get_reg_val(env, index_reg, decode->rex.x, addr_size) <<
|
scaled_index = get_reg_val(env, index_reg, decode->rex.rex,
|
||||||
|
decode->rex.x, addr_size) <<
|
||||||
decode->sib.scale;
|
decode->sib.scale;
|
||||||
}
|
}
|
||||||
return base + scaled_index;
|
return base + scaled_index;
|
||||||
|
@ -1776,7 +1783,8 @@ void calc_modrm_operand32(CPUX86State *env, struct x86_decode *decode,
|
||||||
if (decode->modrm.rm == R_EBP || decode->modrm.rm == R_ESP) {
|
if (decode->modrm.rm == R_EBP || decode->modrm.rm == R_ESP) {
|
||||||
seg = R_SS;
|
seg = R_SS;
|
||||||
}
|
}
|
||||||
ptr += get_reg_val(env, decode->modrm.rm, decode->rex.b, addr_size);
|
ptr += get_reg_val(env, decode->modrm.rm, decode->rex.rex,
|
||||||
|
decode->rex.b, addr_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (X86_DECODE_CMD_LEA == decode->cmd) {
|
if (X86_DECODE_CMD_LEA == decode->cmd) {
|
||||||
|
@ -1805,7 +1813,8 @@ void calc_modrm_operand64(CPUX86State *env, struct x86_decode *decode,
|
||||||
} else if (0 == mod && 5 == rm) {
|
} else if (0 == mod && 5 == rm) {
|
||||||
ptr = RIP(env) + decode->len + (int32_t) offset;
|
ptr = RIP(env) + decode->len + (int32_t) offset;
|
||||||
} else {
|
} else {
|
||||||
ptr = get_reg_val(env, src, decode->rex.b, 8) + (int64_t) offset;
|
ptr = get_reg_val(env, src, decode->rex.rex, decode->rex.b, 8) +
|
||||||
|
(int64_t) offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (X86_DECODE_CMD_LEA == decode->cmd) {
|
if (X86_DECODE_CMD_LEA == decode->cmd) {
|
||||||
|
@ -1822,8 +1831,8 @@ void calc_modrm_operand(CPUX86State *env, struct x86_decode *decode,
|
||||||
if (3 == decode->modrm.mod) {
|
if (3 == decode->modrm.mod) {
|
||||||
op->reg = decode->modrm.reg;
|
op->reg = decode->modrm.reg;
|
||||||
op->type = X86_VAR_REG;
|
op->type = X86_VAR_REG;
|
||||||
op->ptr = get_reg_ref(env, decode->modrm.rm, decode->rex.b,
|
op->ptr = get_reg_ref(env, decode->modrm.rm, decode->rex.rex,
|
||||||
decode->operand_size);
|
decode->rex.b, decode->operand_size);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -303,8 +303,10 @@ uint64_t sign(uint64_t val, int size);
|
||||||
|
|
||||||
uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode);
|
uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode);
|
||||||
|
|
||||||
target_ulong get_reg_ref(CPUX86State *env, int reg, int is_extended, int size);
|
target_ulong get_reg_ref(CPUX86State *env, int reg, int rex, int is_extended,
|
||||||
target_ulong get_reg_val(CPUX86State *env, int reg, int is_extended, int size);
|
int size);
|
||||||
|
target_ulong get_reg_val(CPUX86State *env, int reg, int rex, int is_extended,
|
||||||
|
int size);
|
||||||
void calc_modrm_operand(CPUX86State *env, struct x86_decode *decode,
|
void calc_modrm_operand(CPUX86State *env, struct x86_decode *decode,
|
||||||
struct x86_decode_op *op);
|
struct x86_decode_op *op);
|
||||||
target_ulong decode_linear_addr(CPUX86State *env, struct x86_decode *decode,
|
target_ulong decode_linear_addr(CPUX86State *env, struct x86_decode *decode,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Definitions for Hyper-V guest/hypervisor interaction
|
* Definitions for Hyper-V guest/hypervisor interaction - x86-specific part
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Parallels International GmbH
|
* Copyright (c) 2017-2018 Virtuozzo International GmbH.
|
||||||
*
|
*
|
||||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
* See the COPYING file in the top-level directory.
|
* See the COPYING file in the top-level directory.
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
#ifndef TARGET_I386_HYPERV_PROTO_H
|
#ifndef TARGET_I386_HYPERV_PROTO_H
|
||||||
#define TARGET_I386_HYPERV_PROTO_H
|
#define TARGET_I386_HYPERV_PROTO_H
|
||||||
|
|
||||||
#include "qemu/bitmap.h"
|
#include "hw/hyperv/hyperv-proto.h"
|
||||||
|
|
||||||
#define HV_CPUID_VENDOR_AND_MAX_FUNCTIONS 0x40000000
|
#define HV_CPUID_VENDOR_AND_MAX_FUNCTIONS 0x40000000
|
||||||
#define HV_CPUID_INTERFACE 0x40000001
|
#define HV_CPUID_INTERFACE 0x40000001
|
||||||
|
@ -58,6 +58,7 @@
|
||||||
#define HV_APIC_ACCESS_RECOMMENDED (1u << 3)
|
#define HV_APIC_ACCESS_RECOMMENDED (1u << 3)
|
||||||
#define HV_SYSTEM_RESET_RECOMMENDED (1u << 4)
|
#define HV_SYSTEM_RESET_RECOMMENDED (1u << 4)
|
||||||
#define HV_RELAXED_TIMING_RECOMMENDED (1u << 5)
|
#define HV_RELAXED_TIMING_RECOMMENDED (1u << 5)
|
||||||
|
#define HV_CLUSTER_IPI_RECOMMENDED (1u << 10)
|
||||||
#define HV_EX_PROCESSOR_MASKS_RECOMMENDED (1u << 11)
|
#define HV_EX_PROCESSOR_MASKS_RECOMMENDED (1u << 11)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -137,25 +138,6 @@
|
||||||
#define HV_X64_MSR_TSC_EMULATION_CONTROL 0x40000107
|
#define HV_X64_MSR_TSC_EMULATION_CONTROL 0x40000107
|
||||||
#define HV_X64_MSR_TSC_EMULATION_STATUS 0x40000108
|
#define HV_X64_MSR_TSC_EMULATION_STATUS 0x40000108
|
||||||
|
|
||||||
/*
|
|
||||||
* Hypercall status code
|
|
||||||
*/
|
|
||||||
#define HV_STATUS_SUCCESS 0
|
|
||||||
#define HV_STATUS_INVALID_HYPERCALL_CODE 2
|
|
||||||
#define HV_STATUS_INVALID_HYPERCALL_INPUT 3
|
|
||||||
#define HV_STATUS_INVALID_ALIGNMENT 4
|
|
||||||
#define HV_STATUS_INVALID_PARAMETER 5
|
|
||||||
#define HV_STATUS_INSUFFICIENT_MEMORY 11
|
|
||||||
#define HV_STATUS_INVALID_CONNECTION_ID 18
|
|
||||||
#define HV_STATUS_INSUFFICIENT_BUFFERS 19
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Hypercall numbers
|
|
||||||
*/
|
|
||||||
#define HV_POST_MESSAGE 0x005c
|
|
||||||
#define HV_SIGNAL_EVENT 0x005d
|
|
||||||
#define HV_HYPERCALL_FAST (1u << 16)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hypercall MSR bits
|
* Hypercall MSR bits
|
||||||
*/
|
*/
|
||||||
|
@ -165,7 +147,6 @@
|
||||||
* Synthetic interrupt controller definitions
|
* Synthetic interrupt controller definitions
|
||||||
*/
|
*/
|
||||||
#define HV_SYNIC_VERSION 1
|
#define HV_SYNIC_VERSION 1
|
||||||
#define HV_SINT_COUNT 16
|
|
||||||
#define HV_SYNIC_ENABLE (1u << 0)
|
#define HV_SYNIC_ENABLE (1u << 0)
|
||||||
#define HV_SIMP_ENABLE (1u << 0)
|
#define HV_SIMP_ENABLE (1u << 0)
|
||||||
#define HV_SIEFP_ENABLE (1u << 0)
|
#define HV_SIEFP_ENABLE (1u << 0)
|
||||||
|
@ -175,94 +156,5 @@
|
||||||
|
|
||||||
#define HV_STIMER_COUNT 4
|
#define HV_STIMER_COUNT 4
|
||||||
|
|
||||||
/*
|
|
||||||
* Message size
|
|
||||||
*/
|
|
||||||
#define HV_MESSAGE_PAYLOAD_SIZE 240
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Message types
|
|
||||||
*/
|
|
||||||
#define HV_MESSAGE_NONE 0x00000000
|
|
||||||
#define HV_MESSAGE_VMBUS 0x00000001
|
|
||||||
#define HV_MESSAGE_UNMAPPED_GPA 0x80000000
|
|
||||||
#define HV_MESSAGE_GPA_INTERCEPT 0x80000001
|
|
||||||
#define HV_MESSAGE_TIMER_EXPIRED 0x80000010
|
|
||||||
#define HV_MESSAGE_INVALID_VP_REGISTER_VALUE 0x80000020
|
|
||||||
#define HV_MESSAGE_UNRECOVERABLE_EXCEPTION 0x80000021
|
|
||||||
#define HV_MESSAGE_UNSUPPORTED_FEATURE 0x80000022
|
|
||||||
#define HV_MESSAGE_EVENTLOG_BUFFERCOMPLETE 0x80000040
|
|
||||||
#define HV_MESSAGE_X64_IOPORT_INTERCEPT 0x80010000
|
|
||||||
#define HV_MESSAGE_X64_MSR_INTERCEPT 0x80010001
|
|
||||||
#define HV_MESSAGE_X64_CPUID_INTERCEPT 0x80010002
|
|
||||||
#define HV_MESSAGE_X64_EXCEPTION_INTERCEPT 0x80010003
|
|
||||||
#define HV_MESSAGE_X64_APIC_EOI 0x80010004
|
|
||||||
#define HV_MESSAGE_X64_LEGACY_FP_ERROR 0x80010005
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Message flags
|
|
||||||
*/
|
|
||||||
#define HV_MESSAGE_FLAG_PENDING 0x1
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event flags number per SINT
|
|
||||||
*/
|
|
||||||
#define HV_EVENT_FLAGS_COUNT (256 * 8)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Connection id valid bits
|
|
||||||
*/
|
|
||||||
#define HV_CONNECTION_ID_MASK 0x00ffffff
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Input structure for POST_MESSAGE hypercall
|
|
||||||
*/
|
|
||||||
struct hyperv_post_message_input {
|
|
||||||
uint32_t connection_id;
|
|
||||||
uint32_t _reserved;
|
|
||||||
uint32_t message_type;
|
|
||||||
uint32_t payload_size;
|
|
||||||
uint8_t payload[HV_MESSAGE_PAYLOAD_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Input structure for SIGNAL_EVENT hypercall
|
|
||||||
*/
|
|
||||||
struct hyperv_signal_event_input {
|
|
||||||
uint32_t connection_id;
|
|
||||||
uint16_t flag_number;
|
|
||||||
uint16_t _reserved_zero;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* SynIC message structures
|
|
||||||
*/
|
|
||||||
struct hyperv_message_header {
|
|
||||||
uint32_t message_type;
|
|
||||||
uint8_t payload_size;
|
|
||||||
uint8_t message_flags; /* HV_MESSAGE_FLAG_XX */
|
|
||||||
uint8_t _reserved[2];
|
|
||||||
uint64_t sender;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct hyperv_message {
|
|
||||||
struct hyperv_message_header header;
|
|
||||||
uint8_t payload[HV_MESSAGE_PAYLOAD_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct hyperv_message_page {
|
|
||||||
struct hyperv_message slot[HV_SINT_COUNT];
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* SynIC event flags structures
|
|
||||||
*/
|
|
||||||
struct hyperv_event_flags {
|
|
||||||
DECLARE_BITMAP(flags, HV_EVENT_FLAGS_COUNT);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct hyperv_event_flags_page {
|
|
||||||
struct hyperv_event_flags slot[HV_SINT_COUNT];
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Stubs for CONFIG_HYPERV=n
|
||||||
|
*
|
||||||
|
* Copyright (c) 2015-2018 Virtuozzo International GmbH.
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "hyperv.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM
|
||||||
|
int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit)
|
||||||
|
{
|
||||||
|
switch (exit->type) {
|
||||||
|
case KVM_EXIT_HYPERV_SYNIC:
|
||||||
|
if (!cpu->hyperv_synic) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tracking the changes in the MSRs is unnecessary as there are no
|
||||||
|
* users for them beside save/load, which is handled nicely by the
|
||||||
|
* generic MSR save/load code
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
case KVM_EXIT_HYPERV_HCALL:
|
||||||
|
exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE;
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int hyperv_x86_synic_add(X86CPU *cpu)
|
||||||
|
{
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hyperv_x86_synic_reset(X86CPU *cpu)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void hyperv_x86_synic_update(X86CPU *cpu)
|
||||||
|
{
|
||||||
|
}
|
|
@ -14,16 +14,36 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
#include "hyperv.h"
|
#include "hyperv.h"
|
||||||
|
#include "hw/hyperv/hyperv.h"
|
||||||
#include "hyperv-proto.h"
|
#include "hyperv-proto.h"
|
||||||
|
|
||||||
uint32_t hyperv_vp_index(X86CPU *cpu)
|
int hyperv_x86_synic_add(X86CPU *cpu)
|
||||||
{
|
{
|
||||||
return CPU(cpu)->cpu_index;
|
hyperv_synic_add(CPU(cpu));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
X86CPU *hyperv_find_vcpu(uint32_t vp_index)
|
void hyperv_x86_synic_reset(X86CPU *cpu)
|
||||||
{
|
{
|
||||||
return X86_CPU(qemu_get_cpu(vp_index));
|
hyperv_synic_reset(CPU(cpu));
|
||||||
|
}
|
||||||
|
|
||||||
|
void hyperv_x86_synic_update(X86CPU *cpu)
|
||||||
|
{
|
||||||
|
CPUX86State *env = &cpu->env;
|
||||||
|
bool enable = env->msr_hv_synic_control & HV_SYNIC_ENABLE;
|
||||||
|
hwaddr msg_page_addr = (env->msr_hv_synic_msg_page & HV_SIMP_ENABLE) ?
|
||||||
|
(env->msr_hv_synic_msg_page & TARGET_PAGE_MASK) : 0;
|
||||||
|
hwaddr event_page_addr = (env->msr_hv_synic_evt_page & HV_SIEFP_ENABLE) ?
|
||||||
|
(env->msr_hv_synic_evt_page & TARGET_PAGE_MASK) : 0;
|
||||||
|
hyperv_synic_update(CPU(cpu), enable, msg_page_addr, event_page_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void async_synic_update(CPUState *cs, run_on_cpu_data data)
|
||||||
|
{
|
||||||
|
qemu_mutex_lock_iothread();
|
||||||
|
hyperv_x86_synic_update(X86_CPU(cs));
|
||||||
|
qemu_mutex_unlock_iothread();
|
||||||
}
|
}
|
||||||
|
|
||||||
int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit)
|
int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit)
|
||||||
|
@ -36,11 +56,6 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* For now just track changes in SynIC control and msg/evt pages msr's.
|
|
||||||
* When SynIC messaging/events processing will be added in future
|
|
||||||
* here we will do messages queues flushing and pages remapping.
|
|
||||||
*/
|
|
||||||
switch (exit->u.synic.msr) {
|
switch (exit->u.synic.msr) {
|
||||||
case HV_X64_MSR_SCONTROL:
|
case HV_X64_MSR_SCONTROL:
|
||||||
env->msr_hv_synic_control = exit->u.synic.control;
|
env->msr_hv_synic_control = exit->u.synic.control;
|
||||||
|
@ -54,98 +69,33 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit)
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this will run in this cpu thread before it returns to KVM, but in a
|
||||||
|
* safe environment (i.e. when all cpus are quiescent) -- this is
|
||||||
|
* necessary because memory hierarchy is being changed
|
||||||
|
*/
|
||||||
|
async_safe_run_on_cpu(CPU(cpu), async_synic_update, RUN_ON_CPU_NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
case KVM_EXIT_HYPERV_HCALL: {
|
case KVM_EXIT_HYPERV_HCALL: {
|
||||||
uint16_t code;
|
uint16_t code = exit->u.hcall.input & 0xffff;
|
||||||
|
bool fast = exit->u.hcall.input & HV_HYPERCALL_FAST;
|
||||||
|
uint64_t param = exit->u.hcall.params[0];
|
||||||
|
|
||||||
code = exit->u.hcall.input & 0xffff;
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case HV_POST_MESSAGE:
|
case HV_POST_MESSAGE:
|
||||||
|
exit->u.hcall.result = hyperv_hcall_post_message(param, fast);
|
||||||
|
break;
|
||||||
case HV_SIGNAL_EVENT:
|
case HV_SIGNAL_EVENT:
|
||||||
|
exit->u.hcall.result = hyperv_hcall_signal_event(param, fast);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE;
|
exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kvm_hv_sint_ack_handler(EventNotifier *notifier)
|
|
||||||
{
|
|
||||||
HvSintRoute *sint_route = container_of(notifier, HvSintRoute,
|
|
||||||
sint_ack_notifier);
|
|
||||||
event_notifier_test_and_clear(notifier);
|
|
||||||
if (sint_route->sint_ack_clb) {
|
|
||||||
sint_route->sint_ack_clb(sint_route);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HvSintRoute *kvm_hv_sint_route_create(uint32_t vp_index, uint32_t sint,
|
|
||||||
HvSintAckClb sint_ack_clb)
|
|
||||||
{
|
|
||||||
HvSintRoute *sint_route;
|
|
||||||
int r, gsi;
|
|
||||||
|
|
||||||
sint_route = g_malloc0(sizeof(*sint_route));
|
|
||||||
r = event_notifier_init(&sint_route->sint_set_notifier, false);
|
|
||||||
if (r) {
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = event_notifier_init(&sint_route->sint_ack_notifier, false);
|
|
||||||
if (r) {
|
|
||||||
goto err_sint_set_notifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
event_notifier_set_handler(&sint_route->sint_ack_notifier,
|
|
||||||
kvm_hv_sint_ack_handler);
|
|
||||||
|
|
||||||
gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint);
|
|
||||||
if (gsi < 0) {
|
|
||||||
goto err_gsi;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state,
|
|
||||||
&sint_route->sint_set_notifier,
|
|
||||||
&sint_route->sint_ack_notifier, gsi);
|
|
||||||
if (r) {
|
|
||||||
goto err_irqfd;
|
|
||||||
}
|
|
||||||
sint_route->gsi = gsi;
|
|
||||||
sint_route->sint_ack_clb = sint_ack_clb;
|
|
||||||
sint_route->vp_index = vp_index;
|
|
||||||
sint_route->sint = sint;
|
|
||||||
|
|
||||||
return sint_route;
|
|
||||||
|
|
||||||
err_irqfd:
|
|
||||||
kvm_irqchip_release_virq(kvm_state, gsi);
|
|
||||||
err_gsi:
|
|
||||||
event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL);
|
|
||||||
event_notifier_cleanup(&sint_route->sint_ack_notifier);
|
|
||||||
err_sint_set_notifier:
|
|
||||||
event_notifier_cleanup(&sint_route->sint_set_notifier);
|
|
||||||
err:
|
|
||||||
g_free(sint_route);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void kvm_hv_sint_route_destroy(HvSintRoute *sint_route)
|
|
||||||
{
|
|
||||||
kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state,
|
|
||||||
&sint_route->sint_set_notifier,
|
|
||||||
sint_route->gsi);
|
|
||||||
kvm_irqchip_release_virq(kvm_state, sint_route->gsi);
|
|
||||||
event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL);
|
|
||||||
event_notifier_cleanup(&sint_route->sint_ack_notifier);
|
|
||||||
event_notifier_cleanup(&sint_route->sint_set_notifier);
|
|
||||||
g_free(sint_route);
|
|
||||||
}
|
|
||||||
|
|
||||||
int kvm_hv_sint_route_set_sint(HvSintRoute *sint_route)
|
|
||||||
{
|
|
||||||
return event_notifier_set(&sint_route->sint_set_notifier);
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,30 +16,14 @@
|
||||||
|
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
#include "qemu/event_notifier.h"
|
#include "hw/hyperv/hyperv.h"
|
||||||
|
|
||||||
typedef struct HvSintRoute HvSintRoute;
|
|
||||||
typedef void (*HvSintAckClb)(HvSintRoute *sint_route);
|
|
||||||
|
|
||||||
struct HvSintRoute {
|
|
||||||
uint32_t sint;
|
|
||||||
uint32_t vp_index;
|
|
||||||
int gsi;
|
|
||||||
EventNotifier sint_set_notifier;
|
|
||||||
EventNotifier sint_ack_notifier;
|
|
||||||
HvSintAckClb sint_ack_clb;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_KVM
|
||||||
int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit);
|
int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit);
|
||||||
|
#endif
|
||||||
|
|
||||||
HvSintRoute *kvm_hv_sint_route_create(uint32_t vp_index, uint32_t sint,
|
int hyperv_x86_synic_add(X86CPU *cpu);
|
||||||
HvSintAckClb sint_ack_clb);
|
void hyperv_x86_synic_reset(X86CPU *cpu);
|
||||||
|
void hyperv_x86_synic_update(X86CPU *cpu);
|
||||||
void kvm_hv_sint_route_destroy(HvSintRoute *sint_route);
|
|
||||||
|
|
||||||
int kvm_hv_sint_route_set_sint(HvSintRoute *sint_route);
|
|
||||||
|
|
||||||
uint32_t hyperv_vp_index(X86CPU *cpu);
|
|
||||||
X86CPU *hyperv_find_vcpu(uint32_t vp_index);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -608,7 +608,8 @@ static bool hyperv_enabled(X86CPU *cpu)
|
||||||
cpu->hyperv_synic ||
|
cpu->hyperv_synic ||
|
||||||
cpu->hyperv_stimer ||
|
cpu->hyperv_stimer ||
|
||||||
cpu->hyperv_reenlightenment ||
|
cpu->hyperv_reenlightenment ||
|
||||||
cpu->hyperv_tlbflush);
|
cpu->hyperv_tlbflush ||
|
||||||
|
cpu->hyperv_ipi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kvm_arch_set_tsc_khz(CPUState *cs)
|
static int kvm_arch_set_tsc_khz(CPUState *cs)
|
||||||
|
@ -733,9 +734,20 @@ static int hyperv_handle_properties(CPUState *cs)
|
||||||
env->features[FEAT_HYPERV_EAX] |= HV_VP_RUNTIME_AVAILABLE;
|
env->features[FEAT_HYPERV_EAX] |= HV_VP_RUNTIME_AVAILABLE;
|
||||||
}
|
}
|
||||||
if (cpu->hyperv_synic) {
|
if (cpu->hyperv_synic) {
|
||||||
if (!has_msr_hv_synic ||
|
unsigned int cap = KVM_CAP_HYPERV_SYNIC;
|
||||||
kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_SYNIC, 0)) {
|
if (!cpu->hyperv_synic_kvm_only) {
|
||||||
fprintf(stderr, "Hyper-V SynIC is not supported by kernel\n");
|
if (!cpu->hyperv_vpindex) {
|
||||||
|
fprintf(stderr, "Hyper-V SynIC "
|
||||||
|
"(requested by 'hv-synic' cpu flag) "
|
||||||
|
"requires Hyper-V VP_INDEX ('hv-vpindex')\n");
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
cap = KVM_CAP_HYPERV_SYNIC2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_msr_hv_synic || !kvm_check_extension(cs->kvm_state, cap)) {
|
||||||
|
fprintf(stderr, "Hyper-V SynIC (requested by 'hv-synic' cpu flag) "
|
||||||
|
"is not supported by kernel\n");
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,12 +765,14 @@ static int hyperv_handle_properties(CPUState *cs)
|
||||||
|
|
||||||
static int hyperv_init_vcpu(X86CPU *cpu)
|
static int hyperv_init_vcpu(X86CPU *cpu)
|
||||||
{
|
{
|
||||||
|
CPUState *cs = CPU(cpu);
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (cpu->hyperv_vpindex && !hv_vpindex_settable) {
|
if (cpu->hyperv_vpindex && !hv_vpindex_settable) {
|
||||||
/*
|
/*
|
||||||
* the kernel doesn't support setting vp_index; assert that its value
|
* the kernel doesn't support setting vp_index; assert that its value
|
||||||
* is in sync
|
* is in sync
|
||||||
*/
|
*/
|
||||||
int ret;
|
|
||||||
struct {
|
struct {
|
||||||
struct kvm_msrs info;
|
struct kvm_msrs info;
|
||||||
struct kvm_msr_entry entries[1];
|
struct kvm_msr_entry entries[1];
|
||||||
|
@ -767,18 +781,38 @@ static int hyperv_init_vcpu(X86CPU *cpu)
|
||||||
.entries[0].index = HV_X64_MSR_VP_INDEX,
|
.entries[0].index = HV_X64_MSR_VP_INDEX,
|
||||||
};
|
};
|
||||||
|
|
||||||
ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, &msr_data);
|
ret = kvm_vcpu_ioctl(cs, KVM_GET_MSRS, &msr_data);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
assert(ret == 1);
|
assert(ret == 1);
|
||||||
|
|
||||||
if (msr_data.entries[0].data != hyperv_vp_index(cpu)) {
|
if (msr_data.entries[0].data != hyperv_vp_index(CPU(cpu))) {
|
||||||
error_report("kernel's vp_index != QEMU's vp_index");
|
error_report("kernel's vp_index != QEMU's vp_index");
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cpu->hyperv_synic) {
|
||||||
|
uint32_t synic_cap = cpu->hyperv_synic_kvm_only ?
|
||||||
|
KVM_CAP_HYPERV_SYNIC : KVM_CAP_HYPERV_SYNIC2;
|
||||||
|
ret = kvm_vcpu_enable_cap(cs, synic_cap, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_report("failed to turn on HyperV SynIC in KVM: %s",
|
||||||
|
strerror(-ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cpu->hyperv_synic_kvm_only) {
|
||||||
|
ret = hyperv_x86_synic_add(cpu);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_report("failed to create HyperV SynIC: %s",
|
||||||
|
strerror(-ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -888,6 +922,17 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
||||||
c->eax |= HV_REMOTE_TLB_FLUSH_RECOMMENDED;
|
c->eax |= HV_REMOTE_TLB_FLUSH_RECOMMENDED;
|
||||||
c->eax |= HV_EX_PROCESSOR_MASKS_RECOMMENDED;
|
c->eax |= HV_EX_PROCESSOR_MASKS_RECOMMENDED;
|
||||||
}
|
}
|
||||||
|
if (cpu->hyperv_ipi) {
|
||||||
|
if (kvm_check_extension(cs->kvm_state,
|
||||||
|
KVM_CAP_HYPERV_SEND_IPI) <= 0) {
|
||||||
|
fprintf(stderr, "Hyper-V IPI send support "
|
||||||
|
"(requested by 'hv-ipi' cpu flag) "
|
||||||
|
" is not supported by kernel\n");
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
c->eax |= HV_CLUSTER_IPI_RECOMMENDED;
|
||||||
|
c->eax |= HV_EX_PROCESSOR_MASKS_RECOMMENDED;
|
||||||
|
}
|
||||||
|
|
||||||
c->ebx = cpu->hyperv_spinlock_attempts;
|
c->ebx = cpu->hyperv_spinlock_attempts;
|
||||||
|
|
||||||
|
@ -1153,7 +1198,7 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_report_err(local_err);
|
error_report_err(local_err);
|
||||||
error_free(invtsc_mig_blocker);
|
error_free(invtsc_mig_blocker);
|
||||||
goto fail;
|
return r;
|
||||||
}
|
}
|
||||||
/* for savevm */
|
/* for savevm */
|
||||||
vmstate_x86_cpu.unmigratable = 1;
|
vmstate_x86_cpu.unmigratable = 1;
|
||||||
|
@ -1226,6 +1271,8 @@ void kvm_arch_reset_vcpu(X86CPU *cpu)
|
||||||
for (i = 0; i < ARRAY_SIZE(env->msr_hv_synic_sint); i++) {
|
for (i = 0; i < ARRAY_SIZE(env->msr_hv_synic_sint); i++) {
|
||||||
env->msr_hv_synic_sint[i] = HV_SINT_MASKED;
|
env->msr_hv_synic_sint[i] = HV_SINT_MASKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hyperv_x86_synic_reset(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1937,7 +1984,8 @@ static int kvm_put_msrs(X86CPU *cpu, int level)
|
||||||
kvm_msr_entry_add(cpu, HV_X64_MSR_VP_RUNTIME, env->msr_hv_runtime);
|
kvm_msr_entry_add(cpu, HV_X64_MSR_VP_RUNTIME, env->msr_hv_runtime);
|
||||||
}
|
}
|
||||||
if (cpu->hyperv_vpindex && hv_vpindex_settable) {
|
if (cpu->hyperv_vpindex && hv_vpindex_settable) {
|
||||||
kvm_msr_entry_add(cpu, HV_X64_MSR_VP_INDEX, hyperv_vp_index(cpu));
|
kvm_msr_entry_add(cpu, HV_X64_MSR_VP_INDEX,
|
||||||
|
hyperv_vp_index(CPU(cpu)));
|
||||||
}
|
}
|
||||||
if (cpu->hyperv_synic) {
|
if (cpu->hyperv_synic) {
|
||||||
int j;
|
int j;
|
||||||
|
@ -2686,7 +2734,6 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level)
|
||||||
events.exception.nr = env->exception_injected;
|
events.exception.nr = env->exception_injected;
|
||||||
events.exception.has_error_code = env->has_error_code;
|
events.exception.has_error_code = env->has_error_code;
|
||||||
events.exception.error_code = env->error_code;
|
events.exception.error_code = env->error_code;
|
||||||
events.exception.pad = 0;
|
|
||||||
|
|
||||||
events.interrupt.injected = (env->interrupt_injected >= 0);
|
events.interrupt.injected = (env->interrupt_injected >= 0);
|
||||||
events.interrupt.nr = env->interrupt_injected;
|
events.interrupt.nr = env->interrupt_injected;
|
||||||
|
@ -2695,7 +2742,6 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level)
|
||||||
events.nmi.injected = env->nmi_injected;
|
events.nmi.injected = env->nmi_injected;
|
||||||
events.nmi.pending = env->nmi_pending;
|
events.nmi.pending = env->nmi_pending;
|
||||||
events.nmi.masked = !!(env->hflags2 & HF2_NMI_MASK);
|
events.nmi.masked = !!(env->hflags2 & HF2_NMI_MASK);
|
||||||
events.nmi.pad = 0;
|
|
||||||
|
|
||||||
events.sipi_vector = env->sipi_vector;
|
events.sipi_vector = env->sipi_vector;
|
||||||
events.flags = 0;
|
events.flags = 0;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "hw/i386/pc.h"
|
#include "hw/i386/pc.h"
|
||||||
#include "hw/isa/isa.h"
|
#include "hw/isa/isa.h"
|
||||||
#include "migration/cpu.h"
|
#include "migration/cpu.h"
|
||||||
|
#include "hyperv.h"
|
||||||
|
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
|
|
||||||
|
@ -672,11 +673,19 @@ static bool hyperv_synic_enable_needed(void *opaque)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hyperv_synic_post_load(void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
X86CPU *cpu = opaque;
|
||||||
|
hyperv_x86_synic_update(cpu);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const VMStateDescription vmstate_msr_hyperv_synic = {
|
static const VMStateDescription vmstate_msr_hyperv_synic = {
|
||||||
.name = "cpu/msr_hyperv_synic",
|
.name = "cpu/msr_hyperv_synic",
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.needed = hyperv_synic_enable_needed,
|
.needed = hyperv_synic_enable_needed,
|
||||||
|
.post_load = hyperv_synic_post_load,
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT64(env.msr_hv_synic_control, X86CPU),
|
VMSTATE_UINT64(env.msr_hv_synic_control, X86CPU),
|
||||||
VMSTATE_UINT64(env.msr_hv_synic_evt_page, X86CPU),
|
VMSTATE_UINT64(env.msr_hv_synic_evt_page, X86CPU),
|
||||||
|
|
|
@ -34,14 +34,19 @@ int64_t ptimer_test_time_ns;
|
||||||
int use_icount = 1;
|
int use_icount = 1;
|
||||||
bool qtest_allowed;
|
bool qtest_allowed;
|
||||||
|
|
||||||
void timer_init_tl(QEMUTimer *ts,
|
void timer_init_full(QEMUTimer *ts,
|
||||||
QEMUTimerList *timer_list, int scale,
|
QEMUTimerListGroup *timer_list_group, QEMUClockType type,
|
||||||
QEMUTimerCB *cb, void *opaque)
|
int scale, int attributes,
|
||||||
|
QEMUTimerCB *cb, void *opaque)
|
||||||
{
|
{
|
||||||
ts->timer_list = timer_list;
|
if (!timer_list_group) {
|
||||||
|
timer_list_group = &main_loop_tlg;
|
||||||
|
}
|
||||||
|
ts->timer_list = timer_list_group->tl[type];
|
||||||
ts->cb = cb;
|
ts->cb = cb;
|
||||||
ts->opaque = opaque;
|
ts->opaque = opaque;
|
||||||
ts->scale = scale;
|
ts->scale = scale;
|
||||||
|
ts->attributes = attributes;
|
||||||
ts->expire_time = -1;
|
ts->expire_time = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -271,7 +271,7 @@ static void qemu_input_queue_process(void *opaque)
|
||||||
item = QTAILQ_FIRST(queue);
|
item = QTAILQ_FIRST(queue);
|
||||||
switch (item->type) {
|
switch (item->type) {
|
||||||
case QEMU_INPUT_QUEUE_DELAY:
|
case QEMU_INPUT_QUEUE_DELAY:
|
||||||
timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT)
|
timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)
|
||||||
+ item->delay_ms);
|
+ item->delay_ms);
|
||||||
return;
|
return;
|
||||||
case QEMU_INPUT_QUEUE_EVENT:
|
case QEMU_INPUT_QUEUE_EVENT:
|
||||||
|
@ -301,7 +301,7 @@ static void qemu_input_queue_delay(struct QemuInputEventQueueHead *queue,
|
||||||
queue_count++;
|
queue_count++;
|
||||||
|
|
||||||
if (start_timer) {
|
if (start_timer) {
|
||||||
timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_EXT)
|
timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)
|
||||||
+ item->delay_ms);
|
+ item->delay_ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -448,8 +448,9 @@ void qemu_input_event_send_key_delay(uint32_t delay_ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!kbd_timer) {
|
if (!kbd_timer) {
|
||||||
kbd_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_EXT,
|
kbd_timer = timer_new_full(NULL, QEMU_CLOCK_VIRTUAL,
|
||||||
qemu_input_queue_process, &kbd_queue);
|
SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
|
||||||
|
qemu_input_queue_process, &kbd_queue);
|
||||||
}
|
}
|
||||||
if (queue_count < queue_limit) {
|
if (queue_count < queue_limit) {
|
||||||
qemu_input_queue_delay(&kbd_queue, kbd_timer,
|
qemu_input_queue_delay(&kbd_queue, kbd_timer,
|
||||||
|
|
|
@ -339,14 +339,19 @@ int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void timer_init_tl(QEMUTimer *ts,
|
void timer_init_full(QEMUTimer *ts,
|
||||||
QEMUTimerList *timer_list, int scale,
|
QEMUTimerListGroup *timer_list_group, QEMUClockType type,
|
||||||
QEMUTimerCB *cb, void *opaque)
|
int scale, int attributes,
|
||||||
|
QEMUTimerCB *cb, void *opaque)
|
||||||
{
|
{
|
||||||
ts->timer_list = timer_list;
|
if (!timer_list_group) {
|
||||||
|
timer_list_group = &main_loop_tlg;
|
||||||
|
}
|
||||||
|
ts->timer_list = timer_list_group->tl[type];
|
||||||
ts->cb = cb;
|
ts->cb = cb;
|
||||||
ts->opaque = opaque;
|
ts->opaque = opaque;
|
||||||
ts->scale = scale;
|
ts->scale = scale;
|
||||||
|
ts->attributes = attributes;
|
||||||
ts->expire_time = -1;
|
ts->expire_time = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,6 +489,7 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
|
||||||
bool progress = false;
|
bool progress = false;
|
||||||
QEMUTimerCB *cb;
|
QEMUTimerCB *cb;
|
||||||
void *opaque;
|
void *opaque;
|
||||||
|
bool need_replay_checkpoint = false;
|
||||||
|
|
||||||
if (!atomic_read(&timer_list->active_timers)) {
|
if (!atomic_read(&timer_list->active_timers)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -496,12 +502,18 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
|
||||||
|
|
||||||
switch (timer_list->clock->type) {
|
switch (timer_list->clock->type) {
|
||||||
case QEMU_CLOCK_REALTIME:
|
case QEMU_CLOCK_REALTIME:
|
||||||
case QEMU_CLOCK_VIRTUAL_EXT:
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case QEMU_CLOCK_VIRTUAL:
|
case QEMU_CLOCK_VIRTUAL:
|
||||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) {
|
if (replay_mode != REPLAY_MODE_NONE) {
|
||||||
goto out;
|
/* Checkpoint for virtual clock is redundant in cases where
|
||||||
|
* it's being triggered with only non-EXTERNAL timers, because
|
||||||
|
* these timers don't change guest state directly.
|
||||||
|
* Since it has conditional dependence on specific timers, it is
|
||||||
|
* subject to race conditions and requires special handling.
|
||||||
|
* See below.
|
||||||
|
*/
|
||||||
|
need_replay_checkpoint = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QEMU_CLOCK_HOST:
|
case QEMU_CLOCK_HOST:
|
||||||
|
@ -516,14 +528,39 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract expired timers from active timers list and and process them.
|
||||||
|
*
|
||||||
|
* In rr mode we need "filtered" checkpointing for virtual clock. The
|
||||||
|
* checkpoint must be recorded/replayed before processing any non-EXTERNAL timer,
|
||||||
|
* and that must only be done once since the clock value stays the same. Because
|
||||||
|
* non-EXTERNAL timers may appear in the timers list while it being processed,
|
||||||
|
* the checkpoint can be issued at a time until no timers are left and we are
|
||||||
|
* done".
|
||||||
|
*/
|
||||||
current_time = qemu_clock_get_ns(timer_list->clock->type);
|
current_time = qemu_clock_get_ns(timer_list->clock->type);
|
||||||
for(;;) {
|
qemu_mutex_lock(&timer_list->active_timers_lock);
|
||||||
qemu_mutex_lock(&timer_list->active_timers_lock);
|
while ((ts = timer_list->active_timers)) {
|
||||||
ts = timer_list->active_timers;
|
|
||||||
if (!timer_expired_ns(ts, current_time)) {
|
if (!timer_expired_ns(ts, current_time)) {
|
||||||
qemu_mutex_unlock(&timer_list->active_timers_lock);
|
/* No expired timers left. The checkpoint can be skipped
|
||||||
|
* if no timers fired or they were all external.
|
||||||
|
*/
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (need_replay_checkpoint
|
||||||
|
&& !(ts->attributes & QEMU_TIMER_ATTR_EXTERNAL)) {
|
||||||
|
/* once we got here, checkpoint clock only once */
|
||||||
|
need_replay_checkpoint = false;
|
||||||
|
qemu_mutex_unlock(&timer_list->active_timers_lock);
|
||||||
|
if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
qemu_mutex_lock(&timer_list->active_timers_lock);
|
||||||
|
/* The lock was released; start over again in case the list was
|
||||||
|
* modified.
|
||||||
|
*/
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* remove timer from the list before calling the callback */
|
/* remove timer from the list before calling the callback */
|
||||||
timer_list->active_timers = ts->next;
|
timer_list->active_timers = ts->next;
|
||||||
|
@ -531,12 +568,15 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
|
||||||
ts->expire_time = -1;
|
ts->expire_time = -1;
|
||||||
cb = ts->cb;
|
cb = ts->cb;
|
||||||
opaque = ts->opaque;
|
opaque = ts->opaque;
|
||||||
qemu_mutex_unlock(&timer_list->active_timers_lock);
|
|
||||||
|
|
||||||
/* run the callback (the timer list can be modified) */
|
/* run the callback (the timer list can be modified) */
|
||||||
|
qemu_mutex_unlock(&timer_list->active_timers_lock);
|
||||||
cb(opaque);
|
cb(opaque);
|
||||||
|
qemu_mutex_lock(&timer_list->active_timers_lock);
|
||||||
|
|
||||||
progress = true;
|
progress = true;
|
||||||
}
|
}
|
||||||
|
qemu_mutex_unlock(&timer_list->active_timers_lock);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
qemu_event_set(&timer_list->timers_done_ev);
|
qemu_event_set(&timer_list->timers_done_ev);
|
||||||
|
@ -598,7 +638,6 @@ int64_t qemu_clock_get_ns(QEMUClockType type)
|
||||||
return get_clock();
|
return get_clock();
|
||||||
default:
|
default:
|
||||||
case QEMU_CLOCK_VIRTUAL:
|
case QEMU_CLOCK_VIRTUAL:
|
||||||
case QEMU_CLOCK_VIRTUAL_EXT:
|
|
||||||
if (use_icount) {
|
if (use_icount) {
|
||||||
return cpu_get_icount();
|
return cpu_get_icount();
|
||||||
} else {
|
} else {
|
||||||
|
|
110
vl.c
110
vl.c
|
@ -147,8 +147,15 @@ bool enable_cpu_pm = false;
|
||||||
int nb_nics;
|
int nb_nics;
|
||||||
NICInfo nd_table[MAX_NICS];
|
NICInfo nd_table[MAX_NICS];
|
||||||
int autostart;
|
int autostart;
|
||||||
static int rtc_utc = 1;
|
static enum {
|
||||||
static int rtc_date_offset = -1; /* -1 means no change */
|
RTC_BASE_UTC,
|
||||||
|
RTC_BASE_LOCALTIME,
|
||||||
|
RTC_BASE_DATETIME,
|
||||||
|
} rtc_base_type = RTC_BASE_UTC;
|
||||||
|
static time_t rtc_ref_start_datetime;
|
||||||
|
static int rtc_realtime_clock_offset; /* used only with QEMU_CLOCK_REALTIME */
|
||||||
|
static int rtc_host_datetime_offset = -1; /* valid & used only with
|
||||||
|
RTC_BASE_DATETIME */
|
||||||
QEMUClockType rtc_clock;
|
QEMUClockType rtc_clock;
|
||||||
int vga_interface_type = VGA_NONE;
|
int vga_interface_type = VGA_NONE;
|
||||||
static DisplayOptions dpy;
|
static DisplayOptions dpy;
|
||||||
|
@ -242,6 +249,7 @@ static struct {
|
||||||
static QemuOptsList qemu_rtc_opts = {
|
static QemuOptsList qemu_rtc_opts = {
|
||||||
.name = "rtc",
|
.name = "rtc",
|
||||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_rtc_opts.head),
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_rtc_opts.head),
|
||||||
|
.merge_lists = true,
|
||||||
.desc = {
|
.desc = {
|
||||||
{
|
{
|
||||||
.name = "base",
|
.name = "base",
|
||||||
|
@ -780,28 +788,42 @@ void qemu_system_vmstop_request(RunState state)
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
/* real time host monotonic timer */
|
/* RTC reference time/date access */
|
||||||
|
static time_t qemu_ref_timedate(QEMUClockType clock)
|
||||||
static time_t qemu_time(void)
|
|
||||||
{
|
{
|
||||||
return qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
|
time_t value = qemu_clock_get_ms(clock) / 1000;
|
||||||
|
switch (clock) {
|
||||||
|
case QEMU_CLOCK_REALTIME:
|
||||||
|
value -= rtc_realtime_clock_offset;
|
||||||
|
/* no break */
|
||||||
|
case QEMU_CLOCK_VIRTUAL:
|
||||||
|
value += rtc_ref_start_datetime;
|
||||||
|
break;
|
||||||
|
case QEMU_CLOCK_HOST:
|
||||||
|
if (rtc_base_type == RTC_BASE_DATETIME) {
|
||||||
|
value -= rtc_host_datetime_offset;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************/
|
|
||||||
/* host time/date access */
|
|
||||||
void qemu_get_timedate(struct tm *tm, int offset)
|
void qemu_get_timedate(struct tm *tm, int offset)
|
||||||
{
|
{
|
||||||
time_t ti = qemu_time();
|
time_t ti = qemu_ref_timedate(rtc_clock);
|
||||||
|
|
||||||
ti += offset;
|
ti += offset;
|
||||||
if (rtc_date_offset == -1) {
|
|
||||||
if (rtc_utc)
|
switch (rtc_base_type) {
|
||||||
gmtime_r(&ti, tm);
|
case RTC_BASE_DATETIME:
|
||||||
else
|
case RTC_BASE_UTC:
|
||||||
localtime_r(&ti, tm);
|
|
||||||
} else {
|
|
||||||
ti -= rtc_date_offset;
|
|
||||||
gmtime_r(&ti, tm);
|
gmtime_r(&ti, tm);
|
||||||
|
break;
|
||||||
|
case RTC_BASE_LOCALTIME:
|
||||||
|
localtime_r(&ti, tm);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -809,23 +831,28 @@ int qemu_timedate_diff(struct tm *tm)
|
||||||
{
|
{
|
||||||
time_t seconds;
|
time_t seconds;
|
||||||
|
|
||||||
if (rtc_date_offset == -1)
|
switch (rtc_base_type) {
|
||||||
if (rtc_utc)
|
case RTC_BASE_DATETIME:
|
||||||
seconds = mktimegm(tm);
|
case RTC_BASE_UTC:
|
||||||
else {
|
seconds = mktimegm(tm);
|
||||||
struct tm tmp = *tm;
|
break;
|
||||||
tmp.tm_isdst = -1; /* use timezone to figure it out */
|
case RTC_BASE_LOCALTIME:
|
||||||
seconds = mktime(&tmp);
|
{
|
||||||
}
|
struct tm tmp = *tm;
|
||||||
else
|
tmp.tm_isdst = -1; /* use timezone to figure it out */
|
||||||
seconds = mktimegm(tm) + rtc_date_offset;
|
seconds = mktime(&tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
return seconds - qemu_time();
|
return seconds - qemu_ref_timedate(QEMU_CLOCK_HOST);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void configure_rtc_date_offset(const char *startdate)
|
static void configure_rtc_base_datetime(const char *startdate)
|
||||||
{
|
{
|
||||||
time_t rtc_start_date;
|
time_t rtc_start_datetime;
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
|
|
||||||
if (sscanf(startdate, "%d-%d-%dT%d:%d:%d", &tm.tm_year, &tm.tm_mon,
|
if (sscanf(startdate, "%d-%d-%dT%d:%d:%d", &tm.tm_year, &tm.tm_mon,
|
||||||
|
@ -841,33 +868,40 @@ static void configure_rtc_date_offset(const char *startdate)
|
||||||
}
|
}
|
||||||
tm.tm_year -= 1900;
|
tm.tm_year -= 1900;
|
||||||
tm.tm_mon--;
|
tm.tm_mon--;
|
||||||
rtc_start_date = mktimegm(&tm);
|
rtc_start_datetime = mktimegm(&tm);
|
||||||
if (rtc_start_date == -1) {
|
if (rtc_start_datetime == -1) {
|
||||||
date_fail:
|
date_fail:
|
||||||
error_report("invalid date format");
|
error_report("invalid datetime format");
|
||||||
error_printf("valid formats: "
|
error_printf("valid formats: "
|
||||||
"'2006-06-17T16:01:21' or '2006-06-17'\n");
|
"'2006-06-17T16:01:21' or '2006-06-17'\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
rtc_date_offset = qemu_time() - rtc_start_date;
|
rtc_host_datetime_offset = rtc_ref_start_datetime - rtc_start_datetime;
|
||||||
|
rtc_ref_start_datetime = rtc_start_datetime;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void configure_rtc(QemuOpts *opts)
|
static void configure_rtc(QemuOpts *opts)
|
||||||
{
|
{
|
||||||
const char *value;
|
const char *value;
|
||||||
|
|
||||||
|
/* Set defaults */
|
||||||
|
rtc_clock = QEMU_CLOCK_HOST;
|
||||||
|
rtc_ref_start_datetime = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
|
||||||
|
rtc_realtime_clock_offset = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
|
||||||
|
|
||||||
value = qemu_opt_get(opts, "base");
|
value = qemu_opt_get(opts, "base");
|
||||||
if (value) {
|
if (value) {
|
||||||
if (!strcmp(value, "utc")) {
|
if (!strcmp(value, "utc")) {
|
||||||
rtc_utc = 1;
|
rtc_base_type = RTC_BASE_UTC;
|
||||||
} else if (!strcmp(value, "localtime")) {
|
} else if (!strcmp(value, "localtime")) {
|
||||||
Error *blocker = NULL;
|
Error *blocker = NULL;
|
||||||
rtc_utc = 0;
|
rtc_base_type = RTC_BASE_LOCALTIME;
|
||||||
error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED,
|
error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED,
|
||||||
"-rtc base=localtime");
|
"-rtc base=localtime");
|
||||||
replay_add_blocker(blocker);
|
replay_add_blocker(blocker);
|
||||||
} else {
|
} else {
|
||||||
configure_rtc_date_offset(value);
|
rtc_base_type = RTC_BASE_DATETIME;
|
||||||
|
configure_rtc_base_datetime(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value = qemu_opt_get(opts, "clock");
|
value = qemu_opt_get(opts, "clock");
|
||||||
|
@ -3017,7 +3051,6 @@ int main(int argc, char **argv, char **envp)
|
||||||
error_reportf_err(err, "cannot initialize crypto: ");
|
error_reportf_err(err, "cannot initialize crypto: ");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
rtc_clock = QEMU_CLOCK_HOST;
|
|
||||||
|
|
||||||
QLIST_INIT (&vm_change_state_head);
|
QLIST_INIT (&vm_change_state_head);
|
||||||
os_setup_early_signal_handling();
|
os_setup_early_signal_handling();
|
||||||
|
@ -3737,7 +3770,6 @@ int main(int argc, char **argv, char **envp)
|
||||||
if (!opts) {
|
if (!opts) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
configure_rtc(opts);
|
|
||||||
break;
|
break;
|
||||||
case QEMU_OPTION_tb_size:
|
case QEMU_OPTION_tb_size:
|
||||||
#ifndef CONFIG_TCG
|
#ifndef CONFIG_TCG
|
||||||
|
@ -3955,6 +3987,8 @@ int main(int argc, char **argv, char **envp)
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configure_rtc(qemu_find_opts_singleton("rtc"));
|
||||||
|
|
||||||
machine_class = select_machine();
|
machine_class = select_machine();
|
||||||
|
|
||||||
set_memory_options(&ram_slots, &maxram_size, machine_class);
|
set_memory_options(&ram_slots, &maxram_size, machine_class);
|
||||||
|
|
Loading…
Reference in New Issue