From: David Woodhouse <d...@amazon.co.uk> The original Oracle version of this made it a CPU property, but it isn't really a per-CPU thing. I then tried making it a KVM accelerator property but moved to a machine property for two reasons. One is that it allows us to set it in default_machine_opts for the xenfv platform when not running on actual Xen, and also because theoretically we *could* do this with TCG too; we'd just have to implement a bunch of the stuff that KVM already does for us.
Signed-off-by: David Woodhouse <d...@amazon.co.uk> --- hw/i386/pc.c | 32 +++++++++++++++++++++++++++ hw/i386/pc_piix.c | 10 +++++++-- include/hw/i386/pc.h | 3 +++ target/i386/kvm/kvm.c | 17 ++++++++++++++ target/i386/meson.build | 1 + target/i386/xen.c | 49 +++++++++++++++++++++++++++++++++++++++++ target/i386/xen.h | 19 ++++++++++++++++ 7 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 target/i386/xen.c create mode 100644 target/i386/xen.h diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 546b703cb4..9bada1a8ff 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1811,6 +1811,32 @@ static void pc_machine_set_max_fw_size(Object *obj, Visitor *v, pcms->max_fw_size = value; } +static void pc_machine_get_xen_version(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + uint32_t value = pcms->xen_version; + + visit_type_uint32(v, name, &value, errp); +} + +static void pc_machine_set_xen_version(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + Error *error = NULL; + uint32_t value; + + visit_type_uint32(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + + pcms->xen_version = value; +} static void pc_machine_initfn(Object *obj) { @@ -1978,6 +2004,12 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) NULL, NULL); object_class_property_set_description(oc, PC_MACHINE_SMBIOS_EP, "SMBIOS Entry Point type [32, 64]"); + + object_class_property_add(oc, "xen-version", "uint32", + pc_machine_get_xen_version, pc_machine_set_xen_version, + NULL, NULL); + object_class_property_set_description(oc, "xen-version", + "Xen version to be emulated (in XENVER_version form e.g. 0x4000a for 4.10)"); } static const TypeInfo pc_machine_info = { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 0ad0ed1603..13286d0739 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -876,7 +876,10 @@ static void xenfv_4_2_machine_options(MachineClass *m) pc_i440fx_4_2_machine_options(m); m->desc = "Xen Fully-virtualized PC"; m->max_cpus = HVM_MAX_VCPUS; - m->default_machine_opts = "accel=xen,suppress-vmdesc=on"; + if (xen_enabled()) + m->default_machine_opts = "accel=xen,suppress-vmdesc=on"; + else + m->default_machine_opts = "accel=kvm,xen-version=0x40002"; } DEFINE_PC_MACHINE(xenfv_4_2, "xenfv-4.2", pc_xen_hvm_init, @@ -888,7 +891,10 @@ static void xenfv_3_1_machine_options(MachineClass *m) m->desc = "Xen Fully-virtualized PC"; m->alias = "xenfv"; m->max_cpus = HVM_MAX_VCPUS; - m->default_machine_opts = "accel=xen,suppress-vmdesc=on"; + if (xen_enabled()) + m->default_machine_opts = "accel=xen,suppress-vmdesc=on"; + else + m->default_machine_opts = "accel=kvm,xen-version=0x30001"; } DEFINE_PC_MACHINE(xenfv, "xenfv-3.1", pc_xen_hvm_init, diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index c95333514e..9b14b18836 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -52,6 +52,9 @@ typedef struct PCMachineState { bool default_bus_bypass_iommu; uint64_t max_fw_size; + /* Xen HVM emulation */ + uint32_t xen_version; + /* ACPI Memory hotplug IO base address */ hwaddr memhp_io_base; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index a213209379..ff3ea245cf 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -31,6 +31,7 @@ #include "sysemu/runstate.h" #include "kvm_i386.h" #include "sev.h" +#include "xen.h" #include "hyperv.h" #include "hyperv-proto.h" @@ -2459,6 +2460,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) { uint64_t identity_base = 0xfffbc000; uint64_t shadow_mem; + uint32_t xen_version; int ret; struct utsname utsname; Error *local_err = NULL; @@ -2513,6 +2515,21 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } } + xen_version = object_property_get_int(OBJECT(ms), "xen-version", NULL); + if (xen_version == (uint32_t) -1) + xen_version = 0; + if (xen_version) { +#ifdef CONFIG_XEN + ret = kvm_xen_init(s, xen_version); + if (ret < 0) { + return ret; + } +#else + error_report("kvm: Xen support not enabled in qemu"); + return -ENOTSUP; +#endif + } + ret = kvm_get_supported_msrs(s); if (ret < 0) { return ret; diff --git a/target/i386/meson.build b/target/i386/meson.build index ae38dc9563..5253193853 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -7,6 +7,7 @@ i386_ss.add(files( 'cpu-dump.c', )) i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c')) +i386_ss.add(when: 'CONFIG_XEN', if_true: files('xen.c')) # x86 cpu type i386_ss.add(when: 'CONFIG_KVM', if_true: files('host-cpu.c')) diff --git a/target/i386/xen.c b/target/i386/xen.c new file mode 100644 index 0000000000..bc183dce4e --- /dev/null +++ b/target/i386/xen.c @@ -0,0 +1,49 @@ +/* + * Xen HVM emulation support in KVM + * + * Copyright © 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 "kvm/kvm_i386.h" +#include "xen.h" + +int kvm_xen_init(KVMState *s, uint32_t xen_version) +{ + const int required_caps = KVM_XEN_HVM_CONFIG_HYPERCALL_MSR | + KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL | KVM_XEN_HVM_CONFIG_SHARED_INFO; + struct kvm_xen_hvm_config cfg = { + .msr = XEN_HYPERCALL_MSR, + .flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL, + }; + int xen_caps, ret; + + xen_caps = kvm_check_extension(s, KVM_CAP_XEN_HVM); + if (required_caps & ~xen_caps) { + error_report("kvm: Xen HVM guest support not present or insufficient"); + return -ENOSYS; + } + + if (xen_caps & KVM_XEN_HVM_CONFIG_EVTCHN_SEND) { + struct kvm_xen_hvm_attr ha = { + .type = KVM_XEN_ATTR_TYPE_XEN_VERSION, + .u.xen_version = xen_version, + }; + (void)kvm_vm_ioctl(s, KVM_XEN_HVM_SET_ATTR, &ha); + + cfg.flags |= KVM_XEN_HVM_CONFIG_EVTCHN_SEND; + } + + ret = kvm_vm_ioctl(s, KVM_XEN_HVM_CONFIG, &cfg); + if (ret < 0) { + error_report("kvm: Failed to enable Xen HVM support: %s", strerror(-ret)); + return ret; + } + + return 0; +} diff --git a/target/i386/xen.h b/target/i386/xen.h new file mode 100644 index 0000000000..6c4f3b7822 --- /dev/null +++ b/target/i386/xen.h @@ -0,0 +1,19 @@ +/* + * Xen HVM emulation support in KVM + * + * Copyright © 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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_I386_XEN_H +#define QEMU_I386_XEN_H + +#define XEN_HYPERCALL_MSR 0x40000000 + +int kvm_xen_init(KVMState *s, uint32_t xen_version); + +#endif /* QEMU_I386_XEN_H */ -- 2.35.3