Edgars Xen queue.

-----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEErET+3BT38evtv0FRKcWWeA9ryoMFAmbYfMIACgkQKcWWeA9r
 yoNPfwgAuK6MyPGEJh75Pe7yEmkVeuL0RPTmet5Ie1WrywNsn91IybQGLctpxr1s
 7m2Zhl3IWXV5Jezfr9gEnLF+LYBQgK0ENJWDTPuIqf7D5ZRtOVeaNqrQVJ10Jomn
 s1pn4kWtHxKsgWTzcdi3qP7vhxn1PllxE+yuOcoYQIHcUp1oQAHr6ApbAcxseWBE
 qPdgxT7nlvFdcKqkzxSgKy5MWjs3xcBg6R5Ywoy+t7lb003swivPnkK6MSt1P03h
 EkQsTWr0Ox4nACOWt15U0MoK5rtBEN7Gsox0FUEPF3QhmAJt75FTFLs8+JhqcnKH
 LMxud5C6t6FcI+kxPqPoIdEWy5uM6g==
 =hW3R
 -----END PGP SIGNATURE-----

Merge tag 'edgar/xen-queue-2024-09-04.for-upstream' of https://gitlab.com/edgar.iglesias/qemu into staging

Edgars Xen queue.

# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCgAdFiEErET+3BT38evtv0FRKcWWeA9ryoMFAmbYfMIACgkQKcWWeA9r
# yoNPfwgAuK6MyPGEJh75Pe7yEmkVeuL0RPTmet5Ie1WrywNsn91IybQGLctpxr1s
# 7m2Zhl3IWXV5Jezfr9gEnLF+LYBQgK0ENJWDTPuIqf7D5ZRtOVeaNqrQVJ10Jomn
# s1pn4kWtHxKsgWTzcdi3qP7vhxn1PllxE+yuOcoYQIHcUp1oQAHr6ApbAcxseWBE
# qPdgxT7nlvFdcKqkzxSgKy5MWjs3xcBg6R5Ywoy+t7lb003swivPnkK6MSt1P03h
# EkQsTWr0Ox4nACOWt15U0MoK5rtBEN7Gsox0FUEPF3QhmAJt75FTFLs8+JhqcnKH
# LMxud5C6t6FcI+kxPqPoIdEWy5uM6g==
# =hW3R
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 04 Sep 2024 16:29:06 BST
# gpg:                using RSA key AC44FEDC14F7F1EBEDBF415129C596780F6BCA83
# gpg: Good signature from "Edgar E. Iglesias (Xilinx key) <edgar.iglesias@xilinx.com>" [unknown]
# gpg:                 aka "Edgar E. Iglesias <edgar.iglesias@gmail.com>" [full]
# Primary key fingerprint: AC44 FEDC 14F7 F1EB EDBF  4151 29C5 9678 0F6B CA83

* tag 'edgar/xen-queue-2024-09-04.for-upstream' of https://gitlab.com/edgar.iglesias/qemu:
  docs/system/i386: xenpvh: Add a basic description
  hw/i386/xen: Add a Xen PVH x86 machine
  hw/xen: pvh-common: Add support for creating PCIe/GPEX
  hw/arm: xenpvh: Reverse virtio-mmio creation order
  hw/arm: xenpvh: Rename xen_arm.c -> xen-pvh.c
  hw/arm: xenpvh: Break out a common PVH machine
  hw/arm: xenpvh: Move stubbed functions to xen-stubs.c
  hw/arm: xenpvh: Remove double-negation in warning
  hw/arm: xenpvh: Add support for SMP guests
  hw/arm: xenpvh: Tweak machine description
  hw/arm: xenpvh: Update file header to use SPDX
  MAINTAINERS: Add docs/system/arm/xenpvh.rst

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2024-09-05 13:02:26 +01:00
commit 7b87a25f49
14 changed files with 754 additions and 273 deletions

View File

@ -559,6 +559,8 @@ F: include/hw/xen/
F: include/sysemu/xen.h
F: include/sysemu/xen-mapcache.h
F: stubs/xen-hw-stub.c
F: docs/system/arm/xenpvh.rst
F: docs/system/i386/xenpvh.rst
Guest CPU Cores (NVMM)
----------------------

