diff mbox series

[v7,8/8] support/testing: add virglrenderer test

Message ID 20260515-virglrenderer-v7-8-1f1814b65ccd@gmail.com
State Changes Requested
Headers show
Series Add virglrenderer support | expand

Commit Message

Joseph Kogut May 16, 2026, 2:03 a.m. UTC
Add basic smoke test for glmark2 running in a nested qemu guest with
virtio-gpu-gl-pci.

The outer machine uses the Mesa softpipe driver for GLES support with
minimal dependencies. Just test virgl for now, as lavapipe
(software Vulkan driver) requires LLVM, and this validates
virglrenderer.

Signed-off-by: Joseph Kogut <joseph.kogut@gmail.com>
---
 DEVELOPERS                                         |   1 +
 .../testing/tests/package/test_virglrenderer.py    | 117 +++++++++++++++++++++
 .../test_virglrenderer/linux-virgl.fragment        |   6 ++
 .../test_virglrenderer/remove-qemu-blobs.sh        |  12 +++
 .../rootfs-overlay/etc/profile.d/stty-raw.sh       |   3 +
 5 files changed, 139 insertions(+)

Comments

Arnout Vandecappelle May 21, 2026, 8:45 p.m. UTC | #1
On 16/05/2026 04:03, Joseph Kogut wrote:
> Add basic smoke test for glmark2 running in a nested qemu guest with
> virtio-gpu-gl-pci.
> 
> The outer machine uses the Mesa softpipe driver for GLES support with
> minimal dependencies. Just test virgl for now, as lavapipe
> (software Vulkan driver) requires LLVM, and this validates
> virglrenderer.
> 
> Signed-off-by: Joseph Kogut <joseph.kogut@gmail.com>
> ---
>   DEVELOPERS                                         |   1 +
>   .../testing/tests/package/test_virglrenderer.py    | 117 +++++++++++++++++++++
>   .../test_virglrenderer/linux-virgl.fragment        |   6 ++
>   .../test_virglrenderer/remove-qemu-blobs.sh        |  12 +++
>   .../rootfs-overlay/etc/profile.d/stty-raw.sh       |   3 +
>   5 files changed, 139 insertions(+)
> 
> diff --git a/DEVELOPERS b/DEVELOPERS
> index 774eb0b746..0c0779e8b3 100644
> --- a/DEVELOPERS
> +++ b/DEVELOPERS
> @@ -1766,6 +1766,7 @@ F:	package/sentry-cli/
>   F:	package/sentry-native/
>   F:	package/unclutter-xfixes/
>   F:	package/virglrenderer/
> +F:	support/testing/tests/package/test_virglrenderer.py
>   
>   N:	Joshua Henderson <joshua.henderson@microchip.com>
>   F:	package/qt5/qt5wayland/EGL_NOT_INITIALIZED
> diff --git a/support/testing/tests/package/test_virglrenderer.py b/support/testing/tests/package/test_virglrenderer.py
> new file mode 100644
> index 0000000000..521a4b67cd
> --- /dev/null
> +++ b/support/testing/tests/package/test_virglrenderer.py
> @@ -0,0 +1,117 @@
> +import os
> +
> +import infra
> +import infra.basetest
> +
> +
> +class TestVirglrendererNestedQemu(infra.basetest.BRTest):
> +    # We reuse the same initramfs-enabled kernel for the outer target and

  I think it's better to separate them, that makes it more clear what should be 
enabled in the host and what should be enabled in the guest. For the kernel it's 
OK to reuse them, but I think the rootfs's should be separated.

  Therefore also I think it's better to use a cpio than an initramfs.

> +    # nested guest. This fragment adds initramfs and 9p support so the outer
> +    # target can mount the runner's images directory and launch the same kernel
> +    # under target-side QEMU. We also enable DRM_VGEM to create the necessary

  I guess that's the only reason why one of the prebuilt kernels can't be used...

> +    # DRI device node, a requirement of qemu when using virgl.
> +    kern_frag = \
> +        infra.filepath("tests/package/test_virglrenderer/linux-virgl.fragment")
> +    post_build = \
> +        infra.filepath("tests/package/test_virglrenderer/remove-qemu-blobs.sh")
> +    rootfs_overlay = \
> +        infra.filepath("tests/package/test_virglrenderer/rootfs-overlay")
> +
> +    # Softpipe has no hardware dependencies and does not pull LLVM into the
> +    # build, unlike llvmpipe/lavapipe.

  Since there are no hardware dependencies, we can actually run it on aarch64 
