diff options
Diffstat (limited to 'hw/xen')
-rw-r--r-- | hw/xen/meson.build | 1 | ||||
-rw-r--r-- | hw/xen/trace-events | 4 | ||||
-rw-r--r-- | hw/xen/xen-pvh-common.c | 362 |
3 files changed, 367 insertions, 0 deletions
diff --git a/hw/xen/meson.build b/hw/xen/meson.build index d887fa9..4a486e3 100644 --- a/hw/xen/meson.build +++ b/hw/xen/meson.build @@ -15,6 +15,7 @@ xen_specific_ss = ss.source_set() xen_specific_ss.add(files( 'xen-mapcache.c', 'xen-hvm-common.c', + 'xen-pvh-common.c', )) if have_xen_pci_passthrough xen_specific_ss.add(files( diff --git a/hw/xen/trace-events b/hw/xen/trace-events index d1b27f6..a07fe41 100644 --- a/hw/xen/trace-events +++ b/hw/xen/trace-events @@ -64,6 +64,10 @@ destroy_hvm_domain_cannot_acquire_handle(void) "Cannot acquire xenctrl handle" destroy_hvm_domain_failed_action(const char *action, int sts, char *errno_s) "xc_domain_shutdown failed to issue %s, sts %d, %s" destroy_hvm_domain_action(int xen_domid, const char *action) "Issued domain %d %s" +# xen-pvh-common.c +xen_create_virtio_mmio_devices(int i, int irq, uint64_t base) "Created virtio-mmio device %d: irq %d base 0x%"PRIx64 +xen_enable_tpm(uint64_t addr) "Connected tpmdev at address 0x%"PRIx64 + # xen-mapcache.c xen_map_cache(uint64_t phys_addr) "want 0x%"PRIx64 xen_remap_bucket(uint64_t index) "index 0x%"PRIx64 diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c new file mode 100644 index 0000000..28d7168 --- /dev/null +++ b/hw/xen/xen-pvh-common.c @@ -0,0 +1,362 @@ +/* + * QEMU Xen PVH machine - common code. + * + * Copyright (c) 2024 Advanced Micro Devices, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "hw/boards.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "sysemu/tpm.h" +#include "sysemu/tpm_backend.h" +#include "hw/xen/xen-pvh-common.h" +#include "trace.h" + +static const MemoryListener xen_memory_listener = { + .region_add = xen_region_add, + .region_del = xen_region_del, + .log_start = NULL, + .log_stop = NULL, + .log_sync = NULL, + .log_global_start = NULL, + .log_global_stop = NULL, + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, +}; + +static void xen_pvh_init_ram(XenPVHMachineState *s, + MemoryRegion *sysmem) +{ + MachineState *ms = MACHINE(s); + ram_addr_t block_len, ram_size[2]; + + if (ms->ram_size <= s->cfg.ram_low.size) { + ram_size[0] = ms->ram_size; + ram_size[1] = 0; + block_len = s->cfg.ram_low.base + ram_size[0]; + } else { + ram_size[0] = s->cfg.ram_low.size; + ram_size[1] = ms->ram_size - s->cfg.ram_low.size; + block_len = s->cfg.ram_high.base + ram_size[1]; + } + + memory_region_init_ram(&xen_memory, NULL, "xen.ram", block_len, + &error_fatal); + + memory_region_init_alias(&s->ram.low, NULL, "xen.ram.lo", &xen_memory, + s->cfg.ram_low.base, ram_size[0]); + memory_region_add_subregion(sysmem, s->cfg.ram_low.base, &s->ram.low); + if (ram_size[1] > 0) { + memory_region_init_alias(&s->ram.high, NULL, "xen.ram.hi", &xen_memory, + s->cfg.ram_high.base, ram_size[1]); + memory_region_add_subregion(sysmem, s->cfg.ram_high.base, &s->ram.high); + } + + /* Setup support for grants. */ + memory_region_init_ram(&xen_grants, NULL, "xen.grants", block_len, + &error_fatal); + memory_region_add_subregion(sysmem, XEN_GRANT_ADDR_OFF, &xen_grants); +} + +static void xen_set_irq(void *opaque, int irq, int level) +{ + if (xendevicemodel_set_irq_level(xen_dmod, xen_domid, irq, level)) { + error_report("xendevicemodel_set_irq_level failed"); + } +} + +static void xen_create_virtio_mmio_devices(XenPVHMachineState *s) +{ + int i; + + /* + * We create the transports in reverse order. Since qbus_realize() + * prepends (not appends) new child buses, the decrementing loop below will + * create a list of virtio-mmio buses with increasing base addresses. + * + * When a -device option is processed from the command line, + * qbus_find_recursive() picks the next free virtio-mmio bus in forwards + * order. + * + * This is what the Xen tools expect. + */ + for (i = s->cfg.virtio_mmio_num - 1; i >= 0; i--) { + hwaddr base = s->cfg.virtio_mmio.base + i * s->cfg.virtio_mmio.size; + qemu_irq irq = qemu_allocate_irq(xen_set_irq, NULL, + s->cfg.virtio_mmio_irq_base + i); + + sysbus_create_simple("virtio-mmio", base, irq); + + trace_xen_create_virtio_mmio_devices(i, + s->cfg.virtio_mmio_irq_base + i, + base); + } +} + +#ifdef CONFIG_TPM +static void xen_enable_tpm(XenPVHMachineState *s) +{ + Error *errp = NULL; + DeviceState *dev; + SysBusDevice *busdev; + + TPMBackend *be = qemu_find_tpm_be("tpm0"); + if (be == NULL) { + error_report("Couldn't find tmp0 backend"); + return; + } + dev = qdev_new(TYPE_TPM_TIS_SYSBUS); + object_property_set_link(OBJECT(dev), "tpmdev", OBJECT(be), &errp); + object_property_set_str(OBJECT(dev), "tpmdev", be->id, &errp); + busdev = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(busdev, &error_fatal); + sysbus_mmio_map(busdev, 0, s->cfg.tpm.base); + + trace_xen_enable_tpm(s->cfg.tpm.base); +} +#endif + +/* + * We use the GPEX PCIe controller with its internal INTX PCI interrupt + * swizzling. This swizzling is emulated in QEMU and routes all INTX + * interrupts from endpoints down to only 4 INTX interrupts. + * See include/hw/pci/pci.h : pci_swizzle() + */ +static inline void xenpvh_gpex_init(XenPVHMachineState *s, + XenPVHMachineClass *xpc, + MemoryRegion *sysmem) +{ + MemoryRegion *ecam_reg; + MemoryRegion *mmio_reg; + DeviceState *dev; + int i; + + object_initialize_child(OBJECT(s), "gpex", &s->pci.gpex, + TYPE_GPEX_HOST); + dev = DEVICE(&s->pci.gpex); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_add_subregion(sysmem, s->cfg.pci_ecam.base, ecam_reg); + + mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + + if (s->cfg.pci_mmio.size) { + memory_region_init_alias(&s->pci.mmio_alias, OBJECT(dev), "pcie-mmio", + mmio_reg, + s->cfg.pci_mmio.base, s->cfg.pci_mmio.size); + memory_region_add_subregion(sysmem, s->cfg.pci_mmio.base, + &s->pci.mmio_alias); + } + + if (s->cfg.pci_mmio_high.size) { + memory_region_init_alias(&s->pci.mmio_high_alias, OBJECT(dev), + "pcie-mmio-high", + mmio_reg, s->cfg.pci_mmio_high.base, s->cfg.pci_mmio_high.size); + memory_region_add_subregion(sysmem, s->cfg.pci_mmio_high.base, + &s->pci.mmio_high_alias); + } + + /* + * PVH implementations with PCI enabled must provide set_pci_intx_irq() + * and optionally an implementation of set_pci_link_route(). + */ + assert(xpc->set_pci_intx_irq); + + for (i = 0; i < GPEX_NUM_IRQS; i++) { + qemu_irq irq = qemu_allocate_irq(xpc->set_pci_intx_irq, s, i); + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); + gpex_set_irq_num(GPEX_HOST(dev), i, s->cfg.pci_intx_irq_base + i); + if (xpc->set_pci_link_route) { + xpc->set_pci_link_route(i, s->cfg.pci_intx_irq_base + i); + } + } +} + +static void xen_pvh_init(MachineState *ms) +{ + XenPVHMachineState *s = XEN_PVH_MACHINE(ms); + XenPVHMachineClass *xpc = XEN_PVH_MACHINE_GET_CLASS(s); + MemoryRegion *sysmem = get_system_memory(); + + if (ms->ram_size == 0) { + warn_report("%s: ram size not specified. QEMU machine started" + " without IOREQ (no emulated devices including virtio)", + MACHINE_CLASS(object_get_class(OBJECT(ms)))->desc); + return; + } + + xen_pvh_init_ram(s, sysmem); + xen_register_ioreq(&s->ioreq, ms->smp.max_cpus, &xen_memory_listener); + + if (s->cfg.virtio_mmio_num) { + xen_create_virtio_mmio_devices(s); + } + +#ifdef CONFIG_TPM + if (xpc->has_tpm) { + if (s->cfg.tpm.base) { + xen_enable_tpm(s); + } else { + warn_report("tpm-base-addr is not set. TPM will not be enabled"); + } + } +#endif + + /* Non-zero pci-ecam-size enables PCI. */ + if (s->cfg.pci_ecam.size) { + if (s->cfg.pci_ecam.size != 256 * MiB) { + error_report("pci-ecam-size only supports values 0 or 0x10000000"); + exit(EXIT_FAILURE); + } + xenpvh_gpex_init(s, xpc, sysmem); + } + + /* Call the implementation specific init. */ + if (xpc->init) { + xpc->init(ms); + } +} + +#define XEN_PVH_PROP_MEMMAP_SETTER(n, f) \ +static void xen_pvh_set_ ## n ## _ ## f(Object *obj, Visitor *v, \ + const char *name, void *opaque, \ + Error **errp) \ +{ \ + XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); \ + uint64_t value; \ + \ + if (!visit_type_size(v, name, &value, errp)) { \ + return; \ + } \ + xp->cfg.n.f = value; \ +} + +#define XEN_PVH_PROP_MEMMAP_GETTER(n, f) \ +static void xen_pvh_get_ ## n ## _ ## f(Object *obj, Visitor *v, \ + const char *name, void *opaque, \ + Error **errp) \ +{ \ + XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); \ + uint64_t value = xp->cfg.n.f; \ + \ + visit_type_uint64(v, name, &value, errp); \ +} + +#define XEN_PVH_PROP_MEMMAP_BASE(n) \ + XEN_PVH_PROP_MEMMAP_SETTER(n, base) \ + XEN_PVH_PROP_MEMMAP_GETTER(n, base) \ + +#define XEN_PVH_PROP_MEMMAP_SIZE(n) \ + XEN_PVH_PROP_MEMMAP_SETTER(n, size) \ + XEN_PVH_PROP_MEMMAP_GETTER(n, size) + +#define XEN_PVH_PROP_MEMMAP(n) \ + XEN_PVH_PROP_MEMMAP_BASE(n) \ + XEN_PVH_PROP_MEMMAP_SIZE(n) + +XEN_PVH_PROP_MEMMAP(ram_low) +XEN_PVH_PROP_MEMMAP(ram_high) +/* TPM only has a base-addr option. */ +XEN_PVH_PROP_MEMMAP_BASE(tpm) +XEN_PVH_PROP_MEMMAP(virtio_mmio) +XEN_PVH_PROP_MEMMAP(pci_ecam) +XEN_PVH_PROP_MEMMAP(pci_mmio) +XEN_PVH_PROP_MEMMAP(pci_mmio_high) + +void xen_pvh_class_setup_common_props(XenPVHMachineClass *xpc) +{ + ObjectClass *oc = OBJECT_CLASS(xpc); + MachineClass *mc = MACHINE_CLASS(xpc); + +#define OC_MEMMAP_PROP_BASE(c, prop_name, name) \ +do { \ + object_class_property_add(c, prop_name "-base", "uint64_t", \ + xen_pvh_get_ ## name ## _base, \ + xen_pvh_set_ ## name ## _base, NULL, NULL); \ + object_class_property_set_description(oc, prop_name "-base", \ + "Set base address for " prop_name); \ +} while (0) + +#define OC_MEMMAP_PROP_SIZE(c, prop_name, name) \ +do { \ + object_class_property_add(c, prop_name "-size", "uint64_t", \ + xen_pvh_get_ ## name ## _size, \ + xen_pvh_set_ ## name ## _size, NULL, NULL); \ + object_class_property_set_description(oc, prop_name "-size", \ + "Set memory range size for " prop_name); \ +} while (0) + +#define OC_MEMMAP_PROP(c, prop_name, name) \ +do { \ + OC_MEMMAP_PROP_BASE(c, prop_name, name); \ + OC_MEMMAP_PROP_SIZE(c, prop_name, name); \ +} while (0) + + /* + * We provide memmap properties to allow Xen to move things to other + * addresses for example when users need to accomodate the memory-map + * for 1:1 mapped devices/memory. + */ + OC_MEMMAP_PROP(oc, "ram-low", ram_low); + OC_MEMMAP_PROP(oc, "ram-high", ram_high); + + if (xpc->has_virtio_mmio) { + OC_MEMMAP_PROP(oc, "virtio-mmio", virtio_mmio); + } + + if (xpc->has_pci) { + OC_MEMMAP_PROP(oc, "pci-ecam", pci_ecam); + OC_MEMMAP_PROP(oc, "pci-mmio", pci_mmio); + OC_MEMMAP_PROP(oc, "pci-mmio-high", pci_mmio_high); + } + +#ifdef CONFIG_TPM + if (xpc->has_tpm) { + object_class_property_add(oc, "tpm-base-addr", "uint64_t", + xen_pvh_get_tpm_base, + xen_pvh_set_tpm_base, + NULL, NULL); + object_class_property_set_description(oc, "tpm-base-addr", + "Set Base address for TPM device."); + + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); + } +#endif +} + +static void xen_pvh_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = xen_pvh_init; + + mc->desc = "Xen PVH machine"; + mc->max_cpus = 1; + mc->default_machine_opts = "accel=xen"; + /* Set to zero to make sure that the real ram size is passed. */ + mc->default_ram_size = 0; +} + +static const TypeInfo xen_pvh_info = { + .name = TYPE_XEN_PVH_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(XenPVHMachineState), + .class_size = sizeof(XenPVHMachineClass), + .class_init = xen_pvh_class_init, +}; + +static void xen_pvh_register_types(void) +{ + type_register_static(&xen_pvh_info); +} + +type_init(xen_pvh_register_types); |