View File

@ -0,0 +1,49 @@
Xen PVH machine (``xenpvh``)
=========================================
Xen supports a spectrum of types of guests that vary in how they depend
on HW virtualization features, emulation models and paravirtualization.
PVH is a mode that uses HW virtualization features (like HVM) but tries
to avoid emulation models and instead use passthrough or
paravirtualized devices.
QEMU can be used to provide PV virtio devices on an emulated PCIe controller.
That is the purpose of this minimal machine.
Supported devices
-----------------
The x86 Xen PVH QEMU machine provide the following devices:
- RAM
- GPEX host bridge
- virtio-pci devices
The idea is to only connect virtio-pci devices but in theory any compatible
PCI device model will work depending on Xen and guest support.
Running
-------
The Xen tools will typically construct a command-line and launch QEMU
for you when needed. But here's an example of what it can look like in
case you need to construct one manually:
.. code-block:: console
qemu-system-i386 -xen-domid 3 -no-shutdown \
-chardev socket,id=libxl-cmd,path=/var/run/xen/qmp-libxl-3,server=on,wait=off \
-mon chardev=libxl-cmd,mode=control \
-chardev socket,id=libxenstat-cmd,path=/var/run/xen/qmp-libxenstat-3,server=on,wait=off \
-mon chardev=libxenstat-cmd,mode=control \
-nodefaults \
-no-user-config \
-xen-attach -name g0 \
-vnc none \
-display none \
-device virtio-net-pci,id=nic0,netdev=net0,mac=00:16:3e:5c:81:78 \
-netdev type=tap,id=net0,ifname=vif3.0-emu,br=xenbr0,script=no,downscript=no \
-smp 4,maxcpus=4 \
-nographic \
-machine xenpvh,ram-low-base=0,ram-low-size=2147483648,ram-high-base=4294967296,ram-high-size=2147483648,pci-ecam-base=824633720832,pci-ecam-size=268435456,pci-mmio-base=4026531840,pci-mmio-size=33554432,pci-mmio-high-base=824902156288,pci-mmio-high-size=68719476736 \
-m 4096

View File

@ -26,6 +26,7 @@ Architectural features
i386/cpu
i386/hyperv
i386/xen
i386/xenpvh
i386/kvm-pv
i386/sgx
i386/amd-memory-encryption

View File

@ -59,7 +59,10 @@ arm_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.
arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c'))
arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c'))
arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c'))
arm_ss.add(when: 'CONFIG_XEN', if_true: files('xen_arm.c'))
arm_ss.add(when: 'CONFIG_XEN', if_true: files(
'xen-stubs.c',
'xen-pvh.c',
))
system_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c'))
system_ss.add(when: 'CONFIG_CHEETAH', if_true: files('palm.c'))

View File

@ -68,10 +68,5 @@ z2_aer915_send_too_long(int8_t msg) "message too long (%i bytes)"
z2_aer915_send(uint8_t reg, uint8_t value) "reg %d value 0x%02x"
z2_aer915_event(int8_t event, int8_t len) "i2c event =0x%x len=%d bytes"
# xen_arm.c
xen_create_virtio_mmio_devices(int i, int irq, uint64_t base) "Created virtio-mmio device %d: irq %d base 0x%"PRIx64
xen_init_ram(uint64_t machine_ram_size) "Initialized xen ram with size 0x%"PRIx64
xen_enable_tpm(uint64_t addr) "Connected tpmdev at address 0x%"PRIx64
# bcm2838.c
bcm2838_gic_set_irq(int irq, int level) "gic irq:%d lvl:%d"

89
hw/arm/xen-pvh.c Normal file
View File