(or arm) instead off x86_64, no? That is preferable, because that we detect 
cross-compilation issues and everything is emulated so there's definitely no 
dependency on the build environment.

> +    config = \
> +        f"""
> +        BR2_x86_64=y
> +        BR2_x86_nehalem=y
> +        BR2_TOOLCHAIN_EXTERNAL=y
> +        BR2_TOOLCHAIN_EXTERNAL_BOOTLIN_X86_64_CORE_I7_GLIBC_STABLE=y
> +        BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y

  Why do we need eudev?

> +        BR2_TARGET_GENERIC_GETTY_PORT="ttyS0"
> +        BR2_ROOTFS_OVERLAY="{rootfs_overlay}"
> +        BR2_ROOTFS_POST_BUILD_SCRIPT="{post_build}"
> +        BR2_LINUX_KERNEL=y
> +        BR2_LINUX_KERNEL_CUSTOM_VERSION=y
> +        BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.21"
> +        BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
> +        BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/x86_64/linux.config"
> +        BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="{kern_frag}"
> +        BR2_LINUX_KERNEL_NEEDS_HOST_LIBELF=y
> +        # BR2_PACKAGE_EUDEV_ENABLE_HWDB is not set
> +        BR2_PACKAGE_GLMARK2=y
> +        BR2_PACKAGE_LIBGLVND=y
> +        BR2_PACKAGE_MESA3D=y
> +        BR2_PACKAGE_MESA3D_GALLIUM_DRIVER_SOFTPIPE=y
> +        BR2_PACKAGE_MESA3D_GALLIUM_DRIVER_VIRGL=y

  This again confuses me - why do you need both softpipe and virgl? Or is it 
just because we are reusing the same binary in the host and guest?

> +        BR2_PACKAGE_MESA3D_OPENGL_EGL=y
> +        BR2_PACKAGE_MESA3D_OPENGL_ES=y
> +        BR2_PACKAGE_QEMU=y
> +        BR2_PACKAGE_QEMU_SYSTEM=y
> +        # BR2_PACKAGE_QEMU_SYSTEM_KVM is not set
> +        BR2_PACKAGE_QEMU_SYSTEM_TCG=y
> +        BR2_PACKAGE_QEMU_CHOOSE_TARGETS=y
> +        BR2_PACKAGE_QEMU_TARGET_X86_64=y
> +        BR2_PACKAGE_QEMU_VIRGLRENDERER=y
> +        BR2_TARGET_ROOTFS_INITRAMFS=y
> +        # BR2_TARGET_ROOTFS_TAR is not set
> +        """
> +
> +    def boot_outer(self):
> +        kern = os.path.join(self.builddir, "images", "bzImage")
> +        img_dir = os.path.join(self.builddir, "images")
> +        virtfs_tag = "br-imgs"
> +        virtfs_opts = [
> +            f"local,path={img_dir}",
> +            f"mount_tag={virtfs_tag}",
> +            "security_model=mapped-xattr",
> +            "readonly=on"
> +        ]
> +        self.emulator.boot(arch="x86_64",
> +                           kernel=kern,
> +                           kernel_cmdline=["console=ttyS0"],
> +                           options=["-cpu", "Nehalem",
> +                                    "-m", "1536M",
> +                                    "-smp", "4",

  Any reason to use more than one CPU?

> +                                    "-vga", "none",
> +                                    "-virtfs", ",".join(virtfs_opts)])
> +        self.emulator.login()
> +        self.assertRunOk(f"mount -t 9p {virtfs_tag} /mnt/")
> +
> +    def boot_nested(self):
> +        qemu_cmd = "env LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=softpipe"
> +        qemu_cmd += " qemu-system-x86_64"
> +        qemu_cmd += " -M q35,accel=tcg"
> +        qemu_cmd += " -cpu Nehalem"
> +        qemu_cmd += " -m 512M"
> +        qemu_cmd += " -smp 4"
> +        qemu_cmd += " -nodefaults"
> +        qemu_cmd += " -display egl-headless,gl=on"
> +        qemu_cmd += " -device virtio-gpu-gl-pci"
> +        qemu_cmd += " -serial stdio"
> +        qemu_cmd += " -kernel /mnt/bzImage"
> +        qemu_cmd += " -append 'console=ttyS0 br-nested-virgl'"
> +
> +        # qemu runs in the foreground on the current serial console. Once it
> +        # starts, the next login prompt belongs to the nested guest.
> +        self.emulator.qemu.sendline(qemu_cmd)
> +        self.emulator.login(timeout=180)
> +
> +    def test_run(self):
> +        self.boot_outer()
> +        self.assertRunOk("while [ ! -e /dev/dri/renderD128 ]; do sleep 1; done",
> +                         timeout=30)
> +
> +        self.boot_nested()
> +
> +        out, ret = self.emulator.run("cat /proc/cmdline")
> +        self.assertEqual(ret, 0)
> +        self.assertIn("br-nested-virgl", out[0])
> +
> +        cmd = "env MESA_LOADER_DRIVER_OVERRIDE=virtio_gpu"
> +        cmd += " glmark2-es2-drm --validate --off-screen"
> +        cmd += " --benchmark build:use-vbo=false"
> +        out, ret = self.emulator.run(cmd, timeout=60)
> +        self.assertEqual(ret, 0)

  So, this would fail if qemu wasn't built with virglrenderer?
