mirror of https://gitee.com/openkylin/qemu.git
qgraph project from GSoC 2018
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQEcBAABAgAGBQJcgUbdAAoJEL/70l94x66D4XIH/0owFpmv3QMUwi1dop35aC/k SXurtHSgJ5B5JEXNL0a34ZSaK9UNGH/ugIln5RYfANLfZ+NZdrlK3sPweAR7OSsk CqyHyjuLqRGwd61KYRZYbI2vjJ4zb9GKeiaQvoKXXyBO3CSs6A+OYGEklEdWiE1h fTU5PGnp3sMQrz+AQSnpoBWuJX3mAr7P94US3pZZTBC0h1FGIQezIOm1XJ7AdyYF KGqfqEk5mNKJLzkvnaCLLwFXlzMVOno+rhenOxU2B42p8UxA4YhhU6O8baaTfwMi BjgNLvJ1+EPRBqJd4XBgQXTHtAeJZ34SSU0fPveiXMyO1Np7C1oeoGmQMxPbnl4= =PRFv -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream-qgraph' into staging qgraph project from GSoC 2018 # gpg: Signature made Thu 07 Mar 2019 16:29:17 GMT # gpg: using RSA key BFFBD25F78C7AE83 # 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 * remotes/bonzini/tags/for-upstream-qgraph: (57 commits) qos-test: megasas test node qos-test: e1000 test node qos-test: eepro100 test node qos-test: es1370 test node qos-test: vmxnet3 test node qos-test: usb-hcd-ohci test node qos-test: spapr-phb test node qos-test: pcnet test node qos-test: nvme test node qos-test: ne2k_pci test node qos-test: ipoctal232 test node qos-test: tpci200 test node qos-test: ac97 test node tests: move virtio entirely to qos-test tests/libqos: remove pre-qgraph QVirtioPCIDevice API qos-test: virtio-scsi test node tests/libqos: virtio-scsi driver and interface nodes qos-test: vhost-user test node vhost-user-test: always use 256 MiB of guest memory tests/libqos: support multiqueue for virtio-net ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
06a1564fcc
|
@ -632,7 +632,7 @@ static void tcp_chr_update_read_handler(Chardev *chr)
|
|||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
|
||||
if (s->listener) {
|
||||
if (s->listener && s->state == TCP_CHARDEV_STATE_DISCONNECTED) {
|
||||
/*
|
||||
* It's possible that chardev context is changed in
|
||||
* qemu_chr_be_update_read_handlers(). Reset it for QIO net
|
||||
|
|
|
@ -7733,7 +7733,7 @@ fi
|
|||
# tests might fail. Prefer to keep the relevant files in their own
|
||||
# directory and symlink the directory instead.
|
||||
DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests tests/vm"
|
||||
DIRS="$DIRS tests/fp"
|
||||
DIRS="$DIRS tests/fp tests/qgraph"
|
||||
DIRS="$DIRS docs docs/interop fsdev scsi"
|
||||
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
|
||||
DIRS="$DIRS roms/seabios roms/vgabios"
|
||||
|
|
|
@ -45,6 +45,7 @@ typedef enum {
|
|||
MODULE_INIT_QOM,
|
||||
MODULE_INIT_TRACE,
|
||||
MODULE_INIT_XEN_BACKEND,
|
||||
MODULE_INIT_LIBQOS,
|
||||
MODULE_INIT_MAX
|
||||
} module_init_type;
|
||||
|
||||
|
@ -54,6 +55,7 @@ typedef enum {
|
|||
#define trace_init(function) module_init(function, MODULE_INIT_TRACE)
|
||||
#define xen_backend_init(function) module_init(function, \
|
||||
MODULE_INIT_XEN_BACKEND)
|
||||
#define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS)
|
||||
|
||||
#define block_module_load_one(lib) module_load_one("block-", lib)
|
||||
#define ui_module_load_one(lib) module_load_one("ui-", lib)
|
||||
|
|
|
@ -151,37 +151,10 @@ check-qtest-generic-y += tests/qmp-cmd-test$(EXESUF)
|
|||
check-qtest-generic-y += tests/device-introspect-test$(EXESUF)
|
||||
check-qtest-generic-y += tests/cdrom-test$(EXESUF)
|
||||
|
||||
check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF)
|
||||
|
||||
check-qtest-virtioserial-$(CONFIG_VIRTIO_SERIAL) += tests/virtio-console-test$(EXESUF)
|
||||
|
||||
check-qtest-virtio-$(CONFIG_VIRTIO_NET) += tests/virtio-net-test$(EXESUF)
|
||||
check-qtest-virtio-$(CONFIG_VIRTIO_BALLOON) += tests/virtio-balloon-test$(EXESUF)
|
||||
check-qtest-virtio-$(CONFIG_VIRTIO_BLK) += tests/virtio-blk-test$(EXESUF)
|
||||
check-qtest-virtio-$(CONFIG_VIRTIO_RNG) += tests/virtio-rng-test$(EXESUF)
|
||||
check-qtest-virtio-$(CONFIG_VIRTIO_SCSI) += tests/virtio-scsi-test$(EXESUF)
|
||||
ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
|
||||
check-qtest-virtio-$(CONFIG_VIRTIO_9P) += tests/virtio-9p-test$(EXESUF)
|
||||
endif
|
||||
check-qtest-virtio-$(CONFIG_VIRTIO_SERIAL) += tests/virtio-serial-test$(EXESUF)
|
||||
check-qtest-virtio-y += $(check-qtest-virtioserial-y)
|
||||
|
||||
check-qtest-pci-y += tests/e1000-test$(EXESUF)
|
||||
check-qtest-pci-y += tests/e1000e-test$(EXESUF)
|
||||
check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF)
|
||||
check-qtest-pci-$(CONFIG_PCNET_PCI) += tests/pcnet-test$(EXESUF)
|
||||
check-qtest-pci-$(CONFIG_EEPRO100_PCI) += tests/eepro100-test$(EXESUF)
|
||||
check-qtest-pci-$(CONFIG_NE2000_PCI) += tests/ne2000-test$(EXESUF)
|
||||
check-qtest-pci-$(CONFIG_NVME_PCI) += tests/nvme-test$(EXESUF)
|
||||
check-qtest-pci-$(CONFIG_AC97) += tests/ac97-test$(EXESUF)
|
||||
check-qtest-pci-$(CONFIG_ES1370) += tests/es1370-test$(EXESUF)
|
||||
check-qtest-pci-$(CONFIG_VIRTIO) += $(check-qtest-virtio-y)
|
||||
check-qtest-pci-$(CONFIG_IPACK) += tests/tpci200-test$(EXESUF)
|
||||
check-qtest-pci-$(CONFIG_IPACK) += $(check-qtest-ipack-y)
|
||||
check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF)
|
||||
check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF)
|
||||
check-qtest-pci-$(CONFIG_IVSHMEM_DEVICE) += tests/ivshmem-test$(EXESUF)
|
||||
check-qtest-pci-$(CONFIG_MEGASAS_SCSI_PCI) += tests/megasas-test$(EXESUF)
|
||||
|
||||
check-qtest-i386-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/fdc-test$(EXESUF)
|
||||
|
@ -203,11 +176,9 @@ check-qtest-i386-y += tests/drive_del-test$(EXESUF)
|
|||
check-qtest-i386-$(CONFIG_WDT_IB700) += tests/wdt_ib700-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/tco-test$(EXESUF)
|
||||
check-qtest-i386-y += $(check-qtest-pci-y)
|
||||
check-qtest-i386-$(CONFIG_VMXNET3_PCI) += tests/vmxnet3-test$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_PVPANIC) += tests/pvpanic-test$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_I82801B11) += tests/i82801b11-test$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_IOH3420) += tests/ioh3420-test$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_USB_OHCI) += tests/usb-hcd-ohci-test$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_USB_UHCI) += tests/usb-hcd-uhci-test$(EXESUF)
|
||||
ifeq ($(CONFIG_USB_ECHI)$(CONFIG_USB_UHCI),yy)
|
||||
check-qtest-i386-y += tests/usb-hcd-ehci-test$(EXESUF)
|
||||
|
@ -216,7 +187,6 @@ check-qtest-i386-$(CONFIG_USB_XHCI_NEC) += tests/usb-hcd-xhci-test$(EXESUF)
|
|||
check-qtest-i386-y += tests/cpu-plug-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/q35-test$(EXESUF)
|
||||
check-qtest-i386-y += tests/vmgenid-test$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_TPM_CRB) += tests/tpm-crb-swtpm-test$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_TPM_CRB) += tests/tpm-crb-test$(EXESUF)
|
||||
check-qtest-i386-$(CONFIG_TPM_TIS) += tests/tpm-tis-swtpm-test$(EXESUF)
|
||||
|
@ -229,7 +199,6 @@ check-qtest-i386-y += tests/test-announce-self$(EXESUF)
|
|||
check-qtest-i386-y += tests/test-x86-cpuid-compat$(EXESUF)
|
||||
check-qtest-i386-y += tests/numa-test$(EXESUF)
|
||||
check-qtest-x86_64-y += $(check-qtest-i386-y)
|
||||
check-qtest-x86_64-$(CONFIG_SDHCI) += tests/sdhci-test$(EXESUF)
|
||||
|
||||
check-qtest-alpha-y += tests/boot-serial-test$(EXESUF)
|
||||
check-qtest-alpha-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF)
|
||||
|
@ -260,17 +229,14 @@ check-qtest-ppc-y += tests/boot-serial-test$(EXESUF)
|
|||
check-qtest-ppc-$(CONFIG_M48T59) += tests/m48t59-test$(EXESUF)
|
||||
|
||||
check-qtest-ppc64-y += $(check-qtest-ppc-y)
|
||||
check-qtest-ppc64-$(CONFIG_PSERIES) += tests/spapr-phb-test$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_PSERIES) += tests/device-plug-test$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_POWERNV) += tests/pnv-xscom-test$(EXESUF)
|
||||
check-qtest-ppc64-y += tests/migration-test$(EXESUF)
|
||||
check-qtest-ppc64-y += tests/test-announce-self$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_PSERIES) += tests/rtas-test$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_USB_OHCI) += tests/usb-hcd-ohci-test$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_USB_UHCI) += tests/usb-hcd-uhci-test$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_USB_XHCI_NEC) += tests/usb-hcd-xhci-test$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_VIRTIO) += $(check-qtest-virtio-y)
|
||||
check-qtest-ppc64-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF)
|
||||
check-qtest-ppc64-$(CONFIG_RTL8139_PCI) += tests/test-filter-redirector$(EXESUF)
|
||||
|
@ -296,14 +262,11 @@ check-qtest-arm-y += tests/pca9552-test$(EXESUF)
|
|||
check-qtest-arm-y += tests/ds1338-test$(EXESUF)
|
||||
check-qtest-arm-y += tests/microbit-test$(EXESUF)
|
||||
check-qtest-arm-y += tests/m25p80-test$(EXESUF)
|
||||
check-qtest-arm-$(CONFIG_VIRTIO_BLK) += tests/virtio-blk-test$(EXESUF)
|
||||
check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF)
|
||||
check-qtest-arm-y += tests/boot-serial-test$(EXESUF)
|
||||
check-qtest-arm-$(CONFIG_SDHCI) += tests/sdhci-test$(EXESUF)
|
||||
check-qtest-arm-y += tests/hexloader-test$(EXESUF)
|
||||
|
||||
check-qtest-aarch64-y = tests/numa-test$(EXESUF)
|
||||
check-qtest-aarch64-$(CONFIG_SDHCI) += tests/sdhci-test$(EXESUF)
|
||||
check-qtest-aarch64-y += tests/boot-serial-test$(EXESUF)
|
||||
check-qtest-aarch64-y += tests/migration-test$(EXESUF)
|
||||
|
||||
|
@ -732,7 +695,10 @@ tests/test-crypto-ivgen$(EXESUF): tests/test-crypto-ivgen.o $(test-crypto-obj-y)
|
|||
tests/test-crypto-afsplit$(EXESUF): tests/test-crypto-afsplit.o $(test-crypto-obj-y)
|
||||
tests/test-crypto-block$(EXESUF): tests/test-crypto-block.o $(test-crypto-obj-y)
|
||||
|
||||
libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o
|
||||
libqgraph-obj-y = tests/libqos/qgraph.o
|
||||
|
||||
libqos-obj-y = $(libqgraph-obj-y) tests/libqos/pci.o tests/libqos/fw_cfg.o
|
||||
libqos-obj-y += tests/libqos/malloc.o
|
||||
libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
|
||||
libqos-spapr-obj-y = $(libqos-obj-y) tests/libqos/malloc-spapr.o
|
||||
libqos-spapr-obj-y += tests/libqos/libqos-spapr.o
|
||||
|
@ -744,7 +710,64 @@ libqos-pc-obj-y += tests/libqos/ahci.o
|
|||
libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
|
||||
libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o
|
||||
libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o
|
||||
libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o
|
||||
|
||||
# Devices
|
||||
qos-test-obj-y = tests/qos-test.o $(libqgraph-obj-y)
|
||||
qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
|
||||
qos-test-obj-y += tests/libqos/e1000e.o
|
||||
qos-test-obj-y += tests/libqos/sdhci.o
|
||||
qos-test-obj-y += tests/libqos/tpci200.o
|
||||
qos-test-obj-y += tests/libqos/virtio.o
|
||||
qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o
|
||||
qos-test-obj-y += tests/libqos/virtio-balloon.o
|
||||
qos-test-obj-y += tests/libqos/virtio-blk.o
|
||||
qos-test-obj-y += tests/libqos/virtio-mmio.o
|
||||
qos-test-obj-y += tests/libqos/virtio-net.o
|
||||
qos-test-obj-y += tests/libqos/virtio-pci.o
|
||||
qos-test-obj-y += tests/libqos/virtio-rng.o
|
||||
qos-test-obj-y += tests/libqos/virtio-scsi.o
|
||||
qos-test-obj-y += tests/libqos/virtio-serial.o
|
||||
|
||||
# Machines
|
||||
qos-test-obj-y += tests/libqos/aarch64-xlnx-zcu102-machine.o
|
||||
qos-test-obj-y += tests/libqos/arm-raspi2-machine.o
|
||||
qos-test-obj-y += tests/libqos/arm-sabrelite-machine.o
|
||||
qos-test-obj-y += tests/libqos/arm-smdkc210-machine.o
|
||||
qos-test-obj-y += tests/libqos/arm-virt-machine.o
|
||||
qos-test-obj-y += tests/libqos/arm-xilinx-zynq-a9-machine.o
|
||||
qos-test-obj-y += tests/libqos/ppc64_pseries-machine.o
|
||||
qos-test-obj-y += tests/libqos/x86_64_pc-machine.o
|
||||
|
||||
# Tests
|
||||
qos-test-obj-y += tests/ac97-test.o
|
||||
qos-test-obj-y += tests/e1000-test.o
|
||||
qos-test-obj-y += tests/e1000e-test.o
|
||||
qos-test-obj-y += tests/eepro100-test.o
|
||||
qos-test-obj-y += tests/es1370-test.o
|
||||
qos-test-obj-y += tests/ipoctal232-test.o
|
||||
qos-test-obj-y += tests/megasas-test.o
|
||||
qos-test-obj-y += tests/ne2000-test.o
|
||||
qos-test-obj-y += tests/nvme-test.o
|
||||
qos-test-obj-y += tests/pci-test.o
|
||||
qos-test-obj-y += tests/pcnet-test.o
|
||||
qos-test-obj-y += tests/sdhci-test.o
|
||||
qos-test-obj-y += tests/spapr-phb-test.o
|
||||
qos-test-obj-y += tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y)
|
||||
qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y)
|
||||
qos-test-obj-y += tests/virtio-test.o
|
||||
qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o
|
||||
qos-test-obj-y += tests/virtio-blk-test.o
|
||||
qos-test-obj-y += tests/virtio-net-test.o
|
||||
qos-test-obj-y += tests/virtio-rng-test.o
|
||||
qos-test-obj-y += tests/virtio-scsi-test.o
|
||||
qos-test-obj-y += tests/virtio-serial-test.o
|
||||
qos-test-obj-y += tests/vmxnet3-test.o
|
||||
|
||||
check-unit-y += tests/test-qgraph$(EXESUF)
|
||||
tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y)
|
||||
|
||||
check-qtest-generic-y += tests/qos-test$(EXESUF)
|
||||
tests/qos-test$(EXESUF): $(qos-test-obj-y)
|
||||
|
||||
tests/qmp-test$(EXESUF): tests/qmp-test.o
|
||||
tests/qmp-cmd-test$(EXESUF): tests/qmp-cmd-test.o
|
||||
|
@ -753,7 +776,6 @@ tests/rtc-test$(EXESUF): tests/rtc-test.o
|
|||
tests/m48t59-test$(EXESUF): tests/m48t59-test.o
|
||||
tests/hexloader-test$(EXESUF): tests/hexloader-test.o
|
||||
tests/endianness-test$(EXESUF): tests/endianness-test.o
|
||||
tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y)
|
||||
tests/prom-env-test$(EXESUF): tests/prom-env-test.o $(libqos-obj-y)
|
||||
tests/rtas-test$(EXESUF): tests/rtas-test.o $(libqos-spapr-obj-y)
|
||||
tests/fdc-test$(EXESUF): tests/fdc-test.o
|
||||
|
@ -775,50 +797,27 @@ tests/m25p80-test$(EXESUF): tests/m25p80-test.o
|
|||
tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y)
|
||||
tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y)
|
||||
tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y)
|
||||
tests/e1000-test$(EXESUF): tests/e1000-test.o
|
||||
tests/e1000e-test$(EXESUF): tests/e1000e-test.o $(libqos-pc-obj-y)
|
||||
tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y)
|
||||
tests/pcnet-test$(EXESUF): tests/pcnet-test.o
|
||||
tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o
|
||||
tests/eepro100-test$(EXESUF): tests/eepro100-test.o
|
||||
tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o
|
||||
tests/ne2000-test$(EXESUF): tests/ne2000-test.o
|
||||
tests/wdt_ib700-test$(EXESUF): tests/wdt_ib700-test.o
|
||||
tests/tco-test$(EXESUF): tests/tco-test.o $(libqos-pc-obj-y)
|
||||
tests/virtio-balloon-test$(EXESUF): tests/virtio-balloon-test.o $(libqos-virtio-obj-y)
|
||||
tests/virtio-blk-test$(EXESUF): tests/virtio-blk-test.o $(libqos-virtio-obj-y)
|
||||
tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o
|
||||
tests/virtio-net-test$(EXESUF): tests/virtio-net-test.o $(libqos-pc-obj-y) $(libqos-virtio-obj-y)
|
||||
tests/virtio-rng-test$(EXESUF): tests/virtio-rng-test.o $(libqos-pc-obj-y)
|
||||
tests/virtio-scsi-test$(EXESUF): tests/virtio-scsi-test.o $(libqos-virtio-obj-y)
|
||||
tests/virtio-9p-test$(EXESUF): tests/virtio-9p-test.o $(libqos-virtio-obj-y)
|
||||
tests/virtio-serial-test$(EXESUF): tests/virtio-serial-test.o $(libqos-virtio-obj-y)
|
||||
tests/virtio-console-test$(EXESUF): tests/virtio-console-test.o $(libqos-virtio-obj-y)
|
||||
tests/tpci200-test$(EXESUF): tests/tpci200-test.o
|
||||
tests/display-vga-test$(EXESUF): tests/display-vga-test.o
|
||||
tests/ipoctal232-test$(EXESUF): tests/ipoctal232-test.o
|
||||
tests/qom-test$(EXESUF): tests/qom-test.o
|
||||
tests/test-hmp$(EXESUF): tests/test-hmp.o
|
||||
tests/machine-none-test$(EXESUF): tests/machine-none-test.o
|
||||
tests/device-plug-test$(EXESUF): tests/device-plug-test.o
|
||||
tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-virtio-obj-y)
|
||||
tests/nvme-test$(EXESUF): tests/nvme-test.o $(libqos-pc-obj-y)
|
||||
tests/drive_del-test$(EXESUF): tests/drive_del-test.o
|
||||
tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o
|
||||
tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o
|
||||
tests/ac97-test$(EXESUF): tests/ac97-test.o
|
||||
tests/es1370-test$(EXESUF): tests/es1370-test.o
|
||||
tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o
|
||||
tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o
|
||||
tests/usb-hcd-ohci-test$(EXESUF): tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y)
|
||||
tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y)
|
||||
tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y)
|
||||
tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
|
||||
tests/cpu-plug-test$(EXESUF): tests/cpu-plug-test.o
|
||||
tests/migration-test$(EXESUF): tests/migration-test.o
|
||||
tests/test-announce-self$(EXESUF): tests/test-announce-self.o
|
||||
tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o $(test-util-obj-y) \
|
||||
$(qtest-obj-y) $(test-io-obj-y) $(libqos-virtio-obj-y) $(libqos-pc-obj-y) \
|
||||
$(chardev-obj-y)
|
||||
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
|
||||
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y)
|
||||
tests/test-keyval$(EXESUF): tests/test-keyval.o $(test-util-obj-y) $(test-qapi-obj-y)
|
||||
|
@ -828,14 +827,12 @@ tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y)
|
|||
tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y)
|
||||
tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y)
|
||||
tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
|
||||
tests/megasas-test$(EXESUF): tests/megasas-test.o $(libqos-spapr-obj-y) $(libqos-pc-obj-y)
|
||||
tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a
|
||||
tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
|
||||
tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o
|
||||
tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y)
|
||||
tests/numa-test$(EXESUF): tests/numa-test.o
|
||||
tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o
|
||||
tests/sdhci-test$(EXESUF): tests/sdhci-test.o $(libqos-pc-obj-y)
|
||||
tests/cdrom-test$(EXESUF): tests/cdrom-test.o tests/boot-sector.o $(libqos-obj-y)
|
||||
|
||||
tests/migration/stress$(EXESUF): tests/migration/stress.o
|
||||
|
|
|
@ -9,23 +9,48 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void nop(void)
|
||||
typedef struct QAC97 QAC97;
|
||||
|
||||
struct QAC97 {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice dev;
|
||||
};
|
||||
|
||||
static void *ac97_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QAC97 *ac97 = obj;
|
||||
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return &ac97->dev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in e1000e\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void *ac97_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
|
||||
{
|
||||
int ret;
|
||||
QAC97 *ac97 = g_new0(QAC97, 1);
|
||||
QPCIBus *bus = pci_bus;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/ac97/nop", nop);
|
||||
|
||||
qtest_start("-device AC97");
|
||||
ret = g_test_run();
|
||||
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
qpci_device_init(&ac97->dev, bus, addr);
|
||||
ac97->obj.get_driver = ac97_get_driver;
|
||||
return &ac97->obj;
|
||||
}
|
||||
|
||||
static void ac97_register_nodes(void)
|
||||
{
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0",
|
||||
};
|
||||
add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
|
||||
|
||||
qos_node_create_driver("AC97", ac97_create);
|
||||
qos_node_produces("AC97", "pci-device");
|
||||
qos_node_consumes("AC97", "pci-bus", &opts);
|
||||
}
|
||||
|
||||
libqos_init(ac97_register_nodes);
|
||||
|
|
|
@ -162,7 +162,7 @@ static AHCIQState *ahci_vboot(const char *cli, va_list ap)
|
|||
s = g_new0(AHCIQState, 1);
|
||||
s->parent = qtest_pc_vboot(cli, ap);
|
||||
global_qtest = s->parent->qts;
|
||||
alloc_set_flags(s->parent->alloc, ALLOC_LEAK_ASSERT);
|
||||
alloc_set_flags(&s->parent->alloc, ALLOC_LEAK_ASSERT);
|
||||
|
||||
/* Verify that we have an AHCI device present. */
|
||||
s->dev = get_ahci_device(s->parent->qts, &s->fingerprint);
|
||||
|
@ -1039,7 +1039,7 @@ static void test_dma_fragmented(void)
|
|||
generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE);
|
||||
|
||||
/* Create a DMA buffer in guest memory, and write our pattern to it. */
|
||||
ptr = guest_alloc(ahci->parent->alloc, bufsize);
|
||||
ptr = guest_alloc(&ahci->parent->alloc, bufsize);
|
||||
g_assert(ptr);
|
||||
bufwrite(ptr, tx, bufsize);
|
||||
|
||||
|
@ -1059,7 +1059,7 @@ static void test_dma_fragmented(void)
|
|||
|
||||
/* Read back the guest's receive buffer into local memory */
|
||||
bufread(ptr, rx, bufsize);
|
||||
guest_free(ahci->parent->alloc, ptr);
|
||||
guest_free(&ahci->parent->alloc, ptr);
|
||||
|
||||
g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0);
|
||||
|
||||
|
|
|
@ -63,6 +63,24 @@ static void test_drive_without_dev(void)
|
|||
qtest_end();
|
||||
}
|
||||
|
||||
/*
|
||||
* qvirtio_get_dev_type:
|
||||
* Returns: the preferred virtio bus/device type for the current architecture.
|
||||
* TODO: delete this
|
||||
*/
|
||||
static const char *qvirtio_get_dev_type(void)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
|
||||
if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) {
|
||||
return "device"; /* for virtio-mmio */
|
||||
} else if (g_str_equal(arch, "s390x")) {
|
||||
return "ccw";
|
||||
} else {
|
||||
return "pci";
|
||||
}
|
||||
}
|
||||
|
||||
static void test_after_failed_device_add(void)
|
||||
{
|
||||
char driver[32];
|
||||
|
@ -119,16 +137,11 @@ static void test_drive_del_device_del(void)
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
qtest_add_func("/drive_del/without-dev", test_drive_without_dev);
|
||||
|
||||
/* TODO I guess any arch with a hot-pluggable virtio bus would do */
|
||||
if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64") ||
|
||||
!strcmp(arch, "ppc") || !strcmp(arch, "ppc64") ||
|
||||
!strcmp(arch, "s390x")) {
|
||||
if (qvirtio_get_dev_type() != NULL) {
|
||||
qtest_add_func("/drive_del/after_failed_device_add",
|
||||
test_after_failed_device_add);
|
||||
qtest_add_func("/blockdev/drive_del_device_del",
|
||||
|
|
|
@ -9,22 +9,15 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void test_device(gconstpointer data)
|
||||
{
|
||||
const char *model = data;
|
||||
QTestState *s;
|
||||
char *args;
|
||||
typedef struct QE1000 QE1000;
|
||||
|
||||
args = g_strdup_printf("-device %s", model);
|
||||
s = qtest_start(args);
|
||||
|
||||
if (s) {
|
||||
qtest_quit(s);
|
||||
}
|
||||
g_free(args);
|
||||
}
|
||||
struct QE1000 {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice dev;
|
||||
};
|
||||
|
||||
static const char *models[] = {
|
||||
"e1000",
|
||||
|
@ -33,19 +26,42 @@ static const char *models[] = {
|
|||
"e1000-82545em",
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void *e1000_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
int i;
|
||||
QE1000 *e1000 = obj;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(models); i++) {
|
||||
char *path;
|
||||
|
||||
path = g_strdup_printf("e1000/%s", models[i]);
|
||||
qtest_add_data_func(path, models[i], test_device);
|
||||
g_free(path);
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return &e1000->dev;
|
||||
}
|
||||
|
||||
return g_test_run();
|
||||
fprintf(stderr, "%s not present in e1000e\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *e1000_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
|
||||
{
|
||||
QE1000 *e1000 = g_new0(QE1000, 1);
|
||||
QPCIBus *bus = pci_bus;
|
||||
|
||||
qpci_device_init(&e1000->dev, bus, addr);
|
||||
e1000->obj.get_driver = e1000_get_driver;
|
||||
|
||||
return &e1000->obj;
|
||||
}
|
||||
|
||||
static void e1000_register_nodes(void)
|
||||
{
|
||||
int i;
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0",
|
||||
};
|
||||
add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(models); i++) {
|
||||
qos_node_create_driver(models[i], e1000_create);
|
||||
qos_node_consumes(models[i], "pci-bus", &opts);
|
||||
qos_node_produces(models[i], "pci-device");
|
||||
}
|
||||
}
|
||||
|
||||
libqos_init(e1000_register_nodes);
|
||||
|
|
|
@ -32,210 +32,9 @@
|
|||
#include "qemu/iov.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/malloc-pc.h"
|
||||
#include "libqos/malloc-generic.h"
|
||||
#include "libqos/e1000e.h"
|
||||
|
||||
#define E1000E_IMS (0x00d0)
|
||||
|
||||
#define E1000E_STATUS (0x0008)
|
||||
#define E1000E_STATUS_LU BIT(1)
|
||||
#define E1000E_STATUS_ASDV1000 BIT(9)
|
||||
|
||||
#define E1000E_CTRL (0x0000)
|
||||
#define E1000E_CTRL_RESET BIT(26)
|
||||
|
||||
#define E1000E_RCTL (0x0100)
|
||||
#define E1000E_RCTL_EN BIT(1)
|
||||
#define E1000E_RCTL_UPE BIT(3)
|
||||
#define E1000E_RCTL_MPE BIT(4)
|
||||
|
||||
#define E1000E_RFCTL (0x5008)
|
||||
#define E1000E_RFCTL_EXTEN BIT(15)
|
||||
|
||||
#define E1000E_TCTL (0x0400)
|
||||
#define E1000E_TCTL_EN BIT(1)
|
||||
|
||||
#define E1000E_CTRL_EXT (0x0018)
|
||||
#define E1000E_CTRL_EXT_DRV_LOAD BIT(28)
|
||||
#define E1000E_CTRL_EXT_TXLSFLOW BIT(22)
|
||||
|
||||
#define E1000E_RX0_MSG_ID (0)
|
||||
#define E1000E_TX0_MSG_ID (1)
|
||||
#define E1000E_OTHER_MSG_ID (2)
|
||||
|
||||
#define E1000E_IVAR (0x00E4)
|
||||
#define E1000E_IVAR_TEST_CFG ((E1000E_RX0_MSG_ID << 0) | BIT(3) | \
|
||||
(E1000E_TX0_MSG_ID << 8) | BIT(11) | \
|
||||
(E1000E_OTHER_MSG_ID << 16) | BIT(19) | \
|
||||
BIT(31))
|
||||
|
||||
#define E1000E_RING_LEN (0x1000)
|
||||
#define E1000E_TXD_LEN (16)
|
||||
#define E1000E_RXD_LEN (16)
|
||||
|
||||
#define E1000E_TDBAL (0x3800)
|
||||
#define E1000E_TDBAH (0x3804)
|
||||
#define E1000E_TDLEN (0x3808)
|
||||
#define E1000E_TDH (0x3810)
|
||||
#define E1000E_TDT (0x3818)
|
||||
|
||||
#define E1000E_RDBAL (0x2800)
|
||||
#define E1000E_RDBAH (0x2804)
|
||||
#define E1000E_RDLEN (0x2808)
|
||||
#define E1000E_RDH (0x2810)
|
||||
#define E1000E_RDT (0x2818)
|
||||
|
||||
typedef struct e1000e_device {
|
||||
QPCIDevice *pci_dev;
|
||||
QPCIBar mac_regs;
|
||||
|
||||
uint64_t tx_ring;
|
||||
uint64_t rx_ring;
|
||||
} e1000e_device;
|
||||
|
||||
static int test_sockets[2];
|
||||
static QGuestAllocator *test_alloc;
|
||||
static QPCIBus *test_bus;
|
||||
|
||||
static void e1000e_pci_foreach_callback(QPCIDevice *dev, int devfn, void *data)
|
||||
{
|
||||
QPCIDevice **res = data;
|
||||
|
||||
g_assert_null(*res);
|
||||
*res = dev;
|
||||
}
|
||||
|
||||
static QPCIDevice *e1000e_device_find(QPCIBus *bus)
|
||||
{
|
||||
static const int e1000e_vendor_id = 0x8086;
|
||||
static const int e1000e_dev_id = 0x10D3;
|
||||
|
||||
QPCIDevice *e1000e_dev = NULL;
|
||||
|
||||
qpci_device_foreach(bus, e1000e_vendor_id, e1000e_dev_id,
|
||||
e1000e_pci_foreach_callback, &e1000e_dev);
|
||||
|
||||
g_assert_nonnull(e1000e_dev);
|
||||
|
||||
return e1000e_dev;
|
||||
}
|
||||
|
||||
static void e1000e_macreg_write(e1000e_device *d, uint32_t reg, uint32_t val)
|
||||
{
|
||||
qpci_io_writel(d->pci_dev, d->mac_regs, reg, val);
|
||||
}
|
||||
|
||||
static uint32_t e1000e_macreg_read(e1000e_device *d, uint32_t reg)
|
||||
{
|
||||
return qpci_io_readl(d->pci_dev, d->mac_regs, reg);
|
||||
}
|
||||
|
||||
static void e1000e_device_init(QPCIBus *bus, e1000e_device *d)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
d->pci_dev = e1000e_device_find(bus);
|
||||
|
||||
/* Enable the device */
|
||||
qpci_device_enable(d->pci_dev);
|
||||
|
||||
/* Map BAR0 (mac registers) */
|
||||
d->mac_regs = qpci_iomap(d->pci_dev, 0, NULL);
|
||||
|
||||
/* Reset the device */
|
||||
val = e1000e_macreg_read(d, E1000E_CTRL);
|
||||
e1000e_macreg_write(d, E1000E_CTRL, val | E1000E_CTRL_RESET);
|
||||
|
||||
/* Enable and configure MSI-X */
|
||||
qpci_msix_enable(d->pci_dev);
|
||||
e1000e_macreg_write(d, E1000E_IVAR, E1000E_IVAR_TEST_CFG);
|
||||
|
||||
/* Check the device status - link and speed */
|
||||
val = e1000e_macreg_read(d, E1000E_STATUS);
|
||||
g_assert_cmphex(val & (E1000E_STATUS_LU | E1000E_STATUS_ASDV1000),
|
||||
==, E1000E_STATUS_LU | E1000E_STATUS_ASDV1000);
|
||||
|
||||
/* Initialize TX/RX logic */
|
||||
e1000e_macreg_write(d, E1000E_RCTL, 0);
|
||||
e1000e_macreg_write(d, E1000E_TCTL, 0);
|
||||
|
||||
/* Notify the device that the driver is ready */
|
||||
val = e1000e_macreg_read(d, E1000E_CTRL_EXT);
|
||||
e1000e_macreg_write(d, E1000E_CTRL_EXT,
|
||||
val | E1000E_CTRL_EXT_DRV_LOAD | E1000E_CTRL_EXT_TXLSFLOW);
|
||||
|
||||
/* Allocate and setup TX ring */
|
||||
d->tx_ring = guest_alloc(test_alloc, E1000E_RING_LEN);
|
||||
g_assert(d->tx_ring != 0);
|
||||
|
||||
e1000e_macreg_write(d, E1000E_TDBAL, (uint32_t) d->tx_ring);
|
||||
e1000e_macreg_write(d, E1000E_TDBAH, (uint32_t) (d->tx_ring >> 32));
|
||||
e1000e_macreg_write(d, E1000E_TDLEN, E1000E_RING_LEN);
|
||||
e1000e_macreg_write(d, E1000E_TDT, 0);
|
||||
e1000e_macreg_write(d, E1000E_TDH, 0);
|
||||
|
||||
/* Enable transmit */
|
||||
e1000e_macreg_write(d, E1000E_TCTL, E1000E_TCTL_EN);
|
||||
|
||||
/* Allocate and setup RX ring */
|
||||
d->rx_ring = guest_alloc(test_alloc, E1000E_RING_LEN);
|
||||
g_assert(d->rx_ring != 0);
|
||||
|
||||
e1000e_macreg_write(d, E1000E_RDBAL, (uint32_t)d->rx_ring);
|
||||
e1000e_macreg_write(d, E1000E_RDBAH, (uint32_t)(d->rx_ring >> 32));
|
||||
e1000e_macreg_write(d, E1000E_RDLEN, E1000E_RING_LEN);
|
||||
e1000e_macreg_write(d, E1000E_RDT, 0);
|
||||
e1000e_macreg_write(d, E1000E_RDH, 0);
|
||||
|
||||
/* Enable receive */
|
||||
e1000e_macreg_write(d, E1000E_RFCTL, E1000E_RFCTL_EXTEN);
|
||||
e1000e_macreg_write(d, E1000E_RCTL, E1000E_RCTL_EN |
|
||||
E1000E_RCTL_UPE |
|
||||
E1000E_RCTL_MPE);
|
||||
|
||||
/* Enable all interrupts */
|
||||
e1000e_macreg_write(d, E1000E_IMS, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
static void e1000e_tx_ring_push(e1000e_device *d, void *descr)
|
||||
{
|
||||
uint32_t tail = e1000e_macreg_read(d, E1000E_TDT);
|
||||
uint32_t len = e1000e_macreg_read(d, E1000E_TDLEN) / E1000E_TXD_LEN;
|
||||
|
||||
memwrite(d->tx_ring + tail * E1000E_TXD_LEN, descr, E1000E_TXD_LEN);
|
||||
e1000e_macreg_write(d, E1000E_TDT, (tail + 1) % len);
|
||||
|
||||
/* Read WB data for the packet transmitted */
|
||||
memread(d->tx_ring + tail * E1000E_TXD_LEN, descr, E1000E_TXD_LEN);
|
||||
}
|
||||
|
||||
static void e1000e_rx_ring_push(e1000e_device *d, void *descr)
|
||||
{
|
||||
uint32_t tail = e1000e_macreg_read(d, E1000E_RDT);
|
||||
uint32_t len = e1000e_macreg_read(d, E1000E_RDLEN) / E1000E_RXD_LEN;
|
||||
|
||||
memwrite(d->rx_ring + tail * E1000E_RXD_LEN, descr, E1000E_RXD_LEN);
|
||||
e1000e_macreg_write(d, E1000E_RDT, (tail + 1) % len);
|
||||
|
||||
/* Read WB data for the packet received */
|
||||
memread(d->rx_ring + tail * E1000E_RXD_LEN, descr, E1000E_RXD_LEN);
|
||||
}
|
||||
|
||||
static void e1000e_wait_isr(e1000e_device *d, uint16_t msg_id)
|
||||
{
|
||||
guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
|
||||
|
||||
do {
|
||||
if (qpci_msix_pending(d->pci_dev, msg_id)) {
|
||||
return;
|
||||
}
|
||||
clock_step(10000);
|
||||
} while (g_get_monotonic_time() < end_time);
|
||||
|
||||
g_error("Timeout expired");
|
||||
}
|
||||
|
||||
static void e1000e_send_verify(e1000e_device *d)
|
||||
static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc)
|
||||
{
|
||||
struct {
|
||||
uint64_t buffer_addr;
|
||||
|
@ -268,7 +67,7 @@ static void e1000e_send_verify(e1000e_device *d)
|
|||
uint32_t recv_len;
|
||||
|
||||
/* Prepare test data buffer */
|
||||
uint64_t data = guest_alloc(test_alloc, data_len);
|
||||
uint64_t data = guest_alloc(alloc, data_len);
|
||||
memwrite(data, "TEST", 5);
|
||||
|
||||
/* Prepare TX descriptor */
|
||||
|
@ -296,10 +95,10 @@ static void e1000e_send_verify(e1000e_device *d)
|
|||
g_assert_cmpstr(buffer, == , "TEST");
|
||||
|
||||
/* Free test data buffer */
|
||||
guest_free(test_alloc, data);
|
||||
guest_free(alloc, data);
|
||||
}
|
||||
|
||||
static void e1000e_receive_verify(e1000e_device *d)
|
||||
static void e1000e_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc)
|
||||
{
|
||||
union {
|
||||
struct {
|
||||
|
@ -348,7 +147,7 @@ static void e1000e_receive_verify(e1000e_device *d)
|
|||
g_assert_cmpint(ret, == , sizeof(test) + sizeof(len));
|
||||
|
||||
/* Prepare test data buffer */
|
||||
uint64_t data = guest_alloc(test_alloc, data_len);
|
||||
uint64_t data = guest_alloc(alloc, data_len);
|
||||
|
||||
/* Prepare RX descriptor */
|
||||
memset(&descr, 0, sizeof(descr));
|
||||
|
@ -369,111 +168,108 @@ static void e1000e_receive_verify(e1000e_device *d)
|
|||
g_assert_cmpstr(buffer, == , "TEST");
|
||||
|
||||
/* Free test data buffer */
|
||||
guest_free(test_alloc, data);
|
||||
guest_free(alloc, data);
|
||||
}
|
||||
|
||||
static void e1000e_device_clear(QPCIBus *bus, e1000e_device *d)
|
||||
static void test_e1000e_init(void *obj, void *data, QGuestAllocator * alloc)
|
||||
{
|
||||
qpci_iounmap(d->pci_dev, d->mac_regs);
|
||||
qpci_msix_disable(d->pci_dev);
|
||||
/* init does nothing */
|
||||
}
|
||||
|
||||
static void data_test_init(e1000e_device *d)
|
||||
static void test_e1000e_tx(void *obj, void *data, QGuestAllocator * alloc)
|
||||
{
|
||||
char *cmdline;
|
||||
QE1000E_PCI *e1000e = obj;
|
||||
QE1000E *d = &e1000e->e1000e;
|
||||
QOSGraphObject *e_object = obj;
|
||||
QPCIDevice *dev = e_object->get_driver(e_object, "pci-device");
|
||||
|
||||
int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, test_sockets);
|
||||
g_assert_cmpint(ret, != , -1);
|
||||
/* FIXME: add spapr support */
|
||||
if (qpci_check_buggy_msi(dev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmdline = g_strdup_printf("-netdev socket,fd=%d,id=hs0 "
|
||||
"-device e1000e,netdev=hs0", test_sockets[1]);
|
||||
g_assert_nonnull(cmdline);
|
||||
|
||||
qtest_start(cmdline);
|
||||
g_free(cmdline);
|
||||
|
||||
test_alloc = pc_alloc_init(global_qtest);
|
||||
g_assert_nonnull(test_alloc);
|
||||
|
||||
test_bus = qpci_init_pc(global_qtest, test_alloc);
|
||||
g_assert_nonnull(test_bus);
|
||||
|
||||
e1000e_device_init(test_bus, d);
|
||||
e1000e_send_verify(d, data, alloc);
|
||||
}
|
||||
|
||||
static void data_test_clear(e1000e_device *d)
|
||||
static void test_e1000e_rx(void *obj, void *data, QGuestAllocator * alloc)
|
||||
{
|
||||
e1000e_device_clear(test_bus, d);
|
||||
close(test_sockets[0]);
|
||||
pc_alloc_uninit(test_alloc);
|
||||
g_free(d->pci_dev);
|
||||
qpci_free_pc(test_bus);
|
||||
qtest_end();
|
||||
QE1000E_PCI *e1000e = obj;
|
||||
QE1000E *d = &e1000e->e1000e;
|
||||
QOSGraphObject *e_object = obj;
|
||||
QPCIDevice *dev = e_object->get_driver(e_object, "pci-device");
|
||||
|
||||
/* FIXME: add spapr support */
|
||||
if (qpci_check_buggy_msi(dev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
e1000e_receive_verify(d, data, alloc);
|
||||
}
|
||||
|
||||
static void test_e1000e_init(gconstpointer data)
|
||||
{
|
||||
e1000e_device d;
|
||||
|
||||
data_test_init(&d);
|
||||
data_test_clear(&d);
|
||||
}
|
||||
|
||||
static void test_e1000e_tx(gconstpointer data)
|
||||
{
|
||||
e1000e_device d;
|
||||
|
||||
data_test_init(&d);
|
||||
e1000e_send_verify(&d);
|
||||
data_test_clear(&d);
|
||||
}
|
||||
|
||||
static void test_e1000e_rx(gconstpointer data)
|
||||
{
|
||||
e1000e_device d;
|
||||
|
||||
data_test_init(&d);
|
||||
e1000e_receive_verify(&d);
|
||||
data_test_clear(&d);
|
||||
}
|
||||
|
||||
static void test_e1000e_multiple_transfers(gconstpointer data)
|
||||
static void test_e1000e_multiple_transfers(void *obj, void *data,
|
||||
QGuestAllocator *alloc)
|
||||
{
|
||||
static const long iterations = 4 * 1024;
|
||||
long i;
|
||||
|
||||
e1000e_device d;
|
||||
QE1000E_PCI *e1000e = obj;
|
||||
QE1000E *d = &e1000e->e1000e;
|
||||
QOSGraphObject *e_object = obj;
|
||||
QPCIDevice *dev = e_object->get_driver(e_object, "pci-device");
|
||||
|
||||
data_test_init(&d);
|
||||
|
||||
for (i = 0; i < iterations; i++) {
|
||||
e1000e_send_verify(&d);
|
||||
e1000e_receive_verify(&d);
|
||||
/* FIXME: add spapr support */
|
||||
if (qpci_check_buggy_msi(dev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < iterations; i++) {
|
||||
e1000e_send_verify(d, data, alloc);
|
||||
e1000e_receive_verify(d, data, alloc);
|
||||
}
|
||||
|
||||
data_test_clear(&d);
|
||||
}
|
||||
|
||||
static void test_e1000e_hotplug(gconstpointer data)
|
||||
static void test_e1000e_hotplug(void *obj, void *data, QGuestAllocator * alloc)
|
||||
{
|
||||
qtest_start("-device e1000e");
|
||||
|
||||
qtest_qmp_device_add("e1000e", "e1000e_net", "{'addr': '0x06'}");
|
||||
qpci_unplug_acpi_device_test("e1000e_net", 0x06);
|
||||
|
||||
qtest_end();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void data_test_clear(void *sockets)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
int *test_sockets = sockets;
|
||||
|
||||
qtest_add_data_func("e1000e/init", NULL, test_e1000e_init);
|
||||
qtest_add_data_func("e1000e/tx", NULL, test_e1000e_tx);
|
||||
qtest_add_data_func("e1000e/rx", NULL, test_e1000e_rx);
|
||||
qtest_add_data_func("e1000e/multiple_transfers", NULL,
|
||||
test_e1000e_multiple_transfers);
|
||||
qtest_add_data_func("e1000e/hotplug", NULL, test_e1000e_hotplug);
|
||||
|
||||
return g_test_run();
|
||||
close(test_sockets[0]);
|
||||
qos_invalidate_command_line();
|
||||
close(test_sockets[1]);
|
||||
g_free(test_sockets);
|
||||
}
|
||||
|
||||
static void *data_test_init(GString *cmd_line, void *arg)
|
||||
{
|
||||
int *test_sockets = g_new(int, 2);
|
||||
int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, test_sockets);
|
||||
g_assert_cmpint(ret, != , -1);
|
||||
|
||||
g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ",
|
||||
test_sockets[1]);
|
||||
|
||||
g_test_queue_destroy(data_test_clear, test_sockets);
|
||||
return test_sockets;
|
||||
}
|
||||
|
||||
static void register_e1000e_test(void)
|
||||
{
|
||||
QOSGraphTestOptions opts = {
|
||||
.before = data_test_init,
|
||||
};
|
||||
|
||||
qos_add_test("init", "e1000e", test_e1000e_init, &opts);
|
||||
qos_add_test("tx", "e1000e", test_e1000e_tx, &opts);
|
||||
qos_add_test("rx", "e1000e", test_e1000e_rx, &opts);
|
||||
qos_add_test("multiple_transfers", "e1000e",
|
||||
test_e1000e_multiple_transfers, &opts);
|
||||
qos_add_test("hotplug", "e1000e", test_e1000e_hotplug, &opts);
|
||||
}
|
||||
|
||||
libqos_init(register_e1000e_test);
|
||||
|
|
|
@ -9,23 +9,15 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
static void test_device(gconstpointer data)
|
||||
{
|
||||
const char *model = data;
|
||||
QTestState *s;
|
||||
char *args;
|
||||
typedef struct QEEPRO100 QEEPRO100;
|
||||
|
||||
args = g_strdup_printf("-device %s", model);
|
||||
s = qtest_start(args);
|
||||
|
||||
/* Tests only initialization so far. TODO: Implement functional tests */
|
||||
|
||||
if (s) {
|
||||
qtest_quit(s);
|
||||
}
|
||||
g_free(args);
|
||||
}
|
||||
struct QEEPRO100 {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice dev;
|
||||
};
|
||||
|
||||
static const char *models[] = {
|
||||
"i82550",
|
||||
|
@ -43,19 +35,42 @@ static const char *models[] = {
|
|||
"i82801",
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void *eepro100_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
int i;
|
||||
QEEPRO100 *eepro100 = obj;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(models); i++) {
|
||||
char *path;
|
||||
|
||||
path = g_strdup_printf("eepro100/%s", models[i]);
|
||||
qtest_add_data_func(path, models[i], test_device);
|
||||
g_free(path);
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return &eepro100->dev;
|
||||
}
|
||||
|
||||
return g_test_run();
|
||||
fprintf(stderr, "%s not present in eepro100\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *eepro100_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
|
||||
{
|
||||
QEEPRO100 *eepro100 = g_new0(QEEPRO100, 1);
|
||||
QPCIBus *bus = pci_bus;
|
||||
|
||||
qpci_device_init(&eepro100->dev, bus, addr);
|
||||
eepro100->obj.get_driver = eepro100_get_driver;
|
||||
|
||||
return &eepro100->obj;
|
||||
}
|
||||
|
||||
static void eepro100_register_nodes(void)
|
||||
{
|
||||
int i;
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0",
|
||||
};
|
||||
|
||||
add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
|
||||
for (i = 0; i < ARRAY_SIZE(models); i++) {
|
||||
qos_node_create_driver(models[i], eepro100_create);
|
||||
qos_node_consumes(models[i], "pci-bus", &opts);
|
||||
qos_node_produces(models[i], "pci-device");
|
||||
}
|
||||
}
|
||||
|
||||
libqos_init(eepro100_register_nodes);
|
||||
|
|
|
@ -9,23 +9,49 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void nop(void)
|
||||
typedef struct QES1370 QES1370;
|
||||
|
||||
struct QES1370 {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice dev;
|
||||
};
|
||||
|
||||
static void *es1370_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QES1370 *es1370 = obj;
|
||||
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return &es1370->dev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in e1000e\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void *es1370_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
|
||||
{
|
||||
int ret;
|
||||
QES1370 *es1370 = g_new0(QES1370, 1);
|
||||
QPCIBus *bus = pci_bus;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/es1370/nop", nop);
|
||||
qpci_device_init(&es1370->dev, bus, addr);
|
||||
es1370->obj.get_driver = es1370_get_driver;
|
||||
|
||||
qtest_start("-device ES1370");
|
||||
ret = g_test_run();
|
||||
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
return &es1370->obj;
|
||||
}
|
||||
|
||||
static void es1370_register_nodes(void)
|
||||
{
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0",
|
||||
};
|
||||
add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
|
||||
|
||||
qos_node_create_driver("ES1370", es1370_create);
|
||||
qos_node_consumes("ES1370", "pci-bus", &opts);
|
||||
qos_node_produces("ES1370", "pci-device");
|
||||
}
|
||||
|
||||
libqos_init(es1370_register_nodes);
|
||||
|
|
|
@ -38,7 +38,7 @@ static QPCIBus *test_start_get_bus(const TestData *s)
|
|||
cmdline = g_strdup_printf("-smp %d", s->num_cpus);
|
||||
qtest_start(cmdline);
|
||||
g_free(cmdline);
|
||||
return qpci_init_pc(global_qtest, NULL);
|
||||
return qpci_new_pc(global_qtest, NULL);
|
||||
}
|
||||
|
||||
static void test_i440fx_defaults(gconstpointer opaque)
|
||||
|
|
|
@ -120,7 +120,7 @@ enum {
|
|||
#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
|
||||
|
||||
static QPCIBus *pcibus = NULL;
|
||||
static QGuestAllocator *guest_malloc;
|
||||
static QGuestAllocator guest_malloc;
|
||||
|
||||
static char tmp_path[] = "/tmp/qtest.XXXXXX";
|
||||
static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX";
|
||||
|
@ -135,7 +135,7 @@ static void ide_test_start(const char *cmdline_fmt, ...)
|
|||
va_end(ap);
|
||||
|
||||
qtest_start(cmdline);
|
||||
guest_malloc = pc_alloc_init(global_qtest);
|
||||
pc_alloc_init(&guest_malloc, global_qtest, 0);
|
||||
|
||||
g_free(cmdline);
|
||||
}
|
||||
|
@ -146,8 +146,7 @@ static void ide_test_quit(void)
|
|||
qpci_free_pc(pcibus);
|
||||
pcibus = NULL;
|
||||
}
|
||||
pc_alloc_uninit(guest_malloc);
|
||||
guest_malloc = NULL;
|
||||
alloc_destroy(&guest_malloc);
|
||||
qtest_end();
|
||||
}
|
||||
|
||||
|
@ -157,7 +156,7 @@ static QPCIDevice *get_pci_device(QPCIBar *bmdma_bar, QPCIBar *ide_bar)
|
|||
uint16_t vendor_id, device_id;
|
||||
|
||||
if (!pcibus) {
|
||||
pcibus = qpci_init_pc(global_qtest, NULL);
|
||||
pcibus = qpci_new_pc(global_qtest, NULL);
|
||||
}
|
||||
|
||||
/* Find PCI device and verify it's the right one */
|
||||
|
@ -246,7 +245,7 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
|
|||
|
||||
/* Setup PRDT */
|
||||
len = sizeof(*prdt) * prdt_entries;
|
||||
guest_prdt = guest_alloc(guest_malloc, len);
|
||||
guest_prdt = guest_alloc(&guest_malloc, len);
|
||||
memwrite(guest_prdt, prdt, len);
|
||||
qpci_io_writel(dev, bmdma_bar, bmreg_prdt, guest_prdt);
|
||||
|
||||
|
@ -311,7 +310,7 @@ static void test_bmdma_simple_rw(void)
|
|||
uint8_t *buf;
|
||||
uint8_t *cmpbuf;
|
||||
size_t len = 512;
|
||||
uintptr_t guest_buf = guest_alloc(guest_malloc, len);
|
||||
uintptr_t guest_buf = guest_alloc(&guest_malloc, len);
|
||||
|
||||
PrdtEntry prdt[] = {
|
||||
{
|
||||
|
@ -381,7 +380,7 @@ static void test_bmdma_trim(void)
|
|||
const uint64_t bad_range = trim_range_le(TEST_IMAGE_SIZE / 512 - 1, 2);
|
||||
size_t len = 512;
|
||||
uint8_t *buf;
|
||||
uintptr_t guest_buf = guest_alloc(guest_malloc, len);
|
||||
uintptr_t guest_buf = guest_alloc(&guest_malloc, len);
|
||||
|
||||
PrdtEntry prdt[] = {
|
||||
{
|
||||
|
@ -625,7 +624,7 @@ static void make_dirty(uint8_t device)
|
|||
|
||||
dev = get_pci_device(&bmdma_bar, &ide_bar);
|
||||
|
||||
guest_buf = guest_alloc(guest_malloc, len);
|
||||
guest_buf = guest_alloc(&guest_malloc, len);
|
||||
buf = g_malloc(len);
|
||||
memset(buf, rand() % 255 + 1, len);
|
||||
g_assert(guest_buf);
|
||||
|
@ -986,7 +985,7 @@ static void test_cdrom_dma(void)
|
|||
"-device ide-cd,drive=sr0,bus=ide.0", tmp_path);
|
||||
qtest_irq_intercept_in(global_qtest, "ioapic");
|
||||
|
||||
guest_buf = guest_alloc(guest_malloc, len);
|
||||
guest_buf = guest_alloc(&guest_malloc, len);
|
||||
prdt[0].addr = cpu_to_le32(guest_buf);
|
||||
prdt[0].size = cpu_to_le32(len | PRDT_EOT);
|
||||
|
||||
|
|
|
@ -9,23 +9,40 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
typedef struct QIpoctal232 QIpoctal232;
|
||||
|
||||
struct QIpoctal232 {
|
||||
QOSGraphObject obj;
|
||||
};
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void nop(void)
|
||||
static void nop(void *obj, void *data, QGuestAllocator *alloc)
|
||||
{
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void *ipoctal232_create(void *pci_bus, QGuestAllocator *alloc,
|
||||
void *addr)
|
||||
{
|
||||
int ret;
|
||||
QIpoctal232 *ipoctal232 = g_new0(QIpoctal232, 1);
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/ipoctal232/tpci200/nop", nop);
|
||||
|
||||
qtest_start("-device tpci200,id=ipack0 -device ipoctal232,bus=ipack0.0");
|
||||
ret = g_test_run();
|
||||
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
return &ipoctal232->obj;
|
||||
}
|
||||
|
||||
static void ipoctal232_register_nodes(void)
|
||||
{
|
||||
qos_node_create_driver("ipoctal232", ipoctal232_create);
|
||||
qos_node_consumes("ipoctal232", "ipack", &(QOSGraphEdgeOptions) {
|
||||
.extra_device_opts = "bus=ipack0.0",
|
||||
});
|
||||
}
|
||||
|
||||
libqos_init(ipoctal232_register_nodes);
|
||||
|
||||
static void register_ipoctal232_test(void)
|
||||
{
|
||||
qos_add_test("nop", "ipoctal232", nop, NULL);
|
||||
}
|
||||
|
||||
libqos_init(register_ipoctal232_test);
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "sdhci.h"
|
||||
|
||||
typedef struct QXlnxZCU102Machine QXlnxZCU102Machine;
|
||||
|
||||
struct QXlnxZCU102Machine {
|
||||
QOSGraphObject obj;
|
||||
QGuestAllocator alloc;
|
||||
QSDHCI_MemoryMapped sdhci;
|
||||
};
|
||||
|
||||
#define ARM_PAGE_SIZE 4096
|
||||
#define XLNX_ZCU102_RAM_ADDR 0
|
||||
#define XLNX_ZCU102_RAM_SIZE 0x20000000
|
||||
|
||||
static void *xlnx_zcu102_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QXlnxZCU102Machine *machine = object;
|
||||
if (!g_strcmp0(interface, "memory")) {
|
||||
return &machine->alloc;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in aarch64/xlnx-zcu102\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static QOSGraphObject *xlnx_zcu102_get_device(void *obj, const char *device)
|
||||
{
|
||||
QXlnxZCU102Machine *machine = obj;
|
||||
if (!g_strcmp0(device, "generic-sdhci")) {
|
||||
return &machine->sdhci.obj;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in aarch64/xlnx-zcu102\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void xlnx_zcu102_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QXlnxZCU102Machine *machine = (QXlnxZCU102Machine *) obj;
|
||||
alloc_destroy(&machine->alloc);
|
||||
}
|
||||
|
||||
static void *qos_create_machine_aarch64_xlnx_zcu102(QTestState *qts)
|
||||
{
|
||||
QXlnxZCU102Machine *machine = g_new0(QXlnxZCU102Machine, 1);
|
||||
|
||||
alloc_init(&machine->alloc, 0,
|
||||
XLNX_ZCU102_RAM_ADDR + (1 << 20),
|
||||
XLNX_ZCU102_RAM_ADDR + XLNX_ZCU102_RAM_SIZE,
|
||||
ARM_PAGE_SIZE);
|
||||
|
||||
machine->obj.get_device = xlnx_zcu102_get_device;
|
||||
machine->obj.get_driver = xlnx_zcu102_get_driver;
|
||||
machine->obj.destructor = xlnx_zcu102_destructor;
|
||||
/* Datasheet: UG1085 (v1.7) */
|
||||
qos_init_sdhci_mm(&machine->sdhci, qts, 0xff160000, &(QSDHCIProperties) {
|
||||
.version = 3,
|
||||
.baseclock = 0,
|
||||
.capab.sdma = true,
|
||||
.capab.reg = 0x280737ec6481
|
||||
});
|
||||
return &machine->obj;
|
||||
}
|
||||
|
||||
static void xlnx_zcu102_register_nodes(void)
|
||||
{
|
||||
qos_node_create_machine("aarch64/xlnx-zcu102",
|
||||
qos_create_machine_aarch64_xlnx_zcu102);
|
||||
qos_node_contains("aarch64/xlnx-zcu102", "generic-sdhci", NULL);
|
||||
}
|
||||
|
||||
libqos_init(xlnx_zcu102_register_nodes);
|
|
@ -130,7 +130,7 @@ QPCIDevice *get_ahci_device(QTestState *qts, uint32_t *fingerprint)
|
|||
uint32_t ahci_fingerprint;
|
||||
QPCIBus *pcibus;
|
||||
|
||||
pcibus = qpci_init_pc(qts, NULL);
|
||||
pcibus = qpci_new_pc(qts, NULL);
|
||||
|
||||
/* Find the AHCI PCI device and verify it's the right one. */
|
||||
ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02));
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "sdhci.h"
|
||||
|
||||
#define ARM_PAGE_SIZE 4096
|
||||
#define RASPI2_RAM_ADDR 0
|
||||
#define RASPI2_RAM_SIZE 0x20000000
|
||||
|
||||
typedef struct QRaspi2Machine QRaspi2Machine;
|
||||
|
||||
struct QRaspi2Machine {
|
||||
QOSGraphObject obj;
|
||||
QGuestAllocator alloc;
|
||||
QSDHCI_MemoryMapped sdhci;
|
||||
};
|
||||
|
||||
static void *raspi2_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QRaspi2Machine *machine = object;
|
||||
if (!g_strcmp0(interface, "memory")) {
|
||||
return &machine->alloc;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/raspi2\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static QOSGraphObject *raspi2_get_device(void *obj, const char *device)
|
||||
{
|
||||
QRaspi2Machine *machine = obj;
|
||||
if (!g_strcmp0(device, "generic-sdhci")) {
|
||||
return &machine->sdhci.obj;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/raspi2\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void raspi2_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QRaspi2Machine *machine = (QRaspi2Machine *) obj;
|
||||
alloc_destroy(&machine->alloc);
|
||||
}
|
||||
|
||||
static void *qos_create_machine_arm_raspi2(QTestState *qts)
|
||||
{
|
||||
QRaspi2Machine *machine = g_new0(QRaspi2Machine, 1);
|
||||
|
||||
alloc_init(&machine->alloc, 0,
|
||||
RASPI2_RAM_ADDR + (1 << 20),
|
||||
RASPI2_RAM_ADDR + RASPI2_RAM_SIZE,
|
||||
ARM_PAGE_SIZE);
|
||||
machine->obj.get_device = raspi2_get_device;
|
||||
machine->obj.get_driver = raspi2_get_driver;
|
||||
machine->obj.destructor = raspi2_destructor;
|
||||
qos_init_sdhci_mm(&machine->sdhci, qts, 0x3f300000, &(QSDHCIProperties) {
|
||||
.version = 3,
|
||||
.baseclock = 52,
|
||||
.capab.sdma = false,
|
||||
.capab.reg = 0x052134b4
|
||||
});
|
||||
return &machine->obj;
|
||||
}
|
||||
|
||||
static void raspi2_register_nodes(void)
|
||||
{
|
||||
qos_node_create_machine("arm/raspi2", qos_create_machine_arm_raspi2);
|
||||
qos_node_contains("arm/raspi2", "generic-sdhci", NULL);
|
||||
}
|
||||
|
||||
libqos_init(raspi2_register_nodes);
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "sdhci.h"
|
||||
|
||||
#define ARM_PAGE_SIZE 4096
|
||||
#define SABRELITE_RAM_START 0x10000000
|
||||
#define SABRELITE_RAM_END 0x30000000
|
||||
|
||||
typedef struct QSabreliteMachine QSabreliteMachine;
|
||||
|
||||
struct QSabreliteMachine {
|
||||
QOSGraphObject obj;
|
||||
QGuestAllocator alloc;
|
||||
QSDHCI_MemoryMapped sdhci;
|
||||
};
|
||||
|
||||
static void *sabrelite_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QSabreliteMachine *machine = object;
|
||||
if (!g_strcmp0(interface, "memory")) {
|
||||
return &machine->alloc;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/sabrelite\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static QOSGraphObject *sabrelite_get_device(void *obj, const char *device)
|
||||
{
|
||||
QSabreliteMachine *machine = obj;
|
||||
if (!g_strcmp0(device, "generic-sdhci")) {
|
||||
return &machine->sdhci.obj;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/sabrelite\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void sabrelite_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QSabreliteMachine *machine = (QSabreliteMachine *) obj;
|
||||
alloc_destroy(&machine->alloc);
|
||||
}
|
||||
|
||||
static void *qos_create_machine_arm_sabrelite(QTestState *qts)
|
||||
{
|
||||
QSabreliteMachine *machine = g_new0(QSabreliteMachine, 1);
|
||||
|
||||
alloc_init(&machine->alloc, 0,
|
||||
SABRELITE_RAM_START,
|
||||
SABRELITE_RAM_END,
|
||||
ARM_PAGE_SIZE);
|
||||
machine->obj.get_device = sabrelite_get_device;
|
||||
machine->obj.get_driver = sabrelite_get_driver;
|
||||
machine->obj.destructor = sabrelite_destructor;
|
||||
qos_init_sdhci_mm(&machine->sdhci, qts, 0x02190000, &(QSDHCIProperties) {
|
||||
.version = 3,
|
||||
.baseclock = 0,
|
||||
.capab.sdma = true,
|
||||
.capab.reg = 0x057834b4,
|
||||
});
|
||||
return &machine->obj;
|
||||
}
|
||||
|
||||
static void sabrelite_register_nodes(void)
|
||||
{
|
||||
qos_node_create_machine("arm/sabrelite", qos_create_machine_arm_sabrelite);
|
||||
qos_node_contains("arm/sabrelite", "generic-sdhci", NULL);
|
||||
}
|
||||
|
||||
libqos_init(sabrelite_register_nodes);
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "sdhci.h"
|
||||
|
||||
#define ARM_PAGE_SIZE 4096
|
||||
#define SMDKC210_RAM_ADDR 0x40000000ull
|
||||
#define SMDKC210_RAM_SIZE 0x40000000ull
|
||||
|
||||
typedef struct QSmdkc210Machine QSmdkc210Machine;
|
||||
|
||||
struct QSmdkc210Machine {
|
||||
QOSGraphObject obj;
|
||||
QGuestAllocator alloc;
|
||||
QSDHCI_MemoryMapped sdhci;
|
||||
};
|
||||
|
||||
static void *smdkc210_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QSmdkc210Machine *machine = object;
|
||||
if (!g_strcmp0(interface, "memory")) {
|
||||
return &machine->alloc;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/smdkc210\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static QOSGraphObject *smdkc210_get_device(void *obj, const char *device)
|
||||
{
|
||||
QSmdkc210Machine *machine = obj;
|
||||
if (!g_strcmp0(device, "generic-sdhci")) {
|
||||
return &machine->sdhci.obj;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/smdkc210\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void smdkc210_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QSmdkc210Machine *machine = (QSmdkc210Machine *) obj;
|
||||
alloc_destroy(&machine->alloc);
|
||||
}
|
||||
|
||||
static void *qos_create_machine_arm_smdkc210(QTestState *qts)
|
||||
{
|
||||
QSmdkc210Machine *machine = g_new0(QSmdkc210Machine, 1);
|
||||
|
||||
alloc_init(&machine->alloc, 0,
|
||||
SMDKC210_RAM_ADDR,
|
||||
SMDKC210_RAM_ADDR + SMDKC210_RAM_SIZE,
|
||||
ARM_PAGE_SIZE);
|
||||
machine->obj.get_device = smdkc210_get_device;
|
||||
machine->obj.get_driver = smdkc210_get_driver;
|
||||
machine->obj.destructor = smdkc210_destructor;
|
||||
qos_init_sdhci_mm(&machine->sdhci, qts, 0x12510000, &(QSDHCIProperties) {
|
||||
.version = 2,
|
||||
.baseclock = 0,
|
||||
.capab.sdma = true,
|
||||
.capab.reg = 0x5e80080,
|
||||
});
|
||||
return &machine->obj;
|
||||
}
|
||||
|
||||
static void smdkc210_register_nodes(void)
|
||||
{
|
||||
qos_node_create_machine("arm/smdkc210", qos_create_machine_arm_smdkc210);
|
||||
qos_node_contains("arm/smdkc210", "generic-sdhci", NULL);
|
||||
}
|
||||
|
||||
libqos_init(smdkc210_register_nodes);
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio-mmio.h"
|
||||
|
||||
#define ARM_PAGE_SIZE 4096
|
||||
#define VIRTIO_MMIO_BASE_ADDR 0x0A003E00
|
||||
#define ARM_VIRT_RAM_ADDR 0x40000000
|
||||
#define ARM_VIRT_RAM_SIZE 0x20000000
|
||||
#define VIRTIO_MMIO_SIZE 0x00000200
|
||||
|
||||
typedef struct QVirtMachine QVirtMachine;
|
||||
|
||||
struct QVirtMachine {
|
||||
QOSGraphObject obj;
|
||||
QGuestAllocator alloc;
|
||||
QVirtioMMIODevice virtio_mmio;
|
||||
};
|
||||
|
||||
static void virt_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QVirtMachine *machine = (QVirtMachine *) obj;
|
||||
alloc_destroy(&machine->alloc);
|
||||
}
|
||||
|
||||
static void *virt_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QVirtMachine *machine = object;
|
||||
if (!g_strcmp0(interface, "memory")) {
|
||||
return &machine->alloc;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/virtio\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static QOSGraphObject *virt_get_device(void *obj, const char *device)
|
||||
{
|
||||
QVirtMachine *machine = obj;
|
||||
if (!g_strcmp0(device, "virtio-mmio")) {
|
||||
return &machine->virtio_mmio.obj;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/virtio\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *qos_create_machine_arm_virt(QTestState *qts)
|
||||
{
|
||||
QVirtMachine *machine = g_new0(QVirtMachine, 1);
|
||||
|
||||
alloc_init(&machine->alloc, 0,
|
||||
ARM_VIRT_RAM_ADDR,
|
||||
ARM_VIRT_RAM_ADDR + ARM_VIRT_RAM_SIZE,
|
||||
ARM_PAGE_SIZE);
|
||||
qvirtio_mmio_init_device(&machine->virtio_mmio, qts, VIRTIO_MMIO_BASE_ADDR,
|
||||
VIRTIO_MMIO_SIZE);
|
||||
|
||||
machine->obj.get_device = virt_get_device;
|
||||
machine->obj.get_driver = virt_get_driver;
|
||||
machine->obj.destructor = virt_destructor;
|
||||
return machine;
|
||||
}
|
||||
|
||||
static void virtio_mmio_register_nodes(void)
|
||||
{
|
||||
qos_node_create_machine("arm/virt", qos_create_machine_arm_virt);
|
||||
qos_node_contains("arm/virt", "virtio-mmio", NULL);
|
||||
}
|
||||
|
||||
libqos_init(virtio_mmio_register_nodes);
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "sdhci.h"
|
||||
|
||||
typedef struct QXilinxZynqA9Machine QXilinxZynqA9Machine;
|
||||
|
||||
struct QXilinxZynqA9Machine {
|
||||
QOSGraphObject obj;
|
||||
QGuestAllocator alloc;
|
||||
QSDHCI_MemoryMapped sdhci;
|
||||
};
|
||||
|
||||
#define ARM_PAGE_SIZE 4096
|
||||
#define XILINX_ZYNQ_A9_RAM_ADDR 0
|
||||
#define XILINX_ZYNQ_A9_RAM_SIZE 0x20000000
|
||||
|
||||
static void *xilinx_zynq_a9_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QXilinxZynqA9Machine *machine = object;
|
||||
if (!g_strcmp0(interface, "memory")) {
|
||||
return &machine->alloc;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/xilinx-zynq-a9\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static QOSGraphObject *xilinx_zynq_a9_get_device(void *obj, const char *device)
|
||||
{
|
||||
QXilinxZynqA9Machine *machine = obj;
|
||||
if (!g_strcmp0(device, "generic-sdhci")) {
|
||||
return &machine->sdhci.obj;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/xilinx-zynq-a9\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void xilinx_zynq_a9_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QXilinxZynqA9Machine *machine = (QXilinxZynqA9Machine *) obj;
|
||||
alloc_destroy(&machine->alloc);
|
||||
}
|
||||
|
||||
static void *qos_create_machine_arm_xilinx_zynq_a9(QTestState *qts)
|
||||
{
|
||||
QXilinxZynqA9Machine *machine = g_new0(QXilinxZynqA9Machine, 1);
|
||||
|
||||
alloc_init(&machine->alloc, 0,
|
||||
XILINX_ZYNQ_A9_RAM_ADDR + (1 << 20),
|
||||
XILINX_ZYNQ_A9_RAM_ADDR + XILINX_ZYNQ_A9_RAM_SIZE,
|
||||
ARM_PAGE_SIZE);
|
||||
|
||||
machine->obj.get_device = xilinx_zynq_a9_get_device;
|
||||
machine->obj.get_driver = xilinx_zynq_a9_get_driver;
|
||||
machine->obj.destructor = xilinx_zynq_a9_destructor;
|
||||
/* Datasheet: UG585 (v1.12.1) */
|
||||
qos_init_sdhci_mm(&machine->sdhci, qts, 0xe0100000, &(QSDHCIProperties) {
|
||||
.version = 2,
|
||||
.baseclock = 0,
|
||||
.capab.sdma = true,
|
||||
.capab.reg = 0x69ec0080,
|
||||
});
|
||||
return &machine->obj;
|
||||
}
|
||||
|
||||
static void xilinx_zynq_a9_register_nodes(void)
|
||||
{
|
||||
qos_node_create_machine("arm/xilinx-zynq-a9",
|
||||
qos_create_machine_arm_xilinx_zynq_a9);
|
||||
qos_node_contains("arm/xilinx-zynq-a9", "generic-sdhci", NULL);
|
||||
}
|
||||
|
||||
libqos_init(xilinx_zynq_a9_register_nodes);
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "qemu-common.h"
|
||||
#include "libqos/pci-pc.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/iov.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "e1000e.h"
|
||||
|
||||
#define E1000E_IMS (0x00d0)
|
||||
|
||||
#define E1000E_STATUS (0x0008)
|
||||
#define E1000E_STATUS_LU BIT(1)
|
||||
#define E1000E_STATUS_ASDV1000 BIT(9)
|
||||
|
||||
#define E1000E_CTRL (0x0000)
|
||||
#define E1000E_CTRL_RESET BIT(26)
|
||||
|
||||
#define E1000E_RCTL (0x0100)
|
||||
#define E1000E_RCTL_EN BIT(1)
|
||||
#define E1000E_RCTL_UPE BIT(3)
|
||||
#define E1000E_RCTL_MPE BIT(4)
|
||||
|
||||
#define E1000E_RFCTL (0x5008)
|
||||
#define E1000E_RFCTL_EXTEN BIT(15)
|
||||
|
||||
#define E1000E_TCTL (0x0400)
|
||||
#define E1000E_TCTL_EN BIT(1)
|
||||
|
||||
#define E1000E_CTRL_EXT (0x0018)
|
||||
#define E1000E_CTRL_EXT_DRV_LOAD BIT(28)
|
||||
#define E1000E_CTRL_EXT_TXLSFLOW BIT(22)
|
||||
|
||||
#define E1000E_IVAR (0x00E4)
|
||||
#define E1000E_IVAR_TEST_CFG ((E1000E_RX0_MSG_ID << 0) | BIT(3) | \
|
||||
(E1000E_TX0_MSG_ID << 8) | BIT(11) | \
|
||||
(E1000E_OTHER_MSG_ID << 16) | BIT(19) | \
|
||||
BIT(31))
|
||||
|
||||
#define E1000E_RING_LEN (0x1000)
|
||||
|
||||
#define E1000E_TDBAL (0x3800)
|
||||
|
||||
#define E1000E_TDBAH (0x3804)
|
||||
#define E1000E_TDH (0x3810)
|
||||
|
||||
#define E1000E_RDBAL (0x2800)
|
||||
#define E1000E_RDBAH (0x2804)
|
||||
#define E1000E_RDH (0x2810)
|
||||
|
||||
#define E1000E_TXD_LEN (16)
|
||||
#define E1000E_RXD_LEN (16)
|
||||
|
||||
static void e1000e_macreg_write(QE1000E *d, uint32_t reg, uint32_t val)
|
||||
{
|
||||
QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
|
||||
qpci_io_writel(&d_pci->pci_dev, d_pci->mac_regs, reg, val);
|
||||
}
|
||||
|
||||
static uint32_t e1000e_macreg_read(QE1000E *d, uint32_t reg)
|
||||
{
|
||||
QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
|
||||
return qpci_io_readl(&d_pci->pci_dev, d_pci->mac_regs, reg);
|
||||
}
|
||||
|
||||
void e1000e_tx_ring_push(QE1000E *d, void *descr)
|
||||
{
|
||||
uint32_t tail = e1000e_macreg_read(d, E1000E_TDT);
|
||||
uint32_t len = e1000e_macreg_read(d, E1000E_TDLEN) / E1000E_TXD_LEN;
|
||||
|
||||
memwrite(d->tx_ring + tail * E1000E_TXD_LEN, descr, E1000E_TXD_LEN);
|
||||
e1000e_macreg_write(d, E1000E_TDT, (tail + 1) % len);
|
||||
|
||||
/* Read WB data for the packet transmitted */
|
||||
memread(d->tx_ring + tail * E1000E_TXD_LEN, descr, E1000E_TXD_LEN);
|
||||
}
|
||||
|
||||
void e1000e_rx_ring_push(QE1000E *d, void *descr)
|
||||
{
|
||||
uint32_t tail = e1000e_macreg_read(d, E1000E_RDT);
|
||||
uint32_t len = e1000e_macreg_read(d, E1000E_RDLEN) / E1000E_RXD_LEN;
|
||||
|
||||
memwrite(d->rx_ring + tail * E1000E_RXD_LEN, descr, E1000E_RXD_LEN);
|
||||
e1000e_macreg_write(d, E1000E_RDT, (tail + 1) % len);
|
||||
|
||||
/* Read WB data for the packet received */
|
||||
memread(d->rx_ring + tail * E1000E_RXD_LEN, descr, E1000E_RXD_LEN);
|
||||
}
|
||||
|
||||
static void e1000e_foreach_callback(QPCIDevice *dev, int devfn, void *data)
|
||||
{
|
||||
QPCIDevice *res = data;
|
||||
memcpy(res, dev, sizeof(QPCIDevice));
|
||||
g_free(dev);
|
||||
}
|
||||
|
||||
void e1000e_wait_isr(QE1000E *d, uint16_t msg_id)
|
||||
{
|
||||
QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
|
||||
guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
|
||||
|
||||
do {
|
||||
if (qpci_msix_pending(&d_pci->pci_dev, msg_id)) {
|
||||
return;
|
||||
}
|
||||
clock_step(10000);
|
||||
} while (g_get_monotonic_time() < end_time);
|
||||
|
||||
g_error("Timeout expired");
|
||||
}
|
||||
|
||||
static void e1000e_pci_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QE1000E_PCI *epci = (QE1000E_PCI *) obj;
|
||||
qpci_iounmap(&epci->pci_dev, epci->mac_regs);
|
||||
qpci_msix_disable(&epci->pci_dev);
|
||||
}
|
||||
|
||||
static void e1000e_pci_start_hw(QOSGraphObject *obj)
|
||||
{
|
||||
QE1000E_PCI *d = (QE1000E_PCI *) obj;
|
||||
uint32_t val;
|
||||
|
||||
/* Enable the device */
|
||||
qpci_device_enable(&d->pci_dev);
|
||||
|
||||
/* Reset the device */
|
||||
val = e1000e_macreg_read(&d->e1000e, E1000E_CTRL);
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_CTRL, val | E1000E_CTRL_RESET);
|
||||
|
||||
/* Enable and configure MSI-X */
|
||||
qpci_msix_enable(&d->pci_dev);
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_IVAR, E1000E_IVAR_TEST_CFG);
|
||||
|
||||
/* Check the device status - link and speed */
|
||||
val = e1000e_macreg_read(&d->e1000e, E1000E_STATUS);
|
||||
g_assert_cmphex(val & (E1000E_STATUS_LU | E1000E_STATUS_ASDV1000),
|
||||
==, E1000E_STATUS_LU | E1000E_STATUS_ASDV1000);
|
||||
|
||||
/* Initialize TX/RX logic */
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_RCTL, 0);
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_TCTL, 0);
|
||||
|
||||
/* Notify the device that the driver is ready */
|
||||
val = e1000e_macreg_read(&d->e1000e, E1000E_CTRL_EXT);
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_CTRL_EXT,
|
||||
val | E1000E_CTRL_EXT_DRV_LOAD | E1000E_CTRL_EXT_TXLSFLOW);
|
||||
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_TDBAL,
|
||||
(uint32_t) d->e1000e.tx_ring);
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_TDBAH,
|
||||
(uint32_t) (d->e1000e.tx_ring >> 32));
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_TDLEN, E1000E_RING_LEN);
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_TDT, 0);
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_TDH, 0);
|
||||
|
||||
/* Enable transmit */
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_TCTL, E1000E_TCTL_EN);
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_RDBAL,
|
||||
(uint32_t)d->e1000e.rx_ring);
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_RDBAH,
|
||||
(uint32_t)(d->e1000e.rx_ring >> 32));
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_RDLEN, E1000E_RING_LEN);
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_RDT, 0);
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_RDH, 0);
|
||||
|
||||
/* Enable receive */
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_RFCTL, E1000E_RFCTL_EXTEN);
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_RCTL, E1000E_RCTL_EN |
|
||||
E1000E_RCTL_UPE |
|
||||
E1000E_RCTL_MPE);
|
||||
|
||||
/* Enable all interrupts */
|
||||
e1000e_macreg_write(&d->e1000e, E1000E_IMS, 0xFFFFFFFF);
|
||||
|
||||
}
|
||||
|
||||
static void *e1000e_pci_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QE1000E_PCI *epci = obj;
|
||||
if (!g_strcmp0(interface, "e1000e-if")) {
|
||||
return &epci->e1000e;
|
||||
}
|
||||
|
||||
/* implicit contains */
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return &epci->pci_dev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in e1000e\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *e1000e_pci_create(void *pci_bus, QGuestAllocator *alloc,
|
||||
void *addr)
|
||||
{
|
||||
QE1000E_PCI *d = g_new0(QE1000E_PCI, 1);
|
||||
QPCIBus *bus = pci_bus;
|
||||
QPCIAddress *address = addr;
|
||||
|
||||
qpci_device_foreach(bus, address->vendor_id, address->device_id,
|
||||
e1000e_foreach_callback, &d->pci_dev);
|
||||
|
||||
/* Map BAR0 (mac registers) */
|
||||
d->mac_regs = qpci_iomap(&d->pci_dev, 0, NULL);
|
||||
|
||||
/* Allocate and setup TX ring */
|
||||
d->e1000e.tx_ring = guest_alloc(alloc, E1000E_RING_LEN);
|
||||
g_assert(d->e1000e.tx_ring != 0);
|
||||
|
||||
/* Allocate and setup RX ring */
|
||||
d->e1000e.rx_ring = guest_alloc(alloc, E1000E_RING_LEN);
|
||||
g_assert(d->e1000e.rx_ring != 0);
|
||||
|
||||
d->obj.get_driver = e1000e_pci_get_driver;
|
||||
d->obj.start_hw = e1000e_pci_start_hw;
|
||||
d->obj.destructor = e1000e_pci_destructor;
|
||||
|
||||
return &d->obj;
|
||||
}
|
||||
|
||||
static void e1000e_register_nodes(void)
|
||||
{
|
||||
QPCIAddress addr = {
|
||||
.vendor_id = 0x8086,
|
||||
.device_id = 0x10D3,
|
||||
};
|
||||
|
||||
/* FIXME: every test using this node needs to setup a -netdev socket,id=hs0
|
||||
* otherwise QEMU is not going to start */
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "netdev=hs0",
|
||||
};
|
||||
add_qpci_address(&opts, &addr);
|
||||
|
||||
qos_node_create_driver("e1000e", e1000e_pci_create);
|
||||
qos_node_consumes("e1000e", "pci-bus", &opts);
|
||||
}
|
||||
|
||||
libqos_init(e1000e_register_nodes);
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef QGRAPH_E1000E
|
||||
#define QGRAPH_E1000E
|
||||
|
||||
#include "libqos/qgraph.h"
|
||||
#include "pci.h"
|
||||
|
||||
#define E1000E_RX0_MSG_ID (0)
|
||||
#define E1000E_TX0_MSG_ID (1)
|
||||
#define E1000E_OTHER_MSG_ID (2)
|
||||
|
||||
#define E1000E_TDLEN (0x3808)
|
||||
#define E1000E_TDT (0x3818)
|
||||
#define E1000E_RDLEN (0x2808)
|
||||
#define E1000E_RDT (0x2818)
|
||||
|
||||
typedef struct QE1000E QE1000E;
|
||||
typedef struct QE1000E_PCI QE1000E_PCI;
|
||||
|
||||
struct QE1000E {
|
||||
uint64_t tx_ring;
|
||||
uint64_t rx_ring;
|
||||
};
|
||||
|
||||
struct QE1000E_PCI {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice pci_dev;
|
||||
QPCIBar mac_regs;
|
||||
QE1000E e1000e;
|
||||
};
|
||||
|
||||
void e1000e_wait_isr(QE1000E *d, uint16_t msg_id);
|
||||
void e1000e_tx_ring_push(QE1000E *d, void *descr);
|
||||
void e1000e_rx_ring_push(QE1000E *d, void *descr);
|
||||
|
||||
#endif
|
|
@ -4,9 +4,8 @@
|
|||
#include "libqos/pci-pc.h"
|
||||
|
||||
static QOSOps qos_ops = {
|
||||
.init_allocator = pc_alloc_init_flags,
|
||||
.uninit_allocator = pc_alloc_uninit,
|
||||
.qpci_init = qpci_init_pc,
|
||||
.alloc_init = pc_alloc_init,
|
||||
.qpci_new = qpci_new_pc,
|
||||
.qpci_free = qpci_free_pc,
|
||||
.shutdown = qtest_pc_shutdown,
|
||||
};
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
#include "libqos/pci-spapr.h"
|
||||
|
||||
static QOSOps qos_ops = {
|
||||
.init_allocator = spapr_alloc_init_flags,
|
||||
.uninit_allocator = spapr_alloc_uninit,
|
||||
.qpci_init = qpci_init_spapr,
|
||||
.alloc_init = spapr_alloc_init,
|
||||
.qpci_new = qpci_new_spapr,
|
||||
.qpci_free = qpci_free_spapr,
|
||||
.shutdown = qtest_spapr_shutdown,
|
||||
};
|
||||
|
|
|
@ -24,8 +24,8 @@ QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap)
|
|||
qs->qts = qtest_init(cmdline);
|
||||
qs->ops = ops;
|
||||
if (ops) {
|
||||
qs->alloc = ops->init_allocator(qs->qts, ALLOC_NO_FLAGS);
|
||||
qs->pcibus = ops->qpci_init(qs->qts, qs->alloc);
|
||||
ops->alloc_init(&qs->alloc, qs->qts, ALLOC_NO_FLAGS);
|
||||
qs->pcibus = ops->qpci_new(qs->qts, &qs->alloc);
|
||||
}
|
||||
|
||||
g_free(cmdline);
|
||||
|
@ -58,11 +58,8 @@ void qtest_common_shutdown(QOSState *qs)
|
|||
qs->ops->qpci_free(qs->pcibus);
|
||||
qs->pcibus = NULL;
|
||||
}
|
||||
if (qs->alloc && qs->ops->uninit_allocator) {
|
||||
qs->ops->uninit_allocator(qs->alloc);
|
||||
qs->alloc = NULL;
|
||||
}
|
||||
}
|
||||
alloc_destroy(&qs->alloc);
|
||||
qtest_quit(qs->qts);
|
||||
g_free(qs);
|
||||
}
|
||||
|
@ -116,7 +113,7 @@ void migrate(QOSState *from, QOSState *to, const char *uri)
|
|||
|
||||
/* If we were running, we can wait for an event. */
|
||||
if (running) {
|
||||
migrate_allocator(from->alloc, to->alloc);
|
||||
migrate_allocator(&from->alloc, &to->alloc);
|
||||
set_context(to);
|
||||
qtest_qmp_eventwait(to->qts, "RESUME");
|
||||
return;
|
||||
|
@ -146,7 +143,7 @@ void migrate(QOSState *from, QOSState *to, const char *uri)
|
|||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
migrate_allocator(from->alloc, to->alloc);
|
||||
migrate_allocator(&from->alloc, &to->alloc);
|
||||
set_context(to);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,21 +3,20 @@
|
|||
|
||||
#include "libqtest.h"
|
||||
#include "libqos/pci.h"
|
||||
#include "libqos/malloc-pc.h"
|
||||
#include "libqos/malloc.h"
|
||||
|
||||
typedef struct QOSState QOSState;
|
||||
|
||||
typedef struct QOSOps {
|
||||
QGuestAllocator *(*init_allocator)(QTestState *qts, QAllocOpts);
|
||||
void (*uninit_allocator)(QGuestAllocator *);
|
||||
QPCIBus *(*qpci_init)(QTestState *qts, QGuestAllocator *alloc);
|
||||
void (*alloc_init)(QGuestAllocator *, QTestState *, QAllocOpts);
|
||||
QPCIBus *(*qpci_new)(QTestState *qts, QGuestAllocator *alloc);
|
||||
void (*qpci_free)(QPCIBus *bus);
|
||||
void (*shutdown)(QOSState *);
|
||||
} QOSOps;
|
||||
|
||||
struct QOSState {
|
||||
QTestState *qts;
|
||||
QGuestAllocator *alloc;
|
||||
QGuestAllocator alloc;
|
||||
QPCIBus *pcibus;
|
||||
QOSOps *ops;
|
||||
};
|
||||
|
@ -36,12 +35,12 @@ void generate_pattern(void *buffer, size_t len, size_t cycle_len);
|
|||
|
||||
static inline uint64_t qmalloc(QOSState *q, size_t bytes)
|
||||
{
|
||||
return guest_alloc(q->alloc, bytes);
|
||||
return guest_alloc(&q->alloc, bytes);
|
||||
}
|
||||
|
||||
static inline void qfree(QOSState *q, uint64_t addr)
|
||||
{
|
||||
guest_free(q->alloc, addr);
|
||||
guest_free(&q->alloc, addr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Basic libqos generic malloc support
|
||||
*
|
||||
* Copyright (c) 2014 Marc Marí
|
||||
*
|
||||
* 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 "libqos/malloc-generic.h"
|
||||
#include "libqos/malloc.h"
|
||||
|
||||
/*
|
||||
* Mostly for valgrind happiness, but it does offer
|
||||
* a chokepoint for debugging guest memory leaks, too.
|
||||
*/
|
||||
void generic_alloc_uninit(QGuestAllocator *allocator)
|
||||
{
|
||||
alloc_uninit(allocator);
|
||||
}
|
||||
|
||||
QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size,
|
||||
uint32_t page_size, QAllocOpts flags)
|
||||
{
|
||||
QGuestAllocator *s;
|
||||
uint64_t start = base_addr + (1 << 20); /* Start at 1MB */
|
||||
|
||||
s = alloc_init_flags(flags, start, start + size);
|
||||
alloc_set_page_size(s, page_size);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
inline QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size,
|
||||
uint32_t page_size)
|
||||
{
|
||||
return generic_alloc_init_flags(base_addr, size, page_size, ALLOC_NO_FLAGS);
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* Basic libqos generic malloc support
|
||||
*
|
||||
* Copyright (c) 2014 Marc Marí
|
||||
*
|
||||
* 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 LIBQOS_MALLOC_GENERIC_H
|
||||
#define LIBQOS_MALLOC_GENERIC_H
|
||||
|
||||
#include "libqos/malloc.h"
|
||||
|
||||
QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size,
|
||||
uint32_t page_size);
|
||||
QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size,
|
||||
uint32_t page_size, QAllocOpts flags);
|
||||
void generic_alloc_uninit(QGuestAllocator *allocator);
|
||||
|
||||
#endif
|
|
@ -20,32 +20,14 @@
|
|||
|
||||
#define PAGE_SIZE (4096)
|
||||
|
||||
/*
|
||||
* Mostly for valgrind happiness, but it does offer
|
||||
* a chokepoint for debugging guest memory leaks, too.
|
||||
*/
|
||||
void pc_alloc_uninit(QGuestAllocator *allocator)
|
||||
void pc_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags)
|
||||
{
|
||||
alloc_uninit(allocator);
|
||||
}
|
||||
|
||||
QGuestAllocator *pc_alloc_init_flags(QTestState *qts, QAllocOpts flags)
|
||||
{
|
||||
QGuestAllocator *s;
|
||||
uint64_t ram_size;
|
||||
QFWCFG *fw_cfg = pc_fw_cfg_init(qts);
|
||||
|
||||
ram_size = qfw_cfg_get_u64(fw_cfg, FW_CFG_RAM_SIZE);
|
||||
s = alloc_init_flags(flags, 1 << 20, MIN(ram_size, 0xE0000000));
|
||||
alloc_set_page_size(s, PAGE_SIZE);
|
||||
alloc_init(s, flags, 1 << 20, MIN(ram_size, 0xE0000000), PAGE_SIZE);
|
||||
|
||||
/* clean-up */
|
||||
g_free(fw_cfg);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
inline QGuestAllocator *pc_alloc_init(QTestState *qts)
|
||||
{
|
||||
return pc_alloc_init_flags(qts, ALLOC_NO_FLAGS);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
|
||||
#include "libqos/malloc.h"
|
||||
|
||||
QGuestAllocator *pc_alloc_init(QTestState *qts);
|
||||
QGuestAllocator *pc_alloc_init_flags(QTestState *qts, QAllocOpts flags);
|
||||
void pc_alloc_uninit(QGuestAllocator *allocator);
|
||||
void pc_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,22 +17,7 @@
|
|||
*/
|
||||
#define SPAPR_MIN_SIZE 0x10000000
|
||||
|
||||
void spapr_alloc_uninit(QGuestAllocator *allocator)
|
||||
void spapr_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags)
|
||||
{
|
||||
alloc_uninit(allocator);
|
||||
}
|
||||
|
||||
QGuestAllocator *spapr_alloc_init_flags(QTestState *qts, QAllocOpts flags)
|
||||
{
|
||||
QGuestAllocator *s;
|
||||
|
||||
s = alloc_init_flags(flags, 1 << 20, SPAPR_MIN_SIZE);
|
||||
alloc_set_page_size(s, PAGE_SIZE);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
QGuestAllocator *spapr_alloc_init(void)
|
||||
{
|
||||
return spapr_alloc_init_flags(NULL, ALLOC_NO_FLAGS);
|
||||
alloc_init(s, flags, 1 << 20, SPAPR_MIN_SIZE, PAGE_SIZE);
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
|
||||
#include "libqos/malloc.h"
|
||||
|
||||
QGuestAllocator *spapr_alloc_init(void);
|
||||
QGuestAllocator *spapr_alloc_init_flags(QTestState *qts, QAllocOpts flags);
|
||||
void spapr_alloc_uninit(QGuestAllocator *allocator);
|
||||
void spapr_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -15,24 +15,12 @@
|
|||
#include "qemu-common.h"
|
||||
#include "qemu/host-utils.h"
|
||||
|
||||
typedef QTAILQ_HEAD(MemList, MemBlock) MemList;
|
||||
|
||||
typedef struct MemBlock {
|
||||
QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME;
|
||||
uint64_t size;
|
||||
uint64_t addr;
|
||||
} MemBlock;
|
||||
|
||||
struct QGuestAllocator {
|
||||
QAllocOpts opts;
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
uint32_t page_size;
|
||||
|
||||
MemList *used;
|
||||
MemList *free;
|
||||
};
|
||||
|
||||
#define DEFAULT_PAGE_SIZE 4096
|
||||
|
||||
static void mlist_delete(MemList *list, MemBlock *node)
|
||||
|
@ -225,7 +213,7 @@ static void mlist_free(QGuestAllocator *s, uint64_t addr)
|
|||
* Mostly for valgrind happiness, but it does offer
|
||||
* a chokepoint for debugging guest memory leaks, too.
|
||||
*/
|
||||
void alloc_uninit(QGuestAllocator *allocator)
|
||||
void alloc_destroy(QGuestAllocator *allocator)
|
||||
{
|
||||
MemBlock *node;
|
||||
MemBlock *tmp;
|
||||
|
@ -261,7 +249,6 @@ void alloc_uninit(QGuestAllocator *allocator)
|
|||
|
||||
g_free(allocator->used);
|
||||
g_free(allocator->free);
|
||||
g_free(allocator);
|
||||
}
|
||||
|
||||
uint64_t guest_alloc(QGuestAllocator *allocator, size_t size)
|
||||
|
@ -297,11 +284,13 @@ void guest_free(QGuestAllocator *allocator, uint64_t addr)
|
|||
}
|
||||
}
|
||||
|
||||
QGuestAllocator *alloc_init(uint64_t start, uint64_t end)
|
||||
void alloc_init(QGuestAllocator *s, QAllocOpts opts,
|
||||
uint64_t start, uint64_t end,
|
||||
size_t page_size)
|
||||
{
|
||||
QGuestAllocator *s = g_malloc0(sizeof(*s));
|
||||
MemBlock *node;
|
||||
|
||||
s->opts = opts;
|
||||
s->start = start;
|
||||
s->end = end;
|
||||
|
||||
|
@ -313,26 +302,7 @@ QGuestAllocator *alloc_init(uint64_t start, uint64_t end)
|
|||
node = mlist_new(s->start, s->end - s->start);
|
||||
QTAILQ_INSERT_HEAD(s->free, node, MLIST_ENTNAME);
|
||||
|
||||
s->page_size = DEFAULT_PAGE_SIZE;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
QGuestAllocator *alloc_init_flags(QAllocOpts opts,
|
||||
uint64_t start, uint64_t end)
|
||||
{
|
||||
QGuestAllocator *s = alloc_init(start, end);
|
||||
s->opts = opts;
|
||||
return s;
|
||||
}
|
||||
|
||||
void alloc_set_page_size(QGuestAllocator *allocator, size_t page_size)
|
||||
{
|
||||
/* Can't alter the page_size for an allocator in-use */
|
||||
g_assert(QTAILQ_EMPTY(allocator->used));
|
||||
|
||||
g_assert(is_power_of_2(page_size));
|
||||
allocator->page_size = page_size;
|
||||
s->page_size = page_size;
|
||||
}
|
||||
|
||||
void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts)
|
||||
|
|
|
@ -23,19 +23,28 @@ typedef enum {
|
|||
ALLOC_PARANOID = 0x04
|
||||
} QAllocOpts;
|
||||
|
||||
typedef struct QGuestAllocator QGuestAllocator;
|
||||
typedef QTAILQ_HEAD(MemList, MemBlock) MemList;
|
||||
|
||||
void alloc_uninit(QGuestAllocator *allocator);
|
||||
typedef struct QGuestAllocator {
|
||||
QAllocOpts opts;
|
||||
uint64_t start;
|
||||
uint64_t end;
|
||||
uint32_t page_size;
|
||||
|
||||
MemList *used;
|
||||
MemList *free;
|
||||
} QGuestAllocator;
|
||||
|
||||
/* Always returns page aligned values */
|
||||
uint64_t guest_alloc(QGuestAllocator *allocator, size_t size);
|
||||
void guest_free(QGuestAllocator *allocator, uint64_t addr);
|
||||
void migrate_allocator(QGuestAllocator *src, QGuestAllocator *dst);
|
||||
|
||||
QGuestAllocator *alloc_init(uint64_t start, uint64_t end);
|
||||
QGuestAllocator *alloc_init_flags(QAllocOpts flags,
|
||||
uint64_t start, uint64_t end);
|
||||
void alloc_set_page_size(QGuestAllocator *allocator, size_t page_size);
|
||||
void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts);
|
||||
|
||||
void alloc_init(QGuestAllocator *alloc, QAllocOpts flags,
|
||||
uint64_t start, uint64_t end,
|
||||
size_t page_size);
|
||||
void alloc_destroy(QGuestAllocator *allocator);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,15 +18,9 @@
|
|||
|
||||
#include "qemu-common.h"
|
||||
|
||||
|
||||
#define ACPI_PCIHP_ADDR 0xae00
|
||||
#define PCI_EJ_BASE 0x0008
|
||||
|
||||
typedef struct QPCIBusPC
|
||||
{
|
||||
QPCIBus bus;
|
||||
} QPCIBusPC;
|
||||
|
||||
static uint8_t qpci_pc_pio_readb(QPCIBus *bus, uint32_t addr)
|
||||
{
|
||||
return qtest_inb(bus->qts, addr);
|
||||
|
@ -116,44 +110,68 @@ static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint3
|
|||
qtest_outl(bus->qts, 0xcfc, value);
|
||||
}
|
||||
|
||||
QPCIBus *qpci_init_pc(QTestState *qts, QGuestAllocator *alloc)
|
||||
static void *qpci_pc_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QPCIBusPC *ret = g_new0(QPCIBusPC, 1);
|
||||
QPCIBusPC *qpci = obj;
|
||||
if (!g_strcmp0(interface, "pci-bus")) {
|
||||
return &qpci->bus;
|
||||
}
|
||||
fprintf(stderr, "%s not present in pci-bus-pc\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
void qpci_init_pc(QPCIBusPC *qpci, QTestState *qts, QGuestAllocator *alloc)
|
||||
{
|
||||
assert(qts);
|
||||
|
||||
ret->bus.pio_readb = qpci_pc_pio_readb;
|
||||
ret->bus.pio_readw = qpci_pc_pio_readw;
|
||||
ret->bus.pio_readl = qpci_pc_pio_readl;
|
||||
ret->bus.pio_readq = qpci_pc_pio_readq;
|
||||
/* tests can use pci-bus */
|
||||
qpci->bus.has_buggy_msi = FALSE;
|
||||
|
||||
ret->bus.pio_writeb = qpci_pc_pio_writeb;
|
||||
ret->bus.pio_writew = qpci_pc_pio_writew;
|
||||
ret->bus.pio_writel = qpci_pc_pio_writel;
|
||||
ret->bus.pio_writeq = qpci_pc_pio_writeq;
|
||||
qpci->bus.pio_readb = qpci_pc_pio_readb;
|
||||
qpci->bus.pio_readw = qpci_pc_pio_readw;
|
||||
qpci->bus.pio_readl = qpci_pc_pio_readl;
|
||||
qpci->bus.pio_readq = qpci_pc_pio_readq;
|
||||
|
||||
ret->bus.memread = qpci_pc_memread;
|
||||
ret->bus.memwrite = qpci_pc_memwrite;
|
||||
qpci->bus.pio_writeb = qpci_pc_pio_writeb;
|
||||
qpci->bus.pio_writew = qpci_pc_pio_writew;
|
||||
qpci->bus.pio_writel = qpci_pc_pio_writel;
|
||||
qpci->bus.pio_writeq = qpci_pc_pio_writeq;
|
||||
|
||||
ret->bus.config_readb = qpci_pc_config_readb;
|
||||
ret->bus.config_readw = qpci_pc_config_readw;
|
||||
ret->bus.config_readl = qpci_pc_config_readl;
|
||||
qpci->bus.memread = qpci_pc_memread;
|
||||
qpci->bus.memwrite = qpci_pc_memwrite;
|
||||
|
||||
ret->bus.config_writeb = qpci_pc_config_writeb;
|
||||
ret->bus.config_writew = qpci_pc_config_writew;
|
||||
ret->bus.config_writel = qpci_pc_config_writel;
|
||||
qpci->bus.config_readb = qpci_pc_config_readb;
|
||||
qpci->bus.config_readw = qpci_pc_config_readw;
|
||||
qpci->bus.config_readl = qpci_pc_config_readl;
|
||||
|
||||
ret->bus.qts = qts;
|
||||
ret->bus.pio_alloc_ptr = 0xc000;
|
||||
ret->bus.mmio_alloc_ptr = 0xE0000000;
|
||||
ret->bus.mmio_limit = 0x100000000ULL;
|
||||
qpci->bus.config_writeb = qpci_pc_config_writeb;
|
||||
qpci->bus.config_writew = qpci_pc_config_writew;
|
||||
qpci->bus.config_writel = qpci_pc_config_writel;
|
||||
|
||||
return &ret->bus;
|
||||
qpci->bus.qts = qts;
|
||||
qpci->bus.pio_alloc_ptr = 0xc000;
|
||||
qpci->bus.mmio_alloc_ptr = 0xE0000000;
|
||||
qpci->bus.mmio_limit = 0x100000000ULL;
|
||||
|
||||
qpci->obj.get_driver = qpci_pc_get_driver;
|
||||
}
|
||||
|
||||
QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc)
|
||||
{
|
||||
QPCIBusPC *qpci = g_new0(QPCIBusPC, 1);
|
||||
qpci_init_pc(qpci, qts, alloc);
|
||||
|
||||
return &qpci->bus;
|
||||
}
|
||||
|
||||
void qpci_free_pc(QPCIBus *bus)
|
||||
{
|
||||
QPCIBusPC *s = container_of(bus, QPCIBusPC, bus);
|
||||
QPCIBusPC *s;
|
||||
|
||||
if (!bus) {
|
||||
return;
|
||||
}
|
||||
s = container_of(bus, QPCIBusPC, bus);
|
||||
|
||||
g_free(s);
|
||||
}
|
||||
|
@ -172,3 +190,11 @@ void qpci_unplug_acpi_device_test(const char *id, uint8_t slot)
|
|||
|
||||
qmp_eventwait("DEVICE_DELETED");
|
||||
}
|
||||
|
||||
static void qpci_pc_register_nodes(void)
|
||||
{
|
||||
qos_node_create_driver("pci-bus-pc", NULL);
|
||||
qos_node_produces("pci-bus-pc", "pci-bus");
|
||||
}
|
||||
|
||||
libqos_init(qpci_pc_register_nodes);
|
||||
|
|
|
@ -15,8 +15,35 @@
|
|||
|
||||
#include "libqos/pci.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
typedef struct QPCIBusPC {
|
||||
QOSGraphObject obj;
|
||||
QPCIBus bus;
|
||||
} QPCIBusPC;
|
||||
|
||||
/* qpci_init_pc():
|
||||
* @ret: A valid QPCIBusPC * pointer
|
||||
* @qts: The %QTestState for this PC machine
|
||||
* @alloc: A previously initialized @alloc providing memory for @qts
|
||||
*
|
||||
* This function initializes an already allocated
|
||||
* QPCIBusPC object.
|
||||
*/
|
||||
void qpci_init_pc(QPCIBusPC *ret, QTestState *qts, QGuestAllocator *alloc);
|
||||
|
||||
/* qpci_pc_new():
|
||||
* @qts: The %QTestState for this PC machine
|
||||
* @alloc: A previously initialized @alloc providing memory for @qts
|
||||
*
|
||||
* This function creates a new QPCIBusPC object,
|
||||
* and properly initialize its fields.
|
||||
*
|
||||
* Returns the QPCIBus *bus field of a newly
|
||||
* allocated QPCIBusPC.
|
||||
*/
|
||||
QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc);
|
||||
|
||||
QPCIBus *qpci_init_pc(QTestState *qts, QGuestAllocator *alloc);
|
||||
void qpci_free_pc(QPCIBus *bus);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -9,33 +9,13 @@
|
|||
#include "libqtest.h"
|
||||
#include "libqos/pci-spapr.h"
|
||||
#include "libqos/rtas.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
#include "hw/pci/pci_regs.h"
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/host-utils.h"
|
||||
|
||||
|
||||
/* From include/hw/pci-host/spapr.h */
|
||||
|
||||
typedef struct QPCIWindow {
|
||||
uint64_t pci_base; /* window address in PCI space */
|
||||
uint64_t size; /* window size */
|
||||
} QPCIWindow;
|
||||
|
||||
typedef struct QPCIBusSPAPR {
|
||||
QPCIBus bus;
|
||||
QGuestAllocator *alloc;
|
||||
|
||||
uint64_t buid;
|
||||
|
||||
uint64_t pio_cpu_base;
|
||||
QPCIWindow pio;
|
||||
|
||||
uint64_t mmio32_cpu_base;
|
||||
QPCIWindow mmio32;
|
||||
} QPCIBusSPAPR;
|
||||
|
||||
/*
|
||||
* PCI devices are always little-endian
|
||||
* SPAPR by default is big-endian
|
||||
|
@ -160,60 +140,93 @@ static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset,
|
|||
#define SPAPR_PCI_MMIO32_WIN_SIZE 0x80000000 /* 2 GiB */
|
||||
#define SPAPR_PCI_IO_WIN_SIZE 0x10000
|
||||
|
||||
QPCIBus *qpci_init_spapr(QTestState *qts, QGuestAllocator *alloc)
|
||||
static void *qpci_spapr_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QPCIBusSPAPR *ret = g_new0(QPCIBusSPAPR, 1);
|
||||
QPCIBusSPAPR *qpci = obj;
|
||||
if (!g_strcmp0(interface, "pci-bus")) {
|
||||
return &qpci->bus;
|
||||
}
|
||||
fprintf(stderr, "%s not present in pci-bus-spapr", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
void qpci_init_spapr(QPCIBusSPAPR *qpci, QTestState *qts,
|
||||
QGuestAllocator *alloc)
|
||||
{
|
||||
assert(qts);
|
||||
|
||||
ret->alloc = alloc;
|
||||
/* tests cannot use spapr, needs to be fixed first */
|
||||
qpci->bus.has_buggy_msi = TRUE;
|
||||
|
||||
ret->bus.pio_readb = qpci_spapr_pio_readb;
|
||||
ret->bus.pio_readw = qpci_spapr_pio_readw;
|
||||
ret->bus.pio_readl = qpci_spapr_pio_readl;
|
||||
ret->bus.pio_readq = qpci_spapr_pio_readq;
|
||||
qpci->alloc = alloc;
|
||||
|
||||
ret->bus.pio_writeb = qpci_spapr_pio_writeb;
|
||||
ret->bus.pio_writew = qpci_spapr_pio_writew;
|
||||
ret->bus.pio_writel = qpci_spapr_pio_writel;
|
||||
ret->bus.pio_writeq = qpci_spapr_pio_writeq;
|
||||
qpci->bus.pio_readb = qpci_spapr_pio_readb;
|
||||
qpci->bus.pio_readw = qpci_spapr_pio_readw;
|
||||
qpci->bus.pio_readl = qpci_spapr_pio_readl;
|
||||
qpci->bus.pio_readq = qpci_spapr_pio_readq;
|
||||
|
||||
ret->bus.memread = qpci_spapr_memread;
|
||||
ret->bus.memwrite = qpci_spapr_memwrite;
|
||||
qpci->bus.pio_writeb = qpci_spapr_pio_writeb;
|
||||
qpci->bus.pio_writew = qpci_spapr_pio_writew;
|
||||
qpci->bus.pio_writel = qpci_spapr_pio_writel;
|
||||
qpci->bus.pio_writeq = qpci_spapr_pio_writeq;
|
||||
|
||||
ret->bus.config_readb = qpci_spapr_config_readb;
|
||||
ret->bus.config_readw = qpci_spapr_config_readw;
|
||||
ret->bus.config_readl = qpci_spapr_config_readl;
|
||||
qpci->bus.memread = qpci_spapr_memread;
|
||||
qpci->bus.memwrite = qpci_spapr_memwrite;
|
||||
|
||||
ret->bus.config_writeb = qpci_spapr_config_writeb;
|
||||
ret->bus.config_writew = qpci_spapr_config_writew;
|
||||
ret->bus.config_writel = qpci_spapr_config_writel;
|
||||
qpci->bus.config_readb = qpci_spapr_config_readb;
|
||||
qpci->bus.config_readw = qpci_spapr_config_readw;
|
||||
qpci->bus.config_readl = qpci_spapr_config_readl;
|
||||
|
||||
qpci->bus.config_writeb = qpci_spapr_config_writeb;
|
||||
qpci->bus.config_writew = qpci_spapr_config_writew;
|
||||
qpci->bus.config_writel = qpci_spapr_config_writel;
|
||||
|
||||
/* FIXME: We assume the default location of the PHB for now.
|
||||
* Ideally we'd parse the device tree deposited in the guest to
|
||||
* get the window locations */
|
||||
ret->buid = 0x800000020000000ULL;
|
||||
qpci->buid = 0x800000020000000ULL;
|
||||
|
||||
ret->pio_cpu_base = SPAPR_PCI_BASE;
|
||||
ret->pio.pci_base = 0;
|
||||
ret->pio.size = SPAPR_PCI_IO_WIN_SIZE;
|
||||
qpci->pio_cpu_base = SPAPR_PCI_BASE;
|
||||
qpci->pio.pci_base = 0;
|
||||
qpci->pio.size = SPAPR_PCI_IO_WIN_SIZE;
|
||||
|
||||
/* 32-bit portion of the MMIO window is at PCI address 2..4 GiB */
|
||||
ret->mmio32_cpu_base = SPAPR_PCI_BASE;
|
||||
ret->mmio32.pci_base = SPAPR_PCI_MMIO32_WIN_SIZE;
|
||||
ret->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE;
|
||||
qpci->mmio32_cpu_base = SPAPR_PCI_BASE;
|
||||
qpci->mmio32.pci_base = SPAPR_PCI_MMIO32_WIN_SIZE;
|
||||
qpci->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE;
|
||||
|
||||
ret->bus.qts = qts;
|
||||
ret->bus.pio_alloc_ptr = 0xc000;
|
||||
ret->bus.mmio_alloc_ptr = ret->mmio32.pci_base;
|
||||
ret->bus.mmio_limit = ret->mmio32.pci_base + ret->mmio32.size;
|
||||
qpci->bus.qts = qts;
|
||||
qpci->bus.pio_alloc_ptr = 0xc000;
|
||||
qpci->bus.mmio_alloc_ptr = qpci->mmio32.pci_base;
|
||||
qpci->bus.mmio_limit = qpci->mmio32.pci_base + qpci->mmio32.size;
|
||||
|
||||
return &ret->bus;
|
||||
qpci->obj.get_driver = qpci_spapr_get_driver;
|
||||
}
|
||||
|
||||
QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc)
|
||||
{
|
||||
QPCIBusSPAPR *qpci = g_new0(QPCIBusSPAPR, 1);
|
||||
qpci_init_spapr(qpci, qts, alloc);
|
||||
|
||||
return &qpci->bus;
|
||||
}
|
||||
|
||||
void qpci_free_spapr(QPCIBus *bus)
|
||||
{
|
||||
QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
|
||||
QPCIBusSPAPR *s;
|
||||
|
||||
if (!bus) {
|
||||
return;
|
||||
}
|
||||
s = container_of(bus, QPCIBusSPAPR, bus);
|
||||
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
static void qpci_spapr_register_nodes(void)
|
||||
{
|
||||
qos_node_create_driver("pci-bus-spapr", NULL);
|
||||
qos_node_produces("pci-bus-spapr", "pci-bus");
|
||||
}
|
||||
|
||||
libqos_init(qpci_spapr_register_nodes);
|
||||
|
|
|
@ -10,8 +10,32 @@
|
|||
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/pci.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
QPCIBus *qpci_init_spapr(QTestState *qts, QGuestAllocator *alloc);
|
||||
/* From include/hw/pci-host/spapr.h */
|
||||
|
||||
typedef struct QPCIWindow {
|
||||
uint64_t pci_base; /* window address in PCI space */
|
||||
uint64_t size; /* window size */
|
||||
} QPCIWindow;
|
||||
|
||||
typedef struct QPCIBusSPAPR {
|
||||
QOSGraphObject obj;
|
||||
QPCIBus bus;
|
||||
QGuestAllocator *alloc;
|
||||
|
||||
uint64_t buid;
|
||||
|
||||
uint64_t pio_cpu_base;
|
||||
QPCIWindow pio;
|
||||
|
||||
uint64_t mmio32_cpu_base;
|
||||
QPCIWindow mmio32;
|
||||
} QPCIBusSPAPR;
|
||||
|
||||
void qpci_init_spapr(QPCIBusSPAPR *ret, QTestState *qts,
|
||||
QGuestAllocator *alloc);
|
||||
QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc);
|
||||
void qpci_free_spapr(QPCIBus *bus);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "hw/pci/pci_regs.h"
|
||||
#include "qemu/host-utils.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
|
||||
void (*func)(QPCIDevice *dev, int devfn, void *data),
|
||||
|
@ -50,13 +51,34 @@ void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
|
|||
}
|
||||
}
|
||||
|
||||
bool qpci_has_buggy_msi(QPCIDevice *dev)
|
||||
{
|
||||
return dev->bus->has_buggy_msi;
|
||||
}
|
||||
|
||||
bool qpci_check_buggy_msi(QPCIDevice *dev)
|
||||
{
|
||||
if (qpci_has_buggy_msi(dev)) {
|
||||
g_test_skip("Skipping due to incomplete support for MSI");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void qpci_device_set(QPCIDevice *dev, QPCIBus *bus, int devfn)
|
||||
{
|
||||
g_assert(dev);
|
||||
|
||||
dev->bus = bus;
|
||||
dev->devfn = devfn;
|
||||
}
|
||||
|
||||
QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn)
|
||||
{
|
||||
QPCIDevice *dev;
|
||||
|
||||
dev = g_malloc0(sizeof(*dev));
|
||||
dev->bus = bus;
|
||||
dev->devfn = devfn;
|
||||
qpci_device_set(dev, bus, devfn);
|
||||
|
||||
if (qpci_config_readw(dev, PCI_VENDOR_ID) == 0xFFFF) {
|
||||
g_free(dev);
|
||||
|
@ -66,6 +88,17 @@ QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn)
|
|||
return dev;
|
||||
}
|
||||
|
||||
void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr)
|
||||
{
|
||||
uint16_t vendor_id, device_id;
|
||||
|
||||
qpci_device_set(dev, bus, addr->devfn);
|
||||
vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID);
|
||||
device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
|
||||
g_assert(!addr->vendor_id || vendor_id == addr->vendor_id);
|
||||
g_assert(!addr->device_id || device_id == addr->device_id);
|
||||
}
|
||||
|
||||
void qpci_device_enable(QPCIDevice *dev)
|
||||
{
|
||||
uint16_t cmd;
|
||||
|
@ -395,3 +428,12 @@ QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr)
|
|||
QPCIBar bar = { .addr = addr };
|
||||
return bar;
|
||||
}
|
||||
|
||||
void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr)
|
||||
{
|
||||
g_assert(addr);
|
||||
g_assert(opts);
|
||||
|
||||
opts->arg = addr;
|
||||
opts->size_arg = sizeof(QPCIAddress);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#define LIBQOS_PCI_H
|
||||
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
#define QPCI_PIO_LIMIT 0x10000
|
||||
|
||||
|
@ -22,6 +23,7 @@
|
|||
typedef struct QPCIDevice QPCIDevice;
|
||||
typedef struct QPCIBus QPCIBus;
|
||||
typedef struct QPCIBar QPCIBar;
|
||||
typedef struct QPCIAddress QPCIAddress;
|
||||
|
||||
struct QPCIBus {
|
||||
uint8_t (*pio_readb)(QPCIBus *bus, uint32_t addr);
|
||||
|
@ -51,6 +53,8 @@ struct QPCIBus {
|
|||
QTestState *qts;
|
||||
uint16_t pio_alloc_ptr;
|
||||
uint64_t mmio_alloc_ptr, mmio_limit;
|
||||
bool has_buggy_msi; /* TRUE for spapr, FALSE for pci */
|
||||
|
||||
};
|
||||
|
||||
struct QPCIBar {
|
||||
|
@ -66,10 +70,20 @@ struct QPCIDevice
|
|||
uint64_t msix_table_off, msix_pba_off;
|
||||
};
|
||||
|
||||
struct QPCIAddress {
|
||||
uint32_t devfn;
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
};
|
||||
|
||||
void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
|
||||
void (*func)(QPCIDevice *dev, int devfn, void *data),
|
||||
void *data);
|
||||
QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn);
|
||||
void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr);
|
||||
|
||||
bool qpci_has_buggy_msi(QPCIDevice *dev);
|
||||
bool qpci_check_buggy_msi(QPCIDevice *dev);
|
||||
|
||||
void qpci_device_enable(QPCIDevice *dev);
|
||||
uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id);
|
||||
|
@ -110,4 +124,6 @@ void qpci_iounmap(QPCIDevice *dev, QPCIBar addr);
|
|||
QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr);
|
||||
|
||||
void qpci_unplug_acpi_device_test(const char *id, uint8_t slot);
|
||||
|
||||
void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr);
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "pci-spapr.h"
|
||||
#include "libqos/malloc-spapr.h"
|
||||
|
||||
typedef struct QSPAPR_pci_host QSPAPR_pci_host;
|
||||
typedef struct Qppc64_pseriesMachine Qppc64_pseriesMachine;
|
||||
|
||||
struct QSPAPR_pci_host {
|
||||
QOSGraphObject obj;
|
||||
QPCIBusSPAPR pci;
|
||||
};
|
||||
|
||||
struct Qppc64_pseriesMachine {
|
||||
QOSGraphObject obj;
|
||||
QGuestAllocator alloc;
|
||||
QSPAPR_pci_host bridge;
|
||||
};
|
||||
|
||||
/* QSPAPR_pci_host */
|
||||
|
||||
static QOSGraphObject *QSPAPR_host_get_device(void *obj, const char *device)
|
||||
{
|
||||
QSPAPR_pci_host *host = obj;
|
||||
if (!g_strcmp0(device, "pci-bus-spapr")) {
|
||||
return &host->pci.obj;
|
||||
}
|
||||
fprintf(stderr, "%s not present in QSPAPR_pci_host\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void qos_create_QSPAPR_host(QSPAPR_pci_host *host,
|
||||
QTestState *qts,
|
||||
QGuestAllocator *alloc)
|
||||
{
|
||||
host->obj.get_device = QSPAPR_host_get_device;
|
||||
qpci_init_spapr(&host->pci, qts, alloc);
|
||||
}
|
||||
|
||||
/* ppc64/pseries machine */
|
||||
|
||||
static void spapr_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
Qppc64_pseriesMachine *machine = (Qppc64_pseriesMachine *) obj;
|
||||
alloc_destroy(&machine->alloc);
|
||||
}
|
||||
|
||||
static void *spapr_get_driver(void *object, const char *interface)
|
||||
{
|
||||
Qppc64_pseriesMachine *machine = object;
|
||||
if (!g_strcmp0(interface, "memory")) {
|
||||
return &machine->alloc;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in ppc64/pseries\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static QOSGraphObject *spapr_get_device(void *obj, const char *device)
|
||||
{
|
||||
Qppc64_pseriesMachine *machine = obj;
|
||||
if (!g_strcmp0(device, "spapr-pci-host-bridge")) {
|
||||
return &machine->bridge.obj;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in ppc64/pseries\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *qos_create_machine_spapr(QTestState *qts)
|
||||
{
|
||||
Qppc64_pseriesMachine *machine = g_new0(Qppc64_pseriesMachine, 1);
|
||||
machine->obj.get_device = spapr_get_device;
|
||||
machine->obj.get_driver = spapr_get_driver;
|
||||
machine->obj.destructor = spapr_destructor;
|
||||
spapr_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS);
|
||||
|
||||
qos_create_QSPAPR_host(&machine->bridge, qts, &machine->alloc);
|
||||
|
||||
return &machine->obj;
|
||||
}
|
||||
|
||||
static void spapr_machine_register_nodes(void)
|
||||
{
|
||||
qos_node_create_machine("ppc64/pseries", qos_create_machine_spapr);
|
||||
qos_node_create_driver("spapr-pci-host-bridge", NULL);
|
||||
qos_node_contains("ppc64/pseries", "spapr-pci-host-bridge", NULL);
|
||||
qos_node_contains("spapr-pci-host-bridge", "pci-bus-spapr", NULL);
|
||||
}
|
||||
|
||||
libqos_init(spapr_machine_register_nodes);
|
||||
|
|
@ -0,0 +1,753 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "libqos/qgraph_internal.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
#define QGRAPH_PRINT_DEBUG 0
|
||||
#define QOS_ROOT ""
|
||||
typedef struct QOSStackElement QOSStackElement;
|
||||
|
||||
/* Graph Edge.*/
|
||||
struct QOSGraphEdge {
|
||||
QOSEdgeType type;
|
||||
char *dest;
|
||||
void *arg; /* just for QEDGE_CONTAINS
|
||||
* and QEDGE_CONSUMED_BY */
|
||||
char *extra_device_opts; /* added to -device option, "," is
|
||||
* automatically added
|
||||
*/
|
||||
char *before_cmd_line; /* added before node cmd_line */
|
||||
char *after_cmd_line; /* added after -device options */
|
||||
char *edge_name; /* used by QEDGE_CONTAINS */
|
||||
QSLIST_ENTRY(QOSGraphEdge) edge_list;
|
||||
};
|
||||
|
||||
typedef QSLIST_HEAD(, QOSGraphEdge) QOSGraphEdgeList;
|
||||
|
||||
/**
|
||||
* Stack used to keep track of the discovered path when using
|
||||
* the DFS algorithm
|
||||
*/
|
||||
struct QOSStackElement {
|
||||
QOSGraphNode *node;
|
||||
QOSStackElement *parent;
|
||||
QOSGraphEdge *parent_edge;
|
||||
int length;
|
||||
};
|
||||
|
||||
/* Each enty in these hash table will consist of <string, node/edge> pair. */
|
||||
static GHashTable *edge_table;
|
||||
static GHashTable *node_table;
|
||||
|
||||
/* stack used by the DFS algorithm to store the path from machine to test */
|
||||
static QOSStackElement qos_node_stack[QOS_PATH_MAX_ELEMENT_SIZE];
|
||||
static int qos_node_tos;
|
||||
|
||||
/**
|
||||
* add_edge(): creates an edge of type @type
|
||||
* from @source to @dest node, and inserts it in the
|
||||
* edges hash table
|
||||
*
|
||||
* Nodes @source and @dest do not necessarily need to exist.
|
||||
* Possibility to add also options (see #QOSGraphEdgeOptions)
|
||||
* edge->edge_name is used as identifier for get_device relationships,
|
||||
* so by default is equal to @dest.
|
||||
*/
|
||||
static void add_edge(const char *source, const char *dest,
|
||||
QOSEdgeType type, QOSGraphEdgeOptions *opts)
|
||||
{
|
||||
char *key;
|
||||
QOSGraphEdgeList *list = g_hash_table_lookup(edge_table, source);
|
||||
|
||||
if (!list) {
|
||||
list = g_new0(QOSGraphEdgeList, 1);
|
||||
key = g_strdup(source);
|
||||
g_hash_table_insert(edge_table, key, list);
|
||||
}
|
||||
|
||||
if (!opts) {
|
||||
opts = &(QOSGraphEdgeOptions) { };
|
||||
}
|
||||
|
||||
QOSGraphEdge *edge = g_new0(QOSGraphEdge, 1);
|
||||
edge->type = type;
|
||||
edge->dest = g_strdup(dest);
|
||||
edge->edge_name = g_strdup(opts->edge_name ?: dest);
|
||||
edge->arg = g_memdup(opts->arg, opts->size_arg);
|
||||
|
||||
edge->before_cmd_line =
|
||||
opts->before_cmd_line ? g_strconcat(" ", opts->before_cmd_line, NULL) : NULL;
|
||||
edge->extra_device_opts =
|
||||
opts->extra_device_opts ? g_strconcat(",", opts->extra_device_opts, NULL) : NULL;
|
||||
edge->after_cmd_line =
|
||||
opts->after_cmd_line ? g_strconcat(" ", opts->after_cmd_line, NULL) : NULL;
|
||||
|
||||
QSLIST_INSERT_HEAD(list, edge, edge_list);
|
||||
}
|
||||
|
||||
/* destroy_edges(): frees all edges inside a given @list */
|
||||
static void destroy_edges(void *list)
|
||||
{
|
||||
QOSGraphEdge *temp;
|
||||
QOSGraphEdgeList *elist = list;
|
||||
|
||||
while (!QSLIST_EMPTY(elist)) {
|
||||
temp = QSLIST_FIRST(elist);
|
||||
QSLIST_REMOVE_HEAD(elist, edge_list);
|
||||
g_free(temp->dest);
|
||||
g_free(temp->before_cmd_line);
|
||||
g_free(temp->after_cmd_line);
|
||||
g_free(temp->extra_device_opts);
|
||||
g_free(temp->edge_name);
|
||||
g_free(temp->arg);
|
||||
g_free(temp);
|
||||
}
|
||||
g_free(elist);
|
||||
}
|
||||
|
||||
/**
|
||||
* create_node(): creates a node @name of type @type
|
||||
* and inserts it to the nodes hash table.
|
||||
* By default, node is not available.
|
||||
*/
|
||||
static QOSGraphNode *create_node(const char *name, QOSNodeType type)
|
||||
{
|
||||
if (g_hash_table_lookup(node_table, name)) {
|
||||
g_printerr("Node %s already created\n", name);
|
||||
abort();
|
||||
}
|
||||
|
||||
QOSGraphNode *node = g_new0(QOSGraphNode, 1);
|
||||
node->type = type;
|
||||
node->available = false;
|
||||
node->name = g_strdup(name);
|
||||
g_hash_table_insert(node_table, node->name, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy_node(): frees a node @val from the nodes hash table.
|
||||
* Note that node->name is not free'd since it will represent the
|
||||
* hash table key
|
||||
*/
|
||||
static void destroy_node(void *val)
|
||||
{
|
||||
QOSGraphNode *node = val;
|
||||
g_free(node->command_line);
|
||||
g_free(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy_string(): frees @key from the nodes hash table.
|
||||
* Actually frees the node->name
|
||||
*/
|
||||
static void destroy_string(void *key)
|
||||
{
|
||||
g_free(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* search_node(): search for a node @key in the nodes hash table
|
||||
* Returns the QOSGraphNode if found, #NULL otherwise
|
||||
*/
|
||||
static QOSGraphNode *search_node(const char *key)
|
||||
{
|
||||
return g_hash_table_lookup(node_table, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_edgelist(): returns the edge list (value) assigned to
|
||||
* the @key in the edge hash table.
|
||||
* This list will contain all edges with source equal to @key
|
||||
*
|
||||
* Returns: on success: the %QOSGraphEdgeList
|
||||
* otherwise: abort()
|
||||
*/
|
||||
static QOSGraphEdgeList *get_edgelist(const char *key)
|
||||
{
|
||||
return g_hash_table_lookup(edge_table, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* search_list_edges(): search for an edge with destination @dest
|
||||
* in the given @edgelist.
|
||||
*
|
||||
* Returns: on success: the %QOSGraphEdge
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
static QOSGraphEdge *search_list_edges(QOSGraphEdgeList *edgelist,
|
||||
const char *dest)
|
||||
{
|
||||
QOSGraphEdge *tmp, *next;
|
||||
if (!edgelist) {
|
||||
return NULL;
|
||||
}
|
||||
QSLIST_FOREACH_SAFE(tmp, edgelist, edge_list, next) {
|
||||
if (g_strcmp0(tmp->dest, dest) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* search_machine(): search for a machine @name in the node hash
|
||||
* table. A machine is the child of the root node.
|
||||
* This function forces the research in the childs of the root,
|
||||
* to check the node is a proper machine
|
||||
*
|
||||
* Returns: on success: the %QOSGraphNode
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
static QOSGraphNode *search_machine(const char *name)
|
||||
{
|
||||
QOSGraphNode *n;
|
||||
QOSGraphEdgeList *root_list = get_edgelist(QOS_ROOT);
|
||||
QOSGraphEdge *e = search_list_edges(root_list, name);
|
||||
if (!e) {
|
||||
return NULL;
|
||||
}
|
||||
n = search_node(e->dest);
|
||||
if (n->type == QNODE_MACHINE) {
|
||||
return n;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* create_interface(): checks if there is already
|
||||
* a node @node in the node hash table, if not
|
||||
* creates a node @node of type #QNODE_INTERFACE
|
||||
* and inserts it. If there is one, check it's
|
||||
* a #QNODE_INTERFACE and abort() if it's not.
|
||||
*/
|
||||
static void create_interface(const char *node)
|
||||
{
|
||||
QOSGraphNode *interface;
|
||||
interface = search_node(node);
|
||||
if (!interface) {
|
||||
create_node(node, QNODE_INTERFACE);
|
||||
} else if (interface->type != QNODE_INTERFACE) {
|
||||
fprintf(stderr, "Error: Node %s is not an interface\n", node);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* build_machine_cmd_line(): builds the command line for the machine
|
||||
* @node. The node name must be a valid qemu identifier, since it
|
||||
* will be used to build the command line.
|
||||
*
|
||||
* It is also possible to pass an optional @args that will be
|
||||
* concatenated to the command line.
|
||||
*
|
||||
* For machines, prepend -M to the machine name. ", @rgs" is added
|
||||
* after the -M <machine> command.
|
||||
*/
|
||||
static void build_machine_cmd_line(QOSGraphNode *node, const char *args)
|
||||
{
|
||||
char *machine = qos_get_machine_type(node->name);
|
||||
if (args) {
|
||||
node->command_line = g_strconcat("-M ", machine, ",", args, NULL);
|
||||
} else {
|
||||
node->command_line = g_strconcat("-M ", machine, " ", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* build_driver_cmd_line(): builds the command line for the driver
|
||||
* @node. The node name must be a valid qemu identifier, since it
|
||||
* will be used to build the command line.
|
||||
*
|
||||
* Driver do not need additional command line, since it will be
|
||||
* provided by the edge options.
|
||||
*
|
||||
* For drivers, prepend -device to the node name.
|
||||
*/
|
||||
static void build_driver_cmd_line(QOSGraphNode *node)
|
||||
{
|
||||
node->command_line = g_strconcat(" -device ", node->name, NULL);
|
||||
}
|
||||
|
||||
/* qos_print_cb(): callback prints all path found by the DFS algorithm. */
|
||||
static void qos_print_cb(QOSGraphNode *path, int length)
|
||||
{
|
||||
#if QGRAPH_PRINT_DEBUG
|
||||
printf("%d elements\n", length);
|
||||
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (path->path_edge) {
|
||||
printf("%s ", path->name);
|
||||
switch (path->path_edge->type) {
|
||||
case QEDGE_PRODUCES:
|
||||
printf("--PRODUCES--> ");
|
||||
break;
|
||||
case QEDGE_CONSUMED_BY:
|
||||
printf("--CONSUMED_BY--> ");
|
||||
break;
|
||||
case QEDGE_CONTAINS:
|
||||
printf("--CONTAINS--> ");
|
||||
break;
|
||||
}
|
||||
path = search_node(path->path_edge->dest);
|
||||
}
|
||||
|
||||
printf("%s\n\n", path->name);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* qos_push(): push a node @el and edge @e in the qos_node_stack */
|
||||
static void qos_push(QOSGraphNode *el, QOSStackElement *parent,
|
||||
QOSGraphEdge *e)
|
||||
{
|
||||
int len = 0; /* root is not counted */
|
||||
if (qos_node_tos == QOS_PATH_MAX_ELEMENT_SIZE) {
|
||||
g_printerr("QOSStack: full stack, cannot push");
|
||||
abort();
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
len = parent->length + 1;
|
||||
}
|
||||
qos_node_stack[qos_node_tos++] = (QOSStackElement) {
|
||||
.node = el,
|
||||
.parent = parent,
|
||||
.parent_edge = e,
|
||||
.length = len,
|
||||
};
|
||||
}
|
||||
|
||||
/* qos_tos(): returns the top of stack, without popping */
|
||||
static QOSStackElement *qos_tos(void)
|
||||
{
|
||||
return &qos_node_stack[qos_node_tos - 1];
|
||||
}
|
||||
|
||||
/* qos_pop(): pops an element from the tos, setting it unvisited*/
|
||||
static QOSStackElement *qos_pop(void)
|
||||
{
|
||||
if (qos_node_tos == 0) {
|
||||
g_printerr("QOSStack: empty stack, cannot pop");
|
||||
abort();
|
||||
}
|
||||
QOSStackElement *e = qos_tos();
|
||||
e->node->visited = false;
|
||||
qos_node_tos--;
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* qos_reverse_path(): reverses the found path, going from
|
||||
* test-to-machine to machine-to-test
|
||||
*/
|
||||
static QOSGraphNode *qos_reverse_path(QOSStackElement *el)
|
||||
{
|
||||
if (!el) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
el->node->path_edge = NULL;
|
||||
|
||||
while (el->parent) {
|
||||
el->parent->node->path_edge = el->parent_edge;
|
||||
el = el->parent;
|
||||
}
|
||||
|
||||
return el->node;
|
||||
}
|
||||
|
||||
/**
|
||||
* qos_traverse_graph(): graph-walking algorithm, using Depth First Search it
|
||||
* starts from the root @machine and walks all possible path until it
|
||||
* reaches a test node.
|
||||
* At that point, it reverses the path found and invokes the @callback.
|
||||
*
|
||||
* Being Depth First Search, time complexity is O(|V| + |E|), while
|
||||
* space is O(|V|). In this case, the maximum stack size is set by
|
||||
* QOS_PATH_MAX_ELEMENT_SIZE.
|
||||
*/
|
||||
static void qos_traverse_graph(QOSGraphNode *root, QOSTestCallback callback)
|
||||
{
|
||||
QOSGraphNode *v, *dest_node, *path;
|
||||
QOSStackElement *s_el;
|
||||
QOSGraphEdge *e, *next;
|
||||
QOSGraphEdgeList *list;
|
||||
|
||||
qos_push(root, NULL, NULL);
|
||||
|
||||
while (qos_node_tos > 0) {
|
||||
s_el = qos_tos();
|
||||
v = s_el->node;
|
||||
if (v->visited) {
|
||||
qos_pop();
|
||||
continue;
|
||||
}
|
||||
v->visited = true;
|
||||
list = get_edgelist(v->name);
|
||||
if (!list) {
|
||||
qos_pop();
|
||||
if (v->type == QNODE_TEST) {
|
||||
v->visited = false;
|
||||
path = qos_reverse_path(s_el);
|
||||
callback(path, s_el->length);
|
||||
}
|
||||
} else {
|
||||
QSLIST_FOREACH_SAFE(e, list, edge_list, next) {
|
||||
dest_node = search_node(e->dest);
|
||||
|
||||
if (!dest_node) {
|
||||
fprintf(stderr, "node %s in %s -> %s does not exist\n",
|
||||
e->dest, v->name, e->dest);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!dest_node->visited && dest_node->available) {
|
||||
qos_push(dest_node, s_el, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* QGRAPH API*/
|
||||
|
||||
QOSGraphNode *qos_graph_get_node(const char *key)
|
||||
{
|
||||
return search_node(key);
|
||||
}
|
||||
|
||||
bool qos_graph_has_node(const char *node)
|
||||
{
|
||||
QOSGraphNode *n = search_node(node);
|
||||
return n != NULL;
|
||||
}
|
||||
|
||||
QOSNodeType qos_graph_get_node_type(const char *node)
|
||||
{
|
||||
QOSGraphNode *n = search_node(node);
|
||||
if (n) {
|
||||
return n->type;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool qos_graph_get_node_availability(const char *node)
|
||||
{
|
||||
QOSGraphNode *n = search_node(node);
|
||||
if (n) {
|
||||
return n->available;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest)
|
||||
{
|
||||
QOSGraphEdgeList *list = get_edgelist(node);
|
||||
return search_list_edges(list, dest);
|
||||
}
|
||||
|
||||
QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge)
|
||||
{
|
||||
if (!edge) {
|
||||
return -1;
|
||||
}
|
||||
return edge->type;;
|
||||
}
|
||||
|
||||
char *qos_graph_edge_get_dest(QOSGraphEdge *edge)
|
||||
{
|
||||
if (!edge) {
|
||||
return NULL;
|
||||
}
|
||||
return edge->dest;
|
||||
}
|
||||
|
||||
void *qos_graph_edge_get_arg(QOSGraphEdge *edge)
|
||||
{
|
||||
if (!edge) {
|
||||
return NULL;
|
||||
}
|
||||
return edge->arg;
|
||||
}
|
||||
|
||||
char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge)
|
||||
{
|
||||
if (!edge) {
|
||||
return NULL;
|
||||
}
|
||||
return edge->after_cmd_line;
|
||||
}
|
||||
|
||||
char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge)
|
||||
{
|
||||
if (!edge) {
|
||||
return NULL;
|
||||
}
|
||||
return edge->before_cmd_line;
|
||||
}
|
||||
|
||||
char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge)
|
||||
{
|
||||
if (!edge) {
|
||||
return NULL;
|
||||
}
|
||||
return edge->extra_device_opts;
|
||||
}
|
||||
|
||||
char *qos_graph_edge_get_name(QOSGraphEdge *edge)
|
||||
{
|
||||
if (!edge) {
|
||||
return NULL;
|
||||
}
|
||||
return edge->edge_name;
|
||||
}
|
||||
|
||||
bool qos_graph_has_edge(const char *start, const char *dest)
|
||||
{
|
||||
QOSGraphEdgeList *list = get_edgelist(start);
|
||||
QOSGraphEdge *e = search_list_edges(list, dest);
|
||||
return e != NULL;
|
||||
}
|
||||
|
||||
QOSGraphNode *qos_graph_get_machine(const char *node)
|
||||
{
|
||||
return search_machine(node);
|
||||
}
|
||||
|
||||
bool qos_graph_has_machine(const char *node)
|
||||
{
|
||||
QOSGraphNode *m = search_machine(node);
|
||||
return m != NULL;
|
||||
}
|
||||
|
||||
void qos_print_graph(void)
|
||||
{
|
||||
qos_graph_foreach_test_path(qos_print_cb);
|
||||
}
|
||||
|
||||
void qos_graph_init(void)
|
||||
{
|
||||
if (!node_table) {
|
||||
node_table = g_hash_table_new_full(g_str_hash, g_str_equal,
|
||||
destroy_string, destroy_node);
|
||||
create_node(QOS_ROOT, QNODE_DRIVER);
|
||||
}
|
||||
|
||||
if (!edge_table) {
|
||||
edge_table = g_hash_table_new_full(g_str_hash, g_str_equal,
|
||||
destroy_string, destroy_edges);
|
||||
}
|
||||
}
|
||||
|
||||
void qos_graph_destroy(void)
|
||||
{
|
||||
if (node_table) {
|
||||
g_hash_table_destroy(node_table);
|
||||
}
|
||||
|
||||
if (edge_table) {
|
||||
g_hash_table_destroy(edge_table);
|
||||
}
|
||||
|
||||
node_table = NULL;
|
||||
edge_table = NULL;
|
||||
}
|
||||
|
||||
void qos_node_destroy(void *key)
|
||||
{
|
||||
g_hash_table_remove(node_table, key);
|
||||
}
|
||||
|
||||
void qos_edge_destroy(void *key)
|
||||
{
|
||||
g_hash_table_remove(edge_table, key);
|
||||
}
|
||||
|
||||
void qos_add_test(const char *name, const char *interface,
|
||||
QOSTestFunc test_func, QOSGraphTestOptions *opts)
|
||||
{
|
||||
QOSGraphNode *node;
|
||||
char *test_name = g_strdup_printf("%s-tests/%s", interface, name);;
|
||||
|
||||
if (!opts) {
|
||||
opts = &(QOSGraphTestOptions) { };
|
||||
}
|
||||
node = create_node(test_name, QNODE_TEST);
|
||||
node->u.test.function = test_func;
|
||||
node->u.test.arg = opts->arg;
|
||||
assert(!opts->edge.arg);
|
||||
assert(!opts->edge.size_arg);
|
||||
|
||||
node->u.test.before = opts->before;
|
||||
node->u.test.subprocess = opts->subprocess;
|
||||
node->available = true;
|
||||
add_edge(interface, test_name, QEDGE_CONSUMED_BY, &opts->edge);
|
||||
g_free(test_name);
|
||||
}
|
||||
|
||||
void qos_node_create_machine(const char *name, QOSCreateMachineFunc function)
|
||||
{
|
||||
qos_node_create_machine_args(name, function, NULL);
|
||||
}
|
||||
|
||||
void qos_node_create_machine_args(const char *name,
|
||||
QOSCreateMachineFunc function,
|
||||
const char *opts)
|
||||
{
|
||||
QOSGraphNode *node = create_node(name, QNODE_MACHINE);
|
||||
build_machine_cmd_line(node, opts);
|
||||
node->u.machine.constructor = function;
|
||||
add_edge(QOS_ROOT, name, QEDGE_CONTAINS, NULL);
|
||||
}
|
||||
|
||||
void qos_node_create_driver(const char *name, QOSCreateDriverFunc function)
|
||||
{
|
||||
QOSGraphNode *node = create_node(name, QNODE_DRIVER);
|
||||
build_driver_cmd_line(node);
|
||||
node->u.driver.constructor = function;
|
||||
}
|
||||
|
||||
void qos_node_contains(const char *container, const char *contained,
|
||||
...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, contained);
|
||||
QOSGraphEdgeOptions *opts;
|
||||
|
||||
do {
|
||||
opts = va_arg(va, QOSGraphEdgeOptions *);
|
||||
add_edge(container, contained, QEDGE_CONTAINS, opts);
|
||||
} while (opts != NULL);
|
||||
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void qos_node_produces(const char *producer, const char *interface)
|
||||
{
|
||||
create_interface(interface);
|
||||
add_edge(producer, interface, QEDGE_PRODUCES, NULL);
|
||||
}
|
||||
|
||||
void qos_node_consumes(const char *consumer, const char *interface,
|
||||
QOSGraphEdgeOptions *opts)
|
||||
{
|
||||
create_interface(interface);
|
||||
add_edge(interface, consumer, QEDGE_CONSUMED_BY, opts);
|
||||
}
|
||||
|
||||
void qos_graph_node_set_availability(const char *node, bool av)
|
||||
{
|
||||
QOSGraphEdgeList *elist;
|
||||
QOSGraphNode *n = search_node(node);
|
||||
QOSGraphEdge *e, *next;
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
n->available = av;
|
||||
elist = get_edgelist(node);
|
||||
if (!elist) {
|
||||
return;
|
||||
}
|
||||
QSLIST_FOREACH_SAFE(e, elist, edge_list, next) {
|
||||
if (e->type == QEDGE_CONTAINS || e->type == QEDGE_PRODUCES) {
|
||||
qos_graph_node_set_availability(e->dest, av);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void qos_graph_foreach_test_path(QOSTestCallback fn)
|
||||
{
|
||||
QOSGraphNode *root = qos_graph_get_node(QOS_ROOT);
|
||||
qos_traverse_graph(root, fn);
|
||||
}
|
||||
|
||||
QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts)
|
||||
{
|
||||
QOSGraphObject *obj;
|
||||
|
||||
g_assert(node->type == QNODE_MACHINE);
|
||||
obj = node->u.machine.constructor(qts);
|
||||
obj->free = g_free;
|
||||
return obj;
|
||||
}
|
||||
|
||||
QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent,
|
||||
QGuestAllocator *alloc, void *arg)
|
||||
{
|
||||
QOSGraphObject *obj;
|
||||
|
||||
g_assert(node->type == QNODE_DRIVER);
|
||||
obj = node->u.driver.constructor(parent, alloc, arg);
|
||||
obj->free = g_free;
|
||||
return obj;
|
||||
}
|
||||
|
||||
void qos_object_destroy(QOSGraphObject *obj)
|
||||
{
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
if (obj->destructor) {
|
||||
obj->destructor(obj);
|
||||
}
|
||||
if (obj->free) {
|
||||
obj->free(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void qos_object_queue_destroy(QOSGraphObject *obj)
|
||||
{
|
||||
g_test_queue_destroy((GDestroyNotify) qos_object_destroy, obj);
|
||||
}
|
||||
|
||||
void qos_object_start_hw(QOSGraphObject *obj)
|
||||
{
|
||||
if (obj->start_hw) {
|
||||
obj->start_hw(obj);
|
||||
}
|
||||
}
|
||||
|
||||
char *qos_get_machine_type(char *name)
|
||||
{
|
||||
while (*name != '\0' && *name != '/') {
|
||||
name++;
|
||||
}
|
||||
|
||||
if (!*name || !name[1]) {
|
||||
fprintf(stderr, "Machine name has to be of the form <arch>/<machine>\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
return name + 1;
|
||||
}
|
||||
|
||||
void qos_delete_cmd_line(const char *name)
|
||||
{
|
||||
QOSGraphNode *node = search_node(name);
|
||||
if (node) {
|
||||
g_free(node->command_line);
|
||||
node->command_line = NULL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,575 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef QGRAPH_H
|
||||
#define QGRAPH_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <gmodule.h>
|
||||
#include <glib.h>
|
||||
#include "qemu/module.h"
|
||||
#include "malloc.h"
|
||||
|
||||
/* maximum path length */
|
||||
#define QOS_PATH_MAX_ELEMENT_SIZE 50
|
||||
|
||||
typedef struct QOSGraphObject QOSGraphObject;
|
||||
typedef struct QOSGraphNode QOSGraphNode;
|
||||
typedef struct QOSGraphEdge QOSGraphEdge;
|
||||
typedef struct QOSGraphNodeOptions QOSGraphNodeOptions;
|
||||
typedef struct QOSGraphEdgeOptions QOSGraphEdgeOptions;
|
||||
typedef struct QOSGraphTestOptions QOSGraphTestOptions;
|
||||
|
||||
/* Constructor for drivers, machines and test */
|
||||
typedef void *(*QOSCreateDriverFunc) (void *parent, QGuestAllocator *alloc,
|
||||
void *addr);
|
||||
typedef void *(*QOSCreateMachineFunc) (QTestState *qts);
|
||||
typedef void (*QOSTestFunc) (void *parent, void *arg, QGuestAllocator *alloc);
|
||||
|
||||
/* QOSGraphObject functions */
|
||||
typedef void *(*QOSGetDriver) (void *object, const char *interface);
|
||||
typedef QOSGraphObject *(*QOSGetDevice) (void *object, const char *name);
|
||||
typedef void (*QOSDestructorFunc) (QOSGraphObject *object);
|
||||
typedef void (*QOSStartFunct) (QOSGraphObject *object);
|
||||
|
||||
/* Test options functions */
|
||||
typedef void *(*QOSBeforeTest) (GString *cmd_line, void *arg);
|
||||
|
||||
/**
|
||||
* SECTION: qgraph.h
|
||||
* @title: Qtest Driver Framework
|
||||
* @short_description: interfaces to organize drivers and tests
|
||||
* as nodes in a graph
|
||||
*
|
||||
* This Qgraph API provides all basic functions to create a graph
|
||||
* and instantiate nodes representing machines, drivers and tests
|
||||
* representing their relations with CONSUMES, PRODUCES, and CONTAINS
|
||||
* edges.
|
||||
*
|
||||
* The idea is to have a framework where each test asks for a specific
|
||||
* driver, and the framework takes care of allocating the proper devices
|
||||
* required and passing the correct command line arguments to QEMU.
|
||||
*
|
||||
* A node can be of four types:
|
||||
* - QNODE_MACHINE: for example "arm/raspi2"
|
||||
* - QNODE_DRIVER: for example "generic-sdhci"
|
||||
* - QNODE_INTERFACE: for example "sdhci" (interface for all "-sdhci" drivers)
|
||||
* an interface is not explicitly created, it will be auto-
|
||||
* matically instantiated when a node consumes or produces
|
||||
* it.
|
||||
* - QNODE_TEST: for example "sdhci-test", consumes an interface and tests
|
||||
* the functions provided
|
||||
*
|
||||
* Notes for the nodes:
|
||||
* - QNODE_MACHINE: each machine struct must have a QGuestAllocator and
|
||||
* implement get_driver to return the allocator passing
|
||||
* "memory". The function can also return NULL if the
|
||||
* allocator is not set.
|
||||
* - QNODE_DRIVER: driver names must be unique, and machines and nodes
|
||||
* planned to be "consumed" by other nodes must match QEMU
|
||||
* drivers name, otherwise they won't be discovered
|
||||
*
|
||||
* An edge relation between two nodes (drivers or machines) X and Y can be:
|
||||
* - X CONSUMES Y: Y can be plugged into X
|
||||
* - X PRODUCES Y: X provides the interface Y
|
||||
* - X CONTAINS Y: Y is part of X component
|
||||
*
|
||||
* Basic framework steps are the following:
|
||||
* - All nodes and edges are created in their respective
|
||||
* machine/driver/test files
|
||||
* - The framework starts QEMU and asks for a list of available devices
|
||||
* and machines (note that only machines and "consumed" nodes are mapped
|
||||
* 1:1 with QEMU devices)
|
||||
* - The framework walks the graph starting from the available machines and
|
||||
* performs a Depth First Search for tests
|
||||
* - Once a test is found, the path is walked again and all drivers are
|
||||
* allocated accordingly and the final interface is passed to the test
|
||||
* - The test is executed
|
||||
* - Unused objects are cleaned and the path discovery is continued
|
||||
*
|
||||
* Depending on the QEMU binary used, only some drivers/machines will be
|
||||
* available and only test that are reached by them will be executed.
|
||||
*
|
||||
* <example>
|
||||
* <title>Creating new driver an its interface</title>
|
||||
* <programlisting>
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
struct My_driver {
|
||||
QOSGraphObject obj;
|
||||
Node_produced prod;
|
||||
Node_contained cont;
|
||||
}
|
||||
|
||||
static void my_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
g_free(obj);
|
||||
}
|
||||
|
||||
static void my_get_driver(void *object, const char *interface) {
|
||||
My_driver *dev = object;
|
||||
if (!g_strcmp0(interface, "my_interface")) {
|
||||
return &dev->prod;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static void my_get_device(void *object, const char *device) {
|
||||
My_driver *dev = object;
|
||||
if (!g_strcmp0(device, "my_driver_contained")) {
|
||||
return &dev->cont;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static void *my_driver_constructor(void *node_consumed,
|
||||
QOSGraphObject *alloc)
|
||||
{
|
||||
My_driver dev = g_new(My_driver, 1);
|
||||
// get the node pointed by the produce edge
|
||||
dev->obj.get_driver = my_get_driver;
|
||||
// get the node pointed by the contains
|
||||
dev->obj.get_device = my_get_device;
|
||||
// free the object
|
||||
dev->obj.destructor = my_destructor;
|
||||
do_something_with_node_consumed(node_consumed);
|
||||
// set all fields of contained device
|
||||
init_contained_device(&dev->cont);
|
||||
return &dev->obj;
|
||||
}
|
||||
|
||||
static void register_my_driver(void)
|
||||
{
|
||||
qos_node_create_driver("my_driver", my_driver_constructor);
|
||||
// contained drivers don't need a constructor,
|
||||
// they will be init by the parent.
|
||||
qos_node_create_driver("my_driver_contained", NULL);
|
||||
|
||||
// For the sake of this example, assume machine x86_64/pc contains
|
||||
// "other_node".
|
||||
// This relation, along with the machine and "other_node" creation,
|
||||
// should be defined in the x86_64_pc-machine.c file.
|
||||
// "my_driver" will then consume "other_node"
|
||||
qos_node_contains("my_driver", "my_driver_contained");
|
||||
qos_node_produces("my_driver", "my_interface");
|
||||
qos_node_consumes("my_driver", "other_node");
|
||||
}
|
||||
* </programlisting>
|
||||
* </example>
|
||||
*
|
||||
* In the above example, all possible types of relations are created:
|
||||
* node "my_driver" consumes, contains and produces other nodes.
|
||||
* more specifically:
|
||||
* x86_64/pc -->contains--> other_node <--consumes-- my_driver
|
||||
* |
|
||||
* my_driver_contained <--contains--+
|
||||
* |
|
||||
* my_interface <--produces--+
|
||||
*
|
||||
* or inverting the consumes edge in consumed_by:
|
||||
*
|
||||
* x86_64/pc -->contains--> other_node --consumed_by--> my_driver
|
||||
* |
|
||||
* my_driver_contained <--contains--+
|
||||
* |
|
||||
* my_interface <--produces--+
|
||||
*
|
||||
* <example>
|
||||
* <title>Creating new test</title>
|
||||
* <programlisting>
|
||||
* #include "libqos/qgraph.h"
|
||||
*
|
||||
* static void my_test_function(void *obj, void *data)
|
||||
* {
|
||||
* Node_produced *interface_to_test = obj;
|
||||
* // test interface_to_test
|
||||
* }
|
||||
*
|
||||
* static void register_my_test(void)
|
||||
* {
|
||||
* qos_add_test("my_interface", "my_test", my_test_function);
|
||||
* }
|
||||
*
|
||||
* libqos_init(register_my_test);
|
||||
*
|
||||
* </programlisting>
|
||||
* </example>
|
||||
*
|
||||
* Here a new test is created, consuming "my_interface" node
|
||||
* and creating a valid path from a machine to a test.
|
||||
* Final graph will be like this:
|
||||
* x86_64/pc -->contains--> other_node <--consumes-- my_driver
|
||||
* |
|
||||
* my_driver_contained <--contains--+
|
||||
* |
|
||||
* my_test --consumes--> my_interface <--produces--+
|
||||
*
|
||||
* or inverting the consumes edge in consumed_by:
|
||||
*
|
||||
* x86_64/pc -->contains--> other_node --consumed_by--> my_driver
|
||||
* |
|
||||
* my_driver_contained <--contains--+
|
||||
* |
|
||||
* my_test <--consumed_by-- my_interface <--produces--+
|
||||
*
|
||||
* Assuming there the binary is
|
||||
* QTEST_QEMU_BINARY=x86_64-softmmu/qemu-system-x86_64
|
||||
* a valid test path will be:
|
||||
* "/x86_64/pc/other_node/my_driver/my_interface/my_test".
|
||||
*
|
||||
* Additional examples are also in libqos/test-qgraph.c
|
||||
*
|
||||
* Command line:
|
||||
* Command line is built by using node names and optional arguments
|
||||
* passed by the user when building the edges.
|
||||
*
|
||||
* There are three types of command line arguments:
|
||||
* - in node : created from the node name. For example, machines will
|
||||
* have "-M <machine>" to its command line, while devices
|
||||
* "-device <device>". It is automatically done by the
|
||||
* framework.
|
||||
* - after node : added as additional argument to the node name.
|
||||
* This argument is added optionally when creating edges,
|
||||
* by setting the parameter @after_cmd_line and
|
||||
* @extra_edge_opts in #QOSGraphEdgeOptions.
|
||||
* The framework automatically adds
|
||||
* a comma before @extra_edge_opts,
|
||||
* because it is going to add attributes
|
||||
* after the destination node pointed by
|
||||
* the edge containing these options, and automatically
|
||||
* adds a space before @after_cmd_line, because it
|
||||
* adds an additional device, not an attribute.
|
||||
* - before node : added as additional argument to the node name.
|
||||
* This argument is added optionally when creating edges,
|
||||
* by setting the parameter @before_cmd_line in
|
||||
* #QOSGraphEdgeOptions. This attribute
|
||||
* is going to add attributes before the destination node
|
||||
* pointed by the edge containing these options. It is
|
||||
* helpful to commands that are not node-representable,
|
||||
* such as "-fdsev" or "-netdev".
|
||||
*
|
||||
* While adding command line in edges is always used, not all nodes names are
|
||||
* used in every path walk: this is because the contained or produced ones
|
||||
* are already added by QEMU, so only nodes that "consumes" will be used to
|
||||
* build the command line. Also, nodes that will have { "abstract" : true }
|
||||
* as QMP attribute will loose their command line, since they are not proper
|
||||
* devices to be added in QEMU.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.arg = NULL,
|
||||
.size_arg = 0,
|
||||
.after_cmd_line = "-device other",
|
||||
.before_cmd_line = "-netdev something",
|
||||
.extra_edge_opts = "addr=04.0",
|
||||
};
|
||||
QOSGraphNode * node = qos_node_create_driver("my_node", constructor);
|
||||
qos_node_consumes_args("my_node", "interface", &opts);
|
||||
*
|
||||
* Will produce the following command line:
|
||||
* "-netdev something -device my_node,addr=04.0 -device other"
|
||||
*/
|
||||
|
||||
/**
|
||||
* Edge options to be passed to the contains/consumes *_args function.
|
||||
*/
|
||||
struct QOSGraphEdgeOptions {
|
||||
void *arg; /*
|
||||
* optional arg that will be used by
|
||||
* dest edge
|
||||
*/
|
||||
uint32_t size_arg; /*
|
||||
* optional arg size that will be used by
|
||||
* dest edge
|
||||
*/
|
||||
const char *extra_device_opts;/*
|
||||
*optional additional command line for dest
|
||||
* edge, used to add additional attributes
|
||||
* *after* the node command line, the
|
||||
* framework automatically prepends ","
|
||||
* to this argument.
|
||||
*/
|
||||
const char *before_cmd_line; /*
|
||||
* optional additional command line for dest
|
||||
* edge, used to add additional attributes
|
||||
* *before* the node command line, usually
|
||||
* other non-node represented commands,
|
||||
* like "-fdsev synt"
|
||||
*/
|
||||
const char *after_cmd_line; /*
|
||||
* optional extra command line to be added
|
||||
* after the device command. This option
|
||||
* is used to add other devices
|
||||
* command line that depend on current node.
|
||||
* Automatically prepends " " to this
|
||||
* argument
|
||||
*/
|
||||
const char *edge_name; /*
|
||||
* optional edge to differentiate multiple
|
||||
* devices with same node name
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* Test options to be passed to the test functions.
|
||||
*/
|
||||
struct QOSGraphTestOptions {
|
||||
QOSGraphEdgeOptions edge; /* edge arguments that will be used by test.
|
||||
* Note that test *does not* use edge_name,
|
||||
* and uses instead arg and size_arg as
|
||||
* data arg for its test function.
|
||||
*/
|
||||
void *arg; /* passed to the .before function, or to the
|
||||
* test function if there is no .before
|
||||
* function
|
||||
*/
|
||||
QOSBeforeTest before; /* executed before the test. Can add
|
||||
* additional parameters to the command line
|
||||
* and modify the argument to the test function.
|
||||
*/
|
||||
bool subprocess; /* run the test in a subprocess */
|
||||
};
|
||||
|
||||
/**
|
||||
* Each driver, test or machine of this framework will have a
|
||||
* QOSGraphObject as first field.
|
||||
*
|
||||
* This set of functions offered by QOSGraphObject are executed
|
||||
* in different stages of the framework:
|
||||
* - get_driver / get_device : Once a machine-to-test path has been
|
||||
* found, the framework traverses it again and allocates all the
|
||||
* nodes, using the provided constructor. To satisfy their relations,
|
||||
* i.e. for produces or contains, where a struct constructor needs
|
||||
* an external parameter represented by the previous node,
|
||||
* the framework will call get_device (for contains) or
|
||||
* get_driver (for produces), depending on the edge type, passing
|
||||
* them the name of the next node to be taken and getting from them
|
||||
* the corresponding pointer to the actual structure of the next node to
|
||||
* be used in the path.
|
||||
*
|
||||
* - start_hw: This function is executed after all the path objects
|
||||
* have been allocated, but before the test is run. It starts the hw, setting
|
||||
* the initial configurations (*_device_enable) and making it ready for the
|
||||
* test.
|
||||
*
|
||||
* - destructor: Opposite to the node constructor, destroys the object.
|
||||
* This function is called after the test has been executed, and performs
|
||||
* a complete cleanup of each node allocated field. In case no constructor
|
||||
* is provided, no destructor will be called.
|
||||
*
|
||||
*/
|
||||
struct QOSGraphObject {
|
||||
/* for produces edges, returns void * */
|
||||
QOSGetDriver get_driver;
|
||||
/* for contains edges, returns a QOSGraphObject * */
|
||||
QOSGetDevice get_device;
|
||||
/* start the hw, get ready for the test */
|
||||
QOSStartFunct start_hw;
|
||||
/* destroy this QOSGraphObject */
|
||||
QOSDestructorFunc destructor;
|
||||
/* free the memory associated to the QOSGraphObject and its contained
|
||||
* children */
|
||||
GDestroyNotify free;
|
||||
};
|
||||
|
||||
/**
|
||||
* qos_graph_init(): initialize the framework, creates two hash
|
||||
* tables: one for the nodes and another for the edges.
|
||||
*/
|
||||
void qos_graph_init(void);
|
||||
|
||||
/**
|
||||
* qos_graph_destroy(): deallocates all the hash tables,
|
||||
* freeing all nodes and edges.
|
||||
*/
|
||||
void qos_graph_destroy(void);
|
||||
|
||||
/**
|
||||
* qos_node_destroy(): removes and frees a node from the,
|
||||
* nodes hash table.
|
||||
*/
|
||||
void qos_node_destroy(void *key);
|
||||
|
||||
/**
|
||||
* qos_edge_destroy(): removes and frees an edge from the,
|
||||
* edges hash table.
|
||||
*/
|
||||
void qos_edge_destroy(void *key);
|
||||
|
||||
/**
|
||||
* qos_add_test(): adds a test node @name to the nodes hash table.
|
||||
*
|
||||
* The test will consume a @interface node, and once the
|
||||
* graph walking algorithm has found it, the @test_func will be
|
||||
* executed. It also has the possibility to
|
||||
* add an optional @opts (see %QOSGraphNodeOptions).
|
||||
*
|
||||
* For tests, opts->edge.arg and size_arg represent the arg to pass
|
||||
* to @test_func
|
||||
*/
|
||||
void qos_add_test(const char *name, const char *interface,
|
||||
QOSTestFunc test_func,
|
||||
QOSGraphTestOptions *opts);
|
||||
|
||||
/**
|
||||
* qos_node_create_machine(): creates the machine @name and
|
||||
* adds it to the node hash table.
|
||||
*
|
||||
* This node will be of type QNODE_MACHINE and have @function
|
||||
* as constructor
|
||||
*/
|
||||
void qos_node_create_machine(const char *name, QOSCreateMachineFunc function);
|
||||
|
||||
/**
|
||||
* qos_node_create_machine_args(): same as qos_node_create_machine,
|
||||
* but with the possibility to add an optional ", @opts" after -M machine
|
||||
* command line.
|
||||
*/
|
||||
void qos_node_create_machine_args(const char *name,
|
||||
QOSCreateMachineFunc function,
|
||||
const char *opts);
|
||||
|
||||
/**
|
||||
* qos_node_create_driver(): creates the driver @name and
|
||||
* adds it to the node hash table.
|
||||
*
|
||||
* This node will be of type QNODE_DRIVER and have @function
|
||||
* as constructor
|
||||
*/
|
||||
void qos_node_create_driver(const char *name, QOSCreateDriverFunc function);
|
||||
|
||||
/**
|
||||
* qos_node_contains(): creates an edge of type QEDGE_CONTAINS and
|
||||
* adds it to the edge list mapped to @container in the
|
||||
* edge hash table.
|
||||
*
|
||||
* This edge will have @container as source and @contained as destination.
|
||||
*
|
||||
* It also has the possibility to add optional NULL-terminated
|
||||
* @opts parameters (see %QOSGraphEdgeOptions)
|
||||
*
|
||||
* This function can be useful when there are multiple devices
|
||||
* with the same node name contained in a machine/other node
|
||||
*
|
||||
* For example, if "arm/raspi2" contains 2 "generic-sdhci"
|
||||
* devices, the right commands will be:
|
||||
* qos_node_create_machine("arm/raspi2");
|
||||
* qos_node_create_driver("generic-sdhci", constructor);
|
||||
* //assume rest of the fields are set NULL
|
||||
* QOSGraphEdgeOptions op1 = { .edge_name = "emmc" };
|
||||
* QOSGraphEdgeOptions op2 = { .edge_name = "sdcard" };
|
||||
* qos_node_contains("arm/raspi2", "generic-sdhci", &op1, &op2, NULL);
|
||||
*
|
||||
* Of course this also requires that the @container's get_device function
|
||||
* should implement a case for "emmc" and "sdcard".
|
||||
*
|
||||
* For contains, op1.arg and op1.size_arg represent the arg to pass
|
||||
* to @contained constructor to properly initialize it.
|
||||
*/
|
||||
void qos_node_contains(const char *container, const char *contained, ...);
|
||||
|
||||
/**
|
||||
* qos_node_produces(): creates an edge of type QEDGE_PRODUCES and
|
||||
* adds it to the edge list mapped to @producer in the
|
||||
* edge hash table.
|
||||
*
|
||||
* This edge will have @producer as source and @interface as destination.
|
||||
*/
|
||||
void qos_node_produces(const char *producer, const char *interface);
|
||||
|
||||
/**
|
||||
* qos_node_consumes(): creates an edge of type QEDGE_CONSUMED_BY and
|
||||
* adds it to the edge list mapped to @interface in the
|
||||
* edge hash table.
|
||||
*
|
||||
* This edge will have @interface as source and @consumer as destination.
|
||||
* It also has the possibility to add an optional @opts
|
||||
* (see %QOSGraphEdgeOptions)
|
||||
*/
|
||||
void qos_node_consumes(const char *consumer, const char *interface,
|
||||
QOSGraphEdgeOptions *opts);
|
||||
|
||||
/**
|
||||
* qos_invalidate_command_line(): invalidates current command line, so that
|
||||
* qgraph framework cannot try to cache the current command line and
|
||||
* forces QEMU to restart.
|
||||
*/
|
||||
void qos_invalidate_command_line(void);
|
||||
|
||||
/**
|
||||
* qos_get_current_command_line(): return the command line required by the
|
||||
* machine and driver objects. This is the same string that was passed to
|
||||
* the test's "before" callback, if any.
|
||||
*/
|
||||
const char *qos_get_current_command_line(void);
|
||||
|
||||
/**
|
||||
* qos_allocate_objects():
|
||||
* @qts: The #QTestState that will be referred to by the machine object.
|
||||
* @alloc: Where to store the allocator for the machine object, or %NULL.
|
||||
*
|
||||
* Allocate driver objects for the current test
|
||||
* path, but relative to the QTestState @qts.
|
||||
*
|
||||
* Returns a test object just like the one that was passed to
|
||||
* the test function, but relative to @qts.
|
||||
*/
|
||||
void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc);
|
||||
|
||||
/**
|
||||
* qos_object_destroy(): calls the destructor for @obj
|
||||
*/
|
||||
void qos_object_destroy(QOSGraphObject *obj);
|
||||
|
||||
/**
|
||||
* qos_object_queue_destroy(): queue the destructor for @obj so that it is
|
||||
* called at the end of the test
|
||||
*/
|
||||
void qos_object_queue_destroy(QOSGraphObject *obj);
|
||||
|
||||
/**
|
||||
* qos_object_start_hw(): calls the start_hw function for @obj
|
||||
*/
|
||||
void qos_object_start_hw(QOSGraphObject *obj);
|
||||
|
||||
/**
|
||||
* qos_machine_new(): instantiate a new machine node
|
||||
* @node: A machine node to be instantiated
|
||||
* @qts: The #QTestState that will be referred to by the machine object.
|
||||
*
|
||||
* Returns a machine object.
|
||||
*/
|
||||
QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts);
|
||||
|
||||
/**
|
||||
* qos_machine_new(): instantiate a new driver node
|
||||
* @node: A driver node to be instantiated
|
||||
* @parent: A #QOSGraphObject to be consumed by the new driver node
|
||||
* @alloc: An allocator to be used by the new driver node.
|
||||
* @arg: The argument for the consumed-by edge to @node.
|
||||
*
|
||||
* Calls the constructor for the driver object.
|
||||
*/
|
||||
QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent,
|
||||
QGuestAllocator *alloc, void *arg);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef QGRAPH_EXTRA_H
|
||||
#define QGRAPH_EXTRA_H
|
||||
|
||||
/* This header is declaring additional helper functions defined in
|
||||
* libqos/qgraph.c
|
||||
* It should not be included in tests
|
||||
*/
|
||||
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
typedef struct QOSGraphMachine QOSGraphMachine;
|
||||
typedef enum QOSEdgeType QOSEdgeType;
|
||||
typedef enum QOSNodeType QOSNodeType;
|
||||
|
||||
/* callback called when the walk path algorithm found a
|
||||
* valid path
|
||||
*/
|
||||
typedef void (*QOSTestCallback) (QOSGraphNode *path, int len);
|
||||
|
||||
/* edge types*/
|
||||
enum QOSEdgeType {
|
||||
QEDGE_CONTAINS,
|
||||
QEDGE_PRODUCES,
|
||||
QEDGE_CONSUMED_BY
|
||||
};
|
||||
|
||||
/* node types*/
|
||||
enum QOSNodeType {
|
||||
QNODE_MACHINE,
|
||||
QNODE_DRIVER,
|
||||
QNODE_INTERFACE,
|
||||
QNODE_TEST
|
||||
};
|
||||
|
||||
/* Graph Node */
|
||||
struct QOSGraphNode {
|
||||
QOSNodeType type;
|
||||
bool available; /* set by QEMU via QMP, used during graph walk */
|
||||
bool visited; /* used during graph walk */
|
||||
char *name; /* used to identify the node */
|
||||
char *command_line; /* used to start QEMU at test execution */
|
||||
union {
|
||||
struct {
|
||||
QOSCreateDriverFunc constructor;
|
||||
} driver;
|
||||
struct {
|
||||
QOSCreateMachineFunc constructor;
|
||||
} machine;
|
||||
struct {
|
||||
QOSTestFunc function;
|
||||
void *arg;
|
||||
QOSBeforeTest before;
|
||||
bool subprocess;
|
||||
} test;
|
||||
} u;
|
||||
|
||||
/**
|
||||
* only used when traversing the path, never rely on that except in the
|
||||
* qos_traverse_graph callback function
|
||||
*/
|
||||
QOSGraphEdge *path_edge;
|
||||
};
|
||||
|
||||
/**
|
||||
* qos_graph_get_node(): returns the node mapped to that @key.
|
||||
* It performs an hash map search O(1)
|
||||
*
|
||||
* Returns: on success: the %QOSGraphNode
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
QOSGraphNode *qos_graph_get_node(const char *key);
|
||||
|
||||
/**
|
||||
* qos_graph_has_node(): returns #TRUE if the node
|
||||
* has map has a node mapped to that @key.
|
||||
*/
|
||||
bool qos_graph_has_node(const char *node);
|
||||
|
||||
/**
|
||||
* qos_graph_get_node_type(): returns the %QOSNodeType
|
||||
* of the node @node.
|
||||
* It performs an hash map search O(1)
|
||||
* Returns: on success: the %QOSNodeType
|
||||
* otherwise: #-1
|
||||
*/
|
||||
QOSNodeType qos_graph_get_node_type(const char *node);
|
||||
|
||||
/**
|
||||
* qos_graph_get_node_availability(): returns the availability (boolean)
|
||||
* of the node @node.
|
||||
*/
|
||||
bool qos_graph_get_node_availability(const char *node);
|
||||
|
||||
/**
|
||||
* qos_graph_get_edge(): returns the edge
|
||||
* linking of the node @node with @dest.
|
||||
*
|
||||
* Returns: on success: the %QOSGraphEdge
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_type(): returns the edge type
|
||||
* of the edge @edge.
|
||||
*
|
||||
* Returns: on success: the %QOSEdgeType
|
||||
* otherwise: #-1
|
||||
*/
|
||||
QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_dest(): returns the name of the node
|
||||
* pointed as destination of edge @edge.
|
||||
*
|
||||
* Returns: on success: the destination
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
char *qos_graph_edge_get_dest(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_has_edge(): returns #TRUE if there
|
||||
* exists an edge from @start to @dest.
|
||||
*/
|
||||
bool qos_graph_has_edge(const char *start, const char *dest);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_arg(): returns the args assigned
|
||||
* to that @edge.
|
||||
*
|
||||
* Returns: on success: the arg
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
void *qos_graph_edge_get_arg(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_after_cmd_line(): returns the edge
|
||||
* command line that will be added after all the node arguments
|
||||
* and all the before_cmd_line arguments.
|
||||
*
|
||||
* Returns: on success: the char* arg
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_before_cmd_line(): returns the edge
|
||||
* command line that will be added before the node command
|
||||
* line argument.
|
||||
*
|
||||
* Returns: on success: the char* arg
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_extra_device_opts(): returns the arg
|
||||
* command line that will be added to the node command
|
||||
* line argument.
|
||||
*
|
||||
* Returns: on success: the char* arg
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_edge_get_name(): returns the name
|
||||
* assigned to the destination node (different only)
|
||||
* if there are multiple devices with the same node name
|
||||
* e.g. a node has two "generic-sdhci", "emmc" and "sdcard"
|
||||
* there will be two edges with edge_name ="emmc" and "sdcard"
|
||||
*
|
||||
* Returns always the char* edge_name
|
||||
*/
|
||||
char *qos_graph_edge_get_name(QOSGraphEdge *edge);
|
||||
|
||||
/**
|
||||
* qos_graph_get_machine(): returns the machine assigned
|
||||
* to that @node name.
|
||||
*
|
||||
* It performs a search only trough the list of machines
|
||||
* (i.e. the QOS_ROOT child).
|
||||
*
|
||||
* Returns: on success: the %QOSGraphNode
|
||||
* otherwise: #NULL
|
||||
*/
|
||||
QOSGraphNode *qos_graph_get_machine(const char *node);
|
||||
|
||||
/**
|
||||
* qos_graph_has_machine(): returns #TRUE if the node
|
||||
* has map has a node mapped to that @node.
|
||||
*/
|
||||
bool qos_graph_has_machine(const char *node);
|
||||
|
||||
|
||||
/**
|
||||
* qos_print_graph(): walks the graph and prints
|
||||
* all machine-to-test paths.
|
||||
*/
|
||||
void qos_print_graph(void);
|
||||
|
||||
/**
|
||||
* qos_graph_foreach_test_path(): executes the Depth First search
|
||||
* algorithm and applies @fn to all discovered paths.
|
||||
*
|
||||
* See qos_traverse_graph() in qgraph.c for more info on
|
||||
* how it works.
|
||||
*/
|
||||
void qos_graph_foreach_test_path(QOSTestCallback fn);
|
||||
|
||||
/**
|
||||
* qos_get_machine_type(): return QEMU machine type for a machine node.
|
||||
* This function requires every machine @name to be in the form
|
||||
* <arch>/<machine_name>, like "arm/raspi2" or "x86_64/pc".
|
||||
*
|
||||
* The function will validate the format and return a pointer to
|
||||
* @machine to <machine_name>. For example, when passed "x86_64/pc"
|
||||
* it will return "pc".
|
||||
*
|
||||
* Note that this function *does not* allocate any new string.
|
||||
*/
|
||||
char *qos_get_machine_type(char *name);
|
||||
|
||||
/**
|
||||
* qos_delete_cmd_line(): delete the
|
||||
* command line present in node mapped with key @name.
|
||||
*
|
||||
* This function is called when the QMP query returns a node with
|
||||
* { "abstract" : true } attribute.
|
||||
*/
|
||||
void qos_delete_cmd_line(const char *name);
|
||||
|
||||
/**
|
||||
* qos_graph_node_set_availability(): sets the node identified
|
||||
* by @node with availability @av.
|
||||
*/
|
||||
void qos_graph_node_set_availability(const char *node, bool av);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "pci.h"
|
||||
#include "sdhci.h"
|
||||
#include "hw/pci/pci.h"
|
||||
|
||||
static void set_qsdhci_fields(QSDHCI *s, uint8_t version, uint8_t baseclock,
|
||||
bool sdma, uint64_t reg)
|
||||
{
|
||||
s->props.version = version;
|
||||
s->props.baseclock = baseclock;
|
||||
s->props.capab.sdma = sdma;
|
||||
s->props.capab.reg = reg;
|
||||
}
|
||||
|
||||
/* Memory mapped implementation of QSDHCI */
|
||||
|
||||
static uint16_t sdhci_mm_readw(QSDHCI *s, uint32_t reg)
|
||||
{
|
||||
QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci);
|
||||
return qtest_readw(smm->qts, smm->addr + reg);
|
||||
}
|
||||
|
||||
static uint64_t sdhci_mm_readq(QSDHCI *s, uint32_t reg)
|
||||
{
|
||||
QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci);
|
||||
return qtest_readq(smm->qts, smm->addr + reg);
|
||||
}
|
||||
|
||||
static void sdhci_mm_writeq(QSDHCI *s, uint32_t reg, uint64_t val)
|
||||
{
|
||||
QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci);
|
||||
qtest_writeq(smm->qts, smm->addr + reg, val);
|
||||
}
|
||||
|
||||
static void *sdhci_mm_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QSDHCI_MemoryMapped *smm = obj;
|
||||
if (!g_strcmp0(interface, "sdhci")) {
|
||||
return &smm->sdhci;
|
||||
}
|
||||
fprintf(stderr, "%s not present in generic-sdhci\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts,
|
||||
uint32_t addr, QSDHCIProperties *common)
|
||||
{
|
||||
sdhci->obj.get_driver = sdhci_mm_get_driver;
|
||||
sdhci->sdhci.readw = sdhci_mm_readw;
|
||||
sdhci->sdhci.readq = sdhci_mm_readq;
|
||||
sdhci->sdhci.writeq = sdhci_mm_writeq;
|
||||
memcpy(&sdhci->sdhci.props, common, sizeof(QSDHCIProperties));
|
||||
sdhci->addr = addr;
|
||||
sdhci->qts = qts;
|
||||
}
|
||||
|
||||
/* PCI implementation of QSDHCI */
|
||||
|
||||
static uint16_t sdhci_pci_readw(QSDHCI *s, uint32_t reg)
|
||||
{
|
||||
QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci);
|
||||
return qpci_io_readw(&spci->dev, spci->mem_bar, reg);
|
||||
}
|
||||
|
||||
static uint64_t sdhci_pci_readq(QSDHCI *s, uint32_t reg)
|
||||
{
|
||||
QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci);
|
||||
return qpci_io_readq(&spci->dev, spci->mem_bar, reg);
|
||||
}
|
||||
|
||||
static void sdhci_pci_writeq(QSDHCI *s, uint32_t reg, uint64_t val)
|
||||
{
|
||||
QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci);
|
||||
return qpci_io_writeq(&spci->dev, spci->mem_bar, reg, val);
|
||||
}
|
||||
|
||||
static void *sdhci_pci_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QSDHCI_PCI *spci = object;
|
||||
if (!g_strcmp0(interface, "sdhci")) {
|
||||
return &spci->sdhci;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in sdhci-pci\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void sdhci_pci_start_hw(QOSGraphObject *obj)
|
||||
{
|
||||
QSDHCI_PCI *spci = (QSDHCI_PCI *)obj;
|
||||
qpci_device_enable(&spci->dev);
|
||||
}
|
||||
|
||||
static void sdhci_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QSDHCI_PCI *spci = (QSDHCI_PCI *)obj;
|
||||
qpci_iounmap(&spci->dev, spci->mem_bar);
|
||||
}
|
||||
|
||||
static void *sdhci_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
|
||||
{
|
||||
QSDHCI_PCI *spci = g_new0(QSDHCI_PCI, 1);
|
||||
QPCIBus *bus = pci_bus;
|
||||
uint64_t barsize;
|
||||
|
||||
qpci_device_init(&spci->dev, bus, addr);
|
||||
spci->mem_bar = qpci_iomap(&spci->dev, 0, &barsize);
|
||||
spci->sdhci.readw = sdhci_pci_readw;
|
||||
spci->sdhci.readq = sdhci_pci_readq;
|
||||
spci->sdhci.writeq = sdhci_pci_writeq;
|
||||
set_qsdhci_fields(&spci->sdhci, 2, 0, 1, 0x057834b4);
|
||||
|
||||
spci->obj.get_driver = sdhci_pci_get_driver;
|
||||
spci->obj.start_hw = sdhci_pci_start_hw;
|
||||
spci->obj.destructor = sdhci_destructor;
|
||||
return &spci->obj;
|
||||
}
|
||||
|
||||
static void qsdhci_register_nodes(void)
|
||||
{
|
||||
QPCIAddress addr = {
|
||||
.devfn = QPCI_DEVFN(4, 0),
|
||||
.vendor_id = PCI_VENDOR_ID_REDHAT,
|
||||
.device_id = PCI_DEVICE_ID_REDHAT_SDHCI,
|
||||
};
|
||||
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0",
|
||||
};
|
||||
|
||||
/* generic-sdhci */
|
||||
qos_node_create_driver("generic-sdhci", NULL);
|
||||
qos_node_produces("generic-sdhci", "sdhci");
|
||||
|
||||
/* sdhci-pci */
|
||||
add_qpci_address(&opts, &addr);
|
||||
qos_node_create_driver("sdhci-pci", sdhci_pci_create);
|
||||
qos_node_produces("sdhci-pci", "sdhci");
|
||||
qos_node_consumes("sdhci-pci", "pci-bus", &opts);
|
||||
|
||||
}
|
||||
|
||||
libqos_init(qsdhci_register_nodes);
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef QGRAPH_QSDHCI
|
||||
#define QGRAPH_QSDHCI
|
||||
|
||||
#include "libqos/qgraph.h"
|
||||
#include "pci.h"
|
||||
|
||||
typedef struct QSDHCI QSDHCI;
|
||||
typedef struct QSDHCI_MemoryMapped QSDHCI_MemoryMapped;
|
||||
typedef struct QSDHCI_PCI QSDHCI_PCI;
|
||||
typedef struct QSDHCIProperties QSDHCIProperties;
|
||||
|
||||
/* Properties common to all QSDHCI devices */
|
||||
struct QSDHCIProperties {
|
||||
uint8_t version;
|
||||
uint8_t baseclock;
|
||||
struct {
|
||||
bool sdma;
|
||||
uint64_t reg;
|
||||
} capab;
|
||||
};
|
||||
|
||||
struct QSDHCI {
|
||||
uint16_t (*readw)(QSDHCI *s, uint32_t reg);
|
||||
uint64_t (*readq)(QSDHCI *s, uint32_t reg);
|
||||
void (*writeq)(QSDHCI *s, uint32_t reg, uint64_t val);
|
||||
QSDHCIProperties props;
|
||||
};
|
||||
|
||||
/* Memory Mapped implementation of QSDHCI */
|
||||
struct QSDHCI_MemoryMapped {
|
||||
QOSGraphObject obj;
|
||||
QTestState *qts;
|
||||
QSDHCI sdhci;
|
||||
uint64_t addr;
|
||||
};
|
||||
|
||||
/* PCI implementation of QSDHCI */
|
||||
struct QSDHCI_PCI {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice dev;
|
||||
QSDHCI sdhci;
|
||||
QPCIBar mem_bar;
|
||||
};
|
||||
|
||||
/**
|
||||
* qos_init_sdhci_mm(): external constructor used by all drivers/machines
|
||||
* that "contain" a #QSDHCI_MemoryMapped driver
|
||||
*/
|
||||
void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts,
|
||||
uint32_t addr, QSDHCIProperties *common);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* QTest testcase for tpci200 PCI-IndustryPack bridge
|
||||
*
|
||||
* Copyright (c) 2014 SUSE LINUX Products GmbH
|
||||
*
|
||||
* 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 "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
typedef struct QTpci200 QTpci200;
|
||||
typedef struct QIpack QIpack;
|
||||
|
||||
struct QIpack {
|
||||
|
||||
};
|
||||
struct QTpci200 {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice dev;
|
||||
QIpack ipack;
|
||||
};
|
||||
|
||||
/* tpci200 */
|
||||
static void *tpci200_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QTpci200 *tpci200 = obj;
|
||||
if (!g_strcmp0(interface, "ipack")) {
|
||||
return &tpci200->ipack;
|
||||
}
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return &tpci200->dev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in tpci200\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *tpci200_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
|
||||
{
|
||||
QTpci200 *tpci200 = g_new0(QTpci200, 1);
|
||||
QPCIBus *bus = pci_bus;
|
||||
|
||||
qpci_device_init(&tpci200->dev, bus, addr);
|
||||
tpci200->obj.get_driver = tpci200_get_driver;
|
||||
return &tpci200->obj;
|
||||
}
|
||||
|
||||
static void tpci200_register_nodes(void)
|
||||
{
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0,id=ipack0",
|
||||
};
|
||||
add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
|
||||
|
||||
qos_node_create_driver("tpci200", tpci200_create);
|
||||
qos_node_consumes("tpci200", "pci-bus", &opts);
|
||||
qos_node_produces("tpci200", "ipack");
|
||||
qos_node_produces("tpci200", "pci-device");
|
||||
}
|
||||
|
||||
libqos_init(tpci200_register_nodes);
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "standard-headers/linux/virtio_ids.h"
|
||||
#include "libqos/virtio-9p.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
static QGuestAllocator *alloc;
|
||||
|
||||
static void virtio_9p_cleanup(QVirtio9P *interface)
|
||||
{
|
||||
qvirtqueue_cleanup(interface->vdev->bus, interface->vq, alloc);
|
||||
}
|
||||
|
||||
static void virtio_9p_setup(QVirtio9P *interface)
|
||||
{
|
||||
interface->vq = qvirtqueue_setup(interface->vdev, alloc, 0);
|
||||
qvirtio_set_driver_ok(interface->vdev);
|
||||
}
|
||||
|
||||
/* virtio-9p-device */
|
||||
static void virtio_9p_device_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QVirtio9PDevice *v_9p = (QVirtio9PDevice *) obj;
|
||||
QVirtio9P *v9p = &v_9p->v9p;
|
||||
|
||||
virtio_9p_cleanup(v9p);
|
||||
}
|
||||
|
||||
static void virtio_9p_device_start_hw(QOSGraphObject *obj)
|
||||
{
|
||||
QVirtio9PDevice *v_9p = (QVirtio9PDevice *) obj;
|
||||
QVirtio9P *v9p = &v_9p->v9p;
|
||||
|
||||
virtio_9p_setup(v9p);
|
||||
}
|
||||
|
||||
static void *virtio_9p_get_driver(QVirtio9P *v_9p,
|
||||
const char *interface)
|
||||
{
|
||||
if (!g_strcmp0(interface, "virtio-9p")) {
|
||||
return v_9p;
|
||||
}
|
||||
if (!g_strcmp0(interface, "virtio")) {
|
||||
return v_9p->vdev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in virtio-9p-device\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *virtio_9p_device_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QVirtio9PDevice *v_9p = object;
|
||||
return virtio_9p_get_driver(&v_9p->v9p, interface);
|
||||
}
|
||||
|
||||
static void *virtio_9p_device_create(void *virtio_dev,
|
||||
QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtio9PDevice *virtio_device = g_new0(QVirtio9PDevice, 1);
|
||||
QVirtio9P *interface = &virtio_device->v9p;
|
||||
|
||||
interface->vdev = virtio_dev;
|
||||
alloc = t_alloc;
|
||||
|
||||
virtio_device->obj.destructor = virtio_9p_device_destructor;
|
||||
virtio_device->obj.get_driver = virtio_9p_device_get_driver;
|
||||
virtio_device->obj.start_hw = virtio_9p_device_start_hw;
|
||||
|
||||
return &virtio_device->obj;
|
||||
}
|
||||
|
||||
/* virtio-9p-pci */
|
||||
static void virtio_9p_pci_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QVirtio9PPCI *v9_pci = (QVirtio9PPCI *) obj;
|
||||
QVirtio9P *interface = &v9_pci->v9p;
|
||||
QOSGraphObject *pci_vobj = &v9_pci->pci_vdev.obj;
|
||||
|
||||
virtio_9p_cleanup(interface);
|
||||
qvirtio_pci_destructor(pci_vobj);
|
||||
}
|
||||
|
||||
static void virtio_9p_pci_start_hw(QOSGraphObject *obj)
|
||||
{
|
||||
QVirtio9PPCI *v9_pci = (QVirtio9PPCI *) obj;
|
||||
QVirtio9P *interface = &v9_pci->v9p;
|
||||
QOSGraphObject *pci_vobj = &v9_pci->pci_vdev.obj;
|
||||
|
||||
qvirtio_pci_start_hw(pci_vobj);
|
||||
virtio_9p_setup(interface);
|
||||
}
|
||||
|
||||
static void *virtio_9p_pci_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QVirtio9PPCI *v_9p = object;
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return v_9p->pci_vdev.pdev;
|
||||
}
|
||||
return virtio_9p_get_driver(&v_9p->v9p, interface);
|
||||
}
|
||||
|
||||
static void *virtio_9p_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtio9PPCI *v9_pci = g_new0(QVirtio9PPCI, 1);
|
||||
QVirtio9P *interface = &v9_pci->v9p;
|
||||
QOSGraphObject *obj = &v9_pci->pci_vdev.obj;
|
||||
|
||||
virtio_pci_init(&v9_pci->pci_vdev, pci_bus, addr);
|
||||
interface->vdev = &v9_pci->pci_vdev.vdev;
|
||||
alloc = t_alloc;
|
||||
|
||||
g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_9P);
|
||||
|
||||
obj->destructor = virtio_9p_pci_destructor;
|
||||
obj->start_hw = virtio_9p_pci_start_hw;
|
||||
obj->get_driver = virtio_9p_pci_get_driver;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void virtio_9p_register_nodes(void)
|
||||
{
|
||||
const char *str_simple = "fsdev=fsdev0,mount_tag=" MOUNT_TAG;
|
||||
const char *str_addr = "fsdev=fsdev0,addr=04.0,mount_tag=" MOUNT_TAG;
|
||||
|
||||
QPCIAddress addr = {
|
||||
.devfn = QPCI_DEVFN(4, 0),
|
||||
};
|
||||
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.before_cmd_line = "-fsdev synth,id=fsdev0",
|
||||
};
|
||||
|
||||
/* virtio-9p-device */
|
||||
opts.extra_device_opts = str_simple,
|
||||
qos_node_create_driver("virtio-9p-device", virtio_9p_device_create);
|
||||
qos_node_consumes("virtio-9p-device", "virtio-bus", &opts);
|
||||
qos_node_produces("virtio-9p-device", "virtio");
|
||||
qos_node_produces("virtio-9p-device", "virtio-9p");
|
||||
|
||||
/* virtio-9p-pci */
|
||||
opts.extra_device_opts = str_addr;
|
||||
add_qpci_address(&opts, &addr);
|
||||
qos_node_create_driver("virtio-9p-pci", virtio_9p_pci_create);
|
||||
qos_node_consumes("virtio-9p-pci", "pci-bus", &opts);
|
||||
qos_node_produces("virtio-9p-pci", "pci-device");
|
||||
qos_node_produces("virtio-9p-pci", "virtio");
|
||||
qos_node_produces("virtio-9p-pci", "virtio-9p");
|
||||
|
||||
}
|
||||
|
||||
libqos_init(virtio_9p_register_nodes);
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/virtio-pci.h"
|
||||
|
||||
typedef struct QVirtio9P QVirtio9P;
|
||||
typedef struct QVirtio9PPCI QVirtio9PPCI;
|
||||
typedef struct QVirtio9PDevice QVirtio9PDevice;
|
||||
|
||||
#define MOUNT_TAG "qtest"
|
||||
|
||||
struct QVirtio9P {
|
||||
QVirtioDevice *vdev;
|
||||
QVirtQueue *vq;
|
||||
};
|
||||
|
||||
struct QVirtio9PPCI {
|
||||
QVirtioPCIDevice pci_vdev;
|
||||
QVirtio9P v9p;
|
||||
};
|
||||
|
||||
struct QVirtio9PDevice {
|
||||
QOSGraphObject obj;
|
||||
QVirtio9P v9p;
|
||||
};
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio-balloon.h"
|
||||
|
||||
/* virtio-balloon-device */
|
||||
static void *qvirtio_balloon_get_driver(QVirtioBalloon *v_balloon,
|
||||
const char *interface)
|
||||
{
|
||||
if (!g_strcmp0(interface, "virtio-balloon")) {
|
||||
return v_balloon;
|
||||
}
|
||||
if (!g_strcmp0(interface, "virtio")) {
|
||||
return v_balloon->vdev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in virtio-balloon-device\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *qvirtio_balloon_device_get_driver(void *object,
|
||||
const char *interface)
|
||||
{
|
||||
QVirtioBalloonDevice *v_balloon = object;
|
||||
return qvirtio_balloon_get_driver(&v_balloon->balloon, interface);
|
||||
}
|
||||
|
||||
static void *virtio_balloon_device_create(void *virtio_dev,
|
||||
QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtioBalloonDevice *virtio_bdevice = g_new0(QVirtioBalloonDevice, 1);
|
||||
QVirtioBalloon *interface = &virtio_bdevice->balloon;
|
||||
|
||||
interface->vdev = virtio_dev;
|
||||
|
||||
virtio_bdevice->obj.get_driver = qvirtio_balloon_device_get_driver;
|
||||
|
||||
return &virtio_bdevice->obj;
|
||||
}
|
||||
|
||||
/* virtio-balloon-pci */
|
||||
static void *qvirtio_balloon_pci_get_driver(void *object,
|
||||
const char *interface)
|
||||
{
|
||||
QVirtioBalloonPCI *v_balloon = object;
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return v_balloon->pci_vdev.pdev;
|
||||
}
|
||||
return qvirtio_balloon_get_driver(&v_balloon->balloon, interface);
|
||||
}
|
||||
|
||||
static void *virtio_balloon_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtioBalloonPCI *virtio_bpci = g_new0(QVirtioBalloonPCI, 1);
|
||||
QVirtioBalloon *interface = &virtio_bpci->balloon;
|
||||
QOSGraphObject *obj = &virtio_bpci->pci_vdev.obj;
|
||||
|
||||
|
||||
virtio_pci_init(&virtio_bpci->pci_vdev, pci_bus, addr);
|
||||
interface->vdev = &virtio_bpci->pci_vdev.vdev;
|
||||
|
||||
obj->get_driver = qvirtio_balloon_pci_get_driver;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void virtio_balloon_register_nodes(void)
|
||||
{
|
||||
QPCIAddress addr = {
|
||||
.devfn = QPCI_DEVFN(4, 0),
|
||||
};
|
||||
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0",
|
||||
};
|
||||
|
||||
/* virtio-balloon-device */
|
||||
qos_node_create_driver("virtio-balloon-device",
|
||||
virtio_balloon_device_create);
|
||||
qos_node_consumes("virtio-balloon-device", "virtio-bus", NULL);
|
||||
qos_node_produces("virtio-balloon-device", "virtio");
|
||||
qos_node_produces("virtio-balloon-device", "virtio-balloon");
|
||||
|
||||
/* virtio-balloon-pci */
|
||||
add_qpci_address(&opts, &addr);
|
||||
qos_node_create_driver("virtio-balloon-pci", virtio_balloon_pci_create);
|
||||
qos_node_consumes("virtio-balloon-pci", "pci-bus", &opts);
|
||||
qos_node_produces("virtio-balloon-pci", "pci-device");
|
||||
qos_node_produces("virtio-balloon-pci", "virtio");
|
||||
qos_node_produces("virtio-balloon-pci", "virtio-balloon");
|
||||
}
|
||||
|
||||
libqos_init(virtio_balloon_register_nodes);
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/virtio-pci.h"
|
||||
|
||||
typedef struct QVirtioBalloon QVirtioBalloon;
|
||||
typedef struct QVirtioBalloonPCI QVirtioBalloonPCI;
|
||||
typedef struct QVirtioBalloonDevice QVirtioBalloonDevice;
|
||||
|
||||
struct QVirtioBalloon {
|
||||
QVirtioDevice *vdev;
|
||||
};
|
||||
|
||||
struct QVirtioBalloonPCI {
|
||||
QVirtioPCIDevice pci_vdev;
|
||||
QVirtioBalloon balloon;
|
||||
};
|
||||
|
||||
struct QVirtioBalloonDevice {
|
||||
QOSGraphObject obj;
|
||||
QVirtioBalloon balloon;
|
||||
};
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "standard-headers/linux/virtio_blk.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio-blk.h"
|
||||
|
||||
#define PCI_SLOT 0x04
|
||||
#define PCI_FN 0x00
|
||||
|
||||
/* virtio-blk-device */
|
||||
static void *qvirtio_blk_get_driver(QVirtioBlk *v_blk,
|
||||
const char *interface)
|
||||
{
|
||||
if (!g_strcmp0(interface, "virtio-blk")) {
|
||||
return v_blk;
|
||||
}
|
||||
if (!g_strcmp0(interface, "virtio")) {
|
||||
return v_blk->vdev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in virtio-blk-device\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *qvirtio_blk_device_get_driver(void *object,
|
||||
const char *interface)
|
||||
{
|
||||
QVirtioBlkDevice *v_blk = object;
|
||||
return qvirtio_blk_get_driver(&v_blk->blk, interface);
|
||||
}
|
||||
|
||||
static void *virtio_blk_device_create(void *virtio_dev,
|
||||
QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtioBlkDevice *virtio_blk = g_new0(QVirtioBlkDevice, 1);
|
||||
QVirtioBlk *interface = &virtio_blk->blk;
|
||||
|
||||
interface->vdev = virtio_dev;
|
||||
|
||||
virtio_blk->obj.get_driver = qvirtio_blk_device_get_driver;
|
||||
|
||||
return &virtio_blk->obj;
|
||||
}
|
||||
|
||||
/* virtio-blk-pci */
|
||||
static void *qvirtio_blk_pci_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QVirtioBlkPCI *v_blk = object;
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return v_blk->pci_vdev.pdev;
|
||||
}
|
||||
return qvirtio_blk_get_driver(&v_blk->blk, interface);
|
||||
}
|
||||
|
||||
static void *virtio_blk_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtioBlkPCI *virtio_blk = g_new0(QVirtioBlkPCI, 1);
|
||||
QVirtioBlk *interface = &virtio_blk->blk;
|
||||
QOSGraphObject *obj = &virtio_blk->pci_vdev.obj;
|
||||
|
||||
virtio_pci_init(&virtio_blk->pci_vdev, pci_bus, addr);
|
||||
interface->vdev = &virtio_blk->pci_vdev.vdev;
|
||||
|
||||
g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_BLOCK);
|
||||
|
||||
obj->get_driver = qvirtio_blk_pci_get_driver;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void virtio_blk_register_nodes(void)
|
||||
{
|
||||
/* FIXME: every test using these two nodes needs to setup a
|
||||
* -drive,id=drive0 otherwise QEMU is not going to start.
|
||||
* Therefore, we do not include "produces" edge for virtio
|
||||
* and pci-device yet.
|
||||
*/
|
||||
|
||||
char *arg = g_strdup_printf("id=drv0,drive=drive0,addr=%x.%x",
|
||||
PCI_SLOT, PCI_FN);
|
||||
|
||||
QPCIAddress addr = {
|
||||
.devfn = QPCI_DEVFN(PCI_SLOT, PCI_FN),
|
||||
};
|
||||
|
||||
QOSGraphEdgeOptions opts = { };
|
||||
|
||||
/* virtio-blk-device */
|
||||
opts.extra_device_opts = "drive=drive0";
|
||||
qos_node_create_driver("virtio-blk-device", virtio_blk_device_create);
|
||||
qos_node_consumes("virtio-blk-device", "virtio-bus", &opts);
|
||||
qos_node_produces("virtio-blk-device", "virtio-blk");
|
||||
|
||||
/* virtio-blk-pci */
|
||||
opts.extra_device_opts = arg;
|
||||
add_qpci_address(&opts, &addr);
|
||||
qos_node_create_driver("virtio-blk-pci", virtio_blk_pci_create);
|
||||
qos_node_consumes("virtio-blk-pci", "pci-bus", &opts);
|
||||
qos_node_produces("virtio-blk-pci", "virtio-blk");
|
||||
|
||||
g_free(arg);
|
||||
}
|
||||
|
||||
libqos_init(virtio_blk_register_nodes);
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/virtio-pci.h"
|
||||
|
||||
typedef struct QVirtioBlk QVirtioBlk;
|
||||
typedef struct QVirtioBlkPCI QVirtioBlkPCI;
|
||||
typedef struct QVirtioBlkDevice QVirtioBlkDevice;
|
||||
|
||||
/* virtqueue is created in each test */
|
||||
struct QVirtioBlk {
|
||||
QVirtioDevice *vdev;
|
||||
};
|
||||
|
||||
struct QVirtioBlkPCI {
|
||||
QVirtioPCIDevice pci_vdev;
|
||||
QVirtioBlk blk;
|
||||
};
|
||||
|
||||
struct QVirtioBlkDevice {
|
||||
QOSGraphObject obj;
|
||||
QVirtioBlk blk;
|
||||
};
|
|
@ -12,74 +12,74 @@
|
|||
#include "libqos/virtio.h"
|
||||
#include "libqos/virtio-mmio.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/malloc-generic.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "standard-headers/linux/virtio_ring.h"
|
||||
|
||||
static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t off)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
return readb(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
return qtest_readb(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
|
||||
}
|
||||
|
||||
static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t off)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
return readw(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
return qtest_readw(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
|
||||
}
|
||||
|
||||
static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t off)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
return readl(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
return qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
|
||||
}
|
||||
|
||||
static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t off)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
return readq(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
return qtest_readq(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
|
||||
}
|
||||
|
||||
static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
writel(dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0);
|
||||
return readl(dev->addr + QVIRTIO_MMIO_HOST_FEATURES);
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0);
|
||||
return qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES);
|
||||
}
|
||||
|
||||
static void qvirtio_mmio_set_features(QVirtioDevice *d, uint32_t features)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
dev->features = features;
|
||||
writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0);
|
||||
writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features);
|
||||
qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0);
|
||||
qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features);
|
||||
}
|
||||
|
||||
static uint32_t qvirtio_mmio_get_guest_features(QVirtioDevice *d)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
return dev->features;
|
||||
}
|
||||
|
||||
static uint8_t qvirtio_mmio_get_status(QVirtioDevice *d)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
return (uint8_t)readl(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS);
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
return (uint8_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_STATUS);
|
||||
}
|
||||
|
||||
static void qvirtio_mmio_set_status(QVirtioDevice *d, uint8_t status)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
writel(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status);
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status);
|
||||
}
|
||||
|
||||
static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
uint32_t isr;
|
||||
|
||||
isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1;
|
||||
isr = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1;
|
||||
if (isr != 0) {
|
||||
writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1);
|
||||
qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -88,12 +88,12 @@ static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
|
|||
|
||||
static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
uint32_t isr;
|
||||
|
||||
isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2;
|
||||
isr = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2;
|
||||
if (isr != 0) {
|
||||
writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2);
|
||||
qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -102,34 +102,34 @@ static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d)
|
|||
|
||||
static void qvirtio_mmio_queue_select(QVirtioDevice *d, uint16_t index)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
writel(dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index);
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index);
|
||||
|
||||
g_assert_cmphex(readl(dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0);
|
||||
g_assert_cmphex(qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0);
|
||||
}
|
||||
|
||||
static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice *d)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
return (uint16_t)readl(dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX);
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
return (uint16_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX);
|
||||
}
|
||||
|
||||
static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, uint32_t pfn)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
writel(dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn);
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn);
|
||||
}
|
||||
|
||||
static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d,
|
||||
QGuestAllocator *alloc, uint16_t index)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
QVirtQueue *vq;
|
||||
uint64_t addr;
|
||||
|
||||
vq = g_malloc0(sizeof(*vq));
|
||||
qvirtio_mmio_queue_select(d, index);
|
||||
writel(dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size);
|
||||
qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size);
|
||||
|
||||
vq->index = index;
|
||||
vq->size = qvirtio_mmio_get_queue_size(d);
|
||||
|
@ -139,7 +139,7 @@ static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d,
|
|||
vq->indirect = (dev->features & (1u << VIRTIO_RING_F_INDIRECT_DESC)) != 0;
|
||||
vq->event = (dev->features & (1u << VIRTIO_RING_F_EVENT_IDX)) != 0;
|
||||
|
||||
writel(dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size);
|
||||
qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size);
|
||||
|
||||
/* Check different than 0 */
|
||||
g_assert_cmpint(vq->size, !=, 0);
|
||||
|
@ -163,8 +163,8 @@ static void qvirtio_mmio_virtqueue_cleanup(QVirtQueue *vq,
|
|||
|
||||
static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d;
|
||||
writel(dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index);
|
||||
QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
|
||||
qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index);
|
||||
}
|
||||
|
||||
const QVirtioBus qvirtio_mmio = {
|
||||
|
@ -187,21 +187,45 @@ const QVirtioBus qvirtio_mmio = {
|
|||
.virtqueue_kick = qvirtio_mmio_virtqueue_kick,
|
||||
};
|
||||
|
||||
QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size)
|
||||
static void *qvirtio_mmio_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QVirtioMMIODevice *dev;
|
||||
uint32_t magic;
|
||||
dev = g_malloc0(sizeof(*dev));
|
||||
QVirtioMMIODevice *virtio_mmio = obj;
|
||||
if (!g_strcmp0(interface, "virtio-bus")) {
|
||||
return &virtio_mmio->vdev;
|
||||
}
|
||||
fprintf(stderr, "%s not present in virtio-mmio\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
magic = readl(addr + QVIRTIO_MMIO_MAGIC_VALUE);
|
||||
static void qvirtio_mmio_start_hw(QOSGraphObject *obj)
|
||||
{
|
||||
QVirtioMMIODevice *dev = (QVirtioMMIODevice *) obj;
|
||||
qvirtio_start_device(&dev->vdev);
|
||||
}
|
||||
|
||||
void qvirtio_mmio_init_device(QVirtioMMIODevice *dev, QTestState *qts,
|
||||
uint64_t addr, uint32_t page_size)
|
||||
{
|
||||
uint32_t magic;
|
||||
magic = qtest_readl(qts, addr + QVIRTIO_MMIO_MAGIC_VALUE);
|
||||
g_assert(magic == ('v' | 'i' << 8 | 'r' << 16 | 't' << 24));
|
||||
|
||||
dev->qts = qts;
|
||||
dev->addr = addr;
|
||||
dev->page_size = page_size;
|
||||
dev->vdev.device_type = readl(addr + QVIRTIO_MMIO_DEVICE_ID);
|
||||
dev->vdev.device_type = qtest_readl(qts, addr + QVIRTIO_MMIO_DEVICE_ID);
|
||||
dev->vdev.bus = &qvirtio_mmio;
|
||||
|
||||
writel(addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size);
|
||||
qtest_writel(qts, addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size);
|
||||
|
||||
return dev;
|
||||
dev->obj.get_driver = qvirtio_mmio_get_driver;
|
||||
dev->obj.start_hw = qvirtio_mmio_start_hw;
|
||||
}
|
||||
|
||||
static void virtio_mmio_register_nodes(void)
|
||||
{
|
||||
qos_node_create_driver("virtio-mmio", NULL);
|
||||
qos_node_produces("virtio-mmio", "virtio-bus");
|
||||
}
|
||||
|
||||
libqos_init(virtio_mmio_register_nodes);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#define LIBQOS_VIRTIO_MMIO_H
|
||||
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
#define QVIRTIO_MMIO_MAGIC_VALUE 0x000
|
||||
#define QVIRTIO_MMIO_VERSION 0x004
|
||||
|
@ -33,7 +34,9 @@
|
|||
#define QVIRTIO_MMIO_DEVICE_SPECIFIC 0x100
|
||||
|
||||
typedef struct QVirtioMMIODevice {
|
||||
QOSGraphObject obj;
|
||||
QVirtioDevice vdev;
|
||||
QTestState *qts;
|
||||
uint64_t addr;
|
||||
uint32_t page_size;
|
||||
uint32_t features; /* As it cannot be read later, save it */
|
||||
|
@ -41,6 +44,7 @@ typedef struct QVirtioMMIODevice {
|
|||
|
||||
extern const QVirtioBus qvirtio_mmio;
|
||||
|
||||
QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size);
|
||||
void qvirtio_mmio_init_device(QVirtioMMIODevice *dev, QTestState *qts,
|
||||
uint64_t addr, uint32_t page_size);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio-net.h"
|
||||
#include "hw/virtio/virtio-net.h"
|
||||
|
||||
|
||||
static QGuestAllocator *alloc;
|
||||
|
||||
static void virtio_net_cleanup(QVirtioNet *interface)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < interface->n_queues; i++) {
|
||||
qvirtqueue_cleanup(interface->vdev->bus, interface->queues[i], alloc);
|
||||
}
|
||||
g_free(interface->queues);
|
||||
}
|
||||
|
||||
static void virtio_net_setup(QVirtioNet *interface)
|
||||
{
|
||||
QVirtioDevice *vdev = interface->vdev;
|
||||
uint64_t features;
|
||||
int i;
|
||||
|
||||
features = qvirtio_get_features(vdev);
|
||||
features &= ~(QVIRTIO_F_BAD_FEATURE |
|
||||
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
|
||||
(1u << VIRTIO_RING_F_EVENT_IDX));
|
||||
qvirtio_set_features(vdev, features);
|
||||
|
||||
if (features & (1u << VIRTIO_NET_F_MQ)) {
|
||||
interface->n_queues = qvirtio_config_readw(vdev, 8) * 2;
|
||||
} else {
|
||||
interface->n_queues = 2;
|
||||
}
|
||||
|
||||
interface->queues = g_new(QVirtQueue *, interface->n_queues);
|
||||
for (i = 0; i < interface->n_queues; i++) {
|
||||
interface->queues[i] = qvirtqueue_setup(vdev, alloc, i);
|
||||
}
|
||||
qvirtio_set_driver_ok(vdev);
|
||||
}
|
||||
|
||||
/* virtio-net-device */
|
||||
static void qvirtio_net_device_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QVirtioNetDevice *v_net = (QVirtioNetDevice *) obj;
|
||||
virtio_net_cleanup(&v_net->net);
|
||||
}
|
||||
|
||||
static void qvirtio_net_device_start_hw(QOSGraphObject *obj)
|
||||
{
|
||||
QVirtioNetDevice *v_net = (QVirtioNetDevice *) obj;
|
||||
QVirtioNet *interface = &v_net->net;
|
||||
|
||||
virtio_net_setup(interface);
|
||||
}
|
||||
|
||||
static void *qvirtio_net_get_driver(QVirtioNet *v_net,
|
||||
const char *interface)
|
||||
{
|
||||
if (!g_strcmp0(interface, "virtio-net")) {
|
||||
return v_net;
|
||||
}
|
||||
if (!g_strcmp0(interface, "virtio")) {
|
||||
return v_net->vdev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in virtio-net-device\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *qvirtio_net_device_get_driver(void *object,
|
||||
const char *interface)
|
||||
{
|
||||
QVirtioNetDevice *v_net = object;
|
||||
return qvirtio_net_get_driver(&v_net->net, interface);
|
||||
}
|
||||
|
||||
static void *virtio_net_device_create(void *virtio_dev,
|
||||
QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtioNetDevice *virtio_ndevice = g_new0(QVirtioNetDevice, 1);
|
||||
QVirtioNet *interface = &virtio_ndevice->net;
|
||||
|
||||
interface->vdev = virtio_dev;
|
||||
alloc = t_alloc;
|
||||
|
||||
virtio_ndevice->obj.destructor = qvirtio_net_device_destructor;
|
||||
virtio_ndevice->obj.get_driver = qvirtio_net_device_get_driver;
|
||||
virtio_ndevice->obj.start_hw = qvirtio_net_device_start_hw;
|
||||
|
||||
return &virtio_ndevice->obj;
|
||||
}
|
||||
|
||||
/* virtio-net-pci */
|
||||
static void qvirtio_net_pci_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QVirtioNetPCI *v_net = (QVirtioNetPCI *) obj;
|
||||
QVirtioNet *interface = &v_net->net;
|
||||
QOSGraphObject *pci_vobj = &v_net->pci_vdev.obj;
|
||||
|
||||
virtio_net_cleanup(interface);
|
||||
qvirtio_pci_destructor(pci_vobj);
|
||||
}
|
||||
|
||||
static void qvirtio_net_pci_start_hw(QOSGraphObject *obj)
|
||||
{
|
||||
QVirtioNetPCI *v_net = (QVirtioNetPCI *) obj;
|
||||
QVirtioNet *interface = &v_net->net;
|
||||
QOSGraphObject *pci_vobj = &v_net->pci_vdev.obj;
|
||||
|
||||
qvirtio_pci_start_hw(pci_vobj);
|
||||
virtio_net_setup(interface);
|
||||
}
|
||||
|
||||
static void *qvirtio_net_pci_get_driver(void *object,
|
||||
const char *interface)
|
||||
{
|
||||
QVirtioNetPCI *v_net = object;
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return v_net->pci_vdev.pdev;
|
||||
}
|
||||
return qvirtio_net_get_driver(&v_net->net, interface);
|
||||
}
|
||||
|
||||
static void *virtio_net_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtioNetPCI *virtio_bpci = g_new0(QVirtioNetPCI, 1);
|
||||
QVirtioNet *interface = &virtio_bpci->net;
|
||||
QOSGraphObject *obj = &virtio_bpci->pci_vdev.obj;
|
||||
|
||||
virtio_pci_init(&virtio_bpci->pci_vdev, pci_bus, addr);
|
||||
interface->vdev = &virtio_bpci->pci_vdev.vdev;
|
||||
alloc = t_alloc;
|
||||
|
||||
g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_NET);
|
||||
|
||||
obj->destructor = qvirtio_net_pci_destructor;
|
||||
obj->start_hw = qvirtio_net_pci_start_hw;
|
||||
obj->get_driver = qvirtio_net_pci_get_driver;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void virtio_net_register_nodes(void)
|
||||
{
|
||||
/* FIXME: every test using these nodes needs to setup a
|
||||
* -netdev socket,id=hs0 otherwise QEMU is not going to start.
|
||||
* Therefore, we do not include "produces" edge for virtio
|
||||
* and pci-device yet.
|
||||
*/
|
||||
QPCIAddress addr = {
|
||||
.devfn = QPCI_DEVFN(4, 0),
|
||||
};
|
||||
|
||||
QOSGraphEdgeOptions opts = { };
|
||||
|
||||
/* virtio-net-device */
|
||||
opts.extra_device_opts = "netdev=hs0";
|
||||
qos_node_create_driver("virtio-net-device",
|
||||
virtio_net_device_create);
|
||||
qos_node_consumes("virtio-net-device", "virtio-bus", &opts);
|
||||
qos_node_produces("virtio-net-device", "virtio-net");
|
||||
|
||||
/* virtio-net-pci */
|
||||
opts.extra_device_opts = "netdev=hs0,addr=04.0";
|
||||
add_qpci_address(&opts, &addr);
|
||||
qos_node_create_driver("virtio-net-pci", virtio_net_pci_create);
|
||||
qos_node_consumes("virtio-net-pci", "pci-bus", &opts);
|
||||
qos_node_produces("virtio-net-pci", "virtio-net");
|
||||
}
|
||||
|
||||
libqos_init(virtio_net_register_nodes);
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/virtio-pci.h"
|
||||
|
||||
typedef struct QVirtioNet QVirtioNet;
|
||||
typedef struct QVirtioNetPCI QVirtioNetPCI;
|
||||
typedef struct QVirtioNetDevice QVirtioNetDevice;
|
||||
|
||||
struct QVirtioNet {
|
||||
QVirtioDevice *vdev;
|
||||
int n_queues;
|
||||
QVirtQueue **queues;
|
||||
};
|
||||
|
||||
struct QVirtioNetPCI {
|
||||
QVirtioPCIDevice pci_vdev;
|
||||
QVirtioNet net;
|
||||
};
|
||||
|
||||
struct QVirtioNetDevice {
|
||||
QOSGraphObject obj;
|
||||
QVirtioNet net;
|
||||
};
|
|
@ -15,68 +15,39 @@
|
|||
#include "libqos/pci-pc.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/malloc-pc.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "standard-headers/linux/virtio_ring.h"
|
||||
#include "standard-headers/linux/virtio_pci.h"
|
||||
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pci_regs.h"
|
||||
|
||||
typedef struct QVirtioPCIForeachData {
|
||||
void (*func)(QVirtioDevice *d, void *data);
|
||||
uint16_t device_type;
|
||||
bool has_slot;
|
||||
int slot;
|
||||
void *user_data;
|
||||
} QVirtioPCIForeachData;
|
||||
/* virtio-pci is a superclass of all virtio-xxx-pci devices;
|
||||
* the relation between virtio-pci and virtio-xxx-pci is implicit,
|
||||
* and therefore virtio-pci does not produce virtio and is not
|
||||
* reached by any edge, not even as a "contains" edge.
|
||||
* In facts, every device is a QVirtioPCIDevice with
|
||||
* additional fields, since every one has its own
|
||||
* number of queues and various attributes.
|
||||
* Virtio-pci provides default functions to start the
|
||||
* hw and destroy the object, and nodes that want to
|
||||
* override them should always remember to call the
|
||||
* original qvirtio_pci_destructor and qvirtio_pci_start_hw.
|
||||
*/
|
||||
|
||||
void qvirtio_pci_device_free(QVirtioPCIDevice *dev)
|
||||
static inline bool qvirtio_pci_is_big_endian(QVirtioPCIDevice *dev)
|
||||
{
|
||||
g_free(dev->pdev);
|
||||
g_free(dev);
|
||||
}
|
||||
QPCIBus *bus = dev->pdev->bus;
|
||||
|
||||
static QVirtioPCIDevice *qpcidevice_to_qvirtiodevice(QPCIDevice *pdev)
|
||||
{
|
||||
QVirtioPCIDevice *vpcidev;
|
||||
vpcidev = g_malloc0(sizeof(*vpcidev));
|
||||
|
||||
if (pdev) {
|
||||
vpcidev->pdev = pdev;
|
||||
vpcidev->vdev.device_type =
|
||||
qpci_config_readw(vpcidev->pdev, PCI_SUBSYSTEM_ID);
|
||||
}
|
||||
|
||||
vpcidev->config_msix_entry = -1;
|
||||
|
||||
return vpcidev;
|
||||
}
|
||||
|
||||
static void qvirtio_pci_foreach_callback(
|
||||
QPCIDevice *dev, int devfn, void *data)
|
||||
{
|
||||
QVirtioPCIForeachData *d = data;
|
||||
QVirtioPCIDevice *vpcidev = qpcidevice_to_qvirtiodevice(dev);
|
||||
|
||||
if (vpcidev->vdev.device_type == d->device_type &&
|
||||
(!d->has_slot || vpcidev->pdev->devfn == d->slot << 3)) {
|
||||
d->func(&vpcidev->vdev, d->user_data);
|
||||
} else {
|
||||
qvirtio_pci_device_free(vpcidev);
|
||||
}
|
||||
}
|
||||
|
||||
static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data)
|
||||
{
|
||||
QVirtioPCIDevice **vpcidev = data;
|
||||
assert(!*vpcidev);
|
||||
*vpcidev = (QVirtioPCIDevice *)d;
|
||||
/* FIXME: virtio 1.0 is always little-endian */
|
||||
return qtest_big_endian(bus->qts);
|
||||
}
|
||||
|
||||
#define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled))
|
||||
|
||||
static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
return qpci_io_readb(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
|
||||
}
|
||||
|
||||
|
@ -85,12 +56,12 @@ static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off)
|
|||
* so with a big-endian guest the order has been reversed,
|
||||
* reverse it again
|
||||
* virtio-1.0 is always little-endian, like PCI, but this
|
||||
* case will be managed inside qvirtio_is_big_endian()
|
||||
* case will be managed inside qvirtio_pci_is_big_endian()
|
||||
*/
|
||||
|
||||
static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
uint16_t value;
|
||||
|
||||
value = qpci_io_readw(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
|
||||
|
@ -102,7 +73,7 @@ static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off)
|
|||
|
||||
static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
uint32_t value;
|
||||
|
||||
value = qpci_io_readl(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
|
||||
|
@ -114,7 +85,7 @@ static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off)
|
|||
|
||||
static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
uint64_t val;
|
||||
|
||||
val = qpci_io_readq(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
|
||||
|
@ -127,37 +98,37 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off)
|
|||
|
||||
static uint32_t qvirtio_pci_get_features(QVirtioDevice *d)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES);
|
||||
}
|
||||
|
||||
static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES, features);
|
||||
}
|
||||
|
||||
static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES);
|
||||
}
|
||||
|
||||
static uint8_t qvirtio_pci_get_status(QVirtioDevice *d)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS);
|
||||
}
|
||||
|
||||
static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS, status);
|
||||
}
|
||||
|
||||
static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
QVirtQueuePCI *vqpci = (QVirtQueuePCI *)vq;
|
||||
uint32_t data;
|
||||
|
||||
|
@ -182,7 +153,7 @@ static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
|
|||
|
||||
static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
uint32_t data;
|
||||
|
||||
if (dev->pdev->msix_enabled) {
|
||||
|
@ -206,19 +177,19 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d)
|
|||
|
||||
static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_SEL, index);
|
||||
}
|
||||
|
||||
static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM);
|
||||
}
|
||||
|
||||
static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn);
|
||||
}
|
||||
|
||||
|
@ -270,7 +241,7 @@ static void qvirtio_pci_virtqueue_cleanup(QVirtQueue *vq,
|
|||
|
||||
static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d;
|
||||
QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
|
||||
qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index);
|
||||
}
|
||||
|
||||
|
@ -294,47 +265,6 @@ const QVirtioBus qvirtio_pci = {
|
|||
.virtqueue_kick = qvirtio_pci_virtqueue_kick,
|
||||
};
|
||||
|
||||
static void qvirtio_pci_foreach(QPCIBus *bus, uint16_t device_type,
|
||||
bool has_slot, int slot,
|
||||
void (*func)(QVirtioDevice *d, void *data), void *data)
|
||||
{
|
||||
QVirtioPCIForeachData d = { .func = func,
|
||||
.device_type = device_type,
|
||||
.has_slot = has_slot,
|
||||
.slot = slot,
|
||||
.user_data = data };
|
||||
|
||||
qpci_device_foreach(bus, PCI_VENDOR_ID_REDHAT_QUMRANET, -1,
|
||||
qvirtio_pci_foreach_callback, &d);
|
||||
}
|
||||
|
||||
QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type)
|
||||
{
|
||||
QVirtioPCIDevice *dev = NULL;
|
||||
|
||||
qvirtio_pci_foreach(bus, device_type, false, 0,
|
||||
qvirtio_pci_assign_device, &dev);
|
||||
|
||||
if (dev) {
|
||||
dev->vdev.bus = &qvirtio_pci;
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
QVirtioPCIDevice *qvirtio_pci_device_find_slot(QPCIBus *bus,
|
||||
uint16_t device_type, int slot)
|
||||
{
|
||||
QVirtioPCIDevice *dev = NULL;
|
||||
|
||||
qvirtio_pci_foreach(bus, device_type, true, slot,
|
||||
qvirtio_pci_assign_device, &dev);
|
||||
|
||||
dev->vdev.bus = &qvirtio_pci;
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
void qvirtio_pci_device_enable(QVirtioPCIDevice *d)
|
||||
{
|
||||
qpci_device_enable(d->pdev);
|
||||
|
@ -416,3 +346,54 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
|
|||
vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR);
|
||||
g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
|
||||
}
|
||||
|
||||
void qvirtio_pci_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj;
|
||||
qvirtio_pci_device_disable(dev);
|
||||
g_free(dev->pdev);
|
||||
}
|
||||
|
||||
void qvirtio_pci_start_hw(QOSGraphObject *obj)
|
||||
{
|
||||
QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj;
|
||||
qvirtio_pci_device_enable(dev);
|
||||
qvirtio_start_device(&dev->vdev);
|
||||
}
|
||||
|
||||
static void qvirtio_pci_init_from_pcidev(QVirtioPCIDevice *dev, QPCIDevice *pci_dev)
|
||||
{
|
||||
dev->pdev = pci_dev;
|
||||
dev->vdev.device_type = qpci_config_readw(pci_dev, PCI_SUBSYSTEM_ID);
|
||||
|
||||
dev->config_msix_entry = -1;
|
||||
|
||||
dev->vdev.bus = &qvirtio_pci;
|
||||
dev->vdev.big_endian = qvirtio_pci_is_big_endian(dev);
|
||||
|
||||
/* each virtio-xxx-pci device should override at least this function */
|
||||
dev->obj.get_driver = NULL;
|
||||
dev->obj.start_hw = qvirtio_pci_start_hw;
|
||||
dev->obj.destructor = qvirtio_pci_destructor;
|
||||
}
|
||||
|
||||
void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr)
|
||||
{
|
||||
QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn);
|
||||
g_assert_nonnull(pci_dev);
|
||||
qvirtio_pci_init_from_pcidev(dev, pci_dev);
|
||||
}
|
||||
|
||||
QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr)
|
||||
{
|
||||
QVirtioPCIDevice *dev;
|
||||
QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn);
|
||||
if (!pci_dev) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev = g_new0(QVirtioPCIDevice, 1);
|
||||
qvirtio_pci_init_from_pcidev(dev, pci_dev);
|
||||
dev->obj.free = g_free;
|
||||
return dev;
|
||||
}
|
||||
|
|
|
@ -12,8 +12,10 @@
|
|||
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/pci.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
typedef struct QVirtioPCIDevice {
|
||||
QOSGraphObject obj;
|
||||
QVirtioDevice vdev;
|
||||
QPCIDevice *pdev;
|
||||
QPCIBar bar;
|
||||
|
@ -31,10 +33,18 @@ typedef struct QVirtQueuePCI {
|
|||
|
||||
extern const QVirtioBus qvirtio_pci;
|
||||
|
||||
QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type);
|
||||
QVirtioPCIDevice *qvirtio_pci_device_find_slot(QPCIBus *bus,
|
||||
uint16_t device_type, int slot);
|
||||
void qvirtio_pci_device_free(QVirtioPCIDevice *dev);
|
||||
void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr);
|
||||
QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr);
|
||||
|
||||
/* virtio-pci object functions available for subclasses that
|
||||
* override the original start_hw and destroy
|
||||
* function. All virtio-xxx-pci subclass that override must
|
||||
* take care of calling these two functions in the respective
|
||||
* places
|
||||
*/
|
||||
void qvirtio_pci_destructor(QOSGraphObject *obj);
|
||||
void qvirtio_pci_start_hw(QOSGraphObject *obj);
|
||||
|
||||
|
||||
void qvirtio_pci_device_enable(QVirtioPCIDevice *d);
|
||||
void qvirtio_pci_device_disable(QVirtioPCIDevice *d);
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio-rng.h"
|
||||
|
||||
/* virtio-rng-device */
|
||||
static void *qvirtio_rng_get_driver(QVirtioRng *v_rng,
|
||||
const char *interface)
|
||||
{
|
||||
if (!g_strcmp0(interface, "virtio-rng")) {
|
||||
return v_rng;
|
||||
}
|
||||
if (!g_strcmp0(interface, "virtio")) {
|
||||
return v_rng->vdev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in virtio-rng-device\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *qvirtio_rng_device_get_driver(void *object,
|
||||
const char *interface)
|
||||
{
|
||||
QVirtioRngDevice *v_rng = object;
|
||||
return qvirtio_rng_get_driver(&v_rng->rng, interface);
|
||||
}
|
||||
|
||||
static void *virtio_rng_device_create(void *virtio_dev,
|
||||
QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtioRngDevice *virtio_rdevice = g_new0(QVirtioRngDevice, 1);
|
||||
QVirtioRng *interface = &virtio_rdevice->rng;
|
||||
|
||||
interface->vdev = virtio_dev;
|
||||
|
||||
virtio_rdevice->obj.get_driver = qvirtio_rng_device_get_driver;
|
||||
|
||||
return &virtio_rdevice->obj;
|
||||
}
|
||||
|
||||
/* virtio-rng-pci */
|
||||
static void *qvirtio_rng_pci_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QVirtioRngPCI *v_rng = object;
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return v_rng->pci_vdev.pdev;
|
||||
}
|
||||
return qvirtio_rng_get_driver(&v_rng->rng, interface);
|
||||
}
|
||||
|
||||
static void *virtio_rng_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtioRngPCI *virtio_rpci = g_new0(QVirtioRngPCI, 1);
|
||||
QVirtioRng *interface = &virtio_rpci->rng;
|
||||
QOSGraphObject *obj = &virtio_rpci->pci_vdev.obj;
|
||||
|
||||
virtio_pci_init(&virtio_rpci->pci_vdev, pci_bus, addr);
|
||||
interface->vdev = &virtio_rpci->pci_vdev.vdev;
|
||||
|
||||
obj->get_driver = qvirtio_rng_pci_get_driver;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void virtio_rng_register_nodes(void)
|
||||
{
|
||||
QPCIAddress addr = {
|
||||
.devfn = QPCI_DEVFN(4, 0),
|
||||
};
|
||||
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0",
|
||||
};
|
||||
|
||||
/* virtio-rng-device */
|
||||
qos_node_create_driver("virtio-rng-device", virtio_rng_device_create);
|
||||
qos_node_consumes("virtio-rng-device", "virtio-bus", NULL);
|
||||
qos_node_produces("virtio-rng-device", "virtio");
|
||||
qos_node_produces("virtio-rng-device", "virtio-rng");
|
||||
|
||||
/* virtio-rng-pci */
|
||||
add_qpci_address(&opts, &addr);
|
||||
qos_node_create_driver("virtio-rng-pci", virtio_rng_pci_create);
|
||||
qos_node_consumes("virtio-rng-pci", "pci-bus", &opts);
|
||||
qos_node_produces("virtio-rng-pci", "pci-device");
|
||||
qos_node_produces("virtio-rng-pci", "virtio");
|
||||
qos_node_produces("virtio-rng-pci", "virtio-rng");
|
||||
}
|
||||
|
||||
libqos_init(virtio_rng_register_nodes);
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/virtio-pci.h"
|
||||
|
||||
typedef struct QVirtioRng QVirtioRng;
|
||||
typedef struct QVirtioRngPCI QVirtioRngPCI;
|
||||
typedef struct QVirtioRngDevice QVirtioRngDevice;
|
||||
|
||||
struct QVirtioRng {
|
||||
QVirtioDevice *vdev;
|
||||
};
|
||||
|
||||
struct QVirtioRngPCI {
|
||||
QVirtioPCIDevice pci_vdev;
|
||||
QVirtioRng rng;
|
||||
};
|
||||
|
||||
struct QVirtioRngDevice {
|
||||
QOSGraphObject obj;
|
||||
QVirtioRng rng;
|
||||
};
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "standard-headers/linux/virtio_ids.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio-scsi.h"
|
||||
|
||||
/* virtio-scsi-device */
|
||||
static void *qvirtio_scsi_get_driver(QVirtioSCSI *v_scsi,
|
||||
const char *interface)
|
||||
{
|
||||
if (!g_strcmp0(interface, "virtio-scsi")) {
|
||||
return v_scsi;
|
||||
}
|
||||
if (!g_strcmp0(interface, "virtio")) {
|
||||
return v_scsi->vdev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in virtio-scsi-device\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *qvirtio_scsi_device_get_driver(void *object,
|
||||
const char *interface)
|
||||
{
|
||||
QVirtioSCSIDevice *v_scsi = object;
|
||||
return qvirtio_scsi_get_driver(&v_scsi->scsi, interface);
|
||||
}
|
||||
|
||||
static void *virtio_scsi_device_create(void *virtio_dev,
|
||||
QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtioSCSIDevice *virtio_bdevice = g_new0(QVirtioSCSIDevice, 1);
|
||||
QVirtioSCSI *interface = &virtio_bdevice->scsi;
|
||||
|
||||
interface->vdev = virtio_dev;
|
||||
|
||||
virtio_bdevice->obj.get_driver = qvirtio_scsi_device_get_driver;
|
||||
|
||||
return &virtio_bdevice->obj;
|
||||
}
|
||||
|
||||
/* virtio-scsi-pci */
|
||||
static void *qvirtio_scsi_pci_get_driver(void *object,
|
||||
const char *interface)
|
||||
{
|
||||
QVirtioSCSIPCI *v_scsi = object;
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return v_scsi->pci_vdev.pdev;
|
||||
}
|
||||
return qvirtio_scsi_get_driver(&v_scsi->scsi, interface);
|
||||
}
|
||||
|
||||
static void *virtio_scsi_pci_create(void *pci_bus,
|
||||
QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtioSCSIPCI *virtio_spci = g_new0(QVirtioSCSIPCI, 1);
|
||||
QVirtioSCSI *interface = &virtio_spci->scsi;
|
||||
QOSGraphObject *obj = &virtio_spci->pci_vdev.obj;
|
||||
|
||||
virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr);
|
||||
interface->vdev = &virtio_spci->pci_vdev.vdev;
|
||||
|
||||
g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_SCSI);
|
||||
|
||||
obj->get_driver = qvirtio_scsi_pci_get_driver;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void virtio_scsi_register_nodes(void)
|
||||
{
|
||||
QPCIAddress addr = {
|
||||
.devfn = QPCI_DEVFN(4, 0),
|
||||
};
|
||||
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.before_cmd_line = "-drive id=drv0,if=none,file=null-co://,format=raw",
|
||||
.after_cmd_line = "-device scsi-hd,bus=vs0.0,drive=drv0",
|
||||
};
|
||||
|
||||
/* virtio-scsi-device */
|
||||
opts.extra_device_opts = "id=vs0";
|
||||
qos_node_create_driver("virtio-scsi-device",
|
||||
virtio_scsi_device_create);
|
||||
qos_node_consumes("virtio-scsi-device", "virtio-bus", &opts);
|
||||
qos_node_produces("virtio-scsi-device", "virtio-scsi");
|
||||
|
||||
/* virtio-scsi-pci */
|
||||
opts.extra_device_opts = "id=vs0,addr=04.0";
|
||||
add_qpci_address(&opts, &addr);
|
||||
qos_node_create_driver("virtio-scsi-pci", virtio_scsi_pci_create);
|
||||
qos_node_consumes("virtio-scsi-pci", "pci-bus", &opts);
|
||||
qos_node_produces("virtio-scsi-pci", "pci-device");
|
||||
qos_node_produces("virtio-scsi-pci", "virtio-scsi");
|
||||
}
|
||||
|
||||
libqos_init(virtio_scsi_register_nodes);
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/virtio-pci.h"
|
||||
|
||||
typedef struct QVirtioSCSI QVirtioSCSI;
|
||||
typedef struct QVirtioSCSIPCI QVirtioSCSIPCI;
|
||||
typedef struct QVirtioSCSIDevice QVirtioSCSIDevice;
|
||||
|
||||
struct QVirtioSCSI {
|
||||
QVirtioDevice *vdev;
|
||||
};
|
||||
|
||||
struct QVirtioSCSIPCI {
|
||||
QVirtioPCIDevice pci_vdev;
|
||||
QVirtioSCSI scsi;
|
||||
};
|
||||
|
||||
struct QVirtioSCSIDevice {
|
||||
QOSGraphObject obj;
|
||||
QVirtioSCSI scsi;
|
||||
};
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio-serial.h"
|
||||
|
||||
static void *qvirtio_serial_get_driver(QVirtioSerial *v_serial,
|
||||
const char *interface)
|
||||
{
|
||||
if (!g_strcmp0(interface, "virtio-serial")) {
|
||||
return v_serial;
|
||||
}
|
||||
if (!g_strcmp0(interface, "virtio")) {
|
||||
return v_serial->vdev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in virtio-serial-device\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *qvirtio_serial_device_get_driver(void *object,
|
||||
const char *interface)
|
||||
{
|
||||
QVirtioSerialDevice *v_serial = object;
|
||||
return qvirtio_serial_get_driver(&v_serial->serial, interface);
|
||||
}
|
||||
|
||||
static void *virtio_serial_device_create(void *virtio_dev,
|
||||
QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtioSerialDevice *virtio_device = g_new0(QVirtioSerialDevice, 1);
|
||||
QVirtioSerial *interface = &virtio_device->serial;
|
||||
|
||||
interface->vdev = virtio_dev;
|
||||
|
||||
virtio_device->obj.get_driver = qvirtio_serial_device_get_driver;
|
||||
|
||||
return &virtio_device->obj;
|
||||
}
|
||||
|
||||
/* virtio-serial-pci */
|
||||
static void *qvirtio_serial_pci_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QVirtioSerialPCI *v_serial = object;
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return v_serial->pci_vdev.pdev;
|
||||
}
|
||||
return qvirtio_serial_get_driver(&v_serial->serial, interface);
|
||||
}
|
||||
|
||||
static void *virtio_serial_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
|
||||
void *addr)
|
||||
{
|
||||
QVirtioSerialPCI *virtio_spci = g_new0(QVirtioSerialPCI, 1);
|
||||
QVirtioSerial *interface = &virtio_spci->serial;
|
||||
QOSGraphObject *obj = &virtio_spci->pci_vdev.obj;
|
||||
|
||||
virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr);
|
||||
interface->vdev = &virtio_spci->pci_vdev.vdev;
|
||||
|
||||
obj->get_driver = qvirtio_serial_pci_get_driver;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void virtio_serial_register_nodes(void)
|
||||
{
|
||||
QPCIAddress addr = {
|
||||
.devfn = QPCI_DEVFN(4, 0),
|
||||
};
|
||||
|
||||
QOSGraphEdgeOptions edge_opts = { };
|
||||
|
||||
/* virtio-serial-device */
|
||||
edge_opts.extra_device_opts = "id=vser0";
|
||||
qos_node_create_driver("virtio-serial-device",
|
||||
virtio_serial_device_create);
|
||||
qos_node_consumes("virtio-serial-device", "virtio-bus", &edge_opts);
|
||||
qos_node_produces("virtio-serial-device", "virtio");
|
||||
qos_node_produces("virtio-serial-device", "virtio-serial");
|
||||
|
||||
/* virtio-serial-pci */
|
||||
edge_opts.extra_device_opts = "id=vser0,addr=04.0";
|
||||
add_qpci_address(&edge_opts, &addr);
|
||||
qos_node_create_driver("virtio-serial-pci", virtio_serial_pci_create);
|
||||
qos_node_consumes("virtio-serial-pci", "pci-bus", &edge_opts);
|
||||
qos_node_produces("virtio-serial-pci", "pci-device");
|
||||
qos_node_produces("virtio-serial-pci", "virtio");
|
||||
qos_node_produces("virtio-serial-pci", "virtio-serial");
|
||||
}
|
||||
|
||||
libqos_init(virtio_serial_register_nodes);
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/virtio-pci.h"
|
||||
|
||||
typedef struct QVirtioSerial QVirtioSerial;
|
||||
typedef struct QVirtioSerialPCI QVirtioSerialPCI;
|
||||
typedef struct QVirtioSerialDevice QVirtioSerialDevice;
|
||||
|
||||
struct QVirtioSerial {
|
||||
QVirtioDevice *vdev;
|
||||
};
|
||||
|
||||
struct QVirtioSerialPCI {
|
||||
QVirtioPCIDevice pci_vdev;
|
||||
QVirtioSerial serial;
|
||||
};
|
||||
|
||||
struct QVirtioSerialDevice {
|
||||
QOSGraphObject obj;
|
||||
QVirtioSerial serial;
|
||||
};
|
|
@ -40,6 +40,7 @@ uint32_t qvirtio_get_features(QVirtioDevice *d)
|
|||
|
||||
void qvirtio_set_features(QVirtioDevice *d, uint32_t features)
|
||||
{
|
||||
d->features = features;
|
||||
d->bus->set_features(d, features);
|
||||
}
|
||||
|
||||
|
@ -349,19 +350,14 @@ void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx)
|
|||
writew(vq->avail + 4 + (2 * vq->size), idx);
|
||||
}
|
||||
|
||||
/*
|
||||
* qvirtio_get_dev_type:
|
||||
* Returns: the preferred virtio bus/device type for the current architecture.
|
||||
*/
|
||||
const char *qvirtio_get_dev_type(void)
|
||||
void qvirtio_start_device(QVirtioDevice *vdev)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
|
||||
if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) {
|
||||
return "device"; /* for virtio-mmio */
|
||||
} else if (g_str_equal(arch, "s390x")) {
|
||||
return "ccw";
|
||||
} else {
|
||||
return "pci";
|
||||
}
|
||||
qvirtio_reset(vdev);
|
||||
qvirtio_set_acknowledge(vdev);
|
||||
qvirtio_set_driver(vdev);
|
||||
}
|
||||
|
||||
bool qvirtio_is_big_endian(QVirtioDevice *d)
|
||||
{
|
||||
return d->big_endian;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ typedef struct QVirtioDevice {
|
|||
const QVirtioBus *bus;
|
||||
/* Device type */
|
||||
uint16_t device_type;
|
||||
uint64_t features;
|
||||
bool big_endian;
|
||||
} QVirtioDevice;
|
||||
|
||||
typedef struct QVirtQueue {
|
||||
|
@ -90,12 +92,6 @@ struct QVirtioBus {
|
|||
void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq);
|
||||
};
|
||||
|
||||
static inline bool qvirtio_is_big_endian(QVirtioDevice *d)
|
||||
{
|
||||
/* FIXME: virtio 1.0 is always little-endian */
|
||||
return qtest_big_endian(global_qtest);
|
||||
}
|
||||
|
||||
static inline uint32_t qvring_size(uint32_t num, uint32_t align)
|
||||
{
|
||||
return ((sizeof(struct vring_desc) * num + sizeof(uint16_t) * (3 + num)
|
||||
|
@ -109,6 +105,7 @@ uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr);
|
|||
uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr);
|
||||
uint32_t qvirtio_get_features(QVirtioDevice *d);
|
||||
void qvirtio_set_features(QVirtioDevice *d, uint32_t features);
|
||||
bool qvirtio_is_big_endian(QVirtioDevice *d);
|
||||
|
||||
void qvirtio_reset(QVirtioDevice *d);
|
||||
void qvirtio_set_acknowledge(QVirtioDevice *d);
|
||||
|
@ -145,6 +142,6 @@ bool qvirtqueue_get_buf(QVirtQueue *vq, uint32_t *desc_idx, uint32_t *len);
|
|||
|
||||
void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx);
|
||||
|
||||
const char *qvirtio_get_dev_type(void);
|
||||
void qvirtio_start_device(QVirtioDevice *vdev);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "pci-pc.h"
|
||||
#include "malloc-pc.h"
|
||||
|
||||
typedef struct QX86PCMachine QX86PCMachine;
|
||||
typedef struct i440FX_pcihost i440FX_pcihost;
|
||||
typedef struct QSDHCI_PCI QSDHCI_PCI;
|
||||
|
||||
struct i440FX_pcihost {
|
||||
QOSGraphObject obj;
|
||||
QPCIBusPC pci;
|
||||
};
|
||||
|
||||
struct QX86PCMachine {
|
||||
QOSGraphObject obj;
|
||||
QGuestAllocator alloc;
|
||||
i440FX_pcihost bridge;
|
||||
};
|
||||
|
||||
/* i440FX_pcihost */
|
||||
|
||||
static QOSGraphObject *i440FX_host_get_device(void *obj, const char *device)
|
||||
{
|
||||
i440FX_pcihost *host = obj;
|
||||
if (!g_strcmp0(device, "pci-bus-pc")) {
|
||||
return &host->pci.obj;
|
||||
}
|
||||
fprintf(stderr, "%s not present in i440FX-pcihost\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void qos_create_i440FX_host(i440FX_pcihost *host,
|
||||
QTestState *qts,
|
||||
QGuestAllocator *alloc)
|
||||
{
|
||||
host->obj.get_device = i440FX_host_get_device;
|
||||
qpci_init_pc(&host->pci, qts, alloc);
|
||||
}
|
||||
|
||||
/* x86_64/pc machine */
|
||||
|
||||
static void pc_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
QX86PCMachine *machine = (QX86PCMachine *) obj;
|
||||
alloc_destroy(&machine->alloc);
|
||||
}
|
||||
|
||||
static void *pc_get_driver(void *object, const char *interface)
|
||||
{
|
||||
QX86PCMachine *machine = object;
|
||||
if (!g_strcmp0(interface, "memory")) {
|
||||
return &machine->alloc;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in x86_64/pc\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static QOSGraphObject *pc_get_device(void *obj, const char *device)
|
||||
{
|
||||
QX86PCMachine *machine = obj;
|
||||
if (!g_strcmp0(device, "i440FX-pcihost")) {
|
||||
return &machine->bridge.obj;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in x86_64/pc\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *qos_create_machine_pc(QTestState *qts)
|
||||
{
|
||||
QX86PCMachine *machine = g_new0(QX86PCMachine, 1);
|
||||
machine->obj.get_device = pc_get_device;
|
||||
machine->obj.get_driver = pc_get_driver;
|
||||
machine->obj.destructor = pc_destructor;
|
||||
pc_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS);
|
||||
qos_create_i440FX_host(&machine->bridge, qts, &machine->alloc);
|
||||
|
||||
return &machine->obj;
|
||||
}
|
||||
|
||||
static void pc_machine_register_nodes(void)
|
||||
{
|
||||
qos_node_create_machine("i386/pc", qos_create_machine_pc);
|
||||
qos_node_contains("i386/pc", "i440FX-pcihost", NULL);
|
||||
|
||||
qos_node_create_machine("x86_64/pc", qos_create_machine_pc);
|
||||
qos_node_contains("x86_64/pc", "i440FX-pcihost", NULL);
|
||||
|
||||
qos_node_create_driver("i440FX-pcihost", NULL);
|
||||
qos_node_contains("i440FX-pcihost", "pci-bus-pc", NULL);
|
||||
}
|
||||
|
||||
libqos_init(pc_machine_register_nodes);
|
|
@ -17,6 +17,9 @@
|
|||
#ifndef LIBQTEST_H
|
||||
#define LIBQTEST_H
|
||||
|
||||
#include "qapi/qmp/qobject.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
|
||||
typedef struct QTestState QTestState;
|
||||
|
||||
extern QTestState *global_qtest;
|
||||
|
@ -598,6 +601,9 @@ static inline QTestState *qtest_start(const char *args)
|
|||
*/
|
||||
static inline void qtest_end(void)
|
||||
{
|
||||
if (!global_qtest) {
|
||||
return;
|
||||
}
|
||||
qtest_quit(global_qtest);
|
||||
global_qtest = NULL;
|
||||
}
|
||||
|
|
|
@ -10,55 +10,49 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "libqos/libqos-pc.h"
|
||||
#include "libqos/libqos-spapr.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
static QOSState *qmegasas_start(const char *extra_opts)
|
||||
typedef struct QMegasas QMegasas;
|
||||
|
||||
struct QMegasas {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice dev;
|
||||
};
|
||||
|
||||
static void *megasas_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QOSState *qs;
|
||||
const char *arch = qtest_get_arch();
|
||||
const char *cmd = "-drive id=hd0,if=none,file=null-co://,format=raw "
|
||||
"-device megasas,id=scsi0,addr=04.0 "
|
||||
"-device scsi-hd,bus=scsi0.0,drive=hd0 %s";
|
||||
QMegasas *megasas = obj;
|
||||
|
||||
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
|
||||
qs = qtest_pc_boot(cmd, extra_opts ? : "");
|
||||
global_qtest = qs->qts;
|
||||
return qs;
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return &megasas->dev;
|
||||
}
|
||||
|
||||
g_printerr("virtio-scsi tests are only available on x86 or ppc64\n");
|
||||
exit(EXIT_FAILURE);
|
||||
fprintf(stderr, "%s not present in megasas\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void qmegasas_stop(QOSState *qs)
|
||||
static void *megasas_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
|
||||
{
|
||||
qtest_shutdown(qs);
|
||||
}
|
||||
QMegasas *megasas = g_new0(QMegasas, 1);
|
||||
QPCIBus *bus = pci_bus;
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void pci_nop(void)
|
||||
{
|
||||
QOSState *qs;
|
||||
qpci_device_init(&megasas->dev, bus, addr);
|
||||
megasas->obj.get_driver = megasas_get_driver;
|
||||
|
||||
qs = qmegasas_start(NULL);
|
||||
qmegasas_stop(qs);
|
||||
return &megasas->obj;
|
||||
}
|
||||
|
||||
/* This used to cause a NULL pointer dereference. */
|
||||
static void megasas_pd_get_info_fuzz(void)
|
||||
static void megasas_pd_get_info_fuzz(void *obj, void *data, QGuestAllocator *alloc)
|
||||
{
|
||||
QPCIDevice *dev;
|
||||
QOSState *qs;
|
||||
QMegasas *megasas = obj;
|
||||
QPCIDevice *dev = &megasas->dev;
|
||||
QPCIBar bar;
|
||||
uint32_t context[256];
|
||||
uint64_t context_pa;
|
||||
int i;
|
||||
|
||||
qs = qmegasas_start(NULL);
|
||||
dev = qpci_device_find(qs->pcibus, QPCI_DEVFN(4,0));
|
||||
g_assert(dev != NULL);
|
||||
|
||||
qpci_device_enable(dev);
|
||||
bar = qpci_iomap(dev, 0, NULL);
|
||||
|
||||
|
@ -71,19 +65,25 @@ static void megasas_pd_get_info_fuzz(void)
|
|||
context[6] = cpu_to_le32(0x02020000);
|
||||
context[7] = cpu_to_le32(0);
|
||||
|
||||
context_pa = qmalloc(qs, sizeof(context));
|
||||
context_pa = guest_alloc(alloc, sizeof(context));
|
||||
memwrite(context_pa, context, sizeof(context));
|
||||
qpci_io_writel(dev, bar, 0x40, context_pa);
|
||||
|
||||
g_free(dev);
|
||||
qmegasas_stop(qs);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void megasas_register_nodes(void)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/megasas/pci/nop", pci_nop);
|
||||
qtest_add_func("/megasas/dcmd/pd-get-info/fuzz", megasas_pd_get_info_fuzz);
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0,id=scsi0",
|
||||
.before_cmd_line = "-drive id=drv0,if=none,file=null-co://,format=raw",
|
||||
.after_cmd_line = "-device scsi-hd,bus=scsi0.0,drive=drv0",
|
||||
};
|
||||
|
||||
return g_test_run();
|
||||
add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
|
||||
|
||||
qos_node_create_driver("megasas", megasas_create);
|
||||
qos_node_consumes("megasas", "pci-bus", &opts);
|
||||
qos_node_produces("megasas", "pci-device");
|
||||
|
||||
qos_add_test("dcmd/pd-get-info/fuzz", "megasas", megasas_pd_get_info_fuzz, NULL);
|
||||
}
|
||||
libqos_init(megasas_register_nodes);
|
||||
|
|
|
@ -9,23 +9,49 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void pci_nop(void)
|
||||
typedef struct QNe2k_pci QNe2k_pci;
|
||||
|
||||
struct QNe2k_pci {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice dev;
|
||||
};
|
||||
|
||||
static void *ne2k_pci_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QNe2k_pci *ne2k_pci = obj;
|
||||
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return &ne2k_pci->dev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in ne2k_pci\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void *ne2k_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
|
||||
{
|
||||
int ret;
|
||||
QNe2k_pci *ne2k_pci = g_new0(QNe2k_pci, 1);
|
||||
QPCIBus *bus = pci_bus;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/ne2000/pci/nop", pci_nop);
|
||||
qpci_device_init(&ne2k_pci->dev, bus, addr);
|
||||
ne2k_pci->obj.get_driver = ne2k_pci_get_driver;
|
||||
|
||||
qtest_start("-device ne2k_pci");
|
||||
ret = g_test_run();
|
||||
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
return &ne2k_pci->obj;
|
||||
}
|
||||
|
||||
static void ne2000_register_nodes(void)
|
||||
{
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0",
|
||||
};
|
||||
add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
|
||||
|
||||
qos_node_create_driver("ne2k_pci", ne2k_pci_create);
|
||||
qos_node_consumes("ne2k_pci", "pci-bus", &opts);
|
||||
qos_node_produces("ne2k_pci", "pci-device");
|
||||
}
|
||||
|
||||
libqos_init(ne2000_register_nodes);
|
||||
|
|
|
@ -10,49 +10,47 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/libqos-pc.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
static QOSState *qnvme_start(const char *extra_opts)
|
||||
typedef struct QNvme QNvme;
|
||||
|
||||
struct QNvme {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice dev;
|
||||
};
|
||||
|
||||
static void *nvme_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QOSState *qs;
|
||||
const char *arch = qtest_get_arch();
|
||||
const char *cmd = "-drive id=drv0,if=none,file=null-co://,format=raw "
|
||||
"-device nvme,addr=0x4.0,serial=foo,drive=drv0 %s";
|
||||
QNvme *nvme = obj;
|
||||
|
||||
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
|
||||
qs = qtest_pc_boot(cmd, extra_opts ? : "");
|
||||
global_qtest = qs->qts;
|
||||
return qs;
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return &nvme->dev;
|
||||
}
|
||||
|
||||
g_printerr("nvme tests are only available on x86\n");
|
||||
exit(EXIT_FAILURE);
|
||||
fprintf(stderr, "%s not present in nvme\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void qnvme_stop(QOSState *qs)
|
||||
static void *nvme_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
|
||||
{
|
||||
qtest_shutdown(qs);
|
||||
QNvme *nvme = g_new0(QNvme, 1);
|
||||
QPCIBus *bus = pci_bus;
|
||||
|
||||
qpci_device_init(&nvme->dev, bus, addr);
|
||||
nvme->obj.get_driver = nvme_get_driver;
|
||||
|
||||
return &nvme->obj;
|
||||
}
|
||||
|
||||
static void nop(void)
|
||||
{
|
||||
QOSState *qs;
|
||||
|
||||
qs = qnvme_start(NULL);
|
||||
qnvme_stop(qs);
|
||||
}
|
||||
|
||||
static void nvmetest_cmb_test(void)
|
||||
/* This used to cause a NULL pointer dereference. */
|
||||
static void nvmetest_oob_cmb_test(void *obj, void *data, QGuestAllocator *alloc)
|
||||
{
|
||||
const int cmb_bar_size = 2 * MiB;
|
||||
QOSState *qs;
|
||||
QPCIDevice *pdev;
|
||||
QNvme *nvme = obj;
|
||||
QPCIDevice *pdev = &nvme->dev;
|
||||
QPCIBar bar;
|
||||
|
||||
qs = qnvme_start("-global nvme.cmb_size_mb=2");
|
||||
pdev = qpci_device_find(qs->pcibus, QPCI_DEVFN(4,0));
|
||||
g_assert(pdev != NULL);
|
||||
|
||||
qpci_device_enable(pdev);
|
||||
bar = qpci_iomap(pdev, 2, NULL);
|
||||
|
||||
|
@ -65,16 +63,24 @@ static void nvmetest_cmb_test(void)
|
|||
g_assert_cmpint(qpci_io_readb(pdev, bar, cmb_bar_size - 1), ==, 0x11);
|
||||
g_assert_cmpint(qpci_io_readw(pdev, bar, cmb_bar_size - 1), !=, 0x2211);
|
||||
g_assert_cmpint(qpci_io_readl(pdev, bar, cmb_bar_size - 1), !=, 0x44332211);
|
||||
g_free(pdev);
|
||||
|
||||
qnvme_stop(qs);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void nvme_register_nodes(void)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/nvme/nop", nop);
|
||||
qtest_add_func("/nvme/cmb_test", nvmetest_cmb_test);
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0,drive=drv0,serial=foo",
|
||||
.before_cmd_line = "-drive id=drv0,if=none,file=null-co://,format=raw",
|
||||
};
|
||||
|
||||
return g_test_run();
|
||||
add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
|
||||
|
||||
qos_node_create_driver("nvme", nvme_create);
|
||||
qos_node_consumes("nvme", "pci-bus", &opts);
|
||||
qos_node_produces("nvme", "pci-device");
|
||||
|
||||
qos_add_test("oob-cmb-access", "nvme", nvmetest_oob_cmb_test, &(QOSGraphTestOptions) {
|
||||
.edge.extra_device_opts = "cmb_size_mb=2"
|
||||
});
|
||||
}
|
||||
|
||||
libqos_init(nvme_register_nodes);
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* QTest testcase for PCI
|
||||
*
|
||||
* Copyright (c) 2018 Red Hat, Inc.
|
||||
*
|
||||
* 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 "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void nop(void *obj, void *data, QGuestAllocator *alloc)
|
||||
{
|
||||
}
|
||||
|
||||
static void register_pci_test(void)
|
||||
{
|
||||
qos_add_test("nop", "pci-device", nop, NULL);
|
||||
}
|
||||
|
||||
libqos_init(register_pci_test);
|
|
@ -9,23 +9,49 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void pci_nop(void)
|
||||
typedef struct QPCNet QPCNet;
|
||||
|
||||
struct QPCNet {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice dev;
|
||||
};
|
||||
|
||||
static void *pcnet_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QPCNet *pcnet = obj;
|
||||
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return &pcnet->dev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in pcnet\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void *pcnet_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
|
||||
{
|
||||
int ret;
|
||||
QPCNet *pcnet = g_new0(QPCNet, 1);
|
||||
QPCIBus *bus = pci_bus;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/pcnet/pci/nop", pci_nop);
|
||||
qpci_device_init(&pcnet->dev, bus, addr);
|
||||
pcnet->obj.get_driver = pcnet_get_driver;
|
||||
|
||||
qtest_start("-device pcnet");
|
||||
ret = g_test_run();
|
||||
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
return &pcnet->obj;
|
||||
}
|
||||
|
||||
static void pcnet_register_nodes(void)
|
||||
{
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0",
|
||||
};
|
||||
add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
|
||||
|
||||
qos_node_create_driver("pcnet", pcnet_create);
|
||||
qos_node_consumes("pcnet", "pci-bus", &opts);
|
||||
qos_node_produces("pcnet", "pci-device");
|
||||
}
|
||||
|
||||
libqos_init(pcnet_register_nodes);
|
||||
|
|
|
@ -87,7 +87,7 @@ static void test_smram_lock(void)
|
|||
|
||||
qtest_start("-M q35");
|
||||
|
||||
pcibus = qpci_init_pc(global_qtest, NULL);
|
||||
pcibus = qpci_new_pc(global_qtest, NULL);
|
||||
g_assert(pcibus != NULL);
|
||||
|
||||
pcidev = qpci_device_find(pcibus, 0);
|
||||
|
@ -146,7 +146,7 @@ static void test_tseg_size(const void *data)
|
|||
g_free(cmdline);
|
||||
|
||||
/* locate the DRAM controller */
|
||||
pcibus = qpci_init_pc(global_qtest, NULL);
|
||||
pcibus = qpci_new_pc(global_qtest, NULL);
|
||||
g_assert(pcibus != NULL);
|
||||
pcidev = qpci_device_find(pcibus, 0);
|
||||
g_assert(pcidev != NULL);
|
||||
|
|
|
@ -0,0 +1,445 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qmp/qlist.h"
|
||||
#include "libqos/malloc.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/qgraph_internal.h"
|
||||
|
||||
static char *old_path;
|
||||
|
||||
static void apply_to_node(const char *name, bool is_machine, bool is_abstract)
|
||||
{
|
||||
char *machine_name = NULL;
|
||||
if (is_machine) {
|
||||
const char *arch = qtest_get_arch();
|
||||
machine_name = g_strconcat(arch, "/", name, NULL);
|
||||
name = machine_name;
|
||||
}
|
||||
qos_graph_node_set_availability(name, true);
|
||||
if (is_abstract) {
|
||||
qos_delete_cmd_line(name);
|
||||
}
|
||||
g_free(machine_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* apply_to_qlist(): using QMP queries QEMU for a list of
|
||||
* machines and devices available, and sets the respective node
|
||||
* as true. If a node is found, also all its produced and contained
|
||||
* child are marked available.
|
||||
*
|
||||
* See qos_graph_node_set_availability() for more info
|
||||
*/
|
||||
static void apply_to_qlist(QList *list, bool is_machine)
|
||||
{
|
||||
const QListEntry *p;
|
||||
const char *name;
|
||||
bool abstract;
|
||||
QDict *minfo;
|
||||
QObject *qobj;
|
||||
QString *qstr;
|
||||
QBool *qbool;
|
||||
|
||||
for (p = qlist_first(list); p; p = qlist_next(p)) {
|
||||
minfo = qobject_to(QDict, qlist_entry_obj(p));
|
||||
qobj = qdict_get(minfo, "name");
|
||||
qstr = qobject_to(QString, qobj);
|
||||
name = qstring_get_str(qstr);
|
||||
|
||||
qobj = qdict_get(minfo, "abstract");
|
||||
if (qobj) {
|
||||
qbool = qobject_to(QBool, qobj);
|
||||
abstract = qbool_get_bool(qbool);
|
||||
} else {
|
||||
abstract = false;
|
||||
}
|
||||
|
||||
apply_to_node(name, is_machine, abstract);
|
||||
qobj = qdict_get(minfo, "alias");
|
||||
if (qobj) {
|
||||
qstr = qobject_to(QString, qobj);
|
||||
name = qstring_get_str(qstr);
|
||||
apply_to_node(name, is_machine, abstract);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* qos_set_machines_devices_available(): sets availability of qgraph
|
||||
* machines and devices.
|
||||
*
|
||||
* This function firstly starts QEMU with "-machine none" option,
|
||||
* and then executes the QMP protocol asking for the list of devices
|
||||
* and machines available.
|
||||
*
|
||||
* for each of these items, it looks up the corresponding qgraph node,
|
||||
* setting it as available. The list currently returns all devices that
|
||||
* are either machines or QEDGE_CONSUMED_BY other nodes.
|
||||
* Therefore, in order to mark all other nodes, it recursively sets
|
||||
* all its QEDGE_CONTAINS and QEDGE_PRODUCES child as available too.
|
||||
*/
|
||||
static void qos_set_machines_devices_available(void)
|
||||
{
|
||||
QDict *response;
|
||||
QDict *args = qdict_new();
|
||||
QList *list;
|
||||
|
||||
qtest_start("-machine none");
|
||||
response = qmp("{ 'execute': 'query-machines' }");
|
||||
list = qdict_get_qlist(response, "return");
|
||||
|
||||
apply_to_qlist(list, true);
|
||||
|
||||
qobject_unref(response);
|
||||
|
||||
qdict_put_bool(args, "abstract", true);
|
||||
qdict_put_str(args, "implements", "device");
|
||||
|
||||
response = qmp("{'execute': 'qom-list-types',"
|
||||
" 'arguments': %p }", args);
|
||||
g_assert(qdict_haskey(response, "return"));
|
||||
list = qdict_get_qlist(response, "return");
|
||||
|
||||
apply_to_qlist(list, false);
|
||||
|
||||
qtest_end();
|
||||
qobject_unref(response);
|
||||
}
|
||||
|
||||
static QGuestAllocator *get_machine_allocator(QOSGraphObject *obj)
|
||||
{
|
||||
return obj->get_driver(obj, "memory");
|
||||
}
|
||||
|
||||
static void restart_qemu_or_continue(char *path)
|
||||
{
|
||||
/* compares the current command line with the
|
||||
* one previously executed: if they are the same,
|
||||
* don't restart QEMU, if they differ, stop previous
|
||||
* QEMU subprocess (if active) and start over with
|
||||
* the new command line
|
||||
*/
|
||||
if (g_strcmp0(old_path, path)) {
|
||||
qtest_end();
|
||||
qos_invalidate_command_line();
|
||||
old_path = g_strdup(path);
|
||||
qtest_start(path);
|
||||
} else { /* if cmd line is the same, reset the guest */
|
||||
qobject_unref(qmp("{ 'execute': 'system_reset' }"));
|
||||
qmp_eventwait("RESET");
|
||||
}
|
||||
}
|
||||
|
||||
void qos_invalidate_command_line(void)
|
||||
{
|
||||
g_free(old_path);
|
||||
old_path = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* allocate_objects(): given an array of nodes @arg,
|
||||
* walks the path invoking all constructors and
|
||||
* passing the corresponding parameter in order to
|
||||
* continue the objects allocation.
|
||||
* Once the test is reached, return the object it consumes.
|
||||
*
|
||||
* Since the machine and QEDGE_CONSUMED_BY nodes allocate
|
||||
* memory in the constructor, g_test_queue_destroy is used so
|
||||
* that after execution they can be safely free'd. (The test's
|
||||
* ->before callback is also welcome to use g_test_queue_destroy).
|
||||
*
|
||||
* Note: as specified in walk_path() too, @arg is an array of
|
||||
* char *, where arg[0] is a pointer to the command line
|
||||
* string that will be used to properly start QEMU when executing
|
||||
* the test, and the remaining elements represent the actual objects
|
||||
* that will be allocated.
|
||||
*/
|
||||
static void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc)
|
||||
{
|
||||
int current = 0;
|
||||
QGuestAllocator *alloc;
|
||||
QOSGraphObject *parent = NULL;
|
||||
QOSGraphEdge *edge;
|
||||
QOSGraphNode *node;
|
||||
void *edge_arg;
|
||||
void *obj;
|
||||
|
||||
node = qos_graph_get_node(path[current]);
|
||||
g_assert(node->type == QNODE_MACHINE);
|
||||
|
||||
obj = qos_machine_new(node, qts);
|
||||
qos_object_queue_destroy(obj);
|
||||
|
||||
alloc = get_machine_allocator(obj);
|
||||
if (p_alloc) {
|
||||
*p_alloc = alloc;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (node->type != QNODE_INTERFACE) {
|
||||
qos_object_start_hw(obj);
|
||||
parent = obj;
|
||||
}
|
||||
|
||||
/* follow edge and get object for next node constructor */
|
||||
current++;
|
||||
edge = qos_graph_get_edge(path[current - 1], path[current]);
|
||||
node = qos_graph_get_node(path[current]);
|
||||
|
||||
if (node->type == QNODE_TEST) {
|
||||
g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY);
|
||||
return obj;
|
||||
}
|
||||
|
||||
switch (qos_graph_edge_get_type(edge)) {
|
||||
case QEDGE_PRODUCES:
|
||||
obj = parent->get_driver(parent, path[current]);
|
||||
break;
|
||||
|
||||
case QEDGE_CONSUMED_BY:
|
||||
edge_arg = qos_graph_edge_get_arg(edge);
|
||||
obj = qos_driver_new(node, obj, alloc, edge_arg);
|
||||
qos_object_queue_destroy(obj);
|
||||
break;
|
||||
|
||||
case QEDGE_CONTAINS:
|
||||
obj = parent->get_device(parent, path[current]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The argument to run_one_test, which is the test function that is registered
|
||||
* with GTest, is a vector of strings. The first item is the initial command
|
||||
* line (before it is modified by the test's "before" function), the remaining
|
||||
* items are node names forming the path to the test node.
|
||||
*/
|
||||
static char **current_path;
|
||||
|
||||
const char *qos_get_current_command_line(void)
|
||||
{
|
||||
return current_path[0];
|
||||
}
|
||||
|
||||
void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc)
|
||||
{
|
||||
return allocate_objects(qts, current_path + 1, p_alloc);
|
||||
}
|
||||
|
||||
/**
|
||||
* run_one_test(): given an array of nodes @arg,
|
||||
* walks the path invoking all constructors and
|
||||
* passing the corresponding parameter in order to
|
||||
* continue the objects allocation.
|
||||
* Once the test is reached, its function is executed.
|
||||
*
|
||||
* Since the machine and QEDGE_CONSUMED_BY nodes allocate
|
||||
* memory in the constructor, g_test_queue_destroy is used so
|
||||
* that after execution they can be safely free'd. The test's
|
||||
* ->before callback is also welcome to use g_test_queue_destroy.
|
||||
*
|
||||
* Note: as specified in walk_path() too, @arg is an array of
|
||||
* char *, where arg[0] is a pointer to the command line
|
||||
* string that will be used to properly start QEMU when executing
|
||||
* the test, and the remaining elements represent the actual objects
|
||||
* that will be allocated.
|
||||
*
|
||||
* The order of execution is the following:
|
||||
* 1) @before test function as defined in the given QOSGraphTestOptions
|
||||
* 2) start QEMU
|
||||
* 3) call all nodes constructor and get_driver/get_device depending on edge,
|
||||
* start the hardware (*_device_enable functions)
|
||||
* 4) start test
|
||||
*/
|
||||
static void run_one_test(const void *arg)
|
||||
{
|
||||
QOSGraphNode *test_node;
|
||||
QGuestAllocator *alloc = NULL;
|
||||
void *obj;
|
||||
char **path = (char **) arg;
|
||||
GString *cmd_line = g_string_new(path[0]);
|
||||
void *test_arg;
|
||||
|
||||
/* Before test */
|
||||
current_path = path;
|
||||
test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]);
|
||||
test_arg = test_node->u.test.arg;
|
||||
if (test_node->u.test.before) {
|
||||
test_arg = test_node->u.test.before(cmd_line, test_arg);
|
||||
}
|
||||
|
||||
restart_qemu_or_continue(cmd_line->str);
|
||||
g_string_free(cmd_line, true);
|
||||
|
||||
obj = qos_allocate_objects(global_qtest, &alloc);
|
||||
test_node->u.test.function(obj, test_arg, alloc);
|
||||
}
|
||||
|
||||
static void subprocess_run_one_test(const void *arg)
|
||||
{
|
||||
const gchar *path = arg;
|
||||
g_test_trap_subprocess(path, 0, 0);
|
||||
g_test_trap_assert_passed();
|
||||
}
|
||||
|
||||
/*
|
||||
* in this function, 2 path will be built:
|
||||
* path_str, a one-string path (ex "pc/i440FX-pcihost/...")
|
||||
* path_vec, a string-array path (ex [0] = "pc", [1] = "i440FX-pcihost").
|
||||
*
|
||||
* path_str will be only used to build the test name, and won't need the
|
||||
* architecture name at beginning, since it will be added by qtest_add_func().
|
||||
*
|
||||
* path_vec is used to allocate all constructors of the path nodes.
|
||||
* Each name in this array except position 0 must correspond to a valid
|
||||
* QOSGraphNode name.
|
||||
* Position 0 is special, initially contains just the <machine> name of
|
||||
* the node, (ex for "x86_64/pc" it will be "pc"), used to build the test
|
||||
* path (see below). After it will contain the command line used to start
|
||||
* qemu with all required devices.
|
||||
*
|
||||
* Note that the machine node name must be with format <arch>/<machine>
|
||||
* (ex "x86_64/pc"), because it will identify the node "x86_64/pc"
|
||||
* and start QEMU with "-M pc". For this reason,
|
||||
* when building path_str, path_vec
|
||||
* initially contains the <machine> at position 0 ("pc"),
|
||||
* and the node name at position 1 (<arch>/<machine>)
|
||||
* ("x86_64/pc"), followed by the rest of the nodes.
|
||||
*/
|
||||
static void walk_path(QOSGraphNode *orig_path, int len)
|
||||
{
|
||||
QOSGraphNode *path;
|
||||
QOSGraphEdge *edge;
|
||||
|
||||
/* etype set to QEDGE_CONSUMED_BY so that machine can add to the command line */
|
||||
QOSEdgeType etype = QEDGE_CONSUMED_BY;
|
||||
|
||||
/* twice QOS_PATH_MAX_ELEMENT_SIZE since each edge can have its arg */
|
||||
char **path_vec = g_new0(char *, (QOS_PATH_MAX_ELEMENT_SIZE * 2));
|
||||
int path_vec_size = 0;
|
||||
|
||||
char *after_cmd = NULL, *before_cmd = NULL, *after_device = NULL;
|
||||
char *node_name = orig_path->name, *path_str;
|
||||
|
||||
GString *cmd_line = g_string_new("");
|
||||
GString *cmd_line2 = g_string_new("");
|
||||
|
||||
path = qos_graph_get_node(node_name); /* root */
|
||||
node_name = qos_graph_edge_get_dest(path->path_edge); /* machine name */
|
||||
|
||||
path_vec[path_vec_size++] = node_name;
|
||||
path_vec[path_vec_size++] = qos_get_machine_type(node_name);
|
||||
|
||||
for (;;) {
|
||||
path = qos_graph_get_node(node_name);
|
||||
if (!path->path_edge) {
|
||||
break;
|
||||
}
|
||||
|
||||
node_name = qos_graph_edge_get_dest(path->path_edge);
|
||||
|
||||
/* append node command line + previous edge command line */
|
||||
if (path->command_line && etype == QEDGE_CONSUMED_BY) {
|
||||
g_string_append(cmd_line, path->command_line);
|
||||
if (after_device) {
|
||||
g_string_append(cmd_line, after_device);
|
||||
}
|
||||
}
|
||||
|
||||
path_vec[path_vec_size++] = qos_graph_edge_get_name(path->path_edge);
|
||||
/* detect if edge has command line args */
|
||||
after_cmd = qos_graph_edge_get_after_cmd_line(path->path_edge);
|
||||
after_device = qos_graph_edge_get_extra_device_opts(path->path_edge);
|
||||
before_cmd = qos_graph_edge_get_before_cmd_line(path->path_edge);
|
||||
edge = qos_graph_get_edge(path->name, node_name);
|
||||
etype = qos_graph_edge_get_type(edge);
|
||||
|
||||
if (before_cmd) {
|
||||
g_string_append(cmd_line, before_cmd);
|
||||
}
|
||||
if (after_cmd) {
|
||||
g_string_append(cmd_line2, after_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
path_vec[path_vec_size++] = NULL;
|
||||
if (after_device) {
|
||||
g_string_append(cmd_line, after_device);
|
||||
}
|
||||
g_string_append(cmd_line, cmd_line2->str);
|
||||
g_string_free(cmd_line2, true);
|
||||
|
||||
/* here position 0 has <arch>/<machine>, position 1 has <machine>.
|
||||
* The path must not have the <arch>, qtest_add_data_func adds it.
|
||||
*/
|
||||
path_str = g_strjoinv("/", path_vec + 1);
|
||||
|
||||
/* put arch/machine in position 1 so run_one_test can do its work
|
||||
* and add the command line at position 0.
|
||||
*/
|
||||
path_vec[1] = path_vec[0];
|
||||
path_vec[0] = g_string_free(cmd_line, false);
|
||||
|
||||
if (path->u.test.subprocess) {
|
||||
gchar *subprocess_path = g_strdup_printf("/%s/%s/subprocess",
|
||||
qtest_get_arch(), path_str);
|
||||
qtest_add_data_func(path_str, subprocess_path, subprocess_run_one_test);
|
||||
g_test_add_data_func(subprocess_path, path_vec, run_one_test);
|
||||
} else {
|
||||
qtest_add_data_func(path_str, path_vec, run_one_test);
|
||||
}
|
||||
|
||||
g_free(path_str);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* main(): heart of the qgraph framework.
|
||||
*
|
||||
* - Initializes the glib test framework
|
||||
* - Creates the graph by invoking the various _init constructors
|
||||
* - Starts QEMU to mark the available devices
|
||||
* - Walks the graph, and each path is added to
|
||||
* the glib test framework (walk_path)
|
||||
* - Runs the tests, calling allocate_object() and allocating the
|
||||
* machine/drivers/test objects
|
||||
* - Cleans up everything
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qos_graph_init();
|
||||
module_call_init(MODULE_INIT_QOM);
|
||||
module_call_init(MODULE_INIT_LIBQOS);
|
||||
qos_set_machines_devices_available();
|
||||
|
||||
qos_graph_foreach_test_path(walk_path);
|
||||
g_test_run();
|
||||
qtest_end();
|
||||
qos_graph_destroy();
|
||||
g_free(old_path);
|
||||
return 0;
|
||||
}
|
|
@ -17,7 +17,7 @@ static void test_rtas_get_time_of_day(void)
|
|||
global_qtest = qs->qts;
|
||||
|
||||
t1 = time(NULL);
|
||||
ret = qrtas_get_time_of_day(qs->qts, qs->alloc, &tm, &ns);
|
||||
ret = qrtas_get_time_of_day(qs->qts, &qs->alloc, &tm, &ns);
|
||||
g_assert_cmpint(ret, ==, 0);
|
||||
t2 = mktimegm(&tm);
|
||||
g_assert(t2 - t1 < 5); /* 5 sec max to run the test */
|
||||
|
|
|
@ -35,7 +35,7 @@ static QPCIDevice *get_device(void)
|
|||
{
|
||||
QPCIDevice *dev;
|
||||
|
||||
pcibus = qpci_init_pc(global_qtest, NULL);
|
||||
pcibus = qpci_new_pc(global_qtest, NULL);
|
||||
qpci_device_foreach(pcibus, 0x10ec, 0x8139, save_fn, &dev);
|
||||
g_assert(dev != NULL);
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include "libqtest.h"
|
||||
#include "libqos/pci-pc.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/sdhci.h"
|
||||
|
||||
#define SDHC_CAPAB 0x40
|
||||
FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8); /* since v2 */
|
||||
|
@ -20,99 +22,11 @@ FIELD(SDHC_CAPAB, SDR, 32, 3); /* since v3 */
|
|||
FIELD(SDHC_CAPAB, DRIVER, 36, 3); /* since v3 */
|
||||
#define SDHC_HCVER 0xFE
|
||||
|
||||
static const struct sdhci_t {
|
||||
const char *arch, *machine;
|
||||
struct {
|
||||
uintptr_t addr;
|
||||
uint8_t version;
|
||||
uint8_t baseclock;
|
||||
struct {
|
||||
bool sdma;
|
||||
uint64_t reg;
|
||||
} capab;
|
||||
} sdhci;
|
||||
struct {
|
||||
uint16_t vendor_id, device_id;
|
||||
} pci;
|
||||
} models[] = {
|
||||
/* PC via PCI */
|
||||
{ "x86_64", "pc",
|
||||
{-1, 2, 0, {1, 0x057834b4} },
|
||||
.pci = { PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_SDHCI } },
|
||||
|
||||
/* Exynos4210 */
|
||||
{ "arm", "smdkc210",
|
||||
{0x12510000, 2, 0, {1, 0x5e80080} } },
|
||||
|
||||
/* i.MX 6 */
|
||||
{ "arm", "sabrelite",
|
||||
{0x02190000, 3, 0, {1, 0x057834b4} } },
|
||||
|
||||
/* BCM2835 */
|
||||
{ "arm", "raspi2",
|
||||
{0x3f300000, 3, 52, {0, 0x052134b4} } },
|
||||
|
||||
/* Zynq-7000 */
|
||||
{ "arm", "xilinx-zynq-a9", /* Datasheet: UG585 (v1.12.1) */
|
||||
{0xe0100000, 2, 0, {1, 0x69ec0080} } },
|
||||
|
||||
/* ZynqMP */
|
||||
{ "aarch64", "xlnx-zcu102", /* Datasheet: UG1085 (v1.7) */
|
||||
{0xff160000, 3, 0, {1, 0x280737ec6481} } },
|
||||
|
||||
};
|
||||
|
||||
typedef struct QSDHCI {
|
||||
struct {
|
||||
QPCIBus *bus;
|
||||
QPCIDevice *dev;
|
||||
} pci;
|
||||
union {
|
||||
QPCIBar mem_bar;
|
||||
uint64_t addr;
|
||||
};
|
||||
} QSDHCI;
|
||||
|
||||
static uint16_t sdhci_readw(QSDHCI *s, uint32_t reg)
|
||||
{
|
||||
uint16_t val;
|
||||
|
||||
if (s->pci.dev) {
|
||||
val = qpci_io_readw(s->pci.dev, s->mem_bar, reg);
|
||||
} else {
|
||||
val = qtest_readw(global_qtest, s->addr + reg);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint64_t sdhci_readq(QSDHCI *s, uint32_t reg)
|
||||
{
|
||||
uint64_t val;
|
||||
|
||||
if (s->pci.dev) {
|
||||
val = qpci_io_readq(s->pci.dev, s->mem_bar, reg);
|
||||
} else {
|
||||
val = qtest_readq(global_qtest, s->addr + reg);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void sdhci_writeq(QSDHCI *s, uint32_t reg, uint64_t val)
|
||||
{
|
||||
if (s->pci.dev) {
|
||||
qpci_io_writeq(s->pci.dev, s->mem_bar, reg, val);
|
||||
} else {
|
||||
qtest_writeq(global_qtest, s->addr + reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_specs_version(QSDHCI *s, uint8_t version)
|
||||
{
|
||||
uint32_t v;
|
||||
|
||||
v = sdhci_readw(s, SDHC_HCVER);
|
||||
v = s->readw(s, SDHC_HCVER);
|
||||
v &= 0xff;
|
||||
v += 1;
|
||||
g_assert_cmpuint(v, ==, version);
|
||||
|
@ -122,7 +36,7 @@ static void check_capab_capareg(QSDHCI *s, uint64_t expec_capab)
|
|||
{
|
||||
uint64_t capab;
|
||||
|
||||
capab = sdhci_readq(s, SDHC_CAPAB);
|
||||
capab = s->readq(s, SDHC_CAPAB);
|
||||
g_assert_cmphex(capab, ==, expec_capab);
|
||||
}
|
||||
|
||||
|
@ -131,11 +45,11 @@ static void check_capab_readonly(QSDHCI *s)
|
|||
const uint64_t vrand = 0x123456789abcdef;
|
||||
uint64_t capab0, capab1;
|
||||
|
||||
capab0 = sdhci_readq(s, SDHC_CAPAB);
|
||||
capab0 = s->readq(s, SDHC_CAPAB);
|
||||
g_assert_cmpuint(capab0, !=, vrand);
|
||||
|
||||
sdhci_writeq(s, SDHC_CAPAB, vrand);
|
||||
capab1 = sdhci_readq(s, SDHC_CAPAB);
|
||||
s->writeq(s, SDHC_CAPAB, vrand);
|
||||
capab1 = s->readq(s, SDHC_CAPAB);
|
||||
g_assert_cmpuint(capab1, !=, vrand);
|
||||
g_assert_cmpuint(capab1, ==, capab0);
|
||||
}
|
||||
|
@ -147,7 +61,7 @@ static void check_capab_baseclock(QSDHCI *s, uint8_t expec_freq)
|
|||
if (!expec_freq) {
|
||||
return;
|
||||
}
|
||||
capab = sdhci_readq(s, SDHC_CAPAB);
|
||||
capab = s->readq(s, SDHC_CAPAB);
|
||||
capab_freq = FIELD_EX64(capab, SDHC_CAPAB, BASECLKFREQ);
|
||||
g_assert_cmpuint(capab_freq, ==, expec_freq);
|
||||
}
|
||||
|
@ -156,7 +70,7 @@ static void check_capab_sdma(QSDHCI *s, bool supported)
|
|||
{
|
||||
uint64_t capab, capab_sdma;
|
||||
|
||||
capab = sdhci_readq(s, SDHC_CAPAB);
|
||||
capab = s->readq(s, SDHC_CAPAB);
|
||||
capab_sdma = FIELD_EX64(capab, SDHC_CAPAB, SDMA);
|
||||
g_assert_cmpuint(capab_sdma, ==, supported);
|
||||
}
|
||||
|
@ -167,7 +81,7 @@ static void check_capab_v3(QSDHCI *s, uint8_t version)
|
|||
|
||||
if (version < 3) {
|
||||
/* before v3 those fields are RESERVED */
|
||||
capab = sdhci_readq(s, SDHC_CAPAB);
|
||||
capab = s->readq(s, SDHC_CAPAB);
|
||||
capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, SDR);
|
||||
g_assert_cmpuint(capab_v3, ==, 0);
|
||||
capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, DRIVER);
|
||||
|
@ -175,78 +89,21 @@ static void check_capab_v3(QSDHCI *s, uint8_t version)
|
|||
}
|
||||
}
|
||||
|
||||
static QSDHCI *machine_start(const struct sdhci_t *test)
|
||||
static void test_registers(void *obj, void *data, QGuestAllocator *alloc)
|
||||
{
|
||||
QSDHCI *s = g_new0(QSDHCI, 1);
|
||||
QSDHCI *s = obj;
|
||||
|
||||
if (test->pci.vendor_id) {
|
||||
/* PCI */
|
||||
uint16_t vendor_id, device_id;
|
||||
uint64_t barsize;
|
||||
|
||||
global_qtest = qtest_initf("-machine %s -device sdhci-pci",
|
||||
test->machine);
|
||||
|
||||
s->pci.bus = qpci_init_pc(global_qtest, NULL);
|
||||
|
||||
/* Find PCI device and verify it's the right one */
|
||||
s->pci.dev = qpci_device_find(s->pci.bus, QPCI_DEVFN(4, 0));
|
||||
g_assert_nonnull(s->pci.dev);
|
||||
vendor_id = qpci_config_readw(s->pci.dev, PCI_VENDOR_ID);
|
||||
device_id = qpci_config_readw(s->pci.dev, PCI_DEVICE_ID);
|
||||
g_assert(vendor_id == test->pci.vendor_id);
|
||||
g_assert(device_id == test->pci.device_id);
|
||||
s->mem_bar = qpci_iomap(s->pci.dev, 0, &barsize);
|
||||
qpci_device_enable(s->pci.dev);
|
||||
} else {
|
||||
/* SysBus */
|
||||
global_qtest = qtest_initf("-machine %s", test->machine);
|
||||
s->addr = test->sdhci.addr;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void machine_stop(QSDHCI *s)
|
||||
{
|
||||
qpci_free_pc(s->pci.bus);
|
||||
g_free(s->pci.dev);
|
||||
qtest_quit(global_qtest);
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
static void test_machine(const void *data)
|
||||
{
|
||||
const struct sdhci_t *test = data;
|
||||
QSDHCI *s;
|
||||
|
||||
s = machine_start(test);
|
||||
|
||||
check_specs_version(s, test->sdhci.version);
|
||||
check_capab_capareg(s, test->sdhci.capab.reg);
|
||||
check_specs_version(s, s->props.version);
|
||||
check_capab_capareg(s, s->props.capab.reg);
|
||||
check_capab_readonly(s);
|
||||
check_capab_v3(s, test->sdhci.version);
|
||||
check_capab_sdma(s, test->sdhci.capab.sdma);
|
||||
check_capab_baseclock(s, test->sdhci.baseclock);
|
||||
|
||||
machine_stop(s);
|
||||
check_capab_v3(s, s->props.version);
|
||||
check_capab_sdma(s, s->props.capab.sdma);
|
||||
check_capab_baseclock(s, s->props.baseclock);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
static void register_sdhci_test(void)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
char *name;
|
||||
int i;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
for (i = 0; i < ARRAY_SIZE(models); i++) {
|
||||
if (strcmp(arch, models[i].arch)) {
|
||||
continue;
|
||||
}
|
||||
name = g_strdup_printf("sdhci/%s", models[i].machine);
|
||||
qtest_add_data_func(name, &models[i], test_machine);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
return g_test_run();
|
||||
qos_add_test("registers", "sdhci", test_registers, NULL);
|
||||
}
|
||||
|
||||
libqos_init(register_sdhci_test);
|
||||
|
|
|
@ -7,29 +7,25 @@
|
|||
* 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 "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
#define TYPE_SPAPR_PCI_HOST_BRIDGE "spapr-pci-host-bridge"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void test_phb_device(void)
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests,
|
||||
* for example by producing pci-bus.
|
||||
*/
|
||||
static void test_phb_device(void *obj, void *data, QGuestAllocator *alloc)
|
||||
{
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void register_phb_test(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/spapr-phb/device", test_phb_device);
|
||||
|
||||
qtest_start("-device " TYPE_SPAPR_PCI_HOST_BRIDGE ",index=30");
|
||||
|
||||
ret = g_test_run();
|
||||
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
qos_add_test("spapr-phb-test", "ppc64/pseries",
|
||||
test_phb_device, &(QOSGraphTestOptions) {
|
||||
.edge.before_cmd_line = "-device spapr-pci-host-bridge"
|
||||
",index=30",
|
||||
});
|
||||
}
|
||||
|
||||
libqos_init(register_phb_test);
|
||||
|
|
|
@ -64,7 +64,7 @@ static void test_init(TestData *d)
|
|||
global_qtest = qs;
|
||||
qtest_irq_intercept_in(qs, "ioapic");
|
||||
|
||||
d->bus = qpci_init_pc(qs, NULL);
|
||||
d->bus = qpci_new_pc(qs, NULL);
|
||||
d->dev = qpci_device_find(d->bus, QPCI_DEVFN(0x1f, 0x00));
|
||||
g_assert(d->dev != NULL);
|
||||
|
||||
|
|
|
@ -1003,6 +1003,103 @@ static void char_socket_client_test(gconstpointer opaque)
|
|||
g_free(optstr);
|
||||
}
|
||||
|
||||
static void
|
||||
count_closed_event(void *opaque, int event)
|
||||
{
|
||||
int *count = opaque;
|
||||
if (event == CHR_EVENT_CLOSED) {
|
||||
(*count)++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
char_socket_discard_read(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
}
|
||||
|
||||
static void char_socket_server_two_clients_test(gconstpointer opaque)
|
||||
{
|
||||
SocketAddress *incoming_addr = (gpointer) opaque;
|
||||
Chardev *chr;
|
||||
CharBackend be = {0};
|
||||
QObject *qaddr;
|
||||
SocketAddress *addr;
|
||||
Visitor *v;
|
||||
char *optstr;
|
||||
QemuOpts *opts;
|
||||
QIOChannelSocket *ioc1, *ioc2;
|
||||
int closed = 0;
|
||||
|
||||
g_setenv("QTEST_SILENT_ERRORS", "1", 1);
|
||||
/*
|
||||
* We rely on addr containing "nowait", otherwise
|
||||
* qemu_chr_new() will block until a client connects. We
|
||||
* can't spawn our client thread though, because until
|
||||
* qemu_chr_new() returns we don't know what TCP port was
|
||||
* allocated by the OS
|
||||
*/
|
||||
optstr = char_socket_addr_to_opt_str(incoming_addr,
|
||||
false,
|
||||
NULL,
|
||||
true);
|
||||
opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"),
|
||||
optstr, true);
|
||||
g_assert_nonnull(opts);
|
||||
chr = qemu_chr_new_from_opts(opts, NULL, &error_abort);
|
||||
qemu_opts_del(opts);
|
||||
g_assert_nonnull(chr);
|
||||
g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort));
|
||||
|
||||
qaddr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort);
|
||||
g_assert_nonnull(qaddr);
|
||||
|
||||
v = qobject_input_visitor_new(qaddr);
|
||||
visit_type_SocketAddress(v, "addr", &addr, &error_abort);
|
||||
visit_free(v);
|
||||
qobject_unref(qaddr);
|
||||
|
||||
qemu_chr_fe_init(&be, chr, &error_abort);
|
||||
|
||||
qemu_chr_fe_set_handlers(&be, char_socket_can_read, char_socket_discard_read,
|
||||
count_closed_event, NULL,
|
||||
&closed, NULL, true);
|
||||
|
||||
ioc1 = qio_channel_socket_new();
|
||||
qio_channel_socket_connect_sync(ioc1, addr, &error_abort);
|
||||
qemu_chr_wait_connected(chr, &error_abort);
|
||||
|
||||
/* switch the chardev to another context */
|
||||
GMainContext *ctx = g_main_context_new();
|
||||
qemu_chr_fe_set_handlers(&be, char_socket_can_read, char_socket_discard_read,
|
||||
count_closed_event, NULL,
|
||||
&closed, ctx, true);
|
||||
|
||||
/* Start a second connection while the first is still connected.
|
||||
* It will be placed in the listen() backlog, and connect() will
|
||||
* succeed immediately.
|
||||
*/
|
||||
ioc2 = qio_channel_socket_new();
|
||||
qio_channel_socket_connect_sync(ioc2, addr, &error_abort);
|
||||
|
||||
object_unref(OBJECT(ioc1));
|
||||
/* The two connections should now be processed serially. */
|
||||
while (g_main_context_iteration(ctx, TRUE)) {
|
||||
if (closed == 1 && ioc2) {
|
||||
object_unref(OBJECT(ioc2));
|
||||
ioc2 = NULL;
|
||||
}
|
||||
if (closed == 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
qapi_free_SocketAddress(addr);
|
||||
object_unparent(OBJECT(chr));
|
||||
g_main_context_unref(ctx);
|
||||
g_free(optstr);
|
||||
g_unsetenv("QTEST_SILENT_ERRORS");
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_CHARDEV_SERIAL
|
||||
static void char_serial_test(void)
|
||||
|
@ -1342,12 +1439,15 @@ int main(int argc, char **argv)
|
|||
|
||||
SOCKET_SERVER_TEST(tcp, &tcpaddr);
|
||||
SOCKET_CLIENT_TEST(tcp, &tcpaddr);
|
||||
g_test_add_data_func("/char/socket/server/two-clients/tcp", &tcpaddr,
|
||||
char_socket_server_two_clients_test);
|
||||
#ifndef WIN32
|
||||
SOCKET_SERVER_TEST(unix, &unixaddr);
|
||||
SOCKET_CLIENT_TEST(unix, &unixaddr);
|
||||
g_test_add_data_func("/char/socket/server/two-clients/unix", &unixaddr,
|
||||
char_socket_server_two_clients_test);
|
||||
#endif
|
||||
|
||||
|
||||
g_test_add_func("/char/udp", char_udp_test);
|
||||
#ifdef HAVE_CHARDEV_SERIAL
|
||||
g_test_add_func("/char/serial", char_serial_test);
|
||||
|
|
|
@ -0,0 +1,434 @@
|
|||
/*
|
||||
* libqos driver framework
|
||||
*
|
||||
* Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/qgraph_internal.h"
|
||||
|
||||
#define MACHINE_PC "x86_64/pc"
|
||||
#define MACHINE_RASPI2 "arm/raspi2"
|
||||
#define I440FX "i440FX-pcihost"
|
||||
#define PCIBUS_PC "pcibus-pc"
|
||||
#define SDHCI "sdhci"
|
||||
#define PCIBUS "pci-bus"
|
||||
#define SDHCI_PCI "sdhci-pci"
|
||||
#define SDHCI_MM "generic-sdhci"
|
||||
#define REGISTER_TEST "register-test"
|
||||
|
||||
int npath;
|
||||
|
||||
static void *machinefunct(QTestState *qts)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *driverfunct(void *obj, QGuestAllocator *machine, void *arg)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void testfunct(void *obj, void *arg, QGuestAllocator *alloc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static void check_interface(const char *interface)
|
||||
{
|
||||
g_assert_cmpint(qos_graph_has_machine(interface), ==, FALSE);
|
||||
g_assert_nonnull(qos_graph_get_node(interface));
|
||||
g_assert_cmpint(qos_graph_has_node(interface), ==, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_type(interface), ==, QNODE_INTERFACE);
|
||||
qos_graph_node_set_availability(interface, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_availability(interface), ==, TRUE);
|
||||
}
|
||||
|
||||
static void check_machine(const char *machine)
|
||||
{
|
||||
qos_node_create_machine(machine, machinefunct);
|
||||
g_assert_nonnull(qos_graph_get_machine(machine));
|
||||
g_assert_cmpint(qos_graph_has_machine(machine), ==, TRUE);
|
||||
g_assert_nonnull(qos_graph_get_node(machine));
|
||||
g_assert_cmpint(qos_graph_get_node_availability(machine), ==, FALSE);
|
||||
qos_graph_node_set_availability(machine, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_availability(machine), ==, TRUE);
|
||||
g_assert_cmpint(qos_graph_has_node(machine), ==, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_type(machine), ==, QNODE_MACHINE);
|
||||
}
|
||||
|
||||
static void check_contains(const char *machine, const char *driver)
|
||||
{
|
||||
QOSGraphEdge *edge;
|
||||
qos_node_contains(machine, driver, NULL);
|
||||
|
||||
edge = qos_graph_get_edge(machine, driver);
|
||||
g_assert_nonnull(edge);
|
||||
g_assert_cmpint(qos_graph_edge_get_type(edge), ==, QEDGE_CONTAINS);
|
||||
g_assert_cmpint(qos_graph_has_edge(machine, driver), ==, TRUE);
|
||||
}
|
||||
|
||||
static void check_produces(const char *machine, const char *interface)
|
||||
{
|
||||
QOSGraphEdge *edge;
|
||||
|
||||
qos_node_produces(machine, interface);
|
||||
check_interface(interface);
|
||||
edge = qos_graph_get_edge(machine, interface);
|
||||
g_assert_nonnull(edge);
|
||||
g_assert_cmpint(qos_graph_edge_get_type(edge), ==,
|
||||
QEDGE_PRODUCES);
|
||||
g_assert_cmpint(qos_graph_has_edge(machine, interface), ==, TRUE);
|
||||
}
|
||||
|
||||
static void check_consumes(const char *driver, const char *interface)
|
||||
{
|
||||
QOSGraphEdge *edge;
|
||||
|
||||
qos_node_consumes(driver, interface, NULL);
|
||||
check_interface(interface);
|
||||
edge = qos_graph_get_edge(interface, driver);
|
||||
g_assert_nonnull(edge);
|
||||
g_assert_cmpint(qos_graph_edge_get_type(edge), ==, QEDGE_CONSUMED_BY);
|
||||
g_assert_cmpint(qos_graph_has_edge(interface, driver), ==, TRUE);
|
||||
}
|
||||
|
||||
static void check_driver(const char *driver)
|
||||
{
|
||||
qos_node_create_driver(driver, driverfunct);
|
||||
g_assert_cmpint(qos_graph_has_machine(driver), ==, FALSE);
|
||||
g_assert_nonnull(qos_graph_get_node(driver));
|
||||
g_assert_cmpint(qos_graph_has_node(driver), ==, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_type(driver), ==, QNODE_DRIVER);
|
||||
g_assert_cmpint(qos_graph_get_node_availability(driver), ==, FALSE);
|
||||
qos_graph_node_set_availability(driver, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_availability(driver), ==, TRUE);
|
||||
}
|
||||
|
||||
static void check_test(const char *test, const char *interface)
|
||||
{
|
||||
QOSGraphEdge *edge;
|
||||
const char *full_name = g_strdup_printf("%s-tests/%s", interface, test);
|
||||
|
||||
qos_add_test(test, interface, testfunct, NULL);
|
||||
g_assert_cmpint(qos_graph_has_machine(test), ==, FALSE);
|
||||
g_assert_cmpint(qos_graph_has_machine(full_name), ==, FALSE);
|
||||
g_assert_nonnull(qos_graph_get_node(full_name));
|
||||
g_assert_cmpint(qos_graph_has_node(full_name), ==, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_type(full_name), ==, QNODE_TEST);
|
||||
edge = qos_graph_get_edge(interface, full_name);
|
||||
g_assert_nonnull(edge);
|
||||
g_assert_cmpint(qos_graph_edge_get_type(edge), ==,
|
||||
QEDGE_CONSUMED_BY);
|
||||
g_assert_cmpint(qos_graph_has_edge(interface, full_name), ==, TRUE);
|
||||
g_assert_cmpint(qos_graph_get_node_availability(full_name), ==, TRUE);
|
||||
qos_graph_node_set_availability(full_name, FALSE);
|
||||
g_assert_cmpint(qos_graph_get_node_availability(full_name), ==, FALSE);
|
||||
}
|
||||
|
||||
static void count_each_test(QOSGraphNode *path, int len)
|
||||
{
|
||||
npath++;
|
||||
}
|
||||
|
||||
static void check_leaf_discovered(int n)
|
||||
{
|
||||
npath = 0;
|
||||
qos_graph_foreach_test_path(count_each_test);
|
||||
g_assert_cmpint(n, ==, npath);
|
||||
}
|
||||
|
||||
/* G_Test functions */
|
||||
|
||||
static void init_nop(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_machine(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_PC);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_contains(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_contains(MACHINE_PC, I440FX);
|
||||
g_assert_null(qos_graph_get_machine(MACHINE_PC));
|
||||
g_assert_null(qos_graph_get_machine(I440FX));
|
||||
g_assert_null(qos_graph_get_node(MACHINE_PC));
|
||||
g_assert_null(qos_graph_get_node(I440FX));
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_multiple_contains(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_contains(MACHINE_PC, I440FX);
|
||||
check_contains(MACHINE_PC, PCIBUS_PC);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_produces(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_produces(MACHINE_PC, I440FX);
|
||||
g_assert_null(qos_graph_get_machine(MACHINE_PC));
|
||||
g_assert_null(qos_graph_get_machine(I440FX));
|
||||
g_assert_null(qos_graph_get_node(MACHINE_PC));
|
||||
g_assert_nonnull(qos_graph_get_node(I440FX));
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_multiple_produces(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_produces(MACHINE_PC, I440FX);
|
||||
check_produces(MACHINE_PC, PCIBUS_PC);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_consumes(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_consumes(I440FX, SDHCI);
|
||||
g_assert_null(qos_graph_get_machine(I440FX));
|
||||
g_assert_null(qos_graph_get_machine(SDHCI));
|
||||
g_assert_null(qos_graph_get_node(I440FX));
|
||||
g_assert_nonnull(qos_graph_get_node(SDHCI));
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_multiple_consumes(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_consumes(I440FX, SDHCI);
|
||||
check_consumes(PCIBUS_PC, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_driver(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_driver(I440FX);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_test(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_test(REGISTER_TEST, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_machine_contains_driver(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_PC);
|
||||
check_driver(I440FX);
|
||||
check_contains(MACHINE_PC, I440FX);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_driver_contains_driver(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_driver(PCIBUS_PC);
|
||||
check_driver(I440FX);
|
||||
check_contains(PCIBUS_PC, I440FX);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_machine_produces_interface(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_PC);
|
||||
check_produces(MACHINE_PC, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_driver_produces_interface(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_driver(I440FX);
|
||||
check_produces(I440FX, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_machine_consumes_interface(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_PC);
|
||||
check_consumes(MACHINE_PC, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_driver_consumes_interface(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_driver(I440FX);
|
||||
check_consumes(I440FX, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_test_consumes_interface(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_test(REGISTER_TEST, SDHCI);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_full_sample(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_PC);
|
||||
check_contains(MACHINE_PC, I440FX);
|
||||
check_driver(I440FX);
|
||||
check_driver(PCIBUS_PC);
|
||||
check_contains(I440FX, PCIBUS_PC);
|
||||
check_produces(PCIBUS_PC, PCIBUS);
|
||||
check_driver(SDHCI_PCI);
|
||||
qos_node_consumes(SDHCI_PCI, PCIBUS, NULL);
|
||||
check_produces(SDHCI_PCI, SDHCI);
|
||||
check_driver(SDHCI_MM);
|
||||
check_produces(SDHCI_MM, SDHCI);
|
||||
qos_add_test(REGISTER_TEST, SDHCI, testfunct, NULL);
|
||||
check_leaf_discovered(1);
|
||||
qos_print_graph();
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_full_sample_raspi(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_PC);
|
||||
check_contains(MACHINE_PC, I440FX);
|
||||
check_driver(I440FX);
|
||||
check_driver(PCIBUS_PC);
|
||||
check_contains(I440FX, PCIBUS_PC);
|
||||
check_produces(PCIBUS_PC, PCIBUS);
|
||||
check_driver(SDHCI_PCI);
|
||||
qos_node_consumes(SDHCI_PCI, PCIBUS, NULL);
|
||||
check_produces(SDHCI_PCI, SDHCI);
|
||||
check_machine(MACHINE_RASPI2);
|
||||
check_contains(MACHINE_RASPI2, SDHCI_MM);
|
||||
check_driver(SDHCI_MM);
|
||||
check_produces(SDHCI_MM, SDHCI);
|
||||
qos_add_test(REGISTER_TEST, SDHCI, testfunct, NULL);
|
||||
qos_print_graph();
|
||||
check_leaf_discovered(2);
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_cycle(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_RASPI2);
|
||||
check_driver("B");
|
||||
check_driver("C");
|
||||
check_driver("D");
|
||||
check_contains(MACHINE_RASPI2, "B");
|
||||
check_contains("B", "C");
|
||||
check_contains("C", "D");
|
||||
check_contains("D", MACHINE_RASPI2);
|
||||
check_leaf_discovered(0);
|
||||
qos_print_graph();
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_two_test_same_interface(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_RASPI2);
|
||||
check_produces(MACHINE_RASPI2, "B");
|
||||
qos_add_test("C", "B", testfunct, NULL);
|
||||
qos_add_test("D", "B", testfunct, NULL);
|
||||
check_contains(MACHINE_RASPI2, "B");
|
||||
check_leaf_discovered(4);
|
||||
qos_print_graph();
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_test_in_path(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_RASPI2);
|
||||
check_produces(MACHINE_RASPI2, "B");
|
||||
qos_add_test("C", "B", testfunct, NULL);
|
||||
check_driver("D");
|
||||
check_consumes("D", "B");
|
||||
check_produces("D", "E");
|
||||
qos_add_test("F", "E", testfunct, NULL);
|
||||
check_leaf_discovered(2);
|
||||
qos_print_graph();
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
static void test_double_edge(void)
|
||||
{
|
||||
qos_graph_init();
|
||||
check_machine(MACHINE_RASPI2);
|
||||
check_produces("B", "C");
|
||||
qos_node_consumes("C", "B", NULL);
|
||||
qos_add_test("D", "C", testfunct, NULL);
|
||||
check_contains(MACHINE_RASPI2, "B");
|
||||
qos_print_graph();
|
||||
qos_graph_destroy();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/qgraph/init_nop", init_nop);
|
||||
g_test_add_func("/qgraph/test_machine", test_machine);
|
||||
g_test_add_func("/qgraph/test_contains", test_contains);
|
||||
g_test_add_func("/qgraph/test_multiple_contains", test_multiple_contains);
|
||||
g_test_add_func("/qgraph/test_produces", test_produces);
|
||||
g_test_add_func("/qgraph/test_multiple_produces", test_multiple_produces);
|
||||
g_test_add_func("/qgraph/test_consumes", test_consumes);
|
||||
g_test_add_func("/qgraph/test_multiple_consumes",
|
||||
test_multiple_consumes);
|
||||
g_test_add_func("/qgraph/test_driver", test_driver);
|
||||
g_test_add_func("/qgraph/test_test", test_test);
|
||||
g_test_add_func("/qgraph/test_machine_contains_driver",
|
||||
test_machine_contains_driver);
|
||||
g_test_add_func("/qgraph/test_driver_contains_driver",
|
||||
test_driver_contains_driver);
|
||||
g_test_add_func("/qgraph/test_machine_produces_interface",
|
||||
test_machine_produces_interface);
|
||||
g_test_add_func("/qgraph/test_driver_produces_interface",
|
||||
test_driver_produces_interface);
|
||||
g_test_add_func("/qgraph/test_machine_consumes_interface",
|
||||
test_machine_consumes_interface);
|
||||
g_test_add_func("/qgraph/test_driver_consumes_interface",
|
||||
test_driver_consumes_interface);
|
||||
g_test_add_func("/qgraph/test_test_consumes_interface",
|
||||
test_test_consumes_interface);
|
||||
g_test_add_func("/qgraph/test_full_sample", test_full_sample);
|
||||
g_test_add_func("/qgraph/test_full_sample_raspi", test_full_sample_raspi);
|
||||
g_test_add_func("/qgraph/test_cycle", test_cycle);
|
||||
g_test_add_func("/qgraph/test_two_test_same_interface",
|
||||
test_two_test_same_interface);
|
||||
g_test_add_func("/qgraph/test_test_in_path", test_test_in_path);
|
||||
g_test_add_func("/qgraph/test_double_edge", test_double_edge);
|
||||
|
||||
g_test_run();
|
||||
return 0;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* QTest testcase for tpci200 PCI-IndustryPack bridge
|
||||
*
|
||||
* Copyright (c) 2014 SUSE LINUX Products GmbH
|
||||
*
|
||||
* 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 "libqtest.h"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void nop(void)
|
||||
{
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/tpci200/nop", nop);
|
||||
|
||||
qtest_start("-device tpci200");
|
||||
ret = g_test_run();
|
||||
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -52,7 +52,7 @@ static void ehci_port_test(struct qhc *hc, int port, uint32_t expect)
|
|||
|
||||
static void test_init(void)
|
||||
{
|
||||
pcibus = qpci_init_pc(global_qtest, NULL);
|
||||
pcibus = qpci_new_pc(global_qtest, NULL);
|
||||
g_assert(pcibus != NULL);
|
||||
|
||||
qusb_pci_init_one(pcibus, &uhci1, QPCI_DEVFN(0x1d, 0), 4);
|
||||
|
|
|
@ -10,30 +10,58 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/usb.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
typedef struct QOHCI_PCI QOHCI_PCI;
|
||||
|
||||
static void test_ohci_init(void)
|
||||
{
|
||||
struct QOHCI_PCI {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice dev;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
static void test_ohci_hotplug(void)
|
||||
static void test_ohci_hotplug(void *obj, void *data, QGuestAllocator *alloc)
|
||||
{
|
||||
usb_test_hotplug("ohci", "1", NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void *ohci_pci_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
int ret;
|
||||
QOHCI_PCI *ohci_pci = obj;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return &ohci_pci->dev;
|
||||
}
|
||||
|
||||
qtest_add_func("/ohci/pci/init", test_ohci_init);
|
||||
qtest_add_func("/ohci/pci/hotplug", test_ohci_hotplug);
|
||||
|
||||
qtest_start("-device pci-ohci,id=ohci");
|
||||
ret = g_test_run();
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
fprintf(stderr, "%s not present in pci-ohci\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void *ohci_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
|
||||
{
|
||||
QOHCI_PCI *ohci_pci = g_new0(QOHCI_PCI, 1);
|
||||
ohci_pci->obj.get_driver = ohci_pci_get_driver;
|
||||
|
||||
return &ohci_pci->obj;
|
||||
}
|
||||
|
||||
static void ohci_pci_register_nodes(void)
|
||||
{
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0,id=ohci",
|
||||
};
|
||||
add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
|
||||
|
||||
qos_node_create_driver("pci-ohci", ohci_pci_create);
|
||||
qos_node_consumes("pci-ohci", "pci-bus", &opts);
|
||||
qos_node_produces("pci-ohci", "pci-device");
|
||||
}
|
||||
|
||||
libqos_init(ohci_pci_register_nodes);
|
||||
|
||||
static void register_ohci_pci_test(void)
|
||||
{
|
||||
qos_add_test("ohci_pci-test-hotplug", "pci-ohci", test_ohci_hotplug, NULL);
|
||||
}
|
||||
|
||||
libqos_init(register_ohci_pci_test);
|
||||
|
|
|
@ -41,8 +41,7 @@
|
|||
#define QEMU_CMD_MEMFD " -m %d -object memory-backend-memfd,id=mem,size=%dM," \
|
||||
" -numa node,memdev=mem"
|
||||
#define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s%s"
|
||||
#define QEMU_CMD_NETDEV " -netdev vhost-user,id=net0,chardev=%s,vhostforce"
|
||||
#define QEMU_CMD_NET " -device virtio-net-pci,netdev=net0"
|
||||
#define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce"
|
||||
|
||||
#define HUGETLBFS_MAGIC 0x958458f6
|
||||
|
||||
|
@ -136,13 +135,9 @@ enum {
|
|||
};
|
||||
|
||||
typedef struct TestServer {
|
||||
QPCIBus *bus;
|
||||
QVirtioPCIDevice *dev;
|
||||
QVirtQueue *vq[VHOST_MAX_VIRTQUEUES];
|
||||
gchar *socket_path;
|
||||
gchar *mig_path;
|
||||
gchar *chr_name;
|
||||
const gchar *mem_path;
|
||||
gchar *tmpfs;
|
||||
CharBackend chr;
|
||||
int fds_num;
|
||||
|
@ -158,9 +153,9 @@ typedef struct TestServer {
|
|||
bool test_fail;
|
||||
int test_flags;
|
||||
int queues;
|
||||
QGuestAllocator *alloc;
|
||||
} TestServer;
|
||||
|
||||
static const char *init_hugepagefs(void);
|
||||
static TestServer *test_server_new(const gchar *name);
|
||||
static void test_server_free(TestServer *server);
|
||||
static void test_server_listen(TestServer *server);
|
||||
|
@ -171,68 +166,30 @@ enum test_memfd {
|
|||
TEST_MEMFD_NO,
|
||||
};
|
||||
|
||||
static char *get_qemu_cmd(TestServer *s,
|
||||
int mem, enum test_memfd memfd,
|
||||
const char *chr_opts, const char *extra)
|
||||
static void append_vhost_opts(TestServer *s, GString *cmd_line,
|
||||
const char *chr_opts)
|
||||
{
|
||||
if (memfd == TEST_MEMFD_AUTO && qemu_memfd_check(0)) {
|
||||
memfd = TEST_MEMFD_YES;
|
||||
g_string_append_printf(cmd_line, QEMU_CMD_CHR QEMU_CMD_NETDEV,
|
||||
s->chr_name, s->socket_path,
|
||||
chr_opts, s->chr_name);
|
||||
}
|
||||
|
||||
static void append_mem_opts(TestServer *server, GString *cmd_line,
|
||||
int size, enum test_memfd memfd)
|
||||
{
|
||||
if (memfd == TEST_MEMFD_AUTO) {
|
||||
memfd = qemu_memfd_check(0) ? TEST_MEMFD_YES : TEST_MEMFD_NO;
|
||||
}
|
||||
|
||||
if (memfd == TEST_MEMFD_YES) {
|
||||
return g_strdup_printf(QEMU_CMD_MEMFD QEMU_CMD_CHR
|
||||
QEMU_CMD_NETDEV QEMU_CMD_NET "%s", mem, mem,
|
||||
s->chr_name, s->socket_path,
|
||||
chr_opts, s->chr_name, extra);
|
||||
g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size);
|
||||
} else {
|
||||
return g_strdup_printf(QEMU_CMD_MEM QEMU_CMD_CHR
|
||||
QEMU_CMD_NETDEV QEMU_CMD_NET "%s", mem, mem,
|
||||
s->mem_path, s->chr_name, s->socket_path,
|
||||
chr_opts, s->chr_name, extra);
|
||||
const char *root = init_hugepagefs() ? : server->tmpfs;
|
||||
|
||||
g_string_append_printf(cmd_line, QEMU_CMD_MEM, size, size, root);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_virtio_dev(QTestState *qts, TestServer *s, uint32_t features_mask)
|
||||
{
|
||||
uint32_t features;
|
||||
int i;
|
||||
|
||||
s->bus = qpci_init_pc(qts, NULL);
|
||||
g_assert_nonnull(s->bus);
|
||||
|
||||
s->dev = qvirtio_pci_device_find(s->bus, VIRTIO_ID_NET);
|
||||
g_assert_nonnull(s->dev);
|
||||
|
||||
qvirtio_pci_device_enable(s->dev);
|
||||
qvirtio_reset(&s->dev->vdev);
|
||||
qvirtio_set_acknowledge(&s->dev->vdev);
|
||||
qvirtio_set_driver(&s->dev->vdev);
|
||||
|
||||
s->alloc = pc_alloc_init(qts);
|
||||
|
||||
for (i = 0; i < s->queues * 2; i++) {
|
||||
s->vq[i] = qvirtqueue_setup(&s->dev->vdev, s->alloc, i);
|
||||
}
|
||||
|
||||
features = qvirtio_get_features(&s->dev->vdev);
|
||||
features = features & features_mask;
|
||||
qvirtio_set_features(&s->dev->vdev, features);
|
||||
|
||||
qvirtio_set_driver_ok(&s->dev->vdev);
|
||||
}
|
||||
|
||||
static void uninit_virtio_dev(TestServer *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->queues * 2; i++) {
|
||||
qvirtqueue_cleanup(s->dev->vdev.bus, s->vq[i], s->alloc);
|
||||
}
|
||||
pc_alloc_uninit(s->alloc);
|
||||
|
||||
qvirtio_pci_device_free(s->dev);
|
||||
}
|
||||
|
||||
static bool wait_for_fds(TestServer *s)
|
||||
{
|
||||
gint64 end_time;
|
||||
|
@ -467,17 +424,21 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
|
|||
static const char *init_hugepagefs(void)
|
||||
{
|
||||
#ifdef CONFIG_LINUX
|
||||
static const char *hugepagefs;
|
||||
const char *path = getenv("QTEST_HUGETLBFS_PATH");
|
||||
struct statfs fs;
|
||||
int ret;
|
||||
|
||||
if (hugepagefs) {
|
||||
return hugepagefs;
|
||||
}
|
||||
if (!path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (access(path, R_OK | W_OK | X_OK)) {
|
||||
g_test_message("access on path (%s): %s\n", path, strerror(errno));
|
||||
abort();
|
||||
g_test_fail();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -487,17 +448,18 @@ static const char *init_hugepagefs(void)
|
|||
|
||||
if (ret != 0) {
|
||||
g_test_message("statfs on path (%s): %s\n", path, strerror(errno));
|
||||
abort();
|
||||
g_test_fail();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fs.f_type != HUGETLBFS_MAGIC) {
|
||||
g_test_message("Warning: path not on HugeTLBFS: %s\n", path);
|
||||
abort();
|
||||
g_test_fail();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return path;
|
||||
hugepagefs = path;
|
||||
return hugepagefs;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
|
@ -522,7 +484,6 @@ static TestServer *test_server_new(const gchar *name)
|
|||
g_assert(tmpfs);
|
||||
|
||||
server->tmpfs = g_strdup(tmpfs);
|
||||
server->mem_path = init_hugepagefs() ? : server->tmpfs;
|
||||
server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name);
|
||||
server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name);
|
||||
server->chr_name = g_strdup_printf("chr-%s", name);
|
||||
|
@ -600,8 +561,6 @@ static void test_server_free(TestServer *server)
|
|||
}
|
||||
|
||||
g_free(server->chr_name);
|
||||
g_assert(server->bus);
|
||||
qpci_free_pc(server->bus);
|
||||
|
||||
g_main_loop_unref(server->loop);
|
||||
g_main_context_unref(server->context);
|
||||
|
@ -695,70 +654,79 @@ GSourceFuncs test_migrate_source_funcs = {
|
|||
.check = test_migrate_source_check,
|
||||
};
|
||||
|
||||
static void test_read_guest_mem(const void *arg)
|
||||
static void vhost_user_test_cleanup(void *s)
|
||||
{
|
||||
enum test_memfd memfd = GPOINTER_TO_INT(arg);
|
||||
TestServer *server = NULL;
|
||||
char *qemu_cmd = NULL;
|
||||
QTestState *s = NULL;
|
||||
TestServer *server = s;
|
||||
|
||||
server = test_server_new(memfd == TEST_MEMFD_YES ?
|
||||
"read-guest-memfd" : "read-guest-mem");
|
||||
test_server_listen(server);
|
||||
|
||||
qemu_cmd = get_qemu_cmd(server, 512, memfd, "", "");
|
||||
|
||||
s = qtest_start(qemu_cmd);
|
||||
g_free(qemu_cmd);
|
||||
|
||||
init_virtio_dev(global_qtest, server, 1u << VIRTIO_NET_F_MAC);
|
||||
|
||||
if (!wait_for_fds(server)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
read_guest_mem_server(global_qtest, server);
|
||||
|
||||
exit:
|
||||
uninit_virtio_dev(server);
|
||||
|
||||
qtest_quit(s);
|
||||
qos_invalidate_command_line();
|
||||
test_server_free(server);
|
||||
}
|
||||
|
||||
static void test_migrate(void)
|
||||
static void *vhost_user_test_setup(GString *cmd_line, void *arg)
|
||||
{
|
||||
TestServer *s = test_server_new("src");
|
||||
TestServer *server = test_server_new("vhost-user-test");
|
||||
test_server_listen(server);
|
||||
|
||||
append_mem_opts(server, cmd_line, 256, TEST_MEMFD_AUTO);
|
||||
append_vhost_opts(server, cmd_line, "");
|
||||
|
||||
g_test_queue_destroy(vhost_user_test_cleanup, server);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg)
|
||||
{
|
||||
TestServer *server = test_server_new("vhost-user-test");
|
||||
test_server_listen(server);
|
||||
|
||||
append_mem_opts(server, cmd_line, 256, TEST_MEMFD_YES);
|
||||
append_vhost_opts(server, cmd_line, "");
|
||||
|
||||
g_test_queue_destroy(vhost_user_test_cleanup, server);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc)
|
||||
{
|
||||
TestServer *server = arg;
|
||||
|
||||
if (!wait_for_fds(server)) {
|
||||
return;
|
||||
}
|
||||
|
||||
read_guest_mem_server(global_qtest, server);
|
||||
}
|
||||
|
||||
static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc)
|
||||
{
|
||||
TestServer *s = arg;
|
||||
TestServer *dest = test_server_new("dest");
|
||||
GString *dest_cmdline = g_string_new(qos_get_current_command_line());
|
||||
char *uri = g_strdup_printf("%s%s", "unix:", dest->mig_path);
|
||||
QTestState *from, *to;
|
||||
QTestState *to;
|
||||
GSource *source;
|
||||
gchar *cmd, *tmp;
|
||||
QDict *rsp;
|
||||
guint8 *log;
|
||||
guint64 size;
|
||||
|
||||
test_server_listen(s);
|
||||
test_server_listen(dest);
|
||||
|
||||
cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, "", "");
|
||||
from = qtest_start(cmd);
|
||||
g_free(cmd);
|
||||
|
||||
init_virtio_dev(from, s, 1u << VIRTIO_NET_F_MAC);
|
||||
if (!wait_for_fds(s)) {
|
||||
goto exit;
|
||||
return;
|
||||
}
|
||||
|
||||
size = get_log_size(s);
|
||||
g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));
|
||||
g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));
|
||||
|
||||
tmp = g_strdup_printf(" -incoming %s", uri);
|
||||
cmd = get_qemu_cmd(dest, 2, TEST_MEMFD_AUTO, "", tmp);
|
||||
g_free(tmp);
|
||||
to = qtest_init(cmd);
|
||||
g_free(cmd);
|
||||
init_virtio_dev(to, dest, 1u << VIRTIO_NET_F_MAC);
|
||||
test_server_listen(dest);
|
||||
g_string_append_printf(dest_cmdline, " -incoming %s", uri);
|
||||
append_mem_opts(dest, dest_cmdline, 256, TEST_MEMFD_AUTO);
|
||||
append_vhost_opts(dest, dest_cmdline, "");
|
||||
to = qtest_init(dest_cmdline->str);
|
||||
|
||||
/* This would be where you call qos_allocate_objects(to, NULL), if you want
|
||||
* to talk to the QVirtioNet object on the destination.
|
||||
*/
|
||||
|
||||
source = g_source_new(&test_migrate_source_funcs,
|
||||
sizeof(TestMigrateSource));
|
||||
|
@ -799,18 +767,11 @@ static void test_migrate(void)
|
|||
g_assert(wait_for_fds(dest));
|
||||
read_guest_mem_server(to, dest);
|
||||
|
||||
uninit_virtio_dev(dest);
|
||||
qtest_quit(to);
|
||||
|
||||
g_source_destroy(source);
|
||||
g_source_unref(source);
|
||||
|
||||
exit:
|
||||
uninit_virtio_dev(s);
|
||||
|
||||
qtest_quit(to);
|
||||
test_server_free(dest);
|
||||
qtest_quit(from);
|
||||
test_server_free(s);
|
||||
g_free(uri);
|
||||
}
|
||||
|
||||
|
@ -858,20 +819,26 @@ connect_thread(gpointer data)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void test_reconnect_subprocess(void)
|
||||
static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg)
|
||||
{
|
||||
TestServer *s = test_server_new("reconnect");
|
||||
GSource *src;
|
||||
char *cmd;
|
||||
|
||||
g_thread_new("connect", connect_thread, s);
|
||||
cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, ",server", "");
|
||||
qtest_start(cmd);
|
||||
g_free(cmd);
|
||||
append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
|
||||
append_vhost_opts(s, cmd_line, ",server");
|
||||
|
||||
g_test_queue_destroy(vhost_user_test_cleanup, s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc)
|
||||
{
|
||||
TestServer *s = arg;
|
||||
GSource *src;
|
||||
|
||||
init_virtio_dev(global_qtest, s, 1u << VIRTIO_NET_F_MAC);
|
||||
if (!wait_for_fds(s)) {
|
||||
goto exit;
|
||||
return;
|
||||
}
|
||||
|
||||
wait_for_rings_started(s, 2);
|
||||
|
@ -885,159 +852,111 @@ static void test_reconnect_subprocess(void)
|
|||
g_source_unref(src);
|
||||
g_assert(wait_for_fds(s));
|
||||
wait_for_rings_started(s, 2);
|
||||
|
||||
exit:
|
||||
uninit_virtio_dev(s);
|
||||
|
||||
qtest_end();
|
||||
test_server_free(s);
|
||||
return;
|
||||
}
|
||||
|
||||
static void test_reconnect(void)
|
||||
{
|
||||
gchar *path = g_strdup_printf("/%s/vhost-user/reconnect/subprocess",
|
||||
qtest_get_arch());
|
||||
g_test_trap_subprocess(path, 0, 0);
|
||||
g_test_trap_assert_passed();
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
static void test_connect_fail_subprocess(void)
|
||||
static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg)
|
||||
{
|
||||
TestServer *s = test_server_new("connect-fail");
|
||||
char *cmd;
|
||||
|
||||
s->test_fail = true;
|
||||
|
||||
g_thread_new("connect", connect_thread, s);
|
||||
cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, ",server", "");
|
||||
qtest_start(cmd);
|
||||
g_free(cmd);
|
||||
append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
|
||||
append_vhost_opts(s, cmd_line, ",server");
|
||||
|
||||
init_virtio_dev(global_qtest, s, 1u << VIRTIO_NET_F_MAC);
|
||||
if (!wait_for_fds(s)) {
|
||||
goto exit;
|
||||
}
|
||||
wait_for_rings_started(s, 2);
|
||||
g_test_queue_destroy(vhost_user_test_cleanup, s);
|
||||
|
||||
exit:
|
||||
uninit_virtio_dev(s);
|
||||
|
||||
qtest_end();
|
||||
test_server_free(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
static void test_connect_fail(void)
|
||||
{
|
||||
gchar *path = g_strdup_printf("/%s/vhost-user/connect-fail/subprocess",
|
||||
qtest_get_arch());
|
||||
g_test_trap_subprocess(path, 0, 0);
|
||||
g_test_trap_assert_passed();
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
static void test_flags_mismatch_subprocess(void)
|
||||
static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg)
|
||||
{
|
||||
TestServer *s = test_server_new("flags-mismatch");
|
||||
char *cmd;
|
||||
|
||||
s->test_flags = TEST_FLAGS_DISCONNECT;
|
||||
g_thread_new("connect", connect_thread, s);
|
||||
cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, ",server", "");
|
||||
qtest_start(cmd);
|
||||
g_free(cmd);
|
||||
|
||||
init_virtio_dev(global_qtest, s, 1u << VIRTIO_NET_F_MAC);
|
||||
g_thread_new("connect", connect_thread, s);
|
||||
append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
|
||||
append_vhost_opts(s, cmd_line, ",server");
|
||||
|
||||
g_test_queue_destroy(vhost_user_test_cleanup, s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void test_vhost_user_started(void *obj, void *arg, QGuestAllocator *alloc)
|
||||
{
|
||||
TestServer *s = arg;
|
||||
|
||||
if (!wait_for_fds(s)) {
|
||||
goto exit;
|
||||
return;
|
||||
}
|
||||
wait_for_rings_started(s, 2);
|
||||
|
||||
exit:
|
||||
uninit_virtio_dev(s);
|
||||
|
||||
qtest_end();
|
||||
test_server_free(s);
|
||||
}
|
||||
|
||||
static void test_flags_mismatch(void)
|
||||
static void *vhost_user_test_setup_multiqueue(GString *cmd_line, void *arg)
|
||||
{
|
||||
gchar *path = g_strdup_printf("/%s/vhost-user/flags-mismatch/subprocess",
|
||||
qtest_get_arch());
|
||||
g_test_trap_subprocess(path, 0, 0);
|
||||
g_test_trap_assert_passed();
|
||||
g_free(path);
|
||||
}
|
||||
TestServer *s = vhost_user_test_setup(cmd_line, arg);
|
||||
|
||||
|
||||
static void test_multiqueue(void)
|
||||
{
|
||||
TestServer *s = test_server_new("mq");
|
||||
char *cmd;
|
||||
uint32_t features_mask = ~(QVIRTIO_F_BAD_FEATURE |
|
||||
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
|
||||
(1u << VIRTIO_RING_F_EVENT_IDX));
|
||||
s->queues = 2;
|
||||
test_server_listen(s);
|
||||
g_string_append_printf(cmd_line,
|
||||
" -set netdev.hs0.queues=%d"
|
||||
" -global virtio-net-pci.vectors=%d",
|
||||
s->queues, s->queues * 2 + 2);
|
||||
|
||||
if (qemu_memfd_check(0)) {
|
||||
cmd = g_strdup_printf(
|
||||
QEMU_CMD_MEMFD QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d "
|
||||
"-device virtio-net-pci,netdev=net0,mq=on,vectors=%d",
|
||||
512, 512, s->chr_name,
|
||||
s->socket_path, "", s->chr_name,
|
||||
s->queues, s->queues * 2 + 2);
|
||||
} else {
|
||||
cmd = g_strdup_printf(
|
||||
QEMU_CMD_MEM QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d "
|
||||
"-device virtio-net-pci,netdev=net0,mq=on,vectors=%d",
|
||||
512, 512, s->mem_path, s->chr_name,
|
||||
s->socket_path, "", s->chr_name,
|
||||
s->queues, s->queues * 2 + 2);
|
||||
}
|
||||
qtest_start(cmd);
|
||||
g_free(cmd);
|
||||
return s;
|
||||
}
|
||||
|
||||
init_virtio_dev(global_qtest, s, features_mask);
|
||||
static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc)
|
||||
{
|
||||
TestServer *s = arg;
|
||||
|
||||
wait_for_rings_started(s, s->queues * 2);
|
||||
|
||||
uninit_virtio_dev(s);
|
||||
|
||||
qtest_end();
|
||||
|
||||
test_server_free(s);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void register_vhost_user_test(void)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
QOSGraphTestOptions opts = {
|
||||
.before = vhost_user_test_setup,
|
||||
.subprocess = true,
|
||||
};
|
||||
|
||||
module_call_init(MODULE_INIT_QOM);
|
||||
qemu_add_opts(&qemu_chardev_opts);
|
||||
|
||||
qos_add_test("vhost-user/read-guest-mem/memfile",
|
||||
"virtio-net",
|
||||
test_read_guest_mem, &opts);
|
||||
|
||||
if (qemu_memfd_check(0)) {
|
||||
qtest_add_data_func("/vhost-user/read-guest-mem/memfd",
|
||||
GINT_TO_POINTER(TEST_MEMFD_YES),
|
||||
test_read_guest_mem);
|
||||
opts.before = vhost_user_test_setup_memfd;
|
||||
qos_add_test("vhost-user/read-guest-mem/memfd",
|
||||
"virtio-net",
|
||||
test_read_guest_mem, &opts);
|
||||
}
|
||||
qtest_add_data_func("/vhost-user/read-guest-mem/memfile",
|
||||
GINT_TO_POINTER(TEST_MEMFD_NO), test_read_guest_mem);
|
||||
qtest_add_func("/vhost-user/migrate", test_migrate);
|
||||
qtest_add_func("/vhost-user/multiqueue", test_multiqueue);
|
||||
|
||||
qos_add_test("vhost-user/migrate",
|
||||
"virtio-net",
|
||||
test_migrate, &opts);
|
||||
|
||||
/* keeps failing on build-system since Aug 15 2017 */
|
||||
if (getenv("QTEST_VHOST_USER_FIXME")) {
|
||||
qtest_add_func("/vhost-user/reconnect/subprocess",
|
||||
test_reconnect_subprocess);
|
||||
qtest_add_func("/vhost-user/reconnect", test_reconnect);
|
||||
qtest_add_func("/vhost-user/connect-fail/subprocess",
|
||||
test_connect_fail_subprocess);
|
||||
qtest_add_func("/vhost-user/connect-fail", test_connect_fail);
|
||||
qtest_add_func("/vhost-user/flags-mismatch/subprocess",
|
||||
test_flags_mismatch_subprocess);
|
||||
qtest_add_func("/vhost-user/flags-mismatch", test_flags_mismatch);
|
||||
opts.before = vhost_user_test_setup_reconnect;
|
||||
qos_add_test("vhost-user/reconnect", "virtio-net",
|
||||
test_reconnect, &opts);
|
||||
|
||||
opts.before = vhost_user_test_setup_connect_fail;
|
||||
qos_add_test("vhost-user/connect-fail", "virtio-net",
|
||||
test_vhost_user_started, &opts);
|
||||
|
||||
opts.before = vhost_user_test_setup_flags_mismatch;
|
||||
qos_add_test("vhost-user/flags-mismatch", "virtio-net",
|
||||
test_vhost_user_started, &opts);
|
||||
}
|
||||
|
||||
return g_test_run();
|
||||
opts.before = vhost_user_test_setup_multiqueue;
|
||||
opts.edge.extra_device_opts = "mq=on";
|
||||
qos_add_test("vhost-user/multiqueue",
|
||||
"virtio-net",
|
||||
test_multiqueue, &opts);
|
||||
}
|
||||
libqos_init(register_vhost_user_test);
|
||||
|
|
|
@ -9,101 +9,36 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "qemu-common.h"
|
||||
#include "libqos/libqos-pc.h"
|
||||
#include "libqos/libqos-spapr.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/virtio-pci.h"
|
||||
#include "standard-headers/linux/virtio_ids.h"
|
||||
#include "standard-headers/linux/virtio_pci.h"
|
||||
#include "hw/9pfs/9p.h"
|
||||
#include "hw/9pfs/9p-synth.h"
|
||||
#include "libqos/virtio-9p.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
#define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000)
|
||||
static QGuestAllocator *alloc;
|
||||
|
||||
static const char mount_tag[] = "qtest";
|
||||
|
||||
typedef struct {
|
||||
QVirtioDevice *dev;
|
||||
QOSState *qs;
|
||||
QVirtQueue *vq;
|
||||
} QVirtIO9P;
|
||||
|
||||
static QVirtIO9P *qvirtio_9p_start(const char *driver)
|
||||
static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
const char *cmd = "-fsdev synth,id=fsdev0 "
|
||||
"-device %s,fsdev=fsdev0,mount_tag=%s";
|
||||
QVirtIO9P *v9p = g_new0(QVirtIO9P, 1);
|
||||
|
||||
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
|
||||
v9p->qs = qtest_pc_boot(cmd, driver, mount_tag);
|
||||
} else if (strcmp(arch, "ppc64") == 0) {
|
||||
v9p->qs = qtest_spapr_boot(cmd, driver, mount_tag);
|
||||
} else {
|
||||
g_printerr("virtio-9p tests are only available on x86 or ppc64\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
global_qtest = v9p->qs->qts;
|
||||
|
||||
return v9p;
|
||||
}
|
||||
|
||||
static void qvirtio_9p_stop(QVirtIO9P *v9p)
|
||||
{
|
||||
qtest_shutdown(v9p->qs);
|
||||
g_free(v9p);
|
||||
}
|
||||
|
||||
static QVirtIO9P *qvirtio_9p_pci_start(void)
|
||||
{
|
||||
QVirtIO9P *v9p = qvirtio_9p_start("virtio-9p-pci");
|
||||
QVirtioPCIDevice *dev = qvirtio_pci_device_find(v9p->qs->pcibus,
|
||||
VIRTIO_ID_9P);
|
||||
g_assert_nonnull(dev);
|
||||
g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_9P);
|
||||
v9p->dev = (QVirtioDevice *) dev;
|
||||
|
||||
qvirtio_pci_device_enable(dev);
|
||||
qvirtio_reset(v9p->dev);
|
||||
qvirtio_set_acknowledge(v9p->dev);
|
||||
qvirtio_set_driver(v9p->dev);
|
||||
|
||||
v9p->vq = qvirtqueue_setup(v9p->dev, v9p->qs->alloc, 0);
|
||||
|
||||
qvirtio_set_driver_ok(v9p->dev);
|
||||
|
||||
return v9p;
|
||||
}
|
||||
|
||||
static void qvirtio_9p_pci_stop(QVirtIO9P *v9p)
|
||||
{
|
||||
qvirtqueue_cleanup(v9p->dev->bus, v9p->vq, v9p->qs->alloc);
|
||||
qvirtio_pci_device_disable(container_of(v9p->dev, QVirtioPCIDevice, vdev));
|
||||
qvirtio_pci_device_free((QVirtioPCIDevice *)v9p->dev);
|
||||
qvirtio_9p_stop(v9p);
|
||||
}
|
||||
|
||||
static void pci_config(QVirtIO9P *v9p)
|
||||
{
|
||||
size_t tag_len = qvirtio_config_readw(v9p->dev, 0);
|
||||
QVirtio9P *v9p = obj;
|
||||
alloc = t_alloc;
|
||||
size_t tag_len = qvirtio_config_readw(v9p->vdev, 0);
|
||||
char *tag;
|
||||
int i;
|
||||
|
||||
g_assert_cmpint(tag_len, ==, strlen(mount_tag));
|
||||
g_assert_cmpint(tag_len, ==, strlen(MOUNT_TAG));
|
||||
|
||||
tag = g_malloc(tag_len);
|
||||
for (i = 0; i < tag_len; i++) {
|
||||
tag[i] = qvirtio_config_readb(v9p->dev, i + 2);
|
||||
tag[i] = qvirtio_config_readb(v9p->vdev, i + 2);
|
||||
}
|
||||
g_assert_cmpmem(tag, tag_len, mount_tag, tag_len);
|
||||
g_assert_cmpmem(tag, tag_len, MOUNT_TAG, tag_len);
|
||||
g_free(tag);
|
||||
}
|
||||
|
||||
#define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */
|
||||
|
||||
typedef struct {
|
||||
QVirtIO9P *v9p;
|
||||
QVirtio9P *v9p;
|
||||
uint16_t tag;
|
||||
uint64_t t_msg;
|
||||
uint32_t t_size;
|
||||
|
@ -206,7 +141,7 @@ static void v9fs_string_read(P9Req *req, uint16_t *len, char **string)
|
|||
uint16_t tag;
|
||||
} QEMU_PACKED P9Hdr;
|
||||
|
||||
static P9Req *v9fs_req_init(QVirtIO9P *v9p, uint32_t size, uint8_t id,
|
||||
static P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id,
|
||||
uint16_t tag)
|
||||
{
|
||||
P9Req *req = g_new0(P9Req, 1);
|
||||
|
@ -224,7 +159,7 @@ static P9Req *v9fs_req_init(QVirtIO9P *v9p, uint32_t size, uint8_t id,
|
|||
|
||||
req->v9p = v9p;
|
||||
req->t_size = total_size;
|
||||
req->t_msg = guest_alloc(v9p->qs->alloc, req->t_size);
|
||||
req->t_msg = guest_alloc(alloc, req->t_size);
|
||||
v9fs_memwrite(req, &hdr, 7);
|
||||
req->tag = tag;
|
||||
return req;
|
||||
|
@ -232,13 +167,13 @@ static P9Req *v9fs_req_init(QVirtIO9P *v9p, uint32_t size, uint8_t id,
|
|||
|
||||
static void v9fs_req_send(P9Req *req)
|
||||
{
|
||||
QVirtIO9P *v9p = req->v9p;
|
||||
QVirtio9P *v9p = req->v9p;
|
||||
|
||||
req->r_msg = guest_alloc(v9p->qs->alloc, P9_MAX_SIZE);
|
||||
req->r_msg = guest_alloc(alloc, P9_MAX_SIZE);
|
||||
req->free_head = qvirtqueue_add(v9p->vq, req->t_msg, req->t_size, false,
|
||||
true);
|
||||
qvirtqueue_add(v9p->vq, req->r_msg, P9_MAX_SIZE, true, false);
|
||||
qvirtqueue_kick(v9p->dev, v9p->vq, req->free_head);
|
||||
qvirtqueue_kick(v9p->vdev, v9p->vq, req->free_head);
|
||||
req->t_off = 0;
|
||||
}
|
||||
|
||||
|
@ -257,9 +192,9 @@ static const char *rmessage_name(uint8_t id)
|
|||
|
||||
static void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len)
|
||||
{
|
||||
QVirtIO9P *v9p = req->v9p;
|
||||
QVirtio9P *v9p = req->v9p;
|
||||
|
||||
qvirtio_wait_used_elem(v9p->dev, v9p->vq, req->free_head, len,
|
||||
qvirtio_wait_used_elem(v9p->vdev, v9p->vq, req->free_head, len,
|
||||
QVIRTIO_9P_TIMEOUT_US);
|
||||
}
|
||||
|
||||
|
@ -290,10 +225,8 @@ static void v9fs_req_recv(P9Req *req, uint8_t id)
|
|||
|
||||
static void v9fs_req_free(P9Req *req)
|
||||
{
|
||||
QVirtIO9P *v9p = req->v9p;
|
||||
|
||||
guest_free(v9p->qs->alloc, req->t_msg);
|
||||
guest_free(v9p->qs->alloc, req->r_msg);
|
||||
guest_free(alloc, req->t_msg);
|
||||
guest_free(alloc, req->r_msg);
|
||||
g_free(req);
|
||||
}
|
||||
|
||||
|
@ -306,7 +239,7 @@ static void v9fs_rlerror(P9Req *req, uint32_t *err)
|
|||
}
|
||||
|
||||
/* size[4] Tversion tag[2] msize[4] version[s] */
|
||||
static P9Req *v9fs_tversion(QVirtIO9P *v9p, uint32_t msize, const char *version,
|
||||
static P9Req *v9fs_tversion(QVirtio9P *v9p, uint32_t msize, const char *version,
|
||||
uint16_t tag)
|
||||
{
|
||||
P9Req *req;
|
||||
|
@ -341,7 +274,7 @@ static void v9fs_rversion(P9Req *req, uint16_t *len, char **version)
|
|||
}
|
||||
|
||||
/* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */
|
||||
static P9Req *v9fs_tattach(QVirtIO9P *v9p, uint32_t fid, uint32_t n_uname,
|
||||
static P9Req *v9fs_tattach(QVirtio9P *v9p, uint32_t fid, uint32_t n_uname,
|
||||
uint16_t tag)
|
||||
{
|
||||
const char *uname = ""; /* ignored by QEMU */
|
||||
|
@ -370,7 +303,7 @@ static void v9fs_rattach(P9Req *req, v9fs_qid *qid)
|
|||
}
|
||||
|
||||
/* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */
|
||||
static P9Req *v9fs_twalk(QVirtIO9P *v9p, uint32_t fid, uint32_t newfid,
|
||||
static P9Req *v9fs_twalk(QVirtio9P *v9p, uint32_t fid, uint32_t newfid,
|
||||
uint16_t nwname, char *const wnames[], uint16_t tag)
|
||||
{
|
||||
P9Req *req;
|
||||
|
@ -412,7 +345,7 @@ static void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid)
|
|||
}
|
||||
|
||||
/* size[4] Tlopen tag[2] fid[4] flags[4] */
|
||||
static P9Req *v9fs_tlopen(QVirtIO9P *v9p, uint32_t fid, uint32_t flags,
|
||||
static P9Req *v9fs_tlopen(QVirtio9P *v9p, uint32_t fid, uint32_t flags,
|
||||
uint16_t tag)
|
||||
{
|
||||
P9Req *req;
|
||||
|
@ -440,7 +373,7 @@ static void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit)
|
|||
}
|
||||
|
||||
/* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */
|
||||
static P9Req *v9fs_twrite(QVirtIO9P *v9p, uint32_t fid, uint64_t offset,
|
||||
static P9Req *v9fs_twrite(QVirtio9P *v9p, uint32_t fid, uint64_t offset,
|
||||
uint32_t count, const void *data, uint16_t tag)
|
||||
{
|
||||
P9Req *req;
|
||||
|
@ -468,7 +401,7 @@ static void v9fs_rwrite(P9Req *req, uint32_t *count)
|
|||
}
|
||||
|
||||
/* size[4] Tflush tag[2] oldtag[2] */
|
||||
static P9Req *v9fs_tflush(QVirtIO9P *v9p, uint16_t oldtag, uint16_t tag)
|
||||
static P9Req *v9fs_tflush(QVirtio9P *v9p, uint16_t oldtag, uint16_t tag)
|
||||
{
|
||||
P9Req *req;
|
||||
|
||||
|
@ -485,8 +418,10 @@ static void v9fs_rflush(P9Req *req)
|
|||
v9fs_req_free(req);
|
||||
}
|
||||
|
||||
static void fs_version(QVirtIO9P *v9p)
|
||||
static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtio9P *v9p = obj;
|
||||
alloc = t_alloc;
|
||||
const char *version = "9P2000.L";
|
||||
uint16_t server_len;
|
||||
char *server_version;
|
||||
|
@ -501,18 +436,22 @@ static void fs_version(QVirtIO9P *v9p)
|
|||
g_free(server_version);
|
||||
}
|
||||
|
||||
static void fs_attach(QVirtIO9P *v9p)
|
||||
static void fs_attach(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtio9P *v9p = obj;
|
||||
alloc = t_alloc;
|
||||
P9Req *req;
|
||||
|
||||
fs_version(v9p);
|
||||
fs_version(v9p, NULL, t_alloc);
|
||||
req = v9fs_tattach(v9p, 0, getuid(), 0);
|
||||
v9fs_req_wait_for_reply(req, NULL);
|
||||
v9fs_rattach(req, NULL);
|
||||
}
|
||||
|
||||
static void fs_walk(QVirtIO9P *v9p)
|
||||
static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtio9P *v9p = obj;
|
||||
alloc = t_alloc;
|
||||
char *wnames[P9_MAXWELEM];
|
||||
uint16_t nwqid;
|
||||
v9fs_qid *wqid;
|
||||
|
@ -523,7 +462,7 @@ static void fs_walk(QVirtIO9P *v9p)
|
|||
wnames[i] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i);
|
||||
}
|
||||
|
||||
fs_attach(v9p);
|
||||
fs_attach(v9p, NULL, t_alloc);
|
||||
req = v9fs_twalk(v9p, 0, 1, P9_MAXWELEM, wnames, 0);
|
||||
v9fs_req_wait_for_reply(req, NULL);
|
||||
v9fs_rwalk(req, &nwqid, &wqid);
|
||||
|
@ -537,13 +476,15 @@ static void fs_walk(QVirtIO9P *v9p)
|
|||
g_free(wqid);
|
||||
}
|
||||
|
||||
static void fs_walk_no_slash(QVirtIO9P *v9p)
|
||||
static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtio9P *v9p = obj;
|
||||
alloc = t_alloc;
|
||||
char *const wnames[] = { g_strdup(" /") };
|
||||
P9Req *req;
|
||||
uint32_t err;
|
||||
|
||||
fs_attach(v9p);
|
||||
fs_attach(v9p, NULL, t_alloc);
|
||||
req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
|
||||
v9fs_req_wait_for_reply(req, NULL);
|
||||
v9fs_rlerror(req, &err);
|
||||
|
@ -553,13 +494,15 @@ static void fs_walk_no_slash(QVirtIO9P *v9p)
|
|||
g_free(wnames[0]);
|
||||
}
|
||||
|
||||
static void fs_walk_dotdot(QVirtIO9P *v9p)
|
||||
static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtio9P *v9p = obj;
|
||||
alloc = t_alloc;
|
||||
char *const wnames[] = { g_strdup("..") };
|
||||
v9fs_qid root_qid, *wqid;
|
||||
P9Req *req;
|
||||
|
||||
fs_version(v9p);
|
||||
fs_version(v9p, NULL, t_alloc);
|
||||
req = v9fs_tattach(v9p, 0, getuid(), 0);
|
||||
v9fs_req_wait_for_reply(req, NULL);
|
||||
v9fs_rattach(req, &root_qid);
|
||||
|
@ -574,12 +517,14 @@ static void fs_walk_dotdot(QVirtIO9P *v9p)
|
|||
g_free(wnames[0]);
|
||||
}
|
||||
|
||||
static void fs_lopen(QVirtIO9P *v9p)
|
||||
static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtio9P *v9p = obj;
|
||||
alloc = t_alloc;
|
||||
char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) };
|
||||
P9Req *req;
|
||||
|
||||
fs_attach(v9p);
|
||||
fs_attach(v9p, NULL, t_alloc);
|
||||
req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
|
||||
v9fs_req_wait_for_reply(req, NULL);
|
||||
v9fs_rwalk(req, NULL, NULL);
|
||||
|
@ -591,15 +536,17 @@ static void fs_lopen(QVirtIO9P *v9p)
|
|||
g_free(wnames[0]);
|
||||
}
|
||||
|
||||
static void fs_write(QVirtIO9P *v9p)
|
||||
static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtio9P *v9p = obj;
|
||||
alloc = t_alloc;
|
||||
static const uint32_t write_count = P9_MAX_SIZE / 2;
|
||||
char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) };
|
||||
char *buf = g_malloc0(write_count);
|
||||
uint32_t count;
|
||||
P9Req *req;
|
||||
|
||||
fs_attach(v9p);
|
||||
fs_attach(v9p, NULL, t_alloc);
|
||||
req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
|
||||
v9fs_req_wait_for_reply(req, NULL);
|
||||
v9fs_rwalk(req, NULL, NULL);
|
||||
|
@ -617,14 +564,16 @@ static void fs_write(QVirtIO9P *v9p)
|
|||
g_free(wnames[0]);
|
||||
}
|
||||
|
||||
static void fs_flush_success(QVirtIO9P *v9p)
|
||||
static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtio9P *v9p = obj;
|
||||
alloc = t_alloc;
|
||||
char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
|
||||
P9Req *req, *flush_req;
|
||||
uint32_t reply_len;
|
||||
uint8_t should_block;
|
||||
|
||||
fs_attach(v9p);
|
||||
fs_attach(v9p, NULL, t_alloc);
|
||||
req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
|
||||
v9fs_req_wait_for_reply(req, NULL);
|
||||
v9fs_rwalk(req, NULL, NULL);
|
||||
|
@ -652,14 +601,16 @@ static void fs_flush_success(QVirtIO9P *v9p)
|
|||
g_free(wnames[0]);
|
||||
}
|
||||
|
||||
static void fs_flush_ignored(QVirtIO9P *v9p)
|
||||
static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtio9P *v9p = obj;
|
||||
alloc = t_alloc;
|
||||
char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) };
|
||||
P9Req *req, *flush_req;
|
||||
uint32_t count;
|
||||
uint8_t should_block;
|
||||
|
||||
fs_attach(v9p);
|
||||
fs_attach(v9p, NULL, t_alloc);
|
||||
req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0);
|
||||
v9fs_req_wait_for_reply(req, NULL);
|
||||
v9fs_rwalk(req, NULL, NULL);
|
||||
|
@ -687,39 +638,22 @@ static void fs_flush_ignored(QVirtIO9P *v9p)
|
|||
g_free(wnames[0]);
|
||||
}
|
||||
|
||||
typedef void (*v9fs_test_fn)(QVirtIO9P *v9p);
|
||||
|
||||
static void v9fs_run_pci_test(gconstpointer data)
|
||||
static void register_virtio_9p_test(void)
|
||||
{
|
||||
v9fs_test_fn fn = data;
|
||||
QVirtIO9P *v9p = qvirtio_9p_pci_start();
|
||||
|
||||
if (fn) {
|
||||
fn(v9p);
|
||||
}
|
||||
qvirtio_9p_pci_stop(v9p);
|
||||
qos_add_test("config", "virtio-9p", pci_config, NULL);
|
||||
qos_add_test("fs/version/basic", "virtio-9p", fs_version, NULL);
|
||||
qos_add_test("fs/attach/basic", "virtio-9p", fs_attach, NULL);
|
||||
qos_add_test("fs/walk/basic", "virtio-9p", fs_walk, NULL);
|
||||
qos_add_test("fs/walk/no_slash", "virtio-9p", fs_walk_no_slash,
|
||||
NULL);
|
||||
qos_add_test("fs/walk/dotdot_from_root", "virtio-9p",
|
||||
fs_walk_dotdot, NULL);
|
||||
qos_add_test("fs/lopen/basic", "virtio-9p", fs_lopen, NULL);
|
||||
qos_add_test("fs/write/basic", "virtio-9p", fs_write, NULL);
|
||||
qos_add_test("fs/flush/success", "virtio-9p", fs_flush_success,
|
||||
NULL);
|
||||
qos_add_test("fs/flush/ignored", "virtio-9p", fs_flush_ignored,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void v9fs_qtest_pci_add(const char *path, v9fs_test_fn fn)
|
||||
{
|
||||
qtest_add_data_func(path, fn, v9fs_run_pci_test);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
v9fs_qtest_pci_add("/virtio/9p/pci/nop", NULL);
|
||||
v9fs_qtest_pci_add("/virtio/9p/pci/config", pci_config);
|
||||
v9fs_qtest_pci_add("/virtio/9p/pci/fs/version/basic", fs_version);
|
||||
v9fs_qtest_pci_add("/virtio/9p/pci/fs/attach/basic", fs_attach);
|
||||
v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/basic", fs_walk);
|
||||
v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/no_slash", fs_walk_no_slash);
|
||||
v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/dotdot_from_root",
|
||||
fs_walk_dotdot);
|
||||
v9fs_qtest_pci_add("/virtio/9p/pci/fs/lopen/basic", fs_lopen);
|
||||
v9fs_qtest_pci_add("/virtio/9p/pci/fs/write/basic", fs_write);
|
||||
v9fs_qtest_pci_add("/virtio/9p/pci/fs/flush/success", fs_flush_success);
|
||||
v9fs_qtest_pci_add("/virtio/9p/pci/fs/flush/ignored", fs_flush_ignored);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
libqos_init(register_virtio_9p_test);
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* QTest testcase for VirtIO Balloon
|
||||
*
|
||||
* Copyright (c) 2014 SUSE LINUX Products GmbH
|
||||
*
|
||||
* 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 "libqtest.h"
|
||||
#include "libqos/virtio.h"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void balloon_nop(void)
|
||||
{
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/virtio/balloon/nop", balloon_nop);
|
||||
|
||||
global_qtest = qtest_initf("-device virtio-balloon-%s",
|
||||
qvirtio_get_dev_type());
|
||||
ret = g_test_run();
|
||||
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -10,19 +10,11 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/libqos-pc.h"
|
||||
#include "libqos/libqos-spapr.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/virtio-pci.h"
|
||||
#include "libqos/virtio-mmio.h"
|
||||
#include "libqos/malloc-generic.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "standard-headers/linux/virtio_ids.h"
|
||||
#include "standard-headers/linux/virtio_config.h"
|
||||
#include "standard-headers/linux/virtio_ring.h"
|
||||
#include "standard-headers/linux/virtio_blk.h"
|
||||
#include "standard-headers/linux/virtio_pci.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio-blk.h"
|
||||
|
||||
/* TODO actually test the results and get rid of this */
|
||||
#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__))
|
||||
|
@ -30,13 +22,6 @@
|
|||
#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
|
||||
#define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000)
|
||||
#define PCI_SLOT_HP 0x06
|
||||
#define PCI_SLOT 0x04
|
||||
#define PCI_FN 0x00
|
||||
|
||||
#define MMIO_PAGE_SIZE 4096
|
||||
#define MMIO_DEV_BASE_ADDR 0x0A003E00
|
||||
#define MMIO_RAM_ADDR 0x40000000
|
||||
#define MMIO_RAM_SIZE 0x20000000
|
||||
|
||||
typedef struct QVirtioBlkReq {
|
||||
uint32_t type;
|
||||
|
@ -46,87 +31,34 @@ typedef struct QVirtioBlkReq {
|
|||
uint8_t status;
|
||||
} QVirtioBlkReq;
|
||||
|
||||
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
const bool host_is_big_endian = true;
|
||||
#else
|
||||
const bool host_is_big_endian; /* false */
|
||||
#endif
|
||||
|
||||
static void drive_destroy(void *path)
|
||||
{
|
||||
unlink(path);
|
||||
g_free(path);
|
||||
qos_invalidate_command_line();
|
||||
}
|
||||
|
||||
static char *drive_create(void)
|
||||
{
|
||||
int fd, ret;
|
||||
char *tmp_path = g_strdup("/tmp/qtest.XXXXXX");
|
||||
char *t_path = g_strdup("/tmp/qtest.XXXXXX");
|
||||
|
||||
/* Create a temporary raw image */
|
||||
fd = mkstemp(tmp_path);
|
||||
fd = mkstemp(t_path);
|
||||
g_assert_cmpint(fd, >=, 0);
|
||||
ret = ftruncate(fd, TEST_IMAGE_SIZE);
|
||||
g_assert_cmpint(ret, ==, 0);
|
||||
close(fd);
|
||||
|
||||
return tmp_path;
|
||||
}
|
||||
|
||||
static QOSState *pci_test_start(void)
|
||||
{
|
||||
QOSState *qs;
|
||||
const char *arch = qtest_get_arch();
|
||||
char *tmp_path;
|
||||
const char *cmd = "-drive if=none,id=drive0,file=%s,format=raw "
|
||||
"-drive if=none,id=drive1,file=null-co://,format=raw "
|
||||
"-device virtio-blk-pci,id=drv0,drive=drive0,"
|
||||
"addr=%x.%x";
|
||||
|
||||
tmp_path = drive_create();
|
||||
|
||||
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
|
||||
qs = qtest_pc_boot(cmd, tmp_path, PCI_SLOT, PCI_FN);
|
||||
} else if (strcmp(arch, "ppc64") == 0) {
|
||||
qs = qtest_spapr_boot(cmd, tmp_path, PCI_SLOT, PCI_FN);
|
||||
} else {
|
||||
g_printerr("virtio-blk tests are only available on x86 or ppc64\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
global_qtest = qs->qts;
|
||||
unlink(tmp_path);
|
||||
g_free(tmp_path);
|
||||
return qs;
|
||||
}
|
||||
|
||||
static void arm_test_start(void)
|
||||
{
|
||||
char *tmp_path;
|
||||
|
||||
tmp_path = drive_create();
|
||||
|
||||
global_qtest = qtest_initf("-machine virt "
|
||||
"-drive if=none,id=drive0,file=%s,format=raw "
|
||||
"-device virtio-blk-device,drive=drive0",
|
||||
tmp_path);
|
||||
unlink(tmp_path);
|
||||
g_free(tmp_path);
|
||||
}
|
||||
|
||||
static void test_end(void)
|
||||
{
|
||||
qtest_end();
|
||||
}
|
||||
|
||||
static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot)
|
||||
{
|
||||
QVirtioPCIDevice *dev;
|
||||
|
||||
dev = qvirtio_pci_device_find_slot(bus, VIRTIO_ID_BLOCK, slot);
|
||||
g_assert(dev != NULL);
|
||||
g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
|
||||
g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN));
|
||||
|
||||
qvirtio_pci_device_enable(dev);
|
||||
qvirtio_reset(&dev->vdev);
|
||||
qvirtio_set_acknowledge(&dev->vdev);
|
||||
qvirtio_set_driver(&dev->vdev);
|
||||
|
||||
return dev;
|
||||
g_test_queue_destroy(drive_destroy, t_path);
|
||||
return t_path;
|
||||
}
|
||||
|
||||
static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req)
|
||||
|
@ -397,31 +329,21 @@ static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc,
|
|||
}
|
||||
}
|
||||
|
||||
static void pci_basic(void)
|
||||
static void basic(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtioPCIDevice *dev;
|
||||
QOSState *qs;
|
||||
QVirtQueuePCI *vqpci;
|
||||
QVirtioBlk *blk_if = obj;
|
||||
QVirtQueue *vq;
|
||||
vq = qvirtqueue_setup(blk_if->vdev, t_alloc, 0);
|
||||
test_basic(blk_if->vdev, t_alloc, vq);
|
||||
qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc);
|
||||
|
||||
qs = pci_test_start();
|
||||
dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT);
|
||||
|
||||
vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
|
||||
|
||||
test_basic(&dev->vdev, qs->alloc, &vqpci->vq);
|
||||
|
||||
/* End test */
|
||||
qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc);
|
||||
qvirtio_pci_device_disable(dev);
|
||||
qvirtio_pci_device_free(dev);
|
||||
qtest_shutdown(qs);
|
||||
}
|
||||
|
||||
static void pci_indirect(void)
|
||||
static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtioPCIDevice *dev;
|
||||
QVirtQueuePCI *vqpci;
|
||||
QOSState *qs;
|
||||
QVirtQueue *vq;
|
||||
QVirtioBlk *blk_if = obj;
|
||||
QVirtioDevice *dev = blk_if->vdev;
|
||||
QVirtioBlkReq req;
|
||||
QVRingIndirectDesc *indirect;
|
||||
uint64_t req_addr;
|
||||
|
@ -431,22 +353,18 @@ static void pci_indirect(void)
|
|||
uint8_t status;
|
||||
char *data;
|
||||
|
||||
qs = pci_test_start();
|
||||
|
||||
dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT);
|
||||
|
||||
capacity = qvirtio_config_readq(&dev->vdev, 0);
|
||||
capacity = qvirtio_config_readq(dev, 0);
|
||||
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
|
||||
|
||||
features = qvirtio_get_features(&dev->vdev);
|
||||
features = qvirtio_get_features(dev);
|
||||
g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0);
|
||||
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
||||
(1u << VIRTIO_RING_F_EVENT_IDX) |
|
||||
(1u << VIRTIO_BLK_F_SCSI));
|
||||
qvirtio_set_features(&dev->vdev, features);
|
||||
qvirtio_set_features(dev, features);
|
||||
|
||||
vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
|
||||
qvirtio_set_driver_ok(&dev->vdev);
|
||||
vq = qvirtqueue_setup(dev, t_alloc, 0);
|
||||
qvirtio_set_driver_ok(dev);
|
||||
|
||||
/* Write request */
|
||||
req.type = VIRTIO_BLK_T_OUT;
|
||||
|
@ -455,23 +373,23 @@ static void pci_indirect(void)
|
|||
req.data = g_malloc0(512);
|
||||
strcpy(req.data, "TEST");
|
||||
|
||||
req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
|
||||
req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
|
||||
|
||||
g_free(req.data);
|
||||
|
||||
indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2);
|
||||
indirect = qvring_indirect_desc_setup(dev, t_alloc, 2);
|
||||
qvring_indirect_desc_add(indirect, req_addr, 528, false);
|
||||
qvring_indirect_desc_add(indirect, req_addr + 528, 1, true);
|
||||
free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect);
|
||||
qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
|
||||
free_head = qvirtqueue_add_indirect(vq, indirect);
|
||||
qvirtqueue_kick(dev, vq, free_head);
|
||||
|
||||
qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL,
|
||||
qvirtio_wait_used_elem(dev, vq, free_head, NULL,
|
||||
QVIRTIO_BLK_TIMEOUT_US);
|
||||
status = readb(req_addr + 528);
|
||||
g_assert_cmpint(status, ==, 0);
|
||||
|
||||
g_free(indirect);
|
||||
guest_free(qs->alloc, req_addr);
|
||||
guest_free(t_alloc, req_addr);
|
||||
|
||||
/* Read request */
|
||||
req.type = VIRTIO_BLK_T_IN;
|
||||
|
@ -480,17 +398,17 @@ static void pci_indirect(void)
|
|||
req.data = g_malloc0(512);
|
||||
strcpy(req.data, "TEST");
|
||||
|
||||
req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
|
||||
req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
|
||||
|
||||
g_free(req.data);
|
||||
|
||||
indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2);
|
||||
indirect = qvring_indirect_desc_setup(dev, t_alloc, 2);
|
||||
qvring_indirect_desc_add(indirect, req_addr, 16, false);
|
||||
qvring_indirect_desc_add(indirect, req_addr + 16, 513, true);
|
||||
free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect);
|
||||
qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
|
||||
free_head = qvirtqueue_add_indirect(vq, indirect);
|
||||
qvirtqueue_kick(dev, vq, free_head);
|
||||
|
||||
qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL,
|
||||
qvirtio_wait_used_elem(dev, vq, free_head, NULL,
|
||||
QVIRTIO_BLK_TIMEOUT_US);
|
||||
status = readb(req_addr + 528);
|
||||
g_assert_cmpint(status, ==, 0);
|
||||
|
@ -501,50 +419,37 @@ static void pci_indirect(void)
|
|||
g_free(data);
|
||||
|
||||
g_free(indirect);
|
||||
guest_free(qs->alloc, req_addr);
|
||||
|
||||
/* End test */
|
||||
qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc);
|
||||
qvirtio_pci_device_disable(dev);
|
||||
qvirtio_pci_device_free(dev);
|
||||
qtest_shutdown(qs);
|
||||
guest_free(t_alloc, req_addr);
|
||||
qvirtqueue_cleanup(dev->bus, vq, t_alloc);
|
||||
}
|
||||
|
||||
static void pci_config(void)
|
||||
static void config(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtioPCIDevice *dev;
|
||||
QOSState *qs;
|
||||
QVirtioBlk *blk_if = obj;
|
||||
QVirtioDevice *dev = blk_if->vdev;
|
||||
int n_size = TEST_IMAGE_SIZE / 2;
|
||||
uint64_t capacity;
|
||||
|
||||
qs = pci_test_start();
|
||||
|
||||
dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT);
|
||||
|
||||
capacity = qvirtio_config_readq(&dev->vdev, 0);
|
||||
capacity = qvirtio_config_readq(dev, 0);
|
||||
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
|
||||
|
||||
qvirtio_set_driver_ok(&dev->vdev);
|
||||
qvirtio_set_driver_ok(dev);
|
||||
|
||||
qmp_discard_response("{ 'execute': 'block_resize', "
|
||||
" 'arguments': { 'device': 'drive0', "
|
||||
" 'size': %d } }", n_size);
|
||||
qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US);
|
||||
qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US);
|
||||
|
||||
capacity = qvirtio_config_readq(&dev->vdev, 0);
|
||||
capacity = qvirtio_config_readq(dev, 0);
|
||||
g_assert_cmpint(capacity, ==, n_size / 512);
|
||||
|
||||
qvirtio_pci_device_disable(dev);
|
||||
qvirtio_pci_device_free(dev);
|
||||
|
||||
qtest_shutdown(qs);
|
||||
}
|
||||
|
||||
static void pci_msix(void)
|
||||
static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtioPCIDevice *dev;
|
||||
QOSState *qs;
|
||||
QVirtQueuePCI *vqpci;
|
||||
QVirtQueue *vq;
|
||||
QVirtioBlkPCI *blk = obj;
|
||||
QVirtioPCIDevice *pdev = &blk->pci_vdev;
|
||||
QVirtioDevice *dev = &pdev->vdev;
|
||||
QVirtioBlkReq req;
|
||||
int n_size = TEST_IMAGE_SIZE / 2;
|
||||
uint64_t req_addr;
|
||||
|
@ -553,36 +458,38 @@ static void pci_msix(void)
|
|||
uint32_t free_head;
|
||||
uint8_t status;
|
||||
char *data;
|
||||
QOSGraphObject *blk_object = obj;
|
||||
QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
|
||||
|
||||
qs = pci_test_start();
|
||||
if (qpci_check_buggy_msi(pci_dev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT);
|
||||
qpci_msix_enable(dev->pdev);
|
||||
qpci_msix_enable(pdev->pdev);
|
||||
qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
|
||||
|
||||
qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0);
|
||||
|
||||
capacity = qvirtio_config_readq(&dev->vdev, 0);
|
||||
capacity = qvirtio_config_readq(dev, 0);
|
||||
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
|
||||
|
||||
features = qvirtio_get_features(&dev->vdev);
|
||||
features = qvirtio_get_features(dev);
|
||||
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
||||
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
|
||||
(1u << VIRTIO_RING_F_EVENT_IDX) |
|
||||
(1u << VIRTIO_BLK_F_SCSI));
|
||||
qvirtio_set_features(&dev->vdev, features);
|
||||
qvirtio_set_features(dev, features);
|
||||
|
||||
vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
|
||||
qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1);
|
||||
vq = qvirtqueue_setup(dev, t_alloc, 0);
|
||||
qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
|
||||
|
||||
qvirtio_set_driver_ok(&dev->vdev);
|
||||
qvirtio_set_driver_ok(dev);
|
||||
|
||||
qmp_discard_response("{ 'execute': 'block_resize', "
|
||||
" 'arguments': { 'device': 'drive0', "
|
||||
" 'size': %d } }", n_size);
|
||||
|
||||
qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US);
|
||||
qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US);
|
||||
|
||||
capacity = qvirtio_config_readq(&dev->vdev, 0);
|
||||
capacity = qvirtio_config_readq(dev, 0);
|
||||
g_assert_cmpint(capacity, ==, n_size / 512);
|
||||
|
||||
/* Write request */
|
||||
|
@ -592,22 +499,22 @@ static void pci_msix(void)
|
|||
req.data = g_malloc0(512);
|
||||
strcpy(req.data, "TEST");
|
||||
|
||||
req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
|
||||
req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
|
||||
|
||||
g_free(req.data);
|
||||
|
||||
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
|
||||
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true);
|
||||
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
|
||||
qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
|
||||
free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
|
||||
qvirtqueue_add(vq, req_addr + 16, 512, false, true);
|
||||
qvirtqueue_add(vq, req_addr + 528, 1, true, false);
|
||||
qvirtqueue_kick(dev, vq, free_head);
|
||||
|
||||
qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL,
|
||||
qvirtio_wait_used_elem(dev, vq, free_head, NULL,
|
||||
QVIRTIO_BLK_TIMEOUT_US);
|
||||
|
||||
status = readb(req_addr + 528);
|
||||
g_assert_cmpint(status, ==, 0);
|
||||
|
||||
guest_free(qs->alloc, req_addr);
|
||||
guest_free(t_alloc, req_addr);
|
||||
|
||||
/* Read request */
|
||||
req.type = VIRTIO_BLK_T_IN;
|
||||
|
@ -615,18 +522,18 @@ static void pci_msix(void)
|
|||
req.sector = 0;
|
||||
req.data = g_malloc0(512);
|
||||
|
||||
req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
|
||||
req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
|
||||
|
||||
g_free(req.data);
|
||||
|
||||
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
|
||||
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true);
|
||||
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
|
||||
free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
|
||||
qvirtqueue_add(vq, req_addr + 16, 512, true, true);
|
||||
qvirtqueue_add(vq, req_addr + 528, 1, true, false);
|
||||
|
||||
qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
|
||||
qvirtqueue_kick(dev, vq, free_head);
|
||||
|
||||
|
||||
qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL,
|
||||
qvirtio_wait_used_elem(dev, vq, free_head, NULL,
|
||||
QVIRTIO_BLK_TIMEOUT_US);
|
||||
|
||||
status = readb(req_addr + 528);
|
||||
|
@ -637,21 +544,19 @@ static void pci_msix(void)
|
|||
g_assert_cmpstr(data, ==, "TEST");
|
||||
g_free(data);
|
||||
|
||||
guest_free(qs->alloc, req_addr);
|
||||
guest_free(t_alloc, req_addr);
|
||||
|
||||
/* End test */
|
||||
qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc);
|
||||
qpci_msix_disable(dev->pdev);
|
||||
qvirtio_pci_device_disable(dev);
|
||||
qvirtio_pci_device_free(dev);
|
||||
qtest_shutdown(qs);
|
||||
qpci_msix_disable(pdev->pdev);
|
||||
qvirtqueue_cleanup(dev->bus, vq, t_alloc);
|
||||
}
|
||||
|
||||
static void pci_idx(void)
|
||||
static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtioPCIDevice *dev;
|
||||
QOSState *qs;
|
||||
QVirtQueuePCI *vqpci;
|
||||
QVirtQueue *vq;
|
||||
QVirtioBlkPCI *blk = obj;
|
||||
QVirtioPCIDevice *pdev = &blk->pci_vdev;
|
||||
QVirtioDevice *dev = &pdev->vdev;
|
||||
QVirtioBlkReq req;
|
||||
uint64_t req_addr;
|
||||
uint64_t capacity;
|
||||
|
@ -661,28 +566,30 @@ static void pci_idx(void)
|
|||
uint32_t desc_idx;
|
||||
uint8_t status;
|
||||
char *data;
|
||||
QOSGraphObject *blk_object = obj;
|
||||
QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device");
|
||||
|
||||
qs = pci_test_start();
|
||||
if (qpci_check_buggy_msi(pci_dev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT);
|
||||
qpci_msix_enable(dev->pdev);
|
||||
qpci_msix_enable(pdev->pdev);
|
||||
qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0);
|
||||
|
||||
qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0);
|
||||
|
||||
capacity = qvirtio_config_readq(&dev->vdev, 0);
|
||||
capacity = qvirtio_config_readq(dev, 0);
|
||||
g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512);
|
||||
|
||||
features = qvirtio_get_features(&dev->vdev);
|
||||
features = qvirtio_get_features(dev);
|
||||
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
||||
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
|
||||
(1u << VIRTIO_F_NOTIFY_ON_EMPTY) |
|
||||
(1u << VIRTIO_BLK_F_SCSI));
|
||||
qvirtio_set_features(&dev->vdev, features);
|
||||
qvirtio_set_features(dev, features);
|
||||
|
||||
vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
|
||||
qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1);
|
||||
vq = qvirtqueue_setup(dev, t_alloc, 0);
|
||||
qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1);
|
||||
|
||||
qvirtio_set_driver_ok(&dev->vdev);
|
||||
qvirtio_set_driver_ok(dev);
|
||||
|
||||
/* Write request */
|
||||
req.type = VIRTIO_BLK_T_OUT;
|
||||
|
@ -691,16 +598,16 @@ static void pci_idx(void)
|
|||
req.data = g_malloc0(512);
|
||||
strcpy(req.data, "TEST");
|
||||
|
||||
req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
|
||||
req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
|
||||
|
||||
g_free(req.data);
|
||||
|
||||
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
|
||||
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true);
|
||||
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
|
||||
qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
|
||||
free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
|
||||
qvirtqueue_add(vq, req_addr + 16, 512, false, true);
|
||||
qvirtqueue_add(vq, req_addr + 528, 1, true, false);
|
||||
qvirtqueue_kick(dev, vq, free_head);
|
||||
|
||||
qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL,
|
||||
qvirtio_wait_used_elem(dev, vq, free_head, NULL,
|
||||
QVIRTIO_BLK_TIMEOUT_US);
|
||||
|
||||
/* Write request */
|
||||
|
@ -710,25 +617,25 @@ static void pci_idx(void)
|
|||
req.data = g_malloc0(512);
|
||||
strcpy(req.data, "TEST");
|
||||
|
||||
req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
|
||||
req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
|
||||
|
||||
g_free(req.data);
|
||||
|
||||
/* Notify after processing the third request */
|
||||
qvirtqueue_set_used_event(&vqpci->vq, 2);
|
||||
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
|
||||
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true);
|
||||
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
|
||||
qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
|
||||
qvirtqueue_set_used_event(vq, 2);
|
||||
free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
|
||||
qvirtqueue_add(vq, req_addr + 16, 512, false, true);
|
||||
qvirtqueue_add(vq, req_addr + 528, 1, true, false);
|
||||
qvirtqueue_kick(dev, vq, free_head);
|
||||
write_head = free_head;
|
||||
|
||||
/* No notification expected */
|
||||
status = qvirtio_wait_status_byte_no_isr(&dev->vdev,
|
||||
&vqpci->vq, req_addr + 528,
|
||||
status = qvirtio_wait_status_byte_no_isr(dev,
|
||||
vq, req_addr + 528,
|
||||
QVIRTIO_BLK_TIMEOUT_US);
|
||||
g_assert_cmpint(status, ==, 0);
|
||||
|
||||
guest_free(qs->alloc, req_addr);
|
||||
guest_free(t_alloc, req_addr);
|
||||
|
||||
/* Read request */
|
||||
req.type = VIRTIO_BLK_T_IN;
|
||||
|
@ -736,20 +643,20 @@ static void pci_idx(void)
|
|||
req.sector = 1;
|
||||
req.data = g_malloc0(512);
|
||||
|
||||
req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512);
|
||||
req_addr = virtio_blk_request(t_alloc, dev, &req, 512);
|
||||
|
||||
g_free(req.data);
|
||||
|
||||
free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true);
|
||||
qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true);
|
||||
qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false);
|
||||
free_head = qvirtqueue_add(vq, req_addr, 16, false, true);
|
||||
qvirtqueue_add(vq, req_addr + 16, 512, true, true);
|
||||
qvirtqueue_add(vq, req_addr + 528, 1, true, false);
|
||||
|
||||
qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head);
|
||||
qvirtqueue_kick(dev, vq, free_head);
|
||||
|
||||
/* We get just one notification for both requests */
|
||||
qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, write_head, NULL,
|
||||
qvirtio_wait_used_elem(dev, vq, write_head, NULL,
|
||||
QVIRTIO_BLK_TIMEOUT_US);
|
||||
g_assert(qvirtqueue_get_buf(&vqpci->vq, &desc_idx, NULL));
|
||||
g_assert(qvirtqueue_get_buf(vq, &desc_idx, NULL));
|
||||
g_assert_cmpint(desc_idx, ==, free_head);
|
||||
|
||||
status = readb(req_addr + 528);
|
||||
|
@ -760,124 +667,114 @@ static void pci_idx(void)
|
|||
g_assert_cmpstr(data, ==, "TEST");
|
||||
g_free(data);
|
||||
|
||||
guest_free(qs->alloc, req_addr);
|
||||
guest_free(t_alloc, req_addr);
|
||||
|
||||
/* End test */
|
||||
qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc);
|
||||
qpci_msix_disable(dev->pdev);
|
||||
qvirtio_pci_device_disable(dev);
|
||||
qvirtio_pci_device_free(dev);
|
||||
qtest_shutdown(qs);
|
||||
qpci_msix_disable(pdev->pdev);
|
||||
|
||||
qvirtqueue_cleanup(dev->bus, vq, t_alloc);
|
||||
}
|
||||
|
||||
static void pci_hotplug(void)
|
||||
static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtioPCIDevice *dev1 = obj;
|
||||
QVirtioPCIDevice *dev;
|
||||
QOSState *qs;
|
||||
const char *arch = qtest_get_arch();
|
||||
|
||||
qs = pci_test_start();
|
||||
|
||||
/* plug secondary disk */
|
||||
qtest_qmp_device_add("virtio-blk-pci", "drv1",
|
||||
"{'addr': %s, 'drive': 'drive1'}",
|
||||
stringify(PCI_SLOT_HP));
|
||||
stringify(PCI_SLOT_HP) ".0");
|
||||
|
||||
dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT_HP);
|
||||
g_assert(dev);
|
||||
dev = virtio_pci_new(dev1->pdev->bus,
|
||||
&(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0) });
|
||||
g_assert_nonnull(dev);
|
||||
g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
|
||||
qvirtio_pci_device_disable(dev);
|
||||
qvirtio_pci_device_free(dev);
|
||||
qos_object_destroy((QOSGraphObject *)dev);
|
||||
|
||||
/* unplug secondary disk */
|
||||
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
|
||||
qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP);
|
||||
}
|
||||
qtest_shutdown(qs);
|
||||
qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that setting the vring addr on a non-existent virtqueue does
|
||||
* not crash.
|
||||
*/
|
||||
static void test_nonexistent_virtqueue(void)
|
||||
static void test_nonexistent_virtqueue(void *obj, void *data,
|
||||
QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtioBlkPCI *blk = obj;
|
||||
QVirtioPCIDevice *pdev = &blk->pci_vdev;
|
||||
QPCIBar bar0;
|
||||
QOSState *qs;
|
||||
QPCIDevice *dev;
|
||||
|
||||
qs = pci_test_start();
|
||||
dev = qpci_device_find(qs->pcibus, QPCI_DEVFN(4, 0));
|
||||
dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0));
|
||||
g_assert(dev != NULL);
|
||||
|
||||
qpci_device_enable(dev);
|
||||
|
||||
bar0 = qpci_iomap(dev, 0, NULL);
|
||||
|
||||
qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2);
|
||||
qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1);
|
||||
|
||||
|
||||
g_free(dev);
|
||||
qtest_shutdown(qs);
|
||||
}
|
||||
|
||||
static void mmio_basic(void)
|
||||
static void resize(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtioMMIODevice *dev;
|
||||
QVirtQueue *vq;
|
||||
QGuestAllocator *alloc;
|
||||
QVirtioBlk *blk_if = obj;
|
||||
QVirtioDevice *dev = blk_if->vdev;
|
||||
int n_size = TEST_IMAGE_SIZE / 2;
|
||||
uint64_t capacity;
|
||||
QVirtQueue *vq;
|
||||
|
||||
arm_test_start();
|
||||
vq = qvirtqueue_setup(dev, t_alloc, 0);
|
||||
|
||||
dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE);
|
||||
g_assert(dev != NULL);
|
||||
g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK);
|
||||
|
||||
qvirtio_reset(&dev->vdev);
|
||||
qvirtio_set_acknowledge(&dev->vdev);
|
||||
qvirtio_set_driver(&dev->vdev);
|
||||
|
||||
alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE);
|
||||
vq = qvirtqueue_setup(&dev->vdev, alloc, 0);
|
||||
|
||||
test_basic(&dev->vdev, alloc, vq);
|
||||
test_basic(dev, t_alloc, vq);
|
||||
|
||||
qmp_discard_response("{ 'execute': 'block_resize', "
|
||||
" 'arguments': { 'device': 'drive0', "
|
||||
" 'size': %d } }", n_size);
|
||||
|
||||
qvirtio_wait_queue_isr(&dev->vdev, vq, QVIRTIO_BLK_TIMEOUT_US);
|
||||
qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US);
|
||||
|
||||
capacity = qvirtio_config_readq(&dev->vdev, 0);
|
||||
capacity = qvirtio_config_readq(dev, 0);
|
||||
g_assert_cmpint(capacity, ==, n_size / 512);
|
||||
|
||||
/* End test */
|
||||
qvirtqueue_cleanup(dev->vdev.bus, vq, alloc);
|
||||
g_free(dev);
|
||||
generic_alloc_uninit(alloc);
|
||||
test_end();
|
||||
qvirtqueue_cleanup(dev->bus, vq, t_alloc);
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void *virtio_blk_test_setup(GString *cmd_line, void *arg)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
char *tmp_path = drive_create();
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_string_append_printf(cmd_line,
|
||||
" -drive if=none,id=drive0,file=%s,format=raw "
|
||||
"-drive if=none,id=drive1,file=null-co://,format=raw ",
|
||||
tmp_path);
|
||||
|
||||
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0 ||
|
||||
strcmp(arch, "ppc64") == 0) {
|
||||
qtest_add_func("/virtio/blk/pci/basic", pci_basic);
|
||||
qtest_add_func("/virtio/blk/pci/indirect", pci_indirect);
|
||||
qtest_add_func("/virtio/blk/pci/config", pci_config);
|
||||
qtest_add_func("/virtio/blk/pci/nxvirtq", test_nonexistent_virtqueue);
|
||||
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
|
||||
qtest_add_func("/virtio/blk/pci/msix", pci_msix);
|
||||
qtest_add_func("/virtio/blk/pci/idx", pci_idx);
|
||||
}
|
||||
qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug);
|
||||
} else if (strcmp(arch, "arm") == 0) {
|
||||
qtest_add_func("/virtio/blk/mmio/basic", mmio_basic);
|
||||
}
|
||||
|
||||
return g_test_run();
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void register_virtio_blk_test(void)
|
||||
{
|
||||
QOSGraphTestOptions opts = {
|
||||
.before = virtio_blk_test_setup,
|
||||
};
|
||||
|
||||
qos_add_test("indirect", "virtio-blk", indirect, &opts);
|
||||
qos_add_test("config", "virtio-blk", config, &opts);
|
||||
qos_add_test("basic", "virtio-blk", basic, &opts);
|
||||
qos_add_test("resize", "virtio-blk", resize, &opts);
|
||||
|
||||
/* tests just for virtio-blk-pci */
|
||||
qos_add_test("msix", "virtio-blk-pci", msix, &opts);
|
||||
qos_add_test("idx", "virtio-blk-pci", idx, &opts);
|
||||
qos_add_test("nxvirtq", "virtio-blk-pci",
|
||||
test_nonexistent_virtqueue, &opts);
|
||||
qos_add_test("hotplug", "virtio-blk-pci", pci_hotplug, &opts);
|
||||
}
|
||||
|
||||
libqos_init(register_virtio_blk_test);
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* QTest testcase for VirtIO Console
|
||||
*
|
||||
* Copyright (c) 2014 SUSE LINUX Products GmbH
|
||||
*
|
||||
* 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 "libqtest.h"
|
||||
#include "libqos/virtio.h"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void console_nop(void)
|
||||
{
|
||||
global_qtest = qtest_initf("-device virtio-serial-%s,id=vser0 "
|
||||
"-device virtconsole,bus=vser0.0",
|
||||
qvirtio_get_dev_type());
|
||||
qtest_end();
|
||||
}
|
||||
|
||||
static void serialport_nop(void)
|
||||
{
|
||||
global_qtest = qtest_initf("-device virtio-serial-%s,id=vser0 "
|
||||
"-device virtserialport,bus=vser0.0",
|
||||
qvirtio_get_dev_type());
|
||||
qtest_end();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/virtio/console/nop", console_nop);
|
||||
qtest_add_func("/virtio/serialport/nop", serialport_nop);
|
||||
|
||||
return g_test_run();
|
||||
}
|
|
@ -9,18 +9,11 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "qemu/iov.h"
|
||||
#include "libqos/libqos-pc.h"
|
||||
#include "libqos/libqos-spapr.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/virtio-pci.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "hw/virtio/virtio-net.h"
|
||||
#include "standard-headers/linux/virtio_ids.h"
|
||||
#include "standard-headers/linux/virtio_ring.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio-net.h"
|
||||
|
||||
#define PCI_SLOT_HP 0x06
|
||||
#define PCI_SLOT 0x04
|
||||
|
@ -28,65 +21,8 @@
|
|||
#define QVIRTIO_NET_TIMEOUT_US (30 * 1000 * 1000)
|
||||
#define VNET_HDR_SIZE sizeof(struct virtio_net_hdr_mrg_rxbuf)
|
||||
|
||||
static void test_end(void)
|
||||
{
|
||||
qtest_end();
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot)
|
||||
{
|
||||
QVirtioPCIDevice *dev;
|
||||
|
||||
dev = qvirtio_pci_device_find(bus, VIRTIO_ID_NET);
|
||||
g_assert(dev != NULL);
|
||||
g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET);
|
||||
|
||||
qvirtio_pci_device_enable(dev);
|
||||
qvirtio_reset(&dev->vdev);
|
||||
qvirtio_set_acknowledge(&dev->vdev);
|
||||
qvirtio_set_driver(&dev->vdev);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
GCC_FMT_ATTR(1, 2)
|
||||
static QOSState *pci_test_start(const char *cmd, ...)
|
||||
{
|
||||
QOSState *qs;
|
||||
va_list ap;
|
||||
const char *arch = qtest_get_arch();
|
||||
|
||||
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
|
||||
va_start(ap, cmd);
|
||||
qs = qtest_pc_vboot(cmd, ap);
|
||||
va_end(ap);
|
||||
} else if (strcmp(arch, "ppc64") == 0) {
|
||||
va_start(ap, cmd);
|
||||
qs = qtest_spapr_vboot(cmd, ap);
|
||||
va_end(ap);
|
||||
} else {
|
||||
g_printerr("virtio-net tests are only available on x86 or ppc64\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
global_qtest = qs->qts;
|
||||
return qs;
|
||||
}
|
||||
|
||||
static void driver_init(QVirtioDevice *dev)
|
||||
{
|
||||
uint32_t features;
|
||||
|
||||
features = qvirtio_get_features(dev);
|
||||
features = features & ~(QVIRTIO_F_BAD_FEATURE |
|
||||
(1u << VIRTIO_RING_F_INDIRECT_DESC) |
|
||||
(1u << VIRTIO_RING_F_EVENT_IDX));
|
||||
qvirtio_set_features(dev, features);
|
||||
|
||||
qvirtio_set_driver_ok(dev);
|
||||
}
|
||||
|
||||
static void rx_test(QVirtioDevice *dev,
|
||||
QGuestAllocator *alloc, QVirtQueue *vq,
|
||||
int socket)
|
||||
|
@ -196,128 +132,114 @@ static void rx_stop_cont_test(QVirtioDevice *dev,
|
|||
guest_free(alloc, req_addr);
|
||||
}
|
||||
|
||||
static void send_recv_test(QVirtioDevice *dev,
|
||||
QGuestAllocator *alloc, QVirtQueue *rvq,
|
||||
QVirtQueue *tvq, int socket)
|
||||
static void send_recv_test(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
rx_test(dev, alloc, rvq, socket);
|
||||
tx_test(dev, alloc, tvq, socket);
|
||||
QVirtioNet *net_if = obj;
|
||||
QVirtioDevice *dev = net_if->vdev;
|
||||
QVirtQueue *rx = net_if->queues[0];
|
||||
QVirtQueue *tx = net_if->queues[1];
|
||||
int *sv = data;
|
||||
|
||||
rx_test(dev, t_alloc, rx, sv[0]);
|
||||
tx_test(dev, t_alloc, tx, sv[0]);
|
||||
}
|
||||
|
||||
static void stop_cont_test(QVirtioDevice *dev,
|
||||
QGuestAllocator *alloc, QVirtQueue *rvq,
|
||||
QVirtQueue *tvq, int socket)
|
||||
static void stop_cont_test(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
rx_stop_cont_test(dev, alloc, rvq, socket);
|
||||
QVirtioNet *net_if = obj;
|
||||
QVirtioDevice *dev = net_if->vdev;
|
||||
QVirtQueue *rx = net_if->queues[0];
|
||||
int *sv = data;
|
||||
|
||||
rx_stop_cont_test(dev, t_alloc, rx, sv[0]);
|
||||
}
|
||||
|
||||
static void pci_basic(gconstpointer data)
|
||||
{
|
||||
QVirtioPCIDevice *dev;
|
||||
QOSState *qs;
|
||||
QVirtQueuePCI *tx, *rx;
|
||||
void (*func) (QVirtioDevice *dev,
|
||||
QGuestAllocator *alloc,
|
||||
QVirtQueue *rvq,
|
||||
QVirtQueue *tvq,
|
||||
int socket) = data;
|
||||
int sv[2], ret;
|
||||
|
||||
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);
|
||||
g_assert_cmpint(ret, !=, -1);
|
||||
|
||||
qs = pci_test_start("-netdev socket,fd=%d,id=hs0 -device "
|
||||
"virtio-net-pci,netdev=hs0", sv[1]);
|
||||
dev = virtio_net_pci_init(qs->pcibus, PCI_SLOT);
|
||||
|
||||
rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
|
||||
tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 1);
|
||||
|
||||
driver_init(&dev->vdev);
|
||||
func(&dev->vdev, qs->alloc, &rx->vq, &tx->vq, sv[0]);
|
||||
|
||||
/* End test */
|
||||
close(sv[0]);
|
||||
qvirtqueue_cleanup(dev->vdev.bus, &tx->vq, qs->alloc);
|
||||
qvirtqueue_cleanup(dev->vdev.bus, &rx->vq, qs->alloc);
|
||||
qvirtio_pci_device_disable(dev);
|
||||
g_free(dev->pdev);
|
||||
g_free(dev);
|
||||
qtest_shutdown(qs);
|
||||
}
|
||||
|
||||
static void large_tx(gconstpointer data)
|
||||
{
|
||||
QVirtioPCIDevice *dev;
|
||||
QOSState *qs;
|
||||
QVirtQueuePCI *tx, *rx;
|
||||
QVirtQueue *vq;
|
||||
uint64_t req_addr;
|
||||
uint32_t free_head;
|
||||
size_t alloc_size = (size_t)data / 64;
|
||||
int i;
|
||||
|
||||
qs = pci_test_start("-netdev hubport,id=hp0,hubid=0 "
|
||||
"-device virtio-net-pci,netdev=hp0");
|
||||
dev = virtio_net_pci_init(qs->pcibus, PCI_SLOT);
|
||||
|
||||
rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
|
||||
tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 1);
|
||||
|
||||
driver_init(&dev->vdev);
|
||||
vq = &tx->vq;
|
||||
|
||||
/* Bypass the limitation by pointing several descriptors to a single
|
||||
* smaller area */
|
||||
req_addr = guest_alloc(qs->alloc, alloc_size);
|
||||
free_head = qvirtqueue_add(vq, req_addr, alloc_size, false, true);
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
qvirtqueue_add(vq, req_addr, alloc_size, false, i != 63);
|
||||
}
|
||||
qvirtqueue_kick(&dev->vdev, vq, free_head);
|
||||
|
||||
qvirtio_wait_used_elem(&dev->vdev, vq, free_head, NULL,
|
||||
QVIRTIO_NET_TIMEOUT_US);
|
||||
|
||||
qvirtqueue_cleanup(dev->vdev.bus, &tx->vq, qs->alloc);
|
||||
qvirtqueue_cleanup(dev->vdev.bus, &rx->vq, qs->alloc);
|
||||
qvirtio_pci_device_disable(dev);
|
||||
g_free(dev->pdev);
|
||||
g_free(dev);
|
||||
qtest_shutdown(qs);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void hotplug(void)
|
||||
static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
|
||||
qtest_start("-device virtio-net-pci");
|
||||
|
||||
qtest_qmp_device_add("virtio-net-pci", "net1",
|
||||
"{'addr': %s}", stringify(PCI_SLOT_HP));
|
||||
|
||||
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
|
||||
qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP);
|
||||
}
|
||||
|
||||
test_end();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void virtio_net_test_cleanup(void *sockets)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
#ifndef _WIN32
|
||||
qtest_add_data_func("/virtio/net/pci/basic", send_recv_test, pci_basic);
|
||||
qtest_add_data_func("/virtio/net/pci/rx_stop_cont",
|
||||
stop_cont_test, pci_basic);
|
||||
qtest_add_data_func("/virtio/net/pci/large_tx_uint_max",
|
||||
(gconstpointer)UINT_MAX, large_tx);
|
||||
qtest_add_data_func("/virtio/net/pci/large_tx_net_bufsize",
|
||||
(gconstpointer)NET_BUFSIZE, large_tx);
|
||||
#endif
|
||||
qtest_add_func("/virtio/net/pci/hotplug", hotplug);
|
||||
int *sv = sockets;
|
||||
|
||||
return g_test_run();
|
||||
close(sv[0]);
|
||||
qos_invalidate_command_line();
|
||||
close(sv[1]);
|
||||
g_free(sv);
|
||||
}
|
||||
|
||||
static void *virtio_net_test_setup(GString *cmd_line, void *arg)
|
||||
{
|
||||
int ret;
|
||||
int *sv = g_new(int, 2);
|
||||
|
||||
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);
|
||||
g_assert_cmpint(ret, !=, -1);
|
||||
|
||||
g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sv[1]);
|
||||
|
||||
g_test_queue_destroy(virtio_net_test_cleanup, sv);
|
||||
return sv;
|
||||
}
|
||||
|
||||
static void large_tx(void *obj, void *data, QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtioNet *dev = obj;
|
||||
QVirtQueue *vq = dev->queues[1];
|
||||
uint64_t req_addr;
|
||||
uint32_t free_head;
|
||||
size_t alloc_size = (size_t)data / 64;
|
||||
int i;
|
||||
|
||||
/* Bypass the limitation by pointing several descriptors to a single
|
||||
* smaller area */
|
||||
req_addr = guest_alloc(t_alloc, alloc_size);
|
||||
free_head = qvirtqueue_add(vq, req_addr, alloc_size, false, true);
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
qvirtqueue_add(vq, req_addr, alloc_size, false, i != 63);
|
||||
}
|
||||
qvirtqueue_kick(dev->vdev, vq, free_head);
|
||||
|
||||
qvirtio_wait_used_elem(dev->vdev, vq, free_head, NULL,
|
||||
QVIRTIO_NET_TIMEOUT_US);
|
||||
guest_free(t_alloc, req_addr);
|
||||
}
|
||||
|
||||
static void *virtio_net_test_setup_nosocket(GString *cmd_line, void *arg)
|
||||
{
|
||||
g_string_append(cmd_line, " -netdev hubport,hubid=0,id=hs0 ");
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void register_virtio_net_test(void)
|
||||
{
|
||||
QOSGraphTestOptions opts = {
|
||||
.before = virtio_net_test_setup,
|
||||
};
|
||||
|
||||
qos_add_test("hotplug", "virtio-pci", hotplug, &opts);
|
||||
#ifndef _WIN32
|
||||
qos_add_test("basic", "virtio-net", send_recv_test, &opts);
|
||||
qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts);
|
||||
#endif
|
||||
|
||||
/* These tests do not need a loopback backend. */
|
||||
opts.before = virtio_net_test_setup_nosocket;
|
||||
opts.arg = (gpointer)UINT_MAX;
|
||||
qos_add_test("large_tx/uint_max", "virtio-net", large_tx, &opts);
|
||||
opts.arg = (gpointer)NET_BUFSIZE;
|
||||
qos_add_test("large_tx/net_bufsize", "virtio-net", large_tx, &opts);
|
||||
}
|
||||
|
||||
libqos_init(register_virtio_net_test);
|
||||
|
|
|
@ -9,16 +9,12 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/pci.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/virtio-rng.h"
|
||||
|
||||
#define PCI_SLOT_HP 0x06
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void pci_nop(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void hotplug(void)
|
||||
static void rng_hotplug(void *obj, void *data, QGuestAllocator *alloc)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
|
||||
|
@ -30,18 +26,9 @@ static void hotplug(void)
|
|||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void register_virtio_rng_test(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/virtio/rng/pci/nop", pci_nop);
|
||||
qtest_add_func("/virtio/rng/pci/hotplug", hotplug);
|
||||
|
||||
qtest_start("-device virtio-rng-pci");
|
||||
ret = g_test_run();
|
||||
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
qos_add_test("hotplug", "virtio-rng-pci", rng_hotplug, NULL);
|
||||
}
|
||||
|
||||
libqos_init(register_virtio_rng_test);
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "standard-headers/linux/virtio_ids.h"
|
||||
#include "standard-headers/linux/virtio_pci.h"
|
||||
#include "standard-headers/linux/virtio_scsi.h"
|
||||
#include "libqos/virtio-scsi.h"
|
||||
#include "libqos/qgraph.h"
|
||||
|
||||
#define PCI_SLOT 0x02
|
||||
#define PCI_FN 0x00
|
||||
|
@ -27,55 +29,28 @@
|
|||
|
||||
typedef struct {
|
||||
QVirtioDevice *dev;
|
||||
QOSState *qs;
|
||||
int num_queues;
|
||||
QVirtQueue *vq[MAX_NUM_QUEUES + 2];
|
||||
} QVirtIOSCSI;
|
||||
} QVirtioSCSIQueues;
|
||||
|
||||
static QOSState *qvirtio_scsi_start(const char *extra_opts)
|
||||
{
|
||||
QOSState *qs;
|
||||
const char *arch = qtest_get_arch();
|
||||
const char *cmd = "-drive id=drv0,if=none,file=null-co://,format=raw "
|
||||
"-device virtio-scsi-pci,id=vs0 "
|
||||
"-device scsi-hd,bus=vs0.0,drive=drv0 %s";
|
||||
static QGuestAllocator *alloc;
|
||||
|
||||
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
|
||||
qs = qtest_pc_boot(cmd, extra_opts ? : "");
|
||||
} else if (strcmp(arch, "ppc64") == 0) {
|
||||
qs = qtest_spapr_boot(cmd, extra_opts ? : "");
|
||||
} else {
|
||||
g_printerr("virtio-scsi tests are only available on x86 or ppc64\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
global_qtest = qs->qts;
|
||||
return qs;
|
||||
}
|
||||
|
||||
static void qvirtio_scsi_stop(QOSState *qs)
|
||||
{
|
||||
qtest_shutdown(qs);
|
||||
}
|
||||
|
||||
static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs)
|
||||
static void qvirtio_scsi_pci_free(QVirtioSCSIQueues *vs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < vs->num_queues + 2; i++) {
|
||||
qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], vs->qs->alloc);
|
||||
qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], alloc);
|
||||
}
|
||||
qvirtio_pci_device_disable(container_of(vs->dev, QVirtioPCIDevice, vdev));
|
||||
qvirtio_pci_device_free((QVirtioPCIDevice *)vs->dev);
|
||||
qvirtio_scsi_stop(vs->qs);
|
||||
g_free(vs);
|
||||
}
|
||||
|
||||
static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size,
|
||||
static uint64_t qvirtio_scsi_alloc(QVirtioSCSIQueues *vs, size_t alloc_size,
|
||||
const void *data)
|
||||
{
|
||||
uint64_t addr;
|
||||
|
||||
addr = guest_alloc(vs->qs->alloc, alloc_size);
|
||||
addr = guest_alloc(alloc, alloc_size);
|
||||
if (data) {
|
||||
memwrite(addr, data, alloc_size);
|
||||
}
|
||||
|
@ -83,7 +58,8 @@ static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size,
|
|||
return addr;
|
||||
}
|
||||
|
||||
static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb,
|
||||
static uint8_t virtio_scsi_do_command(QVirtioSCSIQueues *vs,
|
||||
const uint8_t *cdb,
|
||||
const uint8_t *data_in,
|
||||
size_t data_in_len,
|
||||
uint8_t *data_out, size_t data_out_len,
|
||||
|
@ -133,42 +109,28 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb,
|
|||
memread(resp_addr, resp_out, sizeof(*resp_out));
|
||||
}
|
||||
|
||||
guest_free(vs->qs->alloc, req_addr);
|
||||
guest_free(vs->qs->alloc, resp_addr);
|
||||
guest_free(vs->qs->alloc, data_in_addr);
|
||||
guest_free(vs->qs->alloc, data_out_addr);
|
||||
guest_free(alloc, req_addr);
|
||||
guest_free(alloc, resp_addr);
|
||||
guest_free(alloc, data_in_addr);
|
||||
guest_free(alloc, data_out_addr);
|
||||
return response;
|
||||
}
|
||||
|
||||
static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot)
|
||||
static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev)
|
||||
{
|
||||
QVirtioSCSIQueues *vs;
|
||||
const uint8_t test_unit_ready_cdb[VIRTIO_SCSI_CDB_SIZE] = {};
|
||||
QVirtIOSCSI *vs;
|
||||
QVirtioPCIDevice *dev;
|
||||
struct virtio_scsi_cmd_resp resp;
|
||||
int i;
|
||||
|
||||
vs = g_new0(QVirtIOSCSI, 1);
|
||||
|
||||
vs->qs = qvirtio_scsi_start("-drive file=blkdebug::null-co://,"
|
||||
"if=none,id=dr1,format=raw,file.align=4k "
|
||||
"-device scsi-hd,drive=dr1,lun=0,scsi-id=1");
|
||||
dev = qvirtio_pci_device_find(vs->qs->pcibus, VIRTIO_ID_SCSI);
|
||||
vs->dev = (QVirtioDevice *)dev;
|
||||
g_assert(dev != NULL);
|
||||
g_assert_cmphex(vs->dev->device_type, ==, VIRTIO_ID_SCSI);
|
||||
|
||||
qvirtio_pci_device_enable(dev);
|
||||
qvirtio_reset(vs->dev);
|
||||
qvirtio_set_acknowledge(vs->dev);
|
||||
qvirtio_set_driver(vs->dev);
|
||||
|
||||
vs->num_queues = qvirtio_config_readl(vs->dev, 0);
|
||||
vs = g_new0(QVirtioSCSIQueues, 1);
|
||||
vs->dev = dev;
|
||||
vs->num_queues = qvirtio_config_readl(dev, 0);
|
||||
|
||||
g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES);
|
||||
|
||||
for (i = 0; i < vs->num_queues + 2; i++) {
|
||||
vs->vq[i] = qvirtqueue_setup(vs->dev, vs->qs->alloc, i);
|
||||
vs->vq[i] = qvirtqueue_setup(dev, alloc, i);
|
||||
}
|
||||
|
||||
/* Clear the POWER ON OCCURRED unit attention */
|
||||
|
@ -184,30 +146,18 @@ static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot)
|
|||
return vs;
|
||||
}
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void pci_nop(void)
|
||||
static void hotplug(void *obj, void *data, QGuestAllocator *alloc)
|
||||
{
|
||||
QOSState *qs;
|
||||
|
||||
qs = qvirtio_scsi_start(NULL);
|
||||
qvirtio_scsi_stop(qs);
|
||||
}
|
||||
|
||||
static void hotplug(void)
|
||||
{
|
||||
QOSState *qs;
|
||||
|
||||
qs = qvirtio_scsi_start(
|
||||
"-drive id=drv1,if=none,file=null-co://,format=raw");
|
||||
qtest_qmp_device_add("scsi-hd", "scsihd", "{'drive': 'drv1'}");
|
||||
qtest_qmp_device_del("scsihd");
|
||||
qvirtio_scsi_stop(qs);
|
||||
}
|
||||
|
||||
/* Test WRITE SAME with the lba not aligned */
|
||||
static void test_unaligned_write_same(void)
|
||||
static void test_unaligned_write_same(void *obj, void *data,
|
||||
QGuestAllocator *t_alloc)
|
||||
{
|
||||
QVirtIOSCSI *vs;
|
||||
QVirtioSCSI *scsi = obj;
|
||||
QVirtioSCSIQueues *vs;
|
||||
uint8_t buf1[512] = { 0 };
|
||||
uint8_t buf2[512] = { 1 };
|
||||
const uint8_t write_same_cdb_1[VIRTIO_SCSI_CDB_SIZE] = {
|
||||
|
@ -220,27 +170,50 @@ static void test_unaligned_write_same(void)
|
|||
0x41, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00
|
||||
};
|
||||
|
||||
vs = qvirtio_scsi_pci_init(PCI_SLOT);
|
||||
alloc = t_alloc;
|
||||
vs = qvirtio_scsi_init(scsi->vdev);
|
||||
|
||||
g_assert_cmphex(0, ==,
|
||||
virtio_scsi_do_command(vs, write_same_cdb_1, NULL, 0, buf1, 512, NULL));
|
||||
virtio_scsi_do_command(vs, write_same_cdb_1, NULL, 0, buf1, 512,
|
||||
NULL));
|
||||
|
||||
g_assert_cmphex(0, ==,
|
||||
virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, NULL));
|
||||
virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512,
|
||||
NULL));
|
||||
|
||||
g_assert_cmphex(0, ==,
|
||||
virtio_scsi_do_command(vs, write_same_cdb_ndob, NULL, 0, NULL, 0, NULL));
|
||||
virtio_scsi_do_command(vs, write_same_cdb_ndob, NULL, 0, NULL, 0,
|
||||
NULL));
|
||||
|
||||
qvirtio_scsi_pci_free(vs);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void *virtio_scsi_hotplug_setup(GString *cmd_line, void *arg)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/virtio/scsi/pci/nop", pci_nop);
|
||||
qtest_add_func("/virtio/scsi/pci/hotplug", hotplug);
|
||||
qtest_add_func("/virtio/scsi/pci/scsi-disk/unaligned-write-same",
|
||||
test_unaligned_write_same);
|
||||
|
||||
return g_test_run();
|
||||
g_string_append(cmd_line,
|
||||
" -drive id=drv1,if=none,file=null-co://,format=raw");
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void *virtio_scsi_setup(GString *cmd_line, void *arg)
|
||||
{
|
||||
g_string_append(cmd_line,
|
||||
" -drive file=blkdebug::null-co://,"
|
||||
"if=none,id=dr1,format=raw,file.align=4k "
|
||||
"-device scsi-hd,drive=dr1,lun=0,scsi-id=1");
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void register_virtio_scsi_test(void)
|
||||
{
|
||||
QOSGraphTestOptions opts = { };
|
||||
|
||||
opts.before = virtio_scsi_hotplug_setup;
|
||||
qos_add_test("hotplug", "virtio-scsi", hotplug, &opts);
|
||||
|
||||
opts.before = virtio_scsi_setup;
|
||||
qos_add_test("unaligned-write-same", "virtio-scsi",
|
||||
test_unaligned_write_same, &opts);
|
||||
}
|
||||
|
||||
libqos_init(register_virtio_scsi_test);
|
||||
|
|
|
@ -9,33 +9,30 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/virtio.h"
|
||||
#include "libqos/virtio-serial.h"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void virtio_serial_nop(void)
|
||||
static void virtio_serial_nop(void *obj, void *data, QGuestAllocator *alloc)
|
||||
{
|
||||
/* no operation */
|
||||
}
|
||||
|
||||
static void hotplug(void)
|
||||
static void serial_hotplug(void *obj, void *data, QGuestAllocator *alloc)
|
||||
{
|
||||
qtest_qmp_device_add("virtserialport", "hp-port", "{}");
|
||||
|
||||
qtest_qmp_device_del("hp-port");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void register_virtio_serial_test(void)
|
||||
{
|
||||
int ret;
|
||||
QOSGraphTestOptions opts = { };
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/virtio/serial/nop", virtio_serial_nop);
|
||||
qtest_add_func("/virtio/serial/hotplug", hotplug);
|
||||
opts.edge.before_cmd_line = "-device virtconsole,bus=vser0.0";
|
||||
qos_add_test("console-nop", "virtio-serial", virtio_serial_nop, &opts);
|
||||
|
||||
global_qtest = qtest_initf("-device virtio-serial-%s",
|
||||
qvirtio_get_dev_type());
|
||||
ret = g_test_run();
|
||||
opts.edge.before_cmd_line = "-device virtserialport,bus=vser0.0";
|
||||
qos_add_test("serialport-nop", "virtio-serial", virtio_serial_nop, &opts);
|
||||
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
qos_add_test("hotplug", "virtio-serial", serial_hotplug, NULL);
|
||||
}
|
||||
libqos_init(register_virtio_serial_test);
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* QTest testcase for virtio
|
||||
*
|
||||
* Copyright (c) 2018 Red Hat, Inc.
|
||||
*
|
||||
* 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 "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void nop(void *obj, void *data, QGuestAllocator *alloc)
|
||||
{
|
||||
}
|
||||
|
||||
static void register_virtio_test(void)
|
||||
{
|
||||
qos_add_test("nop", "virtio", nop, NULL);
|
||||
}
|
||||
|
||||
libqos_init(register_virtio_test);
|
|
@ -9,23 +9,49 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "libqtest.h"
|
||||
#include "libqos/qgraph.h"
|
||||
#include "libqos/pci.h"
|
||||
|
||||
/* Tests only initialization so far. TODO: Replace with functional tests */
|
||||
static void nop(void)
|
||||
typedef struct QVmxnet3 QVmxnet3;
|
||||
|
||||
struct QVmxnet3 {
|
||||
QOSGraphObject obj;
|
||||
QPCIDevice dev;
|
||||
};
|
||||
|
||||
static void *vmxnet3_get_driver(void *obj, const char *interface)
|
||||
{
|
||||
QVmxnet3 *vmxnet3 = obj;
|
||||
|
||||
if (!g_strcmp0(interface, "pci-device")) {
|
||||
return &vmxnet3->dev;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in vmxnet3\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static void *vmxnet3_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
|
||||
{
|
||||
int ret;
|
||||
QVmxnet3 *vmxnet3 = g_new0(QVmxnet3, 1);
|
||||
QPCIBus *bus = pci_bus;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/vmxnet3/nop", nop);
|
||||
qpci_device_init(&vmxnet3->dev, bus, addr);
|
||||
vmxnet3->obj.get_driver = vmxnet3_get_driver;
|
||||
|
||||
qtest_start("-device vmxnet3");
|
||||
ret = g_test_run();
|
||||
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
return &vmxnet3->obj;
|
||||
}
|
||||
|
||||
static void vmxnet3_register_nodes(void)
|
||||
{
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.extra_device_opts = "addr=04.0",
|
||||
};
|
||||
add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
|
||||
|
||||
qos_node_create_driver("vmxnet3", vmxnet3_create);
|
||||
qos_node_consumes("vmxnet3", "pci-bus", &opts);
|
||||
qos_node_produces("vmxnet3", "pci-device");
|
||||
}
|
||||
|
||||
libqos_init(vmxnet3_register_nodes);
|
||||
|
|
Loading…
Reference in New Issue