@ -0,0 +1,89 @@
/*
* QEMU ARM Xen PVH Machine
*
* SPDX-License-Identifier: MIT
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qapi/qapi-commands-migration.h"
#include "hw/boards.h"
#include "sysemu/sysemu.h"
#include "hw/xen/xen-pvh-common.h"
#include "hw/xen/arch_hvm.h"
#define TYPE_XEN_ARM MACHINE_TYPE_NAME("xenpvh")
/*
* VIRTIO_MMIO_DEV_SIZE is imported from tools/libs/light/libxl_arm.c under Xen
* repository.
*
* Origin: git://xenbits.xen.org/xen.git 2128143c114c
*/
#define VIRTIO_MMIO_DEV_SIZE 0x200
#define NR_VIRTIO_MMIO_DEVICES \
(GUEST_VIRTIO_MMIO_SPI_LAST - GUEST_VIRTIO_MMIO_SPI_FIRST)
static void xen_arm_instance_init(Object *obj)
{
XenPVHMachineState *s = XEN_PVH_MACHINE(obj);
/* Default values. */
s->cfg.ram_low = (MemMapEntry) { GUEST_RAM0_BASE, GUEST_RAM0_SIZE };
s->cfg.ram_high = (MemMapEntry) { GUEST_RAM1_BASE, GUEST_RAM1_SIZE };
s->cfg.virtio_mmio_num = NR_VIRTIO_MMIO_DEVICES;
s->cfg.virtio_mmio_irq_base = GUEST_VIRTIO_MMIO_SPI_FIRST;
s->cfg.virtio_mmio = (MemMapEntry) { GUEST_VIRTIO_MMIO_BASE,
VIRTIO_MMIO_DEV_SIZE };
}
static void xen_arm_machine_class_init(ObjectClass *oc, void *data)
{
XenPVHMachineClass *xpc = XEN_PVH_MACHINE_CLASS(oc);
MachineClass *mc = MACHINE_CLASS(oc);
mc->desc = "Xen PVH ARM machine";
/*
* mc->max_cpus holds the MAX value allowed in the -smp command-line opts.
*
* 1. If users don't pass any -smp option:
* ms->smp.cpus will default to 1.
* ms->smp.max_cpus will default to 1.
*
* 2. If users pass -smp X:
* ms->smp.cpus will be set to X.
* ms->smp.max_cpus will also be set to X.
*
* 3. If users pass -smp X,maxcpus=Y:
* ms->smp.cpus will be set to X.
* ms->smp.max_cpus will be set to Y.
*
* In scenarios 2 and 3, if X or Y are set to something larger than
* mc->max_cpus, QEMU will bail out with an error message.
*/
mc->max_cpus = GUEST_MAX_VCPUS;
/* List of supported features known to work on PVH ARM. */
xpc->has_tpm = true;
xpc->has_virtio_mmio = true;
xen_pvh_class_setup_common_props(xpc);
}
static const TypeInfo xen_arm_machine_type = {
.name = TYPE_XEN_ARM,
.parent = TYPE_XEN_PVH_MACHINE,
.class_init = xen_arm_machine_class_init,
.instance_size = sizeof(XenPVHMachineState),
.instance_init = xen_arm_instance_init,
};
static void xen_arm_machine_register_types(void)
{
type_register_static(&xen_arm_machine_type);
}
type_init(xen_arm_machine_register_types)

32
hw/arm/xen-stubs.c Normal file
View File