> +
> +        renderer = "\n".join([line for line in out if "GL_RENDERER:" in line])
> +        self.assertRegex(renderer, r"(?i)virgl")

  Or just this one?

> +        self.assertRegex(renderer, r"(?i)softpipe")
> diff --git a/support/testing/tests/package/test_virglrenderer/linux-virgl.fragment b/support/testing/tests/package/test_virglrenderer/linux-virgl.fragment
> new file mode 100644
> index 0000000000..1023aedc6f
> --- /dev/null
> +++ b/support/testing/tests/package/test_virglrenderer/linux-virgl.fragment
> @@ -0,0 +1,6 @@
> +CONFIG_BLK_DEV_INITRD=y
> +CONFIG_DRM_VGEM=y
> +CONFIG_NET_9P=y
> +CONFIG_NET_9P_VIRTIO=y
> +CONFIG_9P_FS=y
> +CONFIG_9P_FS_POSIX_ACL=y
> diff --git a/support/testing/tests/package/test_virglrenderer/remove-qemu-blobs.sh b/support/testing/tests/package/test_virglrenderer/remove-qemu-blobs.sh
> new file mode 100755
> index 0000000000..ff040aa96b
> --- /dev/null
> +++ b/support/testing/tests/package/test_virglrenderer/remove-qemu-blobs.sh
> @@ -0,0 +1,12 @@
> +#!/bin/sh
> +
> +set -eu
> +
> +qemu_dir="${TARGET_DIR}/usr/share/qemu"
> +
> +find "${qemu_dir}" -type f \
> +	! -name 'bios-256k.bin' \
> +	! -name 'kvmvapic.bin' \
> +	! -name 'linuxboot.bin' \
> +	! -name 'linuxboot_dma.bin' \
> +	-delete

  Why do you need to remove those? Is it just because otherwise the cpio becomes 
too big to be used for an initramfs?

  Regards,
  Arnout

> diff --git a/support/testing/tests/package/test_virglrenderer/rootfs-overlay/etc/profile.d/stty-raw.sh b/support/testing/tests/package/test_virglrenderer/rootfs-overlay/etc/profile.d/stty-raw.sh
> new file mode 100644
> index 0000000000..bc1857a910
> --- /dev/null
> +++ b/support/testing/tests/package/test_virglrenderer/rootfs-overlay/etc/profile.d/stty-raw.sh
> @@ -0,0 +1,3 @@
> +# Avoid double-cooking the nested serial console, otherwise the test
> +# infrastructure cannot reliably retrieve return codes.
> +grep -Fq br-nested-virgl /proc/cmdline && stty raw
>
Joseph Kogut May 22, 2026, 11:24 a.m. UTC | #2
Hello Arnout,

