summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml6
-rw-r--r--Kconfig.host33
-rw-r--r--Makefile35
-rw-r--r--Makefile.objs1
-rw-r--r--Makefile.target12
-rw-r--r--chardev/char-socket.c2
-rwxr-xr-xconfigure109
-rw-r--r--default-configs/alpha-softmmu.mak26
-rw-r--r--default-configs/arm-softmmu.mak18
-rw-r--r--default-configs/cris-softmmu.mak6
-rw-r--r--default-configs/hppa-softmmu.mak20
-rw-r--r--default-configs/hyperv.mak2
-rw-r--r--default-configs/i386-softmmu.mak93
-rw-r--r--default-configs/lm32-softmmu.mak12
-rw-r--r--default-configs/m68k-softmmu.mak4
-rw-r--r--default-configs/microblaze-softmmu.mak12
-rw-r--r--default-configs/mips-softmmu-common.mak10
-rw-r--r--default-configs/mips64el-softmmu.mak2
-rw-r--r--default-configs/moxie-softmmu.mak7
-rw-r--r--default-configs/nios2-softmmu.mak6
-rw-r--r--default-configs/or1k-softmmu.mak5
-rw-r--r--default-configs/pci.mak51
-rw-r--r--default-configs/ppc-softmmu.mak60
-rw-r--r--default-configs/ppc64-softmmu.mak13
-rw-r--r--default-configs/riscv32-softmmu.mak21
-rw-r--r--default-configs/riscv64-softmmu.mak22
-rw-r--r--default-configs/s390x-softmmu.mak23
-rw-r--r--default-configs/sh4-softmmu.mak28
-rw-r--r--default-configs/sh4eb-softmmu.mak22
-rw-r--r--default-configs/sound.mak4
-rw-r--r--default-configs/sparc-softmmu.mak24
-rw-r--r--default-configs/sparc64-softmmu.mak25
-rw-r--r--default-configs/unicore32-softmmu.mak6
-rw-r--r--default-configs/usb.mak11
-rw-r--r--default-configs/virtio.mak15
-rw-r--r--default-configs/xtensa-softmmu.mak8
-rw-r--r--default-configs/xtensaeb-softmmu.mak7
-rw-r--r--docs/devel/build-system.txt1
-rw-r--r--docs/devel/index.rst1
-rw-r--r--docs/devel/kconfig.rst306
-rw-r--r--hw/9pfs/Kconfig4
-rw-r--r--hw/9pfs/Makefile.objs2
-rw-r--r--hw/Kconfig73
-rw-r--r--hw/Makefile.objs4
-rw-r--r--hw/acpi/Kconfig29
-rw-r--r--hw/adc/Kconfig2
-rw-r--r--hw/alpha/Kconfig12
-rw-r--r--hw/arm/Kconfig124
-rw-r--r--hw/audio/Kconfig52
-rw-r--r--hw/block/Kconfig39
-rw-r--r--hw/block/Makefile.objs3
-rw-r--r--hw/block/dataplane/Makefile.objs2
-rw-r--r--hw/bt/Kconfig2
-rw-r--r--hw/char/Kconfig42
-rw-r--r--hw/core/Kconfig11
-rw-r--r--hw/cpu/Kconfig8
-rw-r--r--hw/cris/Kconfig9
-rw-r--r--hw/display/Kconfig108
-rw-r--r--hw/display/Makefile.objs4
-rw-r--r--hw/dma/Kconfig21
-rw-r--r--hw/gpio/Kconfig9
-rw-r--r--hw/hppa/Kconfig10
-rw-r--r--hw/hyperv/Kconfig8
-rw-r--r--hw/i2c/Kconfig27
-rw-r--r--hw/i2c/Makefile.objs2
-rw-r--r--hw/i386/Kconfig99
-rw-r--r--hw/i386/Makefile.objs5
-rw-r--r--hw/ide/Kconfig54
-rw-r--r--hw/input/Kconfig33
-rw-r--r--hw/intc/Kconfig57
-rw-r--r--hw/ipack/Kconfig4
-rw-r--r--hw/ipmi/Kconfig22
-rw-r--r--hw/isa/Kconfig53
-rw-r--r--hw/lm32/Kconfig13
-rw-r--r--hw/m68k/Kconfig9
-rw-r--r--hw/mem/Kconfig11
-rw-r--r--hw/microblaze/Kconfig20
-rw-r--r--hw/mips/Kconfig21
-rw-r--r--hw/misc/Kconfig118
-rw-r--r--hw/misc/macio/Kconfig11
-rw-r--r--hw/moxie/Kconfig3
-rw-r--r--hw/net/Kconfig125
-rw-r--r--hw/nios2/Kconfig8
-rw-r--r--hw/nvram/Kconfig9
-rw-r--r--hw/openrisc/Kconfig5
-rw-r--r--hw/pci-bridge/Kconfig29
-rw-r--r--hw/pci-host/Kconfig51
-rw-r--r--hw/pci/Kconfig9
-rw-r--r--hw/pci/Makefile.objs9
-rw-r--r--hw/pcmcia/Kconfig2
-rw-r--r--hw/ppc/Kconfig121
-rw-r--r--hw/riscv/Kconfig33
-rw-r--r--hw/s390x/Kconfig11
-rw-r--r--hw/s390x/Makefile.objs4
-rw-r--r--hw/scsi/Kconfig54
-rw-r--r--hw/scsi/Makefile.objs2
-rw-r--r--hw/sd/Kconfig17
-rw-r--r--hw/sh4/Kconfig23
-rw-r--r--hw/smbios/Kconfig2
-rw-r--r--hw/sparc/Kconfig26
-rw-r--r--hw/sparc64/Kconfig19
-rw-r--r--hw/ssi/Kconfig18
-rw-r--r--hw/timer/Kconfig63
-rw-r--r--hw/tpm/Kconfig24
-rw-r--r--hw/tricore/Kconfig2
-rw-r--r--hw/unicore32/Kconfig5
-rw-r--r--hw/usb/Kconfig91
-rw-r--r--hw/usb/Makefile.objs2
-rw-r--r--hw/vfio/Kconfig36
-rw-r--r--hw/virtio/Kconfig31
-rw-r--r--hw/virtio/Makefile.objs2
-rw-r--r--hw/watchdog/Kconfig16
-rw-r--r--hw/xtensa/Kconfig8
-rw-r--r--hw/xtensa/Makefile.objs2
-rw-r--r--include/migration/qemu-file-types.h2
-rw-r--r--include/qemu/module.h2
-rw-r--r--linux-user/elfload.c37
-rw-r--r--linux-user/fd-trans.c9
-rw-r--r--linux-user/nios2/cpu_loop.c6
-rw-r--r--linux-user/strace.c12
-rw-r--r--linux-user/strace.list2
-rw-r--r--linux-user/syscall.c53
-rw-r--r--migration/qemu-file.h1
-rw-r--r--net/Makefile.objs2
-rw-r--r--net/slirp.c58
-rw-r--r--rules.mak2
-rw-r--r--scripts/make_device_config.sh30
-rw-r--r--scripts/minikconf.py708
-rw-r--r--slirp/Makefile47
-rw-r--r--slirp/Makefile.objs34
-rw-r--r--slirp/src/arp_table.c (renamed from slirp/arp_table.c)0
-rw-r--r--slirp/src/bootp.c (renamed from slirp/bootp.c)0
-rw-r--r--slirp/src/bootp.h (renamed from slirp/bootp.h)0
-rw-r--r--slirp/src/cksum.c (renamed from slirp/cksum.c)0
-rw-r--r--slirp/src/debug.h (renamed from slirp/debug.h)0
-rw-r--r--slirp/src/dhcpv6.c (renamed from slirp/dhcpv6.c)0
-rw-r--r--slirp/src/dhcpv6.h (renamed from slirp/dhcpv6.h)0
-rw-r--r--slirp/src/dnssearch.c (renamed from slirp/dnssearch.c)0
-rw-r--r--slirp/src/if.c (renamed from slirp/if.c)0
-rw-r--r--slirp/src/if.h (renamed from slirp/if.h)0
-rw-r--r--slirp/src/ip.h (renamed from slirp/ip.h)0
-rw-r--r--slirp/src/ip6.h (renamed from slirp/ip6.h)0
-rw-r--r--slirp/src/ip6_icmp.c (renamed from slirp/ip6_icmp.c)0
-rw-r--r--slirp/src/ip6_icmp.h (renamed from slirp/ip6_icmp.h)0
-rw-r--r--slirp/src/ip6_input.c (renamed from slirp/ip6_input.c)0
-rw-r--r--slirp/src/ip6_output.c (renamed from slirp/ip6_output.c)0
-rw-r--r--slirp/src/ip_icmp.c (renamed from slirp/ip_icmp.c)0
-rw-r--r--slirp/src/ip_icmp.h (renamed from slirp/ip_icmp.h)0
-rw-r--r--slirp/src/ip_input.c (renamed from slirp/ip_input.c)0
-rw-r--r--slirp/src/ip_output.c (renamed from slirp/ip_output.c)0
-rw-r--r--slirp/src/libslirp.h (renamed from slirp/libslirp.h)10
-rw-r--r--slirp/src/main.h (renamed from slirp/main.h)0
-rw-r--r--slirp/src/mbuf.c (renamed from slirp/mbuf.c)0
-rw-r--r--slirp/src/mbuf.h (renamed from slirp/mbuf.h)0
-rw-r--r--slirp/src/misc.c (renamed from slirp/misc.c)3
-rw-r--r--slirp/src/misc.h (renamed from slirp/misc.h)0
-rw-r--r--slirp/src/ncsi-pkt.h (renamed from slirp/ncsi-pkt.h)0
-rw-r--r--slirp/src/ncsi.c (renamed from slirp/ncsi.c)0
-rw-r--r--slirp/src/ndp_table.c (renamed from slirp/ndp_table.c)0
-rw-r--r--slirp/src/qtailq.h (renamed from slirp/qtailq.h)0
-rw-r--r--slirp/src/sbuf.c (renamed from slirp/sbuf.c)0
-rw-r--r--slirp/src/sbuf.h (renamed from slirp/sbuf.h)0
-rw-r--r--slirp/src/slirp.c (renamed from slirp/slirp.c)14
-rw-r--r--slirp/src/slirp.h (renamed from slirp/slirp.h)2
-rw-r--r--slirp/src/socket.c (renamed from slirp/socket.c)11
-rw-r--r--slirp/src/socket.h (renamed from slirp/socket.h)0
-rw-r--r--slirp/src/state.c (renamed from slirp/state.c)52
-rw-r--r--slirp/src/state.h0
-rw-r--r--slirp/src/stream.c119
-rw-r--r--slirp/src/stream.h34
-rw-r--r--slirp/src/tcp.h (renamed from slirp/tcp.h)0
-rw-r--r--slirp/src/tcp_input.c (renamed from slirp/tcp_input.c)2
-rw-r--r--slirp/src/tcp_output.c (renamed from slirp/tcp_output.c)0
-rw-r--r--slirp/src/tcp_subr.c (renamed from slirp/tcp_subr.c)16
-rw-r--r--slirp/src/tcp_timer.c (renamed from slirp/tcp_timer.c)0
-rw-r--r--slirp/src/tcp_timer.h (renamed from slirp/tcp_timer.h)0
-rw-r--r--slirp/src/tcp_var.h (renamed from slirp/tcp_var.h)0
-rw-r--r--slirp/src/tcpip.h (renamed from slirp/tcpip.h)0
-rw-r--r--slirp/src/tftp.c (renamed from slirp/tftp.c)0
-rw-r--r--slirp/src/tftp.h (renamed from slirp/tftp.h)0
-rw-r--r--slirp/src/udp.c (renamed from slirp/udp.c)1
-rw-r--r--slirp/src/udp.h (renamed from slirp/udp.h)0
-rw-r--r--slirp/src/udp6.c (renamed from slirp/udp6.c)0
-rw-r--r--slirp/src/util.c (renamed from slirp/util.c)4
-rw-r--r--slirp/src/util.h (renamed from slirp/util.h)4
-rw-r--r--slirp/src/vmstate.c413
-rw-r--r--slirp/src/vmstate.h396
-rw-r--r--slirp/state.h9
-rw-r--r--target/hppa/translate.c21
-rw-r--r--tests/Makefile.include129
-rw-r--r--tests/ac97-test.c47
-rw-r--r--tests/ahci-test.c6
-rw-r--r--tests/drive_del-test.c25
-rw-r--r--tests/e1000-test.c64
-rw-r--r--tests/e1000e-test.c358
-rw-r--r--tests/eepro100-test.c65
-rw-r--r--tests/es1370-test.c46
-rw-r--r--tests/i440fx-test.c2
-rw-r--r--tests/ide-test.c19
-rw-r--r--tests/ipoctal232-test.c35
-rw-r--r--tests/libqos/aarch64-xlnx-zcu102-machine.c94
-rw-r--r--tests/libqos/ahci.c2
-rw-r--r--tests/libqos/arm-raspi2-machine.c91
-rw-r--r--tests/libqos/arm-sabrelite-machine.c91
-rw-r--r--tests/libqos/arm-smdkc210-machine.c91
-rw-r--r--tests/libqos/arm-virt-machine.c90
-rw-r--r--tests/libqos/arm-xilinx-zynq-a9-machine.c94
-rw-r--r--tests/libqos/e1000e.c260
-rw-r--r--tests/libqos/e1000e.h53
-rw-r--r--tests/libqos/libqos-pc.c5
-rw-r--r--tests/libqos/libqos-spapr.c5
-rw-r--r--tests/libqos/libqos.c13
-rw-r--r--tests/libqos/libqos.h13
-rw-r--r--tests/libqos/malloc-generic.c39
-rw-r--r--tests/libqos/malloc-generic.h21
-rw-r--r--tests/libqos/malloc-pc.c22
-rw-r--r--tests/libqos/malloc-pc.h4
-rw-r--r--tests/libqos/malloc-spapr.c19
-rw-r--r--tests/libqos/malloc-spapr.h4
-rw-r--r--tests/libqos/malloc.c42
-rw-r--r--tests/libqos/malloc.h21
-rw-r--r--tests/libqos/pci-pc.c86
-rw-r--r--tests/libqos/pci-pc.h29
-rw-r--r--tests/libqos/pci-spapr.c119
-rw-r--r--tests/libqos/pci-spapr.h26
-rw-r--r--tests/libqos/pci.c46
-rw-r--r--tests/libqos/pci.h16
-rw-r--r--tests/libqos/ppc64_pseries-machine.c111
-rw-r--r--tests/libqos/qgraph.c753
-rw-r--r--tests/libqos/qgraph.h575
-rw-r--r--tests/libqos/qgraph_internal.h257
-rw-r--r--tests/libqos/sdhci.c163
-rw-r--r--tests/libqos/sdhci.h70
-rw-r--r--tests/libqos/tpci200.c65
-rw-r--r--tests/libqos/virtio-9p.c173
-rw-r--r--tests/libqos/virtio-9p.h42
-rw-r--r--tests/libqos/virtio-balloon.c113
-rw-r--r--tests/libqos/virtio-balloon.h39
-rw-r--r--tests/libqos/virtio-blk.c124
-rw-r--r--tests/libqos/virtio-blk.h40
-rw-r--r--tests/libqos/virtio-mmio.c116
-rw-r--r--tests/libqos/virtio-mmio.h6
-rw-r--r--tests/libqos/virtio-net.c195
-rw-r--r--tests/libqos/virtio-net.h41
-rw-r--r--tests/libqos/virtio-pci.c187
-rw-r--r--tests/libqos/virtio-pci.h18
-rw-r--r--tests/libqos/virtio-rng.c110
-rw-r--r--tests/libqos/virtio-rng.h39
-rw-r--r--tests/libqos/virtio-scsi.c117
-rw-r--r--tests/libqos/virtio-scsi.h39
-rw-r--r--tests/libqos/virtio-serial.c110
-rw-r--r--tests/libqos/virtio-serial.h39
-rw-r--r--tests/libqos/virtio.c24
-rw-r--r--tests/libqos/virtio.h11
-rw-r--r--tests/libqos/x86_64_pc-machine.c114
-rw-r--r--tests/libqtest.h6
-rw-r--r--tests/megasas-test.c80
-rw-r--r--tests/ne2000-test.c46
-rw-r--r--tests/nvme-test.c78
-rw-r--r--tests/pci-test.c25
-rw-r--r--tests/pcnet-test.c46
-rw-r--r--tests/q35-test.c4
-rw-r--r--tests/qos-test.c445
-rw-r--r--tests/rtas-test.c2
-rw-r--r--tests/rtl8139-test.c2
-rw-r--r--tests/sdhci-test.c185
-rw-r--r--tests/spapr-phb-test.c32
-rw-r--r--tests/tco-test.c2
-rw-r--r--tests/test-char.c102
-rw-r--r--tests/test-qgraph.c434
-rw-r--r--tests/tpci200-test.c31
-rw-r--r--tests/usb-hcd-ehci-test.c2
-rw-r--r--tests/usb-hcd-ohci-test.c54
-rw-r--r--tests/vhost-user-test.c385
-rw-r--r--tests/virtio-9p-test.c220
-rw-r--r--tests/virtio-balloon-test.c33
-rw-r--r--tests/virtio-blk-test.c471
-rw-r--r--tests/virtio-console-test.c38
-rw-r--r--tests/virtio-net-test.c226
-rw-r--r--tests/virtio-rng-test.c27
-rw-r--r--tests/virtio-scsi-test.c145
-rw-r--r--tests/virtio-serial-test.c27
-rw-r--r--tests/virtio-test.c25
-rw-r--r--tests/vmxnet3-test.c46
-rw-r--r--util/Makefile.objs1
-rw-r--r--util/main-loop.c2
-rw-r--r--vl.c3
287 files changed, 11413 insertions, 2879 deletions
diff --git a/.travis.yml b/.travis.yml
index cca57f4314..e942175dd3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -151,6 +151,12 @@ matrix:
# We manually include builds which we disable "make check" for
- env:
+ - CONFIG="--without-default-devices"
+ - TEST_CMD=""
+
+
+ # We manually include builds which we disable "make check" for
+ - env:
- CONFIG="--enable-debug --enable-tcg-interpreter"
- TEST_CMD=""
diff --git a/Kconfig.host b/Kconfig.host
new file mode 100644
index 0000000000..add5b179f7
--- /dev/null
+++ b/Kconfig.host
@@ -0,0 +1,33 @@
+# These are "proxy" symbols used to pass config-host.mak values
+# down to Kconfig. See also MINIKCONF_ARGS in the Makefile:
+# these two need to be kept in sync.
+
+config KVM
+ bool
+
+config LINUX
+ bool
+
+config OPENGL
+ bool
+
+config X11
+ bool
+
+config SPICE
+ bool
+
+config IVSHMEM
+ bool
+
+config TPM
+ bool
+
+config VHOST_USER
+ bool
+
+config XEN
+ bool
+
+config VIRTFS
+ bool
diff --git a/Makefile b/Makefile
index cad585b4d6..d2463c9237 100644
--- a/Makefile
+++ b/Makefile
@@ -327,8 +327,8 @@ DOCS=
endif
SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR)
-SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
-SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS))
+SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(filter %-softmmu, $(TARGET_DIRS)))
+SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %.d, $(SUBDIR_DEVICES_MAK))
ifeq ($(SUBDIR_DEVICES_MAK),)
config-all-devices.mak:
@@ -343,9 +343,26 @@ endif
-include $(SUBDIR_DEVICES_MAK_DEP)
-%/config-devices.mak: default-configs/%.mak $(SRC_PATH)/scripts/make_device_config.sh
- $(call quiet-command, \
- $(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $< $*-config-devices.mak.d $@ > $@.tmp,"GEN","$@.tmp")
+# This has to be kept in sync with Kconfig.host.
+MINIKCONF_ARGS = \
+ $(CONFIG_MINIKCONF_MODE) \
+ $@ $*-config.devices.mak.d $< $(MINIKCONF_INPUTS) \
+ CONFIG_KVM=$(CONFIG_KVM) \
+ CONFIG_SPICE=$(CONFIG_SPICE) \
+ CONFIG_IVSHMEM=$(CONFIG_IVSHMEM) \
+ CONFIG_TPM=$(CONFIG_TPM) \
+ CONFIG_XEN=$(CONFIG_XEN) \
+ CONFIG_OPENGL=$(CONFIG_OPENGL) \
+ CONFIG_X11=$(CONFIG_X11) \
+ CONFIG_VHOST_USER=$(CONFIG_VHOST_USER) \
+ CONFIG_VIRTFS=$(CONFIG_VIRTFS) \
+ CONFIG_LINUX=$(CONFIG_LINUX)
+
+MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig
+MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \
+
+$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_INPUTS) $(BUILD_DIR)/config-host.mak
+ $(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) > $@.tmp, "GEN", "$@.tmp")
$(call quiet-command, if test -f $@; then \
if cmp -s $@.old $@; then \
mv $@.tmp $@; \
@@ -397,8 +414,7 @@ dummy := $(call unnest-vars,, \
ui-obj-m \
audio-obj-y \
audio-obj-m \
- trace-obj-y \
- slirp-obj-y)
+ trace-obj-y)
include $(SRC_PATH)/tests/Makefile.include
@@ -457,7 +473,10 @@ CAP_CFLAGS += -DCAPSTONE_HAS_X86
subdir-capstone: .git-submodule-status
$(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE))
-$(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) $(slirp-obj-y) \
+subdir-slirp: .git-submodule-status
+ $(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp BUILD_DIR="$(BUILD_DIR)/slirp" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(QEMU_CFLAGS)")
+
+$(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) \
$(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY))
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
diff --git a/Makefile.objs b/Makefile.objs
index 6e91ee5674..ef65a6c12e 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -4,7 +4,6 @@ stub-obj-y = stubs/ util/ crypto/
util-obj-y = util/ qobject/ qapi/
chardev-obj-y = chardev/
-slirp-obj-$(CONFIG_SLIRP) = slirp/
#######################################################################
# authz-obj-y is code used by both qemu system emulation and qemu-img
diff --git a/Makefile.target b/Makefile.target
index 3b79e7074c..40830c5646 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -4,9 +4,12 @@ BUILD_DIR?=$(CURDIR)/..
include ../config-host.mak
include config-target.mak
-include config-devices.mak
include $(SRC_PATH)/rules.mak
+ifdef CONFIG_SOFTMMU
+include config-devices.mak
+endif
+
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
ifdef CONFIG_LINUX
QEMU_CFLAGS += -I../linux-headers
@@ -174,7 +177,6 @@ target-obj-y :=
block-obj-y :=
common-obj-y :=
chardev-obj-y :=
-slirp-obj-y :=
include $(SRC_PATH)/Makefile.objs
dummy := $(call unnest-vars,,target-obj-y)
target-obj-y-save := $(target-obj-y)
@@ -188,8 +190,7 @@ dummy := $(call unnest-vars,.., \
qom-obj-y \
io-obj-y \
common-obj-y \
- common-obj-m \
- slirp-obj-y)
+ common-obj-m)
target-obj-y := $(target-obj-y-save)
all-obj-y += $(common-obj-y)
all-obj-y += $(target-obj-y)
@@ -199,9 +200,10 @@ all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
-all-obj-$(CONFIG_SOFTMMU) += $(slirp-obj-y)
+ifdef CONFIG_SOFTMMU
$(QEMU_PROG_BUILD): config-devices.mak
+endif
COMMON_LDADDS = ../libqemuutil.a
diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index 4fcdd8aedd..6d287babfb 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -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
diff --git a/configure b/configure
index b970bc4356..b354e74185 100755
--- a/configure
+++ b/configure
@@ -406,7 +406,7 @@ includedir="\${prefix}/include"
sysconfdir="\${prefix}/etc"
local_statedir="\${prefix}/var"
confsuffix="/qemu"
-slirp="yes"
+slirp=""
oss_lib=""
bsd="no"
linux="no"
@@ -466,7 +466,7 @@ gcrypt_hmac="no"
auth_pam=""
vte=""
virglrenderer=""
-tpm="yes"
+tpm=""
libssh2=""
live_block_migration="yes"
numa=""
@@ -487,7 +487,7 @@ libxml2=""
docker="no"
debug_mutex="no"
libpmem=""
-libudev="no"
+default_devices="yes"
# cross compilers defaults, can be overridden with --cross-cc-ARCH
cross_cc_aarch64="aarch64-linux-gnu-gcc"
@@ -996,6 +996,10 @@ for opt do
;;
--with-trace-file=*) trace_file="$optarg"
;;
+ --with-default-devices) default_devices="yes"
+ ;;
+ --without-default-devices) default_devices="no"
+ ;;
--enable-gprof) gprof="yes"
;;
--enable-gcov) gcov="yes"
@@ -1105,6 +1109,10 @@ for opt do
;;
--disable-slirp) slirp="no"
;;
+ --enable-slirp=git) slirp="git"
+ ;;
+ --enable-slirp=system) slirp="system"
+ ;;
--disable-vde) vde="no"
;;
--enable-vde) vde="yes"
@@ -3873,20 +3881,20 @@ EOF
fi
##########################################
-# TPM passthrough is only on x86 Linux
+# TPM emulation is only on POSIX
-if test "$targetos" = Linux && { test "$cpu" = i386 || test "$cpu" = x86_64; }; then
- tpm_passthrough=$tpm
-else
- tpm_passthrough=no
+if test "$tpm" = ""; then
+ if test "$mingw32" = "yes"; then
+ tpm=no
+ else
+ tpm=yes
+ fi
+elif test "$tpm" = "yes"; then
+ if test "$mingw32" = "yes" ; then
+ error_exit "TPM emulation only available on POSIX systems"
+ fi
fi
-# TPM emulator is for all posix systems
-if test "$mingw32" != "yes"; then
- tpm_emulator=$tpm
-else
- tpm_emulator=no
-fi
##########################################
# attr probe
@@ -5766,6 +5774,55 @@ if test "$libpmem" != "no"; then
fi
##########################################
+# check for slirp
+
+case "$slirp" in
+ "" | yes)
+ if $pkg_config slirp; then
+ slirp=system
+ elif test -e "${source_path}/.git" && test $git_update = 'yes' ; then
+ slirp=git
+ elif test -e "${source_path}/slirp/Makefile" ; then
+ slirp=internal
+ elif test -z "$slirp" ; then
+ slirp=no
+ else
+ feature_not_found "slirp" "Install slirp devel or git submodule"
+ fi
+ ;;
+
+ system)
+ if ! $pkg_config slirp; then
+ feature_not_found "slirp" "Install slirp devel"
+ fi
+ ;;
+esac
+
+case "$slirp" in
+ git | internal)
+ if test "$slirp" = git; then
+ git_submodules="${git_submodules} slirp"
+ fi
+ mkdir -p slirp
+ slirp_cflags="-I\$(SRC_PATH)/slirp/src -I\$(BUILD_DIR)/slirp/src"
+ slirp_libs="-L\$(BUILD_DIR)/slirp -lslirp"
+ ;;
+
+ system)
+ slirp_version=$($pkg_config --modversion slirp 2>/dev/null)
+ slirp_cflags=$($pkg_config --cflags slirp 2>/dev/null)
+ slirp_libs=$($pkg_config --libs slirp 2>/dev/null)
+ ;;
+
+ no)
+ ;;
+ *)
+ error_exit "Unknown state for slirp: $slirp"
+ ;;
+esac
+
+
+##########################################
# End of CC checks
# After here, no more $cc or $ld runs
@@ -6122,7 +6179,8 @@ echo "QEMU_LDFLAGS $QEMU_LDFLAGS"
echo "make $make"
echo "install $install"
echo "python $python ($python_version)"
-if test "$slirp" = "yes" ; then
+echo "slirp support $slirp $(echo_version $slirp $slirp_version)"
+if test "$slirp" != "no" ; then
echo "smbd $smbd"
fi
echo "module support $modules"
@@ -6261,6 +6319,7 @@ echo "capstone $capstone"
echo "docker $docker"
echo "libpmem support $libpmem"
echo "libudev $libudev"
+echo "default devices $default_devices"
if test "$supported_cpu" = "no"; then
echo
@@ -6322,6 +6381,11 @@ echo "GIT_UPDATE=$git_update" >> $config_host_mak
echo "ARCH=$ARCH" >> $config_host_mak
+if test "$default_devices" = "yes" ; then
+ echo "CONFIG_MINIKCONF_MODE=--defconfig" >> $config_host_mak
+else
+ echo "CONFIG_MINIKCONF_MODE=--allnoconfig" >> $config_host_mak
+fi
if test "$debug_tcg" = "yes" ; then
echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak
fi
@@ -6383,9 +6447,14 @@ fi
if test "$profiler" = "yes" ; then
echo "CONFIG_PROFILER=y" >> $config_host_mak
fi
-if test "$slirp" = "yes" ; then
+if test "$slirp" != "no"; then
echo "CONFIG_SLIRP=y" >> $config_host_mak
echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak
+ echo "SLIRP_CFLAGS=$slirp_cflags" >> $config_host_mak
+ echo "SLIRP_LIBS=$slirp_libs" >> $config_host_mak
+fi
+if [ "$slirp" = "git" -o "$slirp" = "internal" ]; then
+ echo "config-host.h: subdir-slirp" >> $config_host_mak
fi
if test "$vde" = "yes" ; then
echo "CONFIG_VDE=y" >> $config_host_mak
@@ -7438,12 +7507,18 @@ fi
if supported_xen_target $target; then
echo "CONFIG_XEN=y" >> $config_target_mak
+ echo "$target/config-devices.mak: CONFIG_XEN=y" >> $config_host_mak
if test "$xen_pci_passthrough" = yes; then
echo "CONFIG_XEN_PCI_PASSTHROUGH=y" >> "$config_target_mak"
fi
+else
+ echo "$target/config-devices.mak: CONFIG_XEN=n" >> $config_host_mak
fi
if supported_kvm_target $target; then
echo "CONFIG_KVM=y" >> $config_target_mak
+ echo "$target/config-devices.mak: CONFIG_KVM=y" >> $config_host_mak
+else
+ echo "$target/config-devices.mak: CONFIG_KVM=n" >> $config_host_mak
fi
if supported_hax_target $target; then
echo "CONFIG_HAX=y" >> $config_target_mak
@@ -7658,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"
diff --git a/default-configs/alpha-softmmu.mak b/default-configs/alpha-softmmu.mak
index 49cb7ce351..d186fe8e9b 100644
--- a/default-configs/alpha-softmmu.mak
+++ b/default-configs/alpha-softmmu.mak
@@ -1,22 +1,10 @@
# Default configuration for alpha-softmmu
-include pci.mak
-include usb.mak
-CONFIG_SERIAL=y
-CONFIG_SERIAL_ISA=y
-CONFIG_I82374=y
-CONFIG_I8254=y
-CONFIG_I8257=y
-CONFIG_PARALLEL=y
-CONFIG_FDC=y
-CONFIG_PCKBD=y
-CONFIG_VGA_CIRRUS=y
-CONFIG_IDE_CORE=y
-CONFIG_IDE_QDEV=y
-CONFIG_VMWARE_VGA=y
-CONFIG_IDE_CMD646=y
-CONFIG_I8259=y
-CONFIG_MC146818RTC=y
-CONFIG_ISA_TESTDEV=y
-CONFIG_SMC37C669=y
+# Uncomment the following lines to disable these optional devices:
+#
+#CONFIG_PCI_DEVICES=n
+#CONFIG_TEST_DEVICES=n
+
+# Boards:
+#
CONFIG_DP264=y
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index bd6943b691..2a7efc1167 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -1,13 +1,11 @@
# Default configuration for arm-softmmu
-include pci.mak
-include usb.mak
+CONFIG_PCI=y
+CONFIG_PCI_DEVICES=y
CONFIG_VGA=y
CONFIG_NAND=y
CONFIG_ECC=y
CONFIG_SERIAL=y
-CONFIG_PTIMER=y
-CONFIG_SD=y
CONFIG_MAX7310=y
CONFIG_WM8750=y
CONFIG_TWL92230=y
@@ -25,7 +23,6 @@ CONFIG_DDC=y
CONFIG_SII9022=y
CONFIG_ADS7846=y
CONFIG_MAX111X=y
-CONFIG_SSI=y
CONFIG_SSI_SD=y
CONFIG_SSI_M25P80=y
CONFIG_LAN9118=y
@@ -37,7 +34,6 @@ CONFIG_DS1338=y
CONFIG_PFLASH_CFI01=y
CONFIG_PFLASH_CFI02=y
CONFIG_MICRODRIVE=y
-CONFIG_USB=y
CONFIG_USB_MUSB=y
CONFIG_USB_EHCI_SYSBUS=y
CONFIG_PLATFORM_BUS=y
@@ -51,7 +47,6 @@ CONFIG_ARM_V7M=y
CONFIG_NETDUINO2=y
CONFIG_ARM_GIC=y
-CONFIG_ARM_GIC_KVM=$(CONFIG_KVM)
CONFIG_ARM_TIMER=y
CONFIG_ARM_MPTIMER=y
CONFIG_A9_GTIMER=y
@@ -71,7 +66,6 @@ CONFIG_CADENCE=y
CONFIG_XGMAC=y
CONFIG_EXYNOS4=y
CONFIG_PXA2XX=y
-CONFIG_I2C=y
CONFIG_BITBANG_I2C=y
CONFIG_FRAMEBUFFER=y
CONFIG_XILINX_SPIPS=y
@@ -125,11 +119,8 @@ CONFIG_VERSATILE=y
CONFIG_VERSATILE_PCI=y
CONFIG_VERSATILE_I2C=y
+CONFIG_PCI_EXPRESS=y
CONFIG_PCI_EXPRESS_GENERIC_BRIDGE=y
-CONFIG_VFIO=$(CONFIG_LINUX)
-CONFIG_VFIO_PLATFORM=y
-CONFIG_VFIO_XGMAC=y
-CONFIG_VFIO_AMD_XGBE=y
CONFIG_SDHCI=y
CONFIG_INTEGRATOR=y
@@ -165,3 +156,6 @@ CONFIG_PCI_EXPRESS_DESIGNWARE=y
CONFIG_STRONGARM=y
CONFIG_HIGHBANK=y
CONFIG_MUSICPAL=y
+
+# for realview and versatilepb
+CONFIG_LSI_SCSI_PCI=y
diff --git a/default-configs/cris-softmmu.mak b/default-configs/cris-softmmu.mak
index a637c4b4bf..5932cf4d06 100644
--- a/default-configs/cris-softmmu.mak
+++ b/default-configs/cris-softmmu.mak
@@ -1,7 +1,5 @@
# Default configuration for cris-softmmu
-CONFIG_ETRAXFS=y
-CONFIG_NAND=y
-CONFIG_PTIMER=y
-CONFIG_PFLASH_CFI02=y
+# Boards:
+#
CONFIG_AXIS=y
diff --git a/default-configs/hppa-softmmu.mak b/default-configs/hppa-softmmu.mak
index b594a6ddd9..b64c5eb3ff 100644
--- a/default-configs/hppa-softmmu.mak
+++ b/default-configs/hppa-softmmu.mak
@@ -1,13 +1,9 @@
-include pci.mak
-include usb.mak
-CONFIG_SERIAL=y
-CONFIG_SERIAL_ISA=y
-CONFIG_ISA_BUS=y
-CONFIG_I8259=y
-CONFIG_E1000_PCI=y
-CONFIG_IDE_ISA=y
-CONFIG_IDE_CMD646=y
-# CONFIG_IDE_MMIO=y
-CONFIG_VIRTIO_VGA=y
-CONFIG_MC146818RTC=y
+# Default configuration for hppa-softmmu
+
+# Uncomment the following lines to disable these optional devices:
+#
+#CONFIG_PCI_DEVICES=n
+
+# Boards:
+#
CONFIG_DINO=y
diff --git a/default-configs/hyperv.mak b/default-configs/hyperv.mak
deleted file mode 100644
index 5d0d9fd830..0000000000
--- a/default-configs/hyperv.mak
+++ /dev/null
@@ -1,2 +0,0 @@
-CONFIG_HYPERV=$(CONFIG_KVM)
-CONFIG_HYPERV_TESTDEV=y
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 15b628757b..ba3fb3ff50 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -1,74 +1,27 @@
# Default configuration for i386-softmmu
-include pci.mak
-include sound.mak
-include usb.mak
-include hyperv.mak
-CONFIG_QXL=$(CONFIG_SPICE)
-CONFIG_VGA_ISA=y
-CONFIG_VGA_CIRRUS=y
-CONFIG_VMWARE_VGA=y
-CONFIG_VMXNET3_PCI=y
-CONFIG_VIRTIO_VGA=y
-CONFIG_VMMOUSE=y
-CONFIG_IPMI=y
-CONFIG_IPMI_LOCAL=y
-CONFIG_IPMI_EXTERN=y
-CONFIG_ISA_IPMI_KCS=y
-CONFIG_ISA_IPMI_BT=y
-CONFIG_SERIAL=y
-CONFIG_SERIAL_ISA=y
-CONFIG_PARALLEL=y
-CONFIG_I8254=y
-CONFIG_PCSPK=y
-CONFIG_PCKBD=y
-CONFIG_FDC=y
-CONFIG_ACPI=y
-CONFIG_ACPI_X86=y
-CONFIG_ACPI_X86_ICH=y
-CONFIG_ACPI_MEMORY_HOTPLUG=y
-CONFIG_ACPI_CPU_HOTPLUG=y
-CONFIG_APM=y
-CONFIG_I8257=y
-CONFIG_IDE_ISA=y
-CONFIG_IDE_PIIX=y
-CONFIG_NE2000_ISA=y
-CONFIG_HPET=y
-CONFIG_APPLESMC=y
-CONFIG_I8259=y
-CONFIG_PFLASH_CFI01=y
-CONFIG_TPM_TIS=$(CONFIG_TPM)
-CONFIG_TPM_CRB=$(CONFIG_TPM)
-CONFIG_MC146818RTC=y
-CONFIG_PCI_PIIX=y
-CONFIG_WDT_IB700=y
-CONFIG_ISA_DEBUG=y
-CONFIG_ISA_TESTDEV=y
-CONFIG_VMPORT=y
-CONFIG_SGA=y
-CONFIG_LPC_ICH9=y
-CONFIG_PCI_EXPRESS_Q35=y
-CONFIG_APIC=y
-CONFIG_IOAPIC=y
-CONFIG_PVPANIC=y
-CONFIG_MEM_DEVICE=y
-CONFIG_DIMM=y
-CONFIG_NVDIMM=y
-CONFIG_ACPI_NVDIMM=y
-CONFIG_PCIE_PORT=y
-CONFIG_XIO3130=y
-CONFIG_IOH3420=y
-CONFIG_I82801B11=y
-CONFIG_SMBIOS=y
-CONFIG_PXB=y
-CONFIG_ACPI_VMGENID=y
-CONFIG_ACPI_SMBUS=y
-CONFIG_SMBUS_EEPROM=y
-CONFIG_FW_CFG_DMA=y
-CONFIG_I2C=y
-CONFIG_SEV=$(CONFIG_KVM)
-CONFIG_VTD=y
-CONFIG_AMD_IOMMU=y
-CONFIG_PAM=y
+# Uncomment the following lines to disable these optional devices:
+#
+#CONFIG_AMD_IOMMU=n
+#CONFIG_APPLESMC=n
+#CONFIG_FDC=n
+#CONFIG_HPET=n
+#CONFIG_HYPERV=n
+#CONFIG_ISA_DEBUG=n
+#CONFIG_ISA_IPMI_BT=n
+#CONFIG_ISA_IPMI_KCS=n
+#CONFIG_PCI_DEVICES=n
+#CONFIG_PVPANIC=n
+#CONFIG_QXL=n
+#CONFIG_SEV=n
+#CONFIG_SGA=n
+#CONFIG_TEST_DEVICES=n
+#CONFIG_TPM_CRB=n
+#CONFIG_TPM_TIS=n
+#CONFIG_VTD=n
+
+# Boards:
+#
+CONFIG_ISAPC=y
CONFIG_I440FX=y
CONFIG_Q35=y
diff --git a/default-configs/lm32-softmmu.mak b/default-configs/lm32-softmmu.mak
index 4049b23562..6d259665d6 100644
--- a/default-configs/lm32-softmmu.mak
+++ b/default-configs/lm32-softmmu.mak
@@ -1,10 +1,10 @@
# Default configuration for lm32-softmmu
+# Uncomment the following lines to disable these optional devices:
+#
+#CONFIG_MILKYMIST_TMU2=n # disabling it actually causes compile-time failures
+
+# Boards:
+#
CONFIG_LM32=y
CONFIG_MILKYMIST=y
-CONFIG_MILKYMIST_TMU2=$(call land,$(CONFIG_X11),$(CONFIG_OPENGL))
-CONFIG_FRAMEBUFFER=y
-CONFIG_PTIMER=y
-CONFIG_PFLASH_CFI01=y
-CONFIG_PFLASH_CFI02=y
-CONFIG_SD=y
diff --git a/default-configs/m68k-softmmu.mak b/default-configs/m68k-softmmu.mak
index 27f5274244..e17495e2a0 100644
--- a/default-configs/m68k-softmmu.mak
+++ b/default-configs/m68k-softmmu.mak
@@ -1,6 +1,6 @@
# Default configuration for m68k-softmmu
-CONFIG_COLDFIRE=y
-CONFIG_PTIMER=y
+# Boards:
+#
CONFIG_AN5206=y
CONFIG_MCF5208=y
diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/microblaze-softmmu.mak
index 14837cf74a..db8c6e4bba 100644
--- a/default-configs/microblaze-softmmu.mak
+++ b/default-configs/microblaze-softmmu.mak
@@ -1,15 +1,7 @@
# Default configuration for microblaze-softmmu
-CONFIG_PTIMER=y
-CONFIG_PFLASH_CFI01=y
-CONFIG_SERIAL=y
-CONFIG_XILINX=y
-CONFIG_XILINX_AXI=y
-CONFIG_XILINX_SPI=y
-CONFIG_XILINX_ETHLITE=y
-CONFIG_SSI=y
-CONFIG_SSI_M25P80=y
-CONFIG_XLNX_ZYNQMP=y
+# Boards:
+#
CONFIG_PETALOGIX_S3ADSP1800=y
CONFIG_PETALOGIX_ML605=y
CONFIG_XLNX_ZYNQMP_PMU=y
diff --git a/default-configs/mips-softmmu-common.mak b/default-configs/mips-softmmu-common.mak
index ded74980e1..0795d522db 100644
--- a/default-configs/mips-softmmu-common.mak
+++ b/default-configs/mips-softmmu-common.mak
@@ -1,10 +1,9 @@
# Common mips*-softmmu CONFIG defines
-include pci.mak
-include sound.mak
-include usb.mak
+CONFIG_ISA_BUS=y
+CONFIG_PCI=y
+CONFIG_PCI_DEVICES=y
CONFIG_ESP=y
-CONFIG_SCSI=y
CONFIG_VGA_ISA=y
CONFIG_VGA_ISA_MM=y
CONFIG_VGA_CIRRUS=y
@@ -31,13 +30,12 @@ CONFIG_MIPSNET=y
CONFIG_PFLASH_CFI01=y
CONFIG_I8259=y
CONFIG_MC146818RTC=y
-CONFIG_ISA_TESTDEV=y
CONFIG_EMPTY_SLOT=y
CONFIG_MIPS_CPS=y
CONFIG_MIPS_ITU=y
-CONFIG_I2C=y
CONFIG_R4K=y
CONFIG_MALTA=y
CONFIG_MIPSSIM=y
CONFIG_ACPI_SMBUS=y
CONFIG_SMBUS_EEPROM=y
+CONFIG_TEST_DEVICES=y
diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak
index 9eb1208b58..8b255efc54 100644
--- a/default-configs/mips64el-softmmu.mak
+++ b/default-configs/mips64el-softmmu.mak
@@ -10,6 +10,8 @@ CONFIG_JAZZ=y
CONFIG_G364FB=y
CONFIG_JAZZ_LED=y
CONFIG_VT82C686=y
+CONFIG_AHCI=y
CONFIG_MIPS_BOSTON=y
CONFIG_FITLOADER=y
+CONFIG_PCI_EXPRESS=y
CONFIG_PCI_EXPRESS_XILINX=y
diff --git a/default-configs/moxie-softmmu.mak b/default-configs/moxie-softmmu.mak
index 17ba906dc2..bd50da3c58 100644
--- a/default-configs/moxie-softmmu.mak
+++ b/default-configs/moxie-softmmu.mak
@@ -1,8 +1,5 @@
# Default configuration for moxie-softmmu
-CONFIG_ISA_BUS=y
-CONFIG_MC146818RTC=y
-CONFIG_SERIAL=y
-CONFIG_SERIAL_ISA=y
-CONFIG_VGA=y
+# Boards:
+#
CONFIG_MOXIESIM=y
diff --git a/default-configs/nios2-softmmu.mak b/default-configs/nios2-softmmu.mak
index ab42d0fc28..e11dc54960 100644
--- a/default-configs/nios2-softmmu.mak
+++ b/default-configs/nios2-softmmu.mak
@@ -1,7 +1,5 @@
# Default configuration for nios2-softmmu
-CONFIG_NIOS2=y
-CONFIG_SERIAL=y
-CONFIG_PTIMER=y
-CONFIG_ALTERA_TIMER=y
+# Boards:
+#
CONFIG_NIOS2_10M50=y
diff --git a/default-configs/or1k-softmmu.mak b/default-configs/or1k-softmmu.mak
index 6a0f2ef6cf..168101c39a 100644
--- a/default-configs/or1k-softmmu.mak
+++ b/default-configs/or1k-softmmu.mak
@@ -1,6 +1,5 @@
# Default configuration for or1k-softmmu
-CONFIG_SERIAL=y
-CONFIG_OPENCORES_ETH=y
-CONFIG_OMPIC=y
+# Boards:
+#
CONFIG_OR1K_SIM=y
diff --git a/default-configs/pci.mak b/default-configs/pci.mak
deleted file mode 100644
index 037636fa33..0000000000
--- a/default-configs/pci.mak
+++ /dev/null
@@ -1,51 +0,0 @@
-CONFIG_PCI=y
-# For now, CONFIG_IDE_CORE requires ISA, so we enable it here
-CONFIG_ISA_BUS=y
-CONFIG_VIRTIO_PCI=y
-include virtio.mak
-CONFIG_USB_UHCI=y
-CONFIG_USB_OHCI=y
-CONFIG_USB_EHCI=y
-CONFIG_USB_XHCI=y
-CONFIG_USB_XHCI_NEC=y
-CONFIG_NE2000_PCI=y
-CONFIG_EEPRO100_PCI=y
-CONFIG_PCNET_PCI=y
-CONFIG_PCNET_COMMON=y
-CONFIG_AC97=y
-CONFIG_HDA=y
-CONFIG_ES1370=y
-CONFIG_SCSI=y
-CONFIG_LSI_SCSI_PCI=y
-CONFIG_VMW_PVSCSI_SCSI_PCI=y
-CONFIG_MEGASAS_SCSI_PCI=y
-CONFIG_MPTSAS_SCSI_PCI=y
-CONFIG_RTL8139_PCI=y
-CONFIG_E1000_PCI=y
-CONFIG_E1000E_PCI_EXPRESS=y
-CONFIG_IDE_CORE=y
-CONFIG_IDE_QDEV=y
-CONFIG_IDE_PCI=y
-CONFIG_AHCI=y
-CONFIG_ESP=y
-CONFIG_ESP_PCI=y
-CONFIG_SERIAL=y
-CONFIG_SERIAL_ISA=y
-CONFIG_SERIAL_PCI=y
-CONFIG_CAN_BUS=y
-CONFIG_CAN_SJA1000=y
-CONFIG_CAN_PCI=y
-CONFIG_IPACK=y
-CONFIG_WDT_IB6300ESB=y
-CONFIG_PCI_TESTDEV=y
-CONFIG_NVME_PCI=y
-CONFIG_SD=y
-CONFIG_SDHCI=y
-CONFIG_EDU=y
-CONFIG_VGA=y
-CONFIG_VGA_PCI=y
-CONFIG_BOCHS_DISPLAY=y
-CONFIG_IVSHMEM_DEVICE=$(CONFIG_IVSHMEM)
-CONFIG_ROCKER=y
-CONFIG_VFIO=$(CONFIG_LINUX)
-CONFIG_VFIO_PCI=y
diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
index 52acb7cf39..6ea36d4090 100644
--- a/default-configs/ppc-softmmu.mak
+++ b/default-configs/ppc-softmmu.mak
@@ -1,77 +1,17 @@
# Default configuration for ppc-softmmu
-include pci.mak
-include sound.mak
-include usb.mak
-
# For embedded PPCs:
-CONFIG_PPC4XX=y
-CONFIG_M48T59=y
-CONFIG_SERIAL=y
-CONFIG_I8257=y
-CONFIG_OPENPIC=y
-CONFIG_PPCE500_PCI=y
-CONFIG_PFLASH_CFI01=y
-CONFIG_PFLASH_CFI02=y
-CONFIG_PTIMER=y
-CONFIG_I8259=y
-CONFIG_XILINX=y
-CONFIG_XILINX_ETHLITE=y
CONFIG_E500=y
-CONFIG_OPENPIC_KVM=$(call land,$(CONFIG_E500),$(CONFIG_KVM))
-CONFIG_PLATFORM_BUS=y
-CONFIG_ETSEC=y
CONFIG_PPC405=y
CONFIG_PPC440=y
CONFIG_VIRTEX=y
# For Sam460ex
CONFIG_SAM460EX=y
-CONFIG_USB_EHCI_SYSBUS=y
-CONFIG_SM501=y
-CONFIG_DDC=y
-CONFIG_IDE_SII3112=y
-CONFIG_I2C=y
-CONFIG_AT24C=y
-CONFIG_BITBANG_I2C=y
-CONFIG_M41T80=y
-CONFIG_VGA_CIRRUS=y
-CONFIG_SMBUS_EEPROM=y
# For Macs
-CONFIG_ESCC=y
-CONFIG_MACIO=y
-CONFIG_MACIO_GPIO=y
-CONFIG_SUNGEM=y
-CONFIG_MOS6522=y
-CONFIG_CUDA=y
-CONFIG_ADB=y
-CONFIG_MAC_NVRAM=y
-CONFIG_MAC_DBDMA=y
-CONFIG_MAC_PMU=y
-CONFIG_HEATHROW_PIC=y
-CONFIG_GRACKLE_PCI=y
-CONFIG_UNIN_PCI=y
-CONFIG_DEC_PCI=y
-CONFIG_IDE_MACIO=y
CONFIG_MAC_OLDWORLD=y
CONFIG_MAC_NEWWORLD=y
# For PReP
CONFIG_PREP=y
-CONFIG_PREP_PCI=y
-CONFIG_SERIAL_ISA=y
-CONFIG_MC146818RTC=y
-CONFIG_ISA_TESTDEV=y
-CONFIG_RS6000_MC=y
-CONFIG_PARALLEL=y
-CONFIG_I82374=y
-CONFIG_I82378=y
-CONFIG_I8254=y
-CONFIG_PCKBD=y
-CONFIG_FDC=y
-CONFIG_NE2000_ISA=y
-CONFIG_PC87312=y
-CONFIG_PCSPK=y
-CONFIG_IDE_ISA=y
-CONFIG_CS4231A=y
diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
index 7f34ad0528..cca52665d9 100644
--- a/default-configs/ppc64-softmmu.mak
+++ b/default-configs/ppc64-softmmu.mak
@@ -5,19 +5,6 @@ include ppc-softmmu.mak
# For PowerNV
CONFIG_POWERNV=y
-CONFIG_IPMI=y
-CONFIG_IPMI_LOCAL=y
-CONFIG_IPMI_EXTERN=y
-CONFIG_ISA_IPMI_BT=y
# For pSeries
CONFIG_PSERIES=y
-CONFIG_VIRTIO_VGA=y
-CONFIG_XICS=$(CONFIG_PSERIES)
-CONFIG_XICS_SPAPR=$(CONFIG_PSERIES)
-CONFIG_XICS_KVM=$(call land,$(CONFIG_PSERIES),$(CONFIG_KVM))
-CONFIG_XIVE=$(CONFIG_PSERIES)
-CONFIG_XIVE_SPAPR=$(CONFIG_PSERIES)
-CONFIG_MEM_DEVICE=y
-CONFIG_DIMM=y
-CONFIG_SPAPR_RNG=y
diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak
index 65337166e1..1ae077ed87 100644
--- a/default-configs/riscv32-softmmu.mak
+++ b/default-configs/riscv32-softmmu.mak
@@ -1,21 +1,12 @@
-# Default configuration for riscv-softmmu
+# Default configuration for riscv32-softmmu
-include pci.mak
-include usb.mak
-
-CONFIG_SERIAL=y
-CONFIG_VIRTIO_MMIO=y
-
-CONFIG_CADENCE=y
-
-CONFIG_PCI_EXPRESS_GENERIC_BRIDGE=y
-
-CONFIG_VGA=y
-CONFIG_VGA_PCI=y
+# Uncomment the following lines to disable these optional devices:
+#
+#CONFIG_PCI_DEVICES=n
+# Boards:
+#
CONFIG_SPIKE=y
-CONFIG_HART=y
CONFIG_SIFIVE_E=y
-CONFIG_SIFIVE=y
CONFIG_SIFIVE_U=y
CONFIG_RISCV_VIRT=y
diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak
index 65337166e1..235c6f473f 100644
--- a/default-configs/riscv64-softmmu.mak
+++ b/default-configs/riscv64-softmmu.mak
@@ -1,21 +1,3 @@
-# Default configuration for riscv-softmmu
+# Default configuration for riscv64-softmmu
-include pci.mak
-include usb.mak
-
-CONFIG_SERIAL=y
-CONFIG_VIRTIO_MMIO=y
-
-CONFIG_CADENCE=y
-
-CONFIG_PCI_EXPRESS_GENERIC_BRIDGE=y
-
-CONFIG_VGA=y
-CONFIG_VGA_PCI=y
-
-CONFIG_SPIKE=y
-CONFIG_HART=y
-CONFIG_SIFIVE_E=y
-CONFIG_SIFIVE=y
-CONFIG_SIFIVE_U=y
-CONFIG_RISCV_VIRT=y
+include riscv32-softmmu.mak
diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak
index 6f2c6cec18..f2287a133f 100644
--- a/default-configs/s390x-softmmu.mak
+++ b/default-configs/s390x-softmmu.mak
@@ -1,12 +1,13 @@
-CONFIG_PCI=y
-CONFIG_VIRTIO_PCI=$(CONFIG_PCI)
-include virtio.mak
-CONFIG_SCLPCONSOLE=y
-CONFIG_TERMINAL3270=y
-CONFIG_S390_FLIC=y
-CONFIG_S390_FLIC_KVM=$(CONFIG_KVM)
-CONFIG_WDT_DIAG288=y
+# Default configuration for s390x-softmmu
+
+# Uncomment the following lines to disable these optional devices:
+#
+#CONFIG_TERMINAL3270=n
+#CONFIG_VFIO_AP=n
+#CONFIG_VFIO_CCW=n
+#CONFIG_VIRTIO_PCI=n
+#CONFIG_WDT_DIAG288=n
+
+# Boards:
+#
CONFIG_S390_CCW_VIRTIO=y
-CONFIG_VFIO=$(CONFIG_LINUX)
-CONFIG_VFIO_CCW=y
-CONFIG_VFIO_AP=y
diff --git a/default-configs/sh4-softmmu.mak b/default-configs/sh4-softmmu.mak
index 1fdb009151..565e8b0b5d 100644
--- a/default-configs/sh4-softmmu.mak
+++ b/default-configs/sh4-softmmu.mak
@@ -1,23 +1,11 @@
-# Default configuration for sh4-softmmu
+# Default configuration for sh4eb-softmmu
-include pci.mak
-include usb.mak
-CONFIG_SERIAL=y
-CONFIG_SERIAL_ISA=y
-CONFIG_PTIMER=y
-CONFIG_PFLASH_CFI02=y
-CONFIG_SH4=y
-CONFIG_IDE_MMIO=y
-CONFIG_SM501=y
-CONFIG_I2C=y
-CONFIG_DDC=y
-CONFIG_ISA_TESTDEV=y
-CONFIG_I82378=y
-CONFIG_I8259=y
-CONFIG_I8254=y
-CONFIG_PCSPK=y
-CONFIG_I82374=y
-CONFIG_I8257=y
-CONFIG_MC146818RTC=y
+# Uncomment the following lines to disable these optional devices:
+#
+#CONFIG_PCI_DEVICES=n
+#CONFIG_TEST_DEVICES=n
+
+# Boards:
+#
CONFIG_R2D=y
CONFIG_SHIX=y
diff --git a/default-configs/sh4eb-softmmu.mak b/default-configs/sh4eb-softmmu.mak
index 3b550a5fe8..522a7a50fa 100644
--- a/default-configs/sh4eb-softmmu.mak
+++ b/default-configs/sh4eb-softmmu.mak
@@ -1,23 +1,3 @@
# Default configuration for sh4eb-softmmu
-include pci.mak
-include usb.mak
-CONFIG_SERIAL=y
-CONFIG_SERIAL_ISA=y
-CONFIG_PTIMER=y
-CONFIG_PFLASH_CFI02=y
-CONFIG_SH4=y
-CONFIG_IDE_MMIO=y
-CONFIG_SM501=y
-CONFIG_I2C=y
-CONFIG_DDC=y
-CONFIG_ISA_TESTDEV=y
-CONFIG_I82378=y
-CONFIG_I8259=y
-CONFIG_I8254=y
-CONFIG_PCSPK=y
-CONFIG_I82374=y
-CONFIG_I8257=y
-CONFIG_MC146818RTC=y
-CONFIG_R2D=y
-CONFIG_SHIX=y
+include sh4-softmmu.mak
diff --git a/default-configs/sound.mak b/default-configs/sound.mak
deleted file mode 100644
index 4f22c34b5d..0000000000
--- a/default-configs/sound.mak
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG_SB16=y
-CONFIG_ADLIB=y
-CONFIG_GUS=y
-CONFIG_CS4231A=y
diff --git a/default-configs/sparc-softmmu.mak b/default-configs/sparc-softmmu.mak
index 59a4a3d693..ee85218115 100644
--- a/default-configs/sparc-softmmu.mak
+++ b/default-configs/sparc-softmmu.mak
@@ -1,23 +1,11 @@
# Default configuration for sparc-softmmu
-CONFIG_ISA_BUS=y
-CONFIG_ECC=y
-CONFIG_SCSI=y
-CONFIG_ESP=y
-CONFIG_ESCC=y
-CONFIG_M48T59=y
-CONFIG_PTIMER=y
-CONFIG_FDC=y
-CONFIG_EMPTY_SLOT=y
-CONFIG_PCNET_COMMON=y
-CONFIG_LANCE=y
-CONFIG_TCX=y
-CONFIG_CG3=y
-CONFIG_SLAVIO=y
-CONFIG_CS4231=y
-CONFIG_GRLIB=y
-CONFIG_STP2000=y
-CONFIG_ECCMEMCTL=y
+# Uncomment the following lines to disable these optional devices:
+#
+#CONFIG_TCX=n
+#CONFIG_CG3=n
+# Boards:
+#
CONFIG_SUN4M=y
CONFIG_LEON3=y
diff --git a/default-configs/sparc64-softmmu.mak b/default-configs/sparc64-softmmu.mak
index 1fae4888db..e50030a229 100644
--- a/default-configs/sparc64-softmmu.mak
+++ b/default-configs/sparc64-softmmu.mak
@@ -1,21 +1,12 @@
# Default configuration for sparc64-softmmu
-include pci.mak
-include usb.mak
-CONFIG_M48T59=y
-CONFIG_PTIMER=y
-CONFIG_SERIAL=y
-CONFIG_SERIAL_ISA=y
-CONFIG_PARALLEL=y
-CONFIG_PCKBD=y
-CONFIG_FDC=y
-CONFIG_IDE_ISA=y
-CONFIG_IDE_CMD646=y
-CONFIG_PCI_SABRE=y
-CONFIG_SIMBA=y
-CONFIG_SUNHME=y
-CONFIG_MC146818RTC=y
-CONFIG_ISA_TESTDEV=y
-CONFIG_SUN4V_RTC=y
+# Uncomment the following lines to disable these optional devices:
+#
+#CONFIG_PCI_DEVICES=n
+#CONFIG_SUNHME=n
+#CONFIG_TEST_DEVICES=n
+
+# Boards:
+#
CONFIG_SUN4U=y
CONFIG_NIAGARA=y
diff --git a/default-configs/unicore32-softmmu.mak b/default-configs/unicore32-softmmu.mak
index 5f6c4a8047..0bfce48c6d 100644
--- a/default-configs/unicore32-softmmu.mak
+++ b/default-configs/unicore32-softmmu.mak
@@ -1,5 +1,5 @@
# Default configuration for unicore32-softmmu
-CONFIG_ISA_BUS=y
+
+# Boards:
+#
CONFIG_PUV3=y
-CONFIG_PTIMER=y
-CONFIG_PCKBD=y
diff --git a/default-configs/usb.mak b/default-configs/usb.mak
deleted file mode 100644
index e42cfeabbe..0000000000
--- a/default-configs/usb.mak
+++ /dev/null
@@ -1,11 +0,0 @@
-CONFIG_USB=y
-CONFIG_USB_TABLET_WACOM=y
-CONFIG_USB_STORAGE_BOT=y
-CONFIG_USB_STORAGE_UAS=y
-CONFIG_USB_STORAGE_MTP=y
-CONFIG_SCSI=y
-CONFIG_USB_SMARTCARD=y
-CONFIG_USB_AUDIO=y
-CONFIG_USB_SERIAL=y
-CONFIG_USB_NETWORK=y
-CONFIG_USB_BLUETOOTH=y
diff --git a/default-configs/virtio.mak b/default-configs/virtio.mak
deleted file mode 100644
index b653aa06b1..0000000000
--- a/default-configs/virtio.mak
+++ /dev/null
@@ -1,15 +0,0 @@
-CONFIG_VHOST_USER_SCSI=$(CONFIG_VHOST_USER)
-CONFIG_VHOST_USER_BLK=$(CONFIG_VHOST_USER)
-CONFIG_VIRTIO=y
-CONFIG_VIRTIO_9P=$(CONFIG_VIRTFS)
-CONFIG_VIRTIO_BALLOON=y
-CONFIG_VIRTIO_BLK=y
-CONFIG_VIRTIO_CRYPTO=y
-CONFIG_VIRTIO_GPU=y
-CONFIG_VIRTIO_INPUT=y
-CONFIG_VIRTIO_NET=y
-CONFIG_VIRTIO_RNG=y
-CONFIG_SCSI=y
-CONFIG_VIRTIO_SCSI=y
-CONFIG_VIRTIO_SERIAL=y
-CONFIG_VIRTIO_INPUT_HOST=$(CONFIG_LINUX)
diff --git a/default-configs/xtensa-softmmu.mak b/default-configs/xtensa-softmmu.mak
index baf90ca162..7e4d1cc097 100644
--- a/default-configs/xtensa-softmmu.mak
+++ b/default-configs/xtensa-softmmu.mak
@@ -1,8 +1,6 @@
# Default configuration for Xtensa
-CONFIG_SERIAL=y
-CONFIG_OPENCORES_ETH=y
-CONFIG_PFLASH_CFI01=y
-
+# Boards:
+#
CONFIG_XTENSA_SIM=y
-CONFIG_XTENSA_FPGA=y
+CONFIG_XTENSA_XTFPGA=y
diff --git a/default-configs/xtensaeb-softmmu.mak b/default-configs/xtensaeb-softmmu.mak
index baf90ca162..f7e48c750c 100644
--- a/default-configs/xtensaeb-softmmu.mak
+++ b/default-configs/xtensaeb-softmmu.mak
@@ -1,8 +1,3 @@
# Default configuration for Xtensa
-CONFIG_SERIAL=y
-CONFIG_OPENCORES_ETH=y
-CONFIG_PFLASH_CFI01=y
-
-CONFIG_XTENSA_SIM=y
-CONFIG_XTENSA_FPGA=y
+include xtensa-softmmu.mak
diff --git a/docs/devel/build-system.txt b/docs/devel/build-system.txt
index f9fd27fab0..addd274eeb 100644
--- a/docs/devel/build-system.txt
+++ b/docs/devel/build-system.txt
@@ -417,7 +417,6 @@ into each QEMU system and userspace emulator targets. They merely
contain a long list of config variable definitions. For example,
default-configs/x86_64-softmmu.mak has:
- include pci.mak
include sound.mak
include usb.mak
CONFIG_QXL=$(CONFIG_SPICE)
diff --git a/docs/devel/index.rst b/docs/devel/index.rst
index cd0fa6c9ba..6b11e49caa 100644
--- a/docs/devel/index.rst
+++ b/docs/devel/index.rst
@@ -13,6 +13,7 @@ Contents:
.. toctree::
:maxdepth: 2
+ kconfig
loads-stores
memory
migration
diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst
new file mode 100644
index 0000000000..cce146f87d
--- /dev/null
+++ b/docs/devel/kconfig.rst
@@ -0,0 +1,306 @@
+================
+QEMU and Kconfig
+================
+
+QEMU is a very versatile emulator; it can be built for a variety of
+targets, where each target can emulate various boards and at the same
+time different targets can share large amounts of code. For example,
+a POWER and an x86 board can run the same code to emulate a PCI network
+card, even though the boards use different PCI host bridges, and they
+can run the same code to emulate a SCSI disk while using different
+SCSI adapters. ARM, s390 and x86 boards can all present a virtio-blk
+disk to their guests, but with three different virtio guest interfaces.
+
+Each QEMU target enables a subset of the boards, devices and buses that
+are included in QEMU's source code. As a result, each QEMU executable
+only links a small subset of the files that form QEMU's source code;
+anything that is not needed to support a particular target is culled.
+
+QEMU uses a simple domain-specific language to describe the dependencies
+between components. This is useful for two reasons:
+
+* new targets and boards can be added without knowing in detail the
+ architecture of the hardware emulation subsystems. Boards only have
+ to list the components they need, and the compiled executable will
+ include all the required dependencies and all the devices that the
+ user can add to that board;
+
+* users can easily build reduced versions of QEMU that support only a subset
+ of boards or devices. For example, by default most targets will include
+ all emulated PCI devices that QEMU supports, but the build process is
+ configurable and it is easy to drop unnecessary (or otherwise unwanted)
+ code to make a leaner binary.
+
+This domain-specific language is based on the Kconfig language that
+originated in the Linux kernel, though it was heavily simplified and
+the handling of dependencies is stricter in QEMU.
+
+Unlike Linux, there is no user interface to edit the configuration, which
+is instead specified in per-target files under the ``default-configs/``
+directory of the QEMU source tree. This is because, unlike Linux,
+configuration and dependencies can be treated as a black box when building
+QEMU; the default configuration that QEMU ships with should be okay in
+almost all cases.
+
+The Kconfig language
+--------------------
+
+Kconfig defines configurable components in files named ``hw/*/Kconfig``.
+Note that configurable components are _not_ visible in C code as preprocessor
+symbols; they are only visible in the Makefile. Each configurable component
+defines a Makefile variable whose name starts with ``CONFIG_``.
+
+All elements have boolean (true/false) type; truth is written as ``y``, while
+falsehood is written ``n``. They are defined in a Kconfig
+stanza like the following::
+
+ config ARM_VIRT
+ bool
+ imply PCI_DEVICES
+ imply VFIO_AMD_XGBE
+ imply VFIO_XGMAC
+ select A15MPCORE
+ select ACPI
+ select ARM_SMMUV3
+
+The ``config`` keyword introduces a new configuration element. In the example
+above, Makefiles will have access to a variable named ``CONFIG_ARM_VIRT``,
+with value ``y`` or ``n`` (respectively for boolean true and false).
+
+Boolean expressions can be used within the language, whenever ``<expr>``
+is written in the remainder of this section. The ``&&``, ``||`` and
+``!`` operators respectively denote conjunction (AND), disjunction (OR)
+and negation (NOT).
+
+The ``bool`` data type declaration is optional, but it is suggested to
+include it for clarity and future-proofing. After ``bool`` the following
+directives can be included:
+
+**dependencies**: ``depends on <expr>``
+
+ This defines a dependency for this configurable element. Dependencies
+ evaluate an expression and force the value of the variable to false
+ if the expression is false.
+
+**reverse dependencies**: ``select <symbol> [if <expr>]``
+
+ While ``depends on`` can force a symbol to false, reverse dependencies can
+ be used to force another symbol to true. In the following example,
+ ``CONFIG_BAZ`` will be true whenever ``CONFIG_FOO`` is true::
+
+ config FOO
+ select BAZ
+
+ The optional expression will prevent ``select`` from having any effect
+ unless it is true.
+
+ Note that unlike Linux's Kconfig implementation, QEMU will detect
+ contradictions between ``depends on`` and ``select`` statements and prevent
+ you from building such a configuration.
+
+**default value**: ``default <value> [if <expr>]``
+
+ Default values are assigned to the config symbol if no other value was
+ set by the user via ``default-configs/*.mak`` files, and only if
+ ``select`` or ``depends on`` directives do not force the value to true
+ or false respectively. ``<value>`` can be ``y`` or ``n``; it cannot
+ be an arbitrary Boolean expression. However, a condition for applying
+ the default value can be added with ``if``.
+
+ A configuration element can have any number of default values (usually,
+ if more than one default is present, they will have different
+ conditions). If multiple default values satisfy their condition,
+ only the first defined one is active.
+
+**reverse default** (weak reverse dependency): ``imply <symbol> [if <expr>]``
+
+ This is similar to ``select`` as it applies a lower limit of ``y``
+ to another symbol. However, the lower limit is only a default
+ and the "implied" symbol's value may still be set to ``n`` from a
+ ``default-configs/*.mak`` files. The following two examples are
+ equivalent::
+
+ config FOO
+ bool
+ imply BAZ
+
+ config BAZ
+ bool
+ default y if FOO
+
+ The next section explains where to use ``imply`` or ``default y``.
+
+Guidelines for writing Kconfig files
+------------------------------------
+
+Configurable elements in QEMU fall under five broad groups. Each group
+declares its dependencies in different ways:
+
+**subsystems**, of which **buses** are a special case
+
+ Example::
+
+ config SCSI
+ bool
+
+ Subsystems always default to false (they have no ``default`` directive)
+ and are never visible in ``default-configs/*.mak`` files. It's
+ up to other symbols to ``select`` whatever subsystems they require.
+
+ They sometimes have ``select`` directives to bring in other required
+ subsystems or buses. For example, ``AUX`` (the DisplayPort auxiliary
+ channel "bus") selects ``I2C`` because it can act as an I2C master too.
+
+**devices**
+
+ Example::
+
+ config MEGASAS_SCSI_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select SCSI
+
+ Devices are the most complex of the five. They can have a variety
+ of directives that cooperate so that a default configuration includes
+ all the devices that can be accessed from QEMU.
+
+ Devices *depend on* the bus that they lie on, for example a PCI
+ device would specify ``depends on PCI``. An MMIO device will likely
+ have no ``depends on`` directive. Devices also *select* the buses
+ that the device provides, for example a SCSI adapter would specify
+ ``select SCSI``. Finally, devices are usually ``default y`` if and
+ only if they have at least one ``depends on``; the default could be
+ conditional on a device group.
+
+ Devices also select any optional subsystem that they use; for example
+ a video card might specify ``select EDID`` if it needs to build EDID
+ information and publish it to the guest.
+
+**device groups**
+
+ Example::
+
+ config PCI_DEVICES
+ bool
+
+ Device groups provide a convenient mechanism to enable/disable many
+ devices in one go. This is useful when a set of devices is likely to
+ be enabled/disabled by several targets. Device groups usually need
+ no directive and are not used in the Makefile either; they only appear
+ as conditions for ``default y`` directives.
+
+ QEMU currently has two device groups, ``PCI_DEVICES`` and
+ ``TEST_DEVICES``. PCI devices usually have a ``default y if
+ PCI_DEVICES`` directive rather than just ``default y``. This lets
+ some boards (notably s390) easily support a subset of PCI devices,
+ for example only VFIO (passthrough) and virtio-pci devices.
+ ``TEST_DEVICES`` instead is used for devices that are rarely used on
+ production virtual machines, but provide useful hooks to test QEMU
+ or KVM.
+
+**boards**
+
+ Example::
+
+ config SUN4M
+ bool
+ imply TCX
+ imply CG3
+ select CS4231
+ select ECCMEMCTL
+ select EMPTY_SLOT
+ select ESCC
+ select ESP
+ select FDC
+ select SLAVIO
+ select LANCE
+ select M48T59
+ select STP2000
+
+ Boards specify their constituent devices using ``imply`` and ``select``
+ directives. A device should be listed under ``select`` if the board
+ cannot be started at all without it. It should be listed under
+ ``imply`` if (depending on the QEMU command line) the board may or
+ may not be started without it. Boards also default to false; they are
+ enabled by the ``default-configs/*.mak`` for the target they apply to.
+
+**internal elements**
+
+ Example::
+
+ config ECCMEMCTL
+ bool
+ select ECC
+
+ Internal elements group code that is useful in several boards or
+ devices. They are usually enabled with ``select`` and in turn select
+ other elements; they are never visible in ``default-configs/*.mak``
+ files, and often not even in the Makefile.
+
+Writing and modifying default configurations
+--------------------------------------------
+
+In addition to the Kconfig files under hw/, each target also includes
+a file called ``default-configs/TARGETNAME-softmmu.mak``. These files
+initialize some Kconfig variables to non-default values and provide the
+starting point to turn on devices and subsystems.
+
+A file in ``default-configs/`` looks like the following example::
+
+ # Default configuration for alpha-softmmu
+
+ # Uncomment the following lines to disable these optional devices:
+ #
+ #CONFIG_PCI_DEVICES=n
+ #CONFIG_TEST_DEVICES=n
+
+ # Boards:
+ #
+ CONFIG_DP264=y
+
+The first part, consisting of commented-out ``=n`` assignments, tells
+the user which devices or device groups are implied by the boards.
+The second part, consisting of ``=y`` assignments, tells the user which
+boards are supported by the target. The user will typically modify
+the default configuration by uncommenting lines in the first group,
+or commenting out lines in the second group.
+
+It is also possible to run QEMU's configure script with the
+``--with-default-devices`` option. When this is done, everything defaults
+to ``n`` unless it is ``select``ed or explicitly switched on in the
+``.mak`` files. In other words, ``default`` and ``imply`` directives
+are disabled. When QEMU is built with this option, the user will probably
+want to change some lines in the first group, for example like this::
+
+ CONFIG_PCI_DEVICES=y
+ #CONFIG_TEST_DEVICES=n
+
+and/or pick a subset of the devices in those device groups. Right now
+there is no single place that lists all the optional devices for
+``CONFIG_PCI_DEVICES`` and ``CONFIG_TEST_DEVICES``. In the future,
+we expect that ``.mak`` files will be automatically generated, so that
+they will include all these symbols and some help text on what they do.
+
+``Kconfig.host``
+----------------
+
+In some special cases, a configurable element depends on host features
+that are detected by QEMU's configure script; for example some devices
+depend on the availability of KVM or on the presence of a library on
+the host.
+
+These symbols should be listed in ``Kconfig.host`` like this::
+
+ config KVM
+ bool
+
+and also listed as follows in the top-level Makefile's ``MINIKCONF_ARGS``
+variable::
+
+ MINIKCONF_ARGS = \
+ $@ $*-config.devices.mak.d $< $(MINIKCONF_INPUTS) \
+ CONFIG_KVM=$(CONFIG_KVM) \
+ CONFIG_SPICE=$(CONFIG_SPICE) \
+ CONFIG_TPM=$(CONFIG_TPM) \
+ ...
diff --git a/hw/9pfs/Kconfig b/hw/9pfs/Kconfig
new file mode 100644
index 0000000000..8c5032c575
--- /dev/null
+++ b/hw/9pfs/Kconfig
@@ -0,0 +1,4 @@
+config VIRTIO_9P
+ bool
+ default y
+ depends on VIRTFS && VIRTIO
diff --git a/hw/9pfs/Makefile.objs b/hw/9pfs/Makefile.objs
index 8ac04962bd..70ded6fd8f 100644
--- a/hw/9pfs/Makefile.objs
+++ b/hw/9pfs/Makefile.objs
@@ -1,11 +1,9 @@
-ifeq ($(call lor,$(CONFIG_VIRTIO_9P),$(CONFIG_XEN)),y)
common-obj-y = 9p.o 9p-util.o
common-obj-y += 9p-local.o 9p-xattr.o
common-obj-y += 9p-xattr-user.o 9p-posix-acl.o
common-obj-y += coth.o cofs.o codir.o cofile.o
common-obj-y += coxattr.o 9p-synth.o
common-obj-y += 9p-proxy.o
-endif
common-obj-$(CONFIG_XEN) += xen-9p-backend.o
obj-$(CONFIG_VIRTIO_9P) += virtio-9p-device.o
diff --git a/hw/Kconfig b/hw/Kconfig
new file mode 100644
index 0000000000..d5ecd02070
--- /dev/null
+++ b/hw/Kconfig
@@ -0,0 +1,73 @@
+# devices Kconfig
+source 9pfs/Kconfig
+source acpi/Kconfig
+source adc/Kconfig
+source audio/Kconfig
+source block/Kconfig
+source bt/Kconfig
+source char/Kconfig
+source core/Kconfig
+source display/Kconfig
+source dma/Kconfig
+source gpio/Kconfig
+source hyperv/Kconfig
+source i2c/Kconfig
+source ide/Kconfig
+source input/Kconfig
+source intc/Kconfig
+source ipack/Kconfig
+source ipmi/Kconfig
+source isa/Kconfig
+source mem/Kconfig
+source misc/Kconfig
+source net/Kconfig
+source nvram/Kconfig
+source pci-bridge/Kconfig
+source pci-host/Kconfig
+source pcmcia/Kconfig
+source pci/Kconfig
+source scsi/Kconfig
+source sd/Kconfig
+source smbios/Kconfig
+source ssi/Kconfig
+source timer/Kconfig
+source tpm/Kconfig
+source usb/Kconfig
+source virtio/Kconfig
+source vfio/Kconfig
+source watchdog/Kconfig
+
+# arch Kconfig
+source arm/Kconfig
+source alpha/Kconfig
+source cris/Kconfig
+source hppa/Kconfig
+source i386/Kconfig
+source lm32/Kconfig
+source m68k/Kconfig
+source microblaze/Kconfig
+source mips/Kconfig
+source moxie/Kconfig
+source nios2/Kconfig
+source openrisc/Kconfig
+source ppc/Kconfig
+source riscv/Kconfig
+source s390x/Kconfig
+source sh4/Kconfig
+source sparc/Kconfig
+source sparc64/Kconfig
+source tricore/Kconfig
+source unicore32/Kconfig
+source xtensa/Kconfig
+
+# Symbols used by multiple targets
+config TEST_DEVICES
+ bool
+
+config XILINX
+ bool
+ select PTIMER # for hw/timer/xilinx_timer.c
+
+config XILINX_AXI
+ bool
+ select PTIMER # for hw/dma/xilinx_axidma.c
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index e2fcd6aafc..82aa7fab8e 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -1,4 +1,4 @@
-devices-dirs-$(call land,$(CONFIG_VIRTFS),$(call lor,$(CONFIG_VIRTIO),$(CONFIG_XEN))) += 9pfs/
+devices-dirs-$(call lor,$(CONFIG_VIRTIO_9P),$(call land,$(CONFIG_VIRTFS),$(CONFIG_XEN))) += 9pfs/
devices-dirs-$(CONFIG_SOFTMMU) += acpi/
devices-dirs-$(CONFIG_SOFTMMU) += adc/
devices-dirs-$(CONFIG_SOFTMMU) += audio/
@@ -10,7 +10,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += display/
devices-dirs-$(CONFIG_SOFTMMU) += dma/
devices-dirs-$(CONFIG_SOFTMMU) += gpio/
devices-dirs-$(CONFIG_HYPERV) += hyperv/
-devices-dirs-$(CONFIG_SOFTMMU) += i2c/
+devices-dirs-$(CONFIG_I2C) += i2c/
devices-dirs-$(CONFIG_SOFTMMU) += ide/
devices-dirs-$(CONFIG_SOFTMMU) += input/
devices-dirs-$(CONFIG_SOFTMMU) += intc/
diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
new file mode 100644
index 0000000000..eca3beed75
--- /dev/null
+++ b/hw/acpi/Kconfig
@@ -0,0 +1,29 @@
+config ACPI
+ bool
+
+config ACPI_X86
+ bool
+ select ACPI
+ select ACPI_NVDIMM
+ select ACPI_CPU_HOTPLUG
+ select ACPI_MEMORY_HOTPLUG
+
+config ACPI_X86_ICH
+ bool
+ select ACPI_X86
+
+config ACPI_CPU_HOTPLUG
+ bool
+
+config ACPI_MEMORY_HOTPLUG
+ bool
+ select MEM_DEVICE
+
+config ACPI_NVDIMM
+ bool
+ depends on ACPI
+
+config ACPI_VMGENID
+ bool
+ default y
+ depends on PC
diff --git a/hw/adc/Kconfig b/hw/adc/Kconfig
new file mode 100644
index 0000000000..25d2229fb8
--- /dev/null
+++ b/hw/adc/Kconfig
@@ -0,0 +1,2 @@
+config STM32F2XX_ADC
+ bool
diff --git a/hw/alpha/Kconfig b/hw/alpha/Kconfig
new file mode 100644
index 0000000000..22cefd9577
--- /dev/null
+++ b/hw/alpha/Kconfig
@@ -0,0 +1,12 @@
+config DP264
+ bool
+ imply PCI_DEVICES
+ imply TEST_DEVICES
+ select I82374
+ select I8254
+ select I8259
+ select IDE_CMD646
+ select MC146818RTC
+ select PCI
+ select PCKBD
+ select SMC37C669
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
new file mode 100644
index 0000000000..d298fbdc89
--- /dev/null
+++ b/hw/arm/Kconfig
@@ -0,0 +1,124 @@
+config ARM_VIRT
+ bool
+ imply VFIO_PLATFORM
+
+config DIGIC
+ bool
+ select PTIMER
+
+config EXYNOS4
+ bool
+ select PTIMER
+
+config HIGHBANK
+ bool
+
+config INTEGRATOR
+ bool
+
+config MAINSTONE
+ bool
+
+config MUSICPAL
+ bool
+ select PTIMER
+
+config NETDUINO2
+ bool
+
+config NSERIES
+ bool
+
+config OMAP
+ bool
+
+config PXA2XX
+ bool
+
+config REALVIEW
+ bool
+
+config STELLARIS
+ bool
+
+config STRONGARM
+ bool
+
+config VERSATILE
+ bool
+
+config ZYNQ
+ bool
+
+config ARM_V7M
+ bool
+
+config ALLWINNER_A10
+ bool
+
+config RASPI
+ bool
+
+config STM32F205_SOC
+ bool
+
+config XLNX_ZYNQMP_ARM
+ bool
+
+config XLNX_VERSAL
+ bool
+
+config FSL_IMX25
+ bool
+
+config FSL_IMX31
+ bool
+
+config FSL_IMX6
+ bool
+
+config ASPEED_SOC
+ bool
+
+config MPS2
+ bool
+
+config FSL_IMX7
+ bool
+
+config ARM_SMMUV3
+ bool
+
+config FSL_IMX6UL
+ bool
+
+config NRF51_SOC
+ bool
+
+config MSF2
+ bool
+ select PTIMER
+
+config ZAURUS
+ bool
+
+config A9MPCORE
+ bool
+
+config A15MPCORE
+ bool
+
+config ARM11MPCORE
+ bool
+
+config ARMSSE
+ bool
+
+config ARMSSE_CPUID
+ bool
+
+config ARMSSE_MHU
+ bool
+
+config MUSCA
+ bool
diff --git a/hw/audio/Kconfig b/hw/audio/Kconfig
new file mode 100644
index 0000000000..e9c6fed826
--- /dev/null
+++ b/hw/audio/Kconfig
@@ -0,0 +1,52 @@
+config SB16
+ bool
+ default y
+ depends on ISA_BUS
+
+config ES1370
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+
+config AC97
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+
+config ADLIB
+ bool
+ default y
+ depends on ISA_BUS
+
+config GUS
+ bool
+ default y
+ depends on ISA_BUS
+
+config CS4231A
+ bool
+ default y
+ depends on ISA_BUS
+
+config HDA
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+
+config PCSPK
+ bool
+ default y
+ depends on I8254
+
+config WM8750
+ bool
+ depends on I2C
+
+config PL041
+ bool
+
+config CS4231
+ bool
+
+config MARVELL_88W8618
+ bool
diff --git a/hw/block/Kconfig b/hw/block/Kconfig
new file mode 100644
index 0000000000..df96dc5dcc
--- /dev/null
+++ b/hw/block/Kconfig
@@ -0,0 +1,39 @@
+config FDC
+ bool
+ # FIXME: there is no separate file for the MMIO floppy disk controller, so
+ # select ISA_BUS here instead of polluting each board that requires one
+ select ISA_BUS
+
+config SSI_M25P80
+ bool
+
+config NAND
+ bool
+
+config PFLASH_CFI01
+ bool
+
+config PFLASH_CFI02
+ bool
+
+config ECC
+ bool
+
+config ONENAND
+ bool
+
+config NVME_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+
+config VIRTIO_BLK
+ bool
+ default y
+ depends on VIRTIO
+
+config VHOST_USER_BLK
+ bool
+ # Only PCI devices are provided for now
+ default y if VIRTIO_PCI
+ depends on VIRTIO && VHOST_USER && LINUX
diff --git a/hw/block/Makefile.objs b/hw/block/Makefile.objs
index e206b8e712..f5f643f0cc 100644
--- a/hw/block/Makefile.objs
+++ b/hw/block/Makefile.objs
@@ -12,5 +12,6 @@ common-obj-$(CONFIG_NVME_PCI) += nvme.o
obj-$(CONFIG_SH4) += tc58128.o
obj-$(CONFIG_VIRTIO_BLK) += virtio-blk.o
-obj-$(CONFIG_VIRTIO_BLK) += dataplane/
obj-$(CONFIG_VHOST_USER_BLK) += vhost-user-blk.o
+
+obj-y += dataplane/
diff --git a/hw/block/dataplane/Makefile.objs b/hw/block/dataplane/Makefile.objs
index c6c68dbc00..0c5270268e 100644
--- a/hw/block/dataplane/Makefile.objs
+++ b/hw/block/dataplane/Makefile.objs
@@ -1,2 +1,2 @@
-obj-y += virtio-blk.o
+obj-$(CONFIG_VIRTIO_BLK) += virtio-blk.o
obj-$(CONFIG_XEN) += xen-block.o
diff --git a/hw/bt/Kconfig b/hw/bt/Kconfig
new file mode 100644
index 0000000000..554a9ee75e
--- /dev/null
+++ b/hw/bt/Kconfig
@@ -0,0 +1,2 @@
+config BLUETOOTH
+ bool
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
new file mode 100644
index 0000000000..6360c9fffa
--- /dev/null
+++ b/hw/char/Kconfig
@@ -0,0 +1,42 @@
+config ESCC
+ bool
+
+config PARALLEL
+ bool
+ default y
+ depends on ISA_BUS
+
+config PL011
+ bool
+
+config SERIAL
+ bool
+
+config SERIAL_ISA
+ bool
+ default y
+ depends on ISA_BUS
+ select SERIAL
+
+config SERIAL_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select SERIAL
+
+config VIRTIO_SERIAL
+ bool
+ default y
+ depends on VIRTIO
+
+config STM32F2XX_USART
+ bool
+
+config CMSDK_APB_UART
+ bool
+
+config SCLPCONSOLE
+ bool
+
+config TERMINAL3270
+ bool
diff --git a/hw/core/Kconfig b/hw/core/Kconfig
new file mode 100644
index 0000000000..c2a1ae8122
--- /dev/null
+++ b/hw/core/Kconfig
@@ -0,0 +1,11 @@
+config EMPTY_SLOT
+ bool
+
+config PTIMER
+ bool
+
+config FITLOADER
+ bool
+
+config PLATFORM_BUS
+ bool
diff --git a/hw/cpu/Kconfig b/hw/cpu/Kconfig
new file mode 100644
index 0000000000..1767d028ac
--- /dev/null
+++ b/hw/cpu/Kconfig
@@ -0,0 +1,8 @@
+config ARM11MPCORE
+ bool
+
+config A9MPCORE
+ bool
+
+config A15MPCORE
+ bool
diff --git a/hw/cris/Kconfig b/hw/cris/Kconfig
new file mode 100644
index 0000000000..884ad2cbc0
--- /dev/null
+++ b/hw/cris/Kconfig
@@ -0,0 +1,9 @@
+config AXIS
+ bool
+ select ETRAXFS
+ select PFLASH_CFI02
+ select NAND
+
+config ETRAXFS
+ bool
+ select PTIMER
diff --git a/hw/display/Kconfig b/hw/display/Kconfig
new file mode 100644
index 0000000000..a96ea763a8
--- /dev/null
+++ b/hw/display/Kconfig
@@ -0,0 +1,108 @@
+config EDID
+ bool
+
+config FW_CFG_DMA
+ bool
+
+config ADS7846
+ bool
+
+config VGA_CIRRUS
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select VGA
+
+config G364FB
+ bool
+
+config JAZZ_LED
+ bool
+
+config PL110
+ bool
+
+config SII9022
+ bool
+ depends on I2C
+
+config SSD0303
+ bool
+ depends on I2C
+
+config SSD0323
+ bool
+
+config VGA_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select VGA
+ select EDID
+
+config VGA_ISA
+ bool
+ depends on ISA_BUS
+ select VGA
+
+config VGA_ISA_MM
+ bool
+ select VGA
+
+config VMWARE_VGA
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select VGA
+
+config BOCHS_DISPLAY
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select VGA
+ select EDID
+
+config BLIZZARD
+ bool
+
+config FRAMEBUFFER
+ bool
+
+config MILKYMIST_TMU2
+ bool
+ depends on OPENGL && X11
+
+config SM501
+ bool
+ select I2C
+ select DDC
+ select SERIAL
+
+config TCX
+ bool
+
+config CG3
+ bool
+
+config VGA
+ bool
+
+config QXL
+ bool
+ depends on SPICE && PCI
+ select VGA
+
+config VIRTIO_GPU
+ bool
+ default y
+ depends on VIRTIO
+ select EDID
+
+config VIRTIO_VGA
+ bool
+ default y if PCI_DEVICES
+ depends on VIRTIO_PCI
+ select VGA
+
+config DPCD
+ bool
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index 7c4ae9a0fd..576fca4eb6 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-y += edid-generate.o
+common-obj-$(CONFIG_EDID) += edid-generate.o edid-region.o
common-obj-$(CONFIG_FW_CFG_DMA) += ramfb.o
common-obj-$(CONFIG_FW_CFG_DMA) += ramfb-standalone.o
@@ -15,12 +15,10 @@ common-obj-$(CONFIG_SSD0323) += ssd0323.o
common-obj-$(CONFIG_XEN) += xenfb.o
common-obj-$(CONFIG_VGA_PCI) += vga-pci.o
-common-obj-$(CONFIG_VGA_PCI) += edid-region.o
common-obj-$(CONFIG_VGA_ISA) += vga-isa.o
common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o
common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o
common-obj-$(CONFIG_BOCHS_DISPLAY) += bochs-display.o
-common-obj-$(CONFIG_BOCHS_DISPLAY) += edid-region.o
common-obj-$(CONFIG_BLIZZARD) += blizzard.o
common-obj-$(CONFIG_EXYNOS4) += exynos4210_fimd.o
diff --git a/hw/dma/Kconfig b/hw/dma/Kconfig
new file mode 100644
index 0000000000..751dec5426
--- /dev/null
+++ b/hw/dma/Kconfig
@@ -0,0 +1,21 @@
+config RC4030
+ bool
+
+config PL080
+ bool
+
+config PL330
+ bool
+
+config I82374
+ bool
+ select I8257
+
+config I8257
+ bool
+
+config ZYNQ_DEVCFG
+ bool
+
+config STP2000
+ bool
diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig
new file mode 100644
index 0000000000..9227cb5598
--- /dev/null
+++ b/hw/gpio/Kconfig
@@ -0,0 +1,9 @@
+config MAX7310
+ bool
+ depends on I2C
+
+config PL061
+ bool
+
+config GPIO_KEY
+ bool
diff --git a/hw/hppa/Kconfig b/hw/hppa/Kconfig
new file mode 100644
index 0000000000..2d9b072c21
--- /dev/null
+++ b/hw/hppa/Kconfig
@@ -0,0 +1,10 @@
+config DINO
+ bool
+ imply PCI_DEVICES
+ select PCI
+ select SERIAL
+ select ISA_BUS
+ select I8259
+ select IDE_CMD646
+ select MC146818RTC
+ select LSI_SCSI_PCI
diff --git a/hw/hyperv/Kconfig b/hw/hyperv/Kconfig
new file mode 100644
index 0000000000..a1fa8ff9be
--- /dev/null
+++ b/hw/hyperv/Kconfig
@@ -0,0 +1,8 @@
+config HYPERV
+ bool
+ depends on KVM
+
+config HYPERV_TESTDEV
+ bool
+ default y if TEST_DEVICES
+ depends on HYPERV
diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig
new file mode 100644
index 0000000000..ef1caa6d89
--- /dev/null
+++ b/hw/i2c/Kconfig
@@ -0,0 +1,27 @@
+config I2C
+ bool
+
+config SMBUS_EEPROM
+ bool
+ depends on I2C
+
+config DDC
+ bool
+ depends on I2C
+ select EDID
+
+config VERSATILE_I2C
+ bool
+ select I2C
+
+config ACPI_SMBUS
+ bool
+ select I2C
+
+config BITBANG_I2C
+ bool
+ select I2C
+
+config IMX_I2C
+ bool
+ select I2C
diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs
index 9205cbee16..2a3c106551 100644
--- a/hw/i2c/Makefile.objs
+++ b/hw/i2c/Makefile.objs
@@ -2,7 +2,7 @@ common-obj-$(CONFIG_I2C) += core.o smbus_slave.o smbus_master.o
common-obj-$(CONFIG_SMBUS_EEPROM) += smbus_eeprom.o
common-obj-$(CONFIG_DDC) += i2c-ddc.o
common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o
-common-obj-$(CONFIG_ACPI_X86) += smbus_ich9.o
+common-obj-$(CONFIG_ACPI_X86_ICH) += smbus_ich9.o
common-obj-$(CONFIG_ACPI_SMBUS) += pm_smbus.o
common-obj-$(CONFIG_BITBANG_I2C) += bitbang_i2c.o
common-obj-$(CONFIG_EXYNOS4) += exynos4210_i2c.o
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
new file mode 100644
index 0000000000..78fd70396a
--- /dev/null
+++ b/hw/i386/Kconfig
@@ -0,0 +1,99 @@
+config SEV
+ bool
+ depends on KVM
+
+config PC
+ bool
+ imply APPLESMC
+ imply HYPERV
+ imply ISA_IPMI_KCS
+ imply ISA_IPMI_BT
+ imply ISA_DEBUG
+ imply PCI_DEVICES
+ imply PVPANIC
+ imply QXL
+ imply SEV
+ imply SGA
+ imply TEST_DEVICES
+ imply TPM_CRB
+ imply TPM_TIS
+ select FDC
+ select I8259
+ select I8254
+ select PCKBD
+ select PCSPK
+ select I82374
+ select I8257
+ select MC146818RTC
+ # Needed by the board code:
+ select PARALLEL
+ # For ACPI builder:
+ select SERIAL_ISA
+ select ACPI_VMGENID
+
+config PC_PCI
+ bool
+ select APIC
+ select IOAPIC
+ select APM
+ select PC
+
+config PC_ACPI
+ bool
+ select ACPI_X86
+ select ACPI_CPU_HOTPLUG
+ select ACPI_MEMORY_HOTPLUG
+ select SMBUS_EEPROM
+ select PFLASH_CFI01
+ depends on ACPI_SMBUS
+
+config I440FX
+ bool
+ select PC_PCI
+ select PC_ACPI
+ select ACPI_SMBUS
+ select PCI_PIIX
+ select IDE_PIIX
+ select DIMM
+ select SMBIOS
+ select VMPORT
+ select VMMOUSE
+ select FW_CFG_DMA
+
+config ISAPC
+ bool
+ select ISA_BUS
+ select PC
+ select IDE_ISA
+ select VGA_ISA
+ # FIXME: it is in the same file as i440fx, and does not compile
+ # if separated
+ depends on I440FX
+
+config Q35
+ bool
+ imply VTD
+ imply AMD_IOMMU
+ select PC_PCI
+ select PC_ACPI
+ select PCI_EXPRESS_Q35
+ select LPC_ICH9
+ select AHCI
+ select DIMM
+ select SMBIOS
+ select VMPORT
+ select VMMOUSE
+ select FW_CFG_DMA
+
+config VTD
+ bool
+
+config AMD_IOMMU
+ bool
+
+config VMPORT
+ bool
+
+config VMMOUSE
+ bool
+ depends on VMPORT
diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs
index 3de7ca2bb9..27248a0777 100644
--- a/hw/i386/Makefile.objs
+++ b/hw/i386/Makefile.objs
@@ -4,8 +4,9 @@ obj-y += pc.o
obj-$(CONFIG_I440FX) += pc_piix.o
obj-$(CONFIG_Q35) += pc_q35.o
obj-y += pc_sysfw.o
-obj-$(CONFIG_VTD) += x86-iommu.o intel_iommu.o
-obj-$(CONFIG_AMD_IOMMU) += x86-iommu.o amd_iommu.o
+obj-y += x86-iommu.o
+obj-$(CONFIG_VTD) += intel_iommu.o
+obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o
obj-$(CONFIG_XEN) += ../xenpv/ xen/
obj-$(CONFIG_VMPORT) += vmport.o
obj-$(CONFIG_VMMOUSE) += vmmouse.o
diff --git a/hw/ide/Kconfig b/hw/ide/Kconfig
new file mode 100644
index 0000000000..ab47b6a7a3
--- /dev/null
+++ b/hw/ide/Kconfig
@@ -0,0 +1,54 @@
+config IDE_CORE
+ bool
+
+config IDE_QDEV
+ bool
+ select IDE_CORE
+
+config IDE_PCI
+ bool
+ depends on PCI
+ select IDE_CORE
+
+config IDE_ISA
+ bool
+ depends on ISA_BUS
+ select IDE_QDEV
+
+config IDE_PIIX
+ bool
+ select IDE_PCI
+ select IDE_QDEV
+
+config IDE_CMD646
+ bool
+ select IDE_PCI
+ select IDE_QDEV
+
+config IDE_MACIO
+ bool
+ select IDE_QDEV
+
+config IDE_MMIO
+ bool
+ select IDE_QDEV
+
+config IDE_VIA
+ bool
+ select IDE_PCI
+ select IDE_QDEV
+
+config MICRODRIVE
+ bool
+ select IDE_QDEV
+
+config AHCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select IDE_QDEV
+
+config IDE_SII3112
+ bool
+ select IDE_PCI
+ select IDE_QDEV
diff --git a/hw/input/Kconfig b/hw/input/Kconfig
new file mode 100644
index 0000000000..e2e66f0858
--- /dev/null
+++ b/hw/input/Kconfig
@@ -0,0 +1,33 @@
+config ADB
+ bool
+
+config LM832X
+ bool
+ depends on I2C
+
+config PCKBD
+ bool
+ default y
+ depends on ISA_BUS
+
+config PL050
+ bool
+
+config STELLARIS_INPUT
+ bool
+
+config TSC2005
+ bool
+
+config VIRTIO_INPUT
+ bool
+ default y
+ depends on VIRTIO
+
+config VIRTIO_INPUT_HOST
+ bool
+ default y
+ depends on VIRTIO && LINUX
+
+config TSC210X
+ bool
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
new file mode 100644
index 0000000000..de10a6bcbf
--- /dev/null
+++ b/hw/intc/Kconfig
@@ -0,0 +1,57 @@
+config HEATHROW_PIC
+ bool
+
+config I8259
+ bool
+
+config PL190
+ bool
+
+config IOAPIC
+ bool
+
+config ARM_GIC
+ bool
+
+config OPENPIC
+ bool
+
+config APIC
+ bool
+
+config ARM_GIC_KVM
+ bool
+ default y
+ depends on ARM_GIC && KVM
+
+config OPENPIC_KVM
+ bool
+ default y
+ depends on OPENPIC && KVM
+
+config XICS
+ bool
+ depends on POWERNV || PSERIES
+
+config XICS_SPAPR
+ bool
+ select XICS
+
+config XICS_KVM
+ bool
+ default y
+ depends on XICS && KVM
+
+config ALLWINNER_A10_PIC
+ bool
+
+config S390_FLIC
+ bool
+
+config S390_FLIC_KVM
+ bool
+ default y
+ depends on S390_FLIC && KVM
+
+config OMPIC
+ bool
diff --git a/hw/ipack/Kconfig b/hw/ipack/Kconfig
new file mode 100644
index 0000000000..f8da24a62b
--- /dev/null
+++ b/hw/ipack/Kconfig
@@ -0,0 +1,4 @@
+config IPACK
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
diff --git a/hw/ipmi/Kconfig b/hw/ipmi/Kconfig
new file mode 100644
index 0000000000..b944fae100
--- /dev/null
+++ b/hw/ipmi/Kconfig
@@ -0,0 +1,22 @@
+config IPMI
+ bool
+
+config IPMI_LOCAL
+ bool
+ default y
+ depends on IPMI
+
+config IPMI_EXTERN
+ bool
+ default y
+ depends on IPMI
+
+config ISA_IPMI_KCS
+ bool
+ depends on ISA_BUS
+ select IPMI
+
+config ISA_IPMI_BT
+ bool
+ depends on ISA_BUS
+ select IPMI
diff --git a/hw/isa/Kconfig b/hw/isa/Kconfig
new file mode 100644
index 0000000000..57e09a0cb8
--- /dev/null
+++ b/hw/isa/Kconfig
@@ -0,0 +1,53 @@
+config ISA_BUS
+ bool
+
+config APM
+ bool
+
+config I82378
+ bool
+ select ISA_BUS
+ select I8259
+ select I8254
+ select I82374
+ select MC146818RTC
+
+config PC87312
+ bool
+ select ISA_BUS
+ select I8259
+ select I8254
+ select I8257
+ select MC146818RTC
+ select SERIAL_ISA
+ select PARALLEL
+ select FDC
+ select IDE_ISA
+
+config PIIX4
+ bool
+ # For historical reasons, SuperIO devices are created in the board
+ # for PIIX4.
+ select ISA_BUS
+
+config VT82C686
+ bool
+ select ISA_BUS
+ select ACPI_SMBUS
+ select SERIAL_ISA
+ select FDC
+
+config SMC37C669
+ bool
+ select ISA_BUS
+ select SERIAL_ISA
+ select PARALLEL
+ select FDC
+
+config LPC_ICH9
+ bool
+ # For historical reasons, SuperIO devices are created in the board
+ # for ICH9.
+ select ISA_BUS
+ select ACPI_SMBUS
+ select ACPI_X86_ICH
diff --git a/hw/lm32/Kconfig b/hw/lm32/Kconfig
new file mode 100644
index 0000000000..3d09c2dd6f
--- /dev/null
+++ b/hw/lm32/Kconfig
@@ -0,0 +1,13 @@
+config LM32
+ bool
+ select PTIMER
+ select PFLASH_CFI02
+
+config MILKYMIST
+ bool
+ # FIXME: disabling it results in compile-time errors
+ select MILKYMIST_TMU2 if OPENGL && X11
+ select PTIMER
+ select PFLASH_CFI01
+ select FRAMEBUFFER
+ select SD
diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig
new file mode 100644
index 0000000000..49ef0b3f6d
--- /dev/null
+++ b/hw/m68k/Kconfig
@@ -0,0 +1,9 @@
+config AN5206
+ bool
+ select COLDFIRE
+ select PTIMER
+
+config MCF5208
+ bool
+ select COLDFIRE
+ select PTIMER
diff --git a/hw/mem/Kconfig b/hw/mem/Kconfig
new file mode 100644
index 0000000000..620fd4cb59
--- /dev/null
+++ b/hw/mem/Kconfig
@@ -0,0 +1,11 @@
+config DIMM
+ bool
+ select MEM_DEVICE
+
+config MEM_DEVICE
+ bool
+
+config NVDIMM
+ bool
+ default y
+ depends on PC
diff --git a/hw/microblaze/Kconfig b/hw/microblaze/Kconfig
new file mode 100644
index 0000000000..c4dc120973
--- /dev/null
+++ b/hw/microblaze/Kconfig
@@ -0,0 +1,20 @@
+config PETALOGIX_S3ADSP1800
+ bool
+ select PFLASH_CFI01
+ select XILINX
+ select XILINX_AXI
+ select XILINX_ETHLITE
+
+config PETALOGIX_ML605
+ bool
+ select PFLASH_CFI01
+ select SERIAL
+ select SSI_M25P80
+ select XILINX
+ select XILINX_AXI
+ select XILINX_ETHLITE
+ select XILINX_SPI
+
+config XLNX_ZYNQMP_PMU
+ bool
+ select XLNX_ZYNQMP
diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig
new file mode 100644
index 0000000000..cdc07e59b6
--- /dev/null
+++ b/hw/mips/Kconfig
@@ -0,0 +1,21 @@
+config R4K
+ bool
+
+config MALTA
+ bool
+
+config MIPSSIM
+ bool
+
+config JAZZ
+ bool
+
+config FULONG
+ bool
+
+config MIPS_CPS
+ bool
+ select PTIMER
+
+config MIPS_BOSTON
+ bool
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
new file mode 100644
index 0000000000..2c60be99bc
--- /dev/null
+++ b/hw/misc/Kconfig
@@ -0,0 +1,118 @@
+config APPLESMC
+ bool
+ depends on ISA_BUS
+
+config MAX111X
+ bool
+
+config TMP105
+ bool
+ depends on I2C
+
+config TMP421
+ bool
+ depends on I2C
+
+config ISA_DEBUG
+ bool
+ depends on ISA_BUS
+
+config SGA
+ bool
+ depends on ISA_BUS
+
+config ISA_TESTDEV
+ bool
+ default y if TEST_DEVICES
+ depends on ISA_BUS
+
+config PCI_TESTDEV
+ bool
+ default y if TEST_DEVICES
+ depends on PCI
+
+config EDU
+ bool
+ default y if TEST_DEVICES
+ depends on PCI
+
+config PCA9552
+ bool
+ depends on I2C
+
+config PL310
+ bool
+
+config INTEGRATOR_DEBUG
+ bool
+
+config A9SCU
+ bool
+
+config ARM11SCU
+ bool
+
+config MOS6522
+ bool
+
+config MACIO
+ bool
+ select CUDA
+ select ESCC
+ select IDE_MACIO
+ select MAC_DBDMA
+ select MAC_NVRAM
+ select MOS6522
+
+config IVSHMEM_DEVICE
+ bool
+ default y if PCI_DEVICES
+ depends on PCI && LINUX && IVSHMEM
+
+config ECCMEMCTL
+ bool
+ select ECC
+
+config IMX
+ bool
+ select PTIMER
+
+config STM32F2XX_SYSCFG
+ bool
+
+config MIPS_ITU
+ bool
+
+config MPS2_FPGAIO
+ bool
+
+config MPS2_SCC
+ bool
+
+config TZ_MPC
+ bool
+
+config TZ_MSC
+ bool
+
+config TZ_PPC
+ bool
+
+config IOTKIT_SECCTL
+ bool
+
+config IOTKIT_SYSCTL
+ bool
+
+config IOTKIT_SYSINFO
+ bool
+
+config PVPANIC
+ bool
+ depends on ISA_BUS
+
+config AUX
+ bool
+ select I2C
+
+source macio/Kconfig
diff --git a/hw/misc/macio/Kconfig b/hw/misc/macio/Kconfig
new file mode 100644
index 0000000000..c6caeb672f
--- /dev/null
+++ b/hw/misc/macio/Kconfig
@@ -0,0 +1,11 @@
+config CUDA
+ bool
+
+config MAC_PMU
+ bool
+
+config MAC_DBDMA
+ bool
+
+config MACIO_GPIO
+ bool
diff --git a/hw/moxie/Kconfig b/hw/moxie/Kconfig
new file mode 100644
index 0000000000..3793ef0372
--- /dev/null
+++ b/hw/moxie/Kconfig
@@ -0,0 +1,3 @@
+config MOXIESIM
+ bool
+ select SERIAL
diff --git a/hw/net/Kconfig b/hw/net/Kconfig
new file mode 100644
index 0000000000..c00ec03cd1
--- /dev/null
+++ b/hw/net/Kconfig
@@ -0,0 +1,125 @@
+config DP8393X
+ bool
+
+config NE2000_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+
+config EEPRO100_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+
+config PCNET_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select PCNET_COMMON
+
+config PCNET_COMMON
+ bool
+
+config E1000_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+
+config E1000E_PCI_EXPRESS
+ bool
+ default y if PCI_DEVICES
+ depends on PCI_EXPRESS
+
+config RTL8139_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+
+config VMXNET3_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+
+config SMC91C111
+ bool
+
+config LAN9118
+ bool
+ select PTIMER
+
+config NE2000_ISA
+ bool
+ default y
+ depends on ISA_BUS
+ depends on PCI # for NE2000State
+ select NE2000_PCI
+
+config OPENCORES_ETH
+ bool
+
+config XGMAC
+ bool
+
+config MIPSNET
+ bool
+
+config ALLWINNER_EMAC
+ bool
+
+config IMX_FEC
+ bool
+
+config CADENCE
+ bool
+
+config STELLARIS_ENET
+ bool
+
+config LANCE
+ bool
+ select PCNET_COMMON
+
+config SUNHME
+ bool
+
+config FTGMAC100
+ bool
+
+config SUNGEM
+ bool
+ depends on PCI
+
+config COLDFIRE
+ bool
+
+config XILINX_ETHLITE
+ bool
+
+config VIRTIO_NET
+ bool
+ default y
+ depends on VIRTIO
+
+config ETSEC
+ bool
+ select PTIMER
+
+config ROCKER
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+
+config CAN_BUS
+ bool
+
+config CAN_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select CAN_BUS
+
+config CAN_SJA1000
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select CAN_BUS
diff --git a/hw/nios2/Kconfig b/hw/nios2/Kconfig
new file mode 100644
index 0000000000..ab953e0077
--- /dev/null
+++ b/hw/nios2/Kconfig
@@ -0,0 +1,8 @@
+config NIOS2_10M50
+ bool
+ select NIOS2
+ select SERIAL
+ select ALTERA_TIMER
+
+config NIOS2
+ bool
diff --git a/hw/nvram/Kconfig b/hw/nvram/Kconfig
new file mode 100644
index 0000000000..ebaa749ce9
--- /dev/null
+++ b/hw/nvram/Kconfig
@@ -0,0 +1,9 @@
+config DS1225Y
+ bool
+
+config AT24C
+ bool
+ depends on I2C
+
+config MAC_NVRAM
+ bool
diff --git a/hw/openrisc/Kconfig b/hw/openrisc/Kconfig
new file mode 100644
index 0000000000..6c1e86884e
--- /dev/null
+++ b/hw/openrisc/Kconfig
@@ -0,0 +1,5 @@
+config OR1K_SIM
+ bool
+ select SERIAL
+ select OPENCORES_ETH
+ select OMPIC
diff --git a/hw/pci-bridge/Kconfig b/hw/pci-bridge/Kconfig
new file mode 100644
index 0000000000..b167b98497
--- /dev/null
+++ b/hw/pci-bridge/Kconfig
@@ -0,0 +1,29 @@
+config PCIE_PORT
+ bool
+ default y if PCI_DEVICES
+ depends on PCI_EXPRESS
+
+config PXB
+ bool
+ default y if Q35
+
+config XIO3130
+ bool
+ default y if PCI_DEVICES
+ depends on PCI_EXPRESS
+
+config IOH3420
+ bool
+ default y if PCI_DEVICES
+ depends on PCI_EXPRESS
+
+config I82801B11
+ bool
+ default y if PCI_DEVICES
+ depends on PCI_EXPRESS
+
+config DEC_PCI
+ bool
+
+config SIMBA
+ bool
diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig
new file mode 100644
index 0000000000..b39ea297ba
--- /dev/null
+++ b/hw/pci-host/Kconfig
@@ -0,0 +1,51 @@
+config PAM
+ bool
+
+config PREP_PCI
+ select PCI
+ bool
+
+config GRACKLE_PCI
+ select PCI
+ bool
+
+config UNIN_PCI
+ bool
+ select PCI
+ select DEC_PCI
+ select OPENPIC
+
+config PPCE500_PCI
+ select PCI
+ bool
+
+config VERSATILE_PCI
+ select PCI
+ bool
+
+config PCI_SABRE
+ select PCI
+ bool
+
+config PCI_PIIX
+ bool
+ select PCI
+ select PAM
+ select ISA_BUS
+
+config PCI_EXPRESS_Q35
+ bool
+ select PCI_EXPRESS
+ select PAM
+
+config PCI_EXPRESS_GENERIC_BRIDGE
+ bool
+ select PCI_EXPRESS
+
+config PCI_EXPRESS_XILINX
+ bool
+ select PCI_EXPRESS
+
+config PCI_EXPRESS_DESIGNWARE
+ bool
+ select PCI_EXPRESS
diff --git a/hw/pci/Kconfig b/hw/pci/Kconfig
new file mode 100644
index 0000000000..3b8638b51d
--- /dev/null
+++ b/hw/pci/Kconfig
@@ -0,0 +1,9 @@
+config PCI
+ bool
+
+config PCI_EXPRESS
+ bool
+ select PCI
+
+config PCI_DEVICES
+ bool
diff --git a/hw/pci/Makefile.objs b/hw/pci/Makefile.objs
index 9f905e6344..c78f2fb24b 100644
--- a/hw/pci/Makefile.objs
+++ b/hw/pci/Makefile.objs
@@ -2,8 +2,13 @@ common-obj-$(CONFIG_PCI) += pci.o pci_bridge.o
common-obj-$(CONFIG_PCI) += msix.o msi.o
common-obj-$(CONFIG_PCI) += shpc.o
common-obj-$(CONFIG_PCI) += slotid_cap.o
-common-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
-common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
+common-obj-$(CONFIG_PCI) += pci_host.o
+
+# The functions in these modules can be used by devices too. Since we
+# allow plugging PCIe devices into PCI buses, include them even if
+# CONFIG_PCI_EXPRESS=n.
+common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o
+common-obj-$(CONFIG_PCI_EXPRESS) += pcie_port.o pcie_host.o
common-obj-$(call lnot,$(CONFIG_PCI)) += pci-stub.o
common-obj-$(CONFIG_ALL) += pci-stub.o
diff --git a/hw/pcmcia/Kconfig b/hw/pcmcia/Kconfig
new file mode 100644
index 0000000000..41f2df9136
--- /dev/null
+++ b/hw/pcmcia/Kconfig
@@ -0,0 +1,2 @@
+config PCMCIA
+ bool
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
new file mode 100644
index 0000000000..2b83637511
--- /dev/null
+++ b/hw/ppc/Kconfig
@@ -0,0 +1,121 @@
+config PSERIES
+ bool
+ imply PCI_DEVICES
+ imply TEST_DEVICES
+ select DIMM
+ select PCI
+ select SPAPR_VSCSI
+ select VFIO if LINUX # needed by spapr_pci_vfio.c
+ select XICS_SPAPR
+ select XIVE_SPAPR
+
+config SPAPR_RNG
+ bool
+ default y
+ depends on PSERIES
+
+config POWERNV
+ bool
+ imply PCI_DEVICES
+ imply TEST_DEVICES
+ select ISA_IPMI_BT
+ select IPMI_LOCAL
+ select ISA_BUS
+ select MC146818RTC
+ select XICS
+ select XIVE
+
+config PPC405
+ bool
+ select M48T59
+ select PFLASH_CFI02
+ select PPC4XX
+ select SERIAL
+
+config PPC440
+ bool
+ imply PCI_DEVICES
+ imply TEST_DEVICES
+ select PCI_EXPRESS
+ select PPC4XX
+ select SERIAL
+
+config PPC4XX
+ bool
+ select BITBANG_I2C
+ select PCI
+
+config SAM460EX
+ bool
+ select PFLASH_CFI01
+ select IDE_SII3112
+ select M41T80
+ select PPC440
+ select SERIAL
+ select SM501
+ select SMBUS_EEPROM
+ select USB_EHCI_SYSBUS
+ select USB_OHCI
+
+config PREP
+ bool
+ imply PCI_DEVICES
+ imply TEST_DEVICES
+ select CS4231A
+ select PREP_PCI
+ select I82374
+ select I82378
+ select LSI_SCSI_PCI
+ select M48T59
+ select PC87312
+ select RS6000_MC
+
+config RS6000_MC
+ bool
+
+config MAC_OLDWORLD
+ bool
+ imply PCI_DEVICES
+ imply SUNGEM
+ imply TEST_DEVICES
+ select ADB
+ select GRACKLE_PCI
+ select HEATHROW_PIC
+ select MACIO
+
+config MAC_NEWWORLD
+ bool
+ imply PCI_DEVICES
+ imply SUNGEM
+ imply TEST_DEVICES
+ select ADB
+ select MACIO
+ select MACIO_GPIO
+ select MAC_PMU
+ select UNIN_PCI
+
+config E500
+ bool
+ imply AT24C
+ select ETSEC
+ select OPENPIC
+ select PLATFORM_BUS
+ select PPCE500_PCI
+ select SERIAL
+
+config VIRTEX
+ bool
+ select PFLASH_CFI01
+ select SERIAL
+ select XILINX
+ select XILINX_ETHLITE
+
+config XIVE
+ bool
+ depends on POWERNV || PSERIES
+
+config XIVE_SPAPR
+ bool
+ default y
+ depends on PSERIES
+ select XIVE
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
new file mode 100644
index 0000000000..e0ee3043a6
--- /dev/null
+++ b/hw/riscv/Kconfig
@@ -0,0 +1,33 @@
+config HTIF
+ bool
+
+config HART
+ bool
+
+config SIFIVE
+ bool
+
+config SIFIVE_E
+ bool
+ select HART
+ select SIFIVE
+
+config SIFIVE_U
+ bool
+ select CADENCE
+ select HART
+ select SIFIVE
+
+config SPIKE
+ bool
+ select HART
+ select HTIF
+ select SIFIVE
+
+config RISCV_VIRT
+ bool
+ select HART
+ select SERIAL
+ select VIRTIO_MMIO
+ select PCI_EXPRESS_GENERIC_BRIDGE
+ select SIFIVE
diff --git a/hw/s390x/Kconfig b/hw/s390x/Kconfig
new file mode 100644
index 0000000000..a7046ea41f
--- /dev/null
+++ b/hw/s390x/Kconfig
@@ -0,0 +1,11 @@
+config S390_CCW_VIRTIO
+ bool
+ imply VIRTIO_PCI
+ imply TERMINAL3270
+ imply VFIO_AP
+ imply VFIO_CCW
+ imply WDT_DIAG288
+ select PCI
+ select S390_FLIC
+ select SCLPCONSOLE
+ select VIRTIO_CCW
diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
index bfd5326d7c..e02ed80b68 100644
--- a/hw/s390x/Makefile.objs
+++ b/hw/s390x/Makefile.objs
@@ -6,7 +6,8 @@ obj-y += sclpcpu.o
obj-y += ipl.o
obj-y += css.o
obj-$(CONFIG_S390_CCW_VIRTIO) += s390-virtio-ccw.o
-obj-y += 3270-ccw.o
+obj-$(CONFIG_TERMINAL3270) += 3270-ccw.o
+ifeq ($(CONFIG_VIRTIO_CCW),y)
obj-y += virtio-ccw.o
obj-$(CONFIG_VIRTIO_SERIAL) += virtio-ccw-serial.o
obj-$(CONFIG_VIRTIO_BALLOON) += virtio-ccw-balloon.o
@@ -19,6 +20,7 @@ obj-$(CONFIG_VIRTIO_NET) += virtio-ccw-net.o
obj-$(CONFIG_VIRTIO_BLK) += virtio-ccw-blk.o
obj-$(call land,$(CONFIG_VIRTIO_9P),$(CONFIG_VIRTFS)) += virtio-ccw-9p.o
obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock-ccw.o
+endif
obj-y += css-bridge.o
obj-y += ccw-device.o
obj-y += s390-pci-bus.o s390-pci-inst.o
diff --git a/hw/scsi/Kconfig b/hw/scsi/Kconfig
new file mode 100644
index 0000000000..b3ba540c17
--- /dev/null
+++ b/hw/scsi/Kconfig
@@ -0,0 +1,54 @@
+config SCSI
+ bool
+
+config LSI_SCSI_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select SCSI
+
+config MPTSAS_SCSI_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select SCSI
+
+config MEGASAS_SCSI_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select SCSI
+
+config VMW_PVSCSI_SCSI_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select SCSI
+
+config ESP
+ bool
+ select SCSI
+
+config ESP_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select ESP
+
+config SPAPR_VSCSI
+ bool
+ default y
+ depends on PSERIES
+ select SCSI
+
+config VIRTIO_SCSI
+ bool
+ default y
+ depends on VIRTIO
+ select SCSI
+
+config VHOST_USER_SCSI
+ bool
+ # Only PCI devices are provided for now
+ default y if VIRTIO_PCI
+ depends on VIRTIO && VHOST_USER && LINUX
diff --git a/hw/scsi/Makefile.objs b/hw/scsi/Makefile.objs
index 45167baeaf..54b36ed8b1 100644
--- a/hw/scsi/Makefile.objs
+++ b/hw/scsi/Makefile.objs
@@ -6,7 +6,7 @@ common-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o
common-obj-$(CONFIG_VMW_PVSCSI_SCSI_PCI) += vmw_pvscsi.o
common-obj-$(CONFIG_ESP) += esp.o
common-obj-$(CONFIG_ESP_PCI) += esp-pci.o
-obj-$(CONFIG_PSERIES) += spapr_vscsi.o
+obj-$(CONFIG_SPAPR_VSCSI) += spapr_vscsi.o
ifeq ($(CONFIG_VIRTIO_SCSI),y)
obj-y += virtio-scsi.o virtio-scsi-dataplane.o
diff --git a/hw/sd/Kconfig b/hw/sd/Kconfig
new file mode 100644
index 0000000000..864f535011
--- /dev/null
+++ b/hw/sd/Kconfig
@@ -0,0 +1,17 @@
+config PL181
+ bool
+ select SD
+
+config SSI_SD
+ bool
+ depends on SSI
+ select SD
+
+config SD
+ bool
+
+config SDHCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select SD
diff --git a/hw/sh4/Kconfig b/hw/sh4/Kconfig
new file mode 100644
index 0000000000..8597613a35
--- /dev/null
+++ b/hw/sh4/Kconfig
@@ -0,0 +1,23 @@
+config R2D
+ bool
+ imply PCI_DEVICES
+ imply TEST_DEVICES
+ select I82378 if TEST_DEVICES
+ select IDE_MMIO
+ select PFLASH_CFI02
+ select USB_OHCI
+ select PCI
+ select SM501
+ select SH4
+
+config SHIX
+ bool
+ select SH7750
+ select SH4
+
+config SH7750
+ bool
+
+config SH4
+ bool
+ select PTIMER
diff --git a/hw/smbios/Kconfig b/hw/smbios/Kconfig
new file mode 100644
index 0000000000..553adf4bfc
--- /dev/null
+++ b/hw/smbios/Kconfig
@@ -0,0 +1,2 @@
+config SMBIOS
+ bool
diff --git a/hw/sparc/Kconfig b/hw/sparc/Kconfig
new file mode 100644
index 0000000000..2a83a8010e
--- /dev/null
+++ b/hw/sparc/Kconfig
@@ -0,0 +1,26 @@
+config SUN4M
+ bool
+ imply TCX
+ imply CG3
+ select CS4231
+ select ECCMEMCTL
+ select EMPTY_SLOT
+ select ESCC
+ select ESP
+ select FDC
+ select SLAVIO
+ select LANCE
+ select M48T59
+ select STP2000
+
+config LEON3
+ bool
+ select GRLIB
+
+config GRLIB
+ bool
+ select PTIMER
+
+config SLAVIO
+ bool
+ select PTIMER
diff --git a/hw/sparc64/Kconfig b/hw/sparc64/Kconfig
new file mode 100644
index 0000000000..4a8166ebb7
--- /dev/null
+++ b/hw/sparc64/Kconfig
@@ -0,0 +1,19 @@
+config SUN4U
+ bool
+ imply PCI_DEVICES
+ imply SUNHME
+ imply TEST_DEVICES
+ select M48T59
+ select ISA_BUS
+ select FDC
+ select SERIAL_ISA
+ select PCI_SABRE
+ select IDE_CMD646
+ select PARALLEL
+ select PCKBD
+ select SIMBA
+
+config NIAGARA
+ bool
+ select EMPTY_SLOT
+ select SUN4V_RTC
diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig
new file mode 100644
index 0000000000..9e54a0c8dd
--- /dev/null
+++ b/hw/ssi/Kconfig
@@ -0,0 +1,18 @@
+config PL022
+ bool
+ select SSI
+
+config SSI
+ bool
+
+config XILINX_SPI
+ bool
+ select SSI
+
+config XILINX_SPIPS
+ bool
+ select SSI
+
+config STM32F2XX_SPI
+ bool
+ select SSI
diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig
new file mode 100644
index 0000000000..51921eb63f
--- /dev/null
+++ b/hw/timer/Kconfig
@@ -0,0 +1,63 @@
+config ARM_TIMER
+ bool
+ select PTIMER
+
+config ARM_MPTIMER
+ bool
+ select PTIMER
+
+config A9_GTIMER
+ bool
+
+config DS1338
+ bool
+ depends on I2C
+
+config HPET
+ bool
+ default y if PC
+
+config I8254
+ bool
+
+config M41T80
+ bool
+ depends on I2C
+
+config M48T59
+ bool
+
+config PL031
+ bool
+
+config TWL92230
+ bool
+ depends on I2C
+
+config XLNX_ZYNQMP
+ bool
+
+config ALTERA_TIMER
+ bool
+ select PTIMER
+
+config MC146818RTC
+ bool
+
+config ALLWINNER_A10_PIT
+ bool
+ select PTIMER
+
+config STM32F2XX_TIMER
+ bool
+
+config SUN4V_RTC
+ bool
+
+config CMSDK_APB_TIMER
+ bool
+ select PTIMER
+
+config CMSDK_APB_DUALTIMER
+ bool
+ select PTIMER
diff --git a/hw/tpm/Kconfig b/hw/tpm/Kconfig
new file mode 100644
index 0000000000..4c8ee87d67
--- /dev/null
+++ b/hw/tpm/Kconfig
@@ -0,0 +1,24 @@
+config TPMDEV
+ bool
+ depends on TPM
+
+config TPM_TIS
+ bool
+ depends on TPM && ISA_BUS
+ select TPMDEV
+
+config TPM_CRB
+ bool
+ depends on TPM && PC
+ select TPMDEV
+
+config TPM_PASSTHROUGH
+ bool
+ default y
+ # FIXME: should check for x86 host as well
+ depends on TPMDEV && LINUX
+
+config TPM_EMULATOR
+ bool
+ default y
+ depends on TPMDEV
diff --git a/hw/tricore/Kconfig b/hw/tricore/Kconfig
new file mode 100644
index 0000000000..9313409309
--- /dev/null
+++ b/hw/tricore/Kconfig
@@ -0,0 +1,2 @@
+config TRICORE
+ bool
diff --git a/hw/unicore32/Kconfig b/hw/unicore32/Kconfig
new file mode 100644
index 0000000000..4443a29dd2
--- /dev/null
+++ b/hw/unicore32/Kconfig
@@ -0,0 +1,5 @@
+config PUV3
+ bool
+ select ISA_BUS
+ select PCKBD
+ select PTIMER
diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig
new file mode 100644
index 0000000000..a1b7acb12a
--- /dev/null
+++ b/hw/usb/Kconfig
@@ -0,0 +1,91 @@
+config USB
+ bool
+
+config USB_UHCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select USB
+
+config USB_OHCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select USB
+
+config USB_EHCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select USB
+
+config USB_EHCI_SYSBUS
+ bool
+ select USB
+
+config USB_XHCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select USB
+
+config USB_XHCI_NEC
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select USB
+
+config USB_MUSB
+ bool
+ select USB
+
+config TUSB6010
+ bool
+ select USB_MUSB
+
+config USB_TABLET_WACOM
+ bool
+ default y
+ depends on USB
+
+config USB_STORAGE_BOT
+ bool
+ default y
+ depends on USB
+ select SCSI
+
+config USB_STORAGE_UAS
+ bool
+ default y
+ depends on USB
+ select SCSI
+
+config USB_AUDIO
+ bool
+ default y
+ depends on USB
+
+config USB_SERIAL
+ bool
+ default y
+ depends on USB
+
+config USB_NETWORK
+ bool
+ default y
+ depends on USB
+
+config USB_BLUETOOTH
+ bool
+ default y
+ depends on USB
+
+config USB_SMARTCARD
+ bool
+ default y
+ depends on USB
+
+config USB_STORAGE_MTP
+ bool
+ default y
+ depends on USB
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 41be700812..2b929649ac 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -6,7 +6,7 @@ common-obj-$(CONFIG_USB) += desc.o desc-msos.o
common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o
common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o
common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o
-common-obj-$(CONFIG_USB_EHCI_SYSBUS) += hcd-ehci-sysbus.o
+common-obj-$(CONFIG_USB_EHCI_SYSBUS) += hcd-ehci.o hcd-ehci-sysbus.o
common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o
common-obj-$(CONFIG_USB_XHCI_NEC) += hcd-xhci-nec.o
common-obj-$(CONFIG_USB_MUSB) += hcd-musb.o
diff --git a/hw/vfio/Kconfig b/hw/vfio/Kconfig
new file mode 100644
index 0000000000..ebda9fdf22
--- /dev/null
+++ b/hw/vfio/Kconfig
@@ -0,0 +1,36 @@
+config VFIO
+ bool
+ depends on LINUX
+
+config VFIO_PCI
+ bool
+ select VFIO
+ depends on LINUX
+
+config VFIO_CCW
+ bool
+ default y
+ select VFIO
+ depends on LINUX && S390_CCW_VIRTIO
+
+config VFIO_PLATFORM
+ bool
+ default y
+ select VFIO
+ depends on LINUX && PLATFORM_BUS
+
+config VFIO_XGMAC
+ bool
+ default y
+ depends on VFIO_PLATFORM
+
+config VFIO_AMD_XGBE
+ bool
+ default y
+ depends on VFIO_PLATFORM
+
+config VFIO_AP
+ bool
+ default y
+ select VFIO
+ depends on LINUX && S390_CCW_VIRTIO
diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig
new file mode 100644
index 0000000000..e0452de4ba
--- /dev/null
+++ b/hw/virtio/Kconfig
@@ -0,0 +1,31 @@
+config VIRTIO
+ bool
+
+config VIRTIO_RNG
+ bool
+ default y
+ depends on VIRTIO
+
+config VIRTIO_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select VIRTIO
+
+config VIRTIO_MMIO
+ bool
+ select VIRTIO
+
+config VIRTIO_CCW
+ bool
+ select VIRTIO
+
+config VIRTIO_BALLOON
+ bool
+ default y
+ depends on VIRTIO
+
+config VIRTIO_CRYPTO
+ bool
+ default y
+ depends on VIRTIO
diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs
index a3eb8ed866..f2ab667a21 100644
--- a/hw/virtio/Makefile.objs
+++ b/hw/virtio/Makefile.objs
@@ -29,6 +29,8 @@ obj-$(CONFIG_VIRTIO_BLK) += virtio-blk-pci.o
obj-$(CONFIG_VIRTIO_NET) += virtio-net-pci.o
obj-$(CONFIG_VIRTIO_SERIAL) += virtio-serial-pci.o
endif
+else
+common-obj-y += vhost-stub.o
endif
common-obj-$(CONFIG_ALL) += vhost-stub.o
diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig
new file mode 100644
index 0000000000..2118d897c9
--- /dev/null
+++ b/hw/watchdog/Kconfig
@@ -0,0 +1,16 @@
+config CMSDK_APB_WATCHDOG
+ bool
+ select PTIMER
+
+config WDT_IB6300ESB
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+
+config WDT_IB700
+ bool
+ default y
+ depends on ISA_BUS
+
+config WDT_DIAG288
+ bool
diff --git a/hw/xtensa/Kconfig b/hw/xtensa/Kconfig
new file mode 100644
index 0000000000..d72817d012
--- /dev/null
+++ b/hw/xtensa/Kconfig
@@ -0,0 +1,8 @@
+config XTENSA_SIM
+ bool
+
+config XTENSA_XTFPGA
+ bool
+ select OPENCORES_ETH
+ select PFLASH_CFI01
+ select SERIAL
diff --git a/hw/xtensa/Makefile.objs b/hw/xtensa/Makefile.objs
index fa86730e23..0bbfccd6de 100644
--- a/hw/xtensa/Makefile.objs
+++ b/hw/xtensa/Makefile.objs
@@ -2,4 +2,4 @@ obj-y += mx_pic.o
obj-y += pic_cpu.o
obj-y += xtensa_memory.o
obj-$(CONFIG_XTENSA_SIM) += sim.o
-obj-$(CONFIG_XTENSA_FPGA) += xtfpga.o
+obj-$(CONFIG_XTENSA_XTFPGA) += xtfpga.o
diff --git a/include/migration/qemu-file-types.h b/include/migration/qemu-file-types.h
index bd6d7dd7f9..bbe04d4484 100644
--- a/include/migration/qemu-file-types.h
+++ b/include/migration/qemu-file-types.h
@@ -25,6 +25,8 @@
#ifndef QEMU_FILE_H
#define QEMU_FILE_H
+int qemu_file_get_error(QEMUFile *f);
+
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size);
void qemu_put_byte(QEMUFile *f, int v);
diff --git a/include/qemu/module.h b/include/qemu/module.h
index 55dd2beea8..db3065381d 100644
--- a/include/qemu/module.h
+++ b/include/qemu/module.h
@@ -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)
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 6e8762b40d..c1a26021f8 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -500,13 +500,48 @@ static uint32_t get_elf_hwcap2(void)
#undef GET_FEATURE
#undef GET_FEATURE_ID
+#define ELF_PLATFORM get_elf_platform()
+
+static const char *get_elf_platform(void)
+{
+ CPUARMState *env = thread_cpu->env_ptr;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+# define END "b"
+#else
+# define END "l"
+#endif
+
+ if (arm_feature(env, ARM_FEATURE_V8)) {
+ return "v8" END;
+ } else if (arm_feature(env, ARM_FEATURE_V7)) {
+ if (arm_feature(env, ARM_FEATURE_M)) {
+ return "v7m" END;
+ } else {
+ return "v7" END;
+ }
+ } else if (arm_feature(env, ARM_FEATURE_V6)) {
+ return "v6" END;
+ } else if (arm_feature(env, ARM_FEATURE_V5)) {
+ return "v5" END;
+ } else {
+ return "v4" END;
+ }
+
+#undef END
+}
+
#else
/* 64 bit ARM definitions */
#define ELF_START_MMAP 0x80000000
#define ELF_ARCH EM_AARCH64
#define ELF_CLASS ELFCLASS64
-#define ELF_PLATFORM "aarch64"
+#ifdef TARGET_WORDS_BIGENDIAN
+# define ELF_PLATFORM "aarch64_be"
+#else
+# define ELF_PLATFORM "aarch64"
+#endif
static inline void init_thread(struct target_pt_regs *regs,
struct image_info *infop)
diff --git a/linux-user/fd-trans.c b/linux-user/fd-trans.c
index 30425c9df6..612819c1b1 100644
--- a/linux-user/fd-trans.c
+++ b/linux-user/fd-trans.c
@@ -75,6 +75,8 @@ enum {
QEMU_IFLA_BR_MCAST_STATS_ENABLED,
QEMU_IFLA_BR_MCAST_IGMP_VERSION,
QEMU_IFLA_BR_MCAST_MLD_VERSION,
+ QEMU_IFLA_BR_VLAN_STATS_PER_PORT,
+ QEMU_IFLA_BR_MULTI_BOOLOPT,
QEMU___IFLA_BR_MAX,
};
@@ -438,6 +440,7 @@ static abi_long host_to_target_data_bridge_nlattr(struct nlattr *nlattr,
case QEMU_IFLA_BR_MCAST_STATS_ENABLED:
case QEMU_IFLA_BR_MCAST_IGMP_VERSION:
case QEMU_IFLA_BR_MCAST_MLD_VERSION:
+ case QEMU_IFLA_BR_VLAN_STATS_PER_PORT:
break;
/* uint16_t */
case QEMU_IFLA_BR_PRIORITY:
@@ -543,6 +546,12 @@ static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr,
case QEMU_IFLA_BRPORT_ROOT_ID:
case QEMU_IFLA_BRPORT_BRIDGE_ID:
break;
+ /* br_boolopt_multi { uint32_t, uint32_t } */
+ case QEMU_IFLA_BR_MULTI_BOOLOPT:
+ u32 = NLA_DATA(nlattr);
+ u32[0] = tswap32(u32[0]); /* optval */
+ u32[1] = tswap32(u32[1]); /* optmask */
+ break;
default:
gemu_log("Unknown QEMU_IFLA_BRPORT type %d\n", nlattr->nla_type);
break;
diff --git a/linux-user/nios2/cpu_loop.c b/linux-user/nios2/cpu_loop.c
index b96b1aa119..5aa1eca740 100644
--- a/linux-user/nios2/cpu_loop.c
+++ b/linux-user/nios2/cpu_loop.c
@@ -73,6 +73,12 @@ void cpu_loop(CPUNios2State *env)
queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
break;
}
+ case EXCP_DEBUG:
+ info.si_signo = TARGET_SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
+ break;
case 0xaa:
switch (env->regs[R_PC]) {
/*case 0x1000:*/ /* TODO:__kuser_helper_version */
diff --git a/linux-user/strace.c b/linux-user/strace.c
index 7318392e57..6f72a74c09 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -1235,6 +1235,18 @@ print_chdir(const struct syscallname *name,
}
#endif
+#ifdef TARGET_NR_chroot
+static void
+print_chroot(const struct syscallname *name,
+ abi_long arg0, abi_long arg1, abi_long arg2,
+ abi_long arg3, abi_long arg4, abi_long arg5)
+{
+ print_syscall_prologue(name);
+ print_string(arg0, 1);
+ print_syscall_epilogue(name);
+}
+#endif
+
#ifdef TARGET_NR_chmod
static void
print_chmod(const struct syscallname *name,
diff --git a/linux-user/strace.list b/linux-user/strace.list
index ff8bb19f5f..db21ce4177 100644
--- a/linux-user/strace.list
+++ b/linux-user/strace.list
@@ -77,7 +77,7 @@
{ TARGET_NR_chown32, "chown32" , NULL, NULL, NULL },
#endif
#ifdef TARGET_NR_chroot
-{ TARGET_NR_chroot, "chroot" , NULL, NULL, NULL },
+{ TARGET_NR_chroot, "chroot" , NULL, print_chroot, NULL },
#endif
#ifdef TARGET_NR_clock_adjtime
{ TARGET_NR_clock_adjtime, "clock_adjtime" , NULL, print_clock_adjtime, NULL },
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 5bbb72f3d5..208fd1813d 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -2759,6 +2759,7 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp,
}
if (!is_error(ret)) {
msgp->msg_namelen = tswap32(msg.msg_namelen);
+ msgp->msg_flags = tswap32(msg.msg_flags);
if (msg.msg_name != NULL && msg.msg_name != (void *)-1) {
ret = host_to_target_sockaddr(tswapal(msgp->msg_name),
msg.msg_name, msg.msg_namelen);
@@ -2846,7 +2847,7 @@ static abi_long do_sendrecvmmsg(int fd, abi_ulong target_msgvec,
static abi_long do_accept4(int fd, abi_ulong target_addr,
abi_ulong target_addrlen_addr, int flags)
{
- socklen_t addrlen;
+ socklen_t addrlen, ret_addrlen;
void *addr;
abi_long ret;
int host_flags;
@@ -2870,11 +2871,13 @@ static abi_long do_accept4(int fd, abi_ulong target_addr,
addr = alloca(addrlen);
- ret = get_errno(safe_accept4(fd, addr, &addrlen, host_flags));
+ ret_addrlen = addrlen;
+ ret = get_errno(safe_accept4(fd, addr, &ret_addrlen, host_flags));
if (!is_error(ret)) {
- host_to_target_sockaddr(target_addr, addr, addrlen);
- if (put_user_u32(addrlen, target_addrlen_addr))
+ host_to_target_sockaddr(target_addr, addr, MIN(addrlen, ret_addrlen));
+ if (put_user_u32(ret_addrlen, target_addrlen_addr)) {
ret = -TARGET_EFAULT;
+ }
}
return ret;
}
@@ -2883,7 +2886,7 @@ static abi_long do_accept4(int fd, abi_ulong target_addr,
static abi_long do_getpeername(int fd, abi_ulong target_addr,
abi_ulong target_addrlen_addr)
{
- socklen_t addrlen;
+ socklen_t addrlen, ret_addrlen;
void *addr;
abi_long ret;
@@ -2899,11 +2902,13 @@ static abi_long do_getpeername(int fd, abi_ulong target_addr,
addr = alloca(addrlen);
- ret = get_errno(getpeername(fd, addr, &addrlen));
+ ret_addrlen = addrlen;
+ ret = get_errno(getpeername(fd, addr, &ret_addrlen));
if (!is_error(ret)) {
- host_to_target_sockaddr(target_addr, addr, addrlen);
- if (put_user_u32(addrlen, target_addrlen_addr))
+ host_to_target_sockaddr(target_addr, addr, MIN(addrlen, ret_addrlen));
+ if (put_user_u32(ret_addrlen, target_addrlen_addr)) {
ret = -TARGET_EFAULT;
+ }
}
return ret;
}
@@ -2912,7 +2917,7 @@ static abi_long do_getpeername(int fd, abi_ulong target_addr,
static abi_long do_getsockname(int fd, abi_ulong target_addr,
abi_ulong target_addrlen_addr)
{
- socklen_t addrlen;
+ socklen_t addrlen, ret_addrlen;
void *addr;
abi_long ret;
@@ -2928,11 +2933,13 @@ static abi_long do_getsockname(int fd, abi_ulong target_addr,
addr = alloca(addrlen);
- ret = get_errno(getsockname(fd, addr, &addrlen));
+ ret_addrlen = addrlen;
+ ret = get_errno(getsockname(fd, addr, &ret_addrlen));
if (!is_error(ret)) {
- host_to_target_sockaddr(target_addr, addr, addrlen);
- if (put_user_u32(addrlen, target_addrlen_addr))
+ host_to_target_sockaddr(target_addr, addr, MIN(addrlen, ret_addrlen));
+ if (put_user_u32(ret_addrlen, target_addrlen_addr)) {
ret = -TARGET_EFAULT;
+ }
}
return ret;
}
@@ -3004,7 +3011,7 @@ static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags,
abi_ulong target_addr,
abi_ulong target_addrlen)
{
- socklen_t addrlen;
+ socklen_t addrlen, ret_addrlen;
void *addr;
void *host_msg;
abi_long ret;
@@ -3022,10 +3029,12 @@ static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags,
goto fail;
}
addr = alloca(addrlen);
+ ret_addrlen = addrlen;
ret = get_errno(safe_recvfrom(fd, host_msg, len, flags,
- addr, &addrlen));
+ addr, &ret_addrlen));
} else {
addr = NULL; /* To keep compiler quiet. */
+ addrlen = 0; /* To keep compiler quiet. */
ret = get_errno(safe_recvfrom(fd, host_msg, len, flags, NULL, 0));
}
if (!is_error(ret)) {
@@ -3038,8 +3047,9 @@ static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags,
}
}
if (target_addr) {
- host_to_target_sockaddr(target_addr, addr, addrlen);
- if (put_user_u32(addrlen, target_addrlen)) {
+ host_to_target_sockaddr(target_addr, addr,
+ MIN(addrlen, ret_addrlen));
+ if (put_user_u32(ret_addrlen, target_addrlen)) {
ret = -TARGET_EFAULT;
goto fail;
}
@@ -4723,8 +4733,8 @@ static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp,
const int *dst_offsets, *src_offsets;
int target_size;
void *argptr;
- abi_ulong *target_rt_dev_ptr;
- unsigned long *host_rt_dev_ptr;
+ abi_ulong *target_rt_dev_ptr = NULL;
+ unsigned long *host_rt_dev_ptr = NULL;
abi_long ret;
int i;
@@ -4770,6 +4780,9 @@ static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp,
unlock_user(argptr, arg, 0);
ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
+
+ assert(host_rt_dev_ptr != NULL);
+ assert(target_rt_dev_ptr != NULL);
if (*host_rt_dev_ptr != 0) {
unlock_user((void *)*host_rt_dev_ptr,
*target_rt_dev_ptr, 0);
@@ -6999,8 +7012,8 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
_exit(arg1);
return 0; /* avoid warning */
case TARGET_NR_read:
- if (arg3 == 0) {
- return 0;
+ if (arg2 == 0 && arg3 == 0) {
+ return get_errno(safe_read(arg1, 0, 0));
} else {
if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
return -TARGET_EFAULT;
diff --git a/migration/qemu-file.h b/migration/qemu-file.h
index 2ccfcfb2a8..13baf896bd 100644
--- a/migration/qemu-file.h
+++ b/migration/qemu-file.h
@@ -149,7 +149,6 @@ void qemu_update_position(QEMUFile *f, size_t size);
void qemu_file_reset_rate_limit(QEMUFile *f);
void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
int64_t qemu_file_get_rate_limit(QEMUFile *f);
-int qemu_file_get_error(QEMUFile *f);
void qemu_file_set_error(QEMUFile *f, int ret);
int qemu_file_shutdown(QEMUFile *f);
QEMUFile *qemu_file_get_return_path(QEMUFile *f);
diff --git a/net/Makefile.objs b/net/Makefile.objs
index 8262f033b9..c5d076d19c 100644
--- a/net/Makefile.objs
+++ b/net/Makefile.objs
@@ -8,6 +8,8 @@ common-obj-$(call land,$(CONFIG_VIRTIO_NET),$(CONFIG_VHOST_NET_USER)) += vhost-u
common-obj-$(call land,$(call lnot,$(CONFIG_VIRTIO_NET)),$(CONFIG_VHOST_NET_USER)) += vhost-user-stub.o
common-obj-$(CONFIG_ALL) += vhost-user-stub.o
common-obj-$(CONFIG_SLIRP) += slirp.o
+slirp.o-cflags := $(SLIRP_CFLAGS)
+slirp.o-libs := $(SLIRP_LIBS)
common-obj-$(CONFIG_VDE) += vde.o
common-obj-$(CONFIG_NETMAP) += netmap.o
common-obj-y += filter.o
diff --git a/net/slirp.c b/net/slirp.c
index 4ec989b592..95934fb36d 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -37,13 +37,15 @@
#include "monitor/monitor.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"
-#include "slirp/libslirp.h"
+#include <libslirp.h>
#include "chardev/char-fe.h"
#include "sysemu/sysemu.h"
#include "qemu/cutils.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "util.h"
+#include "migration/register.h"
+#include "migration/qemu-file-types.h"
static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
{
@@ -146,6 +148,7 @@ static void net_slirp_cleanup(NetClientState *nc)
g_slist_free_full(s->fwd, slirp_free_fwd);
main_loop_poll_remove_notifier(&s->poll_notifier);
+ unregister_savevm(NULL, "slirp", s->slirp);
slirp_cleanup(s->slirp);
if (s->exit_notifier.notify) {
qemu_remove_exit_notifier(&s->exit_notifier);
@@ -303,6 +306,46 @@ static void net_slirp_poll_notify(Notifier *notifier, void *data)
}
}
+static ssize_t
+net_slirp_stream_read(void *buf, size_t size, void *opaque)
+{
+ QEMUFile *f = opaque;
+
+ return qemu_get_buffer(f, buf, size);
+}
+
+static ssize_t
+net_slirp_stream_write(const void *buf, size_t size, void *opaque)
+{
+ QEMUFile *f = opaque;
+
+ qemu_put_buffer(f, buf, size);
+ if (qemu_file_get_error(f)) {
+ return -1;
+ }
+
+ return size;
+}
+
+static int net_slirp_state_load(QEMUFile *f, void *opaque, int version_id)
+{
+ Slirp *slirp = opaque;
+
+ return slirp_state_load(slirp, version_id, net_slirp_stream_read, f);
+}
+
+static void net_slirp_state_save(QEMUFile *f, void *opaque)
+{
+ Slirp *slirp = opaque;
+
+ slirp_state_save(slirp, net_slirp_stream_write, f);
+}
+
+static SaveVMHandlers savevm_slirp_state = {
+ .save_state = net_slirp_state_save,
+ .load_state = net_slirp_state_load,
+};
+
static int net_slirp_init(NetClientState *peer, const char *model,
const char *name, int restricted,
bool ipv4, const char *vnetwork, const char *vhost,
@@ -523,6 +566,18 @@ static int net_slirp_init(NetClientState *peer, const char *model,
&slirp_cb, s);
QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
+ /*
+ * Make sure the current bitstream version of slirp is 4, to avoid
+ * QEMU migration incompatibilities, if upstream slirp bumped the
+ * version.
+ *
+ * FIXME: use bitfields of features? teach libslirp to save with
+ * specific version?
+ */
+ g_assert(slirp_state_version() == 4);
+ register_savevm_live(NULL, "slirp", 0, slirp_state_version(),
+ &savevm_slirp_state, s->slirp);
+
s->poll_notifier.notify = net_slirp_poll_notify;
main_loop_poll_add_notifier(&s->poll_notifier);
@@ -885,6 +940,7 @@ static ssize_t guestfwd_write(const void *buf, size_t len, void *chr)
static int slirp_guestfwd(SlirpState *s, const char *config_str, Error **errp)
{
+ /* TODO: IPv6 */
struct in_addr server = { .s_addr = 0 };
struct GuestFwd *fwd;
const char *p;
diff --git a/rules.mak b/rules.mak
index 19f3d2c126..df45bcffb4 100644
--- a/rules.mak
+++ b/rules.mak
@@ -144,7 +144,7 @@ cc-option = $(if $(shell $(CC) $1 $2 -S -o /dev/null -xc /dev/null \
cc-c-option = $(if $(shell $(CC) $1 $2 -c -o /dev/null -xc /dev/null \
>/dev/null 2>&1 && echo OK), $2, $3)
-VPATH_SUFFIXES = %.c %.h %.S %.cc %.cpp %.m %.mak %.texi %.sh %.rc
+VPATH_SUFFIXES = %.c %.h %.S %.cc %.cpp %.m %.mak %.texi %.sh %.rc Kconfig%
set-vpath = $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(PATTERN) $1)))
# install-prog list, dir
diff --git a/scripts/make_device_config.sh b/scripts/make_device_config.sh
deleted file mode 100644
index 354af317b3..0000000000
--- a/scripts/make_device_config.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#! /bin/sh
-# Writes a target device config file to stdout, from a default and from
-# include directives therein. Also emits Makefile dependencies.
-#
-# Usage: make_device_config.sh SRC DEPFILE-NAME DEPFILE-TARGET > DEST
-
-src=$1
-dep=$2
-target=$3
-src_dir=$(dirname $src)
-all_includes=
-
-process_includes () {
- cat $1 | grep '^include' | \
- while read include file ; do
- all_includes="$all_includes $src_dir/$file"
- process_includes $src_dir/$file
- done
-}
-
-f=$src
-while [ -n "$f" ] ; do
- f=$(cat $f | tr -d '\r' | awk '/^include / {printf "'$src_dir'/%s ", $2}')
- [ $? = 0 ] || exit 1
- all_includes="$all_includes $f"
-done
-process_includes $src
-
-cat $src $all_includes | grep -v '^include'
-echo "$target: $all_includes" > $dep
diff --git a/scripts/minikconf.py b/scripts/minikconf.py
new file mode 100644
index 0000000000..5421db0ed0
--- /dev/null
+++ b/scripts/minikconf.py
@@ -0,0 +1,708 @@
+#
+# Mini-Kconfig parser
+#
+# Copyright (c) 2015 Red Hat Inc.
+#
+# Authors:
+# Paolo Bonzini <pbonzini@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2
+# or, at your option, any later version. See the COPYING file in
+# the top-level directory.
+
+from __future__ import print_function
+import os
+import sys
+import re
+import random
+
+__all__ = [ 'KconfigDataError', 'KconfigParserError',
+ 'KconfigData', 'KconfigParser' ,
+ 'defconfig', 'allyesconfig', 'allnoconfig', 'randconfig' ]
+
+def debug_print(*args):
+ #print('# ' + (' '.join(str(x) for x in args)))
+ pass
+
+# -------------------------------------------
+# KconfigData implements the Kconfig semantics. For now it can only
+# detect undefined symbols, i.e. symbols that were referenced in
+# assignments or dependencies but were not declared with "config FOO".
+#
+# Semantic actions are represented by methods called do_*. The do_var
+# method return the semantic value of a variable (which right now is
+# just its name).
+# -------------------------------------------
+
+class KconfigDataError(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+
+ def __str__(self):
+ return self.msg
+
+allyesconfig = lambda x: True
+allnoconfig = lambda x: False
+defconfig = lambda x: x
+randconfig = lambda x: random.randint(0, 1) == 1
+
+class KconfigData:
+ class Expr:
+ def __and__(self, rhs):
+ return KconfigData.AND(self, rhs)
+ def __or__(self, rhs):
+ return KconfigData.OR(self, rhs)
+ def __invert__(self):
+ return KconfigData.NOT(self)
+
+ # Abstract methods
+ def add_edges_to(self, var):
+ pass
+ def evaluate(self):
+ assert False
+
+ class AND(Expr):
+ def __init__(self, lhs, rhs):
+ self.lhs = lhs
+ self.rhs = rhs
+ def __str__(self):
+ return "(%s && %s)" % (self.lhs, self.rhs)
+
+ def add_edges_to(self, var):
+ self.lhs.add_edges_to(var)
+ self.rhs.add_edges_to(var)
+ def evaluate(self):
+ return self.lhs.evaluate() and self.rhs.evaluate()
+
+ class OR(Expr):
+ def __init__(self, lhs, rhs):
+ self.lhs = lhs
+ self.rhs = rhs
+ def __str__(self):
+ return "(%s || %s)" % (self.lhs, self.rhs)
+
+ def add_edges_to(self, var):
+ self.lhs.add_edges_to(var)
+ self.rhs.add_edges_to(var)
+ def evaluate(self):
+ return self.lhs.evaluate() or self.rhs.evaluate()
+
+ class NOT(Expr):
+ def __init__(self, lhs):
+ self.lhs = lhs
+ def __str__(self):
+ return "!%s" % (self.lhs)
+
+ def add_edges_to(self, var):
+ self.lhs.add_edges_to(var)
+ def evaluate(self):
+ return not self.lhs.evaluate()
+
+ class Var(Expr):
+ def __init__(self, name):
+ self.name = name
+ self.value = None
+ self.outgoing = set()
+ self.clauses_for_var = list()
+ def __str__(self):
+ return self.name
+
+ def has_value(self):
+ return not (self.value is None)
+ def set_value(self, val, clause):
+ self.clauses_for_var.append(clause)
+ if self.has_value() and self.value != val:
+ print("The following clauses were found for " + self.name)
+ for i in self.clauses_for_var:
+ print(" " + str(i), file=sys.stderr)
+ raise KconfigDataError('contradiction between clauses when setting %s' % self)
+ debug_print("=> %s is now %s" % (self.name, val))
+ self.value = val
+
+ # depth first search of the dependency graph
+ def dfs(self, visited, f):
+ if self in visited:
+ return
+ visited.add(self)
+ for v in self.outgoing:
+ v.dfs(visited, f)
+ f(self)
+
+ def add_edges_to(self, var):
+ self.outgoing.add(var)
+ def evaluate(self):
+ if not self.has_value():
+ raise KconfigDataError('cycle found including %s' % self)
+ return self.value
+
+ class Clause:
+ def __init__(self, dest):
+ self.dest = dest
+ def priority(self):
+ return 0
+ def process(self):
+ pass
+
+ class AssignmentClause(Clause):
+ def __init__(self, dest, value):
+ KconfigData.Clause.__init__(self, dest)
+ self.value = value
+ def __str__(self):
+ return "CONFIG_%s=%s" % (self.dest, 'y' if self.value else 'n')
+
+ def process(self):
+ self.dest.set_value(self.value, self)
+
+ class DefaultClause(Clause):
+ def __init__(self, dest, value, cond=None):
+ KconfigData.Clause.__init__(self, dest)
+ self.value = value
+ self.cond = cond
+ if not (self.cond is None):
+ self.cond.add_edges_to(self.dest)
+ def __str__(self):
+ value = 'y' if self.value else 'n'
+ if self.cond is None:
+ return "config %s default %s" % (self.dest, value)
+ else:
+ return "config %s default %s if %s" % (self.dest, value, self.cond)
+
+ def priority(self):
+ # Defaults are processed just before leaving the variable
+ return -1
+ def process(self):
+ if not self.dest.has_value() and \
+ (self.cond is None or self.cond.evaluate()):
+ self.dest.set_value(self.value, self)
+
+ class DependsOnClause(Clause):
+ def __init__(self, dest, expr):
+ KconfigData.Clause.__init__(self, dest)
+ self.expr = expr
+ self.expr.add_edges_to(self.dest)
+ def __str__(self):
+ return "config %s depends on %s" % (self.dest, self.expr)
+
+ def process(self):
+ if not self.expr.evaluate():
+ self.dest.set_value(False, self)
+
+ class SelectClause(Clause):
+ def __init__(self, dest, cond):
+ KconfigData.Clause.__init__(self, dest)
+ self.cond = cond
+ self.cond.add_edges_to(self.dest)
+ def __str__(self):
+ return "select %s if %s" % (self.dest, self.cond)
+
+ def process(self):
+ if self.cond.evaluate():
+ self.dest.set_value(True, self)
+
+ def __init__(self, value_mangler=defconfig):
+ self.value_mangler = value_mangler
+ self.previously_included = []
+ self.incl_info = None
+ self.defined_vars = set()
+ self.referenced_vars = dict()
+ self.clauses = list()
+
+ # semantic analysis -------------
+
+ def check_undefined(self):
+ undef = False
+ for i in self.referenced_vars:
+ if not (i in self.defined_vars):
+ print("undefined symbol %s" % (i), file=sys.stderr)
+ undef = True
+ return undef
+
+ def compute_config(self):
+ if self.check_undefined():
+ raise KconfigDataError("there were undefined symbols")
+ return None
+
+ debug_print("Input:")
+ for clause in self.clauses:
+ debug_print(clause)
+
+ debug_print("\nDependency graph:")
+ for i in self.referenced_vars:
+ debug_print(i, "->", [str(x) for x in self.referenced_vars[i].outgoing])
+
+ # The reverse of the depth-first order is the topological sort
+ dfo = dict()
+ visited = set()
+ debug_print("\n")
+ def visit_fn(var):
+ debug_print(var, "has DFS number", len(dfo))
+ dfo[var] = len(dfo)
+
+ for name, v in self.referenced_vars.items():
+ self.do_default(v, False)
+ v.dfs(visited, visit_fn)
+
+ # Put higher DFS numbers and higher priorities first. This
+ # places the clauses in topological order and places defaults
+ # after assignments and dependencies.
+ self.clauses.sort(key=lambda x: (-dfo[x.dest], -x.priority()))
+
+ debug_print("\nSorted clauses:")
+ for clause in self.clauses:
+ debug_print(clause)
+ clause.process()
+
+ debug_print("")
+ values = dict()
+ for name, v in self.referenced_vars.items():
+ debug_print("Evaluating", name)
+ values[name] = v.evaluate()
+
+ return values
+
+ # semantic actions -------------
+
+ def do_declaration(self, var):
+ if (var in self.defined_vars):
+ raise KconfigDataError('variable "' + var + '" defined twice')
+
+ self.defined_vars.add(var.name)
+
+ # var is a string with the variable's name.
+ def do_var(self, var):
+ if (var in self.referenced_vars):
+ return self.referenced_vars[var]
+
+ var_obj = self.referenced_vars[var] = KconfigData.Var(var)
+ return var_obj
+
+ def do_assignment(self, var, val):
+ self.clauses.append(KconfigData.AssignmentClause(var, val))
+
+ def do_default(self, var, val, cond=None):
+ val = self.value_mangler(val)
+ self.clauses.append(KconfigData.DefaultClause(var, val, cond))
+
+ def do_depends_on(self, var, expr):
+ self.clauses.append(KconfigData.DependsOnClause(var, expr))
+
+ def do_select(self, var, symbol, cond=None):
+ cond = (cond & var) if cond is not None else var
+ self.clauses.append(KconfigData.SelectClause(symbol, cond))
+
+ def do_imply(self, var, symbol, cond=None):
+ # "config X imply Y [if COND]" is the same as
+ # "config Y default y if X [&& COND]"
+ cond = (cond & var) if cond is not None else var
+ self.do_default(symbol, True, cond)
+
+# -------------------------------------------
+# KconfigParser implements a recursive descent parser for (simplified)
+# Kconfig syntax.
+# -------------------------------------------
+
+# tokens table
+TOKENS = {}
+TOK_NONE = -1
+TOK_LPAREN = 0; TOKENS[TOK_LPAREN] = '"("';
+TOK_RPAREN = 1; TOKENS[TOK_RPAREN] = '")"';
+TOK_EQUAL = 2; TOKENS[TOK_EQUAL] = '"="';
+TOK_AND = 3; TOKENS[TOK_AND] = '"&&"';
+TOK_OR = 4; TOKENS[TOK_OR] = '"||"';
+TOK_NOT = 5; TOKENS[TOK_NOT] = '"!"';
+TOK_DEPENDS = 6; TOKENS[TOK_DEPENDS] = '"depends"';
+TOK_ON = 7; TOKENS[TOK_ON] = '"on"';
+TOK_SELECT = 8; TOKENS[TOK_SELECT] = '"select"';
+TOK_IMPLY = 9; TOKENS[TOK_IMPLY] = '"imply"';
+TOK_CONFIG = 10; TOKENS[TOK_CONFIG] = '"config"';
+TOK_DEFAULT = 11; TOKENS[TOK_DEFAULT] = '"default"';
+TOK_Y = 12; TOKENS[TOK_Y] = '"y"';
+TOK_N = 13; TOKENS[TOK_N] = '"n"';
+TOK_SOURCE = 14; TOKENS[TOK_SOURCE] = '"source"';
+TOK_BOOL = 15; TOKENS[TOK_BOOL] = '"bool"';
+TOK_IF = 16; TOKENS[TOK_IF] = '"if"';
+TOK_ID = 17; TOKENS[TOK_ID] = 'identifier';
+TOK_EOF = 18; TOKENS[TOK_EOF] = 'end of file';
+
+class KconfigParserError(Exception):
+ def __init__(self, parser, msg, tok=None):
+ self.loc = parser.location()
+ tok = tok or parser.tok
+ if tok != TOK_NONE:
+ location = TOKENS.get(tok, None) or ('"%s"' % tok)
+ msg = '%s before %s' % (msg, location)
+ self.msg = msg
+
+ def __str__(self):
+ return "%s: %s" % (self.loc, self.msg)
+
+class KconfigParser:
+
+ @classmethod
+ def parse(self, fp, mode=None):
+ data = KconfigData(mode or KconfigParser.defconfig)
+ parser = KconfigParser(data)
+ parser.parse_file(fp)
+ return data
+
+ def __init__(self, data):
+ self.data = data
+
+ def parse_file(self, fp):
+ self.abs_fname = os.path.abspath(fp.name)
+ self.fname = fp.name
+ self.data.previously_included.append(self.abs_fname)
+ self.src = fp.read()
+ if self.src == '' or self.src[-1] != '\n':
+ self.src += '\n'
+ self.cursor = 0
+ self.line = 1
+ self.line_pos = 0
+ self.get_token()
+ self.parse_config()
+
+ def do_assignment(self, var, val):
+ if not var.startswith("CONFIG_"):
+ raise Error('assigned variable should start with CONFIG_')
+ var = self.data.do_var(var[7:])
+ self.data.do_assignment(var, val)
+
+ # file management -----
+
+ def error_path(self):
+ inf = self.data.incl_info
+ res = ""
+ while inf:
+ res = ("In file included from %s:%d:\n" % (inf['file'],
+ inf['line'])) + res
+ inf = inf['parent']
+ return res
+
+ def location(self):
+ col = 1
+ for ch in self.src[self.line_pos:self.pos]:
+ if ch == '\t':
+ col += 8 - ((col - 1) % 8)
+ else:
+ col += 1
+ return '%s%s:%d:%d' %(self.error_path(), self.fname, self.line, col)
+
+ def do_include(self, include):
+ incl_abs_fname = os.path.join(os.path.dirname(self.abs_fname),
+ include)
+ # catch inclusion cycle
+ inf = self.data.incl_info
+ while inf:
+ if incl_abs_fname == os.path.abspath(inf['file']):
+ raise KconfigParserError(self, "Inclusion loop for %s"
+ % include)
+ inf = inf['parent']
+
+ # skip multiple include of the same file
+ if incl_abs_fname in self.data.previously_included:
+ return
+ try:
+ fp = open(incl_abs_fname, 'r')
+ except IOError as e:
+ raise KconfigParserError(self,
+ '%s: %s' % (e.strerror, include))
+
+ inf = self.data.incl_info
+ self.data.incl_info = { 'file': self.fname, 'line': self.line,
+ 'parent': inf }
+ KconfigParser(self.data).parse_file(fp)
+ self.data.incl_info = inf
+
+ # recursive descent parser -----
+
+ # y_or_n: Y | N
+ def parse_y_or_n(self):
+ if self.tok == TOK_Y:
+ self.get_token()
+ return True
+ if self.tok == TOK_N:
+ self.get_token()
+ return False
+ raise KconfigParserError(self, 'Expected "y" or "n"')
+
+ # var: ID
+ def parse_var(self):
+ if self.tok == TOK_ID:
+ val = self.val
+ self.get_token()
+ return self.data.do_var(val)
+ else:
+ raise KconfigParserError(self, 'Expected identifier')
+
+ # assignment_var: ID (starting with "CONFIG_")
+ def parse_assignment_var(self):
+ if self.tok == TOK_ID:
+ val = self.val
+ if not val.startswith("CONFIG_"):
+ raise KconfigParserError(self,
+ 'Expected identifier starting with "CONFIG_"', TOK_NONE)
+ self.get_token()
+ return self.data.do_var(val[7:])
+ else:
+ raise KconfigParserError(self, 'Expected identifier')
+
+ # assignment: var EQUAL y_or_n
+ def parse_assignment(self):
+ var = self.parse_assignment_var()
+ if self.tok != TOK_EQUAL:
+ raise KconfigParserError(self, 'Expected "="')
+ self.get_token()
+ self.data.do_assignment(var, self.parse_y_or_n())
+
+ # primary: NOT primary
+ # | LPAREN expr RPAREN
+ # | var
+ def parse_primary(self):
+ if self.tok == TOK_NOT:
+ self.get_token()
+ val = ~self.parse_primary()
+ elif self.tok == TOK_LPAREN:
+ self.get_token()
+ val = self.parse_expr()
+ if self.tok != TOK_RPAREN:
+ raise KconfigParserError(self, 'Expected ")"')
+ self.get_token()
+ elif self.tok == TOK_ID:
+ val = self.parse_var()
+ else:
+ raise KconfigParserError(self, 'Expected "!" or "(" or identifier')
+ return val
+
+ # disj: primary (OR primary)*
+ def parse_disj(self):
+ lhs = self.parse_primary()
+ while self.tok == TOK_OR:
+ self.get_token()
+ lhs = lhs | self.parse_primary()
+ return lhs
+
+ # expr: disj (AND disj)*
+ def parse_expr(self):
+ lhs = self.parse_disj()
+ while self.tok == TOK_AND:
+ self.get_token()
+ lhs = lhs & self.parse_disj()
+ return lhs
+
+ # condition: IF expr
+ # | empty
+ def parse_condition(self):
+ if self.tok == TOK_IF:
+ self.get_token()
+ return self.parse_expr()
+ else:
+ return None
+
+ # property: DEFAULT y_or_n condition
+ # | DEPENDS ON expr
+ # | SELECT var condition
+ # | BOOL
+ def parse_property(self, var):
+ if self.tok == TOK_DEFAULT:
+ self.get_token()
+ val = self.parse_y_or_n()
+ cond = self.parse_condition()
+ self.data.do_default(var, val, cond)
+ elif self.tok == TOK_DEPENDS:
+ self.get_token()
+ if self.tok != TOK_ON:
+ raise KconfigParserError(self, 'Expected "on"')
+ self.get_token()
+ self.data.do_depends_on(var, self.parse_expr())
+ elif self.tok == TOK_SELECT:
+ self.get_token()
+ symbol = self.parse_var()
+ cond = self.parse_condition()
+ self.data.do_select(var, symbol, cond)
+ elif self.tok == TOK_IMPLY:
+ self.get_token()
+ symbol = self.parse_var()
+ cond = self.parse_condition()
+ self.data.do_imply(var, symbol, cond)
+ elif self.tok == TOK_BOOL:
+ self.get_token()
+ else:
+ raise KconfigParserError(self, 'Error in recursive descent?')
+
+ # properties: properties property
+ # | /* empty */
+ def parse_properties(self, var):
+ had_default = False
+ while self.tok == TOK_DEFAULT or self.tok == TOK_DEPENDS or \
+ self.tok == TOK_SELECT or self.tok == TOK_BOOL or \
+ self.tok == TOK_IMPLY:
+ self.parse_property(var)
+
+ # for nicer error message
+ if self.tok != TOK_SOURCE and self.tok != TOK_CONFIG and \
+ self.tok != TOK_ID and self.tok != TOK_EOF:
+ raise KconfigParserError(self, 'expected "source", "config", identifier, '
+ + '"default", "depends on", "imply" or "select"')
+
+ # declaration: config var properties
+ def parse_declaration(self):
+ if self.tok == TOK_CONFIG:
+ self.get_token()
+ var = self.parse_var()
+ self.data.do_declaration(var)
+ self.parse_properties(var)
+ else:
+ raise KconfigParserError(self, 'Error in recursive descent?')
+
+ # clause: SOURCE
+ # | declaration
+ # | assignment
+ def parse_clause(self):
+ if self.tok == TOK_SOURCE:
+ val = self.val
+ self.get_token()
+ self.do_include(val)
+ elif self.tok == TOK_CONFIG:
+ self.parse_declaration()
+ elif self.tok == TOK_ID:
+ self.parse_assignment()
+ else:
+ raise KconfigParserError(self, 'expected "source", "config" or identifier')
+
+ # config: clause+ EOF
+ def parse_config(self):
+ while self.tok != TOK_EOF:
+ self.parse_clause()
+ return self.data
+
+ # scanner -----
+
+ def get_token(self):
+ while True:
+ self.tok = self.src[self.cursor]
+ self.pos = self.cursor
+ self.cursor += 1
+
+ self.val = None
+ self.tok = self.scan_token()
+ if self.tok is not None:
+ return
+
+ def check_keyword(self, rest):
+ if not self.src.startswith(rest, self.cursor):
+ return False
+ length = len(rest)
+ if self.src[self.cursor + length].isalnum() or self.src[self.cursor + length] == '|':
+ return False
+ self.cursor += length
+ return True
+
+ def scan_token(self):
+ if self.tok == '#':
+ self.cursor = self.src.find('\n', self.cursor)
+ return None
+ elif self.tok == '=':
+ return TOK_EQUAL
+ elif self.tok == '(':
+ return TOK_LPAREN
+ elif self.tok == ')':
+ return TOK_RPAREN
+ elif self.tok == '&' and self.src[self.pos+1] == '&':
+ self.cursor += 1
+ return TOK_AND
+ elif self.tok == '|' and self.src[self.pos+1] == '|':
+ self.cursor += 1
+ return TOK_OR
+ elif self.tok == '!':
+ return TOK_NOT
+ elif self.tok == 'd' and self.check_keyword("epends"):
+ return TOK_DEPENDS
+ elif self.tok == 'o' and self.check_keyword("n"):
+ return TOK_ON
+ elif self.tok == 's' and self.check_keyword("elect"):
+ return TOK_SELECT
+ elif self.tok == 'i' and self.check_keyword("mply"):
+ return TOK_IMPLY
+ elif self.tok == 'c' and self.check_keyword("onfig"):
+ return TOK_CONFIG
+ elif self.tok == 'd' and self.check_keyword("efault"):
+ return TOK_DEFAULT
+ elif self.tok == 'b' and self.check_keyword("ool"):
+ return TOK_BOOL
+ elif self.tok == 'i' and self.check_keyword("f"):
+ return TOK_IF
+ elif self.tok == 'y' and self.check_keyword(""):
+ return TOK_Y
+ elif self.tok == 'n' and self.check_keyword(""):
+ return TOK_N
+ elif (self.tok == 's' and self.check_keyword("ource")) or \
+ self.tok == 'i' and self.check_keyword("nclude"):
+ # source FILENAME
+ # include FILENAME
+ while self.src[self.cursor].isspace():
+ self.cursor += 1
+ start = self.cursor
+ self.cursor = self.src.find('\n', self.cursor)
+ self.val = self.src[start:self.cursor]
+ return TOK_SOURCE
+ elif self.tok.isalpha():
+ # identifier
+ while self.src[self.cursor].isalnum() or self.src[self.cursor] == '_':
+ self.cursor += 1
+ self.val = self.src[self.pos:self.cursor]
+ return TOK_ID
+ elif self.tok == '\n':
+ if self.cursor == len(self.src):
+ return TOK_EOF
+ self.line += 1
+ self.line_pos = self.cursor
+ elif not self.tok.isspace():
+ raise KconfigParserError(self, 'invalid input')
+
+ return None
+
+if __name__ == '__main__':
+ argv = sys.argv
+ mode = defconfig
+ if len(sys.argv) > 1:
+ if argv[1] == '--defconfig':
+ del argv[1]
+ elif argv[1] == '--randconfig':
+ random.seed()
+ mode = randconfig
+ del argv[1]
+ elif argv[1] == '--allyesconfig':
+ mode = allyesconfig
+ del argv[1]
+ elif argv[1] == '--allnoconfig':
+ mode = allnoconfig
+ del argv[1]
+
+ if len(argv) == 1:
+ print ("%s: at least one argument is required" % argv[0], file=sys.stderr)
+ sys.exit(1)
+
+ if argv[1].startswith('-'):
+ print ("%s: invalid option %s" % (argv[0], argv[1]), file=sys.stderr)
+ sys.exit(1)
+
+ data = KconfigData(mode)
+ parser = KconfigParser(data)
+ for arg in argv[3:]:
+ m = re.match(r'^(CONFIG_[A-Z0-9_]+)=([yn]?)$', arg)
+ if m is not None:
+ name, value = m.groups()
+ parser.do_assignment(name, value == 'y')
+ else:
+ fp = open(arg, 'r')
+ parser.parse_file(fp)
+ fp.close()
+
+ config = data.compute_config()
+ for key in sorted(config.keys()):
+ print ('CONFIG_%s=%s' % (key, ('y' if config[key] else 'n')))
+
+ deps = open(argv[2], 'w')
+ for fname in data.previously_included:
+ print ('%s: %s' % (argv[1], fname), file=deps)
+ deps.close()
diff --git a/slirp/Makefile b/slirp/Makefile
new file mode 100644
index 0000000000..6d48f626ba
--- /dev/null
+++ b/slirp/Makefile
@@ -0,0 +1,47 @@
+ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
+BUILD_DIR ?= .
+
+LIBSLIRP = $(BUILD_DIR)/libslirp.a
+
+all: $(LIBSLIRP)
+
+SRCS := $(wildcard src/*.c)
+OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o)
+DEPS := $(OBJS:%.o=%.d)
+
+INC_DIRS := $(BUILD_DIR)/src
+INC_FLAGS := $(addprefix -I,$(INC_DIRS))
+
+override CFLAGS += \
+ -DG_LOG_DOMAIN='"Slirp"' \
+ $(shell $(PKG_CONFIG) --cflags glib-2.0) \
+ $(INC_FLAGS) \
+ -MMD -MP
+override LDFLAGS += $(shell $(PKG_CONFIG) --libs glib-2.0)
+
+$(LIBSLIRP): $(OBJS)
+
+.PHONY: clean
+
+clean:
+ rm -r $(OBJS) $(DEPS) $(LIBSLIRP)
+
+$(BUILD_DIR)/src/%.o: $(ROOT_DIR)/src/%.c
+ @$(MKDIR_P) $(dir $@)
+ $(call quiet-command,$(CC) $(CFLAGS) -c -o $@ $<,"CC","$@")
+
+%.a:
+ $(call quiet-command,rm -f $@ && $(AR) rcs $@ $^,"AR","$@")
+
+PKG_CONFIG ?= pkg-config
+MKDIR_P ?= mkdir -p
+quiet-command-run = $(if $(V),,$(if $2,printf " %-7s %s\n" $2 $3 && ))$1
+quiet-@ = $(if $(V),,@)
+quiet-command = $(quiet-@)$(call quiet-command-run,$1,$2,$3)
+
+print-%:
+ @echo '$*=$($*)'
+
+.SUFFIXES:
+
+-include $(DEPS)
diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs
deleted file mode 100644
index 88340a583b..0000000000
--- a/slirp/Makefile.objs
+++ /dev/null
@@ -1,34 +0,0 @@
-slirp-obj-y = slirp.mo
-
-slirp.mo-objs = \
- arp_table.o \
- bootp.o \
- cksum.o \
- dhcpv6.o \
- dnssearch.o \
- if.o \
- ip6_icmp.o \
- ip6_input.o \
- ip6_output.o \
- ip_icmp.o \
- ip_input.o \
- ip_output.o \
- mbuf.o \
- misc.o \
- ncsi.o \
- ndp_table.o \
- sbuf.o \
- slirp.o \
- socket.o \
- state.o \
- tcp_input.o \
- tcp_output.o \
- tcp_subr.o \
- tcp_timer.o \
- tftp.o \
- udp.o \
- udp6.o \
- util.o \
- $(NULL)
-
-slirp.mo-cflags = -DG_LOG_DOMAIN=\"Slirp\" -DWITH_QEMU
diff --git a/slirp/arp_table.c b/slirp/src/arp_table.c
index 58eafdcfd8..58eafdcfd8 100644
--- a/slirp/arp_table.c
+++ b/slirp/src/arp_table.c
diff --git a/slirp/bootp.c b/slirp/src/bootp.c
index d396849a05..d396849a05 100644
--- a/slirp/bootp.c
+++ b/slirp/src/bootp.c
diff --git a/slirp/bootp.h b/slirp/src/bootp.h
index 4043489835..4043489835 100644
--- a/slirp/bootp.h
+++ b/slirp/src/bootp.h
diff --git a/slirp/cksum.c b/slirp/src/cksum.c
index 25bfa67348..25bfa67348 100644
--- a/slirp/cksum.c
+++ b/slirp/src/cksum.c
diff --git a/slirp/debug.h b/slirp/src/debug.h
index 44d922df37..44d922df37 100644
--- a/slirp/debug.h
+++ b/slirp/src/debug.h
diff --git a/slirp/dhcpv6.c b/slirp/src/dhcpv6.c
index e655c7d5b1..e655c7d5b1 100644
--- a/slirp/dhcpv6.c
+++ b/slirp/src/dhcpv6.c
diff --git a/slirp/dhcpv6.h b/slirp/src/dhcpv6.h
index 3373f6cb89..3373f6cb89 100644
--- a/slirp/dhcpv6.h
+++ b/slirp/src/dhcpv6.h
diff --git a/slirp/dnssearch.c b/slirp/src/dnssearch.c
index c459cece8d..c459cece8d 100644
--- a/slirp/dnssearch.c
+++ b/slirp/src/dnssearch.c
diff --git a/slirp/if.c b/slirp/src/if.c
index 1830cc396c..1830cc396c 100644
--- a/slirp/if.c
+++ b/slirp/src/if.c
diff --git a/slirp/if.h b/slirp/src/if.h
index 69569c10df..69569c10df 100644
--- a/slirp/if.h
+++ b/slirp/src/if.h
diff --git a/slirp/ip.h b/slirp/src/ip.h
index 73a4d2a3d2..73a4d2a3d2 100644
--- a/slirp/ip.h
+++ b/slirp/src/ip.h
diff --git a/slirp/ip6.h b/slirp/src/ip6.h
index 1b3364f960..1b3364f960 100644
--- a/slirp/ip6.h
+++ b/slirp/src/ip6.h
diff --git a/slirp/ip6_icmp.c b/slirp/src/ip6_icmp.c
index c1e3d30470..c1e3d30470 100644
--- a/slirp/ip6_icmp.c
+++ b/slirp/src/ip6_icmp.c
diff --git a/slirp/ip6_icmp.h b/slirp/src/ip6_icmp.h
index e8ed753db5..e8ed753db5 100644
--- a/slirp/ip6_icmp.h
+++ b/slirp/src/ip6_icmp.h
diff --git a/slirp/ip6_input.c b/slirp/src/ip6_input.c
index 1b8c003c66..1b8c003c66 100644
--- a/slirp/ip6_input.c
+++ b/slirp/src/ip6_input.c
diff --git a/slirp/ip6_output.c b/slirp/src/ip6_output.c
index 19d1ae7748..19d1ae7748 100644
--- a/slirp/ip6_output.c
+++ b/slirp/src/ip6_output.c
diff --git a/slirp/ip_icmp.c b/slirp/src/ip_icmp.c
index 120108f582..120108f582 100644
--- a/slirp/ip_icmp.c
+++ b/slirp/src/ip_icmp.c
diff --git a/slirp/ip_icmp.h b/slirp/src/ip_icmp.h
index a4e5b8b265..a4e5b8b265 100644
--- a/slirp/ip_icmp.h
+++ b/slirp/src/ip_icmp.h
diff --git a/slirp/ip_input.c b/slirp/src/ip_input.c
index e0b94b0e42..e0b94b0e42 100644
--- a/slirp/ip_input.c
+++ b/slirp/src/ip_input.c
diff --git a/slirp/ip_output.c b/slirp/src/ip_output.c
index f6ec141df5..f6ec141df5 100644
--- a/slirp/ip_output.c
+++ b/slirp/src/ip_output.c
diff --git a/slirp/libslirp.h b/slirp/src/libslirp.h
index fccab42518..2d13950065 100644
--- a/slirp/libslirp.h
+++ b/slirp/src/libslirp.h
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
+#include <sys/types.h>
#ifdef _WIN32
#include <winsock2.h>
@@ -26,6 +27,7 @@ enum {
SLIRP_POLL_HUP = 1 << 4,
};
+typedef ssize_t (*SlirpReadCb)(void *buf, size_t len, void *opaque);
typedef ssize_t (*SlirpWriteCb)(const void *buf, size_t len, void *opaque);
typedef void (*SlirpTimerCb)(void *opaque);
typedef int (*SlirpAddPollCb)(int fd, int events, void *opaque);
@@ -100,6 +102,14 @@ void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr,
int guest_port, const uint8_t *buf, int size);
size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
int guest_port);
+
+void slirp_state_save(Slirp *s, SlirpWriteCb write_cb, void *opaque);
+
+int slirp_state_load(Slirp *s, int version_id,
+ SlirpReadCb read_cb, void *opaque);
+
+int slirp_state_version(void);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/slirp/main.h b/slirp/src/main.h
index f11d4572b7..f11d4572b7 100644
--- a/slirp/main.h
+++ b/slirp/src/main.h
diff --git a/slirp/mbuf.c b/slirp/src/mbuf.c
index 521c02c967..521c02c967 100644
--- a/slirp/mbuf.c
+++ b/slirp/src/mbuf.c
diff --git a/slirp/mbuf.h b/slirp/src/mbuf.h
index e2d443418a..e2d443418a 100644
--- a/slirp/mbuf.h
+++ b/slirp/src/mbuf.h
diff --git a/slirp/misc.c b/slirp/src/misc.c
index d9fc586a24..937a418d4e 100644
--- a/slirp/misc.c
+++ b/slirp/src/misc.c
@@ -28,6 +28,7 @@ remque(void *a)
element->qh_rlink = NULL;
}
+/* TODO: IPv6 */
struct gfwd_list *
add_guestfwd(struct gfwd_list **ex_ptr,
SlirpWriteCb write_cb, void *opaque,
@@ -254,6 +255,8 @@ char *slirp_connection_info(Slirp *slirp)
" Protocol[State] FD Source Address Port "
"Dest. Address Port RecvQ SendQ\n");
+ /* TODO: IPv6 */
+
for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {
if (so->so_state & SS_HOSTFWD) {
state = "HOST_FORWARD";
diff --git a/slirp/misc.h b/slirp/src/misc.h
index c2ceadb591..c2ceadb591 100644
--- a/slirp/misc.h
+++ b/slirp/src/misc.h
diff --git a/slirp/ncsi-pkt.h b/slirp/src/ncsi-pkt.h
index ea07d1cd0f..ea07d1cd0f 100644
--- a/slirp/ncsi-pkt.h
+++ b/slirp/src/ncsi-pkt.h
diff --git a/slirp/ncsi.c b/slirp/src/ncsi.c
index 359f52c284..359f52c284 100644
--- a/slirp/ncsi.c
+++ b/slirp/src/ncsi.c
diff --git a/slirp/ndp_table.c b/slirp/src/ndp_table.c
index 34ea4fdf1f..34ea4fdf1f 100644
--- a/slirp/ndp_table.c
+++ b/slirp/src/ndp_table.c
diff --git a/slirp/qtailq.h b/slirp/src/qtailq.h
index a89b0c439a..a89b0c439a 100644
--- a/slirp/qtailq.h
+++ b/slirp/src/qtailq.h
diff --git a/slirp/sbuf.c b/slirp/src/sbuf.c
index 51a9f0cc7d..51a9f0cc7d 100644
--- a/slirp/sbuf.c
+++ b/slirp/src/sbuf.c
diff --git a/slirp/sbuf.h b/slirp/src/sbuf.h
index 1cb9a42834..1cb9a42834 100644
--- a/slirp/sbuf.h
+++ b/slirp/src/sbuf.h
diff --git a/slirp/slirp.c b/slirp/src/slirp.c
index 55591430dc..18af670a0a 100644
--- a/slirp/slirp.c
+++ b/slirp/src/slirp.c
@@ -23,9 +23,6 @@
*/
#include "slirp.h"
-#ifdef WITH_QEMU
-#include "state.h"
-#endif
#ifndef _WIN32
#include <net/if.h>
@@ -326,9 +323,6 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork,
translate_dnssearch(slirp, vdnssearch);
}
-#ifdef WITH_QEMU
- slirp_state_register(slirp);
-#endif
return slirp;
}
@@ -342,9 +336,6 @@ void slirp_cleanup(Slirp *slirp)
g_free(e);
}
-#ifdef WITH_QEMU
- slirp_state_unregister(slirp);
-#endif
ip_cleanup(slirp);
ip6_cleanup(slirp);
m_cleanup(slirp);
@@ -729,6 +720,7 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
if (ah->ar_tip == slirp->vnameserver_addr.s_addr ||
ah->ar_tip == slirp->vhost_addr.s_addr)
goto arp_ok;
+ /* TODO: IPv6 */
for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
if (ex_ptr->ex_addr.s_addr == ah->ar_tip)
goto arp_ok;
@@ -945,6 +937,7 @@ int if_encap(Slirp *slirp, struct mbuf *ifm)
}
/* Drop host forwarding rule, return 0 if found. */
+/* TODO: IPv6 */
int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
int host_port)
{
@@ -970,6 +963,7 @@ int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
return -1;
}
+/* TODO: IPv6 */
int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
int host_port, struct in_addr guest_addr, int guest_port)
{
@@ -988,6 +982,7 @@ int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
return 0;
}
+/* TODO: IPv6 */
static bool
check_guestfwd(Slirp *slirp, struct in_addr *guest_addr, int guest_port)
{
@@ -1065,6 +1060,7 @@ slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, int guest_port)
{
struct socket *so;
+ /* TODO: IPv6 */
for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {
if (so->so_faddr.s_addr == guest_addr.s_addr &&
htons(so->so_fport) == guest_port) {
diff --git a/slirp/slirp.h b/slirp/src/slirp.h
index 752a4cd8c8..8068ba1d1e 100644
--- a/slirp/slirp.h
+++ b/slirp/src/slirp.h
@@ -106,7 +106,7 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr,
struct ndpentry {
unsigned char eth_addr[ETH_ALEN]; /* sender hardware address */
struct in6_addr ip_addr; /* sender IP address */
-} SLIRP_PACKED;
+};
#define NDP_TABLE_SIZE 16
diff --git a/slirp/socket.c b/slirp/src/socket.c
index 4876ea3f31..f2428a3ae8 100644
--- a/slirp/socket.c
+++ b/slirp/src/socket.c
@@ -529,6 +529,15 @@ sorecvfrom(struct socket *so)
int n;
#endif
+ if (ioctlsocket(so->s, FIONREAD, &n) != 0) {
+ DEBUG_MISC(" ioctlsocket errno = %d-%s\n",
+ errno,strerror(errno));
+ return;
+ }
+ if (n == 0) {
+ return;
+ }
+
m = m_get(so->slirp);
if (!m) {
return;
@@ -552,7 +561,6 @@ sorecvfrom(struct socket *so)
*/
len = M_FREEROOM(m);
/* if (so->so_fport != htons(53)) { */
- ioctlsocket(so->s, FIONREAD, &n);
if (n > len) {
n = (m->m_data - m->m_dat) + m->m_len + n + 1;
@@ -679,6 +687,7 @@ struct socket *
tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport, uint32_t laddr,
unsigned lport, int flags)
{
+ /* TODO: IPv6 */
struct sockaddr_in addr;
struct socket *so;
int s, opt = 1;
diff --git a/slirp/socket.h b/slirp/src/socket.h
index e4d12cd591..e4d12cd591 100644
--- a/slirp/socket.h
+++ b/slirp/src/socket.h
diff --git a/slirp/state.c b/slirp/src/state.c
index 0e5a706e87..f5dd80cdc8 100644
--- a/slirp/state.c
+++ b/slirp/src/state.c
@@ -21,13 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include "qemu/osdep.h"
-
#include "slirp.h"
+#include "vmstate.h"
#include "state.h"
-#include "migration/vmstate.h"
-#include "migration/qemu-file-types.h"
-#include "migration/register.h"
+#include "stream.h"
static int slirp_tcp_post_load(void *opaque, int version)
{
@@ -180,7 +177,7 @@ static int slirp_socket_pre_load(void *opaque)
#else
/* Win uses u_long rather than uint32_t - but it's still 32bits long */
#define VMSTATE_SIN4_ADDR(f, s, t) VMSTATE_SINGLE_TEST(f, s, t, 0, \
- vmstate_info_uint32, u_long)
+ slirp_vmstate_info_uint32, u_long)
#endif
/* The OS provided ss_family field isn't that portable; it's size
@@ -322,10 +319,13 @@ static const VMStateDescription vmstate_slirp = {
}
};
-static void slirp_state_save(QEMUFile *f, void *opaque)
+void slirp_state_save(Slirp *slirp, SlirpWriteCb write_cb, void *opaque)
{
- Slirp *slirp = opaque;
struct gfwd_list *ex_ptr;
+ SlirpOStream f = {
+ .write_cb = write_cb,
+ .opaque = opaque,
+ };
for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
if (ex_ptr->write_cb) {
@@ -336,25 +336,29 @@ static void slirp_state_save(QEMUFile *f, void *opaque)
continue;
}
- qemu_put_byte(f, 42);
- vmstate_save_state(f, &vmstate_slirp_socket, so, NULL);
+ slirp_ostream_write_u8(&f, 42);
+ slirp_vmstate_save_state(&f, &vmstate_slirp_socket, so);
}
- qemu_put_byte(f, 0);
+ slirp_ostream_write_u8(&f, 0);
- vmstate_save_state(f, &vmstate_slirp, slirp, NULL);
+ slirp_vmstate_save_state(&f, &vmstate_slirp, slirp);
}
-static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
+int slirp_state_load(Slirp *slirp, int version_id,
+ SlirpReadCb read_cb, void *opaque)
{
- Slirp *slirp = opaque;
struct gfwd_list *ex_ptr;
+ SlirpIStream f = {
+ .read_cb = read_cb,
+ .opaque = opaque,
+ };
- while (qemu_get_byte(f)) {
+ while (slirp_istream_read_u8(&f)) {
int ret;
struct socket *so = socreate(slirp);
- ret = vmstate_load_state(f, &vmstate_slirp_socket, so, version_id);
+ ret = slirp_vmstate_load_state(&f, &vmstate_slirp_socket, so, version_id);
if (ret < 0) {
return ret;
}
@@ -375,20 +379,10 @@ static int slirp_state_load(QEMUFile *f, void *opaque, int version_id)
}
}
- return vmstate_load_state(f, &vmstate_slirp, slirp, version_id);
-}
-
-void slirp_state_register(Slirp *slirp)
-{
- static SaveVMHandlers savevm_slirp_state = {
- .save_state = slirp_state_save,
- .load_state = slirp_state_load,
- };
-
- register_savevm_live(NULL, "slirp", 0, 4, &savevm_slirp_state, slirp);
+ return slirp_vmstate_load_state(&f, &vmstate_slirp, slirp, version_id);
}
-void slirp_state_unregister(Slirp *slirp)
+int slirp_state_version(void)
{
- unregister_savevm(NULL, "slirp", slirp);
+ return 4;
}
diff --git a/slirp/src/state.h b/slirp/src/state.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/slirp/src/state.h
diff --git a/slirp/src/stream.c b/slirp/src/stream.c
new file mode 100644
index 0000000000..d114dde334
--- /dev/null
+++ b/slirp/src/stream.c
@@ -0,0 +1,119 @@
+/*
+ * libslirp io streams
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "stream.h"
+#include <glib.h>
+
+bool slirp_istream_read(SlirpIStream *f, void *buf, size_t size)
+{
+ return f->read_cb(buf, size, f->opaque) == size;
+}
+
+bool slirp_ostream_write(SlirpOStream *f, const void *buf, size_t size)
+{
+ return f->write_cb(buf, size, f->opaque) == size;
+}
+
+uint8_t slirp_istream_read_u8(SlirpIStream *f)
+{
+ uint8_t b;
+
+ if (slirp_istream_read(f, &b, sizeof(b))) {
+ return b;
+ }
+
+ return 0;
+}
+
+bool slirp_ostream_write_u8(SlirpOStream *f, uint8_t b)
+{
+ return slirp_ostream_write(f, &b, sizeof(b));
+}
+
+uint16_t slirp_istream_read_u16(SlirpIStream *f)
+{
+ uint16_t b;
+
+ if (slirp_istream_read(f, &b, sizeof(b))) {
+ return GUINT16_FROM_BE(b);
+ }
+
+ return 0;
+}
+
+bool slirp_ostream_write_u16(SlirpOStream *f, uint16_t b)
+{
+ b = GUINT16_TO_BE(b);
+ return slirp_ostream_write(f, &b, sizeof(b));
+}
+
+uint32_t slirp_istream_read_u32(SlirpIStream *f)
+{
+ uint32_t b;
+
+ if (slirp_istream_read(f, &b, sizeof(b))) {
+ return GUINT32_FROM_BE(b);
+ }
+
+ return 0;
+}
+
+bool slirp_ostream_write_u32(SlirpOStream *f, uint32_t b)
+{
+ b = GUINT32_TO_BE(b);
+ return slirp_ostream_write(f, &b, sizeof(b));
+}
+
+int16_t slirp_istream_read_i16(SlirpIStream *f)
+{
+ int16_t b;
+
+ if (slirp_istream_read(f, &b, sizeof(b))) {
+ return GINT16_FROM_BE(b);
+ }
+
+ return 0;
+}
+
+bool slirp_ostream_write_i16(SlirpOStream *f, int16_t b)
+{
+ b = GINT16_TO_BE(b);
+ return slirp_ostream_write(f, &b, sizeof(b));
+}
+
+int32_t slirp_istream_read_i32(SlirpIStream *f)
+{
+ int32_t b;
+
+ if (slirp_istream_read(f, &b, sizeof(b))) {
+ return GINT32_FROM_BE(b);
+ }
+
+ return 0;
+}
+
+bool slirp_ostream_write_i32(SlirpOStream *f, int32_t b)
+{
+ b = GINT32_TO_BE(b);
+ return slirp_ostream_write(f, &b, sizeof(b));
+}
diff --git a/slirp/src/stream.h b/slirp/src/stream.h
new file mode 100644
index 0000000000..985334c043
--- /dev/null
+++ b/slirp/src/stream.h
@@ -0,0 +1,34 @@
+#ifndef STREAM_H_
+#define STREAM_H_
+
+#include "libslirp.h"
+
+typedef struct SlirpIStream {
+ SlirpReadCb read_cb;
+ void *opaque;
+} SlirpIStream;
+
+typedef struct SlirpOStream {
+ SlirpWriteCb write_cb;
+ void *opaque;
+} SlirpOStream;
+
+bool slirp_istream_read(SlirpIStream *f, void *buf, size_t size);
+bool slirp_ostream_write(SlirpOStream *f, const void *buf, size_t size);
+
+uint8_t slirp_istream_read_u8(SlirpIStream *f);
+bool slirp_ostream_write_u8(SlirpOStream *f, uint8_t b);
+
+uint16_t slirp_istream_read_u16(SlirpIStream *f);
+bool slirp_ostream_write_u16(SlirpOStream *f, uint16_t b);
+
+uint32_t slirp_istream_read_u32(SlirpIStream *f);
+bool slirp_ostream_write_u32(SlirpOStream *f, uint32_t b);
+
+int16_t slirp_istream_read_i16(SlirpIStream *f);
+bool slirp_ostream_write_i16(SlirpOStream *f, int16_t b);
+
+int32_t slirp_istream_read_i32(SlirpIStream *f);
+bool slirp_ostream_write_i32(SlirpOStream *f, int32_t b);
+
+#endif /* STREAM_H_ */
diff --git a/slirp/tcp.h b/slirp/src/tcp.h
index 47aaea6c5b..47aaea6c5b 100644
--- a/slirp/tcp.h
+++ b/slirp/src/tcp.h
diff --git a/slirp/tcp_input.c b/slirp/src/tcp_input.c
index 6749b32f5d..b10477fc57 100644
--- a/slirp/tcp_input.c
+++ b/slirp/src/tcp_input.c
@@ -388,6 +388,7 @@ findso:
* as if it was LISTENING, and continue...
*/
if (so == NULL) {
+ /* TODO: IPv6 */
if (slirp->restricted) {
/* Any hostfwds will have an existing socket, so we only get here
* for non-hostfwd connections. These should be dropped, unless it
@@ -609,6 +610,7 @@ findso:
* If this is destined for the control address, then flag to
* tcp_ctl once connected, otherwise connect
*/
+ /* TODO: IPv6 */
if (af == AF_INET &&
(so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
diff --git a/slirp/tcp_output.c b/slirp/src/tcp_output.c
index e9674df121..e9674df121 100644
--- a/slirp/tcp_output.c
+++ b/slirp/src/tcp_output.c
diff --git a/slirp/tcp_subr.c b/slirp/src/tcp_subr.c
index 262a42d6c8..1db59caa89 100644
--- a/slirp/tcp_subr.c
+++ b/slirp/src/tcp_subr.c
@@ -626,6 +626,7 @@ tcp_emu(struct socket *so, struct mbuf *m)
switch(so->so_emu) {
int x, i;
+ /* TODO: IPv6 */
case EMU_IDENT:
/*
* Identification protocol as per rfc-1413
@@ -660,16 +661,18 @@ tcp_emu(struct socket *so, struct mbuf *m)
tmpso->so_fport == n1) {
if (getsockname(tmpso->s,
(struct sockaddr *)&addr, &addrlen) == 0)
- n2 = ntohs(addr.sin_port);
+ n2 = addr.sin_port;
break;
}
}
+ NTOHS(n1);
+ NTOHS(n2);
+ so_rcv->sb_cc = snprintf(so_rcv->sb_data,
+ so_rcv->sb_datalen,
+ "%d,%d\r\n", n1, n2);
+ so_rcv->sb_rptr = so_rcv->sb_data;
+ so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc;
}
- so_rcv->sb_cc = snprintf(so_rcv->sb_data,
- so_rcv->sb_datalen,
- "%d,%d\r\n", n1, n2);
- so_rcv->sb_rptr = so_rcv->sb_data;
- so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc;
}
m_free(m);
return 0;
@@ -962,6 +965,7 @@ int tcp_ctl(struct socket *so)
DEBUG_CALL("tcp_ctl");
DEBUG_ARG("so = %p", so);
+ /* TODO: IPv6 */
if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
/* Check if it's pty_exec */
for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
diff --git a/slirp/tcp_timer.c b/slirp/src/tcp_timer.c
index 7be54570af..7be54570af 100644
--- a/slirp/tcp_timer.c
+++ b/slirp/src/tcp_timer.c
diff --git a/slirp/tcp_timer.h b/slirp/src/tcp_timer.h
index b25b3911d7..b25b3911d7 100644
--- a/slirp/tcp_timer.h
+++ b/slirp/src/tcp_timer.h
diff --git a/slirp/tcp_var.h b/slirp/src/tcp_var.h
index 27ef1a51cb..27ef1a51cb 100644
--- a/slirp/tcp_var.h
+++ b/slirp/src/tcp_var.h
diff --git a/slirp/tcpip.h b/slirp/src/tcpip.h
index 07dbf2c432..07dbf2c432 100644
--- a/slirp/tcpip.h
+++ b/slirp/src/tcpip.h
diff --git a/slirp/tftp.c b/slirp/src/tftp.c
index 2d8f978786..2d8f978786 100644
--- a/slirp/tftp.c
+++ b/slirp/src/tftp.c
diff --git a/slirp/tftp.h b/slirp/src/tftp.h
index a4c4a64e64..a4c4a64e64 100644
--- a/slirp/tftp.h
+++ b/slirp/src/tftp.h
diff --git a/slirp/udp.c b/slirp/src/udp.c
index 3d9a19b85a..fa9f4a08bd 100644
--- a/slirp/udp.c
+++ b/slirp/src/udp.c
@@ -322,6 +322,7 @@ struct socket *
udp_listen(Slirp *slirp, uint32_t haddr, unsigned hport, uint32_t laddr,
unsigned lport, int flags)
{
+ /* TODO: IPv6 */
struct sockaddr_in addr;
struct socket *so;
socklen_t addrlen = sizeof(struct sockaddr_in);
diff --git a/slirp/udp.h b/slirp/src/udp.h
index 3d29504caa..3d29504caa 100644
--- a/slirp/udp.h
+++ b/slirp/src/udp.h
diff --git a/slirp/udp6.c b/slirp/src/udp6.c
index be5cba1f54..be5cba1f54 100644
--- a/slirp/udp6.c
+++ b/slirp/src/udp6.c
diff --git a/slirp/util.c b/slirp/src/util.c
index 1cbaa26b60..5ec2fa87ab 100644
--- a/slirp/util.c
+++ b/slirp/src/util.c
@@ -31,8 +31,8 @@
#include <fcntl.h>
#include <stdint.h>
-#if defined(_WIN32) && !defined(WITH_QEMU)
-int inet_aton(const char *cp, struct in_addr *ia)
+#if defined(_WIN32)
+int slirp_inet_aton(const char *cp, struct in_addr *ia)
{
uint32_t addr = inet_addr(cp);
if (addr == 0xffffffff) {
diff --git a/slirp/util.h b/slirp/src/util.h
index c4207a49d6..e94ee4e7f1 100644
--- a/slirp/util.h
+++ b/slirp/src/util.h
@@ -138,8 +138,8 @@ int slirp_getsockopt_wrap(int sockfd, int level, int optname,
#define setsockopt slirp_setsockopt_wrap
int slirp_setsockopt_wrap(int sockfd, int level, int optname,
const void *optval, int optlen);
-
-int inet_aton(const char *cp, struct in_addr *ia);
+#define inet_aton slirp_inet_aton
+int slirp_inet_aton(const char *cp, struct in_addr *ia);
#else
#define closesocket(s) close(s)
#define ioctlsocket(s, r, v) ioctl(s, r, v)
diff --git a/slirp/src/vmstate.c b/slirp/src/vmstate.c
new file mode 100644
index 0000000000..4d08b47c61
--- /dev/null
+++ b/slirp/src/vmstate.c
@@ -0,0 +1,413 @@
+/*
+ * VMState interpreter
+ *
+ * Copyright (c) 2009-2018 Red Hat Inc
+ *
+ * Authors:
+ * Juan Quintela <quintela@redhat.com>
+ *
+ * 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 <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <glib.h>
+
+#include "stream.h"
+#include "vmstate.h"
+
+static int get_nullptr(SlirpIStream *f, void *pv, size_t size,
+ const VMStateField *field)
+{
+ if (slirp_istream_read_u8(f) == VMS_NULLPTR_MARKER) {
+ return 0;
+ }
+ g_warning("vmstate: get_nullptr expected VMS_NULLPTR_MARKER");
+ return -EINVAL;
+}
+
+static int put_nullptr(SlirpOStream *f, void *pv, size_t size,
+ const VMStateField *field)
+
+{
+ if (pv == NULL) {
+ slirp_ostream_write_u8(f, VMS_NULLPTR_MARKER);
+ return 0;
+ }
+ g_warning("vmstate: put_nullptr must be called with pv == NULL");
+ return -EINVAL;
+}
+
+const VMStateInfo slirp_vmstate_info_nullptr = {
+ .name = "uint64",
+ .get = get_nullptr,
+ .put = put_nullptr,
+};
+
+/* 8 bit unsigned int */
+
+static int get_uint8(SlirpIStream *f, void *pv, size_t size, const VMStateField *field)
+{
+ uint8_t *v = pv;
+ *v = slirp_istream_read_u8(f);
+ return 0;
+}
+
+static int put_uint8(SlirpOStream *f, void *pv, size_t size, const VMStateField *field)
+{
+ uint8_t *v = pv;
+ slirp_ostream_write_u8(f, *v);
+ return 0;
+}
+
+const VMStateInfo slirp_vmstate_info_uint8 = {
+ .name = "uint8",
+ .get = get_uint8,
+ .put = put_uint8,
+};
+
+/* 16 bit unsigned int */
+
+static int get_uint16(SlirpIStream *f, void *pv, size_t size,
+ const VMStateField *field)
+{
+ uint16_t *v = pv;
+ *v = slirp_istream_read_u16(f);
+ return 0;
+}
+
+static int put_uint16(SlirpOStream *f, void *pv, size_t size,
+ const VMStateField *field)
+{
+ uint16_t *v = pv;
+ slirp_ostream_write_u16(f, *v);
+ return 0;
+}
+
+const VMStateInfo slirp_vmstate_info_uint16 = {
+ .name = "uint16",
+ .get = get_uint16,
+ .put = put_uint16,
+};
+
+/* 32 bit unsigned int */
+
+static int get_uint32(SlirpIStream *f, void *pv, size_t size,
+ const VMStateField *field)
+{
+ uint32_t *v = pv;
+ *v = slirp_istream_read_u32(f);
+ return 0;
+}
+
+static int put_uint32(SlirpOStream *f, void *pv, size_t size,
+ const VMStateField *field)
+{
+ uint32_t *v = pv;
+ slirp_ostream_write_u32(f, *v);
+ return 0;
+}
+
+const VMStateInfo slirp_vmstate_info_uint32 = {
+ .name = "uint32",
+ .get = get_uint32,
+ .put = put_uint32,
+};
+
+/* 16 bit int */
+
+static int get_int16(SlirpIStream *f, void *pv, size_t size, const VMStateField *field)
+{
+ int16_t *v = pv;
+ *v = slirp_istream_read_i16(f);
+ return 0;
+}
+
+static int put_int16(SlirpOStream *f, void *pv, size_t size, const VMStateField *field)
+{
+ int16_t *v = pv;
+ slirp_ostream_write_i16(f, *v);
+ return 0;
+}
+
+const VMStateInfo slirp_vmstate_info_int16 = {
+ .name = "int16",
+ .get = get_int16,
+ .put = put_int16,
+};
+
+/* 32 bit int */
+
+static int get_int32(SlirpIStream *f, void *pv, size_t size, const VMStateField *field)
+{
+ int32_t *v = pv;
+ *v = slirp_istream_read_i32(f);
+ return 0;
+}
+
+static int put_int32(SlirpOStream *f, void *pv, size_t size, const VMStateField *field)
+{
+ int32_t *v = pv;
+ slirp_ostream_write_i32(f, *v);
+ return 0;
+}
+
+const VMStateInfo slirp_vmstate_info_int32 = {
+ .name = "int32",
+ .get = get_int32,
+ .put = put_int32,
+};
+
+/* vmstate_info_tmp, see VMSTATE_WITH_TMP, the idea is that we allocate
+ * a temporary buffer and the pre_load/pre_save methods in the child vmsd
+ * copy stuff from the parent into the child and do calculations to fill
+ * in fields that don't really exist in the parent but need to be in the
+ * stream.
+ */
+static int get_tmp(SlirpIStream *f, void *pv, size_t size, const VMStateField *field)
+{
+ int ret;
+ const VMStateDescription *vmsd = field->vmsd;
+ int version_id = field->version_id;
+ void *tmp = g_malloc(size);
+
+ /* Writes the parent field which is at the start of the tmp */
+ *(void **)tmp = pv;
+ ret = slirp_vmstate_load_state(f, vmsd, tmp, version_id);
+ g_free(tmp);
+ return ret;
+}
+
+static int put_tmp(SlirpOStream *f, void *pv, size_t size, const VMStateField *field)
+{
+ const VMStateDescription *vmsd = field->vmsd;
+ void *tmp = g_malloc(size);
+ int ret;
+
+ /* Writes the parent field which is at the start of the tmp */
+ *(void **)tmp = pv;
+ ret = slirp_vmstate_save_state(f, vmsd, tmp);
+ g_free(tmp);
+
+ return ret;
+}
+
+const VMStateInfo slirp_vmstate_info_tmp = {
+ .name = "tmp",
+ .get = get_tmp,
+ .put = put_tmp,
+};
+
+/* uint8_t buffers */
+
+static int get_buffer(SlirpIStream *f, void *pv, size_t size,
+ const VMStateField *field)
+{
+ slirp_istream_read(f, pv, size);
+ return 0;
+}
+
+static int put_buffer(SlirpOStream *f, void *pv, size_t size,
+ const VMStateField *field)
+{
+ slirp_ostream_write(f, pv, size);
+ return 0;
+}
+
+const VMStateInfo slirp_vmstate_info_buffer = {
+ .name = "buffer",
+ .get = get_buffer,
+ .put = put_buffer,
+};
+
+static int vmstate_n_elems(void *opaque, const VMStateField *field)
+{
+ int n_elems = 1;
+
+ if (field->flags & VMS_ARRAY) {
+ n_elems = field->num;
+ } else if (field->flags & VMS_VARRAY_INT32) {
+ n_elems = *(int32_t *)(opaque + field->num_offset);
+ } else if (field->flags & VMS_VARRAY_UINT32) {
+ n_elems = *(uint32_t *)(opaque + field->num_offset);
+ } else if (field->flags & VMS_VARRAY_UINT16) {
+ n_elems = *(uint16_t *)(opaque + field->num_offset);
+ } else if (field->flags & VMS_VARRAY_UINT8) {
+ n_elems = *(uint8_t *)(opaque + field->num_offset);
+ }
+
+ if (field->flags & VMS_MULTIPLY_ELEMENTS) {
+ n_elems *= field->num;
+ }
+
+ return n_elems;
+}
+
+static int vmstate_size(void *opaque, const VMStateField *field)
+{
+ int size = field->size;
+
+ if (field->flags & VMS_VBUFFER) {
+ size = *(int32_t *)(opaque + field->size_offset);
+ if (field->flags & VMS_MULTIPLY) {
+ size *= field->size;
+ }
+ }
+
+ return size;
+}
+
+static int
+vmstate_save_state_v(SlirpOStream *f, const VMStateDescription *vmsd,
+ void *opaque, int version_id)
+{
+ int ret = 0;
+ const VMStateField *field = vmsd->fields;
+
+ if (vmsd->pre_save) {
+ ret = vmsd->pre_save(opaque);
+ if (ret) {
+ g_warning("pre-save failed: %s", vmsd->name);
+ return ret;
+ }
+ }
+
+ while (field->name) {
+ if ((field->field_exists &&
+ field->field_exists(opaque, version_id)) ||
+ (!field->field_exists &&
+ field->version_id <= version_id)) {
+ void *first_elem = opaque + field->offset;
+ int i, n_elems = vmstate_n_elems(opaque, field);
+ int size = vmstate_size(opaque, field);
+
+ if (field->flags & VMS_POINTER) {
+ first_elem = *(void **)first_elem;
+ assert(first_elem || !n_elems || !size);
+ }
+ for (i = 0; i < n_elems; i++) {
+ void *curr_elem = first_elem + size * i;
+ ret = 0;
+
+ if (field->flags & VMS_ARRAY_OF_POINTER) {
+ assert(curr_elem);
+ curr_elem = *(void **)curr_elem;
+ }
+ if (!curr_elem && size) {
+ /* if null pointer write placeholder and do not follow */
+ assert(field->flags & VMS_ARRAY_OF_POINTER);
+ ret = slirp_vmstate_info_nullptr.put(f, curr_elem, size, NULL);
+ } else if (field->flags & VMS_STRUCT) {
+ ret = slirp_vmstate_save_state(f, field->vmsd, curr_elem);
+ } else if (field->flags & VMS_VSTRUCT) {
+ ret = vmstate_save_state_v(f, field->vmsd, curr_elem,
+ field->struct_version_id);
+ } else {
+ ret = field->info->put(f, curr_elem, size, field);
+ }
+ if (ret) {
+ g_warning("Save of field %s/%s failed",
+ vmsd->name, field->name);
+ return ret;
+ }
+ }
+ } else {
+ if (field->flags & VMS_MUST_EXIST) {
+ g_warning("Output state validation failed: %s/%s",
+ vmsd->name, field->name);
+ assert(!(field->flags & VMS_MUST_EXIST));
+ }
+ }
+ field++;
+ }
+
+ return 0;
+}
+
+int slirp_vmstate_save_state(SlirpOStream *f, const VMStateDescription *vmsd,
+ void *opaque)
+{
+ return vmstate_save_state_v(f, vmsd, opaque, vmsd->version_id);
+}
+
+static void vmstate_handle_alloc(void *ptr, VMStateField *field, void *opaque)
+{
+ if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) {
+ size_t size = vmstate_size(opaque, field);
+ size *= vmstate_n_elems(opaque, field);
+ if (size) {
+ *(void **)ptr = g_malloc(size);
+ }
+ }
+}
+
+int slirp_vmstate_load_state(SlirpIStream *f, const VMStateDescription *vmsd,
+ void *opaque, int version_id)
+{
+ VMStateField *field = vmsd->fields;
+ int ret = 0;
+
+ if (version_id > vmsd->version_id) {
+ g_warning("%s: incoming version_id %d is too new "
+ "for local version_id %d",
+ vmsd->name, version_id, vmsd->version_id);
+ return -EINVAL;
+ }
+ if (vmsd->pre_load) {
+ int ret = vmsd->pre_load(opaque);
+ if (ret) {
+ return ret;
+ }
+ }
+ while (field->name) {
+ if ((field->field_exists &&
+ field->field_exists(opaque, version_id)) ||
+ (!field->field_exists &&
+ field->version_id <= version_id)) {
+ void *first_elem = opaque + field->offset;
+ int i, n_elems = vmstate_n_elems(opaque, field);
+ int size = vmstate_size(opaque, field);
+
+ vmstate_handle_alloc(first_elem, field, opaque);
+ if (field->flags & VMS_POINTER) {
+ first_elem = *(void **)first_elem;
+ assert(first_elem || !n_elems || !size);
+ }
+ for (i = 0; i < n_elems; i++) {
+ void *curr_elem = first_elem + size * i;
+
+ if (field->flags & VMS_ARRAY_OF_POINTER) {
+ curr_elem = *(void **)curr_elem;
+ }
+ if (!curr_elem && size) {
+ /* if null pointer check placeholder and do not follow */
+ assert(field->flags & VMS_ARRAY_OF_POINTER);
+ ret = slirp_vmstate_info_nullptr.get(f, curr_elem, size, NULL);
+ } else if (field->flags & VMS_STRUCT) {
+ ret = slirp_vmstate_load_state(f, field->vmsd, curr_elem,
+ field->vmsd->version_id);
+ } else if (field->flags & VMS_VSTRUCT) {
+ ret = slirp_vmstate_load_state(f, field->vmsd, curr_elem,
+ field->struct_version_id);
+ } else {
+ ret = field->info->get(f, curr_elem, size, field);
+ }
+ if (ret < 0) {
+ g_warning("Failed to load %s:%s", vmsd->name,
+ field->name);
+ return ret;
+ }
+ }
+ } else if (field->flags & VMS_MUST_EXIST) {
+ g_warning("Input validation failed: %s/%s",
+ vmsd->name, field->name);
+ return -1;
+ }
+ field++;
+ }
+ if (vmsd->post_load) {
+ ret = vmsd->post_load(opaque, version_id);
+ }
+ return ret;
+}
diff --git a/slirp/src/vmstate.h b/slirp/src/vmstate.h
new file mode 100644
index 0000000000..cfa7b8c825
--- /dev/null
+++ b/slirp/src/vmstate.h
@@ -0,0 +1,396 @@
+/*
+ * QEMU migration/snapshot declarations
+ *
+ * Copyright (c) 2009-2011 Red Hat, Inc.
+ *
+ * Original author: Juan Quintela <quintela@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef VMSTATE_H_
+#define VMSTATE_H_
+
+#include <unistd.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "slirp.h"
+#include "stream.h"
+
+#define stringify(s) tostring(s)
+#define tostring(s) #s
+
+typedef struct VMStateInfo VMStateInfo;
+typedef struct VMStateDescription VMStateDescription;
+typedef struct VMStateField VMStateField;
+
+int slirp_vmstate_save_state(SlirpOStream *f, const VMStateDescription *vmsd,
+ void *opaque);
+int slirp_vmstate_load_state(SlirpIStream *f, const VMStateDescription *vmsd,
+ void *opaque, int version_id);
+
+/* VMStateInfo allows customized migration of objects that don't fit in
+ * any category in VMStateFlags. Additional information is always passed
+ * into get and put in terms of field and vmdesc parameters. However
+ * these two parameters should only be used in cases when customized
+ * handling is needed, such as QTAILQ. For primitive data types such as
+ * integer, field and vmdesc parameters should be ignored inside get/put.
+ */
+struct VMStateInfo {
+ const char *name;
+ int (*get)(SlirpIStream *f, void *pv, size_t size, const VMStateField *field);
+ int (*put)(SlirpOStream *f, void *pv, size_t size, const VMStateField *field);
+};
+
+enum VMStateFlags {
+ /* Ignored */
+ VMS_SINGLE = 0x001,
+
+ /* The struct member at opaque + VMStateField.offset is a pointer
+ * to the actual field (e.g. struct a { uint8_t *b;
+ * }). Dereference the pointer before using it as basis for
+ * further pointer arithmetic (see e.g. VMS_ARRAY). Does not
+ * affect the meaning of VMStateField.num_offset or
+ * VMStateField.size_offset; see VMS_VARRAY* and VMS_VBUFFER for
+ * those. */
+ VMS_POINTER = 0x002,
+
+ /* The field is an array of fixed size. VMStateField.num contains
+ * the number of entries in the array. The size of each entry is
+ * given by VMStateField.size and / or opaque +
+ * VMStateField.size_offset; see VMS_VBUFFER and
+ * VMS_MULTIPLY. Each array entry will be processed individually
+ * (VMStateField.info.get()/put() if VMS_STRUCT is not set,
+ * recursion into VMStateField.vmsd if VMS_STRUCT is set). May not
+ * be combined with VMS_VARRAY*. */
+ VMS_ARRAY = 0x004,
+
+ /* The field is itself a struct, containing one or more
+ * fields. Recurse into VMStateField.vmsd. Most useful in
+ * combination with VMS_ARRAY / VMS_VARRAY*, recursing into each
+ * array entry. */
+ VMS_STRUCT = 0x008,
+
+ /* The field is an array of variable size. The int32_t at opaque +
+ * VMStateField.num_offset contains the number of entries in the
+ * array. See the VMS_ARRAY description regarding array handling
+ * in general. May not be combined with VMS_ARRAY or any other
+ * VMS_VARRAY*. */
+ VMS_VARRAY_INT32 = 0x010,
+
+ /* Ignored */
+ VMS_BUFFER = 0x020,
+
+ /* The field is a (fixed-size or variable-size) array of pointers
+ * (e.g. struct a { uint8_t *b[]; }). Dereference each array entry
+ * before using it. Note: Does not imply any one of VMS_ARRAY /
+ * VMS_VARRAY*; these need to be set explicitly. */
+ VMS_ARRAY_OF_POINTER = 0x040,
+
+ /* The field is an array of variable size. The uint16_t at opaque
+ * + VMStateField.num_offset (subject to VMS_MULTIPLY_ELEMENTS)
+ * contains the number of entries in the array. See the VMS_ARRAY
+ * description regarding array handling in general. May not be
+ * combined with VMS_ARRAY or any other VMS_VARRAY*. */
+ VMS_VARRAY_UINT16 = 0x080,
+
+ /* The size of the individual entries (a single array entry if
+ * VMS_ARRAY or any of VMS_VARRAY* are set, or the field itself if
+ * neither is set) is variable (i.e. not known at compile-time),
+ * but the same for all entries. Use the int32_t at opaque +
+ * VMStateField.size_offset (subject to VMS_MULTIPLY) to determine
+ * the size of each (and every) entry. */
+ VMS_VBUFFER = 0x100,
+
+ /* Multiply the entry size given by the int32_t at opaque +
+ * VMStateField.size_offset (see VMS_VBUFFER description) with
+ * VMStateField.size to determine the number of bytes to be
+ * allocated. Only valid in combination with VMS_VBUFFER. */
+ VMS_MULTIPLY = 0x200,
+
+ /* The field is an array of variable size. The uint8_t at opaque +
+ * VMStateField.num_offset (subject to VMS_MULTIPLY_ELEMENTS)
+ * contains the number of entries in the array. See the VMS_ARRAY
+ * description regarding array handling in general. May not be
+ * combined with VMS_ARRAY or any other VMS_VARRAY*. */
+ VMS_VARRAY_UINT8 = 0x400,
+
+ /* The field is an array of variable size. The uint32_t at opaque
+ * + VMStateField.num_offset (subject to VMS_MULTIPLY_ELEMENTS)
+ * contains the number of entries in the array. See the VMS_ARRAY
+ * description regarding array handling in general. May not be
+ * combined with VMS_ARRAY or any other VMS_VARRAY*. */
+ VMS_VARRAY_UINT32 = 0x800,
+
+ /* Fail loading the serialised VM state if this field is missing
+ * from the input. */
+ VMS_MUST_EXIST = 0x1000,
+
+ /* When loading serialised VM state, allocate memory for the
+ * (entire) field. Only valid in combination with
+ * VMS_POINTER. Note: Not all combinations with other flags are
+ * currently supported, e.g. VMS_ALLOC|VMS_ARRAY_OF_POINTER won't
+ * cause the individual entries to be allocated. */
+ VMS_ALLOC = 0x2000,
+
+ /* Multiply the number of entries given by the integer at opaque +
+ * VMStateField.num_offset (see VMS_VARRAY*) with VMStateField.num
+ * to determine the number of entries in the array. Only valid in
+ * combination with one of VMS_VARRAY*. */
+ VMS_MULTIPLY_ELEMENTS = 0x4000,
+
+ /* A structure field that is like VMS_STRUCT, but uses
+ * VMStateField.struct_version_id to tell which version of the
+ * structure we are referencing to use. */
+ VMS_VSTRUCT = 0x8000,
+};
+
+struct VMStateField {
+ const char *name;
+ size_t offset;
+ size_t size;
+ size_t start;
+ int num;
+ size_t num_offset;
+ size_t size_offset;
+ const VMStateInfo *info;
+ enum VMStateFlags flags;
+ const VMStateDescription *vmsd;
+ int version_id;
+ int struct_version_id;
+ bool (*field_exists)(void *opaque, int version_id);
+};
+
+struct VMStateDescription {
+ const char *name;
+ int version_id;
+ int (*pre_load)(void *opaque);
+ int (*post_load)(void *opaque, int version_id);
+ int (*pre_save)(void *opaque);
+ VMStateField *fields;
+};
+
+
+extern const VMStateInfo slirp_vmstate_info_int16;
+extern const VMStateInfo slirp_vmstate_info_int32;
+extern const VMStateInfo slirp_vmstate_info_uint8;
+extern const VMStateInfo slirp_vmstate_info_uint16;
+extern const VMStateInfo slirp_vmstate_info_uint32;
+
+/** Put this in the stream when migrating a null pointer.*/
+#define VMS_NULLPTR_MARKER (0x30U) /* '0' */
+extern const VMStateInfo slirp_vmstate_info_nullptr;
+
+extern const VMStateInfo slirp_vmstate_info_buffer;
+extern const VMStateInfo slirp_vmstate_info_tmp;
+
+#define type_check_array(t1,t2,n) ((t1(*)[n])0 - (t2*)0)
+#define type_check_pointer(t1,t2) ((t1**)0 - (t2*)0)
+#define typeof_field(type, field) typeof(((type *)0)->field)
+#define type_check(t1,t2) ((t1*)0 - (t2*)0)
+
+#define vmstate_offset_value(_state, _field, _type) \
+ (offsetof(_state, _field) + \
+ type_check(_type, typeof_field(_state, _field)))
+
+#define vmstate_offset_pointer(_state, _field, _type) \
+ (offsetof(_state, _field) + \
+ type_check_pointer(_type, typeof_field(_state, _field)))
+
+#define vmstate_offset_array(_state, _field, _type, _num) \
+ (offsetof(_state, _field) + \
+ type_check_array(_type, typeof_field(_state, _field), _num))
+
+#define vmstate_offset_buffer(_state, _field) \
+ vmstate_offset_array(_state, _field, uint8_t, \
+ sizeof(typeof_field(_state, _field)))
+
+/* In the macros below, if there is a _version, that means the macro's
+ * field will be processed only if the version being received is >=
+ * the _version specified. In general, if you add a new field, you
+ * would increment the structure's version and put that version
+ * number into the new field so it would only be processed with the
+ * new version.
+ *
+ * In particular, for VMSTATE_STRUCT() and friends the _version does
+ * *NOT* pick the version of the sub-structure. It works just as
+ * specified above. The version of the top-level structure received
+ * is passed down to all sub-structures. This means that the
+ * sub-structures must have version that are compatible with all the
+ * structures that use them.
+ *
+ * If you want to specify the version of the sub-structure, use
+ * VMSTATE_VSTRUCT(), which allows the specific sub-structure version
+ * to be directly specified.
+ */
+
+#define VMSTATE_SINGLE_TEST(_field, _state, _test, _version, _info, _type) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .field_exists = (_test), \
+ .size = sizeof(_type), \
+ .info = &(_info), \
+ .flags = VMS_SINGLE, \
+ .offset = vmstate_offset_value(_state, _field, _type), \
+}
+
+#define VMSTATE_ARRAY(_field, _state, _num, _version, _info, _type) {\
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .num = (_num), \
+ .info = &(_info), \
+ .size = sizeof(_type), \
+ .flags = VMS_ARRAY, \
+ .offset = vmstate_offset_array(_state, _field, _type, _num), \
+}
+
+#define VMSTATE_STRUCT_TEST(_field, _state, _test, _version, _vmsd, _type) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .field_exists = (_test), \
+ .vmsd = &(_vmsd), \
+ .size = sizeof(_type), \
+ .flags = VMS_STRUCT, \
+ .offset = vmstate_offset_value(_state, _field, _type), \
+}
+
+#define VMSTATE_STRUCT_POINTER_V(_field, _state, _version, _vmsd, _type) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .vmsd = &(_vmsd), \
+ .size = sizeof(_type *), \
+ .flags = VMS_STRUCT|VMS_POINTER, \
+ .offset = vmstate_offset_pointer(_state, _field, _type), \
+}
+
+#define VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, _test, _version, _vmsd, _type) { \
+ .name = (stringify(_field)), \
+ .num = (_num), \
+ .field_exists = (_test), \
+ .version_id = (_version), \
+ .vmsd = &(_vmsd), \
+ .size = sizeof(_type), \
+ .flags = VMS_STRUCT|VMS_ARRAY, \
+ .offset = vmstate_offset_array(_state, _field, _type, _num),\
+}
+
+#define VMSTATE_STATIC_BUFFER(_field, _state, _version, _test, _start, _size) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .field_exists = (_test), \
+ .size = (_size - _start), \
+ .info = &slirp_vmstate_info_buffer, \
+ .flags = VMS_BUFFER, \
+ .offset = vmstate_offset_buffer(_state, _field) + _start, \
+}
+
+#define VMSTATE_VBUFFER_UINT32(_field, _state, _version, _test, _field_size) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .field_exists = (_test), \
+ .size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\
+ .info = &slirp_vmstate_info_buffer, \
+ .flags = VMS_VBUFFER|VMS_POINTER, \
+ .offset = offsetof(_state, _field), \
+}
+
+#define QEMU_BUILD_BUG_ON_STRUCT(x) \
+ struct { \
+ int:(x) ? -1 : 1; \
+ }
+
+#define QEMU_BUILD_BUG_ON_ZERO(x) (sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)) - \
+ sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)))
+
+/* Allocate a temporary of type 'tmp_type', set tmp->parent to _state
+ * and execute the vmsd on the temporary. Note that we're working with
+ * the whole of _state here, not a field within it.
+ * We compile time check that:
+ * That _tmp_type contains a 'parent' member that's a pointer to the
+ * '_state' type
+ * That the pointer is right at the start of _tmp_type.
+ */
+#define VMSTATE_WITH_TMP(_state, _tmp_type, _vmsd) { \
+ .name = "tmp", \
+ .size = sizeof(_tmp_type) + \
+ QEMU_BUILD_BUG_ON_ZERO(offsetof(_tmp_type, parent) != 0) + \
+ type_check_pointer(_state, \
+ typeof_field(_tmp_type, parent)), \
+ .vmsd = &(_vmsd), \
+ .info = &slirp_vmstate_info_tmp, \
+}
+
+#define VMSTATE_SINGLE(_field, _state, _version, _info, _type) \
+ VMSTATE_SINGLE_TEST(_field, _state, NULL, _version, _info, _type)
+
+#define VMSTATE_STRUCT(_field, _state, _version, _vmsd, _type) \
+ VMSTATE_STRUCT_TEST(_field, _state, NULL, _version, _vmsd, _type)
+
+#define VMSTATE_STRUCT_POINTER(_field, _state, _vmsd, _type) \
+ VMSTATE_STRUCT_POINTER_V(_field, _state, 0, _vmsd, _type)
+
+#define VMSTATE_STRUCT_ARRAY(_field, _state, _num, _version, _vmsd, _type) \
+ VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, NULL, _version, \
+ _vmsd, _type)
+
+#define VMSTATE_INT16_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_int16, int16_t)
+#define VMSTATE_INT32_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_int32, int32_t)
+
+#define VMSTATE_UINT8_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_uint8, uint8_t)
+#define VMSTATE_UINT16_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_uint16, uint16_t)
+#define VMSTATE_UINT32_V(_f, _s, _v) \
+ VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_uint32, uint32_t)
+
+#define VMSTATE_INT16(_f, _s) \
+ VMSTATE_INT16_V(_f, _s, 0)
+#define VMSTATE_INT32(_f, _s) \
+ VMSTATE_INT32_V(_f, _s, 0)
+
+#define VMSTATE_UINT8(_f, _s) \
+ VMSTATE_UINT8_V(_f, _s, 0)
+#define VMSTATE_UINT16(_f, _s) \
+ VMSTATE_UINT16_V(_f, _s, 0)
+#define VMSTATE_UINT32(_f, _s) \
+ VMSTATE_UINT32_V(_f, _s, 0)
+
+#define VMSTATE_UINT16_TEST(_f, _s, _t) \
+ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, slirp_vmstate_info_uint16, uint16_t)
+
+#define VMSTATE_UINT32_TEST(_f, _s, _t) \
+ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, slirp_vmstate_info_uint32, uint32_t)
+
+#define VMSTATE_INT16_ARRAY_V(_f, _s, _n, _v) \
+ VMSTATE_ARRAY(_f, _s, _n, _v, slirp_vmstate_info_int16, int16_t)
+
+#define VMSTATE_INT16_ARRAY(_f, _s, _n) \
+ VMSTATE_INT16_ARRAY_V(_f, _s, _n, 0)
+
+#define VMSTATE_BUFFER_V(_f, _s, _v) \
+ VMSTATE_STATIC_BUFFER(_f, _s, _v, NULL, 0, sizeof(typeof_field(_s, _f)))
+
+#define VMSTATE_BUFFER(_f, _s) \
+ VMSTATE_BUFFER_V(_f, _s, 0)
+
+#define VMSTATE_END_OF_LIST() \
+ {}
+
+#endif
diff --git a/slirp/state.h b/slirp/state.h
deleted file mode 100644
index 154866898f..0000000000
--- a/slirp/state.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef SLIRP_STATE_H_
-#define SLIRP_STATE_H_
-
-#include "libslirp.h"
-
-void slirp_state_register(Slirp *slirp);
-void slirp_state_unregister(Slirp *slirp);
-
-#endif /* SLIRP_STATE_H_ */
diff --git a/target/hppa/translate.c b/target/hppa/translate.c
index b4fd307b77..dc5636fe94 100644
--- a/target/hppa/translate.c
+++ b/target/hppa/translate.c
@@ -2007,16 +2007,15 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset)
/* Privilege 0 is maximum and is allowed to decrease. */
return offset;
case 3:
- /* Privilege 3 is minimum and is never allowed increase. */
+ /* Privilege 3 is minimum and is never allowed to increase. */
dest = get_temp(ctx);
tcg_gen_ori_reg(dest, offset, 3);
break;
default:
- dest = tcg_temp_new();
+ dest = get_temp(ctx);
tcg_gen_andi_reg(dest, offset, -4);
tcg_gen_ori_reg(dest, dest, ctx->privilege);
tcg_gen_movcond_reg(TCG_COND_GTU, dest, dest, offset, dest, offset);
- tcg_temp_free(dest);
break;
}
return dest;
@@ -3489,12 +3488,16 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
static bool trans_blr(DisasContext *ctx, arg_blr *a)
{
- TCGv_reg tmp = get_temp(ctx);
-
- tcg_gen_shli_reg(tmp, load_gpr(ctx, a->x), 3);
- tcg_gen_addi_reg(tmp, tmp, ctx->iaoq_f + 8);
- /* The computation here never changes privilege level. */
- return do_ibranch(ctx, tmp, a->l, a->n);
+ if (a->x) {
+ TCGv_reg tmp = get_temp(ctx);
+ tcg_gen_shli_reg(tmp, load_gpr(ctx, a->x), 3);
+ tcg_gen_addi_reg(tmp, tmp, ctx->iaoq_f + 8);
+ /* The computation here never changes privilege level. */
+ return do_ibranch(ctx, tmp, a->l, a->n);
+ } else {
+ /* BLR R0,RX is a good way to load PC+8 into RX. */
+ return do_dbranch(ctx, ctx->iaoq_f + 8, a->l, a->n);
+ }
}
static bool trans_bv(DisasContext *ctx, arg_bv *a)
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 97e1cb90a3..44af41515b 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -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
diff --git a/tests/ac97-test.c b/tests/ac97-test.c
index e0d177bd9c..532fb1cc98 100644
--- a/tests/ac97-test.c
+++ b/tests/ac97-test.c
@@ -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();
+ qpci_device_init(&ac97->dev, bus, addr);
+ ac97->obj.get_driver = ac97_get_driver;
+ return &ac97->obj;
+}
- qtest_end();
+static void ac97_register_nodes(void)
+{
+ QOSGraphEdgeOptions opts = {
+ .extra_device_opts = "addr=04.0",
+ };
+ add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
- return ret;
+ 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);
diff --git a/tests/ahci-test.c b/tests/ahci-test.c
index 5dd380e5ea..9f07e6f2ce 100644
--- a/tests/ahci-test.c
+++ b/tests/ahci-test.c
@@ -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);
diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c
index 4a1a0889c8..2f9474e03c 100644
--- a/tests/drive_del-test.c
+++ b/tests/drive_del-test.c
@@ -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",
diff --git a/tests/e1000-test.c b/tests/e1000-test.c
index 0c5fcdcc44..9e67916169 100644
--- a/tests/e1000-test.c
+++ b/tests/e1000-test.c
@@ -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;
-
- args = g_strdup_printf("-device %s", model);
- s = qtest_start(args);
+typedef struct QE1000 QE1000;
- 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);
+ if (!g_strcmp0(interface, "pci-device")) {
+ return &e1000->dev;
+ }
- for (i = 0; i < ARRAY_SIZE(models); i++) {
- char *path;
+ fprintf(stderr, "%s not present in e1000e\n", interface);
+ g_assert_not_reached();
+}
- path = g_strdup_printf("e1000/%s", models[i]);
- qtest_add_data_func(path, models[i], test_device);
- g_free(path);
- }
+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) });
- return g_test_run();
+ 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);
diff --git a/tests/e1000e-test.c b/tests/e1000e-test.c
index c9408a5d1f..77ba8095bb 100644
--- a/tests/e1000e-test.c
+++ b/tests/e1000e-test.c
@@ -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);
-}
-
-static void e1000e_device_clear(QPCIBus *bus, e1000e_device *d)
-{
- qpci_iounmap(d->pci_dev, d->mac_regs);
- qpci_msix_disable(d->pci_dev);
-}
-
-static void data_test_init(e1000e_device *d)
-{
- char *cmdline;
-
- int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, test_sockets);
- g_assert_cmpint(ret, != , -1);
-
- 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);
+ guest_free(alloc, data);
}
-static void data_test_clear(e1000e_device *d)
+static void test_e1000e_init(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();
+ /* init does nothing */
}
-static void test_e1000e_init(gconstpointer data)
+static void test_e1000e_tx(void *obj, void *data, QGuestAllocator * alloc)
{
- e1000e_device d;
-
- data_test_init(&d);
- data_test_clear(&d);
-}
-
-static void test_e1000e_tx(gconstpointer data)
-{
- 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");
+
+ /* FIXME: add spapr support */
+ if (qpci_check_buggy_msi(dev)) {
+ return;
+ }
- data_test_init(&d);
- e1000e_send_verify(&d);
- data_test_clear(&d);
+ e1000e_send_verify(d, data, alloc);
}
-static void test_e1000e_rx(gconstpointer data)
+static void test_e1000e_rx(void *obj, void *data, QGuestAllocator * alloc)
{
- 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");
+
+ /* FIXME: add spapr support */
+ if (qpci_check_buggy_msi(dev)) {
+ return;
+ }
- data_test_init(&d);
- e1000e_receive_verify(&d);
- data_test_clear(&d);
+ e1000e_receive_verify(d, data, alloc);
}
-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);
+ /* FIXME: add spapr support */
+ if (qpci_check_buggy_msi(dev)) {
+ return;
+ }
for (i = 0; i < iterations; i++) {
- e1000e_send_verify(&d);
- e1000e_receive_verify(&d);
+ 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);
+}
+
+static void data_test_clear(void *sockets)
+{
+ int *test_sockets = sockets;
- qtest_end();
+ close(test_sockets[0]);
+ qos_invalidate_command_line();
+ close(test_sockets[1]);
+ g_free(test_sockets);
}
-int main(int argc, char **argv)
+static void *data_test_init(GString *cmd_line, void *arg)
{
- g_test_init(&argc, &argv, NULL);
+ int *test_sockets = g_new(int, 2);
+ int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, test_sockets);
+ g_assert_cmpint(ret, != , -1);
- 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);
+ g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ",
+ test_sockets[1]);
- return g_test_run();
+ 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);
diff --git a/tests/eepro100-test.c b/tests/eepro100-test.c
index bdc8a67d57..90b5c1afd9 100644
--- a/tests/eepro100-test.c
+++ b/tests/eepro100-test.c
@@ -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;
-
- args = g_strdup_printf("-device %s", model);
- s = qtest_start(args);
-
- /* Tests only initialization so far. TODO: Implement functional tests */
+typedef struct QEEPRO100 QEEPRO100;
- 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);
+ if (!g_strcmp0(interface, "pci-device")) {
+ return &eepro100->dev;
+ }
- for (i = 0; i < ARRAY_SIZE(models); i++) {
- char *path;
+ fprintf(stderr, "%s not present in eepro100\n", interface);
+ g_assert_not_reached();
+}
- path = g_strdup_printf("eepro100/%s", models[i]);
- qtest_add_data_func(path, models[i], test_device);
- g_free(path);
- }
+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",
+ };
- return g_test_run();
+ 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);
diff --git a/tests/es1370-test.c b/tests/es1370-test.c
index 199fe193ce..d845cd06f8 100644
--- a/tests/es1370-test.c
+++ b/tests/es1370-test.c
@@ -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();
+ return &es1370->obj;
+}
- qtest_end();
+static void es1370_register_nodes(void)
+{
+ QOSGraphEdgeOptions opts = {
+ .extra_device_opts = "addr=04.0",
+ };
+ add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
- return ret;
+ 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);
diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
index 4390e5591e..69205b58a8 100644
--- a/tests/i440fx-test.c
+++ b/tests/i440fx-test.c
@@ -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)
diff --git a/tests/ide-test.c b/tests/ide-test.c
index 300d64e77d..d863a99f7f 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -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);
diff --git a/tests/ipoctal232-test.c b/tests/ipoctal232-test.c
index 684914164d..42d53718b8 100644
--- a/tests/ipoctal232-test.c
+++ b/tests/ipoctal232-test.c
@@ -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);
+ return &ipoctal232->obj;
+}
- qtest_start("-device tpci200,id=ipack0 -device ipoctal232,bus=ipack0.0");
- ret = g_test_run();
+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",
+ });
+}
- qtest_end();
+libqos_init(ipoctal232_register_nodes);
- return ret;
+static void register_ipoctal232_test(void)
+{
+ qos_add_test("nop", "ipoctal232", nop, NULL);
}
+
+libqos_init(register_ipoctal232_test);
diff --git a/tests/libqos/aarch64-xlnx-zcu102-machine.c b/tests/libqos/aarch64-xlnx-zcu102-machine.c
new file mode 100644
index 0000000000..6fff040b37
--- /dev/null
+++ b/tests/libqos/aarch64-xlnx-zcu102-machine.c
@@ -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);
diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c
index 63fbc9e3c9..cc1b08eabe 100644
--- a/tests/libqos/ahci.c
+++ b/tests/libqos/ahci.c
@@ -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));
diff --git a/tests/libqos/arm-raspi2-machine.c b/tests/libqos/arm-raspi2-machine.c
new file mode 100644
index 0000000000..3aff670f76
--- /dev/null
+++ b/tests/libqos/arm-raspi2-machine.c
@@ -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);
diff --git a/tests/libqos/arm-sabrelite-machine.c b/tests/libqos/arm-sabrelite-machine.c
new file mode 100644
index 0000000000..c4128d8686
--- /dev/null
+++ b/tests/libqos/arm-sabrelite-machine.c
@@ -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);
diff --git a/tests/libqos/arm-smdkc210-machine.c b/tests/libqos/arm-smdkc210-machine.c
new file mode 100644
index 0000000000..1fb9dfc0cb
--- /dev/null
+++ b/tests/libqos/arm-smdkc210-machine.c
@@ -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);
diff --git a/tests/libqos/arm-virt-machine.c b/tests/libqos/arm-virt-machine.c
new file mode 100644
index 0000000000..2abc431ecf
--- /dev/null
+++ b/tests/libqos/arm-virt-machine.c
@@ -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);
diff --git a/tests/libqos/arm-xilinx-zynq-a9-machine.c b/tests/libqos/arm-xilinx-zynq-a9-machine.c
new file mode 100644
index 0000000000..4e199fcd48
--- /dev/null
+++ b/tests/libqos/arm-xilinx-zynq-a9-machine.c
@@ -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);
diff --git a/tests/libqos/e1000e.c b/tests/libqos/e1000e.c
new file mode 100644
index 0000000000..54d3898899
--- /dev/null
+++ b/tests/libqos/e1000e.c
@@ -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);
diff --git a/tests/libqos/e1000e.h b/tests/libqos/e1000e.h
new file mode 100644
index 0000000000..9d37094f43
--- /dev/null
+++ b/tests/libqos/e1000e.h
@@ -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
diff --git a/tests/libqos/libqos-pc.c b/tests/libqos/libqos-pc.c
index a9c1aceaa7..d04abc548b 100644
--- a/tests/libqos/libqos-pc.c
+++ b/tests/libqos/libqos-pc.c
@@ -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,
};
diff --git a/tests/libqos/libqos-spapr.c b/tests/libqos/libqos-spapr.c
index a37791e5d0..8766d543ce 100644
--- a/tests/libqos/libqos-spapr.c
+++ b/tests/libqos/libqos-spapr.c
@@ -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,
};
diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c
index c514187344..636a111a6f 100644
--- a/tests/libqos/libqos.c
+++ b/tests/libqos/libqos.c
@@ -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);
}
diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h
index 07d4b93d1d..149b0be8bc 100644
--- a/tests/libqos/libqos.h
+++ b/tests/libqos/libqos.h
@@ -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
diff --git a/tests/libqos/malloc-generic.c b/tests/libqos/malloc-generic.c
deleted file mode 100644
index 33ce90b925..0000000000
--- a/tests/libqos/malloc-generic.c
+++ /dev/null
@@ -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);
-}
diff --git a/tests/libqos/malloc-generic.h b/tests/libqos/malloc-generic.h
deleted file mode 100644
index 90104ecec9..0000000000
--- a/tests/libqos/malloc-generic.h
+++ /dev/null
@@ -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
diff --git a/tests/libqos/malloc-pc.c b/tests/libqos/malloc-pc.c
index b83cb8f0af..949a99361d 100644
--- a/tests/libqos/malloc-pc.c
+++ b/tests/libqos/malloc-pc.c
@@ -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)
-{
- alloc_uninit(allocator);
-}
-
-QGuestAllocator *pc_alloc_init_flags(QTestState *qts, QAllocOpts flags)
+void pc_alloc_init(QGuestAllocator *s, 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);
}
diff --git a/tests/libqos/malloc-pc.h b/tests/libqos/malloc-pc.h
index 10f3da6cf2..21e75ae004 100644
--- a/tests/libqos/malloc-pc.h
+++ b/tests/libqos/malloc-pc.h
@@ -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
diff --git a/tests/libqos/malloc-spapr.c b/tests/libqos/malloc-spapr.c
index 1c359cea6c..2a6b7e3776 100644
--- a/tests/libqos/malloc-spapr.c
+++ b/tests/libqos/malloc-spapr.c
@@ -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);
}
diff --git a/tests/libqos/malloc-spapr.h b/tests/libqos/malloc-spapr.h
index 52a9346a26..e5fe9bfc4b 100644
--- a/tests/libqos/malloc-spapr.h
+++ b/tests/libqos/malloc-spapr.h
@@ -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
diff --git a/tests/libqos/malloc.c b/tests/libqos/malloc.c
index f7bae47a08..615422a5c4 100644
--- a/tests/libqos/malloc.c
+++ b/tests/libqos/malloc.c
@@ -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)
diff --git a/tests/libqos/malloc.h b/tests/libqos/malloc.h
index 828fddabdb..4d1a2e2bef 100644
--- a/tests/libqos/malloc.h
+++ b/tests/libqos/malloc.h
@@ -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
diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c
index a4fc02b5d8..4ab16facf2 100644
--- a/tests/libqos/pci-pc.c
+++ b/tests/libqos/pci-pc.c
@@ -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;
+
+ 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.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_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.memread = qpci_pc_memread;
- ret->bus.memwrite = qpci_pc_memwrite;
+ qpci->bus.memread = qpci_pc_memread;
+ qpci->bus.memwrite = qpci_pc_memwrite;
- 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.config_readb = qpci_pc_config_readb;
+ qpci->bus.config_readw = qpci_pc_config_readw;
+ qpci->bus.config_readl = qpci_pc_config_readl;
- 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_writeb = qpci_pc_config_writeb;
+ qpci->bus.config_writew = qpci_pc_config_writew;
+ qpci->bus.config_writel = qpci_pc_config_writel;
- ret->bus.qts = qts;
- ret->bus.pio_alloc_ptr = 0xc000;
- ret->bus.mmio_alloc_ptr = 0xE0000000;
- ret->bus.mmio_limit = 0x100000000ULL;
+ qpci->bus.qts = qts;
+ qpci->bus.pio_alloc_ptr = 0xc000;
+ qpci->bus.mmio_alloc_ptr = 0xE0000000;
+ qpci->bus.mmio_limit = 0x100000000ULL;
- return &ret->bus;
+ 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);
diff --git a/tests/libqos/pci-pc.h b/tests/libqos/pci-pc.h
index 491eeac756..4690005232 100644
--- a/tests/libqos/pci-pc.h
+++ b/tests/libqos/pci-pc.h
@@ -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
diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c
index 4c29889b0b..6925925997 100644
--- a/tests/libqos/pci-spapr.c
+++ b/tests/libqos/pci-spapr.c
@@ -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;
+
+ qpci->alloc = alloc;
- 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->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.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_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.memread = qpci_spapr_memread;
- ret->bus.memwrite = qpci_spapr_memwrite;
+ qpci->bus.memread = qpci_spapr_memread;
+ qpci->bus.memwrite = qpci_spapr_memwrite;
- 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.config_readb = qpci_spapr_config_readb;
+ qpci->bus.config_readw = qpci_spapr_config_readw;
+ qpci->bus.config_readl = qpci_spapr_config_readl;
- 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_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);
diff --git a/tests/libqos/pci-spapr.h b/tests/libqos/pci-spapr.h
index 387686dfc8..d9e25631c6 100644
--- a/tests/libqos/pci-spapr.h
+++ b/tests/libqos/pci-spapr.h
@@ -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
diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c
index e8c342c257..662ee7a517 100644
--- a/tests/libqos/pci.c
+++ b/tests/libqos/pci.c
@@ -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);
+}
diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h
index 0b7e936174..8e1d292a7d 100644
--- a/tests/libqos/pci.h
+++ b/tests/libqos/pci.h
@@ -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
diff --git a/tests/libqos/ppc64_pseries-machine.c b/tests/libqos/ppc64_pseries-machine.c
new file mode 100644
index 0000000000..2f3640010d
--- /dev/null
+++ b/tests/libqos/ppc64_pseries-machine.c
@@ -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);
+
diff --git a/tests/libqos/qgraph.c b/tests/libqos/qgraph.c
new file mode 100644
index 0000000000..122efc1b7b
--- /dev/null
+++ b/tests/libqos/qgraph.c
@@ -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;
+ }
+}
diff --git a/tests/libqos/qgraph.h b/tests/libqos/qgraph.h
new file mode 100644
index 0000000000..ef0c73837a
--- /dev/null
+++ b/tests/libqos/qgraph.h
@@ -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
diff --git a/tests/libqos/qgraph_internal.h b/tests/libqos/qgraph_internal.h
new file mode 100644
index 0000000000..2ef748baf6
--- /dev/null
+++ b/tests/libqos/qgraph_internal.h
@@ -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
diff --git a/tests/libqos/sdhci.c b/tests/libqos/sdhci.c
new file mode 100644
index 0000000000..22c33de453
--- /dev/null
+++ b/tests/libqos/sdhci.c
@@ -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);
diff --git a/tests/libqos/sdhci.h b/tests/libqos/sdhci.h
new file mode 100644
index 0000000000..032d815c38
--- /dev/null
+++ b/tests/libqos/sdhci.h
@@ -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
diff --git a/tests/libqos/tpci200.c b/tests/libqos/tpci200.c
new file mode 100644
index 0000000000..98dc532933
--- /dev/null
+++ b/tests/libqos/tpci200.c
@@ -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);
diff --git a/tests/libqos/virtio-9p.c b/tests/libqos/virtio-9p.c
new file mode 100644
index 0000000000..a378b56fa9
--- /dev/null
+++ b/tests/libqos/virtio-9p.c
@@ -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);
diff --git a/tests/libqos/virtio-9p.h b/tests/libqos/virtio-9p.h
new file mode 100644
index 0000000000..dba22772b5
--- /dev/null
+++ b/tests/libqos/virtio-9p.h
@@ -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;
+};
diff --git a/tests/libqos/virtio-balloon.c b/tests/libqos/virtio-balloon.c
new file mode 100644
index 0000000000..7e6e9e9de5
--- /dev/null
+++ b/tests/libqos/virtio-balloon.c
@@ -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);
diff --git a/tests/libqos/virtio-balloon.h b/tests/libqos/virtio-balloon.h
new file mode 100644
index 0000000000..e8066c42bb
--- /dev/null
+++ b/tests/libqos/virtio-balloon.h
@@ -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;
+};
diff --git a/tests/libqos/virtio-blk.c b/tests/libqos/virtio-blk.c
new file mode 100644
index 0000000000..c17bdf4217
--- /dev/null
+++ b/tests/libqos/virtio-blk.c
@@ -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);
diff --git a/tests/libqos/virtio-blk.h b/tests/libqos/virtio-blk.h
new file mode 100644
index 0000000000..dc258496ba
--- /dev/null
+++ b/tests/libqos/virtio-blk.h
@@ -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;
+};
diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c
index 7aa8383338..3678c07ef0 100644
--- a/tests/libqos/virtio-mmio.c
+++ b/tests/libqos/virtio-mmio.c
@@ -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();
+}
+
+static void qvirtio_mmio_start_hw(QOSGraphObject *obj)
+{
+ QVirtioMMIODevice *dev = (QVirtioMMIODevice *) obj;
+ qvirtio_start_device(&dev->vdev);
+}
- magic = readl(addr + QVIRTIO_MMIO_MAGIC_VALUE);
+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);
+
+ dev->obj.get_driver = qvirtio_mmio_get_driver;
+ dev->obj.start_hw = qvirtio_mmio_start_hw;
+}
- return dev;
+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);
diff --git a/tests/libqos/virtio-mmio.h b/tests/libqos/virtio-mmio.h
index e3e52b9ce1..17a17141c3 100644
--- a/tests/libqos/virtio-mmio.h
+++ b/tests/libqos/virtio-mmio.h
@@ -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
diff --git a/tests/libqos/virtio-net.c b/tests/libqos/virtio-net.c
new file mode 100644
index 0000000000..61c56170e9
--- /dev/null
+++ b/tests/libqos/virtio-net.c
@@ -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);
diff --git a/tests/libqos/virtio-net.h b/tests/libqos/virtio-net.h
new file mode 100644
index 0000000000..28238a1b20
--- /dev/null
+++ b/tests/libqos/virtio-net.h
@@ -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;
+};
diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c
index 550dede0a2..993d347830 100644
--- a/tests/libqos/virtio-pci.c
+++ b/tests/libqos/virtio-pci.c
@@ -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;
-
-void qvirtio_pci_device_free(QVirtioPCIDevice *dev)
-{
- g_free(dev->pdev);
- g_free(dev);
-}
-
-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;
-}
+/* 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.
+ */
-static void qvirtio_pci_foreach_callback(
- QPCIDevice *dev, int devfn, void *data)
+static inline bool qvirtio_pci_is_big_endian(QVirtioPCIDevice *dev)
{
- 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);
- }
-}
+ QPCIBus *bus = dev->pdev->bus;
-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;
+}
diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h
index 6ef19094cb..728b4715f1 100644
--- a/tests/libqos/virtio-pci.h
+++ b/tests/libqos/virtio-pci.h
@@ -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);
diff --git a/tests/libqos/virtio-rng.c b/tests/libqos/virtio-rng.c
new file mode 100644
index 0000000000..a1d2c7671c
--- /dev/null
+++ b/tests/libqos/virtio-rng.c
@@ -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);
diff --git a/tests/libqos/virtio-rng.h b/tests/libqos/virtio-rng.h
new file mode 100644
index 0000000000..fbba988875
--- /dev/null
+++ b/tests/libqos/virtio-rng.h
@@ -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;
+};
diff --git a/tests/libqos/virtio-scsi.c b/tests/libqos/virtio-scsi.c
new file mode 100644
index 0000000000..482684d0bc
--- /dev/null
+++ b/tests/libqos/virtio-scsi.c
@@ -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);
diff --git a/tests/libqos/virtio-scsi.h b/tests/libqos/virtio-scsi.h
new file mode 100644
index 0000000000..17a47beddc
--- /dev/null
+++ b/tests/libqos/virtio-scsi.h
@@ -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;
+};
diff --git a/tests/libqos/virtio-serial.c b/tests/libqos/virtio-serial.c
new file mode 100644
index 0000000000..91cedefb8d
--- /dev/null
+++ b/tests/libqos/virtio-serial.c
@@ -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);
diff --git a/tests/libqos/virtio-serial.h b/tests/libqos/virtio-serial.h
new file mode 100644
index 0000000000..b7e2a5d178
--- /dev/null
+++ b/tests/libqos/virtio-serial.h
@@ -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;
+};
diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c
index 0dad5c19ac..5e8f39b4d3 100644
--- a/tests/libqos/virtio.c
+++ b/tests/libqos/virtio.c
@@ -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;
}
diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h
index 69b5b13840..51d2359ace 100644
--- a/tests/libqos/virtio.h
+++ b/tests/libqos/virtio.h
@@ -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
diff --git a/tests/libqos/x86_64_pc-machine.c b/tests/libqos/x86_64_pc-machine.c
new file mode 100644
index 0000000000..8bd0360ba9
--- /dev/null
+++ b/tests/libqos/x86_64_pc-machine.c
@@ -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);
diff --git a/tests/libqtest.h b/tests/libqtest.h
index 5937f91912..a16acd58a6 100644
--- a/tests/libqtest.h
+++ b/tests/libqtest.h
@@ -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;
}
diff --git a/tests/megasas-test.c b/tests/megasas-test.c
index 81837e14af..33aa97042c 100644
--- a/tests/megasas-test.c
+++ b/tests/megasas-test.c
@@ -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";
-
- if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
- qs = qtest_pc_boot(cmd, extra_opts ? : "");
- global_qtest = qs->qts;
- return qs;
+ QMegasas *megasas = obj;
+
+ 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",
+ };
+
+ 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");
- return g_test_run();
+ qos_add_test("dcmd/pd-get-info/fuzz", "megasas", megasas_pd_get_info_fuzz, NULL);
}
+libqos_init(megasas_register_nodes);
diff --git a/tests/ne2000-test.c b/tests/ne2000-test.c
index b7cf3dd2f5..097c2eec6c 100644
--- a/tests/ne2000-test.c
+++ b/tests/ne2000-test.c
@@ -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();
+ return &ne2k_pci->obj;
+}
- qtest_end();
+static void ne2000_register_nodes(void)
+{
+ QOSGraphEdgeOptions opts = {
+ .extra_device_opts = "addr=04.0",
+ };
+ add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
- return ret;
+ 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);
diff --git a/tests/nvme-test.c b/tests/nvme-test.c
index 2700ba838a..b48d3a24b9 100644
--- a/tests/nvme-test.c
+++ b/tests/nvme-test.c
@@ -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";
-
- if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
- qs = qtest_pc_boot(cmd, extra_opts ? : "");
- global_qtest = qs->qts;
- return qs;
+ QNvme *nvme = obj;
+
+ 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;
-static void nop(void)
-{
- QOSState *qs;
+ qpci_device_init(&nvme->dev, bus, addr);
+ nvme->obj.get_driver = nvme_get_driver;
- qs = qnvme_start(NULL);
- qnvme_stop(qs);
+ return &nvme->obj;
}
-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",
+ };
+
+ add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
- return g_test_run();
+ 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);
diff --git a/tests/pci-test.c b/tests/pci-test.c
new file mode 100644
index 0000000000..ff80985093
--- /dev/null
+++ b/tests/pci-test.c
@@ -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);
diff --git a/tests/pcnet-test.c b/tests/pcnet-test.c
index efb1ef44e9..484448cc64 100644
--- a/tests/pcnet-test.c
+++ b/tests/pcnet-test.c
@@ -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();
+ return &pcnet->obj;
+}
- qtest_end();
+static void pcnet_register_nodes(void)
+{
+ QOSGraphEdgeOptions opts = {
+ .extra_device_opts = "addr=04.0",
+ };
+ add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
- return ret;
+ 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);
diff --git a/tests/q35-test.c b/tests/q35-test.c
index 7ea7acc9d8..34b34bc2b9 100644
--- a/tests/q35-test.c
+++ b/tests/q35-test.c
@@ -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);
diff --git a/tests/qos-test.c b/tests/qos-test.c
new file mode 100644
index 0000000000..6b1145eccc
--- /dev/null
+++ b/tests/qos-test.c
@@ -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;
+}
diff --git a/tests/rtas-test.c b/tests/rtas-test.c
index 009bda6d23..ee888676ed 100644
--- a/tests/rtas-test.c
+++ b/tests/rtas-test.c
@@ -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 */
diff --git a/tests/rtl8139-test.c b/tests/rtl8139-test.c
index 49e3f03d98..d6d0c24909 100644
--- a/tests/rtl8139-test.c
+++ b/tests/rtl8139-test.c
@@ -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);
diff --git a/tests/sdhci-test.c b/tests/sdhci-test.c
index 982f5ebbb2..2f177e569f 100644
--- a/tests/sdhci-test.c
+++ b/tests/sdhci-test.c
@@ -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)
-{
- QSDHCI *s = g_new0(QSDHCI, 1);
-
- 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)
+static void test_registers(void *obj, void *data, QGuestAllocator *alloc)
{
- const struct sdhci_t *test = data;
- QSDHCI *s;
+ QSDHCI *s = obj;
- 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);
diff --git a/tests/spapr-phb-test.c b/tests/spapr-phb-test.c
index d3522ea093..39b5766710 100644
--- a/tests/spapr-phb-test.c
+++ b/tests/spapr-phb-test.c
@@ -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 "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);
diff --git a/tests/tco-test.c b/tests/tco-test.c
index 6bee9a37d3..f89a42cdcc 100644
--- a/tests/tco-test.c
+++ b/tests/tco-test.c
@@ -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);
diff --git a/tests/test-char.c b/tests/test-char.c
index 63b4d3289d..de328380c1 100644
--- a/tests/test-char.c
+++ b/tests/test-char.c
@@ -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);
diff --git a/tests/test-qgraph.c b/tests/test-qgraph.c
new file mode 100644
index 0000000000..f6a6565e31
--- /dev/null
+++ b/tests/test-qgraph.c
@@ -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;
+}
diff --git a/tests/tpci200-test.c b/tests/tpci200-test.c
deleted file mode 100644
index 0321ec27ec..0000000000
--- a/tests/tpci200-test.c
+++ /dev/null
@@ -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;
-}
diff --git a/tests/usb-hcd-ehci-test.c b/tests/usb-hcd-ehci-test.c
index f28ea27f37..8bc3e44189 100644
--- a/tests/usb-hcd-ehci-test.c
+++ b/tests/usb-hcd-ehci-test.c
@@ -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);
diff --git a/tests/usb-hcd-ohci-test.c b/tests/usb-hcd-ohci-test.c
index 48ddbfd26d..98af02e898 100644
--- a/tests/usb-hcd-ohci-test.c
+++ b/tests/usb-hcd-ohci-test.c
@@ -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 *obj, void *data, QGuestAllocator *alloc)
+{
+ usb_test_hotplug("ohci", "1", NULL);
}
-static void test_ohci_hotplug(void)
+static void *ohci_pci_get_driver(void *obj, const char *interface)
{
- usb_test_hotplug("ohci", "1", NULL);
+ QOHCI_PCI *ohci_pci = obj;
+
+ if (!g_strcmp0(interface, "pci-device")) {
+ return &ohci_pci->dev;
+ }
+
+ fprintf(stderr, "%s not present in pci-ohci\n", interface);
+ g_assert_not_reached();
}
-int main(int argc, char **argv)
+static void *ohci_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
{
- int ret;
+ QOHCI_PCI *ohci_pci = g_new0(QOHCI_PCI, 1);
+ ohci_pci->obj.get_driver = ohci_pci_get_driver;
- g_test_init(&argc, &argv, NULL);
+ 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) });
- qtest_add_func("/ohci/pci/init", test_ohci_init);
- qtest_add_func("/ohci/pci/hotplug", test_ohci_hotplug);
+ qos_node_create_driver("pci-ohci", ohci_pci_create);
+ qos_node_consumes("pci-ohci", "pci-bus", &opts);
+ qos_node_produces("pci-ohci", "pci-device");
+}
- qtest_start("-device pci-ohci,id=ohci");
- ret = g_test_run();
- qtest_end();
+libqos_init(ohci_pci_register_nodes);
- return ret;
+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);
diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c
index 14a1e2fbac..0c965b3b1e 100644
--- a/tests/vhost-user-test.c
+++ b/tests/vhost-user-test.c
@@ -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,66 +166,28 @@ 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;
- }
-
- 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);
- } 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);
- }
+ 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 init_virtio_dev(QTestState *qts, TestServer *s, uint32_t features_mask)
+static void append_mem_opts(TestServer *server, GString *cmd_line,
+ int size, enum test_memfd memfd)
{
- 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);
+ if (memfd == TEST_MEMFD_AUTO) {
+ memfd = qemu_memfd_check(0) ? TEST_MEMFD_YES : TEST_MEMFD_NO;
}
- 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;
+ if (memfd == TEST_MEMFD_YES) {
+ g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size);
+ } else {
+ const char *root = init_hugepagefs() ? : server->tmpfs;
- for (i = 0; i < s->queues * 2; i++) {
- qvirtqueue_cleanup(s->dev->vdev.bus, s->vq[i], s->alloc);
+ g_string_append_printf(cmd_line, QEMU_CMD_MEM, size, size, root);
}
- pc_alloc_uninit(s->alloc);
-
- qvirtio_pci_device_free(s->dev);
}
static bool wait_for_fds(TestServer *s)
@@ -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", 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", path, strerror(errno));
- abort();
+ g_test_fail();
return NULL;
}
if (fs.f_type != HUGETLBFS_MAGIC) {
g_test_message("Warning: path not on HugeTLBFS: %s", 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);
@@ -601,8 +562,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);
@@ -698,70 +657,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)
+{
+ TestServer *server = s;
+
+ qos_invalidate_command_line();
+ test_server_free(server);
+}
+
+static void *vhost_user_test_setup(GString *cmd_line, void *arg)
{
- enum test_memfd memfd = GPOINTER_TO_INT(arg);
- TestServer *server = NULL;
- char *qemu_cmd = NULL;
- QTestState *s = NULL;
+ 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;
+}
- server = test_server_new(memfd == TEST_MEMFD_YES ?
- "read-guest-memfd" : "read-guest-mem");
+static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg)
+{
+ TestServer *server = test_server_new("vhost-user-test");
test_server_listen(server);
- qemu_cmd = get_qemu_cmd(server, 512, memfd, "", "");
+ 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);
- s = qtest_start(qemu_cmd);
- g_free(qemu_cmd);
+ return server;
+}
- init_virtio_dev(global_qtest, server, 1u << VIRTIO_NET_F_MAC);
+static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc)
+{
+ TestServer *server = arg;
if (!wait_for_fds(server)) {
- goto exit;
+ return;
}
read_guest_mem_server(global_qtest, server);
-
-exit:
- uninit_virtio_dev(server);
-
- qtest_quit(s);
- test_server_free(server);
}
-static void test_migrate(void)
+static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc)
{
- TestServer *s = test_server_new("src");
+ 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));
+
+ 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);
- 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);
+ /* 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));
@@ -802,18 +770,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);
}
@@ -861,20 +822,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);
@@ -888,159 +855,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);
-
- init_virtio_dev(global_qtest, s, 1u << VIRTIO_NET_F_MAC);
- if (!wait_for_fds(s)) {
- goto exit;
- }
- wait_for_rings_started(s, 2);
-exit:
- uninit_virtio_dev(s);
+ g_thread_new("connect", connect_thread, s);
+ append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
+ append_vhost_opts(s, cmd_line, ",server");
- qtest_end();
- test_server_free(s);
-}
+ g_test_queue_destroy(vhost_user_test_cleanup, 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);
+ return s;
}
-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);
- if (!wait_for_fds(s)) {
- goto exit;
- }
- wait_for_rings_started(s, 2);
+ g_thread_new("connect", connect_thread, s);
+ append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO);
+ append_vhost_opts(s, cmd_line, ",server");
-exit:
- uninit_virtio_dev(s);
+ g_test_queue_destroy(vhost_user_test_cleanup, s);
- qtest_end();
- test_server_free(s);
+ return s;
}
-static void test_flags_mismatch(void)
+static void test_vhost_user_started(void *obj, void *arg, QGuestAllocator *alloc)
{
- 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 = arg;
+ if (!wait_for_fds(s)) {
+ return;
+ }
+ wait_for_rings_started(s, 2);
+}
-static void test_multiqueue(void)
+static void *vhost_user_test_setup_multiqueue(GString *cmd_line, void *arg)
{
- 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));
+ TestServer *s = vhost_user_test_setup(cmd_line, arg);
+
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);
diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c
index a2b31085f6..16107ad280 100644
--- a/tests/virtio-9p-test.c
+++ b/tests/virtio-9p-test.c
@@ -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)
-{
- 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)
+static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
{
- 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);
-}
-
-static void v9fs_qtest_pci_add(const char *path, v9fs_test_fn fn)
-{
- qtest_add_data_func(path, fn, v9fs_run_pci_test);
+ 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);
}
-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);
diff --git a/tests/virtio-balloon-test.c b/tests/virtio-balloon-test.c
deleted file mode 100644
index 5a1d0ccbb7..0000000000
--- a/tests/virtio-balloon-test.c
+++ /dev/null
@@ -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;
-}
diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c
index 8d2fc9c710..b02be0274e 100644
--- a/tests/virtio-blk-test.c
+++ b/tests/virtio-blk-test.c
@@ -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;
-
- 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);
+ 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);
- /* 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();
-
- dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT);
- qpci_msix_enable(dev->pdev);
+ if (qpci_check_buggy_msi(pci_dev)) {
+ return;
+ }
- qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0);
+ qpci_msix_enable(pdev->pdev);
+ qvirtio_pci_set_msix_configuration_vector(pdev, t_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();
-
- dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT);
- qpci_msix_enable(dev->pdev);
+ if (qpci_check_buggy_msi(pci_dev)) {
+ return;
+ }
- qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0);
+ qpci_msix_enable(pdev->pdev);
+ qvirtio_pci_set_msix_configuration_vector(pdev, t_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();
-
- 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);
+ vq = qvirtqueue_setup(dev, t_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();
-
- g_test_init(&argc, &argv, NULL);
-
- 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);
- }
+ char *tmp_path = drive_create();
- return g_test_run();
+ 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);
+
+ 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);
diff --git a/tests/virtio-console-test.c b/tests/virtio-console-test.c
deleted file mode 100644
index a7c6f167c3..0000000000
--- a/tests/virtio-console-test.c
+++ /dev/null
@@ -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();
-}
diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c
index e9783e6707..c58e670e2f 100644
--- a/tests/virtio-net-test.c
+++ b/tests/virtio-net-test.c
@@ -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;
+#endif
- ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);
- g_assert_cmpint(ret, !=, -1);
+static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc)
+{
+ const char *arch = qtest_get_arch();
- 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);
+ qtest_qmp_device_add("virtio-net-pci", "net1",
+ "{'addr': %s}", stringify(PCI_SLOT_HP));
- rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0);
- tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 1);
+ if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
+ qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP);
+ }
+}
- driver_init(&dev->vdev);
- func(&dev->vdev, qs->alloc, &rx->vq, &tx->vq, sv[0]);
+static void virtio_net_test_cleanup(void *sockets)
+{
+ int *sv = sockets;
- /* 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);
+ 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(gconstpointer data)
+static void large_tx(void *obj, void *data, QGuestAllocator *t_alloc)
{
- QVirtioPCIDevice *dev;
- QOSState *qs;
- QVirtQueuePCI *tx, *rx;
- QVirtQueue *vq;
+ 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;
- 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);
+ 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);
+ qvirtqueue_kick(dev->vdev, vq, free_head);
- qvirtio_wait_used_elem(&dev->vdev, vq, free_head, NULL,
+ 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);
+ guest_free(t_alloc, req_addr);
}
-#endif
-static void hotplug(void)
+static void *virtio_net_test_setup_nosocket(GString *cmd_line, void *arg)
{
- 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();
+ g_string_append(cmd_line, " -netdev hubport,hubid=0,id=hs0 ");
+ return arg;
}
-int main(int argc, char **argv)
+static void register_virtio_net_test(void)
{
- g_test_init(&argc, &argv, NULL);
+ QOSGraphTestOptions opts = {
+ .before = virtio_net_test_setup,
+ };
+
+ qos_add_test("hotplug", "virtio-pci", hotplug, &opts);
#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);
+ qos_add_test("basic", "virtio-net", send_recv_test, &opts);
+ qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts);
#endif
- qtest_add_func("/virtio/net/pci/hotplug", hotplug);
- return g_test_run();
+ /* 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);
diff --git a/tests/virtio-rng-test.c b/tests/virtio-rng-test.c
index 657d9a4105..5309c7c8ab 100644
--- a/tests/virtio-rng-test.c
+++ b/tests/virtio-rng-test.c
@@ -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);
diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c
index 0d4f25d369..162b31c88d 100644
--- a/tests/virtio-scsi-test.c
+++ b/tests/virtio-scsi-test.c
@@ -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";
-
- 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 QGuestAllocator *alloc;
-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)
-{
- QOSState *qs;
-
- qs = qvirtio_scsi_start(NULL);
- qvirtio_scsi_stop(qs);
-}
-
-static void hotplug(void)
+static void hotplug(void *obj, void *data, QGuestAllocator *alloc)
{
- 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);
+ g_string_append(cmd_line,
+ " -drive id=drv1,if=none,file=null-co://,format=raw");
+ return arg;
+}
- return g_test_run();
+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);
diff --git a/tests/virtio-serial-test.c b/tests/virtio-serial-test.c
index 8da9980a24..85f35e09b7 100644
--- a/tests/virtio-serial-test.c
+++ b/tests/virtio-serial-test.c
@@ -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;
-
- g_test_init(&argc, &argv, NULL);
- qtest_add_func("/virtio/serial/nop", virtio_serial_nop);
- qtest_add_func("/virtio/serial/hotplug", hotplug);
+ QOSGraphTestOptions opts = { };
- global_qtest = qtest_initf("-device virtio-serial-%s",
- qvirtio_get_dev_type());
- ret = g_test_run();
+ opts.edge.before_cmd_line = "-device virtconsole,bus=vser0.0";
+ qos_add_test("console-nop", "virtio-serial", virtio_serial_nop, &opts);
- qtest_end();
+ opts.edge.before_cmd_line = "-device virtserialport,bus=vser0.0";
+ qos_add_test("serialport-nop", "virtio-serial", virtio_serial_nop, &opts);
- return ret;
+ qos_add_test("hotplug", "virtio-serial", serial_hotplug, NULL);
}
+libqos_init(register_virtio_serial_test);
diff --git a/tests/virtio-test.c b/tests/virtio-test.c
new file mode 100644
index 0000000000..804e5371dc
--- /dev/null
+++ b/tests/virtio-test.c
@@ -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);
diff --git a/tests/vmxnet3-test.c b/tests/vmxnet3-test.c
index 159c0ad728..35cdea939b 100644
--- a/tests/vmxnet3-test.c
+++ b/tests/vmxnet3-test.c
@@ -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();
+ return &vmxnet3->obj;
+}
- qtest_end();
+static void vmxnet3_register_nodes(void)
+{
+ QOSGraphEdgeOptions opts = {
+ .extra_device_opts = "addr=04.0",
+ };
+ add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
- return ret;
+ 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);
diff --git a/util/Makefile.objs b/util/Makefile.objs
index 0808575e3e..835fcd69e2 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -3,6 +3,7 @@ util-obj-y += bufferiszero.o
util-obj-y += lockcnt.o
util-obj-y += aiocb.o async.o aio-wait.o thread-pool.o qemu-timer.o
util-obj-y += main-loop.o iohandler.o
+main-loop.o-cflags := $(SLIRP_CFLAGS)
util-obj-$(call lnot,$(CONFIG_ATOMIC64)) += atomic64.o
util-obj-$(CONFIG_POSIX) += aio-posix.o
util-obj-$(CONFIG_POSIX) += compatfd.o
diff --git a/util/main-loop.c b/util/main-loop.c
index d4a521caeb..e1e349ca5c 100644
--- a/util/main-loop.c
+++ b/util/main-loop.c
@@ -26,11 +26,9 @@
#include "qapi/error.h"
#include "qemu/cutils.h"
#include "qemu/timer.h"
-#include "qemu/sockets.h" // struct in_addr needed for libslirp.h
#include "sysemu/qtest.h"
#include "sysemu/cpus.h"
#include "sysemu/replay.h"
-#include "slirp/libslirp.h"
#include "qemu/main-loop.h"
#include "block/aio.h"
#include "qemu/error-report.h"
diff --git a/vl.c b/vl.c
index 4c5cc0d8ad..4a350de5cd 100644
--- a/vl.c
+++ b/vl.c
@@ -106,9 +106,6 @@ int main(int argc, char **argv)
#include "disas/disas.h"
-
-#include "slirp/libslirp.h"
-
#include "trace-root.h"
#include "trace/control.h"
#include "qemu/queue.h"