qemu/hw/xen/xen-pvh-common.c
Edgar E. Iglesias cb988a10f6 hw/xen: xenpvh: Disable buffered IOREQs for ARM
Add a way to enable/disable buffered IOREQs for PVH machines
and disable them for ARM. ARM does not support buffered
IOREQ's nor the legacy way to map IOREQ info pages.

See the following for more details:
https://xenbits.xen.org/gitweb/?p=xen.git;a=commitdiff;h=2fbd7e609e1803ac5e5c26e22aa8e4b5a6cddbb1
https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=xen/arch/arm/ioreq.c;h=2e829d2e7f3760401b96fa7c930e2015fb1cf463;hb=HEAD#l138

Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@amd.com>
2024-10-03 19:37:35 +02:00

364 lines
13 KiB
C

/*
* 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,
xpc->handle_bufioreq,
&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);