On Thu, May 21, 2026 at 3:45 PM Arnout Vandecappelle <arnout@rnout.be> wrote:
>
>
>
> On 16/05/2026 04:03, Joseph Kogut wrote:
> > Add basic smoke test for glmark2 running in a nested qemu guest with
> > virtio-gpu-gl-pci.
> >
> > The outer machine uses the Mesa softpipe driver for GLES support with
> > minimal dependencies. Just test virgl for now, as lavapipe
> > (software Vulkan driver) requires LLVM, and this validates
> > virglrenderer.
> >
> > Signed-off-by: Joseph Kogut <joseph.kogut@gmail.com>
> > ---
> >   DEVELOPERS                                         |   1 +
> >   .../testing/tests/package/test_virglrenderer.py    | 117 +++++++++++++++++++++
> >   .../test_virglrenderer/linux-virgl.fragment        |   6 ++
> >   .../test_virglrenderer/remove-qemu-blobs.sh        |  12 +++
> >   .../rootfs-overlay/etc/profile.d/stty-raw.sh       |   3 +
> >   5 files changed, 139 insertions(+)
> >
> > diff --git a/DEVELOPERS b/DEVELOPERS
> > index 774eb0b746..0c0779e8b3 100644
> > --- a/DEVELOPERS
> > +++ b/DEVELOPERS
> > @@ -1766,6 +1766,7 @@ F:      package/sentry-cli/
> >   F:  package/sentry-native/
> >   F:  package/unclutter-xfixes/
> >   F:  package/virglrenderer/
> > +F:   support/testing/tests/package/test_virglrenderer.py
> >
> >   N:  Joshua Henderson <joshua.henderson@microchip.com>
> >   F:  package/qt5/qt5wayland/EGL_NOT_INITIALIZED
> > diff --git a/support/testing/tests/package/test_virglrenderer.py b/support/testing/tests/package/test_virglrenderer.py
> > new file mode 100644
> > index 0000000000..521a4b67cd
> > --- /dev/null
> > +++ b/support/testing/tests/package/test_virglrenderer.py
> > @@ -0,0 +1,117 @@
> > +import os
> > +
> > +import infra
> > +import infra.basetest
> > +
> > +
> > +class TestVirglrendererNestedQemu(infra.basetest.BRTest):
> > +    # We reuse the same initramfs-enabled kernel for the outer target and
>
>   I think it's better to separate them, that makes it more clear what should be
> enabled in the host and what should be enabled in the guest. For the kernel it's
> OK to reuse them, but I think the rootfs's should be separated.
>
>   Therefore also I think it's better to use a cpio than an initramfs.
>

Okay, I'll update this before resubmitting for the next release.

> > +    # nested guest. This fragment adds initramfs and 9p support so the outer
> > +    # target can mount the runner's images directory and launch the same kernel
> > +    # under target-side QEMU. We also enable DRM_VGEM to create the necessary
>
>   I guess that's the only reason why one of the prebuilt kernels can't be used...
>
> > +    # DRI device node, a requirement of qemu when using virgl.
> > +    kern_frag = \
> > +        infra.filepath("tests/package/test_virglrenderer/linux-virgl.fragment")
> > +    post_build = \
> > +        infra.filepath("tests/package/test_virglrenderer/remove-qemu-blobs.sh")
> > +    rootfs_overlay = \
> > +        infra.filepath("tests/package/test_virglrenderer/rootfs-overlay")
> > +
> > +    # Softpipe has no hardware dependencies and does not pull LLVM into the
> > +    # build, unlike llvmpipe/lavapipe.
>
>   Since there are no hardware dependencies, we can actually run it on aarch64
> (or arm) instead off x86_64, no? That is preferable, because that we detect
> cross-compilation issues and everything is emulated so there's definitely no
> dependency on the build environment.
>

I see no reason this shouldn't work. I'll give it a shot.

