aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.d/buildtest.yml23
-rw-r--r--.gitlab-ci.d/cirrus.yml35
-rw-r--r--.gitlab-ci.d/cirrus/kvm-build.yml31
-rw-r--r--.gitlab-ci.d/qemu-project.yml1
-rw-r--r--.gitlab-ci.d/windows.yml98
-rw-r--r--MAINTAINERS12
-rw-r--r--VERSION2
-rw-r--r--block/nvme.c5
-rw-r--r--blockdev.c10
-rwxr-xr-xconfigure27
-rw-r--r--docs/system/arm/aspeed.rst26
-rw-r--r--dump/dump.c10
-rw-r--r--hw/arm/Kconfig1
-rw-r--r--hw/arm/aspeed.c21
-rw-r--r--hw/arm/boot.c1
-rw-r--r--hw/arm/cubieboard.c2
-rw-r--r--hw/arm/digic_boards.c1
-rw-r--r--hw/arm/highbank.c1
-rw-r--r--hw/arm/imx25_pdk.c2
-rw-r--r--hw/arm/integratorcp.c2
-rw-r--r--hw/arm/mcimx6ul-evk.c2
-rw-r--r--hw/arm/mcimx7d-sabre.c2
-rw-r--r--hw/arm/msf2-som.c2
-rw-r--r--hw/arm/npcm7xx_boards.c7
-rw-r--r--hw/arm/orangepi.c2
-rw-r--r--hw/arm/raspi.c2
-rw-r--r--hw/arm/realview.c2
-rw-r--r--hw/arm/sabrelite.c2
-rw-r--r--hw/arm/sbsa-ref.c1
-rw-r--r--hw/arm/stellaris.c15
-rw-r--r--hw/arm/stm32f405_soc.c1
-rw-r--r--hw/arm/versatilepb.c4
-rw-r--r--hw/arm/vexpress.c7
-rw-r--r--hw/arm/virt-acpi-build.c7
-rw-r--r--hw/arm/virt.c21
-rw-r--r--hw/arm/xilinx_zynq.c16
-rw-r--r--hw/arm/xlnx-versal-virt.c3
-rw-r--r--hw/arm/xlnx-zcu102.c6
-rw-r--r--hw/block/dataplane/virtio-blk.c2
-rw-r--r--hw/block/fdc.c23
-rw-r--r--hw/char/stm32f2xx_usart.c3
-rw-r--r--hw/display/vga-isa.c10
-rw-r--r--hw/intc/Kconfig5
-rw-r--r--hw/intc/arm_gicv3.c2
-rw-r--r--hw/intc/arm_gicv3_cpuif.c13
-rw-r--r--hw/intc/arm_gicv3_cpuif_common.c22
-rw-r--r--hw/intc/arm_gicv3_its.c39
-rw-r--r--hw/intc/meson.build11
-rw-r--r--hw/microblaze/petalogix_ml605_mmu.c2
-rw-r--r--hw/mips/bootloader.c6
-rw-r--r--hw/mips/boston.c5
-rw-r--r--hw/misc/sifive_u_otp.c4
-rw-r--r--hw/net/npcm7xx_emc.c18
-rw-r--r--hw/riscv/microchip_pfsoc.c2
-rw-r--r--hw/riscv/sifive_u.c15
-rw-r--r--hw/sd/ssi-sd.c29
-rw-r--r--hw/sparc64/niagara.c2
-rw-r--r--hw/virtio/trace-events3
-rw-r--r--hw/virtio/virtio-iommu-pci.c12
-rw-r--r--hw/virtio/virtio-iommu.c42
-rw-r--r--include/hw/i386/microvm.h1
-rw-r--r--include/hw/i386/x86.h1
-rw-r--r--include/hw/pci/pci_bridge.h8
-rw-r--r--include/migration/colo.h1
-rw-r--r--include/sysemu/blockdev.h1
-rw-r--r--linux-user/aarch64/cpu_loop.c46
-rw-r--r--linux-user/hexagon/cpu_loop.c1
-rw-r--r--meson.build13
-rw-r--r--meson_options.txt2
-rw-r--r--migration/colo.c33
-rw-r--r--migration/migration.c26
-rw-r--r--migration/multifd-zlib.c48
-rw-r--r--migration/multifd-zstd.c47
-rw-r--r--migration/multifd.c70
-rw-r--r--migration/multifd.h6
-rw-r--r--migration/ram.c11
-rw-r--r--migration/savevm.c5
-rw-r--r--pc-bios/bios-256k.binbin262144 -> 262144 bytes
-rw-r--r--pc-bios/bios-microvm.binbin131072 -> 131072 bytes
-rw-r--r--pc-bios/bios.binbin131072 -> 131072 bytes
-rw-r--r--pc-bios/vgabios-ati.binbin39424 -> 39424 bytes
-rw-r--r--pc-bios/vgabios-bochs-display.binbin28672 -> 28672 bytes
-rw-r--r--pc-bios/vgabios-cirrus.binbin39424 -> 39424 bytes
-rw-r--r--pc-bios/vgabios-qxl.binbin39424 -> 39424 bytes
-rw-r--r--pc-bios/vgabios-ramfb.binbin28672 -> 28672 bytes
-rw-r--r--pc-bios/vgabios-stdvga.binbin39424 -> 39424 bytes
-rw-r--r--pc-bios/vgabios-virtio.binbin39424 -> 39424 bytes
-rw-r--r--pc-bios/vgabios-vmware.binbin39424 -> 39424 bytes
-rw-r--r--pc-bios/vgabios.binbin38912 -> 38912 bytes
m---------roms/seabios0
-rw-r--r--scripts/meson-buildoptions.sh3
-rw-r--r--target/arm/debug_helper.c23
-rw-r--r--target/arm/gdbstub.c9
-rw-r--r--target/arm/helper.c6
-rw-r--r--target/arm/helper.h1
-rw-r--r--target/arm/machine.c10
-rw-r--r--target/arm/syndrome.h5
-rw-r--r--target/arm/tlb_helper.c63
-rw-r--r--target/arm/translate-a64.c23
-rw-r--r--target/arm/translate.c58
-rw-r--r--target/hexagon/cpu.h1
-rw-r--r--target/i386/tcg/translate.c12
-rw-r--r--target/rx/cpu.h1
-rw-r--r--tcg/arm/tcg-target.c.inc13
-rw-r--r--tcg/tcg.c8
-rw-r--r--tests/data/acpi/q35/DSDT.viotbin0 -> 9398 bytes
-rw-r--r--tests/data/acpi/q35/VIOT.viotbin0 -> 112 bytes
-rw-r--r--tests/data/acpi/virt/VIOTbin0 -> 88 bytes
-rw-r--r--tests/qtest/bios-tables-test.c39
-rw-r--r--tests/qtest/boot-serial-test.c3
-rw-r--r--tests/qtest/cdrom-test.c8
-rw-r--r--tests/qtest/fdc-test.c38
-rw-r--r--tests/qtest/libqos/libqtest.h8
-rw-r--r--tests/qtest/libqos/meson.build1
-rw-r--r--tests/qtest/libqos/pci.c119
-rw-r--r--tests/qtest/libqos/pci.h1
-rw-r--r--tests/qtest/libqos/virtio-iommu.c126
-rw-r--r--tests/qtest/libqos/virtio-iommu.h40
-rw-r--r--tests/qtest/libqtest.c79
-rw-r--r--tests/qtest/meson.build9
-rw-r--r--tests/qtest/prom-env-test.c8
-rw-r--r--tests/qtest/virtio-iommu-test.c326
-rw-r--r--tests/qtest/virtio-net-failover.c1352
-rw-r--r--tests/tcg/aarch64/Makefile.target4
-rw-r--r--tests/tcg/aarch64/pcalign-a64.c37
-rw-r--r--tests/tcg/arm/Makefile.target4
-rw-r--r--tests/tcg/arm/pcalign-a32.c46
127 files changed, 3024 insertions, 463 deletions
diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml
index 71d0f407ad..7e1cb0b3c2 100644
--- a/.gitlab-ci.d/buildtest.yml
+++ b/.gitlab-ci.d/buildtest.yml
@@ -100,6 +100,17 @@ avocado-system-debian:
IMAGE: debian-amd64
MAKE_CHECK_ARGS: check-avocado
+crash-test-debian:
+ extends: .native_test_job_template
+ needs:
+ - job: build-system-debian
+ artifacts: true
+ variables:
+ IMAGE: debian-amd64
+ script:
+ - cd build
+ - scripts/device-crash-test -q ./qemu-system-i386
+
build-system-fedora:
extends: .native_build_job_template
needs:
@@ -134,6 +145,18 @@ avocado-system-fedora:
IMAGE: fedora
MAKE_CHECK_ARGS: check-avocado
+crash-test-fedora:
+ extends: .native_test_job_template
+ needs:
+ - job: build-system-fedora
+ artifacts: true
+ variables:
+ IMAGE: fedora
+ script:
+ - cd build
+ - scripts/device-crash-test -q ./qemu-system-ppc
+ - scripts/device-crash-test -q ./qemu-system-riscv32
+
build-system-centos:
extends: .native_build_job_template
needs:
diff --git a/.gitlab-ci.d/cirrus.yml b/.gitlab-ci.d/cirrus.yml
index d273a9e713..19e6c21401 100644
--- a/.gitlab-ci.d/cirrus.yml
+++ b/.gitlab-ci.d/cirrus.yml
@@ -89,3 +89,38 @@ x64-macos-11-base-build:
PATH_EXTRA: /usr/local/opt/ccache/libexec:/usr/local/opt/gettext/bin
PKG_CONFIG_PATH: /usr/local/opt/curl/lib/pkgconfig:/usr/local/opt/ncurses/lib/pkgconfig:/usr/local/opt/readline/lib/pkgconfig
TEST_TARGETS: check-unit check-block check-qapi-schema check-softfloat check-qtest-x86_64
+
+
+# The following jobs run VM-based tests via KVM on a Linux-based Cirrus-CI job
+.cirrus_kvm_job:
+ stage: build
+ image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:master
+ needs: []
+ timeout: 80m
+ allow_failure: true
+ script:
+ - sed -e "s|[@]CI_REPOSITORY_URL@|$CI_REPOSITORY_URL|g"
+ -e "s|[@]CI_COMMIT_REF_NAME@|$CI_COMMIT_REF_NAME|g"
+ -e "s|[@]CI_COMMIT_SHA@|$CI_COMMIT_SHA|g"
+ -e "s|[@]NAME@|$NAME|g"
+ -e "s|[@]CONFIGURE_ARGS@|$CONFIGURE_ARGS|g"
+ -e "s|[@]TEST_TARGETS@|$TEST_TARGETS|g"
+ <.gitlab-ci.d/cirrus/kvm-build.yml >.gitlab-ci.d/cirrus/$NAME.yml
+ - cat .gitlab-ci.d/cirrus/$NAME.yml
+ - cirrus-run -v --show-build-log always .gitlab-ci.d/cirrus/$NAME.yml
+ rules:
+ - when: manual
+
+x86-netbsd:
+ extends: .cirrus_kvm_job
+ variables:
+ NAME: netbsd
+ CONFIGURE_ARGS: --target-list=x86_64-softmmu,ppc64-softmmu,aarch64-softmmu
+ TEST_TARGETS: check
+
+x86-openbsd:
+ extends: .cirrus_kvm_job
+ variables:
+ NAME: openbsd
+ CONFIGURE_ARGS: --target-list=i386-softmmu,riscv64-softmmu,mips64-softmmu
+ TEST_TARGETS: check
diff --git a/.gitlab-ci.d/cirrus/kvm-build.yml b/.gitlab-ci.d/cirrus/kvm-build.yml
new file mode 100644
index 0000000000..4334fabf39
--- /dev/null
+++ b/.gitlab-ci.d/cirrus/kvm-build.yml
@@ -0,0 +1,31 @@
+container:
+ image: fedora:35
+ cpu: 4
+ memory: 8Gb
+ kvm: true
+
+env:
+ CIRRUS_CLONE_DEPTH: 1
+ CI_REPOSITORY_URL: "@CI_REPOSITORY_URL@"
+ CI_COMMIT_REF_NAME: "@CI_COMMIT_REF_NAME@"
+ CI_COMMIT_SHA: "@CI_COMMIT_SHA@"
+
+@NAME@_task:
+ @NAME@_vm_cache:
+ folder: $HOME/.cache/qemu-vm
+ install_script:
+ - dnf update -y
+ - dnf install -y git make openssh-clients qemu-img qemu-system-x86 wget
+ clone_script:
+ - git clone --depth 100 "$CI_REPOSITORY_URL" .
+ - git fetch origin "$CI_COMMIT_REF_NAME"
+ - git reset --hard "$CI_COMMIT_SHA"
+ build_script:
+ - if [ -f $HOME/.cache/qemu-vm/images/@NAME@.img ]; then
+ make vm-build-@NAME@ J=$(getconf _NPROCESSORS_ONLN)
+ EXTRA_CONFIGURE_OPTS="@CONFIGURE_ARGS@"
+ BUILD_TARGET="@TEST_TARGETS@" ;
+ else
+ make vm-build-@NAME@ J=$(getconf _NPROCESSORS_ONLN) BUILD_TARGET=help
+ EXTRA_CONFIGURE_OPTS="--disable-system --disable-user --disable-tools" ;
+ fi
diff --git a/.gitlab-ci.d/qemu-project.yml b/.gitlab-ci.d/qemu-project.yml
index b3d79bc429..871262fe0e 100644
--- a/.gitlab-ci.d/qemu-project.yml
+++ b/.gitlab-ci.d/qemu-project.yml
@@ -11,3 +11,4 @@ include:
- local: '/.gitlab-ci.d/static_checks.yml'
- local: '/.gitlab-ci.d/custom-runners.yml'
- local: '/.gitlab-ci.d/cirrus.yml'
+ - local: '/.gitlab-ci.d/windows.yml'
diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml
new file mode 100644
index 0000000000..309f7e7fb8
--- /dev/null
+++ b/.gitlab-ci.d/windows.yml
@@ -0,0 +1,98 @@
+.shared_msys2_builder:
+ tags:
+ - shared-windows
+ - windows
+ - windows-1809
+ cache:
+ key: "${CI_JOB_NAME}-cache"
+ paths:
+ - ${CI_PROJECT_DIR}/msys64/var/cache
+ needs: []
+ stage: build
+ timeout: 70m
+ before_script:
+ - If ( !(Test-Path -Path msys64\var\cache ) ) {
+ mkdir msys64\var\cache
+ }
+ - If ( !(Test-Path -Path msys64\var\cache\msys2.exe ) ) {
+ Invoke-WebRequest
+ "https://github.com/msys2/msys2-installer/releases/download/2021-07-25/msys2-base-x86_64-20210725.sfx.exe"
+ -outfile "msys64\var\cache\msys2.exe"
+ }
+ - msys64\var\cache\msys2.exe -y
+ - ((Get-Content -path .\msys64\etc\\post-install\\07-pacman-key.post -Raw)
+ -replace '--refresh-keys', '--version') |
+ Set-Content -Path ${CI_PROJECT_DIR}\msys64\etc\\post-install\\07-pacman-key.post
+ - .\msys64\usr\bin\bash -lc "sed -i 's/^CheckSpace/#CheckSpace/g' /etc/pacman.conf"
+ - .\msys64\usr\bin\bash -lc 'pacman --noconfirm -Syuu' # Core update
+ - .\msys64\usr\bin\bash -lc 'pacman --noconfirm -Syuu' # Normal update
+ - taskkill /F /FI "MODULES eq msys-2.0.dll"
+
+msys2-64bit:
+ extends: .shared_msys2_builder
+ script:
+ - .\msys64\usr\bin\bash -lc "pacman -Sy --noconfirm --needed
+ diffutils git grep make sed
+ mingw-w64-x86_64-capstone
+ mingw-w64-x86_64-curl
+ mingw-w64-x86_64-cyrus-sasl
+ mingw-w64-x86_64-gcc
+ mingw-w64-x86_64-glib2
+ mingw-w64-x86_64-gnutls
+ mingw-w64-x86_64-libnfs
+ mingw-w64-x86_64-libpng
+ mingw-w64-x86_64-libssh
+ mingw-w64-x86_64-libtasn1
+ mingw-w64-x86_64-libusb
+ mingw-w64-x86_64-libxml2
+ mingw-w64-x86_64-nettle
+ mingw-w64-x86_64-ninja
+ mingw-w64-x86_64-pixman
+ mingw-w64-x86_64-pkgconf
+ mingw-w64-x86_64-python
+ mingw-w64-x86_64-SDL2
+ mingw-w64-x86_64-SDL2_image
+ mingw-w64-x86_64-snappy
+ mingw-w64-x86_64-usbredir
+ mingw-w64-x86_64-zstd "
+ - $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
+ - $env:MSYSTEM = 'MINGW64' # Start a 64 bit Mingw environment
+ - .\msys64\usr\bin\bash -lc './configure --target-list=x86_64-softmmu
+ --enable-capstone=system'
+ - .\msys64\usr\bin\bash -lc "sed -i '/^ROMS=/d' build/config-host.mak"
+ - .\msys64\usr\bin\bash -lc 'make -j2'
+ - .\msys64\usr\bin\bash -lc 'make check'
+
+msys2-32bit:
+ extends: .shared_msys2_builder
+ script:
+ - .\msys64\usr\bin\bash -lc "pacman -Sy --noconfirm --needed
+ diffutils git grep make sed
+ mingw-w64-i686-capstone
+ mingw-w64-i686-curl
+ mingw-w64-i686-cyrus-sasl
+ mingw-w64-i686-gcc
+ mingw-w64-i686-glib2
+ mingw-w64-i686-gnutls
+ mingw-w64-i686-gtk3
+ mingw-w64-i686-libgcrypt
+ mingw-w64-i686-libjpeg-turbo
+ mingw-w64-i686-libssh
+ mingw-w64-i686-libtasn1
+ mingw-w64-i686-libusb
+ mingw-w64-i686-libxml2
+ mingw-w64-i686-lzo2
+ mingw-w64-i686-ninja
+ mingw-w64-i686-pixman
+ mingw-w64-i686-pkgconf
+ mingw-w64-i686-python
+ mingw-w64-i686-snappy
+ mingw-w64-i686-usbredir "
+ - $env:CHERE_INVOKING = 'yes' # Preserve the current working directory
+ - $env:MSYSTEM = 'MINGW32' # Start a 32-bit MinG environment
+ - mkdir output
+ - cd output
+ - ..\msys64\usr\bin\bash -lc "../configure --target-list=ppc64-softmmu
+ --enable-capstone=system"
+ - ..\msys64\usr\bin\bash -lc 'make -j2'
+ - ..\msys64\usr\bin\bash -lc 'make check'
diff --git a/MAINTAINERS b/MAINTAINERS
index 4d2143ff23..fbd6d0b174 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -324,7 +324,7 @@ F: disas/sparc.c
X86 TCG CPUs
M: Paolo Bonzini <pbonzini@redhat.com>
M: Richard Henderson <richard.henderson@linaro.org>
-M: Eduardo Habkost <ehabkost@redhat.com>
+M: Eduardo Habkost <eduardo@habkost.net>
S: Maintained
F: target/i386/tcg/
F: tests/tcg/i386/
@@ -1628,7 +1628,7 @@ F: include/hw/i386/microvm.h
F: pc-bios/bios-microvm.bin
Machine core
-M: Eduardo Habkost <ehabkost@redhat.com>
+M: Eduardo Habkost <eduardo@habkost.net>
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
R: Philippe Mathieu-Daudé <philmd@redhat.com>
S: Supported
@@ -2649,13 +2649,13 @@ F: backends/cryptodev*.c
Python library
M: John Snow <jsnow@redhat.com>
M: Cleber Rosa <crosa@redhat.com>
-R: Eduardo Habkost <ehabkost@redhat.com>
+R: Eduardo Habkost <eduardo@habkost.net>
S: Maintained
F: python/
T: git https://gitlab.com/jsnow/qemu.git python
Python scripts
-M: Eduardo Habkost <ehabkost@redhat.com>
+M: Eduardo Habkost <eduardo@habkost.net>
M: Cleber Rosa <crosa@redhat.com>
S: Odd Fixes
F: scripts/*.py
@@ -2731,7 +2731,7 @@ T: git https://github.com/mdroth/qemu.git qga
QOM
M: Paolo Bonzini <pbonzini@redhat.com>
R: Daniel P. Berrange <berrange@redhat.com>
-R: Eduardo Habkost <ehabkost@redhat.com>
+R: Eduardo Habkost <eduardo@habkost.net>
S: Supported
F: docs/qdev-device-use.txt
F: hw/core/qdev*
@@ -2751,7 +2751,7 @@ F: tests/unit/check-qom-proplist.c
F: tests/unit/test-qdev-global-props.c
QOM boilerplate conversion script
-M: Eduardo Habkost <ehabkost@redhat.com>
+M: Eduardo Habkost <eduardo@habkost.net>
S: Maintained
F: scripts/codeconverter/
diff --git a/VERSION b/VERSION
index f5664554ca..8e8e37d75b 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-6.1.92
+6.2.50
diff --git a/block/nvme.c b/block/nvme.c
index e4f336d79c..fa360b9b3c 100644
--- a/block/nvme.c
+++ b/block/nvme.c
@@ -206,8 +206,9 @@ static void nvme_free_req_queue_cb(void *opaque)
NVMeQueuePair *q = opaque;
qemu_mutex_lock(&q->lock);
- while (qemu_co_enter_next(&q->free_req_queue, &q->lock)) {
- /* Retry all pending requests */
+ while (q->free_req_head != -1 &&
+ qemu_co_enter_next(&q->free_req_queue, &q->lock)) {
+ /* Retry waiting requests */
}
qemu_mutex_unlock(&q->lock);
}
diff --git a/blockdev.c b/blockdev.c
index b35072644e..0eb2823b1b 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -303,16 +303,6 @@ int drive_get_max_bus(BlockInterfaceType type)
return max_bus;
}
-/* Get a block device. This should only be used for single-drive devices
- (e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the
- appropriate bus. */
-DriveInfo *drive_get_next(BlockInterfaceType type)
-{
- static int next_block_unit[IF_COUNT];
-
- return drive_get(type, 0, next_block_unit[type]++);
-}
-
static void bdrv_format_print(void *opaque, const char *name)
{
qemu_printf(" %s", name);
diff --git a/configure b/configure
index d3aac031a5..5fae19858d 100755
--- a/configure
+++ b/configure
@@ -344,7 +344,6 @@ debug_stack_usage="no"
crypto_afalg="no"
tls_priority="NORMAL"
tpm="$default_feature"
-libssh="$default_feature"
live_block_migration=${default_feature:-yes}
numa="$default_feature"
replication=${default_feature:-yes}
@@ -1076,10 +1075,6 @@ for opt do
;;
--enable-tpm) tpm="yes"
;;
- --disable-libssh) libssh="no"
- ;;
- --enable-libssh) libssh="yes"
- ;;
--disable-live-block-migration) live_block_migration="no"
;;
--enable-live-block-migration) live_block_migration="yes"
@@ -1446,7 +1441,6 @@ cat << EOF
live-block-migration Block migration in the main migration stream
coroutine-pool coroutine freelist (better performance)
tpm TPM support
- libssh ssh block device support
numa libnuma support
avx2 AVX2 optimization support
avx512f AVX512F optimization support
@@ -2560,21 +2554,6 @@ if test "$modules" = yes; then
fi
##########################################
-# libssh probe
-if test "$libssh" != "no" ; then
- if $pkg_config --exists "libssh >= 0.8.7"; then
- libssh_cflags=$($pkg_config libssh --cflags)
- libssh_libs=$($pkg_config libssh --libs)
- libssh=yes
- else
- if test "$libssh" = "yes" ; then
- error_exit "libssh required for --enable-libssh"
- fi
- libssh=no
- fi
-fi
-
-##########################################
# TPM emulation is only on POSIX
if test "$tpm" = ""; then
@@ -3631,12 +3610,6 @@ if test "$cmpxchg128" = "yes" ; then
echo "CONFIG_CMPXCHG128=y" >> $config_host_mak
fi
-if test "$libssh" = "yes" ; then
- echo "CONFIG_LIBSSH=y" >> $config_host_mak
- echo "LIBSSH_CFLAGS=$libssh_cflags" >> $config_host_mak
- echo "LIBSSH_LIBS=$libssh_libs" >> $config_host_mak
-fi
-
if test "$live_block_migration" = "yes" ; then
echo "CONFIG_LIVE_BLOCK_MIGRATION=y" >> $config_host_mak
fi
diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst
index cec87e3743..d8b102fa0a 100644
--- a/docs/system/arm/aspeed.rst
+++ b/docs/system/arm/aspeed.rst
@@ -14,6 +14,7 @@ AST2400 SoC based machines :
- ``palmetto-bmc`` OpenPOWER Palmetto POWER8 BMC
- ``quanta-q71l-bmc`` OpenBMC Quanta BMC
+- ``supermicrox11-bmc`` Supermicro X11 BMC
AST2500 SoC based machines :
@@ -21,12 +22,16 @@ AST2500 SoC based machines :
- ``romulus-bmc`` OpenPOWER Romulus POWER9 BMC
- ``witherspoon-bmc`` OpenPOWER Witherspoon POWER9 BMC
- ``sonorapass-bmc`` OCP SonoraPass BMC
-- ``swift-bmc`` OpenPOWER Swift BMC POWER9
+- ``swift-bmc`` OpenPOWER Swift BMC POWER9 (to be removed in v7.0)
+- ``fp5280g2-bmc`` Inspur FP5280G2 BMC
+- ``g220a-bmc`` Bytedance G220A BMC
AST2600 SoC based machines :
- ``ast2600-evb`` Aspeed AST2600 Evaluation board (Cortex-A7)
- ``tacoma-bmc`` OpenPOWER Witherspoon POWER9 AST2600 BMC
+- ``rainier-bmc`` IBM Rainier POWER10 BMC
+- ``fuji-bmc`` Facebook Fuji BMC
Supported devices
-----------------
@@ -51,13 +56,13 @@ Supported devices
* Front LEDs (PCA9552 on I2C bus)
* LPC Peripheral Controller (a subset of subdevices are supported)
* Hash/Crypto Engine (HACE) - Hash support only. TODO: HMAC and RSA
+ * ADC
Missing devices
---------------
* Coprocessor support
- * ADC (out of tree implementation)
* PWM and Fan Controller
* Slave GPIO Controller
* Super I/O Controller
@@ -73,16 +78,25 @@ Missing devices
Boot options
------------
-The Aspeed machines can be started using the ``-kernel`` option to
-load a Linux kernel or from a firmware. Images can be downloaded from
-the OpenBMC jenkins :
+The Aspeed machines can be started using the ``-kernel`` and ``-dtb`` options
+to load a Linux kernel or from a firmware. Images can be downloaded from the
+OpenBMC jenkins :
- https://jenkins.openbmc.org/job/ci-openbmc/lastSuccessfulBuild/distro=ubuntu,label=docker-builder
+ https://jenkins.openbmc.org/job/ci-openbmc/lastSuccessfulBuild/
or directly from the OpenBMC GitHub release repository :
https://github.com/openbmc/openbmc/releases
+To boot a kernel directly from a Linux build tree:
+
+.. code-block:: bash
+
+ $ qemu-system-arm -M ast2600-evb -nographic \
+ -kernel arch/arm/boot/zImage \
+ -dtb arch/arm/boot/dts/aspeed-ast2600-evb.dtb \
+ -initrd rootfs.cpio
+
The image should be attached as an MTD drive. Run :
.. code-block:: bash
diff --git a/dump/dump.c b/dump/dump.c
index 662d0a62cd..a84d8b1598 100644
--- a/dump/dump.c
+++ b/dump/dump.c
@@ -1293,14 +1293,6 @@ static size_t get_len_buf_out(size_t page_size, uint32_t flag_compress)
return 0;
}
-/*
- * check if the page is all 0
- */
-static inline bool is_zero_page(const uint8_t *buf, size_t page_size)
-{
- return buffer_is_zero(buf, page_size);
-}
-
static void write_dump_pages(DumpState *s, Error **errp)
{
int ret = 0;
@@ -1357,7 +1349,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
*/
while (get_next_page(&block_iter, &pfn_iter, &buf, s)) {
/* check zero page */
- if (is_zero_page(buf, s->dump_info.page_size)) {
+ if (buffer_is_zero(buf, s->dump_info.page_size)) {
ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor),
false);
if (ret < 0) {
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 2d37d29f02..e652590943 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -27,6 +27,7 @@ config ARM_VIRT
select DIMM
select ACPI_HW_REDUCED
select ACPI_APEI
+ select ACPI_VIOT
config CHEETAH
bool
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index a77f46b3ad..cf20ae0db5 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -284,12 +284,13 @@ static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size,
}
static void aspeed_board_init_flashes(AspeedSMCState *s,
- const char *flashtype)
+ const char *flashtype,
+ int unit0)
{
int i ;
for (i = 0; i < s->num_cs; ++i) {
- DriveInfo *dinfo = drive_get_next(IF_MTD);
+ DriveInfo *dinfo = drive_get(IF_MTD, 0, unit0 + i);
qemu_irq cs_line;
DeviceState *dev;
@@ -382,10 +383,12 @@ static void aspeed_machine_init(MachineState *machine)
"max_ram", max_ram_size - machine->ram_size);
memory_region_add_subregion(&bmc->ram_container, machine->ram_size, &bmc->max_ram);
- aspeed_board_init_flashes(&bmc->soc.fmc, bmc->fmc_model ?
- bmc->fmc_model : amc->fmc_model);
- aspeed_board_init_flashes(&bmc->soc.spi[0], bmc->spi_model ?
- bmc->spi_model : amc->spi_model);
+ aspeed_board_init_flashes(&bmc->soc.fmc,
+ bmc->fmc_model ? bmc->fmc_model : amc->fmc_model,
+ 0);
+ aspeed_board_init_flashes(&bmc->soc.spi[0],
+ bmc->spi_model ? bmc->spi_model : amc->spi_model,
+ bmc->soc.fmc.num_cs);
/* Install first FMC flash content as a boot rom. */
if (drive0) {
@@ -435,11 +438,13 @@ static void aspeed_machine_init(MachineState *machine)
}
for (i = 0; i < bmc->soc.sdhci.num_slots; i++) {
- sdhci_attach_drive(&bmc->soc.sdhci.slots[i], drive_get_next(IF_SD));
+ sdhci_attach_drive(&bmc->soc.sdhci.slots[i],
+ drive_get(IF_SD, 0, i));
}
if (bmc->soc.emmc.num_slots) {
- sdhci_attach_drive(&bmc->soc.emmc.slots[0], drive_get_next(IF_SD));
+ sdhci_attach_drive(&bmc->soc.emmc.slots[0],
+ drive_get(IF_SD, 0, bmc->soc.sdhci.num_slots));
}
arm_load_kernel(ARM_CPU(first_cpu), machine, &aspeed_board_binfo);
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 74ad397b1f..399f8e837c 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -8,7 +8,6 @@
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qemu/datadir.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c
index 294ba5de6e..5e3372a3c7 100644
--- a/hw/arm/cubieboard.c
+++ b/hw/arm/cubieboard.c
@@ -81,7 +81,7 @@ static void cubieboard_init(MachineState *machine)
}
/* Retrieve SD bus */
- di = drive_get_next(IF_SD);
+ di = drive_get(IF_SD, 0, 0);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
bus = qdev_get_child_bus(DEVICE(a10), "sd-bus");
diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c
index b771a3d8b7..4093af09cb 100644
--- a/hw/arm/digic_boards.c
+++ b/hw/arm/digic_boards.c
@@ -25,7 +25,6 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
-#include "qemu-common.h"
#include "qemu/datadir.h"
#include "hw/boards.h"
#include "qemu/error-report.h"
diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c
index c3cb315dbc..4210894d81 100644
--- a/hw/arm/highbank.c
+++ b/hw/arm/highbank.c
@@ -18,7 +18,6 @@
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qemu/datadir.h"
#include "qapi/error.h"
#include "hw/sysbus.h"
diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c
index bd16acd4d9..6dff000163 100644
--- a/hw/arm/imx25_pdk.c
+++ b/hw/arm/imx25_pdk.c
@@ -123,7 +123,7 @@ static void imx25_pdk_init(MachineState *machine)
DriveInfo *di;
BlockBackend *blk;
- di = drive_get_next(IF_SD);
+ di = drive_get(IF_SD, 0, i);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
bus = qdev_get_child_bus(DEVICE(&s->soc.esdhc[i]), "sd-bus");
carddev = qdev_new(TYPE_SD_CARD);
diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c
index 16e8985953..b109ece3ae 100644
--- a/hw/arm/integratorcp.c
+++ b/hw/arm/integratorcp.c
@@ -649,7 +649,7 @@ static void integratorcp_init(MachineState *machine)
qdev_get_gpio_in_named(icp, ICP_GPIO_MMC_WPROT, 0));
qdev_connect_gpio_out_named(dev, "card-inserted", 0,
qdev_get_gpio_in_named(icp, ICP_GPIO_MMC_CARDIN, 0));
- dinfo = drive_get_next(IF_SD);
+ dinfo = drive_get(IF_SD, 0, 0);
if (dinfo) {
DeviceState *card;
diff --git a/hw/arm/mcimx6ul-evk.c b/hw/arm/mcimx6ul-evk.c
index 77fae874b1..28b4886f48 100644
--- a/hw/arm/mcimx6ul-evk.c
+++ b/hw/arm/mcimx6ul-evk.c
@@ -52,7 +52,7 @@ static void mcimx6ul_evk_init(MachineState *machine)
DriveInfo *di;
BlockBackend *blk;
- di = drive_get_next(IF_SD);
+ di = drive_get(IF_SD, 0, i);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus");
carddev = qdev_new(TYPE_SD_CARD);
diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c
index 935d4b0f1c..50a5ecde31 100644
--- a/hw/arm/mcimx7d-sabre.c
+++ b/hw/arm/mcimx7d-sabre.c
@@ -52,7 +52,7 @@ static void mcimx7d_sabre_init(MachineState *machine)
DriveInfo *di;
BlockBackend *blk;
- di = drive_get_next(IF_SD);
+ di = drive_get(IF_SD, 0, i);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus");
carddev = qdev_new(TYPE_SD_CARD);
diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c
index 396e8b9913..d9f881690e 100644
--- a/hw/arm/msf2-som.c
+++ b/hw/arm/msf2-som.c
@@ -45,7 +45,7 @@ static void emcraft_sf2_s2s010_init(MachineState *machine)
DeviceState *spi_flash;
MSF2State *soc;
MachineClass *mc = MACHINE_GET_CLASS(machine);
- DriveInfo *dinfo = drive_get_next(IF_MTD);
+ DriveInfo *dinfo = drive_get(IF_MTD, 0, 0);
qemu_irq cs_line;
BusState *spi_bus;
MemoryRegion *sysmem = get_system_memory();
diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
index dec7d16ae5..0866d2f4f0 100644
--- a/hw/arm/npcm7xx_boards.c
+++ b/hw/arm/npcm7xx_boards.c
@@ -24,7 +24,6 @@
#include "hw/qdev-core.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
-#include "qemu-common.h"
#include "qemu/datadir.h"
#include "qemu/units.h"
#include "sysemu/blockdev.h"
@@ -84,9 +83,9 @@ static void npcm7xx_connect_dram(NPCM7xxState *soc, MemoryRegion *dram)
&error_abort);
}
-static void sdhci_attach_drive(SDHCIState *sdhci)
+static void sdhci_attach_drive(SDHCIState *sdhci, int unit)
{
- DriveInfo *di = drive_get_next(IF_SD);
+ DriveInfo *di = drive_get(IF_SD, 0, unit);
BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL;
BusState *bus = qdev_get_child_bus(DEVICE(sdhci), "sd-bus");
@@ -374,7 +373,7 @@ static void quanta_gbs_init(MachineState *machine)
drive_get(IF_MTD, 0, 0));
quanta_gbs_i2c_init(soc);
- sdhci_attach_drive(&soc->mmc.sdhci);
+ sdhci_attach_drive(&soc->mmc.sdhci, 0);
npcm7xx_load_kernel(machine, soc);
}
diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c
index 0cf9895ce7..e796382236 100644
--- a/hw/arm/orangepi.c
+++ b/hw/arm/orangepi.c
@@ -85,7 +85,7 @@ static void orangepi_init(MachineState *machine)
qdev_realize(DEVICE(h3), NULL, &error_abort);
/* Retrieve SD bus */
- di = drive_get_next(IF_SD);
+ di = drive_get(IF_SD, 0, 0);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
bus = qdev_get_child_bus(DEVICE(h3), "sd-bus");
diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c
index 146d35382b..b4dd6c1e99 100644
--- a/hw/arm/raspi.c
+++ b/hw/arm/raspi.c
@@ -284,7 +284,7 @@ static void raspi_machine_init(MachineState *machine)
qdev_realize(DEVICE(&s->soc), NULL, &error_fatal);
/* Create and plug in the SD cards */
- di = drive_get_next(IF_SD);
+ di = drive_get(IF_SD, 0, 0);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
bus = qdev_get_child_bus(DEVICE(&s->soc), "sd-bus");
if (bus == NULL) {
diff --git a/hw/arm/realview.c b/hw/arm/realview.c
index 1c54316ba3..ddc70b54a5 100644
--- a/hw/arm/realview.c
+++ b/hw/arm/realview.c
@@ -237,7 +237,7 @@ static void realview_init(MachineState *machine,
qemu_irq_invert(qdev_get_gpio_in(gpio2, 0)));
qdev_connect_gpio_out_named(dev, "card-read-only", 0, mmc_irq[0]);
qdev_connect_gpio_out_named(dev, "card-inserted", 0, mmc_irq[1]);
- dinfo = drive_get_next(IF_SD);
+ dinfo = drive_get(IF_SD, 0, 0);
if (dinfo) {
DeviceState *card;
diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c
index 553608e583..cce49aa25c 100644
--- a/hw/arm/sabrelite.c
+++ b/hw/arm/sabrelite.c
@@ -76,7 +76,7 @@ static void sabrelite_init(MachineState *machine)
if (spi_bus) {
DeviceState *flash_dev;
qemu_irq cs_line;
- DriveInfo *dinfo = drive_get_next(IF_MTD);
+ DriveInfo *dinfo = drive_get(IF_MTD, 0, 0);
flash_dev = qdev_new("sst25vf016b");
if (dinfo) {
diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c
index 358714bd3e..dd944553f7 100644
--- a/hw/arm/sbsa-ref.c
+++ b/hw/arm/sbsa-ref.c
@@ -18,7 +18,6 @@
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qemu/datadir.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c
index 78827ace6b..b6c8a5d609 100644
--- a/hw/arm/stellaris.c
+++ b/hw/arm/stellaris.c
@@ -10,6 +10,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/sysbus.h"
+#include "hw/sd/sd.h"
#include "hw/ssi/ssi.h"
#include "hw/arm/boot.h"
#include "qemu/timer.h"
@@ -1157,6 +1158,9 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
void *bus;
DeviceState *sddev;
DeviceState *ssddev;
+ DriveInfo *dinfo;
+ DeviceState *carddev;
+ BlockBackend *blk;
/*
* Some boards have both an OLED controller and SD card connected to
@@ -1221,8 +1225,17 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
* - Make the ssd0323 OLED controller chipselect active-low
*/
bus = qdev_get_child_bus(dev, "ssi");
-
sddev = ssi_create_peripheral(bus, "ssi-sd");
+
+ dinfo = drive_get(IF_SD, 0, 0);
+ blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL;
+ carddev = qdev_new(TYPE_SD_CARD);
+ qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal);
+ qdev_prop_set_bit(carddev, "spi", true);
+ qdev_realize_and_unref(carddev,
+ qdev_get_child_bus(sddev, "sd-bus"),
+ &error_fatal);
+
ssddev = ssi_create_peripheral(bus, "ssd0323");
gpio_out[GPIO_D][0] = qemu_irq_split(
qdev_get_gpio_in_named(sddev, SSI_GPIO_CS, 0),
diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c
index 0019b7f478..c07947d9f8 100644
--- a/hw/arm/stm32f405_soc.c
+++ b/hw/arm/stm32f405_soc.c
@@ -24,7 +24,6 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
-#include "qemu-common.h"
#include "exec/address-spaces.h"
#include "sysemu/sysemu.h"
#include "hw/arm/stm32f405_soc.h"
diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c
index 575399c4fc..ecc1f6cf74 100644
--- a/hw/arm/versatilepb.c
+++ b/hw/arm/versatilepb.c
@@ -310,7 +310,7 @@ static void versatile_init(MachineState *machine, int board_id)
qdev_connect_gpio_out(sysctl, 0, qdev_get_gpio_in(dev, 0));
dev = sysbus_create_varargs("pl181", 0x10005000, sic[22], sic[1], NULL);
- dinfo = drive_get_next(IF_SD);
+ dinfo = drive_get(IF_SD, 0, 0);
if (dinfo) {
DeviceState *card;
@@ -322,7 +322,7 @@ static void versatile_init(MachineState *machine, int board_id)
}
dev = sysbus_create_varargs("pl181", 0x1000b000, sic[23], sic[2], NULL);
- dinfo = drive_get_next(IF_SD);
+ dinfo = drive_get(IF_SD, 0, 1);
if (dinfo) {
DeviceState *card;
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index 58481c0762..3e99b7918a 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -23,7 +23,6 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
-#include "qemu-common.h"
#include "qemu/datadir.h"
#include "cpu.h"
#include "hw/sysbus.h"
@@ -625,7 +624,7 @@ static void vexpress_common_init(MachineState *machine)
qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT));
qdev_connect_gpio_out_named(dev, "card-inserted", 0,
qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN));
- dinfo = drive_get_next(IF_SD);
+ dinfo = drive_get(IF_SD, 0, 0);
if (dinfo) {
DeviceState *card;
@@ -657,7 +656,7 @@ static void vexpress_common_init(MachineState *machine)
sysbus_create_simple("pl111", map[VE_CLCD], pic[14]);
- dinfo = drive_get_next(IF_PFLASH);
+ dinfo = drive_get(IF_PFLASH, 0, 0);
pflash0 = ve_pflash_cfi01_register(map[VE_NORFLASH0], "vexpress.flash0",
dinfo);
if (!pflash0) {
@@ -673,7 +672,7 @@ static void vexpress_common_init(MachineState *machine)
memory_region_add_subregion(sysmem, map[VE_NORFLASHALIAS], flashalias);
}
- dinfo = drive_get_next(IF_PFLASH);
+ dinfo = drive_get(IF_PFLASH, 0, 1);
if (!ve_pflash_cfi01_register(map[VE_NORFLASH1], "vexpress.flash1",
dinfo)) {
error_report("vexpress: error registering flash 1");
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 674f902652..d0f4867fdf 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -55,6 +55,7 @@
#include "kvm_arm.h"
#include "migration/vmstate.h"
#include "hw/acpi/ghes.h"
+#include "hw/acpi/viot.h"
#define ARM_SPI_BASE 32
@@ -1011,6 +1012,12 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
}
#endif
+ if (vms->iommu == VIRT_IOMMU_VIRTIO) {
+ acpi_add_table(table_offsets, tables_blob);
+ build_viot(ms, tables_blob, tables->linker, vms->virtio_iommu_bdf,
+ vms->oem_id, vms->oem_table_id);
+ }
+
/* XSDT is pointed to by RSDP */
xsdt = tables_blob->len;
build_xsdt(tables_blob, tables->linker, table_offsets, vms->oem_id,
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 30da05dfe0..6bce595aba 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -29,7 +29,6 @@
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qemu/datadir.h"
#include "qemu/units.h"
#include "qemu/option.h"
@@ -2494,6 +2493,11 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
hwaddr db_start = 0, db_end = 0;
char *resv_prop_str;
+ if (vms->iommu != VIRT_IOMMU_NONE) {
+ error_setg(errp, "virt machine does not support multiple IOMMUs");
+ return;
+ }
+
switch (vms->msi_controller) {
case VIRT_MSI_CTRL_NONE:
return;
@@ -2513,8 +2517,9 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
db_start, db_end,
VIRTIO_IOMMU_RESV_MEM_T_MSI);
- qdev_prop_set_uint32(dev, "len-reserved-regions", 1);
- qdev_prop_set_string(dev, "reserved-regions[0]", resv_prop_str);
+ object_property_set_uint(OBJECT(dev), "len-reserved-regions", 1, errp);
+ object_property_set_str(OBJECT(dev), "reserved-regions[0]",
+ resv_prop_str, errp);
g_free(resv_prop_str);
}
}
@@ -2614,16 +2619,10 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
MachineClass *mc = MACHINE_GET_CLASS(machine);
if (device_is_dynamic_sysbus(mc, dev) ||
- (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) {
+ object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
+ object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
return HOTPLUG_HANDLER(machine);
}
- if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
- VirtMachineState *vms = VIRT_MACHINE(machine);
-
- if (!vms->bootinfo.firmware_loaded || !virt_is_acpi_enabled(vms)) {
- return HOTPLUG_HANDLER(machine);
- }
- }
return NULL;
}
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 69c333e91b..50e7268396 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -125,9 +125,10 @@ static void gem_init(NICInfo *nd, uint32_t base, qemu_irq irq)
sysbus_connect_irq(s, 0, irq);
}
-static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq,
- bool is_qspi)
+static inline int zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq,
+ bool is_qspi, int unit0)
{
+ int unit = unit0;
DeviceState *dev;
SysBusDevice *busdev;
SSIBus *spi;
@@ -156,7 +157,7 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq,
spi = (SSIBus *)qdev_get_child_bus(dev, bus_name);
for (j = 0; j < num_ss; ++j) {
- DriveInfo *dinfo = drive_get_next(IF_MTD);
+ DriveInfo *dinfo = drive_get(IF_MTD, 0, unit++);
flash_dev = qdev_new("n25q128");
if (dinfo) {
qdev_prop_set_drive_err(flash_dev, "drive",
@@ -170,6 +171,7 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq,
}
}
+ return unit;
}
static void zynq_init(MachineState *machine)
@@ -247,9 +249,9 @@ static void zynq_init(MachineState *machine)
pic[n] = qdev_get_gpio_in(dev, n);
}
- zynq_init_spi_flashes(0xE0006000, pic[58-IRQ_OFFSET], false);
- zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET], false);
- zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true);
+ n = zynq_init_spi_flashes(0xE0006000, pic[58 - IRQ_OFFSET], false, 0);
+ n = zynq_init_spi_flashes(0xE0007000, pic[81 - IRQ_OFFSET], false, n);
+ n = zynq_init_spi_flashes(0xE000D000, pic[51 - IRQ_OFFSET], true, n);
sysbus_create_simple(TYPE_CHIPIDEA, 0xE0002000, pic[53 - IRQ_OFFSET]);
sysbus_create_simple(TYPE_CHIPIDEA, 0xE0003000, pic[76 - IRQ_OFFSET]);
@@ -298,7 +300,7 @@ static void zynq_init(MachineState *machine)
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, hci_addr);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[hci_irq - IRQ_OFFSET]);
- di = drive_get_next(IF_SD);
+ di = drive_get(IF_SD, 0, n);
blk = di ? blk_by_legacy_dinfo(di) : NULL;
carddev = qdev_new(TYPE_SD_CARD);
qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal);
diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c
index d2f55e29b6..0c5edc898e 100644
--- a/hw/arm/xlnx-versal-virt.c
+++ b/hw/arm/xlnx-versal-virt.c
@@ -669,7 +669,8 @@ static void versal_virt_init(MachineState *machine)
/* Plugin SD cards. */
for (i = 0; i < ARRAY_SIZE(s->soc.pmc.iou.sd); i++) {
- sd_plugin_card(&s->soc.pmc.iou.sd[i], drive_get_next(IF_SD));
+ sd_plugin_card(&s->soc.pmc.iou.sd[i],
+ drive_get(IF_SD, 0, i));
}
s->binfo.ram_size = machine->ram_size;
diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c
index 3dc2b5e8ca..45eb19ab3b 100644
--- a/hw/arm/xlnx-zcu102.c
+++ b/hw/arm/xlnx-zcu102.c
@@ -169,7 +169,7 @@ static void xlnx_zcu102_init(MachineState *machine)
/* Create and plug in the SD cards */
for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) {
BusState *bus;
- DriveInfo *di = drive_get_next(IF_SD);
+ DriveInfo *di = drive_get(IF_SD, 0, i);
BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL;
DeviceState *carddev;
char *bus_name;
@@ -190,7 +190,7 @@ static void xlnx_zcu102_init(MachineState *machine)
BusState *spi_bus;
DeviceState *flash_dev;
qemu_irq cs_line;
- DriveInfo *dinfo = drive_get_next(IF_MTD);
+ DriveInfo *dinfo = drive_get(IF_MTD, 0, i);
gchar *bus_name = g_strdup_printf("spi%d", i);
spi_bus = qdev_get_child_bus(DEVICE(&s->soc), bus_name);
@@ -212,7 +212,7 @@ static void xlnx_zcu102_init(MachineState *machine)
BusState *spi_bus;
DeviceState *flash_dev;
qemu_irq cs_line;
- DriveInfo *dinfo = drive_get_next(IF_MTD);
+ DriveInfo *dinfo = drive_get(IF_MTD, 0, XLNX_ZYNQMP_NUM_SPIS + i);
int bus = i / XLNX_ZYNQMP_NUM_QSPI_BUS_CS;
gchar *bus_name = g_strdup_printf("qspi%d", bus);
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
index 252c3a7a23..ee5a5352dc 100644
--- a/hw/block/dataplane/virtio-blk.c
+++ b/hw/block/dataplane/virtio-blk.c
@@ -222,7 +222,7 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev)
memory_region_transaction_commit();
while (j--) {
- virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
+ virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), j);
}
goto fail_host_notifiers;
}
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
index fa933cd326..21d18ac2e3 100644
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -61,6 +61,12 @@
} while (0)
+/* Anonymous BlockBackend for empty drive */
+static BlockBackend *blk_create_empty_drive(void)
+{
+ return blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
+}
+
/********************************************************/
/* qdev floppy bus */
@@ -486,8 +492,7 @@ static void floppy_drive_realize(DeviceState *qdev, Error **errp)
}
if (!dev->conf.blk) {
- /* Anonymous BlockBackend for an empty drive */
- dev->conf.blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
+ dev->conf.blk = blk_create_empty_drive();
ret = blk_attach_dev(dev->conf.blk, qdev);
assert(ret == 0);
@@ -1161,7 +1166,19 @@ static FDrive *get_drv(FDCtrl *fdctrl, int unit)
static FDrive *get_cur_drv(FDCtrl *fdctrl)
{
- return get_drv(fdctrl, fdctrl->cur_drv);
+ FDrive *cur_drv = get_drv(fdctrl, fdctrl->cur_drv);
+
+ if (!cur_drv->blk) {
+ /*
+ * Kludge: empty drive line selected. Create an anonymous
+ * BlockBackend to avoid NULL deref with various BlockBackend
+ * API calls within this model (CVE-2021-20196).
+ * Due to the controller QOM model limitations, we don't
+ * attach the created to the controller device.
+ */
+ cur_drv->blk = blk_create_empty_drive();
+ }
+ return cur_drv;
}
/* Status A register : 0x00 (read-only) */
diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c
index 8df0832424..fde67f4f03 100644
--- a/hw/char/stm32f2xx_usart.c
+++ b/hw/char/stm32f2xx_usart.c
@@ -103,10 +103,11 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr,
return retvalue;
case USART_DR:
DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr);
+ retvalue = s->usart_dr & 0x3FF;
s->usart_sr &= ~USART_SR_RXNE;
qemu_chr_fe_accept_input(&s->chr);
qemu_set_irq(s->irq, 0);
- return s->usart_dr & 0x3FF;
+ return retvalue;
case USART_BRR:
return s->usart_brr;
case USART_CR1:
diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c
index 8cea84f2be..90851e730b 100644
--- a/hw/display/vga-isa.c
+++ b/hw/display/vga-isa.c
@@ -33,7 +33,6 @@
#include "hw/loader.h"
#include "hw/qdev-properties.h"
#include "qom/object.h"
-#include "qapi/error.h"
#define TYPE_ISA_VGA "isa-vga"
OBJECT_DECLARE_SIMPLE_TYPE(ISAVGAState, ISA_VGA)
@@ -62,15 +61,6 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp)
MemoryRegion *vga_io_memory;
const MemoryRegionPortio *vga_ports, *vbe_ports;
- /*
- * make sure this device is not being added twice, if so
- * exit without crashing qemu
- */
- if (object_resolve_path_type("", TYPE_ISA_VGA, NULL)) {
- error_setg(errp, "at most one %s device is permitted", TYPE_ISA_VGA);
- return;
- }
-
s->global_vmstate = true;
vga_common_init(s, OBJECT(dev));
s->legacy_address_space = isa_address_space(isadev);
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index 78aed93c45..010ded7eae 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -25,6 +25,11 @@ config APIC
select MSI_NONBROKEN
select I8259
+config ARM_GIC_TCG
+ bool
+ default y
+ depends on ARM_GIC && TCG
+
config ARM_GIC_KVM
bool
default y
diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c
index 9f5f815db9..715df5421d 100644
--- a/hw/intc/arm_gicv3.c
+++ b/hw/intc/arm_gicv3.c
@@ -1,5 +1,5 @@
/*
- * ARM Generic Interrupt Controller v3
+ * ARM Generic Interrupt Controller v3 (emulation)
*
* Copyright (c) 2015 Huawei.
* Copyright (c) 2016 Linaro Limited
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index 7fba931450..d7e03d0cab 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -1,5 +1,5 @@
/*
- * ARM Generic Interrupt Controller v3
+ * ARM Generic Interrupt Controller v3 (emulation)
*
* Copyright (c) 2016 Linaro Limited
* Written by Peter Maydell
@@ -21,14 +21,6 @@
#include "hw/irq.h"
#include "cpu.h"
-void gicv3_set_gicv3state(CPUState *cpu, GICv3CPUState *s)
-{
- ARMCPU *arm_cpu = ARM_CPU(cpu);
- CPUARMState *env = &arm_cpu->env;
-
- env->gicv3state = (void *)s;
-};
-
static GICv3CPUState *icc_cs_from_env(CPUARMState *env)
{
return env->gicv3state;
@@ -351,7 +343,8 @@ static uint32_t maintenance_interrupt_state(GICv3CPUState *cs)
/* Scan list registers and fill in the U, NP and EOI bits */
eoi_maintenance_interrupt_state(cs, &value);
- if (cs->ich_hcr_el2 & (ICH_HCR_EL2_LRENPIE | ICH_HCR_EL2_EOICOUNT_MASK)) {
+ if ((cs->ich_hcr_el2 & ICH_HCR_EL2_LRENPIE) &&
+ (cs->ich_hcr_el2 & ICH_HCR_EL2_EOICOUNT_MASK)) {
value |= ICH_MISR_EL2_LRENP;
}
diff --git a/hw/intc/arm_gicv3_cpuif_common.c b/hw/intc/arm_gicv3_cpuif_common.c
new file mode 100644
index 0000000000..ff1239f65d
--- /dev/null
+++ b/hw/intc/arm_gicv3_cpuif_common.c
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ARM Generic Interrupt Controller v3
+ *
+ * Copyright (c) 2016 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This code is licensed under the GPL, version 2 or (at your option)
+ * any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "gicv3_internal.h"
+#include "cpu.h"
+
+void gicv3_set_gicv3state(CPUState *cpu, GICv3CPUState *s)
+{
+ ARMCPU *arm_cpu = ARM_CPU(cpu);
+ CPUARMState *env = &arm_cpu->env;
+
+ env->gicv3state = (void *)s;
+};
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index c929a9cb5c..b99e63d58f 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -274,21 +274,36 @@ static bool process_its_cmd(GICv3ITSState *s, uint64_t value, uint32_t offset,
if (res != MEMTX_OK) {
return result;
}
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: "
+ "invalid dte: %"PRIx64" for %d (MEM_TX: %d)\n",
+ __func__, dte, devid, res);
+ return result;
}
- if ((devid > s->dt.maxids.max_devids) || !dte_valid || !ite_valid ||
- !cte_valid || (eventid > max_eventid)) {
+
+ /*
+ * In this implementation, in case of guest errors we ignore the
+ * command and move onto the next command in the queue.
+ */
+ if (devid > s->dt.maxids.max_devids) {
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes "
- "devid %d or eventid %d or invalid dte %d or"
- "invalid cte %d or invalid ite %d\n",
- __func__, devid, eventid, dte_valid, cte_valid,
- ite_valid);
- /*
- * in this implementation, in case of error
- * we ignore this command and move onto the next
- * command in the queue
- */
+ "%s: invalid command attributes: devid %d>%d",
+ __func__, devid, s->dt.maxids.max_devids);
+
+ } else if (!dte_valid || !ite_valid || !cte_valid) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: "
+ "dte: %s, ite: %s, cte: %s\n",
+ __func__,
+ dte_valid ? "valid" : "invalid",
+ ite_valid ? "valid" : "invalid",
+ cte_valid ? "valid" : "invalid");
+ } else if (eventid > max_eventid) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: eventid %d > %d\n",
+ __func__, eventid, max_eventid);
} else {
/*
* Current implementation only supports rdbase == procnum
diff --git a/hw/intc/meson.build b/hw/intc/meson.build
index c89d2ca180..70080bc161 100644
--- a/hw/intc/meson.build
+++ b/hw/intc/meson.build
@@ -3,12 +3,14 @@ softmmu_ss.add(when: 'CONFIG_ARM_GIC', if_true: files(
'arm_gic.c',
'arm_gic_common.c',
'arm_gicv2m.c',
- 'arm_gicv3.c',
'arm_gicv3_common.c',
- 'arm_gicv3_dist.c',
'arm_gicv3_its_common.c',
- 'arm_gicv3_redist.c',
+))
+softmmu_ss.add(when: 'CONFIG_ARM_GIC_TCG', if_true: files(
+ 'arm_gicv3.c',
+ 'arm_gicv3_dist.c',
'arm_gicv3_its.c',
+ 'arm_gicv3_redist.c',
))
softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_pic.c'))
softmmu_ss.add(when: 'CONFIG_HEATHROW_PIC', if_true: files('heathrow_pic.c'))
@@ -25,7 +27,8 @@ softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_PMU', if_true: files('xlnx-pmu-iomod-in
specific_ss.add(when: 'CONFIG_ALLWINNER_A10_PIC', if_true: files('allwinner-a10-pic.c'))
specific_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c', 'apic_common.c'))
-specific_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif.c'))
+specific_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common.c'))
+specific_ss.add(when: 'CONFIG_ARM_GIC_TCG', if_true: files('arm_gicv3_cpuif.c'))
specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c'))
specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c'))
specific_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c'))
diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index 159db6cbe2..a24fadddca 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -183,7 +183,7 @@ petalogix_ml605_init(MachineState *machine)
spi = (SSIBus *)qdev_get_child_bus(dev, "spi");
for (i = 0; i < NUM_SPI_FLASHES; i++) {
- DriveInfo *dinfo = drive_get_next(IF_MTD);
+ DriveInfo *dinfo = drive_get(IF_MTD, 0, i);
qemu_irq cs_line;
dev = qdev_new("n25q128");
diff --git a/hw/mips/bootloader.c b/hw/mips/bootloader.c
index 6ec8314490..99991f8b2b 100644
--- a/hw/mips/bootloader.c
+++ b/hw/mips/bootloader.c
@@ -182,7 +182,11 @@ void bl_gen_write_ulong(uint32_t **p, target_ulong addr, target_ulong val)
{
bl_gen_load_ulong(p, BL_REG_K0, val);
bl_gen_load_ulong(p, BL_REG_K1, addr);
- bl_gen_sd(p, BL_REG_K0, BL_REG_K1, 0x0);
+ if (bootcpu_supports_isa(ISA_MIPS3)) {
+ bl_gen_sd(p, BL_REG_K0, BL_REG_K1, 0x0);
+ } else {
+ bl_gen_sw(p, BL_REG_K0, BL_REG_K1, 0x0);
+ }
}
void bl_gen_write_u32(uint32_t **p, target_ulong addr, uint32_t val)
diff --git a/hw/mips/boston.c b/hw/mips/boston.c
index 0e3cca5511..59ca08b93a 100644
--- a/hw/mips/boston.c
+++ b/hw/mips/boston.c
@@ -777,14 +777,15 @@ static void boston_mach_init(MachineState *machine)
exit(1);
}
} else if (machine->kernel_filename) {
- uint64_t kernel_entry, kernel_high, kernel_size;
+ uint64_t kernel_entry, kernel_high;
+ ssize_t kernel_size;
kernel_size = load_elf(machine->kernel_filename, NULL,
cpu_mips_kseg0_to_phys, NULL,
&kernel_entry, NULL, &kernel_high,
NULL, 0, EM_MIPS, 1, 0);
- if (kernel_size) {
+ if (kernel_size > 0) {
int dt_size;
g_autofree const void *dtb_file_data, *dtb_load_data;
hwaddr dtb_paddr = QEMU_ALIGN_UP(kernel_high, 64 * KiB);
diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c
index 52fdb750c0..6d5f84e6c2 100644
--- a/hw/misc/sifive_u_otp.c
+++ b/hw/misc/sifive_u_otp.c
@@ -209,9 +209,9 @@ static void sifive_u_otp_realize(DeviceState *dev, Error **errp)
TYPE_SIFIVE_U_OTP, SIFIVE_U_OTP_REG_SIZE);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
- dinfo = drive_get_next(IF_PFLASH);
+ dinfo = drive_get(IF_PFLASH, 0, 0);
if (!dinfo) {
- dinfo = drive_get_next(IF_NONE);
+ dinfo = drive_get(IF_NONE, 0, 0);
if (dinfo) {
warn_report("using \"-drive if=none\" for the OTP is deprecated, "
"use \"-drive if=pflash\" instead.");
diff --git a/hw/net/npcm7xx_emc.c b/hw/net/npcm7xx_emc.c
index 7c892f820f..545b2b7410 100644
--- a/hw/net/npcm7xx_emc.c
+++ b/hw/net/npcm7xx_emc.c
@@ -284,6 +284,12 @@ static void emc_halt_rx(NPCM7xxEMCState *emc, uint32_t mista_flag)
emc_set_mista(emc, mista_flag);
}
+static void emc_enable_rx_and_flush(NPCM7xxEMCState *emc)
+{
+ emc->rx_active = true;
+ qemu_flush_queued_packets(qemu_get_queue(emc->nic));
+}
+
static void emc_set_next_tx_descriptor(NPCM7xxEMCState *emc,
const NPCM7xxEMCTxDesc *tx_desc,
uint32_t desc_addr)
@@ -581,13 +587,6 @@ static ssize_t emc_receive(NetClientState *nc, const uint8_t *buf, size_t len1)
return len;
}
-static void emc_try_receive_next_packet(NPCM7xxEMCState *emc)
-{
- if (emc_can_receive(qemu_get_queue(emc->nic))) {
- qemu_flush_queued_packets(qemu_get_queue(emc->nic));
- }
-}
-
static uint64_t npcm7xx_emc_read(void *opaque, hwaddr offset, unsigned size)
{
NPCM7xxEMCState *emc = opaque;
@@ -703,7 +702,7 @@ static void npcm7xx_emc_write(void *opaque, hwaddr offset,
emc->regs[REG_MGSTA] |= REG_MGSTA_RXHA;
}
if (value & REG_MCMDR_RXON) {
- emc->rx_active = true;
+ emc_enable_rx_and_flush(emc);
} else {
emc_halt_rx(emc, 0);
}
@@ -739,8 +738,7 @@ static void npcm7xx_emc_write(void *opaque, hwaddr offset,
break;
case REG_RSDR:
if (emc->regs[REG_MCMDR] & REG_MCMDR_RXON) {
- emc->rx_active = true;
- emc_try_receive_next_packet(emc);
+ emc_enable_rx_and_flush(emc);
}
break;
case REG_MIIDA:
diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c
index 57d779fb55..d1d065efbc 100644
--- a/hw/riscv/microchip_pfsoc.c
+++ b/hw/riscv/microchip_pfsoc.c
@@ -458,7 +458,7 @@ static void microchip_icicle_kit_machine_init(MachineState *machine)
target_ulong firmware_end_addr, kernel_start_addr;
uint64_t kernel_entry;
uint32_t fdt_load_addr;
- DriveInfo *dinfo = drive_get_next(IF_SD);
+ DriveInfo *dinfo = drive_get(IF_SD, 0, 0);
/* Sanity check on RAM size */
if (machine->ram_size < mc->default_ram_size) {
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index 589ae72a59..aa74e67889 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -46,6 +46,7 @@
#include "hw/char/serial.h"
#include "hw/cpu/cluster.h"
#include "hw/misc/unimp.h"
+#include "hw/sd/sd.h"
#include "hw/ssi/ssi.h"
#include "target/riscv/cpu.h"
#include "hw/riscv/riscv_hart.h"
@@ -536,7 +537,8 @@ static void sifive_u_machine_init(MachineState *machine)
uint32_t fdt_load_addr;
uint64_t kernel_entry;
DriveInfo *dinfo;
- DeviceState *flash_dev, *sd_dev;
+ BlockBackend *blk;
+ DeviceState *flash_dev, *sd_dev, *card_dev;
qemu_irq flash_cs, sd_cs;
/* Initialize SoC */
@@ -670,7 +672,7 @@ static void sifive_u_machine_init(MachineState *machine)
/* Connect an SPI flash to SPI0 */
flash_dev = qdev_new("is25wp256");
- dinfo = drive_get_next(IF_MTD);
+ dinfo = drive_get(IF_MTD, 0, 0);
if (dinfo) {
qdev_prop_set_drive_err(flash_dev, "drive",
blk_by_legacy_dinfo(dinfo),
@@ -686,6 +688,15 @@ static void sifive_u_machine_init(MachineState *machine)
sd_cs = qdev_get_gpio_in_named(sd_dev, SSI_GPIO_CS, 0);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi2), 1, sd_cs);
+
+ dinfo = drive_get(IF_SD, 0, 0);
+ blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL;
+ card_dev = qdev_new(TYPE_SD_CARD);
+ qdev_prop_set_drive_err(card_dev, "drive", blk, &error_fatal);
+ qdev_prop_set_bit(card_dev, "spi", true);
+ qdev_realize_and_unref(card_dev,
+ qdev_get_child_bus(sd_dev, "sd-bus"),
+ &error_fatal);
}
static bool sifive_u_machine_get_start_in_flash(Object *obj, Error **errp)
diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c
index e60854eeef..167c03b780 100644
--- a/hw/sd/ssi-sd.c
+++ b/hw/sd/ssi-sd.c
@@ -368,36 +368,9 @@ static const VMStateDescription vmstate_ssi_sd = {
static void ssi_sd_realize(SSIPeripheral *d, Error **errp)
{
- ERRP_GUARD();
ssi_sd_state *s = SSI_SD(d);
- DeviceState *carddev;
- DriveInfo *dinfo;
qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(d), "sd-bus");
-
- /* Create and plug in the sd card */
- /* FIXME use a qdev drive property instead of drive_get_next() */
- dinfo = drive_get_next(IF_SD);
- carddev = qdev_new(TYPE_SD_CARD);
- if (dinfo) {
- if (!qdev_prop_set_drive_err(carddev, "drive",
- blk_by_legacy_dinfo(dinfo), errp)) {
- goto fail;
- }
- }
-
- if (!object_property_set_bool(OBJECT(carddev), "spi", true, errp)) {
- goto fail;
- }
-
- if (!qdev_realize_and_unref(carddev, BUS(&s->sdbus), errp)) {
- goto fail;
- }
-
- return;
-
-fail:
- error_prepend(errp, "failed to init SD card: ");
}
static void ssi_sd_reset(DeviceState *dev)
@@ -426,7 +399,7 @@ static void ssi_sd_class_init(ObjectClass *klass, void *data)
k->cs_polarity = SSI_CS_LOW;
dc->vmsd = &vmstate_ssi_sd;
dc->reset = ssi_sd_reset;
- /* Reason: init() method uses drive_get_next() */
+ /* Reason: GPIO chip-select line should be wired up */
dc->user_creatable = false;
}
diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c
index f3e42d0326..ccad2c43a3 100644
--- a/hw/sparc64/niagara.c
+++ b/hw/sparc64/niagara.c
@@ -98,7 +98,7 @@ static void add_rom_or_fail(const char *file, const hwaddr addr)
static void niagara_init(MachineState *machine)
{
NiagaraBoardState *s = g_new(NiagaraBoardState, 1);
- DriveInfo *dinfo = drive_get_next(IF_PFLASH);
+ DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
MemoryRegion *sysmem = get_system_memory();
/* init CPUs */
diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events
index 650e521e35..f7ad6be5fb 100644
--- a/hw/virtio/trace-events
+++ b/hw/virtio/trace-events
@@ -91,8 +91,7 @@ virtio_mmio_setting_irq(int level) "virtio_mmio setting IRQ %d"
virtio_iommu_device_reset(void) "reset!"
virtio_iommu_get_features(uint64_t features) "device supports features=0x%"PRIx64
virtio_iommu_device_status(uint8_t status) "driver status = %d"
-virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_range, uint32_t probe_size) "page_size_mask=0x%"PRIx64" start=0x%"PRIx64" end=0x%"PRIx64" domain_range=%d probe_size=0x%x"
-virtio_iommu_set_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_range, uint32_t probe_size) "page_size_mask=0x%"PRIx64" start=0x%"PRIx64" end=0x%"PRIx64" domain_bits=%d probe_size=0x%x"
+virtio_iommu_get_config(uint64_t page_size_mask, uint64_t start, uint64_t end, uint32_t domain_start, uint32_t domain_end, uint32_t probe_size) "page_size_mask=0x%"PRIx64" input range start=0x%"PRIx64" input range end=0x%"PRIx64" domain range start=%d domain range end=%d probe_size=0x%x"
virtio_iommu_attach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
virtio_iommu_detach(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d"
virtio_iommu_map(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 " phys_start=0x%"PRIx64" flags=%d"
diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c
index a160ae6b41..6a1df7fe50 100644
--- a/hw/virtio/virtio-iommu-pci.c
+++ b/hw/virtio/virtio-iommu-pci.c
@@ -48,16 +48,8 @@ static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
VirtIOIOMMU *s = VIRTIO_IOMMU(vdev);
if (!qdev_get_machine_hotplug_handler(DEVICE(vpci_dev))) {
- MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
-
- error_setg(errp,
- "%s machine fails to create iommu-map device tree bindings",
- mc->name);
- error_append_hint(errp,
- "Check your machine implements a hotplug handler "
- "for the virtio-iommu-pci device\n");
- error_append_hint(errp, "Check the guest is booted without FW or with "
- "-no-acpi\n");
+ error_setg(errp, "Check your machine implements a hotplug handler "
+ "for the virtio-iommu-pci device");
return;
}
for (int i = 0; i < s->nb_reserved_regions; i++) {
diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c
index 1b23e8e18c..aa9c16a17b 100644
--- a/hw/virtio/virtio-iommu.c
+++ b/hw/virtio/virtio-iommu.c
@@ -822,27 +822,22 @@ unlock:
static void virtio_iommu_get_config(VirtIODevice *vdev, uint8_t *config_data)
{
VirtIOIOMMU *dev = VIRTIO_IOMMU(vdev);
- struct virtio_iommu_config *config = &dev->config;
-
- trace_virtio_iommu_get_config(config->page_size_mask,
- config->input_range.start,
- config->input_range.end,
- config->domain_range.end,
- config->probe_size);
- memcpy(config_data, &dev->config, sizeof(struct virtio_iommu_config));
-}
-
-static void virtio_iommu_set_config(VirtIODevice *vdev,
- const uint8_t *config_data)
-{
- struct virtio_iommu_config config;
-
- memcpy(&config, config_data, sizeof(struct virtio_iommu_config));
- trace_virtio_iommu_set_config(config.page_size_mask,
- config.input_range.start,
- config.input_range.end,
- config.domain_range.end,
- config.probe_size);
+ struct virtio_iommu_config *dev_config = &dev->config;
+ struct virtio_iommu_config *out_config = (void *)config_data;
+
+ out_config->page_size_mask = cpu_to_le64(dev_config->page_size_mask);
+ out_config->input_range.start = cpu_to_le64(dev_config->input_range.start);
+ out_config->input_range.end = cpu_to_le64(dev_config->input_range.end);
+ out_config->domain_range.start = cpu_to_le32(dev_config->domain_range.start);
+ out_config->domain_range.end = cpu_to_le32(dev_config->domain_range.end);
+ out_config->probe_size = cpu_to_le32(dev_config->probe_size);
+
+ trace_virtio_iommu_get_config(dev_config->page_size_mask,
+ dev_config->input_range.start,
+ dev_config->input_range.end,
+ dev_config->domain_range.start,
+ dev_config->domain_range.end,
+ dev_config->probe_size);
}
static uint64_t virtio_iommu_get_features(VirtIODevice *vdev, uint64_t f,
@@ -983,8 +978,8 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp)
s->event_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, NULL);
s->config.page_size_mask = TARGET_PAGE_MASK;
- s->config.input_range.end = -1UL;
- s->config.domain_range.end = 32;
+ s->config.input_range.end = UINT64_MAX;
+ s->config.domain_range.end = UINT32_MAX;
s->config.probe_size = VIOMMU_PROBE_SIZE;
virtio_add_feature(&s->features, VIRTIO_RING_F_EVENT_IDX);
@@ -1185,7 +1180,6 @@ static void virtio_iommu_class_init(ObjectClass *klass, void *data)
vdc->unrealize = virtio_iommu_device_unrealize;
vdc->reset = virtio_iommu_device_reset;
vdc->get_config = virtio_iommu_get_config;
- vdc->set_config = virtio_iommu_set_config;
vdc->get_features = virtio_iommu_get_features;
vdc->set_status = virtio_iommu_set_status;
vdc->vmsd = &vmstate_virtio_iommu_device;
diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h
index 4d9c732d4b..efcbd926fd 100644
--- a/include/hw/i386/microvm.h
+++ b/include/hw/i386/microvm.h
@@ -18,7 +18,6 @@
#ifndef HW_I386_MICROVM_H
#define HW_I386_MICROVM_H
-#include "qemu-common.h"
#include "exec/hwaddr.h"
#include "qemu/notify.h"
diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h
index bb1cfb8896..a145a30370 100644
--- a/include/hw/i386/x86.h
+++ b/include/hw/i386/x86.h
@@ -17,7 +17,6 @@
#ifndef HW_I386_X86_H
#define HW_I386_X86_H
-#include "qemu-common.h"
#include "exec/hwaddr.h"
#include "qemu/notify.h"
diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h
index a94d350034..30691a6e57 100644
--- a/include/hw/pci/pci_bridge.h
+++ b/include/hw/pci/pci_bridge.h
@@ -138,6 +138,7 @@ typedef struct PCIBridgeQemuCap {
uint64_t mem_pref_64; /* Prefetchable memory to reserve (64-bit MMIO) */
} PCIBridgeQemuCap;
+#define REDHAT_PCI_CAP_TYPE_OFFSET 3
#define REDHAT_PCI_CAP_RESOURCE_RESERVE 1
/*
@@ -152,6 +153,13 @@ typedef struct PCIResReserve {
uint64_t mem_pref_64;
} PCIResReserve;
+#define REDHAT_PCI_CAP_RES_RESERVE_BUS_RES 4
+#define REDHAT_PCI_CAP_RES_RESERVE_IO 8
+#define REDHAT_PCI_CAP_RES_RESERVE_MEM 16
+#define REDHAT_PCI_CAP_RES_RESERVE_PREF_MEM_32 20
+#define REDHAT_PCI_CAP_RES_RESERVE_PREF_MEM_64 24
+#define REDHAT_PCI_CAP_RES_RESERVE_CAP_SIZE 32
+
int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset,
PCIResReserve res_reserve, Error **errp);
diff --git a/include/migration/colo.h b/include/migration/colo.h
index 768e1f04c3..5fbe1a6d5d 100644
--- a/include/migration/colo.h
+++ b/include/migration/colo.h
@@ -37,4 +37,5 @@ COLOMode get_colo_mode(void);
void colo_do_failover(void);
void colo_checkpoint_notify(void *opaque);
+void colo_shutdown(void);
#endif
diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h
index 32c2d6023c..a750f99b79 100644
--- a/include/sysemu/blockdev.h
+++ b/include/sysemu/blockdev.h
@@ -50,7 +50,6 @@ void drive_check_orphaned(void);
DriveInfo *drive_get_by_index(BlockInterfaceType type, int index);
int drive_get_max_bus(BlockInterfaceType type);
int drive_get_max_devs(BlockInterfaceType type);
-DriveInfo *drive_get_next(BlockInterfaceType type);
QemuOpts *drive_def(const char *optstr);
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c
index 97e0728b67..f9f3473288 100644
--- a/linux-user/aarch64/cpu_loop.c
+++ b/linux-user/aarch64/cpu_loop.c
@@ -113,27 +113,35 @@ void cpu_loop(CPUARMState *env)
break;
case EXCP_PREFETCH_ABORT:
case EXCP_DATA_ABORT:
- /* We should only arrive here with EC in {DATAABORT, INSNABORT}. */
ec = syn_get_ec(env->exception.syndrome);
- assert(ec == EC_DATAABORT || ec == EC_INSNABORT);
-
- /* Both EC have the same format for FSC, or close enough. */
- fsc = extract32(env->exception.syndrome, 0, 6);
- switch (fsc) {
- case 0x04 ... 0x07: /* Translation fault, level {0-3} */
- si_signo = TARGET_SIGSEGV;
- si_code = TARGET_SEGV_MAPERR;
+ switch (ec) {
+ case EC_DATAABORT:
+ case EC_INSNABORT:
+ /* Both EC have the same format for FSC, or close enough. */
+ fsc = extract32(env->exception.syndrome, 0, 6);
+ switch (fsc) {
+ case 0x04 ... 0x07: /* Translation fault, level {0-3} */
+ si_signo = TARGET_SIGSEGV;
+ si_code = TARGET_SEGV_MAPERR;
+ break;
+ case 0x09 ... 0x0b: /* Access flag fault, level {1-3} */
+ case 0x0d ... 0x0f: /* Permission fault, level {1-3} */
+ si_signo = TARGET_SIGSEGV;
+ si_code = TARGET_SEGV_ACCERR;
+ break;
+ case 0x11: /* Synchronous Tag Check Fault */
+ si_signo = TARGET_SIGSEGV;
+ si_code = TARGET_SEGV_MTESERR;
+ break;
+ case 0x21: /* Alignment fault */
+ si_signo = TARGET_SIGBUS;
+ si_code = TARGET_BUS_ADRALN;
+ break;
+ default:
+ g_assert_not_reached();
+ }
break;
- case 0x09 ... 0x0b: /* Access flag fault, level {1-3} */
- case 0x0d ... 0x0f: /* Permission fault, level {1-3} */
- si_signo = TARGET_SIGSEGV;
- si_code = TARGET_SEGV_ACCERR;
- break;
- case 0x11: /* Synchronous Tag Check Fault */
- si_signo = TARGET_SIGSEGV;
- si_code = TARGET_SEGV_MTESERR;
- break;
- case 0x21: /* Alignment fault */
+ case EC_PCALIGNMENT:
si_signo = TARGET_SIGBUS;
si_code = TARGET_BUS_ADRALN;
break;
diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c
index 6b24cbaba9..e47f8348d5 100644
--- a/linux-user/hexagon/cpu_loop.c
+++ b/linux-user/hexagon/cpu_loop.c
@@ -19,6 +19,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu-common.h"
#include "qemu.h"
#include "user-internals.h"
#include "cpu_loop-common.h"
diff --git a/meson.build b/meson.build
index 96de1a6ef9..ae67ca28ab 100644
--- a/meson.build
+++ b/meson.build
@@ -874,11 +874,15 @@ if not get_option('glusterfs').auto() or have_block
''', dependencies: glusterfs)
endif
endif
+
libssh = not_found
-if 'CONFIG_LIBSSH' in config_host
- libssh = declare_dependency(compile_args: config_host['LIBSSH_CFLAGS'].split(),
- link_args: config_host['LIBSSH_LIBS'].split())
+if not get_option('libssh').auto() or have_block
+ libssh = dependency('libssh', version: '>=0.8.7',
+ method: 'pkg-config',
+ required: get_option('libssh'),
+ kwargs: static_kwargs)
endif
+
libbzip2 = not_found
if not get_option('bzip2').auto() or have_block
libbzip2 = cc.find_library('bz2', has_headers: ['bzlib.h'],
@@ -1451,6 +1455,7 @@ config_host_data.set('CONFIG_EBPF', libbpf.found())
config_host_data.set('CONFIG_LIBDAXCTL', libdaxctl.found())
config_host_data.set('CONFIG_LIBISCSI', libiscsi.found())
config_host_data.set('CONFIG_LIBNFS', libnfs.found())
+config_host_data.set('CONFIG_LIBSSH', libssh.found())
config_host_data.set('CONFIG_LINUX_AIO', libaio.found())
config_host_data.set('CONFIG_LINUX_IO_URING', linux_io_uring.found())
config_host_data.set('CONFIG_LIBPMEM', libpmem.found())
@@ -3430,7 +3435,7 @@ endif
summary_info += {'seccomp support': seccomp}
summary_info += {'GlusterFS support': glusterfs}
summary_info += {'TPM support': config_host.has_key('CONFIG_TPM')}
-summary_info += {'libssh support': config_host.has_key('CONFIG_LIBSSH')}
+summary_info += {'libssh support': libssh}
summary_info += {'lzo support': lzo}
summary_info += {'snappy support': snappy}
summary_info += {'bzip2 support': libbzip2}
diff --git a/meson_options.txt b/meson_options.txt
index e392323732..4114bfcaa4 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -105,6 +105,8 @@ option('libdaxctl', type : 'feature', value : 'auto',
description: 'libdaxctl support')
option('libpmem', type : 'feature', value : 'auto',
description: 'libpmem support')
+option('libssh', type : 'feature', value : 'auto',
+ description: 'ssh block device support')
option('libudev', type : 'feature', value : 'auto',
description: 'Use libudev to enumerate host devices')
option('libusb', type : 'feature', value : 'auto',
diff --git a/migration/colo.c b/migration/colo.c
index 2415325262..5f7071b3cd 100644
--- a/migration/colo.c
+++ b/migration/colo.c
@@ -530,7 +530,6 @@ static void colo_process_checkpoint(MigrationState *s)
{
QIOChannelBuffer *bioc;
QEMUFile *fb = NULL;
- int64_t current_time = qemu_clock_get_ms(QEMU_CLOCK_HOST);
Error *local_err = NULL;
int ret;
@@ -578,8 +577,8 @@ static void colo_process_checkpoint(MigrationState *s)
qemu_mutex_unlock_iothread();
trace_colo_vm_state_change("stop", "run");
- timer_mod(s->colo_delay_timer,
- current_time + s->parameters.x_checkpoint_delay);
+ timer_mod(s->colo_delay_timer, qemu_clock_get_ms(QEMU_CLOCK_HOST) +
+ s->parameters.x_checkpoint_delay);
while (s->state == MIGRATION_STATUS_COLO) {
if (failover_get_state() != FAILOVER_STATUS_NONE) {
@@ -667,8 +666,6 @@ void migrate_start_colo_process(MigrationState *s)
colo_checkpoint_notify, s);
qemu_sem_init(&s->colo_exit_sem, 0);
- migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE,
- MIGRATION_STATUS_COLO);
colo_process_checkpoint(s);
qemu_mutex_lock_iothread();
}
@@ -683,8 +680,8 @@ static void colo_incoming_process_checkpoint(MigrationIncomingState *mis,
qemu_mutex_lock_iothread();
vm_stop_force_state(RUN_STATE_COLO);
- trace_colo_vm_state_change("run", "stop");
qemu_mutex_unlock_iothread();
+ trace_colo_vm_state_change("run", "stop");
/* FIXME: This is unnecessary for periodic checkpoint mode */
colo_send_message(mis->to_src_file, COLO_MESSAGE_CHECKPOINT_REPLY,
@@ -786,8 +783,8 @@ static void colo_incoming_process_checkpoint(MigrationIncomingState *mis,
vmstate_loading = false;
vm_start();
- trace_colo_vm_state_change("stop", "run");
qemu_mutex_unlock_iothread();
+ trace_colo_vm_state_change("stop", "run");
if (failover_get_state() == FAILOVER_STATUS_RELAUNCH) {
return;
@@ -820,6 +817,26 @@ static void colo_wait_handle_message(MigrationIncomingState *mis,
}
}
+void colo_shutdown(void)
+{
+ MigrationIncomingState *mis = NULL;
+ MigrationState *s = NULL;
+
+ switch (get_colo_mode()) {
+ case COLO_MODE_PRIMARY:
+ s = migrate_get_current();
+ qemu_event_set(&s->colo_checkpoint_event);
+ qemu_sem_post(&s->colo_exit_sem);
+ break;
+ case COLO_MODE_SECONDARY:
+ mis = migration_incoming_get_current();
+ qemu_sem_post(&mis->colo_incoming_sem);
+ break;
+ default:
+ break;
+ }
+}
+
void *colo_process_incoming_thread(void *opaque)
{
MigrationIncomingState *mis = opaque;
@@ -870,8 +887,8 @@ void *colo_process_incoming_thread(void *opaque)
abort();
#endif
vm_start();
- trace_colo_vm_state_change("stop", "run");
qemu_mutex_unlock_iothread();
+ trace_colo_vm_state_change("stop", "run");
colo_send_message(mis->to_src_file, COLO_MESSAGE_CHECKPOINT_READY,
&local_err);
diff --git a/migration/migration.c b/migration/migration.c
index abaf6f9e3d..3de11ae921 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -226,6 +226,12 @@ void migration_cancel(const Error *error)
void migration_shutdown(void)
{
/*
+ * When the QEMU main thread exit, the COLO thread
+ * may wait a semaphore. So, we should wakeup the
+ * COLO thread before migration shutdown.
+ */
+ colo_shutdown();
+ /*
* Cancel the current migration - that will (eventually)
* stop the migration using this structure
*/
@@ -990,6 +996,8 @@ static void populate_time_info(MigrationInfo *info, MigrationState *s)
static void populate_ram_info(MigrationInfo *info, MigrationState *s)
{
+ size_t page_size = qemu_target_page_size();
+
info->has_ram = true;
info->ram = g_malloc0(sizeof(*info->ram));
info->ram->transferred = ram_counters.transferred;
@@ -998,12 +1006,11 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s)
/* legacy value. It is not used anymore */
info->ram->skipped = 0;
info->ram->normal = ram_counters.normal;
- info->ram->normal_bytes = ram_counters.normal *
- qemu_target_page_size();
+ info->ram->normal_bytes = ram_counters.normal * page_size;
info->ram->mbps = s->mbps;
info->ram->dirty_sync_count = ram_counters.dirty_sync_count;
info->ram->postcopy_requests = ram_counters.postcopy_requests;
- info->ram->page_size = qemu_target_page_size();
+ info->ram->page_size = page_size;
info->ram->multifd_bytes = ram_counters.multifd_bytes;
info->ram->pages_per_second = s->pages_per_second;
@@ -3607,12 +3614,7 @@ static void migration_iteration_finish(MigrationState *s)
migration_calculate_complete(s);
runstate_set(RUN_STATE_POSTMIGRATE);
break;
-
- case MIGRATION_STATUS_ACTIVE:
- /*
- * We should really assert here, but since it's during
- * migration, let's try to reduce the usage of assertions.
- */
+ case MIGRATION_STATUS_COLO:
if (!migrate_colo_enabled()) {
error_report("%s: critical error: calling COLO code without "
"COLO enabled", __func__);
@@ -3622,6 +3624,12 @@ static void migration_iteration_finish(MigrationState *s)
* Fixme: we will run VM in COLO no matter its old running state.
* After exited COLO, we will keep running.
*/
+ /* Fallthrough */
+ case MIGRATION_STATUS_ACTIVE:
+ /*
+ * We should really assert here, but since it's during
+ * migration, let's try to reduce the usage of assertions.
+ */
s->vm_was_running = true;
/* Fallthrough */
case MIGRATION_STATUS_FAILED:
diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c
index ab4ba75d75..da6201704c 100644
--- a/migration/multifd-zlib.c
+++ b/migration/multifd-zlib.c
@@ -13,6 +13,7 @@
#include "qemu/osdep.h"
#include <zlib.h>
#include "qemu/rcu.h"
+#include "exec/ramblock.h"
#include "exec/target_page.h"
#include "qapi/error.h"
#include "migration.h"
@@ -42,7 +43,6 @@ struct zlib_data {
*/
static int zlib_send_setup(MultiFDSendParams *p, Error **errp)
{
- uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
struct zlib_data *z = g_malloc0(sizeof(struct zlib_data));
z_stream *zs = &z->zs;
@@ -54,9 +54,8 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp)
error_setg(errp, "multifd %d: deflate init failed", p->id);
return -1;
}
- /* We will never have more than page_count pages */
- z->zbuff_len = page_count * qemu_target_page_size();
- z->zbuff_len *= 2;
+ /* To be safe, we reserve twice the size of the packet */
+ z->zbuff_len = MULTIFD_PACKET_SIZE * 2;
z->zbuff = g_try_malloc(z->zbuff_len);
if (!z->zbuff) {
deflateEnd(&z->zs);
@@ -74,6 +73,7 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp)
* Close the channel and return memory.
*
* @p: Params for the channel that we are using
+ * @errp: pointer to an error
*/
static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp)
{
@@ -95,27 +95,27 @@ static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp)
* Returns 0 for success or -1 for error
*
* @p: Params for the channel that we are using
- * @used: number of pages used
+ * @errp: pointer to an error
*/
-static int zlib_send_prepare(MultiFDSendParams *p, uint32_t used, Error **errp)
+static int zlib_send_prepare(MultiFDSendParams *p, Error **errp)
{
- struct iovec *iov = p->pages->iov;
struct zlib_data *z = p->data;
+ size_t page_size = qemu_target_page_size();
z_stream *zs = &z->zs;
uint32_t out_size = 0;
int ret;
uint32_t i;
- for (i = 0; i < used; i++) {
+ for (i = 0; i < p->pages->num; i++) {
uint32_t available = z->zbuff_len - out_size;
int flush = Z_NO_FLUSH;
- if (i == used - 1) {
+ if (i == p->pages->num - 1) {
flush = Z_SYNC_FLUSH;
}
- zs->avail_in = iov[i].iov_len;
- zs->next_in = iov[i].iov_base;
+ zs->avail_in = page_size;
+ zs->next_in = p->pages->block->host + p->pages->offset[i];
zs->avail_out = available;
zs->next_out = z->zbuff + out_size;
@@ -180,7 +180,6 @@ static int zlib_send_write(MultiFDSendParams *p, uint32_t used, Error **errp)
*/
static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp)
{
- uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
struct zlib_data *z = g_malloc0(sizeof(struct zlib_data));
z_stream *zs = &z->zs;
@@ -194,10 +193,8 @@ static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp)
error_setg(errp, "multifd %d: inflate init failed", p->id);
return -1;
}
- /* We will never have more than page_count pages */
- z->zbuff_len = page_count * qemu_target_page_size();
- /* We know compression "could" use more space */
- z->zbuff_len *= 2;
+ /* To be safe, we reserve twice the size of the packet */
+ z->zbuff_len = MULTIFD_PACKET_SIZE * 2;
z->zbuff = g_try_malloc(z->zbuff_len);
if (!z->zbuff) {
inflateEnd(zs);
@@ -234,17 +231,17 @@ static void zlib_recv_cleanup(MultiFDRecvParams *p)
* Returns 0 for success or -1 for error
*
* @p: Params for the channel that we are using
- * @used: number of pages used
* @errp: pointer to an error
*/
-static int zlib_recv_pages(MultiFDRecvParams *p, uint32_t used, Error **errp)
+static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp)
{
struct zlib_data *z = p->data;
+ size_t page_size = qemu_target_page_size();
z_stream *zs = &z->zs;
uint32_t in_size = p->next_packet_size;
/* we measure the change of total_out */
uint32_t out_size = zs->total_out;
- uint32_t expected_size = used * qemu_target_page_size();
+ uint32_t expected_size = p->pages->num * qemu_target_page_size();
uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK;
int ret;
int i;
@@ -263,17 +260,16 @@ static int zlib_recv_pages(MultiFDRecvParams *p, uint32_t used, Error **errp)
zs->avail_in = in_size;
zs->next_in = z->zbuff;
- for (i = 0; i < used; i++) {
- struct iovec *iov = &p->pages->iov[i];
+ for (i = 0; i < p->pages->num; i++) {
int flush = Z_NO_FLUSH;
unsigned long start = zs->total_out;
- if (i == used - 1) {
+ if (i == p->pages->num - 1) {
flush = Z_SYNC_FLUSH;
}
- zs->avail_out = iov->iov_len;
- zs->next_out = iov->iov_base;
+ zs->avail_out = page_size;
+ zs->next_out = p->pages->block->host + p->pages->offset[i];
/*
* Welcome to inflate semantics
@@ -286,8 +282,8 @@ static int zlib_recv_pages(MultiFDRecvParams *p, uint32_t used, Error **errp)
do {
ret = inflate(zs, flush);
} while (ret == Z_OK && zs->avail_in
- && (zs->total_out - start) < iov->iov_len);
- if (ret == Z_OK && (zs->total_out - start) < iov->iov_len) {
+ && (zs->total_out - start) < page_size);
+ if (ret == Z_OK && (zs->total_out - start) < page_size) {
error_setg(errp, "multifd %d: inflate generated too few output",
p->id);
return -1;
diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c
index 693bddf8c9..2d5b61106c 100644
--- a/migration/multifd-zstd.c
+++ b/migration/multifd-zstd.c
@@ -13,6 +13,7 @@
#include "qemu/osdep.h"
#include <zstd.h>
#include "qemu/rcu.h"
+#include "exec/ramblock.h"
#include "exec/target_page.h"
#include "qapi/error.h"
#include "migration.h"
@@ -47,7 +48,6 @@ struct zstd_data {
*/
static int zstd_send_setup(MultiFDSendParams *p, Error **errp)
{
- uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
struct zstd_data *z = g_new0(struct zstd_data, 1);
int res;
@@ -67,9 +67,8 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp)
p->id, ZSTD_getErrorName(res));
return -1;
}
- /* We will never have more than page_count pages */
- z->zbuff_len = page_count * qemu_target_page_size();
- z->zbuff_len *= 2;
+ /* To be safe, we reserve twice the size of the packet */
+ z->zbuff_len = MULTIFD_PACKET_SIZE * 2;
z->zbuff = g_try_malloc(z->zbuff_len);
if (!z->zbuff) {
ZSTD_freeCStream(z->zcs);
@@ -86,6 +85,7 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp)
* Close the channel and return memory.
*
* @p: Params for the channel that we are using
+ * @errp: pointer to an error
*/
static void zstd_send_cleanup(MultiFDSendParams *p, Error **errp)
{
@@ -108,12 +108,12 @@ static void zstd_send_cleanup(MultiFDSendParams *p, Error **errp)
* Returns 0 for success or -1 for error
*
* @p: Params for the channel that we are using
- * @used: number of pages used
+ * @errp: pointer to an error
*/
-static int zstd_send_prepare(MultiFDSendParams *p, uint32_t used, Error **errp)
+static int zstd_send_prepare(MultiFDSendParams *p, Error **errp)
{
- struct iovec *iov = p->pages->iov;
struct zstd_data *z = p->data;
+ size_t page_size = qemu_target_page_size();
int ret;
uint32_t i;
@@ -121,14 +121,14 @@ static int zstd_send_prepare(MultiFDSendParams *p, uint32_t used, Error **errp)
z->out.size = z->zbuff_len;
z->out.pos = 0;
- for (i = 0; i < used; i++) {
+ for (i = 0; i < p->pages->num; i++) {
ZSTD_EndDirective flush = ZSTD_e_continue;
- if (i == used - 1) {
+ if (i == p->pages->num - 1) {
flush = ZSTD_e_flush;
}
- z->in.src = iov[i].iov_base;
- z->in.size = iov[i].iov_len;
+ z->in.src = p->pages->block->host + p->pages->offset[i];
+ z->in.size = page_size;
z->in.pos = 0;
/*
@@ -191,7 +191,6 @@ static int zstd_send_write(MultiFDSendParams *p, uint32_t used, Error **errp)
*/
static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp)
{
- uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size();
struct zstd_data *z = g_new0(struct zstd_data, 1);
int ret;
@@ -212,10 +211,8 @@ static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp)
return -1;
}
- /* We will never have more than page_count pages */
- z->zbuff_len = page_count * qemu_target_page_size();
- /* We know compression "could" use more space */
- z->zbuff_len *= 2;
+ /* To be safe, we reserve twice the size of the packet */
+ z->zbuff_len = MULTIFD_PACKET_SIZE * 2;
z->zbuff = g_try_malloc(z->zbuff_len);
if (!z->zbuff) {
ZSTD_freeDStream(z->zds);
@@ -254,14 +251,14 @@ static void zstd_recv_cleanup(MultiFDRecvParams *p)
* Returns 0 for success or -1 for error
*
* @p: Params for the channel that we are using
- * @used: number of pages used
* @errp: pointer to an error
*/
-static int zstd_recv_pages(MultiFDRecvParams *p, uint32_t used, Error **errp)
+static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp)
{
uint32_t in_size = p->next_packet_size;
uint32_t out_size = 0;
- uint32_t expected_size = used * qemu_target_page_size();
+ size_t page_size = qemu_target_page_size();
+ uint32_t expected_size = p->pages->num * page_size;
uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK;
struct zstd_data *z = p->data;
int ret;
@@ -282,11 +279,9 @@ static int zstd_recv_pages(MultiFDRecvParams *p, uint32_t used, Error **errp)
z->in.size = in_size;
z->in.pos = 0;
- for (i = 0; i < used; i++) {
- struct iovec *iov = &p->pages->iov[i];
-
- z->out.dst = iov->iov_base;
- z->out.size = iov->iov_len;
+ for (i = 0; i < p->pages->num; i++) {
+ z->out.dst = p->pages->block->host + p->pages->offset[i];
+ z->out.size = page_size;
z->out.pos = 0;
/*
@@ -300,8 +295,8 @@ static int zstd_recv_pages(MultiFDRecvParams *p, uint32_t used, Error **errp)
do {
ret = ZSTD_decompressStream(z->zds, &z->out, &z->in);
} while (ret > 0 && (z->in.size - z->in.pos > 0)
- && (z->out.pos < iov->iov_len));
- if (ret > 0 && (z->out.pos < iov->iov_len)) {
+ && (z->out.pos < page_size));
+ if (ret > 0 && (z->out.pos < page_size)) {
error_setg(errp, "multifd %d: decompressStream buffer too small",
p->id);
return -1;
diff --git a/migration/multifd.c b/migration/multifd.c
index 7c9deb1921..3242f688e5 100644
--- a/migration/multifd.c
+++ b/migration/multifd.c
@@ -66,6 +66,7 @@ static int nocomp_send_setup(MultiFDSendParams *p, Error **errp)
* For no compression this function does nothing.
*
* @p: Params for the channel that we are using
+ * @errp: pointer to an error
*/
static void nocomp_send_cleanup(MultiFDSendParams *p, Error **errp)
{
@@ -81,13 +82,11 @@ static void nocomp_send_cleanup(MultiFDSendParams *p, Error **errp)
* Returns 0 for success or -1 for error
*
* @p: Params for the channel that we are using
- * @used: number of pages used
* @errp: pointer to an error
*/
-static int nocomp_send_prepare(MultiFDSendParams *p, uint32_t used,
- Error **errp)
+static int nocomp_send_prepare(MultiFDSendParams *p, Error **errp)
{
- p->next_packet_size = used * qemu_target_page_size();
+ p->next_packet_size = p->pages->num * qemu_target_page_size();
p->flags |= MULTIFD_FLAG_NOCOMP;
return 0;
}
@@ -142,10 +141,9 @@ static void nocomp_recv_cleanup(MultiFDRecvParams *p)
* Returns 0 for success or -1 for error
*
* @p: Params for the channel that we are using
- * @used: number of pages used
* @errp: pointer to an error
*/
-static int nocomp_recv_pages(MultiFDRecvParams *p, uint32_t used, Error **errp)
+static int nocomp_recv_pages(MultiFDRecvParams *p, Error **errp)
{
uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK;
@@ -154,7 +152,7 @@ static int nocomp_recv_pages(MultiFDRecvParams *p, uint32_t used, Error **errp)
p->id, flags, MULTIFD_FLAG_NOCOMP);
return -1;
}
- return qio_channel_readv_all(p->c, p->pages->iov, used, errp);
+ return qio_channel_readv_all(p->c, p->pages->iov, p->pages->num, errp);
}
static MultiFDMethods multifd_nocomp_ops = {
@@ -252,7 +250,7 @@ static MultiFDPages_t *multifd_pages_init(size_t size)
static void multifd_pages_clear(MultiFDPages_t *pages)
{
- pages->used = 0;
+ pages->num = 0;
pages->allocated = 0;
pages->packet_num = 0;
pages->block = NULL;
@@ -270,7 +268,7 @@ static void multifd_send_fill_packet(MultiFDSendParams *p)
packet->flags = cpu_to_be32(p->flags);
packet->pages_alloc = cpu_to_be32(p->pages->allocated);
- packet->pages_used = cpu_to_be32(p->pages->used);
+ packet->pages_used = cpu_to_be32(p->pages->num);
packet->next_packet_size = cpu_to_be32(p->next_packet_size);
packet->packet_num = cpu_to_be64(p->packet_num);
@@ -278,7 +276,7 @@ static void multifd_send_fill_packet(MultiFDSendParams *p)
strncpy(packet->ramblock, p->pages->block->idstr, 256);
}
- for (i = 0; i < p->pages->used; i++) {
+ for (i = 0; i < p->pages->num; i++) {
/* there are architectures where ram_addr_t is 32 bit */
uint64_t temp = p->pages->offset[i];
@@ -289,7 +287,8 @@ static void multifd_send_fill_packet(MultiFDSendParams *p)
static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp)
{
MultiFDPacket_t *packet = p->packet;
- uint32_t pages_max = MULTIFD_PACKET_SIZE / qemu_target_page_size();
+ size_t page_size = qemu_target_page_size();
+ uint32_t pages_max = MULTIFD_PACKET_SIZE / page_size;
RAMBlock *block;
int i;
@@ -331,18 +330,18 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp)
p->pages = multifd_pages_init(packet->pages_alloc);
}
- p->pages->used = be32_to_cpu(packet->pages_used);
- if (p->pages->used > packet->pages_alloc) {
+ p->pages->num = be32_to_cpu(packet->pages_used);
+ if (p->pages->num > packet->pages_alloc) {
error_setg(errp, "multifd: received packet "
"with %d pages and expected maximum pages are %d",
- p->pages->used, packet->pages_alloc) ;
+ p->pages->num, packet->pages_alloc) ;
return -1;
}
p->next_packet_size = be32_to_cpu(packet->next_packet_size);
p->packet_num = be64_to_cpu(packet->packet_num);
- if (p->pages->used == 0) {
+ if (p->pages->num == 0) {
return 0;
}
@@ -355,17 +354,19 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp)
return -1;
}
- for (i = 0; i < p->pages->used; i++) {
+ p->pages->block = block;
+ for (i = 0; i < p->pages->num; i++) {
uint64_t offset = be64_to_cpu(packet->offset[i]);
- if (offset > (block->used_length - qemu_target_page_size())) {
+ if (offset > (block->used_length - page_size)) {
error_setg(errp, "multifd: offset too long %" PRIu64
" (max " RAM_ADDR_FMT ")",
offset, block->used_length);
return -1;
}
+ p->pages->offset[i] = offset;
p->pages->iov[i].iov_base = block->host + offset;
- p->pages->iov[i].iov_len = qemu_target_page_size();
+ p->pages->iov[i].iov_len = page_size;
}
return 0;
@@ -442,13 +443,13 @@ static int multifd_send_pages(QEMUFile *f)
}
qemu_mutex_unlock(&p->mutex);
}
- assert(!p->pages->used);
+ assert(!p->pages->num);
assert(!p->pages->block);
p->packet_num = multifd_send_state->packet_num++;
multifd_send_state->pages = p->pages;
p->pages = pages;
- transferred = ((uint64_t) pages->used) * qemu_target_page_size()
+ transferred = ((uint64_t) pages->num) * qemu_target_page_size()
+ p->packet_len;
qemu_file_update_transfer(f, transferred);
ram_counters.multifd_bytes += transferred;
@@ -468,12 +469,12 @@ int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset)
}
if (pages->block == block) {
- pages->offset[pages->used] = offset;
- pages->iov[pages->used].iov_base = block->host + offset;
- pages->iov[pages->used].iov_len = qemu_target_page_size();
- pages->used++;
+ pages->offset[pages->num] = offset;
+ pages->iov[pages->num].iov_base = block->host + offset;
+ pages->iov[pages->num].iov_len = qemu_target_page_size();
+ pages->num++;
- if (pages->used < pages->allocated) {
+ if (pages->num < pages->allocated) {
return 1;
}
}
@@ -523,6 +524,9 @@ static void multifd_send_terminate_threads(Error *err)
qemu_mutex_lock(&p->mutex);
p->quit = true;
qemu_sem_post(&p->sem);
+ if (p->c) {
+ qio_channel_shutdown(p->c, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
+ }
qemu_mutex_unlock(&p->mutex);
}
}
@@ -585,7 +589,7 @@ void multifd_send_sync_main(QEMUFile *f)
if (!migrate_use_multifd()) {
return;
}
- if (multifd_send_state->pages->used) {
+ if (multifd_send_state->pages->num) {
if (multifd_send_pages(f) < 0) {
error_report("%s: multifd_send_pages fail", __func__);
return;
@@ -627,7 +631,6 @@ static void *multifd_send_thread(void *opaque)
MultiFDSendParams *p = opaque;
Error *local_err = NULL;
int ret = 0;
- uint32_t flags = 0;
trace_multifd_send_thread_start(p->id);
rcu_register_thread();
@@ -648,13 +651,12 @@ static void *multifd_send_thread(void *opaque)
qemu_mutex_lock(&p->mutex);
if (p->pending_job) {
- uint32_t used = p->pages->used;
+ uint32_t used = p->pages->num;
uint64_t packet_num = p->packet_num;
- flags = p->flags;
+ uint32_t flags = p->flags;
if (used) {
- ret = multifd_send_state->ops->send_prepare(p, used,
- &local_err);
+ ret = multifd_send_state->ops->send_prepare(p, &local_err);
if (ret != 0) {
qemu_mutex_unlock(&p->mutex);
break;
@@ -664,7 +666,7 @@ static void *multifd_send_thread(void *opaque)
p->flags = 0;
p->num_packets++;
p->num_pages += used;
- p->pages->used = 0;
+ p->pages->num = 0;
p->pages->block = NULL;
qemu_mutex_unlock(&p->mutex);
@@ -1090,7 +1092,7 @@ static void *multifd_recv_thread(void *opaque)
break;
}
- used = p->pages->used;
+ used = p->pages->num;
flags = p->flags;
/* recv methods don't know how to handle the SYNC flag */
p->flags &= ~MULTIFD_FLAG_SYNC;
@@ -1101,7 +1103,7 @@ static void *multifd_recv_thread(void *opaque)
qemu_mutex_unlock(&p->mutex);
if (used) {
- ret = multifd_recv_state->ops->recv_pages(p, used, &local_err);
+ ret = multifd_recv_state->ops->recv_pages(p, &local_err);
if (ret != 0) {
break;
}
diff --git a/migration/multifd.h b/migration/multifd.h
index 15c50ca0b2..e57adc783b 100644
--- a/migration/multifd.h
+++ b/migration/multifd.h
@@ -55,7 +55,7 @@ typedef struct {
typedef struct {
/* number of used pages */
- uint32_t used;
+ uint32_t num;
/* number of allocated pages */
uint32_t allocated;
/* global number of generated multifd packets */
@@ -159,7 +159,7 @@ typedef struct {
/* Cleanup for sending side */
void (*send_cleanup)(MultiFDSendParams *p, Error **errp);
/* Prepare the send packet */
- int (*send_prepare)(MultiFDSendParams *p, uint32_t used, Error **errp);
+ int (*send_prepare)(MultiFDSendParams *p, Error **errp);
/* Write the send packet */
int (*send_write)(MultiFDSendParams *p, uint32_t used, Error **errp);
/* Setup for receiving side */
@@ -167,7 +167,7 @@ typedef struct {
/* Cleanup for receiving side */
void (*recv_cleanup)(MultiFDRecvParams *p);
/* Read all pages */
- int (*recv_pages)(MultiFDRecvParams *p, uint32_t used, Error **errp);
+ int (*recv_pages)(MultiFDRecvParams *p, Error **errp);
} MultiFDMethods;
void multifd_register_ops(int method, MultiFDMethods *ops);
diff --git a/migration/ram.c b/migration/ram.c
index 863035d235..57efa67f20 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -81,11 +81,6 @@
/* 0x80 is reserved in migration.h start with 0x100 next */
#define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100
-static inline bool is_zero_range(uint8_t *p, uint64_t size)
-{
- return buffer_is_zero(p, size);
-}
-
XBZRLECacheStats xbzrle_counters;
/* struct contains XBZRLE cache and a static page
@@ -1180,7 +1175,7 @@ static int save_zero_page_to_file(RAMState *rs, QEMUFile *file,
uint8_t *p = block->host + offset;
int len = 0;
- if (is_zero_range(p, TARGET_PAGE_SIZE)) {
+ if (buffer_is_zero(p, TARGET_PAGE_SIZE)) {
len += save_page_header(rs, file, block, offset | RAM_SAVE_FLAG_ZERO);
qemu_put_byte(file, 0);
len += 1;
@@ -3367,7 +3362,7 @@ static inline void *colo_cache_from_block_offset(RAMBlock *block,
*/
void ram_handle_compressed(void *host, uint8_t ch, uint64_t size)
{
- if (ch != 0 || !is_zero_range(host, size)) {
+ if (ch != 0 || !buffer_is_zero(host, size)) {
memset(host, ch, size);
}
}
@@ -3918,7 +3913,6 @@ void colo_flush_ram_cache(void)
unsigned long offset = 0;
memory_global_dirty_log_sync();
- qemu_mutex_lock(&ram_state->bitmap_mutex);
WITH_RCU_READ_LOCK_GUARD() {
RAMBLOCK_FOREACH_NOT_IGNORED(block) {
ramblock_sync_dirty_bitmap(ram_state, block);
@@ -3954,7 +3948,6 @@ void colo_flush_ram_cache(void)
}
}
trace_colo_flush_ram_cache_end();
- qemu_mutex_unlock(&ram_state->bitmap_mutex);
}
/**
diff --git a/migration/savevm.c b/migration/savevm.c
index d59e976d50..0bef031acb 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1685,6 +1685,7 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis,
{
PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_ADVISE);
uint64_t remote_pagesize_summary, local_pagesize_summary, remote_tps;
+ size_t page_size = qemu_target_page_size();
Error *local_err = NULL;
trace_loadvm_postcopy_handle_advise();
@@ -1741,13 +1742,13 @@ static int loadvm_postcopy_handle_advise(MigrationIncomingState *mis,
}
remote_tps = qemu_get_be64(mis->from_src_file);
- if (remote_tps != qemu_target_page_size()) {
+ if (remote_tps != page_size) {
/*
* Again, some differences could be dealt with, but for now keep it
* simple.
*/
error_report("Postcopy needs matching target page sizes (s=%d d=%zd)",
- (int)remote_tps, qemu_target_page_size());
+ (int)remote_tps, page_size);
return -1;
}
diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin
index dea01da468..26f863b609 100644
--- a/pc-bios/bios-256k.bin
+++ b/pc-bios/bios-256k.bin
Binary files differ
diff --git a/pc-bios/bios-microvm.bin b/pc-bios/bios-microvm.bin
index ca6375d65c..ddeb24391d 100644
--- a/pc-bios/bios-microvm.bin
+++ b/pc-bios/bios-microvm.bin
Binary files differ
diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin
index e9fa99f006..6e6cde8c7f 100644
--- a/pc-bios/bios.bin
+++ b/pc-bios/bios.bin
Binary files differ
diff --git a/pc-bios/vgabios-ati.bin b/pc-bios/vgabios-ati.bin
index 0de9c0d3c1..fe497ea4f2 100644
--- a/pc-bios/vgabios-ati.bin
+++ b/pc-bios/vgabios-ati.bin
Binary files differ
diff --git a/pc-bios/vgabios-bochs-display.bin b/pc-bios/vgabios-bochs-display.bin
index 76e076f029..7ca04402e9 100644
--- a/pc-bios/vgabios-bochs-display.bin
+++ b/pc-bios/vgabios-bochs-display.bin
Binary files differ
diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin
index 0f16ad199d..020dfe6c56 100644
--- a/pc-bios/vgabios-cirrus.bin
+++ b/pc-bios/vgabios-cirrus.bin
Binary files differ
diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin
index 420f9ed331..0cc20678a3 100644
--- a/pc-bios/vgabios-qxl.bin
+++ b/pc-bios/vgabios-qxl.bin
Binary files differ
diff --git a/pc-bios/vgabios-ramfb.bin b/pc-bios/vgabios-ramfb.bin
index b9b2e230a5..a7c5ae7c8f 100644
--- a/pc-bios/vgabios-ramfb.bin
+++ b/pc-bios/vgabios-ramfb.bin
Binary files differ
diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin
index b59256572d..abeea373d3 100644
--- a/pc-bios/vgabios-stdvga.bin
+++ b/pc-bios/vgabios-stdvga.bin
Binary files differ
diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin
index c00414ab83..806647a009 100644
--- a/pc-bios/vgabios-virtio.bin
+++ b/pc-bios/vgabios-virtio.bin
Binary files differ
diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin
index 5454aceda8..39e3093667 100644
--- a/pc-bios/vgabios-vmware.bin
+++ b/pc-bios/vgabios-vmware.bin
Binary files differ
diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin
index ae82887d73..c3a66af50e 100644
--- a/pc-bios/vgabios.bin
+++ b/pc-bios/vgabios.bin
Binary files differ
diff --git a/roms/seabios b/roms/seabios
-Subproject 64f37cc530f144e53c190c9e8209a51b58fd5c4
+Subproject 2dd4b9b3f84019668719344b40dba79d681be41
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 7a17ff4218..ae8f18edc2 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -53,6 +53,7 @@ meson_options_help() {
printf "%s\n" ' libiscsi libiscsi userspace initiator'
printf "%s\n" ' libnfs libnfs block device driver'
printf "%s\n" ' libpmem libpmem support'
+ printf "%s\n" ' libssh ssh block device support'
printf "%s\n" ' libudev Use libudev to enumerate host devices'
printf "%s\n" ' libusb libusb support for USB passthrough'
printf "%s\n" ' libxml2 libxml2 support for Parallels image format'
@@ -177,6 +178,8 @@ _meson_option_parse() {
--disable-libnfs) printf "%s" -Dlibnfs=disabled ;;
--enable-libpmem) printf "%s" -Dlibpmem=enabled ;;
--disable-libpmem) printf "%s" -Dlibpmem=disabled ;;
+ --enable-libssh) printf "%s" -Dlibssh=enabled ;;
+ --disable-libssh) printf "%s" -Dlibssh=disabled ;;
--enable-libudev) printf "%s" -Dlibudev=enabled ;;
--disable-libudev) printf "%s" -Dlibudev=disabled ;;
--enable-libusb) printf "%s" -Dlibusb=enabled ;;
diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c
index 2983e36dd3..32f3caec23 100644
--- a/target/arm/debug_helper.c
+++ b/target/arm/debug_helper.c
@@ -220,6 +220,7 @@ bool arm_debug_check_breakpoint(CPUState *cs)
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
+ target_ulong pc;
int n;
/*
@@ -231,6 +232,28 @@ bool arm_debug_check_breakpoint(CPUState *cs)
return false;
}
+ /*
+ * Single-step exceptions have priority over breakpoint exceptions.
+ * If single-step state is active-pending, suppress the bp.
+ */
+ if (arm_singlestep_active(env) && !(env->pstate & PSTATE_SS)) {
+ return false;
+ }
+
+ /*
+ * PC alignment faults have priority over breakpoint exceptions.
+ */
+ pc = is_a64(env) ? env->pc : env->regs[15];
+ if ((is_a64(env) || !env->thumb) && (pc & 3) != 0) {
+ return false;
+ }
+
+ /*
+ * Instruction aborts have priority over breakpoint exceptions.
+ * TODO: We would need to look up the page for PC and verify that
+ * it is present and executable.
+ */
+
for (n = 0; n < ARRAY_SIZE(env->cpu_breakpoint); n++) {
if (bp_wp_matches(cpu, n, false)) {
return true;
diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c
index 134da0d0ae..ca1de47511 100644
--- a/target/arm/gdbstub.c
+++ b/target/arm/gdbstub.c
@@ -77,8 +77,13 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
tmp = ldl_p(mem_buf);
- /* Mask out low bit of PC to workaround gdb bugs. This will probably
- cause problems if we ever implement the Jazelle DBX extensions. */
+ /*
+ * Mask out low bits of PC to workaround gdb bugs.
+ * This avoids an assert in thumb_tr_translate_insn, because it is
+ * architecturally impossible to misalign the pc.
+ * This will probably cause problems if we ever implement the
+ * Jazelle DBX extensions.
+ */
if (n == 15) {
tmp &= ~1;
}
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 9b317899a6..db837d53bd 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -4519,18 +4519,18 @@ static uint64_t tlbi_aa64_range_get_length(CPUARMState *env,
uint64_t exponent;
uint64_t length;
- num = extract64(value, 39, 4);
+ num = extract64(value, 39, 5);
scale = extract64(value, 44, 2);
page_size_granule = extract64(value, 46, 2);
- page_shift = page_size_granule * 2 + 12;
-
if (page_size_granule == 0) {
qemu_log_mask(LOG_GUEST_ERROR, "Invalid page size granule %d\n",
page_size_granule);
return 0;
}
+ page_shift = (page_size_granule - 1) * 2 + 12;
+
exponent = (5 * scale) + 1;
length = (num + 1) << (exponent + page_shift);
diff --git a/target/arm/helper.h b/target/arm/helper.h
index 448a86edfd..b463d9343b 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -47,6 +47,7 @@ DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE,
DEF_HELPER_2(exception_internal, void, env, i32)
DEF_HELPER_4(exception_with_syndrome, void, env, i32, i32, i32)
DEF_HELPER_2(exception_bkpt_insn, void, env, i32)
+DEF_HELPER_2(exception_pc_alignment, noreturn, env, tl)
DEF_HELPER_1(setend, void, env)
DEF_HELPER_2(wfi, void, env, i32)
DEF_HELPER_1(wfe, void, env)
diff --git a/target/arm/machine.c b/target/arm/machine.c
index c74d8c3f4b..135d2420b5 100644
--- a/target/arm/machine.c
+++ b/target/arm/machine.c
@@ -794,6 +794,16 @@ static int cpu_post_load(void *opaque, int version_id)
return -1;
}
}
+
+ /*
+ * Misaligned thumb pc is architecturally impossible.
+ * We have an assert in thumb_tr_translate_insn to verify this.
+ * Fail an incoming migrate to avoid this assert.
+ */
+ if (!is_a64(env) && env->thumb && (env->regs[15] & 1)) {
+ return -1;
+ }
+
if (!kvm_enabled()) {
pmu_op_finish(&cpu->env);
}
diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h
index f30f4130a2..8cde8e7243 100644
--- a/target/arm/syndrome.h
+++ b/target/arm/syndrome.h
@@ -282,4 +282,9 @@ static inline uint32_t syn_illegalstate(void)
return (EC_ILLEGALSTATE << ARM_EL_EC_SHIFT) | ARM_EL_IL;
}
+static inline uint32_t syn_pcalignment(void)
+{
+ return (EC_PCALIGNMENT << ARM_EL_EC_SHIFT) | ARM_EL_IL;
+}
+
#endif /* TARGET_ARM_SYNDROME_H */
diff --git a/target/arm/tlb_helper.c b/target/arm/tlb_helper.c
index 12a934e924..b79004e0cc 100644
--- a/target/arm/tlb_helper.c
+++ b/target/arm/tlb_helper.c
@@ -9,6 +9,7 @@
#include "cpu.h"
#include "internals.h"
#include "exec/exec-all.h"
+#include "exec/helper-proto.h"
static inline uint32_t merge_syn_data_abort(uint32_t template_syn,
unsigned int target_el,
@@ -49,25 +50,11 @@ static inline uint32_t merge_syn_data_abort(uint32_t template_syn,
return syn;
}
-static void QEMU_NORETURN arm_deliver_fault(ARMCPU *cpu, vaddr addr,
- MMUAccessType access_type,
- int mmu_idx, ARMMMUFaultInfo *fi)
+static uint32_t compute_fsr_fsc(CPUARMState *env, ARMMMUFaultInfo *fi,
+ int target_el, int mmu_idx, uint32_t *ret_fsc)
{
- CPUARMState *env = &cpu->env;
- int target_el;
- bool same_el;
- uint32_t syn, exc, fsr, fsc;
ARMMMUIdx arm_mmu_idx = core_to_arm_mmu_idx(env, mmu_idx);
-
- target_el = exception_target_el(env);
- if (fi->stage2) {
- target_el = 2;
- env->cp15.hpfar_el2 = extract64(fi->s2addr, 12, 47) << 4;
- if (arm_is_secure_below_el3(env) && fi->s1ns) {
- env->cp15.hpfar_el2 |= HPFAR_NS;
- }
- }
- same_el = (arm_current_el(env) == target_el);
+ uint32_t fsr, fsc;
if (target_el == 2 || arm_el_is_aa64(env, target_el) ||
arm_s1_regime_using_lpae_format(env, arm_mmu_idx)) {
@@ -88,6 +75,31 @@ static void QEMU_NORETURN arm_deliver_fault(ARMCPU *cpu, vaddr addr,
fsc = 0x3f;
}
+ *ret_fsc = fsc;
+ return fsr;
+}
+
+static void QEMU_NORETURN arm_deliver_fault(ARMCPU *cpu, vaddr addr,
+ MMUAccessType access_type,
+ int mmu_idx, ARMMMUFaultInfo *fi)
+{
+ CPUARMState *env = &cpu->env;
+ int target_el;
+ bool same_el;
+ uint32_t syn, exc, fsr, fsc;
+
+ target_el = exception_target_el(env);
+ if (fi->stage2) {
+ target_el = 2;
+ env->cp15.hpfar_el2 = extract64(fi->s2addr, 12, 47) << 4;
+ if (arm_is_secure_below_el3(env) && fi->s1ns) {
+ env->cp15.hpfar_el2 |= HPFAR_NS;
+ }
+ }
+ same_el = (arm_current_el(env) == target_el);
+
+ fsr = compute_fsr_fsc(env, fi, target_el, mmu_idx, &fsc);
+
if (access_type == MMU_INST_FETCH) {
syn = syn_insn_abort(same_el, fi->ea, fi->s1ptw, fsc);
exc = EXCP_PREFETCH_ABORT;
@@ -123,6 +135,23 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
arm_deliver_fault(cpu, vaddr, access_type, mmu_idx, &fi);
}
+void helper_exception_pc_alignment(CPUARMState *env, target_ulong pc)
+{
+ ARMMMUFaultInfo fi = { .type = ARMFault_Alignment };
+ int target_el = exception_target_el(env);
+ int mmu_idx = cpu_mmu_index(env, true);
+ uint32_t fsc;
+
+ env->exception.vaddress = pc;
+
+ /*
+ * Note that the fsc is not applicable to this exception,
+ * since any syndrome is pcalignment not insn_abort.
+ */
+ env->exception.fsr = compute_fsr_fsc(env, &fi, target_el, mmu_idx, &fsc);
+ raise_exception(env, EXCP_PREFETCH_ABORT, syn_pcalignment(), target_el);
+}
+
#if !defined(CONFIG_USER_ONLY)
/*
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index cec672f229..130a9ff8d5 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -14750,8 +14750,10 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
{
DisasContext *s = container_of(dcbase, DisasContext, base);
CPUARMState *env = cpu->env_ptr;
+ uint64_t pc = s->base.pc_next;
uint32_t insn;
+ /* Singlestep exceptions have the highest priority. */
if (s->ss_active && !s->pstate_ss) {
/* Singlestep state is Active-pending.
* If we're in this state at the start of a TB then either
@@ -14766,13 +14768,28 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
assert(s->base.num_insns == 1);
gen_swstep_exception(s, 0, 0);
s->base.is_jmp = DISAS_NORETURN;
+ s->base.pc_next = pc + 4;
return;
}
- s->pc_curr = s->base.pc_next;
- insn = arm_ldl_code(env, &s->base, s->base.pc_next, s->sctlr_b);
+ if (pc & 3) {
+ /*
+ * PC alignment fault. This has priority over the instruction abort
+ * that we would receive from a translation fault via arm_ldl_code.
+ * This should only be possible after an indirect branch, at the
+ * start of the TB.
+ */
+ assert(s->base.num_insns == 1);
+ gen_helper_exception_pc_alignment(cpu_env, tcg_constant_tl(pc));
+ s->base.is_jmp = DISAS_NORETURN;
+ s->base.pc_next = QEMU_ALIGN_UP(pc, 4);
+ return;
+ }
+
+ s->pc_curr = pc;
+ insn = arm_ldl_code(env, &s->base, pc, s->sctlr_b);
s->insn = insn;
- s->base.pc_next += 4;
+ s->base.pc_next = pc + 4;
s->fp_access_checked = false;
s->sve_access_checked = false;
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 98f5925928..0a3840d227 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -9502,7 +9502,7 @@ static void arm_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
dc->insn_start = tcg_last_op();
}
-static bool arm_pre_translate_insn(DisasContext *dc)
+static bool arm_check_kernelpage(DisasContext *dc)
{
#ifdef CONFIG_USER_ONLY
/* Intercept jump to the magic kernel page. */
@@ -9514,7 +9514,11 @@ static bool arm_pre_translate_insn(DisasContext *dc)
return true;
}
#endif
+ return false;
+}
+static bool arm_check_ss_active(DisasContext *dc)
+{
if (dc->ss_active && !dc->pstate_ss) {
/* Singlestep state is Active-pending.
* If we're in this state at the start of a TB then either
@@ -9548,17 +9552,38 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
CPUARMState *env = cpu->env_ptr;
+ uint32_t pc = dc->base.pc_next;
unsigned int insn;
- if (arm_pre_translate_insn(dc)) {
- dc->base.pc_next += 4;
+ /* Singlestep exceptions have the highest priority. */
+ if (arm_check_ss_active(dc)) {
+ dc->base.pc_next = pc + 4;
return;
}
- dc->pc_curr = dc->base.pc_next;
- insn = arm_ldl_code(env, &dc->base, dc->base.pc_next, dc->sctlr_b);
+ if (pc & 3) {
+ /*
+ * PC alignment fault. This has priority over the instruction abort
+ * that we would receive from a translation fault via arm_ldl_code
+ * (or the execution of the kernelpage entrypoint). This should only
+ * be possible after an indirect branch, at the start of the TB.
+ */
+ assert(dc->base.num_insns == 1);
+ gen_helper_exception_pc_alignment(cpu_env, tcg_constant_tl(pc));
+ dc->base.is_jmp = DISAS_NORETURN;
+ dc->base.pc_next = QEMU_ALIGN_UP(pc, 4);
+ return;
+ }
+
+ if (arm_check_kernelpage(dc)) {
+ dc->base.pc_next = pc + 4;
+ return;
+ }
+
+ dc->pc_curr = pc;
+ insn = arm_ldl_code(env, &dc->base, pc, dc->sctlr_b);
dc->insn = insn;
- dc->base.pc_next += 4;
+ dc->base.pc_next = pc + 4;
disas_arm_insn(dc, insn);
arm_post_translate_insn(dc);
@@ -9617,25 +9642,28 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
CPUARMState *env = cpu->env_ptr;
+ uint32_t pc = dc->base.pc_next;
uint32_t insn;
bool is_16bit;
- if (arm_pre_translate_insn(dc)) {
- dc->base.pc_next += 2;
+ /* Misaligned thumb PC is architecturally impossible. */
+ assert((dc->base.pc_next & 1) == 0);
+
+ if (arm_check_ss_active(dc) || arm_check_kernelpage(dc)) {
+ dc->base.pc_next = pc + 2;
return;
}
- dc->pc_curr = dc->base.pc_next;
- insn = arm_lduw_code(env, &dc->base, dc->base.pc_next, dc->sctlr_b);
+ dc->pc_curr = pc;
+ insn = arm_lduw_code(env, &dc->base, pc, dc->sctlr_b);
is_16bit = thumb_insn_is_16bit(dc, dc->base.pc_next, insn);
- dc->base.pc_next += 2;
+ pc += 2;
if (!is_16bit) {
- uint32_t insn2 = arm_lduw_code(env, &dc->base, dc->base.pc_next,
- dc->sctlr_b);
-
+ uint32_t insn2 = arm_lduw_code(env, &dc->base, pc, dc->sctlr_b);
insn = insn << 16 | insn2;
- dc->base.pc_next += 2;
+ pc += 2;
}
+ dc->base.pc_next = pc;
dc->insn = insn;
if (dc->pstate_il) {
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
index de121d950f..58a0d3870b 100644
--- a/target/hexagon/cpu.h
+++ b/target/hexagon/cpu.h
@@ -23,7 +23,6 @@ typedef struct CPUHexagonState CPUHexagonState;
#include "fpu/softfloat-types.h"
-#include "qemu-common.h"
#include "exec/cpu-defs.h"
#include "hex_regs.h"
#include "mmvec/mmvec.h"
diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
index e9e1451540..05f9336c9b 100644
--- a/target/i386/tcg/translate.c
+++ b/target/i386/tcg/translate.c
@@ -3519,9 +3519,6 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
case 0x171: /* shift xmm, im */
case 0x172:
case 0x173:
- if (b1 >= 2) {
- goto unknown_op;
- }
val = x86_ldub_code(env, s);
if (is_xmm) {
tcg_gen_movi_tl(s->T0, val);
@@ -3540,6 +3537,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
offsetof(CPUX86State, mmx_t0.MMX_L(1)));
op1_offset = offsetof(CPUX86State,mmx_t0);
}
+ assert(b1 < 2);
sse_fn_epp = sse_op_table2[((b - 1) & 3) * 8 +
(((modrm >> 3)) & 7)][b1];
if (!sse_fn_epp) {
@@ -3770,10 +3768,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
rm = modrm & 7;
reg = ((modrm >> 3) & 7) | REX_R(s);
mod = (modrm >> 6) & 3;
- if (b1 >= 2) {
- goto unknown_op;
- }
+ assert(b1 < 2);
sse_fn_epp = sse_op_table6[b].op[b1];
if (!sse_fn_epp) {
goto unknown_op;
@@ -4200,10 +4196,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
rm = modrm & 7;
reg = ((modrm >> 3) & 7) | REX_R(s);
mod = (modrm >> 6) & 3;
- if (b1 >= 2) {
- goto unknown_op;
- }
+ assert(b1 < 2);
sse_fn_eppi = sse_op_table7[b].op[b1];
if (!sse_fn_eppi) {
goto unknown_op;
diff --git a/target/rx/cpu.h b/target/rx/cpu.h
index 4ac71aec37..657db84ef0 100644
--- a/target/rx/cpu.h
+++ b/target/rx/cpu.h
@@ -20,7 +20,6 @@
#define RX_CPU_H
#include "qemu/bitops.h"
-#include "qemu-common.h"
#include "hw/registerfields.h"
#include "cpu-qom.h"
diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc
index 633b8a37ba..9d322cdba6 100644
--- a/tcg/arm/tcg-target.c.inc
+++ b/tcg/arm/tcg-target.c.inc
@@ -2523,8 +2523,13 @@ static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg,
tcg_out_vldst(s, INSN_VLD1 | 0x7d0, arg, arg1, arg2);
return;
case TCG_TYPE_V128:
- /* regs 2; size 8; align 16 */
- tcg_out_vldst(s, INSN_VLD1 | 0xae0, arg, arg1, arg2);
+ /*
+ * We have only 8-byte alignment for the stack per the ABI.
+ * Rather than dynamically re-align the stack, it's easier
+ * to simply not request alignment beyond that. So:
+ * regs 2; size 8; align 8
+ */
+ tcg_out_vldst(s, INSN_VLD1 | 0xad0, arg, arg1, arg2);
return;
default:
g_assert_not_reached();
@@ -2543,8 +2548,8 @@ static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg,
tcg_out_vldst(s, INSN_VST1 | 0x7d0, arg, arg1, arg2);
return;
case TCG_TYPE_V128:
- /* regs 2; size 8; align 16 */
- tcg_out_vldst(s, INSN_VST1 | 0xae0, arg, arg1, arg2);
+ /* See tcg_out_ld re alignment: regs 2; size 8; align 8 */
+ tcg_out_vldst(s, INSN_VST1 | 0xad0, arg, arg1, arg2);
return;
default:
g_assert_not_reached();
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 57f17a4649..934aa8510b 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -3061,7 +3061,13 @@ static void temp_allocate_frame(TCGContext *s, TCGTemp *ts)
g_assert_not_reached();
}
- assert(align <= TCG_TARGET_STACK_ALIGN);
+ /*
+ * Assume the stack is sufficiently aligned.
+ * This affects e.g. ARM NEON, where we have 8 byte stack alignment
+ * and do not require 16 byte vector alignment. This seems slightly
+ * easier than fully parameterizing the above switch statement.
+ */
+ align = MIN(TCG_TARGET_STACK_ALIGN, align);
off = ROUND_UP(s->current_frame_offset, align);
/* If we've exhausted the stack frame, restart with a smaller TB. */
diff --git a/tests/data/acpi/q35/DSDT.viot b/tests/data/acpi/q35/DSDT.viot
new file mode 100644
index 0000000000..1c3b4da5cb
--- /dev/null
+++ b/tests/data/acpi/q35/DSDT.viot
Binary files differ
diff --git a/tests/data/acpi/q35/VIOT.viot b/tests/data/acpi/q35/VIOT.viot
new file mode 100644
index 0000000000..9b179266cc
--- /dev/null
+++ b/tests/data/acpi/q35/VIOT.viot
Binary files differ
diff --git a/tests/data/acpi/virt/VIOT b/tests/data/acpi/virt/VIOT
new file mode 100644
index 0000000000..921f40d88c
--- /dev/null
+++ b/tests/data/acpi/virt/VIOT
Binary files differ
diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c
index 258874167e..9a468e29eb 100644
--- a/tests/qtest/bios-tables-test.c
+++ b/tests/qtest/bios-tables-test.c
@@ -1465,6 +1465,43 @@ static void test_acpi_virt_tcg(void)
free_test_data(&data);
}
+static void test_acpi_q35_viot(void)
+{
+ test_data data = {
+ .machine = MACHINE_Q35,
+ .variant = ".viot",
+ };
+
+ /*
+ * To keep things interesting, two buses bypass the IOMMU.
+ * VIOT should only describes the other two buses.
+ */
+ test_acpi_one("-machine default_bus_bypass_iommu=on "
+ "-device virtio-iommu-pci "
+ "-device pxb-pcie,bus_nr=0x10,id=pcie.100,bus=pcie.0 "
+ "-device pxb-pcie,bus_nr=0x20,id=pcie.200,bus=pcie.0,bypass_iommu=on "
+ "-device pxb-pcie,bus_nr=0x30,id=pcie.300,bus=pcie.0",
+ &data);
+ free_test_data(&data);
+}
+
+static void test_acpi_virt_viot(void)
+{
+ test_data data = {
+ .machine = "virt",
+ .tcg_only = true,
+ .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd",
+ .uefi_fl2 = "pc-bios/edk2-arm-vars.fd",
+ .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2",
+ .ram_start = 0x40000000ULL,
+ .scan_len = 128ULL * 1024 * 1024,
+ };
+
+ test_acpi_one("-cpu cortex-a57 "
+ "-device virtio-iommu-pci", &data);
+ free_test_data(&data);
+}
+
static void test_oem_fields(test_data *data)
{
int i;
@@ -1639,6 +1676,7 @@ int main(int argc, char *argv[])
qtest_add_func("acpi/q35/kvm/xapic", test_acpi_q35_kvm_xapic);
qtest_add_func("acpi/q35/kvm/dmar", test_acpi_q35_kvm_dmar);
}
+ qtest_add_func("acpi/q35/viot", test_acpi_q35_viot);
} else if (strcmp(arch, "aarch64") == 0) {
if (has_tcg) {
qtest_add_func("acpi/virt", test_acpi_virt_tcg);
@@ -1646,6 +1684,7 @@ int main(int argc, char *argv[])
qtest_add_func("acpi/virt/memhp", test_acpi_virt_tcg_memhp);
qtest_add_func("acpi/virt/pxb", test_acpi_virt_tcg_pxb);
qtest_add_func("acpi/virt/oem-fields", test_acpi_oem_fields_virt);
+ qtest_add_func("acpi/virt/viot", test_acpi_virt_viot);
}
}
ret = g_test_run();
diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c
index 83828ba270..4d8e1343bd 100644
--- a/tests/qtest/boot-serial-test.c
+++ b/tests/qtest/boot-serial-test.c
@@ -285,7 +285,8 @@ int main(int argc, char *argv[])
g_test_init(&argc, &argv, NULL);
for (i = 0; tests[i].arch != NULL; i++) {
- if (strcmp(arch, tests[i].arch) == 0) {
+ if (g_str_equal(arch, tests[i].arch) &&
+ qtest_has_machine(tests[i].machine)) {
char *name = g_strdup_printf("boot-serial/%s", tests[i].machine);
qtest_add_data_func(name, &tests[i], test_machine);
g_free(name);
diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c
index 5af944a5fb..c1fcac5c45 100644
--- a/tests/qtest/cdrom-test.c
+++ b/tests/qtest/cdrom-test.c
@@ -109,9 +109,11 @@ static void test_cdrom_param(gconstpointer data)
static void add_cdrom_param_tests(const char **machines)
{
while (*machines) {
- char *testname = g_strdup_printf("cdrom/param/%s", *machines);
- qtest_add_data_func(testname, *machines, test_cdrom_param);
- g_free(testname);
+ if (qtest_has_machine(*machines)) {
+ char *testname = g_strdup_printf("cdrom/param/%s", *machines);
+ qtest_add_data_func(testname, *machines, test_cdrom_param);
+ g_free(testname);
+ }
machines++;
}
}
diff --git a/tests/qtest/fdc-test.c b/tests/qtest/fdc-test.c
index 26b69f7c5c..8f6eee84a4 100644
--- a/tests/qtest/fdc-test.c
+++ b/tests/qtest/fdc-test.c
@@ -32,6 +32,9 @@
/* TODO actually test the results and get rid of this */
#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__))
+#define DRIVE_FLOPPY_BLANK \
+ "-drive if=floppy,file=null-co://,file.read-zeroes=on,format=raw,size=1440k"
+
#define TEST_IMAGE_SIZE 1440 * 1024
#define FLOPPY_BASE 0x3f0
@@ -546,6 +549,40 @@ static void fuzz_registers(void)
}
}
+static bool qtest_check_clang_sanitizer(void)
+{
+#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
+ return true;
+#else
+ g_test_skip("QEMU not configured using --enable-sanitizers");
+ return false;
+#endif
+}
+static void test_cve_2021_20196(void)
+{
+ QTestState *s;
+
+ if (!qtest_check_clang_sanitizer()) {
+ return;
+ }
+
+ s = qtest_initf("-nographic -m 32M -nodefaults " DRIVE_FLOPPY_BLANK);
+
+ qtest_outw(s, 0x3f4, 0x0500);
+ qtest_outb(s, 0x3f5, 0x00);
+ qtest_outb(s, 0x3f5, 0x00);
+ qtest_outw(s, 0x3f4, 0x0000);
+ qtest_outb(s, 0x3f5, 0x00);
+ qtest_outw(s, 0x3f1, 0x0400);
+ qtest_outw(s, 0x3f4, 0x0000);
+ qtest_outw(s, 0x3f4, 0x0000);
+ qtest_outb(s, 0x3f5, 0x00);
+ qtest_outb(s, 0x3f5, 0x01);
+ qtest_outw(s, 0x3f1, 0x0500);
+ qtest_outb(s, 0x3f5, 0x00);
+ qtest_quit(s);
+}
+
int main(int argc, char **argv)
{
int fd;
@@ -576,6 +613,7 @@ int main(int argc, char **argv)
qtest_add_func("/fdc/read_no_dma_18", test_read_no_dma_18);
qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19);
qtest_add_func("/fdc/fuzz-registers", fuzz_registers);
+ qtest_add_func("/fdc/fuzz/cve_2021_20196", test_cve_2021_20196);
ret = g_test_run();
diff --git a/tests/qtest/libqos/libqtest.h b/tests/qtest/libqos/libqtest.h
index 59e9271195..dff6b31cf0 100644
--- a/tests/qtest/libqos/libqtest.h
+++ b/tests/qtest/libqos/libqtest.h
@@ -711,6 +711,14 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine),
bool skip_old_versioned);
/**
+ * qtest_has_machine:
+ * @machine: The machine to look for
+ *
+ * Returns: true if the machine is available in the target binary.
+ */
+bool qtest_has_machine(const char *machine);
+
+/**
* qtest_qmp_device_add_qdict:
* @qts: QTestState instance to operate on
* @drv: Name of the device that should be added
diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build
index 4af1f04787..e988d15791 100644
--- a/tests/qtest/libqos/meson.build
+++ b/tests/qtest/libqos/meson.build
@@ -41,6 +41,7 @@ libqos_srcs = files('../libqtest.c',
'virtio-rng.c',
'virtio-scsi.c',
'virtio-serial.c',
+ 'virtio-iommu.c',
# qgraph machines:
'aarch64-xlnx-zcu102-machine.c',
diff --git a/tests/qtest/libqos/pci.c b/tests/qtest/libqos/pci.c
index e1e96189c8..3a9076ae58 100644
--- a/tests/qtest/libqos/pci.c
+++ b/tests/qtest/libqos/pci.c
@@ -13,6 +13,8 @@
#include "qemu/osdep.h"
#include "pci.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bridge.h"
#include "hw/pci/pci_regs.h"
#include "qemu/host-utils.h"
#include "qgraph.h"
@@ -99,6 +101,123 @@ void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr)
g_assert(!addr->device_id || device_id == addr->device_id);
}
+static uint8_t qpci_find_resource_reserve_capability(QPCIDevice *dev)
+{
+ uint16_t device_id;
+ uint8_t cap = 0;
+
+ if (qpci_config_readw(dev, PCI_VENDOR_ID) != PCI_VENDOR_ID_REDHAT) {
+ return 0;
+ }
+
+ device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
+
+ if (device_id != PCI_DEVICE_ID_REDHAT_PCIE_RP &&
+ device_id != PCI_DEVICE_ID_REDHAT_BRIDGE) {
+ return 0;
+ }
+
+ do {
+ cap = qpci_find_capability(dev, PCI_CAP_ID_VNDR, cap);
+ } while (cap &&
+ qpci_config_readb(dev, cap + REDHAT_PCI_CAP_TYPE_OFFSET) !=
+ REDHAT_PCI_CAP_RESOURCE_RESERVE);
+ if (cap) {
+ uint8_t cap_len = qpci_config_readb(dev, cap + PCI_CAP_FLAGS);
+ if (cap_len < REDHAT_PCI_CAP_RES_RESERVE_CAP_SIZE) {
+ return 0;
+ }
+ }
+ return cap;
+}
+
+static void qpci_secondary_buses_rec(QPCIBus *qbus, int bus, int *pci_bus)
+{
+ QPCIDevice *dev;
+ uint16_t class;
+ uint8_t pribus, secbus, subbus;
+ int index;
+
+ for (index = 0; index < 32; index++) {
+ dev = qpci_device_find(qbus, QPCI_DEVFN(bus + index, 0));
+ if (dev == NULL) {
+ continue;
+ }
+ class = qpci_config_readw(dev, PCI_CLASS_DEVICE);
+ if (class == PCI_CLASS_BRIDGE_PCI) {
+ qpci_config_writeb(dev, PCI_SECONDARY_BUS, 255);
+ qpci_config_writeb(dev, PCI_SUBORDINATE_BUS, 0);
+ }
+ g_free(dev);
+ }
+
+ for (index = 0; index < 32; index++) {
+ dev = qpci_device_find(qbus, QPCI_DEVFN(bus + index, 0));
+ if (dev == NULL) {
+ continue;
+ }
+ class = qpci_config_readw(dev, PCI_CLASS_DEVICE);
+ if (class != PCI_CLASS_BRIDGE_PCI) {
+ g_free(dev);
+ continue;
+ }
+
+ pribus = qpci_config_readb(dev, PCI_PRIMARY_BUS);
+ if (pribus != bus) {
+ qpci_config_writeb(dev, PCI_PRIMARY_BUS, bus);
+ }
+
+ secbus = qpci_config_readb(dev, PCI_SECONDARY_BUS);
+ (*pci_bus)++;
+ if (*pci_bus != secbus) {
+ secbus = *pci_bus;
+ qpci_config_writeb(dev, PCI_SECONDARY_BUS, secbus);
+ }
+
+ subbus = qpci_config_readb(dev, PCI_SUBORDINATE_BUS);
+ qpci_config_writeb(dev, PCI_SUBORDINATE_BUS, 255);
+
+ qpci_secondary_buses_rec(qbus, secbus << 5, pci_bus);
+
+ if (subbus != *pci_bus) {
+ uint8_t res_bus = *pci_bus;
+ uint8_t cap = qpci_find_resource_reserve_capability(dev);
+
+ if (cap) {
+ uint32_t tmp_res_bus;
+
+ tmp_res_bus = qpci_config_readl(dev, cap +
+ REDHAT_PCI_CAP_RES_RESERVE_BUS_RES);
+ if (tmp_res_bus != (uint32_t)-1) {
+ res_bus = tmp_res_bus & 0xFF;
+ if ((uint8_t)(res_bus + secbus) < secbus ||
+ (uint8_t)(res_bus + secbus) < res_bus) {
+ res_bus = 0;
+ }
+ if (secbus + res_bus > *pci_bus) {
+ res_bus = secbus + res_bus;
+ }
+ }
+ }
+ subbus = res_bus;
+ *pci_bus = res_bus;
+ }
+
+ qpci_config_writeb(dev, PCI_SUBORDINATE_BUS, subbus);
+ g_free(dev);
+ }
+}
+
+int qpci_secondary_buses_init(QPCIBus *bus)
+{
+ int last_bus = 0;
+
+ qpci_secondary_buses_rec(bus, 0, &last_bus);
+
+ return last_bus;
+}
+
+
void qpci_device_enable(QPCIDevice *dev)
{
uint16_t cmd;
diff --git a/tests/qtest/libqos/pci.h b/tests/qtest/libqos/pci.h
index ee64fdecbd..becb800f9e 100644
--- a/tests/qtest/libqos/pci.h
+++ b/tests/qtest/libqos/pci.h
@@ -81,6 +81,7 @@ void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
void *data);
QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn);
void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr);
+int qpci_secondary_buses_init(QPCIBus *bus);
bool qpci_has_buggy_msi(QPCIDevice *dev);
bool qpci_check_buggy_msi(QPCIDevice *dev);
diff --git a/tests/qtest/libqos/virtio-iommu.c b/tests/qtest/libqos/virtio-iommu.c
new file mode 100644
index 0000000000..18cba4ca36
--- /dev/null
+++ b/tests/qtest/libqos/virtio-iommu.c
@@ -0,0 +1,126 @@
+/*
+ * libqos driver virtio-iommu-pci framework
+ *
+ * Copyright (c) 2021 Red Hat, Inc.
+ *
+ * Authors:
+ * Eric Auger <eric.auger@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.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "qgraph.h"
+#include "virtio-iommu.h"
+#include "hw/virtio/virtio-iommu.h"
+
+static QGuestAllocator *alloc;
+
+/* virtio-iommu-device */
+static void *qvirtio_iommu_get_driver(QVirtioIOMMU *v_iommu,
+ const char *interface)
+{
+ if (!g_strcmp0(interface, "virtio-iommu")) {
+ return v_iommu;
+ }
+ if (!g_strcmp0(interface, "virtio")) {
+ return v_iommu->vdev;
+ }
+
+ fprintf(stderr, "%s not present in virtio-iommu-device\n", interface);
+ g_assert_not_reached();
+}
+
+static void virtio_iommu_cleanup(QVirtioIOMMU *interface)
+{
+ qvirtqueue_cleanup(interface->vdev->bus, interface->vq, alloc);
+}
+
+static void virtio_iommu_setup(QVirtioIOMMU *interface)
+{
+ QVirtioDevice *vdev = interface->vdev;
+ uint64_t features;
+
+ features = qvirtio_get_features(vdev);
+ features &= ~(QVIRTIO_F_BAD_FEATURE |
+ (1ull << VIRTIO_RING_F_INDIRECT_DESC) |
+ (1ull << VIRTIO_RING_F_EVENT_IDX) |
+ (1ull << VIRTIO_IOMMU_F_BYPASS));
+ qvirtio_set_features(vdev, features);
+ interface->vq = qvirtqueue_setup(interface->vdev, alloc, 0);
+ qvirtio_set_driver_ok(interface->vdev);
+}
+
+/* virtio-iommu-pci */
+static void *qvirtio_iommu_pci_get_driver(void *object, const char *interface)
+{
+ QVirtioIOMMUPCI *v_iommu = object;
+ if (!g_strcmp0(interface, "pci-device")) {
+ return v_iommu->pci_vdev.pdev;
+ }
+ return qvirtio_iommu_get_driver(&v_iommu->iommu, interface);
+}
+
+static void qvirtio_iommu_pci_destructor(QOSGraphObject *obj)
+{
+ QVirtioIOMMUPCI *iommu_pci = (QVirtioIOMMUPCI *) obj;
+ QVirtioIOMMU *interface = &iommu_pci->iommu;
+ QOSGraphObject *pci_vobj = &iommu_pci->pci_vdev.obj;
+
+ virtio_iommu_cleanup(interface);
+ qvirtio_pci_destructor(pci_vobj);
+}
+
+static void qvirtio_iommu_pci_start_hw(QOSGraphObject *obj)
+{
+ QVirtioIOMMUPCI *iommu_pci = (QVirtioIOMMUPCI *) obj;
+ QVirtioIOMMU *interface = &iommu_pci->iommu;
+ QOSGraphObject *pci_vobj = &iommu_pci->pci_vdev.obj;
+
+ qvirtio_pci_start_hw(pci_vobj);
+ virtio_iommu_setup(interface);
+}
+
+
+static void *virtio_iommu_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
+ void *addr)
+{
+ QVirtioIOMMUPCI *virtio_rpci = g_new0(QVirtioIOMMUPCI, 1);
+ QVirtioIOMMU *interface = &virtio_rpci->iommu;
+ QOSGraphObject *obj = &virtio_rpci->pci_vdev.obj;
+
+ virtio_pci_init(&virtio_rpci->pci_vdev, pci_bus, addr);
+ interface->vdev = &virtio_rpci->pci_vdev.vdev;
+ alloc = t_alloc;
+
+ obj->get_driver = qvirtio_iommu_pci_get_driver;
+ obj->start_hw = qvirtio_iommu_pci_start_hw;
+ obj->destructor = qvirtio_iommu_pci_destructor;
+
+ return obj;
+}
+
+static void virtio_iommu_register_nodes(void)
+{
+ QPCIAddress addr = {
+ .devfn = QPCI_DEVFN(4, 0),
+ };
+
+ QOSGraphEdgeOptions opts = {
+ .extra_device_opts = "addr=04.0",
+ };
+
+ /* virtio-iommu-pci */
+ add_qpci_address(&opts, &addr);
+ qos_node_create_driver("virtio-iommu-pci", virtio_iommu_pci_create);
+ qos_node_consumes("virtio-iommu-pci", "pci-bus", &opts);
+ qos_node_produces("virtio-iommu-pci", "pci-device");
+ qos_node_produces("virtio-iommu-pci", "virtio");
+ qos_node_produces("virtio-iommu-pci", "virtio-iommu");
+}
+
+libqos_init(virtio_iommu_register_nodes);
diff --git a/tests/qtest/libqos/virtio-iommu.h b/tests/qtest/libqos/virtio-iommu.h
new file mode 100644
index 0000000000..d753761958
--- /dev/null
+++ b/tests/qtest/libqos/virtio-iommu.h
@@ -0,0 +1,40 @@
+/*
+ * libqos driver virtio-iommu-pci framework
+ *
+ * Copyright (c) 2021 Red Hat, Inc.
+ *
+ * Authors:
+ * Eric Auger <eric.auger@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.
+ *
+ */
+
+#ifndef TESTS_LIBQOS_VIRTIO_IOMMU_H
+#define TESTS_LIBQOS_VIRTIO_IOMMU_H
+
+#include "qgraph.h"
+#include "virtio.h"
+#include "virtio-pci.h"
+
+typedef struct QVirtioIOMMU QVirtioIOMMU;
+typedef struct QVirtioIOMMUPCI QVirtioIOMMUPCI;
+typedef struct QVirtioIOMMUDevice QVirtioIOMMUDevice;
+
+struct QVirtioIOMMU {
+ QVirtioDevice *vdev;
+ QVirtQueue *vq;
+};
+
+struct QVirtioIOMMUPCI {
+ QVirtioPCIDevice pci_vdev;
+ QVirtioIOMMU iommu;
+};
+
+struct QVirtioIOMMUDevice {
+ QOSGraphObject obj;
+ QVirtioIOMMU iommu;
+};
+
+#endif
diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c
index 25aeea385b..65ed949685 100644
--- a/tests/qtest/libqtest.c
+++ b/tests/qtest/libqtest.c
@@ -1321,16 +1321,29 @@ static bool qtest_is_old_versioned_machine(const char *mname)
return res;
}
-void qtest_cb_for_every_machine(void (*cb)(const char *machine),
- bool skip_old_versioned)
+struct MachInfo {
+ char *name;
+ char *alias;
+};
+
+/*
+ * Returns an array with pointers to the available machine names.
+ * The terminating entry has the name set to NULL.
+ */
+static struct MachInfo *qtest_get_machines(void)
{
+ static struct MachInfo *machines;
QDict *response, *minfo;
QList *list;
const QListEntry *p;
QObject *qobj;
QString *qstr;
- const char *mname;
QTestState *qts;
+ int idx;
+
+ if (machines) {
+ return machines;
+ }
qts = qtest_init("-machine none");
response = qtest_qmp(qts, "{ 'execute': 'query-machines' }");
@@ -1338,25 +1351,71 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine),
list = qdict_get_qlist(response, "return");
g_assert(list);
- for (p = qlist_first(list); p; p = qlist_next(p)) {
+ machines = g_new(struct MachInfo, qlist_size(list) + 1);
+
+ for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) {
minfo = qobject_to(QDict, qlist_entry_obj(p));
g_assert(minfo);
+
qobj = qdict_get(minfo, "name");
g_assert(qobj);
qstr = qobject_to(QString, qobj);
g_assert(qstr);
- mname = qstring_get_str(qstr);
+ machines[idx].name = g_strdup(qstring_get_str(qstr));
+
+ qobj = qdict_get(minfo, "alias");
+ if (qobj) { /* The alias is optional */
+ qstr = qobject_to(QString, qobj);
+ g_assert(qstr);
+ machines[idx].alias = g_strdup(qstring_get_str(qstr));
+ } else {
+ machines[idx].alias = NULL;
+ }
+ }
+
+ qtest_quit(qts);
+ qobject_unref(response);
+
+ memset(&machines[idx], 0, sizeof(struct MachInfo)); /* Terminating entry */
+ return machines;
+}
+
+void qtest_cb_for_every_machine(void (*cb)(const char *machine),
+ bool skip_old_versioned)
+{
+ struct MachInfo *machines;
+ int i;
+
+ machines = qtest_get_machines();
+
+ for (i = 0; machines[i].name != NULL; i++) {
/* Ignore machines that cannot be used for qtests */
- if (!strncmp("xenfv", mname, 5) || g_str_equal("xenpv", mname)) {
+ if (!strncmp("xenfv", machines[i].name, 5) ||
+ g_str_equal("xenpv", machines[i].name)) {
continue;
}
- if (!skip_old_versioned || !qtest_is_old_versioned_machine(mname)) {
- cb(mname);
+ if (!skip_old_versioned ||
+ !qtest_is_old_versioned_machine(machines[i].name)) {
+ cb(machines[i].name);
}
}
+}
- qtest_quit(qts);
- qobject_unref(response);
+bool qtest_has_machine(const char *machine)
+{
+ struct MachInfo *machines;
+ int i;
+
+ machines = qtest_get_machines();
+
+ for (i = 0; machines[i].name != NULL; i++) {
+ if (g_str_equal(machine, machines[i].name) ||
+ (machines[i].alias && g_str_equal(machine, machines[i].alias))) {
+ return true;
+ }
+ }
+
+ return false;
}
/*
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index d2ce20d304..ebeac59b3f 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -69,6 +69,10 @@ qtests_i386 = \
(config_all_devices.has_key('CONFIG_RTL8139_PCI') ? ['rtl8139-test'] : []) + \
(config_all_devices.has_key('CONFIG_E1000E_PCI_EXPRESS') ? ['fuzz-e1000e-test'] : []) + \
(config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_VIRTIO_NET') and \
+ config_all_devices.has_key('CONFIG_Q35') and \
+ config_all_devices.has_key('CONFIG_VIRTIO_PCI') and \
+ slirp.found() ? ['virtio-net-failover'] : []) + \
(unpack_edk2_blobs ? ['bios-tables-test'] : []) + \
qtests_pci + \
['fdc-test',
@@ -135,6 +139,7 @@ qtests_ppc = \
['boot-order-test', 'prom-env-test', 'boot-serial-test'] \
qtests_ppc64 = \
+ qtests_ppc + \
(config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + \
(config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) + \
(config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) + \
@@ -184,11 +189,10 @@ qtests_aarch64 = \
(cpu != 'arm' and unpack_edk2_blobs ? ['bios-tables-test'] : []) + \
(config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? ['tpm-tis-device-test'] : []) + \
(config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? ['tpm-tis-device-swtpm-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_XLNX_ZYNQMP_ARM') ? ['xlnx-can-test', 'fuzz-xlnx-dp-test'] : []) + \
['arm-cpu-features',
'numa-test',
'boot-serial-test',
- 'xlnx-can-test',
- 'fuzz-xlnx-dp-test',
'migration-test']
qtests_s390x = \
@@ -231,6 +235,7 @@ qos_test_ss.add(
'virtio-rng-test.c',
'virtio-scsi-test.c',
'virtio-serial-test.c',
+ 'virtio-iommu-test.c',
'vmxnet3-test.c',
)
if have_virtfs
diff --git a/tests/qtest/prom-env-test.c b/tests/qtest/prom-env-test.c
index f41d80154a..bdbb01d8e5 100644
--- a/tests/qtest/prom-env-test.c
+++ b/tests/qtest/prom-env-test.c
@@ -71,9 +71,11 @@ static void add_tests(const char *machines[])
char *name;
for (i = 0; machines[i] != NULL; i++) {
- name = g_strdup_printf("prom-env/%s", machines[i]);
- qtest_add_data_func(name, machines[i], test_machine);
- g_free(name);
+ if (qtest_has_machine(machines[i])) {
+ name = g_strdup_printf("prom-env/%s", machines[i]);
+ qtest_add_data_func(name, machines[i], test_machine);
+ g_free(name);
+ }
}
}
diff --git a/tests/qtest/virtio-iommu-test.c b/tests/qtest/virtio-iommu-test.c
new file mode 100644
index 0000000000..47e68388a0
--- /dev/null
+++ b/tests/qtest/virtio-iommu-test.c
@@ -0,0 +1,326 @@
+/*
+ * QTest testcase for VirtIO IOMMU
+ *
+ * Copyright (c) 2021 Red Hat, Inc.
+ *
+ * Authors:
+ * Eric Auger <eric.auger@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.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest-single.h"
+#include "qemu/module.h"
+#include "libqos/qgraph.h"
+#include "libqos/virtio-iommu.h"
+#include "hw/virtio/virtio-iommu.h"
+
+#define PCI_SLOT_HP 0x06
+#define QVIRTIO_IOMMU_TIMEOUT_US (30 * 1000 * 1000)
+
+static QGuestAllocator *alloc;
+
+static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc)
+{
+ QVirtioIOMMU *v_iommu = obj;
+ QVirtioDevice *dev = v_iommu->vdev;
+ uint64_t input_range_start = qvirtio_config_readq(dev, 8);
+ uint64_t input_range_end = qvirtio_config_readq(dev, 16);
+ uint32_t domain_range_start = qvirtio_config_readl(dev, 24);
+ uint32_t domain_range_end = qvirtio_config_readl(dev, 28);
+
+ g_assert_cmpint(input_range_start, ==, 0);
+ g_assert_cmphex(input_range_end, ==, UINT64_MAX);
+ g_assert_cmpint(domain_range_start, ==, 0);
+ g_assert_cmpint(domain_range_end, ==, UINT32_MAX);
+}
+
+static int read_tail_status(struct virtio_iommu_req_tail *buffer)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ g_assert_cmpint(buffer->reserved[i], ==, 0);
+ }
+ return buffer->status;
+}
+
+/**
+ * send_attach_detach - Send an attach/detach command to the device
+ * @type: VIRTIO_IOMMU_T_ATTACH/VIRTIO_IOMMU_T_DETACH
+ * @domain: domain the endpoint is attached to
+ * @ep: endpoint
+ */
+static int send_attach_detach(QTestState *qts, QVirtioIOMMU *v_iommu,
+ uint8_t type, uint32_t domain, uint32_t ep)
+{
+ QVirtioDevice *dev = v_iommu->vdev;
+ QVirtQueue *vq = v_iommu->vq;
+ uint64_t ro_addr, wr_addr;
+ uint32_t free_head;
+ struct virtio_iommu_req_attach req = {}; /* same layout as detach */
+ size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail);
+ size_t wr_size = sizeof(struct virtio_iommu_req_tail);
+ struct virtio_iommu_req_tail buffer;
+ int ret;
+
+ req.head.type = type;
+ req.domain = cpu_to_le32(domain);
+ req.endpoint = cpu_to_le32(ep);
+
+ ro_addr = guest_alloc(alloc, ro_size);
+ wr_addr = guest_alloc(alloc, wr_size);
+
+ qtest_memwrite(qts, ro_addr, &req, ro_size);
+ free_head = qvirtqueue_add(qts, vq, ro_addr, ro_size, false, true);
+ qvirtqueue_add(qts, vq, wr_addr, wr_size, true, false);
+ qvirtqueue_kick(qts, dev, vq, free_head);
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
+ QVIRTIO_IOMMU_TIMEOUT_US);
+ qtest_memread(qts, wr_addr, &buffer, wr_size);
+ ret = read_tail_status(&buffer);
+ guest_free(alloc, ro_addr);
+ guest_free(alloc, wr_addr);
+ return ret;
+}
+
+/**
+ * send_map - Send a map command to the device
+ * @domain: domain the new mapping is attached to
+ * @virt_start: iova start
+ * @virt_end: iova end
+ * @phys_start: base physical address
+ * @flags: mapping flags
+ */
+static int send_map(QTestState *qts, QVirtioIOMMU *v_iommu,
+ uint32_t domain, uint64_t virt_start, uint64_t virt_end,
+ uint64_t phys_start, uint32_t flags)
+{
+ QVirtioDevice *dev = v_iommu->vdev;
+ QVirtQueue *vq = v_iommu->vq;
+ uint64_t ro_addr, wr_addr;
+ uint32_t free_head;
+ struct virtio_iommu_req_map req;
+ size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail);
+ size_t wr_size = sizeof(struct virtio_iommu_req_tail);
+ struct virtio_iommu_req_tail buffer;
+ int ret;
+
+ req.head.type = VIRTIO_IOMMU_T_MAP;
+ req.domain = cpu_to_le32(domain);
+ req.virt_start = cpu_to_le64(virt_start);
+ req.virt_end = cpu_to_le64(virt_end);
+ req.phys_start = cpu_to_le64(phys_start);
+ req.flags = cpu_to_le32(flags);
+
+ ro_addr = guest_alloc(alloc, ro_size);
+ wr_addr = guest_alloc(alloc, wr_size);
+
+ qtest_memwrite(qts, ro_addr, &req, ro_size);
+ free_head = qvirtqueue_add(qts, vq, ro_addr, ro_size, false, true);
+ qvirtqueue_add(qts, vq, wr_addr, wr_size, true, false);
+ qvirtqueue_kick(qts, dev, vq, free_head);
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
+ QVIRTIO_IOMMU_TIMEOUT_US);
+ qtest_memread(qts, wr_addr, &buffer, wr_size);
+ ret = read_tail_status(&buffer);
+ guest_free(alloc, ro_addr);
+ guest_free(alloc, wr_addr);
+ return ret;
+}
+
+/**
+ * send_unmap - Send an unmap command to the device
+ * @domain: domain the new binding is attached to
+ * @virt_start: iova start
+ * @virt_end: iova end
+ */
+static int send_unmap(QTestState *qts, QVirtioIOMMU *v_iommu,
+ uint32_t domain, uint64_t virt_start, uint64_t virt_end)
+{
+ QVirtioDevice *dev = v_iommu->vdev;
+ QVirtQueue *vq = v_iommu->vq;
+ uint64_t ro_addr, wr_addr;
+ uint32_t free_head;
+ struct virtio_iommu_req_unmap req;
+ size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail);
+ size_t wr_size = sizeof(struct virtio_iommu_req_tail);
+ struct virtio_iommu_req_tail buffer;
+ int ret;
+
+ req.head.type = VIRTIO_IOMMU_T_UNMAP;
+ req.domain = cpu_to_le32(domain);
+ req.virt_start = cpu_to_le64(virt_start);
+ req.virt_end = cpu_to_le64(virt_end);
+
+ ro_addr = guest_alloc(alloc, ro_size);
+ wr_addr = guest_alloc(alloc, wr_size);
+
+ qtest_memwrite(qts, ro_addr, &req, ro_size);
+ free_head = qvirtqueue_add(qts, vq, ro_addr, ro_size, false, true);
+ qvirtqueue_add(qts, vq, wr_addr, wr_size, true, false);
+ qvirtqueue_kick(qts, dev, vq, free_head);
+ qvirtio_wait_used_elem(qts, dev, vq, free_head, NULL,
+ QVIRTIO_IOMMU_TIMEOUT_US);
+ qtest_memread(qts, wr_addr, &buffer, wr_size);
+ ret = read_tail_status(&buffer);
+ guest_free(alloc, ro_addr);
+ guest_free(alloc, wr_addr);
+ return ret;
+}
+
+static void test_attach_detach(void *obj, void *data, QGuestAllocator *t_alloc)
+{
+ QVirtioIOMMU *v_iommu = obj;
+ QTestState *qts = global_qtest;
+ int ret;
+
+ alloc = t_alloc;
+
+ /* type, domain, ep */
+
+ /* attach ep0 to domain 0 */
+ ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_ATTACH, 0, 0);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* attach a non existing device */
+ ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_ATTACH, 0, 444);
+ g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_NOENT);
+
+ /* detach a non existing device (1) */
+ ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_DETACH, 0, 1);
+ g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_NOENT);
+
+ /* move ep0 from domain 0 to domain 1 */
+ ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_ATTACH, 1, 0);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* detach ep0 from domain 0 */
+ ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_DETACH, 0, 0);
+ g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_INVAL);
+
+ /* detach ep0 from domain 1 */
+ ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_DETACH, 1, 0);
+ g_assert_cmpint(ret, ==, 0);
+
+ ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_ATTACH, 1, 0);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_map(qts, v_iommu, 1, 0x0, 0xFFF, 0xa1000,
+ VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_map(qts, v_iommu, 1, 0x2000, 0x2FFF, 0xb1000,
+ VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_DETACH, 1, 0);
+ g_assert_cmpint(ret, ==, 0);
+}
+
+/* Test map/unmap scenari documented in the spec */
+static void test_map_unmap(void *obj, void *data, QGuestAllocator *t_alloc)
+{
+ QVirtioIOMMU *v_iommu = obj;
+ QTestState *qts = global_qtest;
+ int ret;
+
+ alloc = t_alloc;
+
+ /* attach ep0 to domain 1 */
+ ret = send_attach_detach(qts, v_iommu, VIRTIO_IOMMU_T_ATTACH, 1, 0);
+ g_assert_cmpint(ret, ==, 0);
+
+ ret = send_map(qts, v_iommu, 0, 0, 0xFFF, 0xa1000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_NOENT);
+
+ /* domain, virt start, virt end, phys start, flags */
+ ret = send_map(qts, v_iommu, 1, 0x0, 0xFFF, 0xa1000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* send a new mapping overlapping the previous one */
+ ret = send_map(qts, v_iommu, 1, 0, 0xFFFF, 0xb1000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_INVAL);
+
+ ret = send_unmap(qts, v_iommu, 4, 0x10, 0xFFF);
+ g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_NOENT);
+
+ ret = send_unmap(qts, v_iommu, 1, 0x10, 0xFFF);
+ g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_RANGE);
+
+ ret = send_unmap(qts, v_iommu, 1, 0, 0x1000);
+ g_assert_cmpint(ret, ==, 0); /* unmap everything */
+
+ /* Spec example sequence */
+
+ /* 1 */
+ ret = send_unmap(qts, v_iommu, 1, 0, 4);
+ g_assert_cmpint(ret, ==, 0); /* doesn't unmap anything */
+
+ /* 2 */
+ ret = send_map(qts, v_iommu, 1, 0, 9, 0xa1000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_unmap(qts, v_iommu, 1, 0, 9);
+ g_assert_cmpint(ret, ==, 0); /* unmaps [0,9] */
+
+ /* 3 */
+ ret = send_map(qts, v_iommu, 1, 0, 4, 0xb1000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_map(qts, v_iommu, 1, 5, 9, 0xb2000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_unmap(qts, v_iommu, 1, 0, 9);
+ g_assert_cmpint(ret, ==, 0); /* unmaps [0,4] and [5,9] */
+
+ /* 4 */
+ ret = send_map(qts, v_iommu, 1, 0, 9, 0xc1000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+
+ ret = send_unmap(qts, v_iommu, 1, 0, 4);
+ g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_RANGE); /* doesn't unmap anything */
+
+ ret = send_unmap(qts, v_iommu, 1, 0, 10);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* 5 */
+ ret = send_map(qts, v_iommu, 1, 0, 4, 0xd1000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_map(qts, v_iommu, 1, 5, 9, 0xd2000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_unmap(qts, v_iommu, 1, 0, 4);
+ g_assert_cmpint(ret, ==, 0); /* unmaps [0,4] */
+
+ ret = send_unmap(qts, v_iommu, 1, 5, 9);
+ g_assert_cmpint(ret, ==, 0);
+
+ /* 6 */
+ ret = send_map(qts, v_iommu, 1, 0, 4, 0xe2000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_unmap(qts, v_iommu, 1, 0, 9);
+ g_assert_cmpint(ret, ==, 0); /* unmaps [0,4] */
+
+ /* 7 */
+ ret = send_map(qts, v_iommu, 1, 0, 4, 0xf2000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_map(qts, v_iommu, 1, 10, 14, 0xf3000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_unmap(qts, v_iommu, 1, 0, 14);
+ g_assert_cmpint(ret, ==, 0); /* unmaps [0,4] and [10,14] */
+
+ ret = send_map(qts, v_iommu, 1, 10, 14, 0xf3000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_map(qts, v_iommu, 1, 0, 4, 0xf2000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, 0);
+ ret = send_unmap(qts, v_iommu, 1, 0, 4);
+ g_assert_cmpint(ret, ==, 0); /* only unmaps [0,4] */
+ ret = send_map(qts, v_iommu, 1, 10, 14, 0xf3000, VIRTIO_IOMMU_MAP_F_READ);
+ g_assert_cmpint(ret, ==, VIRTIO_IOMMU_S_INVAL); /* 10-14 still is mapped */
+}
+
+static void register_virtio_iommu_test(void)
+{
+ qos_add_test("config", "virtio-iommu", pci_config, NULL);
+ qos_add_test("attach_detach", "virtio-iommu", test_attach_detach, NULL);
+ qos_add_test("map_unmap", "virtio-iommu", test_map_unmap, NULL);
+}
+
+libqos_init(register_virtio_iommu_test);
diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c
new file mode 100644
index 0000000000..4b2ba8a106
--- /dev/null
+++ b/tests/qtest/virtio-net-failover.c
@@ -0,0 +1,1352 @@
+/*
+ * QTest testcase for virtio-net failover
+ *
+ * See docs/system/virtio-net-failover.rst
+ *
+ * Copyright (c) 2021 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "libqos/libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qlist.h"
+#include "qapi/qmp/qjson.h"
+#include "libqos/malloc-pc.h"
+#include "libqos/virtio-pci.h"
+#include "hw/pci/pci.h"
+
+#define ACPI_PCIHP_ADDR_ICH9 0x0cc0
+#define PCI_EJ_BASE 0x0008
+#define PCI_SEL_BASE 0x0010
+
+#define BASE_MACHINE "-M q35 -nodefaults " \
+ "-device pcie-root-port,id=root0,addr=0x1,bus=pcie.0,chassis=1 " \
+ "-device pcie-root-port,id=root1,addr=0x2,bus=pcie.0,chassis=2 "
+
+#define MAC_PRIMARY0 "52:54:00:11:11:11"
+#define MAC_STANDBY0 "52:54:00:22:22:22"
+#define MAC_PRIMARY1 "52:54:00:33:33:33"
+#define MAC_STANDBY1 "52:54:00:44:44:44"
+
+static QGuestAllocator guest_malloc;
+static QPCIBus *pcibus;
+
+static QTestState *machine_start(const char *args, int numbus)
+{
+ QTestState *qts;
+ QPCIDevice *dev;
+ int bus;
+
+ qts = qtest_init(args);
+
+ pc_alloc_init(&guest_malloc, qts, 0);
+ pcibus = qpci_new_pc(qts, &guest_malloc);
+ g_assert(qpci_secondary_buses_init(pcibus) == numbus);
+
+ for (bus = 1; bus <= numbus; bus++) {
+ dev = qpci_device_find(pcibus, QPCI_DEVFN(bus, 0));
+ g_assert_nonnull(dev);
+
+ qpci_device_enable(dev);
+ qpci_iomap(dev, 4, NULL);
+
+ g_free(dev);
+ }
+
+ return qts;
+}
+
+static void machine_stop(QTestState *qts)
+{
+ qpci_free_pc(pcibus);
+ alloc_destroy(&guest_malloc);
+ qtest_quit(qts);
+}
+
+static void test_error_id(void)
+{
+ QTestState *qts;
+ QDict *resp;
+ QDict *err;
+
+ qts = machine_start(BASE_MACHINE
+ "-device virtio-net,bus=root0,id=standby0,failover=on",
+ 2);
+
+ resp = qtest_qmp(qts, "{'execute': 'device_add',"
+ "'arguments': {"
+ "'driver': 'virtio-net',"
+ "'bus': 'root1',"
+ "'failover_pair_id': 'standby0'"
+ "} }");
+ g_assert(qdict_haskey(resp, "error"));
+
+ err = qdict_get_qdict(resp, "error");
+ g_assert(qdict_haskey(err, "desc"));
+
+ g_assert_cmpstr(qdict_get_str(err, "desc"), ==,
+ "Device with failover_pair_id needs to have id");
+
+ qobject_unref(resp);
+
+ machine_stop(qts);
+}
+
+static void test_error_pcie(void)
+{
+ QTestState *qts;
+ QDict *resp;
+ QDict *err;
+
+ qts = machine_start(BASE_MACHINE
+ "-device virtio-net,bus=root0,id=standby0,failover=on",
+ 2);
+
+ resp = qtest_qmp(qts, "{'execute': 'device_add',"
+ "'arguments': {"
+ "'driver': 'virtio-net',"
+ "'id': 'primary0',"
+ "'bus': 'pcie.0',"
+ "'failover_pair_id': 'standby0'"
+ "} }");
+ g_assert(qdict_haskey(resp, "error"));
+
+ err = qdict_get_qdict(resp, "error");
+ g_assert(qdict_haskey(err, "desc"));
+
+ g_assert_cmpstr(qdict_get_str(err, "desc"), ==,
+ "Bus 'pcie.0' does not support hotplugging");
+
+ qobject_unref(resp);
+
+ machine_stop(qts);
+}
+
+static QDict *find_device(QDict *bus, const char *name)
+{
+ const QObject *obj;
+ QList *devices;
+ QList *list;
+
+ devices = qdict_get_qlist(bus, "devices");
+ if (devices == NULL) {
+ return NULL;
+ }
+
+ list = qlist_copy(devices);
+ while ((obj = qlist_pop(list))) {
+ QDict *device;
+
+ device = qobject_to(QDict, obj);
+
+ if (qdict_haskey(device, "pci_bridge")) {
+ QDict *bridge;
+ QDict *bridge_device;
+
+ bridge = qdict_get_qdict(device, "pci_bridge");
+
+ if (qdict_haskey(bridge, "devices")) {
+ bridge_device = find_device(bridge, name);
+ if (bridge_device) {
+ qobject_unref(device);
+ qobject_unref(list);
+ return bridge_device;
+ }
+ }
+ }
+
+ if (!qdict_haskey(device, "qdev_id")) {
+ qobject_unref(device);
+ continue;
+ }
+
+ if (strcmp(qdict_get_str(device, "qdev_id"), name) == 0) {
+ qobject_unref(list);
+ return device;
+ }
+ qobject_unref(device);
+ }
+ qobject_unref(list);
+
+ return NULL;
+}
+
+static QDict *get_bus(QTestState *qts, int num)
+{
+ QObject *obj;
+ QDict *resp;
+ QList *ret;
+
+ resp = qtest_qmp(qts, "{ 'execute': 'query-pci' }");
+ g_assert(qdict_haskey(resp, "return"));
+
+ ret = qdict_get_qlist(resp, "return");
+ g_assert_nonnull(ret);
+
+ while ((obj = qlist_pop(ret))) {
+ QDict *bus;
+
+ bus = qobject_to(QDict, obj);
+ if (!qdict_haskey(bus, "bus")) {
+ qobject_unref(bus);
+ continue;
+ }
+ if (qdict_get_int(bus, "bus") == num) {
+ qobject_unref(resp);
+ return bus;
+ }
+ qobject_ref(bus);
+ }
+ qobject_unref(resp);
+
+ return NULL;
+}
+
+static char *get_mac(QTestState *qts, const char *name)
+{
+ QDict *resp;
+ char *mac;
+
+ resp = qtest_qmp(qts, "{ 'execute': 'qom-get', "
+ "'arguments': { "
+ "'path': %s, "
+ "'property': 'mac' } }", name);
+
+ g_assert(qdict_haskey(resp, "return"));
+
+ mac = g_strdup(qdict_get_str(resp, "return"));
+
+ qobject_unref(resp);
+
+ return mac;
+}
+
+static void check_one_card(QTestState *qts, bool present,
+ const char *id, const char *mac)
+{
+ QDict *device;
+ QDict *bus;
+ char *addr;
+
+ bus = get_bus(qts, 0);
+ device = find_device(bus, id);
+ if (present) {
+ char *path;
+
+ g_assert_nonnull(device);
+ qobject_unref(device);
+
+ path = g_strdup_printf("/machine/peripheral/%s", id);
+ addr = get_mac(qts, path);
+ g_free(path);
+ g_assert_cmpstr(mac, ==, addr);
+ g_free(addr);
+ } else {
+ g_assert_null(device);
+ }
+
+ qobject_unref(bus);
+}
+
+static void test_on(void)
+{
+ QTestState *qts;
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-device virtio-net,bus=root0,id=standby0,"
+ "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
+ "-device virtio-net,bus=root1,id=primary0,"
+ "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0,
+ 2);
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ machine_stop(qts);
+}
+
+static void test_on_mismatch(void)
+{
+ QTestState *qts;
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-device virtio-net,bus=root0,id=standby0,"
+ "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
+ "-netdev user,id=hs1 "
+ "-device virtio-net,bus=root1,id=primary0,"
+ "failover_pair_id=standby1,netdev=hs1,mac="MAC_PRIMARY0,
+ 2);
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ machine_stop(qts);
+}
+
+static void test_off(void)
+{
+ QTestState *qts;
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-device virtio-net,bus=root0,id=standby0,"
+ "failover=off,netdev=hs0,mac="MAC_STANDBY0" "
+ "-netdev user,id=hs1 "
+ "-device virtio-net,bus=root1,id=primary0,"
+ "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0,
+ 2);
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ machine_stop(qts);
+}
+
+static QDict *get_failover_negociated_event(QTestState *qts)
+{
+ QDict *resp;
+ QDict *data;
+
+ resp = qtest_qmp_eventwait_ref(qts, "FAILOVER_NEGOTIATED");
+ g_assert(qdict_haskey(resp, "data"));
+
+ data = qdict_get_qdict(resp, "data");
+ g_assert(qdict_haskey(data, "device-id"));
+ qobject_ref(data);
+ qobject_unref(resp);
+
+ return data;
+}
+
+static QVirtioPCIDevice *start_virtio_net(QTestState *qts, int bus, int slot,
+ const char *id)
+{
+ QVirtioPCIDevice *dev;
+ uint64_t features;
+ QPCIAddress addr;
+ QDict *resp;
+
+ addr.devfn = QPCI_DEVFN((bus << 5) + slot, 0);
+ dev = virtio_pci_new(pcibus, &addr);
+ g_assert_nonnull(dev);
+ qvirtio_pci_device_enable(dev);
+ qvirtio_start_device(&dev->vdev);
+ features = qvirtio_get_features(&dev->vdev);
+ features = features & ~(QVIRTIO_F_BAD_FEATURE |
+ (1ull << VIRTIO_RING_F_INDIRECT_DESC) |
+ (1ull << VIRTIO_RING_F_EVENT_IDX));
+ qvirtio_set_features(&dev->vdev, features);
+ qvirtio_set_driver_ok(&dev->vdev);
+
+ resp = get_failover_negociated_event(qts);
+ g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, id);
+ qobject_unref(resp);
+
+ return dev;
+}
+
+static void test_enabled(void)
+{
+ QTestState *qts;
+ QVirtioPCIDevice *vdev;
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-device virtio-net,bus=root0,id=standby0,"
+ "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
+ "-netdev user,id=hs1 "
+ "-device virtio-net,bus=root1,id=primary0,"
+ "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0" ",
+ 2);
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ vdev = start_virtio_net(qts, 1, 0, "standby0");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ qos_object_destroy((QOSGraphObject *)vdev);
+ machine_stop(qts);
+}
+
+static void test_hotplug_1(void)
+{
+ QTestState *qts;
+ QVirtioPCIDevice *vdev;
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-device virtio-net,bus=root0,id=standby0,"
+ "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
+ "-netdev user,id=hs1 ", 2);
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ vdev = start_virtio_net(qts, 1, 0, "standby0");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "primary0",
+ "{'bus': 'root1',"
+ "'failover_pair_id': 'standby0',"
+ "'netdev': 'hs1',"
+ "'mac': '"MAC_PRIMARY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ qos_object_destroy((QOSGraphObject *)vdev);
+ machine_stop(qts);
+}
+
+static void test_hotplug_1_reverse(void)
+{
+ QTestState *qts;
+ QVirtioPCIDevice *vdev;
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-netdev user,id=hs1 "
+ "-device virtio-net,bus=root1,id=primary0,"
+ "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0" ",
+ 2);
+
+ check_one_card(qts, false, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "standby0",
+ "{'bus': 'root0',"
+ "'failover': 'on',"
+ "'netdev': 'hs0',"
+ "'mac': '"MAC_STANDBY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ vdev = start_virtio_net(qts, 1, 0, "standby0");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ qos_object_destroy((QOSGraphObject *)vdev);
+ machine_stop(qts);
+}
+
+static void test_hotplug_2(void)
+{
+ QTestState *qts;
+ QVirtioPCIDevice *vdev;
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-netdev user,id=hs1 ",
+ 2);
+
+ check_one_card(qts, false, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "standby0",
+ "{'bus': 'root0',"
+ "'failover': 'on',"
+ "'netdev': 'hs0',"
+ "'mac': '"MAC_STANDBY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ vdev = start_virtio_net(qts, 1, 0, "standby0");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "primary0",
+ "{'bus': 'root1',"
+ "'failover_pair_id': 'standby0',"
+ "'netdev': 'hs1',"
+ "'mac': '"MAC_PRIMARY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ qos_object_destroy((QOSGraphObject *)vdev);
+ machine_stop(qts);
+}
+
+static void test_hotplug_2_reverse(void)
+{
+ QTestState *qts;
+ QVirtioPCIDevice *vdev;
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-netdev user,id=hs1 ",
+ 2);
+
+ check_one_card(qts, false, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "primary0",
+ "{'bus': 'root1',"
+ "'failover_pair_id': 'standby0',"
+ "'netdev': 'hs1',"
+ "'mac': '"MAC_PRIMARY0"'}");
+
+ check_one_card(qts, false, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "standby0",
+ "{'bus': 'root0',"
+ "'failover': 'on',"
+ "'netdev': 'hs0',"
+ "'rombar': 0,"
+ "'romfile': '',"
+ "'mac': '"MAC_STANDBY0"'}");
+
+ /*
+ * XXX: sounds like a bug:
+ * The primary should be hidden until the virtio-net driver
+ * negotiates the VIRTIO_NET_F_STANDBY feature by start_virtio_net()
+ */
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ vdev = start_virtio_net(qts, 1, 0, "standby0");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ qos_object_destroy((QOSGraphObject *)vdev);
+ machine_stop(qts);
+}
+
+static QDict *migrate_status(QTestState *qts)
+{
+ QDict *resp, *ret;
+
+ resp = qtest_qmp(qts, "{ 'execute': 'query-migrate' }");
+ g_assert(qdict_haskey(resp, "return"));
+
+ ret = qdict_get_qdict(resp, "return");
+ g_assert(qdict_haskey(ret, "status"));
+ qobject_ref(ret);
+ qobject_unref(resp);
+
+ return ret;
+}
+
+static QDict *get_unplug_primary_event(QTestState *qts)
+{
+ QDict *resp;
+ QDict *data;
+
+ resp = qtest_qmp_eventwait_ref(qts, "UNPLUG_PRIMARY");
+ g_assert(qdict_haskey(resp, "data"));
+
+ data = qdict_get_qdict(resp, "data");
+ g_assert(qdict_haskey(data, "device-id"));
+ qobject_ref(data);
+ qobject_unref(resp);
+
+ return data;
+}
+
+static void test_migrate_out(gconstpointer opaque)
+{
+ QTestState *qts;
+ QDict *resp, *args, *ret;
+ g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
+ const gchar *status;
+ QVirtioPCIDevice *vdev;
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-netdev user,id=hs1 ",
+ 2);
+
+ check_one_card(qts, false, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "standby0",
+ "{'bus': 'root0',"
+ "'failover': 'on',"
+ "'netdev': 'hs0',"
+ "'mac': '"MAC_STANDBY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ vdev = start_virtio_net(qts, 1, 0, "standby0");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "primary0",
+ "{'bus': 'root1',"
+ "'failover_pair_id': 'standby0',"
+ "'netdev': 'hs1',"
+ "'rombar': 0,"
+ "'romfile': '',"
+ "'mac': '"MAC_PRIMARY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ args = qdict_from_jsonf_nofail("{}");
+ g_assert_nonnull(args);
+ qdict_put_str(args, "uri", uri);
+
+ resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* the event is sent when QEMU asks the OS to unplug the card */
+ resp = get_unplug_primary_event(qts);
+ g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0");
+ qobject_unref(resp);
+
+ /* wait the end of the migration setup phase */
+ while (true) {
+ ret = migrate_status(qts);
+
+ status = qdict_get_str(ret, "status");
+ if (strcmp(status, "wait-unplug") == 0) {
+ qobject_unref(ret);
+ break;
+ }
+
+ /* The migration must not start if the card is not ejected */
+ g_assert_cmpstr(status, !=, "active");
+ g_assert_cmpstr(status, !=, "completed");
+ g_assert_cmpstr(status, !=, "failed");
+ g_assert_cmpstr(status, !=, "cancelling");
+ g_assert_cmpstr(status, !=, "cancelled");
+
+ qobject_unref(ret);
+ }
+
+ if (g_test_slow()) {
+ /* check we stay in wait-unplug while the card is not ejected */
+ for (int i = 0; i < 5; i++) {
+ sleep(1);
+ ret = migrate_status(qts);
+ status = qdict_get_str(ret, "status");
+ g_assert_cmpstr(status, ==, "wait-unplug");
+ qobject_unref(ret);
+ }
+ }
+
+ /* OS unplugs the cards, QEMU can move from wait-unplug state */
+ qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
+
+ while (true) {
+ ret = migrate_status(qts);
+
+ status = qdict_get_str(ret, "status");
+ if (strcmp(status, "completed") == 0) {
+ qobject_unref(ret);
+ break;
+ }
+ g_assert_cmpstr(status, !=, "failed");
+ g_assert_cmpstr(status, !=, "cancelling");
+ g_assert_cmpstr(status, !=, "cancelled");
+ qobject_unref(ret);
+ }
+
+ qtest_qmp_eventwait(qts, "STOP");
+
+ /*
+ * in fact, the card is ejected from the point of view of kernel
+ * but not really from QEMU to be able to hotplug it back if
+ * migration fails. So we can't check that:
+ * check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ * check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+ */
+
+ qos_object_destroy((QOSGraphObject *)vdev);
+ machine_stop(qts);
+}
+
+static QDict *get_migration_event(QTestState *qts)
+{
+ QDict *resp;
+ QDict *data;
+
+ resp = qtest_qmp_eventwait_ref(qts, "MIGRATION");
+ g_assert(qdict_haskey(resp, "data"));
+
+ data = qdict_get_qdict(resp, "data");
+ g_assert(qdict_haskey(data, "status"));
+ qobject_ref(data);
+ qobject_unref(resp);
+
+ return data;
+}
+
+static void test_migrate_in(gconstpointer opaque)
+{
+ QTestState *qts;
+ QDict *resp, *args, *ret;
+ g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-netdev user,id=hs1 "
+ "-incoming defer ",
+ 2);
+
+ check_one_card(qts, false, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "standby0",
+ "{'bus': 'root0',"
+ "'failover': 'on',"
+ "'netdev': 'hs0',"
+ "'mac': '"MAC_STANDBY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "primary0",
+ "{'bus': 'root1',"
+ "'failover_pair_id': 'standby0',"
+ "'netdev': 'hs1',"
+ "'rombar': 0,"
+ "'romfile': '',"
+ "'mac': '"MAC_PRIMARY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ args = qdict_from_jsonf_nofail("{}");
+ g_assert_nonnull(args);
+ qdict_put_str(args, "uri", uri);
+
+ resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
+ args);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ resp = get_migration_event(qts);
+ g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
+ qobject_unref(resp);
+
+ resp = get_failover_negociated_event(qts);
+ g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0");
+ qobject_unref(resp);
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_eventwait(qts, "RESUME");
+
+ ret = migrate_status(qts);
+ g_assert_cmpstr(qdict_get_str(ret, "status"), ==, "completed");
+ qobject_unref(ret);
+
+ machine_stop(qts);
+}
+
+static void test_migrate_abort_wait_unplug(gconstpointer opaque)
+{
+ QTestState *qts;
+ QDict *resp, *args, *ret;
+ g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
+ const gchar *status;
+ QVirtioPCIDevice *vdev;
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-netdev user,id=hs1 ",
+ 2);
+
+ check_one_card(qts, false, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "standby0",
+ "{'bus': 'root0',"
+ "'failover': 'on',"
+ "'netdev': 'hs0',"
+ "'mac': '"MAC_STANDBY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ vdev = start_virtio_net(qts, 1, 0, "standby0");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "primary0",
+ "{'bus': 'root1',"
+ "'failover_pair_id': 'standby0',"
+ "'netdev': 'hs1',"
+ "'rombar': 0,"
+ "'romfile': '',"
+ "'mac': '"MAC_PRIMARY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ args = qdict_from_jsonf_nofail("{}");
+ g_assert_nonnull(args);
+ qdict_put_str(args, "uri", uri);
+
+ resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* the event is sent when QEMU asks the OS to unplug the card */
+ resp = get_unplug_primary_event(qts);
+ g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0");
+ qobject_unref(resp);
+
+ resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* migration has been cancelled while the unplug was in progress */
+
+ /* while the card is not ejected, we must be in "cancelling" state */
+ ret = migrate_status(qts);
+
+ status = qdict_get_str(ret, "status");
+ g_assert_cmpstr(status, ==, "cancelling");
+ qobject_unref(ret);
+
+ /* OS unplugs the cards, QEMU can move from wait-unplug state */
+ qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
+
+ while (true) {
+ ret = migrate_status(qts);
+
+ status = qdict_get_str(ret, "status");
+ if (strcmp(status, "cancelled") == 0) {
+ qobject_unref(ret);
+ break;
+ }
+ g_assert_cmpstr(status, !=, "failed");
+ g_assert_cmpstr(status, !=, "active");
+ qobject_unref(ret);
+ }
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ qos_object_destroy((QOSGraphObject *)vdev);
+ machine_stop(qts);
+}
+
+static void test_migrate_abort_active(gconstpointer opaque)
+{
+ QTestState *qts;
+ QDict *resp, *args, *ret;
+ g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
+ const gchar *status;
+ QVirtioPCIDevice *vdev;
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-netdev user,id=hs1 ",
+ 2);
+
+ check_one_card(qts, false, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "standby0",
+ "{'bus': 'root0',"
+ "'failover': 'on',"
+ "'netdev': 'hs0',"
+ "'mac': '"MAC_STANDBY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ vdev = start_virtio_net(qts, 1, 0, "standby0");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "primary0",
+ "{'bus': 'root1',"
+ "'failover_pair_id': 'standby0',"
+ "'netdev': 'hs1',"
+ "'rombar': 0,"
+ "'romfile': '',"
+ "'mac': '"MAC_PRIMARY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ args = qdict_from_jsonf_nofail("{}");
+ g_assert_nonnull(args);
+ qdict_put_str(args, "uri", uri);
+
+ resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* the event is sent when QEMU asks the OS to unplug the card */
+ resp = get_unplug_primary_event(qts);
+ g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0");
+ qobject_unref(resp);
+
+ /* OS unplugs the cards, QEMU can move from wait-unplug state */
+ qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
+
+ while (true) {
+ ret = migrate_status(qts);
+
+ status = qdict_get_str(ret, "status");
+ if (strcmp(status, "wait-unplug") != 0) {
+ qobject_unref(ret);
+ break;
+ }
+ g_assert_cmpstr(status, !=, "failed");
+ qobject_unref(ret);
+ }
+
+ resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ while (true) {
+ ret = migrate_status(qts);
+
+ status = qdict_get_str(ret, "status");
+ if (strcmp(status, "cancelled") == 0) {
+ qobject_unref(ret);
+ break;
+ }
+ g_assert_cmpstr(status, !=, "failed");
+ g_assert_cmpstr(status, !=, "active");
+ qobject_unref(ret);
+ }
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ qos_object_destroy((QOSGraphObject *)vdev);
+ machine_stop(qts);
+}
+
+static void test_migrate_abort_timeout(gconstpointer opaque)
+{
+ QTestState *qts;
+ QDict *resp, *args, *ret;
+ g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
+ const gchar *status;
+ int total;
+ QVirtioPCIDevice *vdev;
+
+ qts = machine_start(BASE_MACHINE
+ "-netdev user,id=hs0 "
+ "-netdev user,id=hs1 ",
+ 2);
+
+ check_one_card(qts, false, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "standby0",
+ "{'bus': 'root0',"
+ "'failover': 'on',"
+ "'netdev': 'hs0',"
+ "'mac': '"MAC_STANDBY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ vdev = start_virtio_net(qts, 1, 0, "standby0");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+
+ qtest_qmp_device_add(qts, "virtio-net", "primary0",
+ "{'bus': 'root1',"
+ "'failover_pair_id': 'standby0',"
+ "'netdev': 'hs1',"
+ "'rombar': 0,"
+ "'romfile': '',"
+ "'mac': '"MAC_PRIMARY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ args = qdict_from_jsonf_nofail("{}");
+ g_assert_nonnull(args);
+ qdict_put_str(args, "uri", uri);
+
+ resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* the event is sent when QEMU asks the OS to unplug the card */
+ resp = get_unplug_primary_event(qts);
+ g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0");
+ qobject_unref(resp);
+
+ resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* migration has been cancelled while the unplug was in progress */
+
+ /* while the card is not ejected, we must be in "cancelling" state */
+
+ total = 0;
+ while (true) {
+ ret = migrate_status(qts);
+
+ status = qdict_get_str(ret, "status");
+ if (strcmp(status, "cancelled") == 0) {
+ qobject_unref(ret);
+ break;
+ }
+ g_assert_cmpstr(status, ==, "cancelling");
+ g_assert(qdict_haskey(ret, "total-time"));
+ total = qdict_get_int(ret, "total-time");
+ qobject_unref(ret);
+ }
+
+ /*
+ * migration timeout in this case is 30 seconds
+ * check we exit on the timeout (ms)
+ */
+ g_assert_cmpint(total, >, 30000);
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+
+ qos_object_destroy((QOSGraphObject *)vdev);
+ machine_stop(qts);
+}
+
+static void test_multi_out(gconstpointer opaque)
+{
+ QTestState *qts;
+ QDict *resp, *args, *ret;
+ g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
+ const gchar *status, *expected;
+ QVirtioPCIDevice *vdev0, *vdev1;
+
+ qts = machine_start(BASE_MACHINE
+ "-device pcie-root-port,id=root2,addr=0x3,bus=pcie.0,chassis=3 "
+ "-device pcie-root-port,id=root3,addr=0x4,bus=pcie.0,chassis=4 "
+ "-netdev user,id=hs0 "
+ "-netdev user,id=hs1 "
+ "-netdev user,id=hs2 "
+ "-netdev user,id=hs3 ",
+ 4);
+
+ check_one_card(qts, false, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, false, "standby1", MAC_STANDBY1);
+ check_one_card(qts, false, "primary1", MAC_PRIMARY1);
+
+ qtest_qmp_device_add(qts, "virtio-net", "standby0",
+ "{'bus': 'root0',"
+ "'failover': 'on',"
+ "'netdev': 'hs0',"
+ "'mac': '"MAC_STANDBY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, false, "standby1", MAC_STANDBY1);
+ check_one_card(qts, false, "primary1", MAC_PRIMARY1);
+
+ qtest_qmp_device_add(qts, "virtio-net", "primary0",
+ "{'bus': 'root1',"
+ "'failover_pair_id': 'standby0',"
+ "'netdev': 'hs1',"
+ "'rombar': 0,"
+ "'romfile': '',"
+ "'mac': '"MAC_PRIMARY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, false, "standby1", MAC_STANDBY1);
+ check_one_card(qts, false, "primary1", MAC_PRIMARY1);
+
+ vdev0 = start_virtio_net(qts, 1, 0, "standby0");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, false, "standby1", MAC_STANDBY1);
+ check_one_card(qts, false, "primary1", MAC_PRIMARY1);
+
+ qtest_qmp_device_add(qts, "virtio-net", "standby1",
+ "{'bus': 'root2',"
+ "'failover': 'on',"
+ "'netdev': 'hs2',"
+ "'mac': '"MAC_STANDBY1"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, true, "standby1", MAC_STANDBY1);
+ check_one_card(qts, false, "primary1", MAC_PRIMARY1);
+
+ qtest_qmp_device_add(qts, "virtio-net", "primary1",
+ "{'bus': 'root3',"
+ "'failover_pair_id': 'standby1',"
+ "'netdev': 'hs3',"
+ "'rombar': 0,"
+ "'romfile': '',"
+ "'mac': '"MAC_PRIMARY1"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, true, "standby1", MAC_STANDBY1);
+ check_one_card(qts, false, "primary1", MAC_PRIMARY1);
+
+ vdev1 = start_virtio_net(qts, 3, 0, "standby1");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, true, "standby1", MAC_STANDBY1);
+ check_one_card(qts, true, "primary1", MAC_PRIMARY1);
+
+ args = qdict_from_jsonf_nofail("{}");
+ g_assert_nonnull(args);
+ qdict_put_str(args, "uri", uri);
+
+ resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ /* the event is sent when QEMU asks the OS to unplug the card */
+ resp = get_unplug_primary_event(qts);
+ if (strcmp(qdict_get_str(resp, "device-id"), "primary0") == 0) {
+ expected = "primary1";
+ } else if (strcmp(qdict_get_str(resp, "device-id"), "primary1") == 0) {
+ expected = "primary0";
+ } else {
+ g_assert_not_reached();
+ }
+ qobject_unref(resp);
+
+ resp = get_unplug_primary_event(qts);
+ g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, expected);
+ qobject_unref(resp);
+
+ /* wait the end of the migration setup phase */
+ while (true) {
+ ret = migrate_status(qts);
+
+ status = qdict_get_str(ret, "status");
+ if (strcmp(status, "wait-unplug") == 0) {
+ qobject_unref(ret);
+ break;
+ }
+
+ /* The migration must not start if the card is not ejected */
+ g_assert_cmpstr(status, !=, "active");
+ g_assert_cmpstr(status, !=, "completed");
+ g_assert_cmpstr(status, !=, "failed");
+ g_assert_cmpstr(status, !=, "cancelling");
+ g_assert_cmpstr(status, !=, "cancelled");
+
+ qobject_unref(ret);
+ }
+
+ /* OS unplugs primary1, but we must wait the second */
+ qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
+
+ ret = migrate_status(qts);
+ status = qdict_get_str(ret, "status");
+ g_assert_cmpstr(status, ==, "wait-unplug");
+ qobject_unref(ret);
+
+ if (g_test_slow()) {
+ /* check we stay in wait-unplug while the card is not ejected */
+ for (int i = 0; i < 5; i++) {
+ sleep(1);
+ ret = migrate_status(qts);
+ status = qdict_get_str(ret, "status");
+ g_assert_cmpstr(status, ==, "wait-unplug");
+ qobject_unref(ret);
+ }
+ }
+
+ /* OS unplugs primary0, QEMU can move from wait-unplug state */
+ qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_SEL_BASE, 2);
+ qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
+
+ while (true) {
+ ret = migrate_status(qts);
+
+ status = qdict_get_str(ret, "status");
+ if (strcmp(status, "completed") == 0) {
+ qobject_unref(ret);
+ break;
+ }
+ g_assert_cmpstr(status, !=, "failed");
+ g_assert_cmpstr(status, !=, "cancelling");
+ g_assert_cmpstr(status, !=, "cancelled");
+ qobject_unref(ret);
+ }
+
+ qtest_qmp_eventwait(qts, "STOP");
+
+ qos_object_destroy((QOSGraphObject *)vdev0);
+ qos_object_destroy((QOSGraphObject *)vdev1);
+ machine_stop(qts);
+}
+
+static void test_multi_in(gconstpointer opaque)
+{
+ QTestState *qts;
+ QDict *resp, *args, *ret;
+ g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
+
+ qts = machine_start(BASE_MACHINE
+ "-device pcie-root-port,id=root2,addr=0x3,bus=pcie.0,chassis=3 "
+ "-device pcie-root-port,id=root3,addr=0x4,bus=pcie.0,chassis=4 "
+ "-netdev user,id=hs0 "
+ "-netdev user,id=hs1 "
+ "-netdev user,id=hs2 "
+ "-netdev user,id=hs3 "
+ "-incoming defer ",
+ 4);
+
+ check_one_card(qts, false, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, false, "standby1", MAC_STANDBY1);
+ check_one_card(qts, false, "primary1", MAC_PRIMARY1);
+
+ qtest_qmp_device_add(qts, "virtio-net", "standby0",
+ "{'bus': 'root0',"
+ "'failover': 'on',"
+ "'netdev': 'hs0',"
+ "'mac': '"MAC_STANDBY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, false, "standby1", MAC_STANDBY1);
+ check_one_card(qts, false, "primary1", MAC_PRIMARY1);
+
+ qtest_qmp_device_add(qts, "virtio-net", "primary0",
+ "{'bus': 'root1',"
+ "'failover_pair_id': 'standby0',"
+ "'netdev': 'hs1',"
+ "'rombar': 0,"
+ "'romfile': '',"
+ "'mac': '"MAC_PRIMARY0"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, false, "standby1", MAC_STANDBY1);
+ check_one_card(qts, false, "primary1", MAC_PRIMARY1);
+
+ qtest_qmp_device_add(qts, "virtio-net", "standby1",
+ "{'bus': 'root2',"
+ "'failover': 'on',"
+ "'netdev': 'hs2',"
+ "'mac': '"MAC_STANDBY1"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, true, "standby1", MAC_STANDBY1);
+ check_one_card(qts, false, "primary1", MAC_PRIMARY1);
+
+ qtest_qmp_device_add(qts, "virtio-net", "primary1",
+ "{'bus': 'root3',"
+ "'failover_pair_id': 'standby1',"
+ "'netdev': 'hs3',"
+ "'rombar': 0,"
+ "'romfile': '',"
+ "'mac': '"MAC_PRIMARY1"'}");
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, false, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, true, "standby1", MAC_STANDBY1);
+ check_one_card(qts, false, "primary1", MAC_PRIMARY1);
+
+ args = qdict_from_jsonf_nofail("{}");
+ g_assert_nonnull(args);
+ qdict_put_str(args, "uri", uri);
+
+ resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
+ args);
+ g_assert(qdict_haskey(resp, "return"));
+ qobject_unref(resp);
+
+ resp = get_migration_event(qts);
+ g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
+ qobject_unref(resp);
+
+ resp = get_failover_negociated_event(qts);
+ g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0");
+ qobject_unref(resp);
+
+ resp = get_failover_negociated_event(qts);
+ g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby1");
+ qobject_unref(resp);
+
+ check_one_card(qts, true, "standby0", MAC_STANDBY0);
+ check_one_card(qts, true, "primary0", MAC_PRIMARY0);
+ check_one_card(qts, true, "standby1", MAC_STANDBY1);
+ check_one_card(qts, true, "primary1", MAC_PRIMARY1);
+
+ qtest_qmp_eventwait(qts, "RESUME");
+
+ ret = migrate_status(qts);
+ g_assert_cmpstr(qdict_get_str(ret, "status"), ==, "completed");
+ qobject_unref(ret);
+
+ machine_stop(qts);
+}
+
+int main(int argc, char **argv)
+{
+ const gchar *tmpdir = g_get_tmp_dir();
+ gchar *tmpfile = g_strdup_printf("%s/failover_test_migrate-%u-%u",
+ tmpdir, getpid(), g_test_rand_int());
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("failover-virtio-net/params/error/id", test_error_id);
+ qtest_add_func("failover-virtio-net/params/error/pcie", test_error_pcie);
+ qtest_add_func("failover-virtio-net/params/on", test_on);
+ qtest_add_func("failover-virtio-net/params/on_mismatch",
+ test_on_mismatch);
+ qtest_add_func("failover-virtio-net/params/off", test_off);
+ qtest_add_func("failover-virtio-net/params/enabled", test_enabled);
+ qtest_add_func("failover-virtio-net/hotplug_1", test_hotplug_1);
+ qtest_add_func("failover-virtio-net/hotplug_1_reverse",
+ test_hotplug_1_reverse);
+ qtest_add_func("failover-virtio-net/hotplug_2", test_hotplug_2);
+ qtest_add_func("failover-virtio-net/hotplug_2_reverse",
+ test_hotplug_2_reverse);
+ qtest_add_data_func("failover-virtio-net/migrate/out", tmpfile,
+ test_migrate_out);
+ qtest_add_data_func("failover-virtio-net/migrate/in", tmpfile,
+ test_migrate_in);
+ qtest_add_data_func("failover-virtio-net/migrate/abort/wait-unplug",
+ tmpfile, test_migrate_abort_wait_unplug);
+ qtest_add_data_func("failover-virtio-net/migrate/abort/active", tmpfile,
+ test_migrate_abort_active);
+ if (g_test_slow()) {
+ qtest_add_data_func("failover-virtio-net/migrate/abort/timeout",
+ tmpfile, test_migrate_abort_timeout);
+ }
+ qtest_add_data_func("failover-virtio-net/multi/out",
+ tmpfile, test_multi_out);
+ qtest_add_data_func("failover-virtio-net/multi/in",
+ tmpfile, test_multi_in);
+
+ ret = g_test_run();
+
+ unlink(tmpfile);
+ g_free(tmpfile);
+
+ return ret;
+}
diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target
index 2c05c90d17..1d967901bd 100644
--- a/tests/tcg/aarch64/Makefile.target
+++ b/tests/tcg/aarch64/Makefile.target
@@ -8,8 +8,8 @@ VPATH += $(ARM_SRC)
AARCH64_SRC=$(SRC_PATH)/tests/tcg/aarch64
VPATH += $(AARCH64_SRC)
-# Float-convert Tests
-AARCH64_TESTS=fcvt
+# Base architecture tests
+AARCH64_TESTS=fcvt pcalign-a64
fcvt: LDFLAGS+=-lm
diff --git a/tests/tcg/aarch64/pcalign-a64.c b/tests/tcg/aarch64/pcalign-a64.c
new file mode 100644
index 0000000000..6b9277f919
--- /dev/null
+++ b/tests/tcg/aarch64/pcalign-a64.c
@@ -0,0 +1,37 @@
+/* Test PC misalignment exception */
+
+#include <assert.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static void *expected;
+
+static void sigbus(int sig, siginfo_t *info, void *vuc)
+{
+ assert(info->si_code == BUS_ADRALN);
+ assert(info->si_addr == expected);
+ exit(EXIT_SUCCESS);
+}
+
+int main()
+{
+ void *tmp;
+
+ struct sigaction sa = {
+ .sa_sigaction = sigbus,
+ .sa_flags = SA_SIGINFO
+ };
+
+ if (sigaction(SIGBUS, &sa, NULL) < 0) {
+ perror("sigaction");
+ return EXIT_FAILURE;
+ }
+
+ asm volatile("adr %0, 1f + 1\n\t"
+ "str %0, %1\n\t"
+ "br %0\n"
+ "1:"
+ : "=&r"(tmp), "=m"(expected));
+ abort();
+}
diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target
index 5ab59ed6ce..f509d823d4 100644
--- a/tests/tcg/arm/Makefile.target
+++ b/tests/tcg/arm/Makefile.target
@@ -29,6 +29,10 @@ run-fcvt: fcvt
$(call run-test,fcvt,$(QEMU) $<,"$< on $(TARGET_NAME)")
$(call diff-out,fcvt,$(ARM_SRC)/fcvt.ref)
+# PC alignment test
+ARM_TESTS += pcalign-a32
+pcalign-a32: CFLAGS+=-marm
+
ifeq ($(CONFIG_ARM_COMPATIBLE_SEMIHOSTING),y)
# Semihosting smoke test for linux-user
diff --git a/tests/tcg/arm/pcalign-a32.c b/tests/tcg/arm/pcalign-a32.c
new file mode 100644
index 0000000000..3c9c8cc97b
--- /dev/null
+++ b/tests/tcg/arm/pcalign-a32.c
@@ -0,0 +1,46 @@
+/* Test PC misalignment exception */
+
+#ifdef __thumb__
+#error "This test must be compiled for ARM"
+#endif
+
+#include <assert.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static void *expected;
+
+static void sigbus(int sig, siginfo_t *info, void *vuc)
+{
+ assert(info->si_code == BUS_ADRALN);
+ assert(info->si_addr == expected);
+ exit(EXIT_SUCCESS);
+}
+
+int main()
+{
+ void *tmp;
+
+ struct sigaction sa = {
+ .sa_sigaction = sigbus,
+ .sa_flags = SA_SIGINFO
+ };
+
+ if (sigaction(SIGBUS, &sa, NULL) < 0) {
+ perror("sigaction");
+ return EXIT_FAILURE;
+ }
+
+ asm volatile("adr %0, 1f + 2\n\t"
+ "str %0, %1\n\t"
+ "bx %0\n"
+ "1:"
+ : "=&r"(tmp), "=m"(expected));
+
+ /*
+ * From v8, it is CONSTRAINED UNPREDICTABLE whether BXWritePC aligns
+ * the address or not. If so, we can legitimately fall through.
+ */
+ return EXIT_SUCCESS;
+}