mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* bugfixes * show machine ACPI support in QAPI * Core Xen emulation support for KVM/x86 # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmQAlrYUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroONWwf/fxDUMcZUvvatNxiVMhNfqEt/cL0F # Durv1PmbbeVh9PP0W7XFkEXO3LCIRDyR4rtmCs7gHGdmzDOWQ+QIWgQijQ/y7ElQ # bTVsvs0+s/6H3csP3dJTJaXSHshbQvrAZTsyk5KcAB6xdL1KqulfLUoGvXJhAmRs # NKZN8un+nuAhFhL0VBWA9eQaP+BVHQI5ItAj8PaoBby4+Q9fNnat6j1/G4iLly8J # dxIwCnuRHLiB3melWtadwbv6ddLJFeZNa50HUIsynqoItTzmRVr+oXz1yfq087dB # 9uksmoqb+icGEdwqs0iYbQ/dhVnIrMDpn/n2Us28S5VdIMVvxr1JEbEkSQ== # =0jY8 # -----END PGP SIGNATURE----- # gpg: Signature made Thu 02 Mar 2023 12:29:42 GMT # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] # 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 * tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (62 commits) Makefile: qemu-bundle is a directory qapi: Add 'acpi' field to 'query-machines' output hw/xen: Subsume xen_be_register_common() into xen_be_init() i386/xen: Document Xen HVM emulation kvm/i386: Add xen-evtchn-max-pirq property hw/xen: Support MSI mapping to PIRQ hw/xen: Support GSI mapping to PIRQ hw/xen: Implement emulated PIRQ hypercall support i386/xen: Implement HYPERVISOR_physdev_op hw/xen: Automatically add xen-platform PCI device for emulated Xen guests hw/xen: Add basic ring handling to xenstore hw/xen: Add xen_xenstore device for xenstore emulation hw/xen: Add backend implementation of interdomain event channel support i386/xen: handle HVMOP_get_param i386/xen: Reserve Xen special pages for console, xenstore rings i386/xen: handle PV timer hypercalls hw/xen: Implement GNTTABOP_query_size i386/xen: Implement HYPERVISOR_grant_table_op and GNTTABOP_[gs]et_verson hw/xen: Support mapping grant frames hw/xen: Add xen_gnttab device for grant table emulation ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -220,7 +220,7 @@ qemu-%.tar.bz2:
|
||||
|
||||
distclean: clean recurse-distclean
|
||||
-$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || :
|
||||
rm -f config-host.mak Makefile.prereqs qemu-bundle
|
||||
rm -f config-host.mak Makefile.prereqs
|
||||
rm -f tests/tcg/*/config-target.mak tests/tcg/config-host.mak
|
||||
rm -f config.status
|
||||
rm -f roms/seabios/config.mak
|
||||
@@ -230,7 +230,7 @@ distclean: clean recurse-distclean
|
||||
rm -f Makefile.ninja Makefile.mtest build.ninja.stamp meson.stamp
|
||||
rm -f config.log
|
||||
rm -f linux-headers/asm
|
||||
rm -Rf .sdk
|
||||
rm -Rf .sdk qemu-bundle
|
||||
|
||||
find-src-path = find "$(SRC_PATH)" -path "$(SRC_PATH)/meson" -prune -o \
|
||||
-type l -prune -o \( -name "*.[chsS]" -o -name "*.[ch].inc" \)
|
||||
|
||||
@@ -3703,6 +3703,9 @@ static void kvm_accel_instance_init(Object *obj)
|
||||
s->kvm_dirty_ring_size = 0;
|
||||
s->notify_vmexit = NOTIFY_VMEXIT_OPTION_RUN;
|
||||
s->notify_window = 0;
|
||||
s->xen_version = 0;
|
||||
s->xen_gnttab_max_frames = 64;
|
||||
s->xen_evtchn_max_pirq = 256;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -171,6 +171,8 @@ static int xen_init(MachineState *ms)
|
||||
* opt out of system RAM being allocated by generic code
|
||||
*/
|
||||
mc->default_ram_id = NULL;
|
||||
|
||||
xen_mode = XEN_ATTACH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
Xen HVM guest support
|
||||
=====================
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
KVM has support for hosting Xen guests, intercepting Xen hypercalls and event
|
||||
channel (Xen PV interrupt) delivery. This allows guests which expect to be
|
||||
run under Xen to be hosted in QEMU under Linux/KVM instead.
|
||||
|
||||
Setup
|
||||
-----
|
||||
|
||||
Xen mode is enabled by setting the ``xen-version`` property of the KVM
|
||||
accelerator, for example for Xen 4.10:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
|qemu_system| --accel kvm,xen-version=0x4000a
|
||||
|
||||
Additionally, virtual APIC support can be advertised to the guest through the
|
||||
``xen-vapic`` CPU flag:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
|qemu_system| --accel kvm,xen-version=0x4000a --cpu host,+xen_vapic
|
||||
|
||||
When Xen support is enabled, QEMU changes hypervisor identification (CPUID
|
||||
0x40000000..0x4000000A) to Xen. The KVM identification and features are not
|
||||
advertised to a Xen guest. If Hyper-V is also enabled, the Xen identification
|
||||
moves to leaves 0x40000100..0x4000010A.
|
||||
|
||||
The Xen platform device is enabled automatically for a Xen guest. This allows
|
||||
a guest to unplug all emulated devices, in order to use Xen PV block and network
|
||||
drivers instead. Note that until the Xen PV device back ends are enabled to work
|
||||
with Xen mode in QEMU, that is unlikely to cause significant joy. Linux guests
|
||||
can be dissuaded from this by adding 'xen_emul_unplug=never' on their command
|
||||
line, and it can also be noted that AHCI disk controllers are exempt from being
|
||||
unplugged, as are passthrough VFIO PCI devices.
|
||||
|
||||
Properties
|
||||
----------
|
||||
|
||||
The following properties exist on the KVM accelerator object:
|
||||
|
||||
``xen-version``
|
||||
This property contains the Xen version in ``XENVER_version`` form, with the
|
||||
major version in the top 16 bits and the minor version in the low 16 bits.
|
||||
Setting this property enables the Xen guest support.
|
||||
|
||||
``xen-evtchn-max-pirq``
|
||||
Xen PIRQs represent an emulated physical interrupt, either GSI or MSI, which
|
||||
can be routed to an event channel instead of to the emulated I/O or local
|
||||
APIC. By default, QEMU permits only 256 PIRQs because this allows maximum
|
||||
compatibility with 32-bit MSI where the higher bits of the PIRQ# would need
|
||||
to be in the upper 64 bits of the MSI message. For guests with large numbers
|
||||
of PCI devices (and none which are limited to 32-bit addressing) it may be
|
||||
desirable to increase this value.
|
||||
|
||||
``xen-gnttab-max-frames``
|
||||
Xen grant tables are the means by which a Xen guest grants access to its
|
||||
memory for PV back ends (disk, network, etc.). Since QEMU only supports v1
|
||||
grant tables which are 8 bytes in size, each page (each frame) of the grant
|
||||
table can reference 512 pages of guest memory. The default number of frames
|
||||
is 64, allowing for 32768 pages of guest memory to be accessed by PV backends
|
||||
through simultaneous grants. For guests with large numbers of PV devices and
|
||||
high throughput, it may be desirable to increase this value.
|
||||
|
||||
OS requirements
|
||||
---------------
|
||||
|
||||
The minimal Xen support in the KVM accelerator requires the host to be running
|
||||
Linux v5.12 or newer. Later versions add optimisations: Linux v5.17 added
|
||||
acceleration of interrupt delivery via the Xen PIRQ mechanism, and Linux v5.19
|
||||
accelerated Xen PV timers and inter-processor interrupts (IPIs).
|
||||
@@ -27,6 +27,7 @@ Architectural features
|
||||
|
||||
i386/cpu
|
||||
i386/hyperv
|
||||
i386/xen
|
||||
i386/kvm-pv
|
||||
i386/sgx
|
||||
i386/amd-memory-encryption
|
||||
|
||||
@@ -1815,3 +1815,32 @@ SRST
|
||||
Dump the FDT in dtb format to *filename*.
|
||||
ERST
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_XEN_EMU)
|
||||
{
|
||||
.name = "xen-event-inject",
|
||||
.args_type = "port:i",
|
||||
.params = "port",
|
||||
.help = "inject event channel",
|
||||
.cmd = hmp_xen_event_inject,
|
||||
},
|
||||
|
||||
SRST
|
||||
``xen-event-inject`` *port*
|
||||
Notify guest via event channel on port *port*.
|
||||
ERST
|
||||
|
||||
|
||||
{
|
||||
.name = "xen-event-list",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.help = "list event channel state",
|
||||
.cmd = hmp_xen_event_list,
|
||||
},
|
||||
|
||||
SRST
|
||||
``xen-event-list``
|
||||
List event channels in the guest
|
||||
ERST
|
||||
#endif
|
||||
|
||||
@@ -41,6 +41,7 @@ source tpm/Kconfig
|
||||
source usb/Kconfig
|
||||
source virtio/Kconfig
|
||||
source vfio/Kconfig
|
||||
source xen/Kconfig
|
||||
source watchdog/Kconfig
|
||||
|
||||
# arch Kconfig
|
||||
|
||||
@@ -102,6 +102,7 @@ MachineInfoList *qmp_query_machines(Error **errp)
|
||||
info->hotpluggable_cpus = mc->has_hotpluggable_cpus;
|
||||
info->numa_mem_supported = mc->numa_mem_supported;
|
||||
info->deprecated = !!mc->deprecation_reason;
|
||||
info->acpi = !!object_class_property_find(OBJECT_CLASS(mc), "acpi");
|
||||
if (mc->default_cpu_type) {
|
||||
info->default_cpu_type = g_strdup(mc->default_cpu_type);
|
||||
}
|
||||
|
||||
@@ -136,3 +136,8 @@ config VMPORT
|
||||
config VMMOUSE
|
||||
bool
|
||||
depends on VMPORT
|
||||
|
||||
config XEN_EMU
|
||||
bool
|
||||
default y
|
||||
depends on KVM && (I386 || X86_64)
|
||||
|
||||
@@ -4,5 +4,18 @@ i386_kvm_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c'))
|
||||
i386_kvm_ss.add(when: 'CONFIG_I8254', if_true: files('i8254.c'))
|
||||
i386_kvm_ss.add(when: 'CONFIG_I8259', if_true: files('i8259.c'))
|
||||
i386_kvm_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c'))
|
||||
i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files(
|
||||
'xen_overlay.c',
|
||||
'xen_evtchn.c',
|
||||
'xen_gnttab.c',
|
||||
'xen_xenstore.c',
|
||||
))
|
||||
|
||||
i386_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss)
|
||||
|
||||
xen_stubs_ss = ss.source_set()
|
||||
xen_stubs_ss.add(when: 'CONFIG_XEN_EMU', if_false: files(
|
||||
'xen-stubs.c',
|
||||
))
|
||||
|
||||
specific_ss.add_all(when: 'CONFIG_SOFTMMU', if_true: xen_stubs_ss)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
kvm_xen_map_pirq(int pirq, int gsi) "pirq %d gsi %d"
|
||||
kvm_xen_unmap_pirq(int pirq, int gsi) "pirq %d gsi %d"
|
||||
kvm_xen_get_free_pirq(int pirq, int type) "pirq %d type %d"
|
||||
kvm_xen_bind_pirq(int pirq, int port) "pirq %d port %d"
|
||||
kvm_xen_unmask_pirq(int pirq, char *dev, int vector) "pirq %d dev %s vector %d"
|
||||
@@ -0,0 +1 @@
|
||||
#include "trace/trace-hw_i386_kvm.h"
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* QEMU Xen emulation: QMP stubs
|
||||
*
|
||||
* Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Authors: David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* 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 "qapi/error.h"
|
||||
#include "qapi/qapi-commands-misc-target.h"
|
||||
|
||||
#include "xen_evtchn.h"
|
||||
|
||||
void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector,
|
||||
uint64_t addr, uint32_t data, bool is_masked)
|
||||
{
|
||||
}
|
||||
|
||||
void xen_evtchn_remove_pci_device(PCIDevice *dev)
|
||||
{
|
||||
}
|
||||
|
||||
bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef TARGET_I386
|
||||
EvtchnInfoList *qmp_xen_event_list(Error **errp)
|
||||
{
|
||||
error_setg(errp, "Xen event channel emulation not enabled");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void qmp_xen_event_inject(uint32_t port, Error **errp)
|
||||
{
|
||||
error_setg(errp, "Xen event channel emulation not enabled");
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* QEMU Xen emulation: Event channel support
|
||||
*
|
||||
* Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Authors: David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* 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 QEMU_XEN_EVTCHN_H
|
||||
#define QEMU_XEN_EVTCHN_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
typedef uint32_t evtchn_port_t;
|
||||
|
||||
void xen_evtchn_create(void);
|
||||
int xen_evtchn_soft_reset(void);
|
||||
int xen_evtchn_set_callback_param(uint64_t param);
|
||||
void xen_evtchn_connect_gsis(qemu_irq *system_gsis);
|
||||
void xen_evtchn_set_callback_level(int level);
|
||||
|
||||
int xen_evtchn_set_port(uint16_t port);
|
||||
|
||||
bool xen_evtchn_set_gsi(int gsi, int level);
|
||||
void xen_evtchn_snoop_msi(PCIDevice *dev, bool is_msix, unsigned int vector,
|
||||
uint64_t addr, uint32_t data, bool is_masked);
|
||||
void xen_evtchn_remove_pci_device(PCIDevice *dev);
|
||||
struct kvm_irq_routing_entry;
|
||||
int xen_evtchn_translate_pirq_msi(struct kvm_irq_routing_entry *route,
|
||||
uint64_t address, uint32_t data);
|
||||
bool xen_evtchn_deliver_pirq_msi(uint64_t address, uint32_t data);
|
||||
|
||||
|
||||
/*
|
||||
* These functions mirror the libxenevtchn library API, providing the QEMU
|
||||
* backend side of "interdomain" event channels.
|
||||
*/
|
||||
struct xenevtchn_handle;
|
||||
struct xenevtchn_handle *xen_be_evtchn_open(void);
|
||||
int xen_be_evtchn_bind_interdomain(struct xenevtchn_handle *xc, uint32_t domid,
|
||||
evtchn_port_t guest_port);
|
||||
int xen_be_evtchn_unbind(struct xenevtchn_handle *xc, evtchn_port_t port);
|
||||
int xen_be_evtchn_close(struct xenevtchn_handle *xc);
|
||||
int xen_be_evtchn_fd(struct xenevtchn_handle *xc);
|
||||
int xen_be_evtchn_notify(struct xenevtchn_handle *xc, evtchn_port_t port);
|
||||
int xen_be_evtchn_unmask(struct xenevtchn_handle *xc, evtchn_port_t port);
|
||||
int xen_be_evtchn_pending(struct xenevtchn_handle *xc);
|
||||
/* Apart from this which is a local addition */
|
||||
int xen_be_evtchn_get_guest_port(struct xenevtchn_handle *xc);
|
||||
|
||||
struct evtchn_status;
|
||||
struct evtchn_close;
|
||||
struct evtchn_unmask;
|
||||
struct evtchn_bind_virq;
|
||||
struct evtchn_bind_pirq;
|
||||
struct evtchn_bind_ipi;
|
||||
struct evtchn_send;
|
||||
struct evtchn_alloc_unbound;
|
||||
struct evtchn_bind_interdomain;
|
||||
struct evtchn_bind_vcpu;
|
||||
struct evtchn_reset;
|
||||
int xen_evtchn_status_op(struct evtchn_status *status);
|
||||
int xen_evtchn_close_op(struct evtchn_close *close);
|
||||
int xen_evtchn_unmask_op(struct evtchn_unmask *unmask);
|
||||
int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq);
|
||||
int xen_evtchn_bind_pirq_op(struct evtchn_bind_pirq *pirq);
|
||||
int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi);
|
||||
int xen_evtchn_send_op(struct evtchn_send *send);
|
||||
int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc);
|
||||
int xen_evtchn_bind_interdomain_op(struct evtchn_bind_interdomain *interdomain);
|
||||
int xen_evtchn_bind_vcpu_op(struct evtchn_bind_vcpu *vcpu);
|
||||
int xen_evtchn_reset_op(struct evtchn_reset *reset);
|
||||
|
||||
struct physdev_map_pirq;
|
||||
struct physdev_unmap_pirq;
|
||||
struct physdev_eoi;
|
||||
struct physdev_irq_status_query;
|
||||
struct physdev_get_free_pirq;
|
||||
int xen_physdev_map_pirq(struct physdev_map_pirq *map);
|
||||
int xen_physdev_unmap_pirq(struct physdev_unmap_pirq *unmap);
|
||||
int xen_physdev_eoi_pirq(struct physdev_eoi *eoi);
|
||||
int xen_physdev_query_pirq(struct physdev_irq_status_query *query);
|
||||
int xen_physdev_get_free_pirq(struct physdev_get_free_pirq *get);
|
||||
|
||||
#endif /* QEMU_XEN_EVTCHN_H */
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* QEMU Xen emulation: Grant table support
|
||||
*
|
||||
* Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Authors: David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* 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/host-utils.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/lockable.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qom/object.h"
|
||||
#include "exec/target_page.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/xen/xen.h"
|
||||
#include "xen_overlay.h"
|
||||
#include "xen_gnttab.h"
|
||||
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/kvm_xen.h"
|
||||
|
||||
#include "hw/xen/interface/memory.h"
|
||||
#include "hw/xen/interface/grant_table.h"
|
||||
|
||||
#define TYPE_XEN_GNTTAB "xen-gnttab"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(XenGnttabState, XEN_GNTTAB)
|
||||
|
||||
#define XEN_PAGE_SHIFT 12
|
||||
#define XEN_PAGE_SIZE (1ULL << XEN_PAGE_SHIFT)
|
||||
|
||||
#define ENTRIES_PER_FRAME_V1 (XEN_PAGE_SIZE / sizeof(grant_entry_v1_t))
|
||||
|
||||
struct XenGnttabState {
|
||||
/*< private >*/
|
||||
SysBusDevice busdev;
|
||||
/*< public >*/
|
||||
|
||||
QemuMutex gnt_lock;
|
||||
|
||||
uint32_t nr_frames;
|
||||
uint32_t max_frames;
|
||||
|
||||
union {
|
||||
grant_entry_v1_t *v1;
|
||||
/* Theoretically, v2 support could be added here. */
|
||||
} entries;
|
||||
|
||||
MemoryRegion gnt_frames;
|
||||
MemoryRegion *gnt_aliases;
|
||||
uint64_t *gnt_frame_gpas;
|
||||
};
|
||||
|
||||
struct XenGnttabState *xen_gnttab_singleton;
|
||||
|
||||
static void xen_gnttab_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
XenGnttabState *s = XEN_GNTTAB(dev);
|
||||
int i;
|
||||
|
||||
if (xen_mode != XEN_EMULATE) {
|
||||
error_setg(errp, "Xen grant table support is for Xen emulation");
|
||||
return;
|
||||
}
|
||||
s->nr_frames = 0;
|
||||
s->max_frames = kvm_xen_get_gnttab_max_frames();
|
||||
memory_region_init_ram(&s->gnt_frames, OBJECT(dev), "xen:grant_table",
|
||||
XEN_PAGE_SIZE * s->max_frames, &error_abort);
|
||||
memory_region_set_enabled(&s->gnt_frames, true);
|
||||
s->entries.v1 = memory_region_get_ram_ptr(&s->gnt_frames);
|
||||
memset(s->entries.v1, 0, XEN_PAGE_SIZE * s->max_frames);
|
||||
|
||||
/* Create individual page-sizes aliases for overlays */
|
||||
s->gnt_aliases = (void *)g_new0(MemoryRegion, s->max_frames);
|
||||
s->gnt_frame_gpas = (void *)g_new(uint64_t, s->max_frames);
|
||||
for (i = 0; i < s->max_frames; i++) {
|
||||
memory_region_init_alias(&s->gnt_aliases[i], OBJECT(dev),
|
||||
NULL, &s->gnt_frames,
|
||||
i * XEN_PAGE_SIZE, XEN_PAGE_SIZE);
|
||||
s->gnt_frame_gpas[i] = INVALID_GPA;
|
||||
}
|
||||
|
||||
qemu_mutex_init(&s->gnt_lock);
|
||||
|
||||
xen_gnttab_singleton = s;
|
||||
}
|
||||
|
||||
static int xen_gnttab_post_load(void *opaque, int version_id)
|
||||
{
|
||||
XenGnttabState *s = XEN_GNTTAB(opaque);
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < s->nr_frames; i++) {
|
||||
if (s->gnt_frame_gpas[i] != INVALID_GPA) {
|
||||
xen_overlay_do_map_page(&s->gnt_aliases[i], s->gnt_frame_gpas[i]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool xen_gnttab_is_needed(void *opaque)
|
||||
{
|
||||
return xen_mode == XEN_EMULATE;
|
||||
}
|
||||
|
||||
static const VMStateDescription xen_gnttab_vmstate = {
|
||||
.name = "xen_gnttab",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = xen_gnttab_is_needed,
|
||||
.post_load = xen_gnttab_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(nr_frames, XenGnttabState),
|
||||
VMSTATE_VARRAY_UINT32(gnt_frame_gpas, XenGnttabState, nr_frames, 0,
|
||||
vmstate_info_uint64, uint64_t),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void xen_gnttab_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = xen_gnttab_realize;
|
||||
dc->vmsd = &xen_gnttab_vmstate;
|
||||
}
|
||||
|
||||
static const TypeInfo xen_gnttab_info = {
|
||||
.name = TYPE_XEN_GNTTAB,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(XenGnttabState),
|
||||
.class_init = xen_gnttab_class_init,
|
||||
};
|
||||
|
||||
void xen_gnttab_create(void)
|
||||
{
|
||||
xen_gnttab_singleton = XEN_GNTTAB(sysbus_create_simple(TYPE_XEN_GNTTAB,
|
||||
-1, NULL));
|
||||
}
|
||||
|
||||
static void xen_gnttab_register_types(void)
|
||||
{
|
||||
type_register_static(&xen_gnttab_info);
|
||||
}
|
||||
|
||||
type_init(xen_gnttab_register_types)
|
||||
|
||||
int xen_gnttab_map_page(uint64_t idx, uint64_t gfn)
|
||||
{
|
||||
XenGnttabState *s = xen_gnttab_singleton;
|
||||
uint64_t gpa = gfn << XEN_PAGE_SHIFT;
|
||||
|
||||
if (!s) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (idx >= s->max_frames) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
QEMU_IOTHREAD_LOCK_GUARD();
|
||||
QEMU_LOCK_GUARD(&s->gnt_lock);
|
||||
|
||||
xen_overlay_do_map_page(&s->gnt_aliases[idx], gpa);
|
||||
|
||||
s->gnt_frame_gpas[idx] = gpa;
|
||||
|
||||
if (s->nr_frames <= idx) {
|
||||
s->nr_frames = idx + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xen_gnttab_set_version_op(struct gnttab_set_version *set)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (set->version) {
|
||||
case 1:
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* Behave as before set_version was introduced. */
|
||||
ret = -ENOSYS;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
set->version = 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xen_gnttab_get_version_op(struct gnttab_get_version *get)
|
||||
{
|
||||
if (get->dom != DOMID_SELF && get->dom != xen_domid) {
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
get->version = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xen_gnttab_query_size_op(struct gnttab_query_size *size)
|
||||
{
|
||||
XenGnttabState *s = xen_gnttab_singleton;
|
||||
|
||||
if (!s) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (size->dom != DOMID_SELF && size->dom != xen_domid) {
|
||||
size->status = GNTST_bad_domain;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size->status = GNTST_okay;
|
||||
size->nr_frames = s->nr_frames;
|
||||
size->max_nr_frames = s->max_frames;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* QEMU Xen emulation: Grant table support
|
||||
*
|
||||
* Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Authors: David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* 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 QEMU_XEN_GNTTAB_H
|
||||
#define QEMU_XEN_GNTTAB_H
|
||||
|
||||
void xen_gnttab_create(void);
|
||||
int xen_gnttab_map_page(uint64_t idx, uint64_t gfn);
|
||||
|
||||
struct gnttab_set_version;
|
||||
struct gnttab_get_version;
|
||||
struct gnttab_query_size;
|
||||
int xen_gnttab_set_version_op(struct gnttab_set_version *set);
|
||||
int xen_gnttab_get_version_op(struct gnttab_get_version *get);
|
||||
int xen_gnttab_query_size_op(struct gnttab_query_size *size);
|
||||
|
||||
#endif /* QEMU_XEN_GNTTAB_H */
|
||||
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* QEMU Xen emulation: Shared/overlay pages support
|
||||
*
|
||||
* Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Authors: David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* 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/host-utils.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qom/object.h"
|
||||
#include "exec/target_page.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/xen/xen.h"
|
||||
#include "xen_overlay.h"
|
||||
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/kvm_xen.h"
|
||||
#include <linux/kvm.h>
|
||||
|
||||
#include "hw/xen/interface/memory.h"
|
||||
|
||||
|
||||
#define TYPE_XEN_OVERLAY "xen-overlay"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(XenOverlayState, XEN_OVERLAY)
|
||||
|
||||
#define XEN_PAGE_SHIFT 12
|
||||
#define XEN_PAGE_SIZE (1ULL << XEN_PAGE_SHIFT)
|
||||
|
||||
struct XenOverlayState {
|
||||
/*< private >*/
|
||||
SysBusDevice busdev;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion shinfo_mem;
|
||||
void *shinfo_ptr;
|
||||
uint64_t shinfo_gpa;
|
||||
bool long_mode;
|
||||
};
|
||||
|
||||
struct XenOverlayState *xen_overlay_singleton;
|
||||
|
||||
void xen_overlay_do_map_page(MemoryRegion *page, uint64_t gpa)
|
||||
{
|
||||
/*
|
||||
* Xen allows guests to map the same page as many times as it likes
|
||||
* into guest physical frames. We don't, because it would be hard
|
||||
* to track and restore them all. One mapping of each page is
|
||||
* perfectly sufficient for all known guests... and we've tested
|
||||
* that theory on a few now in other implementations. dwmw2.
|
||||
*/
|
||||
if (memory_region_is_mapped(page)) {
|
||||
if (gpa == INVALID_GPA) {
|
||||
memory_region_del_subregion(get_system_memory(), page);
|
||||
} else {
|
||||
/* Just move it */
|
||||
memory_region_set_address(page, gpa);
|
||||
}
|
||||
} else if (gpa != INVALID_GPA) {
|
||||
memory_region_add_subregion_overlap(get_system_memory(), gpa, page, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* KVM is the only existing back end for now. Let's not overengineer it yet. */
|
||||
static int xen_overlay_set_be_shinfo(uint64_t gfn)
|
||||
{
|
||||
struct kvm_xen_hvm_attr xa = {
|
||||
.type = KVM_XEN_ATTR_TYPE_SHARED_INFO,
|
||||
.u.shared_info.gfn = gfn,
|
||||
};
|
||||
|
||||
return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &xa);
|
||||
}
|
||||
|
||||
|
||||
static void xen_overlay_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
XenOverlayState *s = XEN_OVERLAY(dev);
|
||||
|
||||
if (xen_mode != XEN_EMULATE) {
|
||||
error_setg(errp, "Xen overlay page support is for Xen emulation");
|
||||
return;
|
||||
}
|
||||
|
||||
memory_region_init_ram(&s->shinfo_mem, OBJECT(dev), "xen:shared_info",
|
||||
XEN_PAGE_SIZE, &error_abort);
|
||||
memory_region_set_enabled(&s->shinfo_mem, true);
|
||||
|
||||
s->shinfo_ptr = memory_region_get_ram_ptr(&s->shinfo_mem);
|
||||
s->shinfo_gpa = INVALID_GPA;
|
||||
s->long_mode = false;
|
||||
memset(s->shinfo_ptr, 0, XEN_PAGE_SIZE);
|
||||
}
|
||||
|
||||
static int xen_overlay_pre_save(void *opaque)
|
||||
{
|
||||
/*
|
||||
* Fetch the kernel's idea of long_mode to avoid the race condition
|
||||
* where the guest has set the hypercall page up in 64-bit mode but
|
||||
* not yet made a hypercall by the time migration happens, so qemu
|
||||
* hasn't yet noticed.
|
||||
*/
|
||||
return xen_sync_long_mode();
|
||||
}
|
||||
|
||||
static int xen_overlay_post_load(void *opaque, int version_id)
|
||||
{
|
||||
XenOverlayState *s = opaque;
|
||||
|
||||
if (s->shinfo_gpa != INVALID_GPA) {
|
||||
xen_overlay_do_map_page(&s->shinfo_mem, s->shinfo_gpa);
|
||||
xen_overlay_set_be_shinfo(s->shinfo_gpa >> XEN_PAGE_SHIFT);
|
||||
}
|
||||
if (s->long_mode) {
|
||||
xen_set_long_mode(true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool xen_overlay_is_needed(void *opaque)
|
||||
{
|
||||
return xen_mode == XEN_EMULATE;
|
||||
}
|
||||
|
||||
static const VMStateDescription xen_overlay_vmstate = {
|
||||
.name = "xen_overlay",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = xen_overlay_is_needed,
|
||||
.pre_save = xen_overlay_pre_save,
|
||||
.post_load = xen_overlay_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(shinfo_gpa, XenOverlayState),
|
||||
VMSTATE_BOOL(long_mode, XenOverlayState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void xen_overlay_reset(DeviceState *dev)
|
||||
{
|
||||
kvm_xen_soft_reset();
|
||||
}
|
||||
|
||||
static void xen_overlay_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = xen_overlay_reset;
|
||||
dc->realize = xen_overlay_realize;
|
||||
dc->vmsd = &xen_overlay_vmstate;
|
||||
}
|
||||
|
||||
static const TypeInfo xen_overlay_info = {
|
||||
.name = TYPE_XEN_OVERLAY,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(XenOverlayState),
|
||||
.class_init = xen_overlay_class_init,
|
||||
};
|
||||
|
||||
void xen_overlay_create(void)
|
||||
{
|
||||
xen_overlay_singleton = XEN_OVERLAY(sysbus_create_simple(TYPE_XEN_OVERLAY,
|
||||
-1, NULL));
|
||||
|
||||
/* If xen_domid wasn't explicitly set, at least make sure it isn't zero. */
|
||||
if (xen_domid == DOMID_QEMU) {
|
||||
xen_domid = 1;
|
||||
};
|
||||
}
|
||||
|
||||
static void xen_overlay_register_types(void)
|
||||
{
|
||||
type_register_static(&xen_overlay_info);
|
||||
}
|
||||
|
||||
type_init(xen_overlay_register_types)
|
||||
|
||||
int xen_overlay_map_shinfo_page(uint64_t gpa)
|
||||
{
|
||||
XenOverlayState *s = xen_overlay_singleton;
|
||||
int ret;
|
||||
|
||||
if (!s) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
assert(qemu_mutex_iothread_locked());
|
||||
|
||||
if (s->shinfo_gpa) {
|
||||
/* If removing shinfo page, turn the kernel magic off first */
|
||||
ret = xen_overlay_set_be_shinfo(INVALID_GFN);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
xen_overlay_do_map_page(&s->shinfo_mem, gpa);
|
||||
if (gpa != INVALID_GPA) {
|
||||
ret = xen_overlay_set_be_shinfo(gpa >> XEN_PAGE_SHIFT);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
s->shinfo_gpa = gpa;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *xen_overlay_get_shinfo_ptr(void)
|
||||
{
|
||||
XenOverlayState *s = xen_overlay_singleton;
|
||||
|
||||
if (!s) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return s->shinfo_ptr;
|
||||
}
|
||||
|
||||
int xen_sync_long_mode(void)
|
||||
{
|
||||
int ret;
|
||||
struct kvm_xen_hvm_attr xa = {
|
||||
.type = KVM_XEN_ATTR_TYPE_LONG_MODE,
|
||||
};
|
||||
|
||||
if (!xen_overlay_singleton) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_GET_ATTR, &xa);
|
||||
if (!ret) {
|
||||
xen_overlay_singleton->long_mode = xa.u.long_mode;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xen_set_long_mode(bool long_mode)
|
||||
{
|
||||
int ret;
|
||||
struct kvm_xen_hvm_attr xa = {
|
||||
.type = KVM_XEN_ATTR_TYPE_LONG_MODE,
|
||||
.u.long_mode = long_mode,
|
||||
};
|
||||
|
||||
if (!xen_overlay_singleton) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &xa);
|
||||
if (!ret) {
|
||||
xen_overlay_singleton->long_mode = xa.u.long_mode;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool xen_is_long_mode(void)
|
||||
{
|
||||
return xen_overlay_singleton && xen_overlay_singleton->long_mode;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* QEMU Xen emulation: Shared/overlay pages support
|
||||
*
|
||||
* Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* Authors: David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* 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 QEMU_XEN_OVERLAY_H
|
||||
#define QEMU_XEN_OVERLAY_H
|
||||
|
||||
void xen_overlay_create(void);
|
||||
|
||||
int xen_overlay_map_shinfo_page(uint64_t gpa);
|
||||
void *xen_overlay_get_shinfo_ptr(void);
|
||||
|
||||
int xen_sync_long_mode(void);
|
||||
int xen_set_long_mode(bool long_mode);
|
||||
bool xen_is_long_mode(void);
|
||||
|
||||
void xen_overlay_do_map_page(MemoryRegion *page, uint64_t gpa);
|
||||
|
||||
#endif /* QEMU_XEN_OVERLAY_H */
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user