> > +    config = \
> > +        f"""
> > +        BR2_x86_64=y
> > +        BR2_x86_nehalem=y
> > +        BR2_TOOLCHAIN_EXTERNAL=y
> > +        BR2_TOOLCHAIN_EXTERNAL_BOOTLIN_X86_64_CORE_I7_GLIBC_STABLE=y
> > +        BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y
>
>   Why do we need eudev?
>

Unfortunately, glmark2 depends on BR2_PACKAGE_HAS_UDEV, though we may
be able to replace it with a different test, or even drop this
dependency from the package.

> > +        BR2_TARGET_GENERIC_GETTY_PORT="ttyS0"
> > +        BR2_ROOTFS_OVERLAY="{rootfs_overlay}"
> > +        BR2_ROOTFS_POST_BUILD_SCRIPT="{post_build}"
> > +        BR2_LINUX_KERNEL=y
> > +        BR2_LINUX_KERNEL_CUSTOM_VERSION=y
> > +        BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.21"
> > +        BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
> > +        BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/x86_64/linux.config"
> > +        BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="{kern_frag}"
> > +        BR2_LINUX_KERNEL_NEEDS_HOST_LIBELF=y
> > +        # BR2_PACKAGE_EUDEV_ENABLE_HWDB is not set
> > +        BR2_PACKAGE_GLMARK2=y
> > +        BR2_PACKAGE_LIBGLVND=y
> > +        BR2_PACKAGE_MESA3D=y
> > +        BR2_PACKAGE_MESA3D_GALLIUM_DRIVER_SOFTPIPE=y
> > +        BR2_PACKAGE_MESA3D_GALLIUM_DRIVER_VIRGL=y
>
>   This again confuses me - why do you need both softpipe and virgl? Or is it
> just because we are reusing the same binary in the host and guest?
>

Softpipe is used in the outer guest, for software GLES rendering.
VirGL forwards GL calls from the inner guest to the outer guest. The
application (glmark2) shows the renderer as something like "VirGL(
softpipe )", indicating that VirGL is being used, and the backend is
softpipe.

> > +        BR2_PACKAGE_MESA3D_OPENGL_EGL=y
> > +        BR2_PACKAGE_MESA3D_OPENGL_ES=y
> > +        BR2_PACKAGE_QEMU=y
> > +        BR2_PACKAGE_QEMU_SYSTEM=y
> > +        # BR2_PACKAGE_QEMU_SYSTEM_KVM is not set
> > +        BR2_PACKAGE_QEMU_SYSTEM_TCG=y
> > +        BR2_PACKAGE_QEMU_CHOOSE_TARGETS=y
> > +        BR2_PACKAGE_QEMU_TARGET_X86_64=y
> > +        BR2_PACKAGE_QEMU_VIRGLRENDERER=y
> > +        BR2_TARGET_ROOTFS_INITRAMFS=y
> > +        # BR2_TARGET_ROOTFS_TAR is not set
> > +        """
> > +
> > +    def boot_outer(self):
> > +        kern = os.path.join(self.builddir, "images", "bzImage")
> > +        img_dir = os.path.join(self.builddir, "images")
> > +        virtfs_tag = "br-imgs"
> > +        virtfs_opts = [
> > +            f"local,path={img_dir}",
> > +            f"mount_tag={virtfs_tag}",
> > +            "security_model=mapped-xattr",
> > +            "readonly=on"
> > +        ]
> > +        self.emulator.boot(arch="x86_64",
> > +                           kernel=kern,
> > +                           kernel_cmdline=["console=ttyS0"],
> > +                           options=["-cpu", "Nehalem",
> > +                                    "-m", "1536M",
> > +                                    "-smp", "4",
>
>   Any reason to use more than one CPU?
>

It should work with one CPU, but enabling a few more allows for
additional throughput for the software renderer, so the test should
complete a little faster.

> > +                                    "-vga", "none",
> > +                                    "-virtfs", ",".join(virtfs_opts)])
> > +        self.emulator.login()
> > +        self.assertRunOk(f"mount -t 9p {virtfs_tag} /mnt/")
> > +
> > +    def boot_nested(self):
> > +        qemu_cmd = "env LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=softpipe"
> > +        qemu_cmd += " qemu-system-x86_64"
> > +        qemu_cmd += " -M q35,accel=tcg"
> > +        qemu_cmd += " -cpu Nehalem"
> > +        qemu_cmd += " -m 512M"
> > +        qemu_cmd += " -smp 4"
> > +        qemu_cmd += " -nodefaults"
> > +        qemu_cmd += " -display egl-headless,gl=on"
> > +        qemu_cmd += " -device virtio-gpu-gl-pci"
> > +        qemu_cmd += " -serial stdio"
> > +        qemu_cmd += " -kernel /mnt/bzImage"
> > +        qemu_cmd += " -append 'console=ttyS0 br-nested-virgl'"
> > +
> > +        # qemu runs in the foreground on the current serial console. Once it
> > +        # starts, the next login prompt belongs to the nested guest.
> > +        self.emulator.qemu.sendline(qemu_cmd)
> > +        self.emulator.login(timeout=180)
> > +
> > +    def test_run(self):
> > +        self.boot_outer()
> > +        self.assertRunOk("while [ ! -e /dev/dri/renderD128 ]; do sleep 1; done",
> > +                         timeout=30)
> > +
> > +        self.boot_nested()
> > +
> > +        out, ret = self.emulator.run("cat /proc/cmdline")
> > +        self.assertEqual(ret, 0)
> > +        self.assertIn("br-nested-virgl", out[0])
> > +
> > +        cmd = "env MESA_LOADER_DRIVER_OVERRIDE=virtio_gpu"
> > +        cmd += " glmark2-es2-drm --validate --off-screen"
> > +        cmd += " --benchmark build:use-vbo=false"
> > +        out, ret = self.emulator.run(cmd, timeout=60)
> > +        self.assertEqual(ret, 0)
>
>   So, this would fail if qemu wasn't built with virglrenderer?

