From: "Daniel P. Berrangé" <berrange@redhat.com>
To: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Cc: qemu-devel@nongnu.org, thuth@redhat.com,
jean-philippe@linaro.org, alex.bennee@linaro.org,
eric.auger@redhat.com, smostafa@google.com
Subject: Re: [PATCH v2] tests/functional: test device passthrough on aarch64
Date: Wed, 2 Jul 2025 14:09:07 +0100 [thread overview]
Message-ID: <aGUvc6XJjsluZtH_@redhat.com> (raw)
In-Reply-To: <20250627200222.5172-1-pierrick.bouvier@linaro.org>
On Fri, Jun 27, 2025 at 01:02:22PM -0700, Pierrick Bouvier wrote:
> This test allows to document and exercise device passthrough, using a
> nested virtual machine setup. Two disks are generated and passed to the
> VM, and their content is compared to original images.
>
> Guest and nested guests commands are executed through two scripts, and
> init used in both system is configured to trigger a kernel panic in case
> any command fails. This is more reliable and readable than executing all
> commands through prompt injection and trying to guess what failed.
>
> Initially, this test was supposed to test smmuv3 nested emulation
> (combining both stages of translation), but I could not find any setup
> (kernel + vmm) able to do the passthrough correctly, despite several
> tries.
>
> Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
> ---
> tests/functional/meson.build | 2 +
> .../test_aarch64_device_passthrough.py | 142 ++++++++++++++++++
> 2 files changed, 144 insertions(+)
> create mode 100755 tests/functional/test_aarch64_device_passthrough.py
>
> diff --git a/tests/functional/meson.build b/tests/functional/meson.build
> index 3021928a9d4..6cc78abb123 100644
> --- a/tests/functional/meson.build
> +++ b/tests/functional/meson.build
> @@ -13,6 +13,7 @@ endif
> test_timeouts = {
> 'aarch64_aspeed_ast2700' : 600,
> 'aarch64_aspeed_ast2700fc' : 600,
> + 'aarch64_device_passthrough' : 720,
> 'aarch64_imx8mp_evk' : 240,
> 'aarch64_raspi4' : 480,
> 'aarch64_reverse_debug' : 180,
> @@ -84,6 +85,7 @@ tests_aarch64_system_quick = [
> tests_aarch64_system_thorough = [
> 'aarch64_aspeed_ast2700',
> 'aarch64_aspeed_ast2700fc',
> + 'aarch64_device_passthrough',
> 'aarch64_imx8mp_evk',
> 'aarch64_raspi3',
> 'aarch64_raspi4',
> diff --git a/tests/functional/test_aarch64_device_passthrough.py b/tests/functional/test_aarch64_device_passthrough.py
> new file mode 100755
> index 00000000000..1f3f158a9ff
> --- /dev/null
> +++ b/tests/functional/test_aarch64_device_passthrough.py
> @@ -0,0 +1,142 @@
> +#!/usr/bin/env python3
> +#
> +# Boots a nested guest and compare content of a device (passthrough) to a
> +# reference image. Both vfio group and iommufd passthrough methods are tested.
> +#
> +# Copyright (c) 2025 Linaro Ltd.
> +#
> +# Author: Pierrick Bouvier <pierrick.bouvier@linaro.org>
> +#
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +import os
> +
> +from qemu_test import QemuSystemTest, Asset
> +from qemu_test import exec_command, wait_for_console_pattern
> +from qemu_test import exec_command_and_wait_for_pattern
> +from random import randbytes
> +
> +guest_script = '''
> +#!/usr/bin/env bash
> +
> +set -euo pipefail
> +set -x
> +
> +# find disks from nvme serial
> +dev_vfio=$(lsblk --nvme | grep vfio | cut -f 1 -d ' ')
> +dev_iommufd=$(lsblk --nvme | grep iommufd | cut -f 1 -d ' ')
> +pci_vfio=$(basename $(readlink -f /sys/block/$dev_vfio/../../../))
> +pci_iommufd=$(basename $(readlink -f /sys/block/$dev_iommufd/../../../))
> +
> +# bind disks to vfio
> +for p in "$pci_vfio" "$pci_iommufd"; do
> + if [ "$(cat /sys/bus/pci/devices/$p/driver_override)" == vfio-pci ]; then
> + continue
> + fi
> + echo $p > /sys/bus/pci/drivers/nvme/unbind
> + echo vfio-pci > /sys/bus/pci/devices/$p/driver_override
> + echo $p > /sys/bus/pci/drivers/vfio-pci/bind
> +done
> +
> +# boot nested guest and execute /host/nested_guest.sh
> +# one disk is passed through vfio group, the other, through iommufd
> +qemu-system-aarch64 \
> +-M virt \
> +-display none \
> +-serial stdio \
> +-cpu host \
> +-enable-kvm \
> +-m 1G \
> +-kernel /host/Image.gz \
> +-drive format=raw,file=/host/guest.ext4,if=virtio \
> +-append "root=/dev/vda init=/init -- bash /host/nested_guest.sh" \
> +-virtfs local,path=/host,mount_tag=host,security_model=mapped,readonly=off \
> +-device vfio-pci,host=$pci_vfio \
> +-object iommufd,id=iommufd0 \
> +-device vfio-pci,host=$pci_iommufd,iommufd=iommufd0
> +'''
> +
> +nested_guest_script = '''
> +#!/usr/bin/env bash
> +
> +set -euo pipefail
> +set -x
> +
> +image_vfio=/host/disk_vfio
> +image_iommufd=/host/disk_iommufd
> +
> +dev_vfio=$(lsblk --nvme | grep vfio | cut -f 1 -d ' ')
> +dev_iommufd=$(lsblk --nvme | grep iommufd | cut -f 1 -d ' ')
> +
> +# compare if devices are identical to original images
> +diff $image_vfio /dev/$dev_vfio
> +diff $image_iommufd /dev/$dev_iommufd
> +
> +echo device_passthrough_test_ok
> +'''
> +
> +class Aarch64DevicePassthrough(QemuSystemTest):
> +
> + # https://github.com/pbo-linaro/qemu-linux-stack
> + #
> + # Linux kernel is compiled with defconfig +
> + # IOMMUFD + VFIO_DEVICE_CDEV + ARM_SMMU_V3_IOMMUFD
> + # https://docs.kernel.org/driver-api/vfio.html#vfio-device-cde
> + ASSET_DEVICE_PASSTHROUGH_STACK = Asset(
> + ('https://fileserver.linaro.org/s/fx5DXxBYme8dw2G/'
> + 'download/device_passthrough.tar.xz'),
> + '812750b664d61c2986f2b149939ae28cafbd60d53e9c7e4b16e97143845e196d')
> +
> + # This tests the device passthrough implementation, by booting a VM
> + # supporting it with two nvme disks attached, and launching a nested VM
> + # reading their content.
> + def test_aarch64_device_passthrough(self):
> + self.set_machine('virt')
> + self.require_accelerator('tcg')
> +
> + self.vm.set_console()
> +
> + stack_path_tar_gz = self.ASSET_DEVICE_PASSTHROUGH_STACK.fetch()
> + self.archive_extract(stack_path_tar_gz, format="tar")
> +
> + stack = self.scratch_file('out')
> + kernel = os.path.join(stack, 'Image.gz')
> + rootfs_host = os.path.join(stack, 'host.ext4')
> + disk_vfio = os.path.join(stack, 'disk_vfio')
> + disk_iommufd = os.path.join(stack, 'disk_iommufd')
> + guest_cmd = os.path.join(stack, 'guest.sh')
> + nested_guest_cmd = os.path.join(stack, 'nested_guest.sh')
Don't incrementally create paths like this - use the
'scratch_file' method for all components
ie
kernel = self.scratch_file('out', 'Image.gz')
rootfs_host = self.scratch_file('out', 'host.ext4')
...etc...
> + # we generate two random disks
> + with open(disk_vfio, "wb") as d: d.write(randbytes(512))
> + with open(disk_iommufd, "wb") as d: d.write(randbytes(1024))
> + with open(guest_cmd, 'w') as s: s.write(guest_script)
> + with open(nested_guest_cmd, 'w') as s: s.write(nested_guest_script)
> +
> + self.vm.add_args('-cpu', 'max')
> + self.vm.add_args('-m', '2G')
> + self.vm.add_args('-M', 'virt,'
> + 'virtualization=on,'
> + 'gic-version=max,'
> + 'iommu=smmuv3')
> + self.vm.add_args('-kernel', kernel)
> + self.vm.add_args('-drive', f'format=raw,file={rootfs_host}')
> + self.vm.add_args('-drive',
> + f'file={disk_vfio},if=none,id=vfio,format=raw')
> + self.vm.add_args('-device', 'nvme,serial=vfio,drive=vfio')
> + self.vm.add_args('-drive',
> + f'file={disk_iommufd},if=none,id=iommufd,format=raw')
> + self.vm.add_args('-device', 'nvme,serial=iommufd,drive=iommufd')
> + self.vm.add_args('-virtfs',
> + f'local,path={stack}/,mount_tag=host,'
> + 'security_model=mapped,readonly=off')
> + # boot and execute guest script
> + # init will trigger a kernel panic if script fails
> + self.vm.add_args('-append',
> + 'root=/dev/vda init=/init -- bash /host/guest.sh')
> +
> + self.vm.launch()
> + wait_for_console_pattern(self, 'device_passthrough_test_ok',
> + failure_message='Kernel panic')
> +
> +if __name__ == '__main__':
> + QemuSystemTest.main()
> --
> 2.47.2
>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
next prev parent reply other threads:[~2025-07-02 15:32 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-27 20:02 [PATCH v2] tests/functional: test device passthrough on aarch64 Pierrick Bouvier
2025-06-27 20:04 ` Pierrick Bouvier
2025-07-02 12:51 ` Cédric Le Goater
2025-07-02 13:35 ` Alex Bennée
2025-07-02 13:48 ` Cédric Le Goater
2025-07-02 20:06 ` Pierrick Bouvier
2025-07-02 20:00 ` Pierrick Bouvier
2025-07-02 13:09 ` Daniel P. Berrangé [this message]
2025-07-02 20:08 ` Pierrick Bouvier
2025-07-03 9:32 ` Daniel P. Berrangé
2025-07-03 14:29 ` Pierrick Bouvier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aGUvc6XJjsluZtH_@redhat.com \
--to=berrange@redhat.com \
--cc=alex.bennee@linaro.org \
--cc=eric.auger@redhat.com \
--cc=jean-philippe@linaro.org \
--cc=pierrick.bouvier@linaro.org \
--cc=qemu-devel@nongnu.org \
--cc=smostafa@google.com \
--cc=thuth@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).