@ -0,0 +1,32 @@
/*
* Stubs for unimplemented Xen functions for ARM.
*
* SPDX-License-Identifier: MIT
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qapi/qapi-commands-migration.h"
#include "hw/boards.h"
#include "sysemu/sysemu.h"
#include "hw/xen/xen-hvm-common.h"
#include "hw/xen/arch_hvm.h"
void arch_handle_ioreq(XenIOState *state, ioreq_t *req)
{
hw_error("Invalid ioreq type 0x%x\n", req->type);
return;
}
void arch_xen_set_memory(XenIOState *state, MemoryRegionSection *section,
bool add)
{
}
void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length)
{
}
void qmp_xen_set_global_dirty_log(bool enable, Error **errp)
{
}

View File

@ -1,267 +0,0 @@
/*
* QEMU ARM Xen PVH Machine
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qapi/qapi-commands-migration.h"
#include "qapi/visitor.h"
#include "hw/boards.h"
#include "hw/irq.h"
#include "hw/sysbus.h"
#include "sysemu/block-backend.h"
#include "sysemu/tpm_backend.h"
#include "sysemu/sysemu.h"
#include "hw/xen/xen-hvm-common.h"
#include "sysemu/tpm.h"
#include "hw/xen/arch_hvm.h"
#include "trace.h"
#define TYPE_XEN_ARM MACHINE_TYPE_NAME("xenpvh")
OBJECT_DECLARE_SIMPLE_TYPE(XenArmState, XEN_ARM)
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,
};
struct XenArmState {
/*< private >*/
MachineState parent;
XenIOState *state;
struct {
uint64_t tpm_base_addr;
} cfg;
};
static MemoryRegion ram_lo, ram_hi;
/*
* VIRTIO_MMIO_DEV_SIZE is imported from tools/libs/light/libxl_arm.c under Xen
* repository.
*
* Origin: git://xenbits.xen.org/xen.git 2128143c114c
*/
#define VIRTIO_MMIO_DEV_SIZE 0x200
#define NR_VIRTIO_MMIO_DEVICES \
(GUEST_VIRTIO_MMIO_SPI_LAST - GUEST_VIRTIO_MMIO_SPI_FIRST)
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(XenArmState *xam)
{
int i;
for (i = 0; i < NR_VIRTIO_MMIO_DEVICES; i++) {
hwaddr base = GUEST_VIRTIO_MMIO_BASE + i * VIRTIO_MMIO_DEV_SIZE;
qemu_irq irq = qemu_allocate_irq(xen_set_irq, NULL,
GUEST_VIRTIO_MMIO_SPI_FIRST + i);
sysbus_create_simple("virtio-mmio", base, irq);
trace_xen_create_virtio_mmio_devices(i,
GUEST_VIRTIO_MMIO_SPI_FIRST + i,
base);
}
}
static void xen_init_ram(MachineState *machine)
{
MemoryRegion *sysmem = get_system_memory();
ram_addr_t block_len, ram_size[GUEST_RAM_BANKS];
trace_xen_init_ram(machine->ram_size);
if (machine->ram_size <= GUEST_RAM0_SIZE) {
ram_size[0] = machine->ram_size;
ram_size[1] = 0;
block_len = GUEST_RAM0_BASE + ram_size[0];
} else {
ram_size[0] = GUEST_RAM0_SIZE;
ram_size[1] = machine->ram_size - GUEST_RAM0_SIZE;
block_len = GUEST_RAM1_BASE + ram_size[1];
}
memory_region_init_ram(&xen_memory, NULL, "xen.ram", block_len,
&error_fatal);
memory_region_init_alias(&ram_lo, NULL, "xen.ram.lo", &xen_memory,
GUEST_RAM0_BASE, ram_size[0]);
memory_region_add_subregion(sysmem, GUEST_RAM0_BASE, &ram_lo);
if (ram_size[1] > 0) {
memory_region_init_alias(&ram_hi, NULL, "xen.ram.hi", &xen_memory,
GUEST_RAM1_BASE, ram_size[1]);
memory_region_add_subregion(sysmem, GUEST_RAM1_BASE, &ram_hi);
}
/* 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);
}
void arch_handle_ioreq(XenIOState *state, ioreq_t *req)
{
hw_error("Invalid ioreq type 0x%x\n", req->type);
return;
}
void arch_xen_set_memory(XenIOState *state, MemoryRegionSection *section,
bool add)
{
}
void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length)
{
}
void qmp_xen_set_global_dirty_log(bool enable, Error **errp)
{
}
#ifdef CONFIG_TPM
static void xen_enable_tpm(XenArmState *xam)
{
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, xam->cfg.tpm_base_addr);
trace_xen_enable_tpm(xam->cfg.tpm_base_addr);
}
#endif
static void xen_arm_init(MachineState *machine)
{
XenArmState *xam = XEN_ARM(machine);
xam->state = g_new0(XenIOState, 1);
if (machine->ram_size == 0) {
warn_report("%s non-zero ram size not specified. QEMU machine started"
" without IOREQ (no emulated devices including virtio)",
MACHINE_CLASS(object_get_class(OBJECT(machine)))->desc);
return;
}
xen_init_ram(machine);
xen_register_ioreq(xam->state, machine->smp.cpus, &xen_memory_listener);
xen_create_virtio_mmio_devices(xam);
#ifdef CONFIG_TPM
if (xam->cfg.tpm_base_addr) {
xen_enable_tpm(xam);
} else {
warn_report("tpm-base-addr is not provided. TPM will not be enabled");
}
#endif
}
#ifdef CONFIG_TPM
static void xen_arm_get_tpm_base_addr(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
XenArmState *xam = XEN_ARM(obj);
uint64_t value = xam->cfg.tpm_base_addr;
visit_type_uint64(v, name, &value, errp);
}
static void xen_arm_set_tpm_base_addr(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
XenArmState *xam = XEN_ARM(obj);
uint64_t value;
if (!visit_type_uint64(v, name, &value, errp)) {
return;
}
xam->cfg.tpm_base_addr = value;
}
#endif
static void xen_arm_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
mc->desc = "Xen Para-virtualized PC";
mc->init = xen_arm_init;
mc->max_cpus = 1;
mc->default_machine_opts = "accel=xen";
/* Set explicitly here to make sure that real ram_size is passed */
mc->default_ram_size = 0;
#ifdef CONFIG_TPM
object_class_property_add(oc, "tpm-base-addr", "uint64_t",
xen_arm_get_tpm_base_addr,
xen_arm_set_tpm_base_addr,
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 const TypeInfo xen_arm_machine_type = {
.name = TYPE_XEN_ARM,
.parent = TYPE_MACHINE,
.class_init = xen_arm_machine_class_init,
.instance_size = sizeof(XenArmState),
};
static void xen_arm_machine_register_types(void)
{
type_register_static(&xen_arm_machine_type);
}
type_init(xen_arm_machine_register_types)

View File

@ -4,6 +4,7 @@ i386_ss.add(when: 'CONFIG_XEN', if_true: files(
))
i386_ss.add(when: ['CONFIG_XEN', xen], if_true: files(
'xen-hvm.c',
'xen-pvh.c',
))
i386_ss.add(when: 'CONFIG_XEN_BUS', if_true: files(

121
hw/i386/xen/xen-pvh.c Normal file
View File

@ -0,0 +1,121 @@
/*
* QEMU Xen PVH x86 Machine
*
* Copyright (c) 2024 Advanced Micro Devices, Inc.
* Written by Edgar E. Iglesias <edgar.iglesias@amd.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "hw/boards.h"
#include "sysemu/sysemu.h"
#include "hw/xen/arch_hvm.h"
#include <xen/hvm/hvm_info_table.h>
#include "hw/xen/xen-pvh-common.h"
#define TYPE_XEN_PVH_X86 MACHINE_TYPE_NAME("xenpvh")
OBJECT_DECLARE_SIMPLE_TYPE(XenPVHx86State, XEN_PVH_X86)
struct XenPVHx86State {
/*< private >*/
XenPVHMachineState parent;
DeviceState **cpu;
};
static DeviceState *xen_pvh_cpu_new(MachineState *ms,
int64_t apic_id)
{
Object *cpu = object_new(ms->cpu_type);
object_property_add_child(OBJECT(ms), "cpu[*]", cpu);
object_property_set_uint(cpu, "apic-id", apic_id, &error_fatal);
qdev_realize(DEVICE(cpu), NULL, &error_fatal);
object_unref(cpu);
return DEVICE(cpu);
}
static void xen_pvh_init(MachineState *ms)
{
XenPVHx86State *xp = XEN_PVH_X86(ms);
int i;
/* Create dummy cores. This will indirectly create the APIC MSI window. */
xp->cpu = g_malloc(sizeof xp->cpu[0] * ms->smp.max_cpus);
for (i = 0; i < ms->smp.max_cpus; i++) {
xp->cpu[i] = xen_pvh_cpu_new(ms, i);
}
}
static void xen_pvh_instance_init(Object *obj)
{
XenPVHMachineState *s = XEN_PVH_MACHINE(obj);
/* Default values. */
s->cfg.ram_low = (MemMapEntry) { 0x0, 0x80000000U };
s->cfg.ram_high = (MemMapEntry) { 0xC000000000ULL, 0x4000000000ULL };
s->cfg.pci_intx_irq_base = 16;
}
/*
* Deliver INTX interrupts to Xen guest.
*/
static void xen_pvh_set_pci_intx_irq(void *opaque, int irq, int level)
{
/*
* Since QEMU emulates all of the swizziling
* We don't want Xen to do any additional swizzling in
* xen_set_pci_intx_level() so we always set device to 0.
*/
if (xen_set_pci_intx_level(xen_domid, 0, 0, 0, irq, level)) {
error_report("xendevicemodel_set_pci_intx_level failed");
}
}
static void xen_pvh_machine_class_init(ObjectClass *oc, void *data)
{
XenPVHMachineClass *xpc = XEN_PVH_MACHINE_CLASS(oc);
MachineClass *mc = MACHINE_CLASS(oc);
mc->desc = "Xen PVH x86 machine";
mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE;
/* mc->max_cpus holds the MAX value allowed in the -smp cmd-line opts. */
mc->max_cpus = HVM_MAX_VCPUS;
/* We have an implementation specific init to create CPU objects. */
xpc->init = xen_pvh_init;
/*
* PCI INTX routing.
*
* We describe the mapping between the 4 INTX interrupt and GSIs
* using xen_set_pci_link_route(). xen_pvh_set_pci_intx_irq is
* used to deliver the interrupt.
*/
xpc->set_pci_intx_irq = xen_pvh_set_pci_intx_irq;
xpc->set_pci_link_route = xen_set_pci_link_route;
/* List of supported features known to work on PVH x86. */
xpc->has_pci = true;
xen_pvh_class_setup_common_props(xpc);
}
static const TypeInfo xen_pvh_x86_machine_type = {
.name = TYPE_XEN_PVH_X86,
.parent = TYPE_XEN_PVH_MACHINE,
.class_init = xen_pvh_machine_class_init,
.instance_init = xen_pvh_instance_init,
.instance_size = sizeof(XenPVHx86State),
};
static void xen_pvh_machine_register_types(void)
{
type_register_static(&xen_pvh_x86_machine_type);
}
type_init(xen_pvh_machine_register_types)

View File

@ -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(

View File

@ -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

362
hw/xen/xen-pvh-common.c Normal file
View File

@ -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);

View File

@ -0,0 +1,88 @@
/*
* QEMU Xen PVH machine - common code.
*
* Copyright (c) 2024 Advanced Micro Devices, Inc.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef XEN_PVH_COMMON_H__
#define XEN_PVH_COMMON_H__
#include <assert.h>
#include "hw/sysbus.h"
#include "hw/hw.h"
#include "hw/xen/xen-hvm-common.h"
#include "hw/pci-host/gpex.h"
#define TYPE_XEN_PVH_MACHINE MACHINE_TYPE_NAME("xen-pvh-base")
OBJECT_DECLARE_TYPE(XenPVHMachineState, XenPVHMachineClass,
XEN_PVH_MACHINE)
struct XenPVHMachineClass {
MachineClass parent;
/* PVH implementation specific init. */
void (*init)(MachineState *state);
/*
* set_pci_intx_irq - Deliver INTX irqs to the guest.
*
* @opaque: pointer to XenPVHMachineState.
* @irq: IRQ after swizzling, between 0-3.
* @level: IRQ level.
*/
void (*set_pci_intx_irq)(void *opaque, int irq, int level);
/*
* set_pci_link_route: - optional implementation call to setup
* routing between INTX IRQ (0 - 3) and GSI's.
*
* @line: line the INTx line (0 => A .. 3 => B)
* @irq: GSI
*/
int (*set_pci_link_route)(uint8_t line, uint8_t irq);
/*
* Each implementation can optionally enable features that it
* supports and are known to work.
*/
bool has_pci;
bool has_tpm;
bool has_virtio_mmio;
};
struct XenPVHMachineState {
/*< private >*/
MachineState parent;
XenIOState ioreq;
struct {
MemoryRegion low;
MemoryRegion high;
} ram;
struct {
GPEXHost gpex;
MemoryRegion mmio_alias;
MemoryRegion mmio_high_alias;
} pci;
struct {
MemMapEntry ram_low, ram_high;
MemMapEntry tpm;
/* Virtio-mmio */
MemMapEntry virtio_mmio;
uint32_t virtio_mmio_num;
uint32_t virtio_mmio_irq_base;
/* PCI */
MemMapEntry pci_ecam, pci_mmio, pci_mmio_high;
uint32_t pci_intx_irq_base;
} cfg;
};
void xen_pvh_class_setup_common_props(XenPVHMachineClass *xpc);
#endif