I can double check, but the virtio_gpu UMD should fail to load, and
not fall back.

> > +
> > +        renderer = "\n".join([line for line in out if "GL_RENDERER:" in line])
> > +        self.assertRegex(renderer, r"(?i)virgl")
>
>   Or just this one?
>

This one will definitely fail without qemu virglrenderer support.

> > +        self.assertRegex(renderer, r"(?i)softpipe")
> > diff --git a/support/testing/tests/package/test_virglrenderer/linux-virgl.fragment b/support/testing/tests/package/test_virglrenderer/linux-virgl.fragment
> > new file mode 100644
> > index 0000000000..1023aedc6f
> > --- /dev/null
> > +++ b/support/testing/tests/package/test_virglrenderer/linux-virgl.fragment
> > @@ -0,0 +1,6 @@
> > +CONFIG_BLK_DEV_INITRD=y
> > +CONFIG_DRM_VGEM=y
> > +CONFIG_NET_9P=y
> > +CONFIG_NET_9P_VIRTIO=y
> > +CONFIG_9P_FS=y
> > +CONFIG_9P_FS_POSIX_ACL=y
> > diff --git a/support/testing/tests/package/test_virglrenderer/remove-qemu-blobs.sh b/support/testing/tests/package/test_virglrenderer/remove-qemu-blobs.sh
> > new file mode 100755
> > index 0000000000..ff040aa96b
> > --- /dev/null
> > +++ b/support/testing/tests/package/test_virglrenderer/remove-qemu-blobs.sh
> > @@ -0,0 +1,12 @@
> > +#!/bin/sh
> > +
> > +set -eu
> > +
> > +qemu_dir="${TARGET_DIR}/usr/share/qemu"
> > +
> > +find "${qemu_dir}" -type f \
> > +     ! -name 'bios-256k.bin' \
> > +     ! -name 'kvmvapic.bin' \
> > +     ! -name 'linuxboot.bin' \
> > +     ! -name 'linuxboot_dma.bin' \
> > +     -delete
>
>   Why do you need to remove those? Is it just because otherwise the cpio becomes
> too big to be used for an initramfs?
>

Dropping additional firmware just allows this test to run with < 2 GB
of memory. It works fine without this, but we must increase the guest
memory allocation.

>   Regards,
>   Arnout
>
> > diff --git a/support/testing/tests/package/test_virglrenderer/rootfs-overlay/etc/profile.d/stty-raw.sh b/support/testing/tests/package/test_virglrenderer/rootfs-overlay/etc/profile.d/stty-raw.sh
> > new file mode 100644
> > index 0000000000..bc1857a910
> > --- /dev/null
> > +++ b/support/testing/tests/package/test_virglrenderer/rootfs-overlay/etc/profile.d/stty-raw.sh
> > @@ -0,0 +1,3 @@
> > +# Avoid double-cooking the nested serial console, otherwise the test
> > +# infrastructure cannot reliably retrieve return codes.
> > +grep -Fq br-nested-virgl /proc/cmdline && stty raw
> >
>

Thanks for the review.

Best,
Joseph
diff mbox series

Patch

diff --git a/DEVELOPERS b/DEVELOPERS
index 774eb0b746..0c0779e8b3 100644
--- a/DEVELOPERS
+++ b/DEVELOPERS
@@ -1766,6 +1766,7 @@  F:	package/sentry-cli/
 F:	package/sentry-native/
 F:	package/unclutter-xfixes/
 F:	package/virglrenderer/
+F:	support/testing/tests/package/test_virglrenderer.py
 
 N:	Joshua Henderson <joshua.henderson@microchip.com>
 F:	package/qt5/qt5wayland/
diff --git a/support/testing/tests/package/test_virglrenderer.py b/support/testing/tests/package/test_virglrenderer.py
new file mode 100644
index 0000000000..521a4b67cd
--- /dev/null
+++ b/support/testing/tests/package/test_virglrenderer.py
@@ -0,0 +1,117 @@ 
+import os
+
+import infra
+import infra.basetest
+
+
+class TestVirglrendererNestedQemu(infra.basetest.BRTest):
+    # We reuse the same initramfs-enabled kernel for the outer target and
+    # nested guest. This fragment adds initramfs and 9p support so the outer
+    # target can mount the runner's images directory and launch the same kernel
+    # under target-side QEMU. We also enable DRM_VGEM to create the necessary
+    # DRI device node, a requirement of qemu when using virgl.
+    kern_frag = \
+        infra.filepath("tests/package/test_virglrenderer/linux-virgl.fragment")
+    post_build = \
+        infra.filepath("tests/package/test_virglrenderer/remove-qemu-blobs.sh")
+    rootfs_overlay = \
+        infra.filepath("tests/package/test_virglrenderer/rootfs-overlay")
+
+    # Softpipe has no hardware dependencies and does not pull LLVM into the
+    # build, unlike llvmpipe/lavapipe.
+    config = \
+        f"""
+        BR2_x86_64=y
+        BR2_x86_nehalem=y
+        BR2_TOOLCHAIN_EXTERNAL=y
+        BR2_TOOLCHAIN_EXTERNAL_BOOTLIN_X86_64_CORE_I7_GLIBC_STABLE=y
+        BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y
+        BR2_TARGET_GENERIC_GETTY_PORT="ttyS0"
+        BR2_ROOTFS_OVERLAY="{rootfs_overlay}"
+        BR2_ROOTFS_POST_BUILD_SCRIPT="{post_build}"
+        BR2_LINUX_KERNEL=y
+        BR2_LINUX_KERNEL_CUSTOM_VERSION=y
+        BR2_LINUX_KERNEL_CUSTOM_VERSION_VALUE="6.18.21"
+        BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
+        BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/qemu/x86_64/linux.config"
+        BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="{kern_frag}"
+        BR2_LINUX_KERNEL_NEEDS_HOST_LIBELF=y
+        # BR2_PACKAGE_EUDEV_ENABLE_HWDB is not set
+        BR2_PACKAGE_GLMARK2=y
+        BR2_PACKAGE_LIBGLVND=y
+        BR2_PACKAGE_MESA3D=y
+        BR2_PACKAGE_MESA3D_GALLIUM_DRIVER_SOFTPIPE=y
+        BR2_PACKAGE_MESA3D_GALLIUM_DRIVER_VIRGL=y
+        BR2_PACKAGE_MESA3D_OPENGL_EGL=y
+        BR2_PACKAGE_MESA3D_OPENGL_ES=y
+        BR2_PACKAGE_QEMU=y
+        BR2_PACKAGE_QEMU_SYSTEM=y
+        # BR2_PACKAGE_QEMU_SYSTEM_KVM is not set
+        BR2_PACKAGE_QEMU_SYSTEM_TCG=y
+        BR2_PACKAGE_QEMU_CHOOSE_TARGETS=y
+        BR2_PACKAGE_QEMU_TARGET_X86_64=y
+        BR2_PACKAGE_QEMU_VIRGLRENDERER=y
+        BR2_TARGET_ROOTFS_INITRAMFS=y
+        # BR2_TARGET_ROOTFS_TAR is not set
+        """
+
+    def boot_outer(self):
+        kern = os.path.join(self.builddir, "images", "bzImage")
+        img_dir = os.path.join(self.builddir, "images")
+        virtfs_tag = "br-imgs"
+        virtfs_opts = [
+            f"local,path={img_dir}",
+            f"mount_tag={virtfs_tag}",
+            "security_model=mapped-xattr",
+            "readonly=on"
+        ]
+        self.emulator.boot(arch="x86_64",
+                           kernel=kern,
+                           kernel_cmdline=["console=ttyS0"],
+                           options=["-cpu", "Nehalem",
+                                    "-m", "1536M",
+                                    "-smp", "4",
+                                    "-vga", "none",
+                                    "-virtfs", ",".join(virtfs_opts)])
+        self.emulator.login()
+        self.assertRunOk(f"mount -t 9p {virtfs_tag} /mnt/")
+
+    def boot_nested(self):
+        qemu_cmd = "env LIBGL_ALWAYS_SOFTWARE=1 GALLIUM_DRIVER=softpipe"
+        qemu_cmd += " qemu-system-x86_64"
+        qemu_cmd += " -M q35,accel=tcg"
+        qemu_cmd += " -cpu Nehalem"
+        qemu_cmd += " -m 512M"
+        qemu_cmd += " -smp 4"
+        qemu_cmd += " -nodefaults"
+        qemu_cmd += " -display egl-headless,gl=on"
+        qemu_cmd += " -device virtio-gpu-gl-pci"
+        qemu_cmd += " -serial stdio"
+        qemu_cmd += " -kernel /mnt/bzImage"
+        qemu_cmd += " -append 'console=ttyS0 br-nested-virgl'"
+
+        # qemu runs in the foreground on the current serial console. Once it
+        # starts, the next login prompt belongs to the nested guest.
+        self.emulator.qemu.sendline(qemu_cmd)
+        self.emulator.login(timeout=180)
+
+    def test_run(self):
+        self.boot_outer()
+        self.assertRunOk("while [ ! -e /dev/dri/renderD128 ]; do sleep 1; done",
+                         timeout=30)
+
+        self.boot_nested()
+
+        out, ret = self.emulator.run("cat /proc/cmdline")
+        self.assertEqual(ret, 0)
+        self.assertIn("br-nested-virgl", out[0])
+
+        cmd = "env MESA_LOADER_DRIVER_OVERRIDE=virtio_gpu"
+        cmd += " glmark2-es2-drm --validate --off-screen"
+        cmd += " --benchmark build:use-vbo=false"
+        out, ret = self.emulator.run(cmd, timeout=60)
+        self.assertEqual(ret, 0)
+
+        renderer = "\n".join([line for line in out if "GL_RENDERER:" in line])
+        self.assertRegex(renderer, r"(?i)virgl")
+        self.assertRegex(renderer, r"(?i)softpipe")
diff --git a/support/testing/tests/package/test_virglrenderer/linux-virgl.fragment b/support/testing/tests/package/test_virglrenderer/linux-virgl.fragment
new file mode 100644
index 0000000000..1023aedc6f
--- /dev/null
+++ b/support/testing/tests/package/test_virglrenderer/linux-virgl.fragment
@@ -0,0 +1,6 @@ 
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_DRM_VGEM=y
+CONFIG_NET_9P=y
+CONFIG_NET_9P_VIRTIO=y
+CONFIG_9P_FS=y
+CONFIG_9P_FS_POSIX_ACL=y
diff --git a/support/testing/tests/package/test_virglrenderer/remove-qemu-blobs.sh b/support/testing/tests/package/test_virglrenderer/remove-qemu-blobs.sh
new file mode 100755
index 0000000000..ff040aa96b
--- /dev/null
+++ b/support/testing/tests/package/test_virglrenderer/remove-qemu-blobs.sh
@@ -0,0 +1,12 @@ 
+#!/bin/sh
+
+set -eu
+
+qemu_dir="${TARGET_DIR}/usr/share/qemu"
+
+find "${qemu_dir}" -type f \
+	! -name 'bios-256k.bin' \
+	! -name 'kvmvapic.bin' \
+	! -name 'linuxboot.bin' \
+	! -name 'linuxboot_dma.bin' \
+	-delete
diff --git a/support/testing/tests/package/test_virglrenderer/rootfs-overlay/etc/profile.d/stty-raw.sh b/support/testing/tests/package/test_virglrenderer/rootfs-overlay/etc/profile.d/stty-raw.sh
new file mode 100644
index 0000000000..bc1857a910
--- /dev/null
+++ b/support/testing/tests/package/test_virglrenderer/rootfs-overlay/etc/profile.d/stty-raw.sh
@@ -0,0 +1,3 @@ 
+# Avoid double-cooking the nested serial console, otherwise the test
+# infrastructure cannot reliably retrieve return codes.
+grep -Fq br-nested-virgl /proc/cmdline && stty raw