aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS6
-rw-r--r--docs/devel/reset.rst4
-rw-r--r--docs/system/arm/aspeed.rst52
-rw-r--r--docs/system/arm/exynos.rst9
-rw-r--r--docs/system/arm/fby35.rst47
-rw-r--r--docs/system/arm/mcimx6ul-evk.rst5
-rw-r--r--docs/system/arm/mcimx7d-sabre.rst5
-rw-r--r--docs/system/arm/nuvoton.rst4
-rw-r--r--docs/system/arm/stm32.rst4
-rw-r--r--docs/system/arm/xlnx-zcu102.rst19
-rw-r--r--docs/system/cpu-hotplug.rst54
-rw-r--r--docs/system/devices/virtio-gpu.rst11
-rw-r--r--docs/system/target-arm.rst9
-rw-r--r--hw/arm/virt.c72
-rw-r--r--hw/display/trace-events3
-rw-r--r--hw/display/virtio-gpu-gl.c62
-rw-r--r--hw/display/virtio-gpu-virgl.c585
-rw-r--r--hw/display/virtio-gpu.c44
-rw-r--r--hw/intc/arm_gic_kvm.c9
-rw-r--r--hw/sd/omap_mmc.c22
-rw-r--r--hw/sd/sd.c8
-rw-r--r--include/hw/sd/sd.h8
-rw-r--r--include/hw/virtio/virtio-gpu.h32
-rw-r--r--meson.build5
-rw-r--r--scripts/symlink-install-tree.py3
-rw-r--r--target/arm/cpu.c14
-rw-r--r--target/arm/cpu.h2
-rw-r--r--target/arm/internals.h5
-rw-r--r--target/arm/kvm.c75
-rw-r--r--target/arm/kvm_arm.h35
-rw-r--r--target/arm/tcg/helper-a64.c2
-rw-r--r--target/arm/vfp_helper.c56
-rw-r--r--tests/functional/meson.build3
-rwxr-xr-xtests/functional/test_arm_collie.py31
-rwxr-xr-xtests/functional/test_arm_sx1.py72
35 files changed, 1110 insertions, 267 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index f48d9142b8..1e88b5738c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -716,6 +716,7 @@ L: qemu-arm@nongnu.org
S: Odd Fixes
F: hw/*/exynos*
F: include/hw/*/exynos*
+F: docs/system/arm/exynos.rst
Calxeda Highbank
M: Rob Herring <robh@kernel.org>
@@ -790,6 +791,7 @@ F: hw/arm/fsl-imx6ul.c
F: hw/misc/imx6ul_ccm.c
F: include/hw/arm/fsl-imx6ul.h
F: include/hw/misc/imx6ul_ccm.h
+F: docs/system/arm/mcimx6ul-evk.rst
MCIMX7D SABRE / i.MX7
M: Peter Maydell <peter.maydell@linaro.org>
@@ -803,6 +805,7 @@ F: include/hw/arm/fsl-imx7.h
F: include/hw/misc/imx7_*.h
F: hw/pci-host/designware.c
F: include/hw/pci-host/designware.h
+F: docs/system/arm/mcimx7d-sabre.rst
MPS2 / MPS3
M: Peter Maydell <peter.maydell@linaro.org>
@@ -929,6 +932,7 @@ F: hw/arm/strongarm*
F: hw/gpio/zaurus.c
F: include/hw/arm/sharpsl.h
F: docs/system/arm/collie.rst
+F: tests/functional/test_arm_collie.py
Stellaris
M: Peter Maydell <peter.maydell@linaro.org>
@@ -1016,6 +1020,7 @@ F: include/hw/ssi/xilinx_spips.h
F: hw/display/dpcd.c
F: include/hw/display/dpcd.h
F: docs/system/arm/xlnx-versal-virt.rst
+F: docs/system/arm/xlnx-zcu102.rst
Xilinx Versal OSPI
M: Francisco Iglesias <francisco.iglesias@amd.com>
@@ -1122,6 +1127,7 @@ F: include/hw/*/*aspeed*
F: hw/net/ftgmac100.c
F: include/hw/net/ftgmac100.h
F: docs/system/arm/aspeed.rst
+F: docs/system/arm/fby35.rst
F: tests/*/*aspeed*
F: tests/*/*ast2700*
F: hw/arm/fby35.c
diff --git a/docs/devel/reset.rst b/docs/devel/reset.rst
index 74c7c0171a..adefd59ef9 100644
--- a/docs/devel/reset.rst
+++ b/docs/devel/reset.rst
@@ -286,8 +286,8 @@ every reset child of the given resettable object. All children must be
resettable too. Additional parameters (a reset type and an opaque pointer) must
be passed to the callback too.
-In ``DeviceClass`` and ``BusClass`` the ``ResettableState`` is located
-``DeviceState`` and ``BusState`` structure. ``child_foreach()`` is implemented
+In ``DeviceClass`` and ``BusClass`` the ``ResettableState`` is located in the
+``DeviceState`` and ``BusState`` structures. ``child_foreach()`` is implemented
to follow the bus hierarchy; for a bus, it calls the function on every child
device; for a device, it calls the function on every bus child. When we reset
the main system bus, we reset the whole machine bus tree.
diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst
index 6733ffd2b9..63910d382f 100644
--- a/docs/system/arm/aspeed.rst
+++ b/docs/system/arm/aspeed.rst
@@ -1,5 +1,5 @@
-Aspeed family boards (``*-bmc``, ``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``)
-===================================================================================
+Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``, ``bletchley-bmc``, ``fuji-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``tiogapass-bmc``, ``tacoma-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``)
+========================================================================================================================================================================================================================================================================================================================================================================================================
The QEMU Aspeed machines model BMCs of various OpenPOWER systems and
Aspeed evaluation boards. They are based on different releases of the
@@ -257,51 +257,3 @@ To boot a kernel directly from a Zephyr build tree:
$ qemu-system-arm -M ast1030-evb -nographic \
-kernel zephyr.elf
-
-Facebook Yosemite v3.5 Platform and CraterLake Server (``fby35``)
-==================================================================
-
-Facebook has a series of multi-node compute server designs named
-Yosemite. The most recent version released was
-`Yosemite v3 <https://www.opencompute.org/documents/ocp-yosemite-v3-platform-design-specification-1v16-pdf>`__.
-
-Yosemite v3.5 is an iteration on this design, and is very similar: there's a
-baseboard with a BMC, and 4 server slots. The new server board design termed
-"CraterLake" includes a Bridge IC (BIC), with room for expansion boards to
-include various compute accelerators (video, inferencing, etc). At the moment,
-only the first server slot's BIC is included.
-
-Yosemite v3.5 is itself a sled which fits into a 40U chassis, and 3 sleds
-can be fit into a chassis. See `here <https://www.opencompute.org/products/423/wiwynn-yosemite-v3-server>`__
-for an example.
-
-In this generation, the BMC is an AST2600 and each BIC is an AST1030. The BMC
-runs `OpenBMC <https://github.com/facebook/openbmc>`__, and the BIC runs
-`OpenBIC <https://github.com/facebook/openbic>`__.
-
-Firmware images can be retrieved from the Github releases or built from the
-source code, see the README's for instructions on that. This image uses the
-"fby35" machine recipe from OpenBMC, and the "yv35-cl" target from OpenBIC.
-Some reference images can also be found here:
-
-.. code-block:: bash
-
- $ wget https://github.com/facebook/openbmc/releases/download/openbmc-e2294ff5d31d/fby35.mtd
- $ wget https://github.com/peterdelevoryas/OpenBIC/releases/download/oby35-cl-2022.13.01/Y35BCL.elf
-
-Since this machine has multiple SoC's, each with their own serial console, the
-recommended way to run it is to allocate a pseudoterminal for each serial
-console and let the monitor use stdio. Also, starting in a paused state is
-useful because it allows you to attach to the pseudoterminals before the boot
-process starts.
-
-.. code-block:: bash
-
- $ qemu-system-arm -machine fby35 \
- -drive file=fby35.mtd,format=raw,if=mtd \
- -device loader,file=Y35BCL.elf,addr=0,cpu-num=2 \
- -serial pty -serial pty -serial mon:stdio \
- -display none -S
- $ screen /dev/tty0 # In a separate TMUX pane, terminal window, etc.
- $ screen /dev/tty1
- $ (qemu) c # Start the boot process once screen is setup.
diff --git a/docs/system/arm/exynos.rst b/docs/system/arm/exynos.rst
new file mode 100644
index 0000000000..86894bc02b
--- /dev/null
+++ b/docs/system/arm/exynos.rst
@@ -0,0 +1,9 @@
+Exynos4 boards (``nuri``, ``smdkc210``)
+=======================================
+
+These are machines which use the Samsung Exynos4210 SoC, which has Cortex-A9 CPUs.
+
+``nuri`` models the Samsung NURI board.
+
+``smdkc210`` models the Samsung SMDKC210 board.
+
diff --git a/docs/system/arm/fby35.rst b/docs/system/arm/fby35.rst
new file mode 100644
index 0000000000..742b887d44
--- /dev/null
+++ b/docs/system/arm/fby35.rst
@@ -0,0 +1,47 @@
+Facebook Yosemite v3.5 Platform and CraterLake Server (``fby35``)
+==================================================================
+
+Facebook has a series of multi-node compute server designs named
+Yosemite. The most recent version released was
+`Yosemite v3 <https://www.opencompute.org/documents/ocp-yosemite-v3-platform-design-specification-1v16-pdf>`__.
+
+Yosemite v3.5 is an iteration on this design, and is very similar: there's a
+baseboard with a BMC, and 4 server slots. The new server board design termed
+"CraterLake" includes a Bridge IC (BIC), with room for expansion boards to
+include various compute accelerators (video, inferencing, etc). At the moment,
+only the first server slot's BIC is included.
+
+Yosemite v3.5 is itself a sled which fits into a 40U chassis, and 3 sleds
+can be fit into a chassis. See `here <https://www.opencompute.org/products/423/wiwynn-yosemite-v3-server>`__
+for an example.
+
+In this generation, the BMC is an AST2600 and each BIC is an AST1030. The BMC
+runs `OpenBMC <https://github.com/facebook/openbmc>`__, and the BIC runs
+`OpenBIC <https://github.com/facebook/openbic>`__.
+
+Firmware images can be retrieved from the Github releases or built from the
+source code, see the README's for instructions on that. This image uses the
+"fby35" machine recipe from OpenBMC, and the "yv35-cl" target from OpenBIC.
+Some reference images can also be found here:
+
+.. code-block:: bash
+
+ $ wget https://github.com/facebook/openbmc/releases/download/openbmc-e2294ff5d31d/fby35.mtd
+ $ wget https://github.com/peterdelevoryas/OpenBIC/releases/download/oby35-cl-2022.13.01/Y35BCL.elf
+
+Since this machine has multiple SoC's, each with their own serial console, the
+recommended way to run it is to allocate a pseudoterminal for each serial
+console and let the monitor use stdio. Also, starting in a paused state is
+useful because it allows you to attach to the pseudoterminals before the boot
+process starts.
+
+.. code-block:: bash
+
+ $ qemu-system-arm -machine fby35 \
+ -drive file=fby35.mtd,format=raw,if=mtd \
+ -device loader,file=Y35BCL.elf,addr=0,cpu-num=2 \
+ -serial pty -serial pty -serial mon:stdio \
+ -display none -S
+ $ screen /dev/tty0 # In a separate TMUX pane, terminal window, etc.
+ $ screen /dev/tty1
+ $ (qemu) c # Start the boot process once screen is setup.
diff --git a/docs/system/arm/mcimx6ul-evk.rst b/docs/system/arm/mcimx6ul-evk.rst
new file mode 100644
index 0000000000..8871138ab3
--- /dev/null
+++ b/docs/system/arm/mcimx6ul-evk.rst
@@ -0,0 +1,5 @@
+NXP MCIMX6UL-EVK (``mcimx6ul-evk``)
+===================================
+
+The ``mcimx6ul-evk`` machine models the NXP i.MX6UltraLite Evaluation Kit
+MCIMX6UL-EVK development board. It has a single Cortex-A7 CPU.
diff --git a/docs/system/arm/mcimx7d-sabre.rst b/docs/system/arm/mcimx7d-sabre.rst
new file mode 100644
index 0000000000..c5d35af1d4
--- /dev/null
+++ b/docs/system/arm/mcimx7d-sabre.rst
@@ -0,0 +1,5 @@
+NXP MCIMX7D Sabre (``mcimx7d-sabre``)
+=====================================
+
+The ``mcimx7d-sabre`` machine models the NXP SABRE Board MCIMX7SABRE,
+based an an i.MX7Dual SoC.
diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index 0424cae4b0..05059378e5 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -1,5 +1,5 @@
-Nuvoton iBMC boards (``*-bmc``, ``npcm750-evb``, ``quanta-gsj``)
-================================================================
+Nuvoton iBMC boards (``kudo-bmc``, ``mori-bmc``, ``npcm750-evb``, ``quanta-gbs-bmc``, ``quanta-gsj``)
+=====================================================================================================
The `Nuvoton iBMC`_ chips (NPCM7xx) are a family of ARM-based SoCs that are
designed to be used as Baseboard Management Controllers (BMCs) in various
diff --git a/docs/system/arm/stm32.rst b/docs/system/arm/stm32.rst
index ca7a55841b..511e3eb9ac 100644
--- a/docs/system/arm/stm32.rst
+++ b/docs/system/arm/stm32.rst
@@ -1,5 +1,5 @@
-STMicroelectronics STM32 boards (``netduino2``, ``netduinoplus2``, ``stm32vldiscovery``)
-========================================================================================
+STMicroelectronics STM32 boards (``netduino2``, ``netduinoplus2``, ``olimex-stm32-h405``, ``stm32vldiscovery``)
+===============================================================================================================
The `STM32`_ chips are a family of 32-bit ARM-based microcontroller by
STMicroelectronics.
diff --git a/docs/system/arm/xlnx-zcu102.rst b/docs/system/arm/xlnx-zcu102.rst
new file mode 100644
index 0000000000..534cd1dc88
--- /dev/null
+++ b/docs/system/arm/xlnx-zcu102.rst
@@ -0,0 +1,19 @@
+Xilinx ZynqMP ZCU102 (``xlnx-zcu102``)
+======================================
+
+The ``xlnx-zcu102`` board models the Xilinx ZynqMP ZCU102 board.
+This board has 4 Cortex-A53 CPUs and 2 Cortex-R5F CPUs.
+
+Machine-specific options
+""""""""""""""""""""""""
+
+The following machine-specific options are supported:
+
+secure
+ Set ``on``/``off`` to enable/disable emulating a guest CPU which implements the
+ Arm Security Extensions (TrustZone). The default is ``off``.
+
+virtualization
+ Set ``on``/``off`` to enable/disable emulating a guest CPU which implements the
+ Arm Virtualization Extensions. The default is ``off``.
+
diff --git a/docs/system/cpu-hotplug.rst b/docs/system/cpu-hotplug.rst
index 015ce2b6ec..cc50937c36 100644
--- a/docs/system/cpu-hotplug.rst
+++ b/docs/system/cpu-hotplug.rst
@@ -33,23 +33,23 @@ vCPU hotplug
{
"return": [
{
- "type": "IvyBridge-IBRS-x86_64-cpu",
- "vcpus-count": 1,
"props": {
- "socket-id": 1,
- "core-id": 0,
+ "core-id": 1,
+ "socket-id": 0,
"thread-id": 0
- }
+ },
+ "type": "IvyBridge-IBRS-x86_64-cpu",
+ "vcpus-count": 1
},
{
- "qom-path": "/machine/unattached/device[0]",
- "type": "IvyBridge-IBRS-x86_64-cpu",
- "vcpus-count": 1,
"props": {
- "socket-id": 0,
"core-id": 0,
+ "socket-id": 0,
"thread-id": 0
- }
+ },
+ "qom-path": "/machine/unattached/device[0]",
+ "type": "IvyBridge-IBRS-x86_64-cpu",
+ "vcpus-count": 1
}
]
}
@@ -58,18 +58,18 @@ vCPU hotplug
(4) The ``query-hotpluggable-cpus`` command returns an object for CPUs
that are present (containing a "qom-path" member) or which may be
hot-plugged (no "qom-path" member). From its output in step (3), we
- can see that ``IvyBridge-IBRS-x86_64-cpu`` is present in socket 0,
- while hot-plugging a CPU into socket 1 requires passing the listed
+ can see that ``IvyBridge-IBRS-x86_64-cpu`` is present in socket 0 core 0,
+ while hot-plugging a CPU into socket 0 core 1 requires passing the listed
properties to QMP ``device_add``::
- (QEMU) device_add id=cpu-2 driver=IvyBridge-IBRS-x86_64-cpu socket-id=1 core-id=0 thread-id=0
+ (QEMU) device_add id=cpu-2 driver=IvyBridge-IBRS-x86_64-cpu socket-id=0 core-id=1 thread-id=0
{
"execute": "device_add",
"arguments": {
- "socket-id": 1,
+ "core-id": 1,
"driver": "IvyBridge-IBRS-x86_64-cpu",
"id": "cpu-2",
- "core-id": 0,
+ "socket-id": 0,
"thread-id": 0
}
}
@@ -83,34 +83,32 @@ vCPU hotplug
(QEMU) query-cpus-fast
{
- "execute": "query-cpus-fast",
"arguments": {}
+ "execute": "query-cpus-fast",
}
{
"return": [
{
- "qom-path": "/machine/unattached/device[0]",
- "target": "x86_64",
- "thread-id": 11534,
"cpu-index": 0,
"props": {
- "socket-id": 0,
"core-id": 0,
+ "socket-id": 0,
"thread-id": 0
},
- "arch": "x86"
+ "qom-path": "/machine/unattached/device[0]",
+ "target": "x86_64",
+ "thread-id": 28957
},
{
- "qom-path": "/machine/peripheral/cpu-2",
- "target": "x86_64",
- "thread-id": 12106,
"cpu-index": 1,
"props": {
- "socket-id": 1,
- "core-id": 0,
+ "core-id": 1,
+ "socket-id": 0,
"thread-id": 0
},
- "arch": "x86"
+ "qom-path": "/machine/peripheral/cpu-2",
+ "target": "x86_64",
+ "thread-id": 29095
}
]
}
@@ -123,10 +121,10 @@ From the 'qmp-shell', invoke the QMP ``device_del`` command::
(QEMU) device_del id=cpu-2
{
- "execute": "device_del",
"arguments": {
"id": "cpu-2"
}
+ "execute": "device_del",
}
{
"return": {}
diff --git a/docs/system/devices/virtio-gpu.rst b/docs/system/devices/virtio-gpu.rst
index cb73dd7998..b7eb0fc0e7 100644
--- a/docs/system/devices/virtio-gpu.rst
+++ b/docs/system/devices/virtio-gpu.rst
@@ -71,6 +71,17 @@ representation back to OpenGL API calls.
.. _Gallium3D: https://www.freedesktop.org/wiki/Software/gallium/
.. _virglrenderer: https://gitlab.freedesktop.org/virgl/virglrenderer/
+Translation of Vulkan API calls is supported since release of `virglrenderer`_
+v1.0.0 using `venus`_ protocol. ``Venus`` virtio-gpu capability set ("capset")
+requires host blob support (``hostmem`` and ``blob`` fields) and should
+be enabled using ``venus`` field. The ``hostmem`` field specifies the size
+of virtio-gpu host memory window. This is typically between 256M and 8G.
+
+.. parsed-literal::
+ -device virtio-gpu-gl,hostmem=8G,blob=true,venus=true
+
+.. _venus: https://gitlab.freedesktop.org/virgl/venus-protocol/
+
virtio-gpu rutabaga
-------------------
diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst
index 3c0a584845..9aaa9c414c 100644
--- a/docs/system/target-arm.rst
+++ b/docs/system/target-arm.rst
@@ -63,10 +63,6 @@ large amounts of RAM. It also supports 64-bit CPUs.
Board-specific documentation
============================
-Unfortunately many of the Arm boards QEMU supports are currently
-undocumented; you can get a complete list by running
-``qemu-system-aarch64 --machine help``.
-
..
This table of contents should be kept sorted alphabetically
by the title text of each file, which isn't the same ordering
@@ -90,11 +86,15 @@ undocumented; you can get a complete list by running
arm/digic
arm/cubieboard
arm/emcraft-sf2
+ arm/exynos
+ arm/fby35
arm/musicpal
arm/kzm
arm/nrf
arm/nuvoton
arm/imx25-pdk
+ arm/mcimx6ul-evk
+ arm/mcimx7d-sabre
arm/orangepi
arm/raspi
arm/collie
@@ -105,6 +105,7 @@ undocumented; you can get a complete list by running
arm/xenpvh
arm/xlnx-versal-virt
arm/xlnx-zynq
+ arm/xlnx-zcu102
Emulated CPU architecture support
=================================
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 8b2b991d97..1a381e9a2b 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2213,7 +2213,7 @@ static void machvirt_init(MachineState *machine)
exit(1);
}
- if (vms->mte && (kvm_enabled() || hvf_enabled())) {
+ if (vms->mte && hvf_enabled()) {
error_report("mach-virt: %s does not support providing "
"MTE to the guest CPU",
current_accel_name());
@@ -2283,39 +2283,51 @@ static void machvirt_init(MachineState *machine)
}
if (vms->mte) {
- /* Create the memory region only once, but link to all cpus. */
- if (!tag_sysmem) {
- /*
- * The property exists only if MemTag is supported.
- * If it is, we must allocate the ram to back that up.
- */
- if (!object_property_find(cpuobj, "tag-memory")) {
- error_report("MTE requested, but not supported "
- "by the guest CPU");
- exit(1);
+ if (tcg_enabled()) {
+ /* Create the memory region only once, but link to all cpus. */
+ if (!tag_sysmem) {
+ /*
+ * The property exists only if MemTag is supported.
+ * If it is, we must allocate the ram to back that up.
+ */
+ if (!object_property_find(cpuobj, "tag-memory")) {
+ error_report("MTE requested, but not supported "
+ "by the guest CPU");
+ exit(1);
+ }
+
+ tag_sysmem = g_new(MemoryRegion, 1);
+ memory_region_init(tag_sysmem, OBJECT(machine),
+ "tag-memory", UINT64_MAX / 32);
+
+ if (vms->secure) {
+ secure_tag_sysmem = g_new(MemoryRegion, 1);
+ memory_region_init(secure_tag_sysmem, OBJECT(machine),
+ "secure-tag-memory",
+ UINT64_MAX / 32);
+
+ /* As with ram, secure-tag takes precedence over tag. */
+ memory_region_add_subregion_overlap(secure_tag_sysmem,
+ 0, tag_sysmem, -1);
+ }
}
- tag_sysmem = g_new(MemoryRegion, 1);
- memory_region_init(tag_sysmem, OBJECT(machine),
- "tag-memory", UINT64_MAX / 32);
-
+ object_property_set_link(cpuobj, "tag-memory",
+ OBJECT(tag_sysmem), &error_abort);
if (vms->secure) {
- secure_tag_sysmem = g_new(MemoryRegion, 1);
- memory_region_init(secure_tag_sysmem, OBJECT(machine),
- "secure-tag-memory", UINT64_MAX / 32);
-
- /* As with ram, secure-tag takes precedence over tag. */
- memory_region_add_subregion_overlap(secure_tag_sysmem, 0,
- tag_sysmem, -1);
+ object_property_set_link(cpuobj, "secure-tag-memory",
+ OBJECT(secure_tag_sysmem),
+ &error_abort);
}
- }
-
- object_property_set_link(cpuobj, "tag-memory", OBJECT(tag_sysmem),
- &error_abort);
- if (vms->secure) {
- object_property_set_link(cpuobj, "secure-tag-memory",
- OBJECT(secure_tag_sysmem),
- &error_abort);
+ } else if (kvm_enabled()) {
+ if (!kvm_arm_mte_supported()) {
+ error_report("MTE requested, but not supported by KVM");
+ exit(1);
+ }
+ kvm_arm_enable_mte(cpuobj, &error_abort);
+ } else {
+ error_report("MTE requested, but not supported ");
+ exit(1);
}
}
diff --git a/hw/display/trace-events b/hw/display/trace-events
index 781f8a3320..d26d663f96 100644
--- a/hw/display/trace-events
+++ b/hw/display/trace-events
@@ -53,6 +53,9 @@ virtio_gpu_cmd_ctx_submit(uint32_t ctx, uint32_t size) "ctx 0x%x, size %d"
virtio_gpu_update_cursor(uint32_t scanout, uint32_t x, uint32_t y, const char *type, uint32_t res) "scanout %d, x %d, y %d, %s, res 0x%x"
virtio_gpu_fence_ctrl(uint64_t fence, uint32_t type) "fence 0x%" PRIx64 ", type 0x%x"
virtio_gpu_fence_resp(uint64_t fence) "fence 0x%" PRIx64
+virtio_gpu_inc_inflight_fences(uint32_t inflight) "in-flight+ %u"
+virtio_gpu_dec_inflight_fences(uint32_t inflight) "in-flight- %u"
+virtio_gpu_cmd_suspended(uint32_t cmd) "cmd 0x%x"
# qxl.c
disable qxl_io_write_vga(int qid, const char *mode, uint32_t addr, uint32_t val) "%d %s addr=%u val=%u"
diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c
index 29d20b0132..7c0e448b46 100644
--- a/hw/display/virtio-gpu-gl.c
+++ b/hw/display/virtio-gpu-gl.c
@@ -29,9 +29,14 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
struct virtio_gpu_scanout *s,
uint32_t resource_id)
{
+ VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
uint32_t width, height;
uint32_t pixels, *data;
+ if (gl->renderer_state != RS_INITED) {
+ return;
+ }
+
data = virgl_renderer_get_cursor_data(resource_id, &width, &height);
if (!data) {
return;
@@ -65,13 +70,22 @@ static void virtio_gpu_gl_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
return;
}
- if (!gl->renderer_inited) {
- virtio_gpu_virgl_init(g);
- gl->renderer_inited = true;
- }
- if (gl->renderer_reset) {
- gl->renderer_reset = false;
+ switch (gl->renderer_state) {
+ case RS_RESET:
virtio_gpu_virgl_reset(g);
+ /* fallthrough */
+ case RS_START:
+ if (virtio_gpu_virgl_init(g)) {
+ gl->renderer_state = RS_INIT_FAILED;
+ return;
+ }
+
+ gl->renderer_state = RS_INITED;
+ break;
+ case RS_INIT_FAILED:
+ return;
+ case RS_INITED:
+ break;
}
cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command));
@@ -98,9 +112,9 @@ static void virtio_gpu_gl_reset(VirtIODevice *vdev)
* GL functions must be called with the associated GL context in main
* thread, and when the renderer is unblocked.
*/
- if (gl->renderer_inited && !gl->renderer_reset) {
+ if (gl->renderer_state == RS_INITED) {
virtio_gpu_virgl_reset_scanout(g);
- gl->renderer_reset = true;
+ gl->renderer_state = RS_RESET;
}
}
@@ -130,8 +144,12 @@ static void virtio_gpu_gl_device_realize(DeviceState *qdev, Error **errp)
}
g->parent_obj.conf.flags |= (1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED);
- VIRTIO_GPU_BASE(g)->virtio_config.num_capsets =
- virtio_gpu_virgl_get_num_capsets(g);
+ g->capset_ids = virtio_gpu_virgl_get_capsets(g);
+ VIRTIO_GPU_BASE(g)->virtio_config.num_capsets = g->capset_ids->len;
+
+#if VIRGL_VERSION_MAJOR >= 1
+ g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_CONTEXT_INIT_ENABLED;
+#endif
virtio_gpu_device_realize(qdev, errp);
}
@@ -139,9 +157,32 @@ static void virtio_gpu_gl_device_realize(DeviceState *qdev, Error **errp)
static Property virtio_gpu_gl_properties[] = {
DEFINE_PROP_BIT("stats", VirtIOGPU, parent_obj.conf.flags,
VIRTIO_GPU_FLAG_STATS_ENABLED, false),
+ DEFINE_PROP_BIT("venus", VirtIOGPU, parent_obj.conf.flags,
+ VIRTIO_GPU_FLAG_VENUS_ENABLED, false),
DEFINE_PROP_END_OF_LIST(),
};
+static void virtio_gpu_gl_device_unrealize(DeviceState *qdev)
+{
+ VirtIOGPU *g = VIRTIO_GPU(qdev);
+ VirtIOGPUGL *gl = VIRTIO_GPU_GL(qdev);
+
+ if (gl->renderer_state >= RS_INITED) {
+#if VIRGL_VERSION_MAJOR >= 1
+ qemu_bh_delete(gl->cmdq_resume_bh);
+#endif
+ if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
+ timer_free(gl->print_stats);
+ }
+ timer_free(gl->fence_poll);
+ virgl_renderer_cleanup(NULL);
+ }
+
+ gl->renderer_state = RS_START;
+
+ g_array_unref(g->capset_ids);
+}
+
static void virtio_gpu_gl_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -155,6 +196,7 @@ static void virtio_gpu_gl_class_init(ObjectClass *klass, void *data)
vgc->update_cursor_data = virtio_gpu_gl_update_cursor_data;
vdc->realize = virtio_gpu_gl_device_realize;
+ vdc->unrealize = virtio_gpu_gl_device_unrealize;
vdc->reset = virtio_gpu_gl_reset;
device_class_set_props(dc, virtio_gpu_gl_properties);
}
diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c
index 9f34d0e661..eedae7357f 100644
--- a/hw/display/virtio-gpu-virgl.c
+++ b/hw/display/virtio-gpu-virgl.c
@@ -17,11 +17,31 @@
#include "trace.h"
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-gpu.h"
+#include "hw/virtio/virtio-gpu-bswap.h"
+#include "hw/virtio/virtio-gpu-pixman.h"
#include "ui/egl-helpers.h"
#include <virglrenderer.h>
+struct virtio_gpu_virgl_resource {
+ struct virtio_gpu_simple_resource base;
+ MemoryRegion *mr;
+};
+
+static struct virtio_gpu_virgl_resource *
+virtio_gpu_virgl_find_resource(VirtIOGPU *g, uint32_t resource_id)
+{
+ struct virtio_gpu_simple_resource *res;
+
+ res = virtio_gpu_find_resource(g, resource_id);
+ if (!res) {
+ return NULL;
+ }
+
+ return container_of(res, struct virtio_gpu_virgl_resource, base);
+}
+
#if VIRGL_RENDERER_CALLBACKS_VERSION >= 4
static void *
virgl_get_egl_display(G_GNUC_UNUSED void *cookie)
@@ -30,16 +50,179 @@ virgl_get_egl_display(G_GNUC_UNUSED void *cookie)
}
#endif
+#if VIRGL_VERSION_MAJOR >= 1
+struct virtio_gpu_virgl_hostmem_region {
+ MemoryRegion mr;
+ struct VirtIOGPU *g;
+ bool finish_unmapping;
+};
+
+static struct virtio_gpu_virgl_hostmem_region *
+to_hostmem_region(MemoryRegion *mr)
+{
+ return container_of(mr, struct virtio_gpu_virgl_hostmem_region, mr);
+}
+
+static void virtio_gpu_virgl_resume_cmdq_bh(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+
+ virtio_gpu_process_cmdq(g);
+}
+
+static void virtio_gpu_virgl_hostmem_region_free(void *obj)
+{
+ MemoryRegion *mr = MEMORY_REGION(obj);
+ struct virtio_gpu_virgl_hostmem_region *vmr;
+ VirtIOGPUBase *b;
+ VirtIOGPUGL *gl;
+
+ vmr = to_hostmem_region(mr);
+ vmr->finish_unmapping = true;
+
+ b = VIRTIO_GPU_BASE(vmr->g);
+ b->renderer_blocked--;
+
+ /*
+ * memory_region_unref() is executed from RCU thread context, while
+ * virglrenderer works only on the main-loop thread that's holding GL
+ * context.
+ */
+ gl = VIRTIO_GPU_GL(vmr->g);
+ qemu_bh_schedule(gl->cmdq_resume_bh);
+}
+
+static int
+virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g,
+ struct virtio_gpu_virgl_resource *res,
+ uint64_t offset)
+{
+ struct virtio_gpu_virgl_hostmem_region *vmr;
+ VirtIOGPUBase *b = VIRTIO_GPU_BASE(g);
+ MemoryRegion *mr;
+ uint64_t size;
+ void *data;
+ int ret;
+
+ if (!virtio_gpu_hostmem_enabled(b->conf)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: hostmem disabled\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ ret = virgl_renderer_resource_map(res->base.resource_id, &data, &size);
+ if (ret) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map virgl resource: %s\n",
+ __func__, strerror(-ret));
+ return ret;
+ }
+
+ vmr = g_new0(struct virtio_gpu_virgl_hostmem_region, 1);
+ vmr->g = g;
+
+ mr = &vmr->mr;
+ memory_region_init_ram_ptr(mr, OBJECT(mr), "blob", size, data);
+ memory_region_add_subregion(&b->hostmem, offset, mr);
+ memory_region_set_enabled(mr, true);
+
+ /*
+ * MR could outlive the resource if MR's reference is held outside of
+ * virtio-gpu. In order to prevent unmapping resource while MR is alive,
+ * and thus, making the data pointer invalid, we will block virtio-gpu
+ * command processing until MR is fully unreferenced and freed.
+ */
+ OBJECT(mr)->free = virtio_gpu_virgl_hostmem_region_free;
+
+ res->mr = mr;
+
+ return 0;
+}
+
+static int
+virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g,
+ struct virtio_gpu_virgl_resource *res,
+ bool *cmd_suspended)
+{
+ struct virtio_gpu_virgl_hostmem_region *vmr;
+ VirtIOGPUBase *b = VIRTIO_GPU_BASE(g);
+ MemoryRegion *mr = res->mr;
+ int ret;
+
+ if (!mr) {
+ return 0;
+ }
+
+ vmr = to_hostmem_region(res->mr);
+
+ /*
+ * Perform async unmapping in 3 steps:
+ *
+ * 1. Begin async unmapping with memory_region_del_subregion()
+ * and suspend/block cmd processing.
+ * 2. Wait for res->mr to be freed and cmd processing resumed
+ * asynchronously by virtio_gpu_virgl_hostmem_region_free().
+ * 3. Finish the unmapping with final virgl_renderer_resource_unmap().
+ */
+ if (vmr->finish_unmapping) {
+ res->mr = NULL;
+ g_free(vmr);
+
+ ret = virgl_renderer_resource_unmap(res->base.resource_id);
+ if (ret) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: failed to unmap virgl resource: %s\n",
+ __func__, strerror(-ret));
+ return ret;
+ }
+ } else {
+ *cmd_suspended = true;
+
+ /* render will be unblocked once MR is freed */
+ b->renderer_blocked++;
+
+ /* memory region owns self res->mr object and frees it by itself */
+ memory_region_set_enabled(mr, false);
+ memory_region_del_subregion(&b->hostmem, mr);
+ object_unparent(OBJECT(mr));
+ }
+
+ return 0;
+}
+#endif
+
static void virgl_cmd_create_resource_2d(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
{
struct virtio_gpu_resource_create_2d c2d;
struct virgl_renderer_resource_create_args args;
+ struct virtio_gpu_virgl_resource *res;
VIRTIO_GPU_FILL_CMD(c2d);
trace_virtio_gpu_cmd_res_create_2d(c2d.resource_id, c2d.format,
c2d.width, c2d.height);
+ if (c2d.resource_id == 0) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n",
+ __func__);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ res = virtio_gpu_virgl_find_resource(g, c2d.resource_id);
+ if (res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n",
+ __func__, c2d.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ res = g_new0(struct virtio_gpu_virgl_resource, 1);
+ res->base.width = c2d.width;
+ res->base.height = c2d.height;
+ res->base.format = c2d.format;
+ res->base.resource_id = c2d.resource_id;
+ res->base.dmabuf_fd = -1;
+ QTAILQ_INSERT_HEAD(&g->reslist, &res->base, next);
+
args.handle = c2d.resource_id;
args.target = 2;
args.format = c2d.format;
@@ -59,11 +242,35 @@ static void virgl_cmd_create_resource_3d(VirtIOGPU *g,
{
struct virtio_gpu_resource_create_3d c3d;
struct virgl_renderer_resource_create_args args;
+ struct virtio_gpu_virgl_resource *res;
VIRTIO_GPU_FILL_CMD(c3d);
trace_virtio_gpu_cmd_res_create_3d(c3d.resource_id, c3d.format,
c3d.width, c3d.height, c3d.depth);
+ if (c3d.resource_id == 0) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n",
+ __func__);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ res = virtio_gpu_virgl_find_resource(g, c3d.resource_id);
+ if (res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n",
+ __func__, c3d.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ res = g_new0(struct virtio_gpu_virgl_resource, 1);
+ res->base.width = c3d.width;
+ res->base.height = c3d.height;
+ res->base.format = c3d.format;
+ res->base.resource_id = c3d.resource_id;
+ res->base.dmabuf_fd = -1;
+ QTAILQ_INSERT_HEAD(&g->reslist, &res->base, next);
+
args.handle = c3d.resource_id;
args.target = c3d.target;
args.format = c3d.format;
@@ -79,15 +286,35 @@ static void virgl_cmd_create_resource_3d(VirtIOGPU *g,
}
static void virgl_cmd_resource_unref(VirtIOGPU *g,
- struct virtio_gpu_ctrl_command *cmd)
+ struct virtio_gpu_ctrl_command *cmd,
+ bool *cmd_suspended)
{
struct virtio_gpu_resource_unref unref;
+ struct virtio_gpu_virgl_resource *res;
struct iovec *res_iovs = NULL;
int num_iovs = 0;
VIRTIO_GPU_FILL_CMD(unref);
trace_virtio_gpu_cmd_res_unref(unref.resource_id);
+ res = virtio_gpu_virgl_find_resource(g, unref.resource_id);
+ if (!res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n",
+ __func__, unref.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+#if VIRGL_VERSION_MAJOR >= 1
+ if (virtio_gpu_virgl_unmap_resource_blob(g, res, cmd_suspended)) {
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+ if (*cmd_suspended) {
+ return;
+ }
+#endif
+
virgl_renderer_resource_detach_iov(unref.resource_id,
&res_iovs,
&num_iovs);
@@ -95,6 +322,10 @@ static void virgl_cmd_resource_unref(VirtIOGPU *g,
virtio_gpu_cleanup_mapping_iov(g, res_iovs, num_iovs);
}
virgl_renderer_resource_unref(unref.resource_id);
+
+ QTAILQ_REMOVE(&g->reslist, &res->base, next);
+
+ g_free(res);
}
static void virgl_cmd_context_create(VirtIOGPU *g,
@@ -106,8 +337,24 @@ static void virgl_cmd_context_create(VirtIOGPU *g,
trace_virtio_gpu_cmd_ctx_create(cc.hdr.ctx_id,
cc.debug_name);
- virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen,
- cc.debug_name);
+ if (cc.context_init) {
+ if (!virtio_gpu_context_init_enabled(g->parent_obj.conf)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: context_init disabled",
+ __func__);
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+
+#if VIRGL_VERSION_MAJOR >= 1
+ virgl_renderer_context_create_with_flags(cc.hdr.ctx_id,
+ cc.context_init,
+ cc.nlen,
+ cc.debug_name);
+ return;
+#endif
+ }
+
+ virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen, cc.debug_name);
}
static void virgl_cmd_context_destroy(VirtIOGPU *g,
@@ -171,7 +418,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
struct virgl_renderer_resource_info info;
void *d3d_tex2d = NULL;
-#ifdef HAVE_VIRGL_D3D_INFO_EXT
+#if VIRGL_VERSION_MAJOR >= 1
struct virgl_renderer_resource_info_ext ext;
memset(&ext, 0, sizeof(ext));
ret = virgl_renderer_resource_get_info_ext(ss.resource_id, &ext);
@@ -375,19 +622,13 @@ static void virgl_cmd_get_capset_info(VirtIOGPU *g,
VIRTIO_GPU_FILL_CMD(info);
memset(&resp, 0, sizeof(resp));
- if (info.capset_index == 0) {
- resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL;
- virgl_renderer_get_cap_set(resp.capset_id,
- &resp.capset_max_version,
- &resp.capset_max_size);
- } else if (info.capset_index == 1) {
- resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL2;
+
+ if (info.capset_index < g->capset_ids->len) {
+ resp.capset_id = g_array_index(g->capset_ids, uint32_t,
+ info.capset_index);
virgl_renderer_get_cap_set(resp.capset_id,
&resp.capset_max_version,
&resp.capset_max_size);
- } else {
- resp.capset_max_version = 0;
- resp.capset_max_size = 0;
}
resp.hdr.type = VIRTIO_GPU_RESP_OK_CAPSET_INFO;
virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp));
@@ -417,9 +658,241 @@ static void virgl_cmd_get_capset(VirtIOGPU *g,
g_free(resp);
}
+#if VIRGL_VERSION_MAJOR >= 1
+static void virgl_cmd_resource_create_blob(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virgl_renderer_resource_create_blob_args virgl_args = { 0 };
+ g_autofree struct virtio_gpu_virgl_resource *res = NULL;
+ struct virtio_gpu_resource_create_blob cblob;
+ struct virgl_renderer_resource_info info;
+ int ret;
+
+ if (!virtio_gpu_blob_enabled(g->parent_obj.conf)) {
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ VIRTIO_GPU_FILL_CMD(cblob);
+ virtio_gpu_create_blob_bswap(&cblob);
+ trace_virtio_gpu_cmd_res_create_blob(cblob.resource_id, cblob.size);
+
+ if (cblob.resource_id == 0) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n",
+ __func__);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ res = virtio_gpu_virgl_find_resource(g, cblob.resource_id);
+ if (res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n",
+ __func__, cblob.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ res = g_new0(struct virtio_gpu_virgl_resource, 1);
+ res->base.resource_id = cblob.resource_id;
+ res->base.blob_size = cblob.size;
+ res->base.dmabuf_fd = -1;
+
+ if (cblob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D) {
+ ret = virtio_gpu_create_mapping_iov(g, cblob.nr_entries, sizeof(cblob),
+ cmd, &res->base.addrs,
+ &res->base.iov, &res->base.iov_cnt);
+ if (!ret) {
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+ }
+
+ virgl_args.res_handle = cblob.resource_id;
+ virgl_args.ctx_id = cblob.hdr.ctx_id;
+ virgl_args.blob_mem = cblob.blob_mem;
+ virgl_args.blob_id = cblob.blob_id;
+ virgl_args.blob_flags = cblob.blob_flags;
+ virgl_args.size = cblob.size;
+ virgl_args.iovecs = res->base.iov;
+ virgl_args.num_iovs = res->base.iov_cnt;
+
+ ret = virgl_renderer_resource_create_blob(&virgl_args);
+ if (ret) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: virgl blob create error: %s\n",
+ __func__, strerror(-ret));
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ virtio_gpu_cleanup_mapping(g, &res->base);
+ return;
+ }
+
+ ret = virgl_renderer_resource_get_info(cblob.resource_id, &info);
+ if (ret) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: resource does not have info %d: %s\n",
+ __func__, cblob.resource_id, strerror(-ret));
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ virtio_gpu_cleanup_mapping(g, &res->base);
+ virgl_renderer_resource_unref(cblob.resource_id);
+ return;
+ }
+
+ res->base.dmabuf_fd = info.fd;
+
+ QTAILQ_INSERT_HEAD(&g->reslist, &res->base, next);
+ res = NULL;
+}
+
+static void virgl_cmd_resource_map_blob(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_map_blob mblob;
+ struct virtio_gpu_virgl_resource *res;
+ struct virtio_gpu_resp_map_info resp;
+ int ret;
+
+ VIRTIO_GPU_FILL_CMD(mblob);
+ virtio_gpu_map_blob_bswap(&mblob);
+
+ res = virtio_gpu_virgl_find_resource(g, mblob.resource_id);
+ if (!res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n",
+ __func__, mblob.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ ret = virtio_gpu_virgl_map_resource_blob(g, res, mblob.offset);
+ if (ret) {
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+
+ memset(&resp, 0, sizeof(resp));
+ resp.hdr.type = VIRTIO_GPU_RESP_OK_MAP_INFO;
+ virgl_renderer_resource_get_map_info(mblob.resource_id, &resp.map_info);
+ virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp));
+}
+
+static void virgl_cmd_resource_unmap_blob(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd,
+ bool *cmd_suspended)
+{
+ struct virtio_gpu_resource_unmap_blob ublob;
+ struct virtio_gpu_virgl_resource *res;
+ int ret;
+
+ VIRTIO_GPU_FILL_CMD(ublob);
+ virtio_gpu_unmap_blob_bswap(&ublob);
+
+ res = virtio_gpu_virgl_find_resource(g, ublob.resource_id);
+ if (!res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n",
+ __func__, ublob.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+
+ ret = virtio_gpu_virgl_unmap_resource_blob(g, res, cmd_suspended);
+ if (ret) {
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+}
+
+static void virgl_cmd_set_scanout_blob(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_framebuffer fb = { 0 };
+ struct virtio_gpu_virgl_resource *res;
+ struct virtio_gpu_set_scanout_blob ss;
+ uint64_t fbend;
+
+ VIRTIO_GPU_FILL_CMD(ss);
+ virtio_gpu_scanout_blob_bswap(&ss);
+ trace_virtio_gpu_cmd_set_scanout_blob(ss.scanout_id, ss.resource_id,
+ ss.r.width, ss.r.height, ss.r.x,
+ ss.r.y);
+
+ if (ss.scanout_id >= g->parent_obj.conf.max_outputs) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
+ __func__, ss.scanout_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
+ return;
+ }
+
+ if (ss.resource_id == 0) {
+ virtio_gpu_disable_scanout(g, ss.scanout_id);
+ return;
+ }
+
+ if (ss.width < 16 ||
+ ss.height < 16 ||
+ ss.r.x + ss.r.width > ss.width ||
+ ss.r.y + ss.r.height > ss.height) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for"
+ " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n",
+ __func__, ss.scanout_id, ss.resource_id,
+ ss.r.x, ss.r.y, ss.r.width, ss.r.height,
+ ss.width, ss.height);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ res = virtio_gpu_virgl_find_resource(g, ss.resource_id);
+ if (!res) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n",
+ __func__, ss.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+ if (res->base.dmabuf_fd < 0) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: resource not backed by dmabuf %d\n",
+ __func__, ss.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+
+ fb.format = virtio_gpu_get_pixman_format(ss.format);
+ if (!fb.format) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: pixel format not supported %d\n",
+ __func__, ss.format);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ fb.bytes_pp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(fb.format), 8);
+ fb.width = ss.width;
+ fb.height = ss.height;
+ fb.stride = ss.strides[0];
+ fb.offset = ss.offsets[0] + ss.r.x * fb.bytes_pp + ss.r.y * fb.stride;
+
+ fbend = fb.offset;
+ fbend += fb.stride * (ss.r.height - 1);
+ fbend += fb.bytes_pp * ss.r.width;
+ if (fbend > res->base.blob_size) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: fb end out of range\n",
+ __func__);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ g->parent_obj.enable = 1;
+ if (virtio_gpu_update_dmabuf(g, ss.scanout_id, &res->base, &fb, &ss.r)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to update dmabuf\n",
+ __func__);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ virtio_gpu_update_scanout(g, ss.scanout_id, &res->base, &fb, &ss.r);
+}
+#endif
+
void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
{
+ bool cmd_suspended = false;
+
VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr);
virgl_renderer_force_ctx_0();
@@ -461,7 +934,7 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
virgl_cmd_resource_flush(g, cmd);
break;
case VIRTIO_GPU_CMD_RESOURCE_UNREF:
- virgl_cmd_resource_unref(g, cmd);
+ virgl_cmd_resource_unref(g, cmd, &cmd_suspended);
break;
case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE:
/* TODO add security */
@@ -483,12 +956,26 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
case VIRTIO_GPU_CMD_GET_EDID:
virtio_gpu_get_edid(g, cmd);
break;
+#if VIRGL_VERSION_MAJOR >= 1
+ case VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB:
+ virgl_cmd_resource_create_blob(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB:
+ virgl_cmd_resource_map_blob(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB:
+ virgl_cmd_resource_unmap_blob(g, cmd, &cmd_suspended);
+ break;
+ case VIRTIO_GPU_CMD_SET_SCANOUT_BLOB:
+ virgl_cmd_set_scanout_blob(g, cmd);
+ break;
+#endif
default:
cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
break;
}
- if (cmd->finished) {
+ if (cmd_suspended || cmd->finished) {
return;
}
if (cmd->error) {
@@ -525,7 +1012,7 @@ static void virgl_write_fence(void *opaque, uint32_t fence)
g_free(cmd);
g->inflight--;
if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
- fprintf(stderr, "inflight: %3d (-)\r", g->inflight);
+ trace_virtio_gpu_dec_inflight_fences(g->inflight);
}
}
}
@@ -574,6 +1061,7 @@ static struct virgl_renderer_callbacks virtio_gpu_3d_cbs = {
static void virtio_gpu_print_stats(void *opaque)
{
VirtIOGPU *g = opaque;
+ VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
if (g->stats.requests) {
fprintf(stderr, "stats: vq req %4d, %3d -- 3D %4d (%5d)\n",
@@ -588,17 +1076,18 @@ static void virtio_gpu_print_stats(void *opaque)
} else {
fprintf(stderr, "stats: idle\r");
}
- timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
+ timer_mod(gl->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
}
static void virtio_gpu_fence_poll(void *opaque)
{
VirtIOGPU *g = opaque;
+ VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
virgl_renderer_poll();
virtio_gpu_process_cmdq(g);
if (!QTAILQ_EMPTY(&g->cmdq) || !QTAILQ_EMPTY(&g->fenceq)) {
- timer_mod(g->fence_poll, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10);
+ timer_mod(gl->fence_poll, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10);
}
}
@@ -626,6 +1115,7 @@ int virtio_gpu_virgl_init(VirtIOGPU *g)
{
int ret;
uint32_t flags = 0;
+ VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
#if VIRGL_RENDERER_CALLBACKS_VERSION >= 4
if (qemu_egl_display) {
@@ -638,6 +1128,11 @@ int virtio_gpu_virgl_init(VirtIOGPU *g)
flags |= VIRGL_RENDERER_D3D11_SHARE_TEXTURE;
}
#endif
+#if VIRGL_VERSION_MAJOR >= 1
+ if (virtio_gpu_venus_enabled(g->parent_obj.conf)) {
+ flags |= VIRGL_RENDERER_VENUS | VIRGL_RENDERER_RENDER_SERVER;
+ }
+#endif
ret = virgl_renderer_init(g, flags, &virtio_gpu_3d_cbs);
if (ret != 0) {
@@ -645,23 +1140,55 @@ int virtio_gpu_virgl_init(VirtIOGPU *g)
return ret;
}
- g->fence_poll = timer_new_ms(QEMU_CLOCK_VIRTUAL,
- virtio_gpu_fence_poll, g);
+ gl->fence_poll = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ virtio_gpu_fence_poll, g);
if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
- g->print_stats = timer_new_ms(QEMU_CLOCK_VIRTUAL,
- virtio_gpu_print_stats, g);
- timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
+ gl->print_stats = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ virtio_gpu_print_stats, g);
+ timer_mod(gl->print_stats,
+ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
}
+
+#if VIRGL_VERSION_MAJOR >= 1
+ gl->cmdq_resume_bh = aio_bh_new(qemu_get_aio_context(),
+ virtio_gpu_virgl_resume_cmdq_bh,
+ g);
+#endif
+
return 0;
}
-int virtio_gpu_virgl_get_num_capsets(VirtIOGPU *g)
+static void virtio_gpu_virgl_add_capset(GArray *capset_ids, uint32_t capset_id)
{
- uint32_t capset2_max_ver, capset2_max_size;
+ g_array_append_val(capset_ids, capset_id);
+}
+
+GArray *virtio_gpu_virgl_get_capsets(VirtIOGPU *g)
+{
+ uint32_t capset_max_ver, capset_max_size;
+ GArray *capset_ids;
+
+ capset_ids = g_array_new(false, false, sizeof(uint32_t));
+
+ /* VIRGL is always supported. */
+ virtio_gpu_virgl_add_capset(capset_ids, VIRTIO_GPU_CAPSET_VIRGL);
+
virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_VIRGL2,
- &capset2_max_ver,
- &capset2_max_size);
+ &capset_max_ver,
+ &capset_max_size);
+ if (capset_max_ver) {
+ virtio_gpu_virgl_add_capset(capset_ids, VIRTIO_GPU_CAPSET_VIRGL2);
+ }
+
+ if (virtio_gpu_venus_enabled(g->parent_obj.conf)) {
+ virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_VENUS,
+ &capset_max_ver,
+ &capset_max_size);
+ if (capset_max_size) {
+ virtio_gpu_virgl_add_capset(capset_ids, VIRTIO_GPU_CAPSET_VENUS);
+ }
+ }
- return capset2_max_ver ? 2 : 1;
+ return capset_ids;
}
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 49fd803393..c0570ef856 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -362,7 +362,7 @@ static void virtio_gpu_resource_create_blob(VirtIOGPU *g,
QTAILQ_INSERT_HEAD(&g->reslist, res, next);
}
-static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id)
+void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id)
{
struct virtio_gpu_scanout *scanout = &g->parent_obj.scanout[scanout_id];
struct virtio_gpu_simple_resource *res;
@@ -579,11 +579,11 @@ static void virtio_unref_resource(pixman_image_t *image, void *data)
pixman_image_unref(data);
}
-static void virtio_gpu_update_scanout(VirtIOGPU *g,
- uint32_t scanout_id,
- struct virtio_gpu_simple_resource *res,
- struct virtio_gpu_framebuffer *fb,
- struct virtio_gpu_rect *r)
+void virtio_gpu_update_scanout(VirtIOGPU *g,
+ uint32_t scanout_id,
+ struct virtio_gpu_simple_resource *res,
+ struct virtio_gpu_framebuffer *fb,
+ struct virtio_gpu_rect *r)
{
struct virtio_gpu_simple_resource *ores;
struct virtio_gpu_scanout *scanout;
@@ -1034,6 +1034,12 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
/* process command */
vgc->process_cmd(g, cmd);
+ /* command suspended */
+ if (!cmd->finished && !(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) {
+ trace_virtio_gpu_cmd_suspended(cmd->cmd_hdr.type);
+ break;
+ }
+
QTAILQ_REMOVE(&g->cmdq, cmd, next);
if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
g->stats.requests++;
@@ -1046,7 +1052,7 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g)
if (g->stats.max_inflight < g->inflight) {
g->stats.max_inflight = g->inflight;
}
- fprintf(stderr, "inflight: %3d (+)\r", g->inflight);
+ trace_virtio_gpu_inc_inflight_fences(g->inflight);
}
} else {
g_free(cmd);
@@ -1066,7 +1072,7 @@ static void virtio_gpu_process_fenceq(VirtIOGPU *g)
g_free(cmd);
g->inflight--;
if (virtio_gpu_stats_enabled(g->parent_obj.conf)) {
- fprintf(stderr, "inflight: %3d (-)\r", g->inflight);
+ trace_virtio_gpu_dec_inflight_fences(g->inflight);
}
}
}
@@ -1455,15 +1461,35 @@ void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
if (virtio_gpu_blob_enabled(g->parent_obj.conf)) {
if (!virtio_gpu_rutabaga_enabled(g->parent_obj.conf) &&
+ !virtio_gpu_virgl_enabled(g->parent_obj.conf) &&
!virtio_gpu_have_udmabuf()) {
error_setg(errp, "need rutabaga or udmabuf for blob resources");
return;
}
+#ifdef VIRGL_VERSION_MAJOR
+ #if VIRGL_VERSION_MAJOR < 1
if (virtio_gpu_virgl_enabled(g->parent_obj.conf)) {
- error_setg(errp, "blobs and virgl are not compatible (yet)");
+ error_setg(errp, "old virglrenderer, blob resources unsupported");
return;
}
+ #endif
+#endif
+ }
+
+ if (virtio_gpu_venus_enabled(g->parent_obj.conf)) {
+#ifdef VIRGL_VERSION_MAJOR
+ #if VIRGL_VERSION_MAJOR >= 1
+ if (!virtio_gpu_blob_enabled(g->parent_obj.conf) ||
+ !virtio_gpu_hostmem_enabled(g->parent_obj.conf)) {
+ error_setg(errp, "venus requires enabled blob and hostmem options");
+ return;
+ }
+ #else
+ error_setg(errp, "old virglrenderer, venus unsupported");
+ return;
+ #endif
+#endif
}
if (!virtio_gpu_base_device_realize(qdev,
diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c
index 53defee7d5..e2a73337b1 100644
--- a/hw/intc/arm_gic_kvm.c
+++ b/hw/intc/arm_gic_kvm.c
@@ -547,17 +547,10 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true,
&error_abort);
}
- } else if (kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
+ } else {
error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
error_append_hint(errp,
"Perhaps the host CPU does not support GICv2?\n");
- } else if (ret != -ENODEV && ret != -ENOTSUP) {
- /*
- * Very ancient kernel without KVM_CAP_DEVICE_CTRL: assume that
- * ENODEV or ENOTSUP mean "can't create GICv2 with KVM_CREATE_DEVICE",
- * and that we will get a GICv2 via KVM_CREATE_IRQCHIP.
- */
- error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
return;
}
diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c
index 91e9a3f1c6..1d4e30e6b7 100644
--- a/hw/sd/omap_mmc.c
+++ b/hw/sd/omap_mmc.c
@@ -103,6 +103,7 @@ static void omap_mmc_fifolevel_update(struct omap_mmc_s *host)
}
}
+/* These must match the encoding of the MMC_CMD Response field */
typedef enum {
sd_nore = 0, /* no response */
sd_r1, /* normal response command */
@@ -112,8 +113,17 @@ typedef enum {
sd_r1b = -1,
} sd_rsp_type_t;
+/* These must match the encoding of the MMC_CMD Type field */
+typedef enum {
+ SD_TYPE_BC = 0, /* broadcast -- no response */
+ SD_TYPE_BCR = 1, /* broadcast with response */
+ SD_TYPE_AC = 2, /* addressed -- no data transfer */
+ SD_TYPE_ADTC = 3, /* addressed with data transfer */
+} MMCCmdType;
+
static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir,
- sd_cmd_type_t type, int busy, sd_rsp_type_t resptype, int init)
+ MMCCmdType type, int busy,
+ sd_rsp_type_t resptype, int init)
{
uint32_t rspstatus, mask;
int rsplen, timeout;
@@ -128,7 +138,7 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir,
if (resptype == sd_r1 && busy)
resptype = sd_r1b;
- if (type == sd_adtc) {
+ if (type == SD_TYPE_ADTC) {
host->fifo_start = 0;
host->fifo_len = 0;
host->transfer = 1;
@@ -433,10 +443,10 @@ static void omap_mmc_write(void *opaque, hwaddr offset,
for (i = 0; i < 8; i ++)
s->rsp[i] = 0x0000;
omap_mmc_command(s, value & 63, (value >> 15) & 1,
- (sd_cmd_type_t) ((value >> 12) & 3),
- (value >> 11) & 1,
- (sd_rsp_type_t) ((value >> 8) & 7),
- (value >> 7) & 1);
+ (MMCCmdType)((value >> 12) & 3),
+ (value >> 11) & 1,
+ (sd_rsp_type_t) ((value >> 8) & 7),
+ (value >> 7) & 1);
omap_mmc_update(s);
break;
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index a5d2d929a8..b2e2d58e01 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -71,6 +71,14 @@ typedef enum {
sd_illegal = -2,
} sd_rsp_type_t;
+typedef enum {
+ sd_spi,
+ sd_bc, /* broadcast -- no response */
+ sd_bcr, /* broadcast with response */
+ sd_ac, /* addressed -- no data transfer */
+ sd_adtc, /* addressed with data transfer */
+} sd_cmd_type_t;
+
enum SDCardModes {
sd_inactive,
sd_card_identification_mode,
diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h
index d35a839f5e..f2458f37b3 100644
--- a/include/hw/sd/sd.h
+++ b/include/hw/sd/sd.h
@@ -75,14 +75,6 @@ typedef enum {
UHS_III = 3, /* currently not supported */
} sd_uhs_mode_t;
-typedef enum {
- sd_spi,
- sd_bc, /* broadcast -- no response */
- sd_bcr, /* broadcast with response */
- sd_ac, /* addressed -- no data transfer */
- sd_adtc, /* addressed with data transfer */
-} sd_cmd_type_t;
-
typedef struct {
uint8_t cmd;
uint32_t arg;
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index e343110e23..553799b8cc 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -97,6 +97,7 @@ enum virtio_gpu_base_conf_flags {
VIRTIO_GPU_FLAG_BLOB_ENABLED,
VIRTIO_GPU_FLAG_CONTEXT_INIT_ENABLED,
VIRTIO_GPU_FLAG_RUTABAGA_ENABLED,
+ VIRTIO_GPU_FLAG_VENUS_ENABLED,
};
#define virtio_gpu_virgl_enabled(_cfg) \
@@ -115,6 +116,8 @@ enum virtio_gpu_base_conf_flags {
(_cfg.flags & (1 << VIRTIO_GPU_FLAG_RUTABAGA_ENABLED))
#define virtio_gpu_hostmem_enabled(_cfg) \
(_cfg.hostmem > 0)
+#define virtio_gpu_venus_enabled(_cfg) \
+ (_cfg.flags & (1 << VIRTIO_GPU_FLAG_VENUS_ENABLED))
struct virtio_gpu_base_conf {
uint32_t max_outputs;
@@ -194,8 +197,6 @@ struct VirtIOGPU {
uint64_t hostmem;
bool processing_cmdq;
- QEMUTimer *fence_poll;
- QEMUTimer *print_stats;
uint32_t inflight;
struct {
@@ -209,6 +210,8 @@ struct VirtIOGPU {
QTAILQ_HEAD(, VGPUDMABuf) bufs;
VGPUDMABuf *primary[VIRTIO_GPU_MAX_SCANOUTS];
} dmabuf;
+
+ GArray *capset_ids;
};
struct VirtIOGPUClass {
@@ -224,11 +227,23 @@ struct VirtIOGPUClass {
Error **errp);
};
+/* VirtIOGPUGL renderer states */
+typedef enum {
+ RS_START, /* starting state */
+ RS_INIT_FAILED, /* failed initialisation */
+ RS_INITED, /* initialised and working */
+ RS_RESET, /* inited and reset pending, moves to start after reset */
+} RenderState;
+
struct VirtIOGPUGL {
struct VirtIOGPU parent_obj;
- bool renderer_inited;
- bool renderer_reset;
+ RenderState renderer_state;
+
+ QEMUTimer *fence_poll;
+ QEMUTimer *print_stats;
+
+ QEMUBH *cmdq_resume_bh;
};
struct VhostUserGPU {
@@ -328,6 +343,13 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g,
struct virtio_gpu_framebuffer *fb,
struct virtio_gpu_rect *r);
+void virtio_gpu_update_scanout(VirtIOGPU *g,
+ uint32_t scanout_id,
+ struct virtio_gpu_simple_resource *res,
+ struct virtio_gpu_framebuffer *fb,
+ struct virtio_gpu_rect *r);
+void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id);
+
/* virtio-gpu-3d.c */
void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd);
@@ -335,6 +357,6 @@ void virtio_gpu_virgl_fence_poll(VirtIOGPU *g);
void virtio_gpu_virgl_reset_scanout(VirtIOGPU *g);
void virtio_gpu_virgl_reset(VirtIOGPU *g);
int virtio_gpu_virgl_init(VirtIOGPU *g);
-int virtio_gpu_virgl_get_num_capsets(VirtIOGPU *g);
+GArray *virtio_gpu_virgl_get_capsets(VirtIOGPU *g);
#endif
diff --git a/meson.build b/meson.build
index 643da1378d..2c9086a3fe 100644
--- a/meson.build
+++ b/meson.build
@@ -2443,10 +2443,7 @@ config_host_data.set('CONFIG_VNC', vnc.found())
config_host_data.set('CONFIG_VNC_JPEG', jpeg.found())
config_host_data.set('CONFIG_VNC_SASL', sasl.found())
if virgl.found()
- config_host_data.set('HAVE_VIRGL_D3D_INFO_EXT',
- cc.has_member('struct virgl_renderer_resource_info_ext', 'd3d_tex2d',
- prefix: '#include <virglrenderer.h>',
- dependencies: virgl))
+ config_host_data.set('VIRGL_VERSION_MAJOR', virgl.version().split('.')[0])
endif
config_host_data.set('CONFIG_VIRTFS', have_virtfs)
config_host_data.set('CONFIG_VTE', vte.found())
diff --git a/scripts/symlink-install-tree.py b/scripts/symlink-install-tree.py
index 8ed97e3c94..b72563895c 100644
--- a/scripts/symlink-install-tree.py
+++ b/scripts/symlink-install-tree.py
@@ -4,6 +4,7 @@ from pathlib import PurePath
import errno
import json
import os
+import shlex
import subprocess
import sys
@@ -14,7 +15,7 @@ def destdir_join(d1: str, d2: str) -> str:
return str(PurePath(d1, *PurePath(d2).parts[1:]))
introspect = os.environ.get('MESONINTROSPECT')
-out = subprocess.run([*introspect.split(' '), '--installed'],
+out = subprocess.run([*shlex.split(introspect), '--installed'],
stdout=subprocess.PIPE, check=True).stdout
for source, dest in json.loads(out).items():
bundle_dest = destdir_join('qemu-bundle', dest)
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 1320fd8c8f..5b751439bd 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -2390,14 +2390,22 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
#ifndef CONFIG_USER_ONLY
/*
- * If we do not have tag-memory provided by the machine,
- * reduce MTE support to instructions enabled at EL0.
+ * If we run with TCG and do not have tag-memory provided by
+ * the machine, then reduce MTE support to instructions enabled at EL0.
* This matches Cortex-A710 BROADCASTMTE input being LOW.
*/
- if (cpu->tag_memory == NULL) {
+ if (tcg_enabled() && cpu->tag_memory == NULL) {
cpu->isar.id_aa64pfr1 =
FIELD_DP64(cpu->isar.id_aa64pfr1, ID_AA64PFR1, MTE, 1);
}
+
+ /*
+ * If MTE is supported by the host, however it should not be
+ * enabled on the guest (i.e mte=off), clear guest's MTE bits."
+ */
+ if (kvm_enabled() && !cpu->kvm_mte) {
+ FIELD_DP64(cpu->isar.id_aa64pfr1, ID_AA64PFR1, MTE, 0);
+ }
#endif
}
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index f065756c5c..8fc8b6398f 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -922,6 +922,8 @@ struct ArchCPU {
/* CPU has memory protection unit */
bool has_mpu;
+ /* CPU has MTE enabled in KVM mode */
+ bool kvm_mte;
/* PMSAv7 MPU number of supported regions */
uint32_t pmsav7_dregion;
/* PMSAv8 MPU number of supported hyp regions */
diff --git a/target/arm/internals.h b/target/arm/internals.h
index 299a96a81a..fd8f7c82aa 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -963,6 +963,7 @@ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx)
static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx)
{
switch (mmu_idx) {
+ case ARMMMUIdx_E10_0:
case ARMMMUIdx_E20_0:
case ARMMMUIdx_Stage1_E0:
case ARMMMUIdx_MUser:
@@ -972,10 +973,6 @@ static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx)
return true;
default:
return false;
- case ARMMMUIdx_E10_0:
- case ARMMMUIdx_E10_1:
- case ARMMMUIdx_E10_1_PAN:
- g_assert_not_reached();
}
}
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index f1f1b5b375..7b6812c0de 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -39,8 +39,10 @@
#include "hw/acpi/acpi.h"
#include "hw/acpi/ghes.h"
#include "target/arm/gtimer.h"
+#include "migration/blocker.h"
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
+ KVM_CAP_INFO(DEVICE_CTRL),
KVM_CAP_LAST_INFO
};
@@ -119,6 +121,21 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try,
if (vmfd < 0) {
goto err;
}
+
+ /*
+ * The MTE capability must be enabled by the VMM before creating
+ * any VCPUs in order to allow the MTE bits of the ID_AA64PFR1
+ * register to be probed correctly, as they are masked if MTE
+ * is not enabled.
+ */
+ if (kvm_arm_mte_supported()) {
+ KVMState kvm_state;
+
+ kvm_state.fd = kvmfd;
+ kvm_state.vmfd = vmfd;
+ kvm_vm_enable_cap(&kvm_state, KVM_CAP_ARM_MTE, 0);
+ }
+
cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0);
if (cpufd < 0) {
goto err;
@@ -675,19 +692,11 @@ static void kvm_arm_set_device_addr(KVMDevice *kd)
{
struct kvm_device_attr *attr = &kd->kdattr;
int ret;
+ uint64_t addr = kd->kda.addr;
- /* If the device control API is available and we have a device fd on the
- * KVMDevice struct, let's use the newer API
- */
- if (kd->dev_fd >= 0) {
- uint64_t addr = kd->kda.addr;
-
- addr |= kd->kda_addr_ormask;
- attr->addr = (uintptr_t)&addr;
- ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr);
- } else {
- ret = kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR, &kd->kda);
- }
+ addr |= kd->kda_addr_ormask;
+ attr->addr = (uintptr_t)&addr;
+ ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr);
if (ret < 0) {
fprintf(stderr, "Failed to set device address: %s\n",
@@ -1793,6 +1802,11 @@ bool kvm_arm_sve_supported(void)
return kvm_check_extension(kvm_state, KVM_CAP_ARM_SVE);
}
+bool kvm_arm_mte_supported(void)
+{
+ return kvm_check_extension(kvm_state, KVM_CAP_ARM_MTE);
+}
+
QEMU_BUILD_BUG_ON(KVM_ARM64_SVE_VQ_MIN != 1);
uint32_t kvm_arm_sve_get_vls(ARMCPU *cpu)
@@ -2417,3 +2431,40 @@ int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp)
}
return 0;
}
+
+void kvm_arm_enable_mte(Object *cpuobj, Error **errp)
+{
+ static bool tried_to_enable;
+ static bool succeeded_to_enable;
+ Error *mte_migration_blocker = NULL;
+ ARMCPU *cpu = ARM_CPU(cpuobj);
+ int ret;
+
+ if (!tried_to_enable) {
+ /*
+ * MTE on KVM is enabled on a per-VM basis (and retrying doesn't make
+ * sense), and we only want a single migration blocker as well.
+ */
+ tried_to_enable = true;
+
+ ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_ARM_MTE, 0);
+ if (ret) {
+ error_setg_errno(errp, -ret, "Failed to enable KVM_CAP_ARM_MTE");
+ return;
+ }
+
+ /* TODO: Add migration support with MTE enabled */
+ error_setg(&mte_migration_blocker,
+ "Live migration disabled due to MTE enabled");
+ if (migrate_add_blocker(&mte_migration_blocker, errp)) {
+ error_free(mte_migration_blocker);
+ return;
+ }
+
+ succeeded_to_enable = true;
+ }
+
+ if (succeeded_to_enable) {
+ cpu->kvm_mte = true;
+ }
+}
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index cfaa0d9bc7..2e6b49bf13 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -22,17 +22,15 @@
* @devid: the KVM device ID
* @group: device control API group for setting addresses
* @attr: device control API address type
- * @dev_fd: device control device file descriptor (or -1 if not supported)
+ * @dev_fd: device control device file descriptor
* @addr_ormask: value to be OR'ed with resolved address
*
- * Remember the memory region @mr, and when it is mapped by the
- * machine model, tell the kernel that base address using the
- * KVM_ARM_SET_DEVICE_ADDRESS ioctl or the newer device control API. @devid
- * should be the ID of the device as defined by KVM_ARM_SET_DEVICE_ADDRESS or
- * the arm-vgic device in the device control API.
- * The machine model may map
- * and unmap the device multiple times; the kernel will only be told the final
- * address at the point where machine init is complete.
+ * Remember the memory region @mr, and when it is mapped by the machine
+ * model, tell the kernel that base address using the device control API.
+ * @devid should be the ID of the device as defined by the arm-vgic device
+ * in the device control API. The machine model may map and unmap the device
+ * multiple times; the kernel will only be told the final address at the
+ * point where machine init is complete.
*/
void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group,
uint64_t attr, int dev_fd, uint64_t addr_ormask);
@@ -189,6 +187,13 @@ bool kvm_arm_pmu_supported(void);
bool kvm_arm_sve_supported(void);
/**
+ * kvm_arm_mte_supported:
+ *
+ * Returns: true if KVM can enable MTE, and false otherwise.
+ */
+bool kvm_arm_mte_supported(void);
+
+/**
* kvm_arm_get_max_vm_ipa_size:
* @ms: Machine state handle
* @fixed_ipa: True when the IPA limit is fixed at 40. This is the case
@@ -214,6 +219,8 @@ void kvm_arm_pvtime_init(ARMCPU *cpu, uint64_t ipa);
int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level);
+void kvm_arm_enable_mte(Object *cpuobj, Error **errp);
+
#else
/*
@@ -235,6 +242,11 @@ static inline bool kvm_arm_sve_supported(void)
return false;
}
+static inline bool kvm_arm_mte_supported(void)
+{
+ return false;
+}
+
/*
* These functions should never actually be called without KVM support.
*/
@@ -283,6 +295,11 @@ static inline uint32_t kvm_arm_sve_get_vls(ARMCPU *cpu)
g_assert_not_reached();
}
+static inline void kvm_arm_enable_mte(Object *cpuobj, Error **errp)
+{
+ g_assert_not_reached();
+}
+
#endif
#endif
diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c
index 56b431faf5..8f42a28d07 100644
--- a/target/arm/tcg/helper-a64.c
+++ b/target/arm/tcg/helper-a64.c
@@ -1348,7 +1348,7 @@ static void do_setm(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc,
/* Do the actual memset: we leave the last partial page to SETE */
stagesetsize = setsize & TARGET_PAGE_MASK;
while (stagesetsize > 0) {
- step = stepfn(env, toaddr, setsize, data, memidx, &mtedesc, ra);
+ step = stepfn(env, toaddr, stagesetsize, data, memidx, &mtedesc, ra);
toaddr += step;
setsize -= step;
stagesetsize -= step;
diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c
index 203d37303b..62638d2b1f 100644
--- a/target/arm/vfp_helper.c
+++ b/target/arm/vfp_helper.c
@@ -59,32 +59,6 @@ static inline int vfp_exceptbits_from_host(int host_bits)
return target_bits;
}
-/* Convert vfp exception flags to target form. */
-static inline int vfp_exceptbits_to_host(int target_bits)
-{
- int host_bits = 0;
-
- if (target_bits & 1) {
- host_bits |= float_flag_invalid;
- }
- if (target_bits & 2) {
- host_bits |= float_flag_divbyzero;
- }
- if (target_bits & 4) {
- host_bits |= float_flag_overflow;
- }
- if (target_bits & 8) {
- host_bits |= float_flag_underflow;
- }
- if (target_bits & 0x10) {
- host_bits |= float_flag_inexact;
- }
- if (target_bits & 0x80) {
- host_bits |= float_flag_input_denormal;
- }
- return host_bits;
-}
-
static uint32_t vfp_get_fpsr_from_host(CPUARMState *env)
{
uint32_t i;
@@ -99,15 +73,14 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env)
return vfp_exceptbits_from_host(i);
}
-static void vfp_set_fpsr_to_host(CPUARMState *env, uint32_t val)
+static void vfp_clear_float_status_exc_flags(CPUARMState *env)
{
/*
- * The exception flags are ORed together when we read fpscr so we
- * only need to preserve the current state in one of our
- * float_status values.
+ * Clear out all the exception-flag information in the float_status
+ * values. The caller should have arranged for env->vfp.fpsr to
+ * be the architecturally up-to-date exception flag information first.
*/
- int i = vfp_exceptbits_to_host(val);
- set_float_exception_flags(i, &env->vfp.fp_status);
+ set_float_exception_flags(0, &env->vfp.fp_status);
set_float_exception_flags(0, &env->vfp.fp_status_f16);
set_float_exception_flags(0, &env->vfp.standard_fp_status);
set_float_exception_flags(0, &env->vfp.standard_fp_status_f16);
@@ -164,7 +137,7 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env)
return 0;
}
-static void vfp_set_fpsr_to_host(CPUARMState *env, uint32_t val)
+static void vfp_clear_float_status_exc_flags(CPUARMState *env)
{
}
@@ -216,8 +189,6 @@ void vfp_set_fpsr(CPUARMState *env, uint32_t val)
{
ARMCPU *cpu = env_archcpu(env);
- vfp_set_fpsr_to_host(env, val);
-
if (arm_feature(env, ARM_FEATURE_NEON) ||
cpu_isar_feature(aa32_mve, cpu)) {
/*
@@ -231,13 +202,18 @@ void vfp_set_fpsr(CPUARMState *env, uint32_t val)
}
/*
- * The only FPSR bits we keep in vfp.fpsr are NZCV:
- * the exception flags IOC|DZC|OFC|UFC|IXC|IDC are stored in
- * fp_status, and QC is in vfp.qc[]. Store the NZCV bits there,
- * and zero any of the other FPSR bits.
+ * NZCV lives only in env->vfp.fpsr. The cumulative exception flags
+ * IOC|DZC|OFC|UFC|IXC|IDC also live in env->vfp.fpsr, with possible
+ * extra pending exception information that hasn't yet been folded in
+ * living in the float_status values (for TCG).
+ * Since this FPSR write gives us the up to date values of the exception
+ * flags, we want to store into vfp.fpsr the NZCV and CEXC bits, zeroing
+ * anything else. We also need to clear out the float_status exception
+ * information so that the next vfp_get_fpsr does not fold in stale data.
*/
- val &= FPSR_NZCV_MASK;
+ val &= FPSR_NZCV_MASK | FPSR_CEXC_MASK;
env->vfp.fpsr = val;
+ vfp_clear_float_status_exc_flags(env);
}
static void vfp_set_fpcr_masked(CPUARMState *env, uint32_t val, uint32_t mask)
diff --git a/tests/functional/meson.build b/tests/functional/meson.build
index 97c1c597e8..84a07970d4 100644
--- a/tests/functional/meson.build
+++ b/tests/functional/meson.build
@@ -18,6 +18,7 @@ test_timeouts = {
'arm_aspeed' : 600,
'arm_raspi2' : 120,
'arm_tuxrun' : 120,
+ 'arm_sx1' : 360,
'mips_malta' : 120,
'netdev_ethtool' : 180,
'ppc_40p' : 240,
@@ -54,8 +55,10 @@ tests_alpha_system_thorough = [
tests_arm_system_thorough = [
'arm_aspeed',
'arm_canona1100',
+ 'arm_collie',
'arm_integratorcp',
'arm_raspi2',
+ 'arm_sx1',
'arm_vexpress',
'arm_tuxrun',
]
diff --git a/tests/functional/test_arm_collie.py b/tests/functional/test_arm_collie.py
new file mode 100755
index 0000000000..7e144a0a8f
--- /dev/null
+++ b/tests/functional/test_arm_collie.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+#
+# Functional test that boots a Linux kernel on a collie machine
+# and checks the console
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+from qemu_test import LinuxKernelTest, Asset
+from qemu_test.utils import archive_extract
+
+class CollieTest(LinuxKernelTest):
+
+ ASSET_ZIMAGE = Asset(
+ 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/collie/zImage',
+ '10ace8abf9e0875ef8a83b8829cc3b5b50bc6d7bc3ca29f19f49f5673a43c13b')
+
+ ASSET_ROOTFS = Asset(
+ 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/collie/rootfs-sa110.cpio',
+ '89ccaaa5c6b33331887047e1618ffe81b0f55909173944347d5d2426f3bcc1f2')
+
+ def test_arm_collie(self):
+ self.set_machine('collie')
+ zimage_path = self.ASSET_ZIMAGE.fetch()
+ rootfs_path = self.ASSET_ROOTFS.fetch()
+ self.vm.add_args('-append', 'rdinit=/sbin/init console=ttySA1')
+ self.launch_kernel(zimage_path,
+ initrd=rootfs_path,
+ wait_for='reboot: Restarting system')
+
+if __name__ == '__main__':
+ LinuxKernelTest.main()
diff --git a/tests/functional/test_arm_sx1.py b/tests/functional/test_arm_sx1.py
new file mode 100755
index 0000000000..2d86405831
--- /dev/null
+++ b/tests/functional/test_arm_sx1.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2024 Linaro Ltd.
+#
+# Functional test that boots a Linux kernel on an sx1 machine
+# and checks the console. We have three variants:
+# * just boot initrd
+# * boot with filesystem on SD card
+# * boot from flash
+# In all cases these images have a userspace that is configured
+# to immediately reboot the system on successful boot, so we
+# only need to wait for QEMU to exit (via -no-reboot).
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+from qemu_test import LinuxKernelTest, Asset
+from qemu_test.utils import archive_extract
+
+class SX1Test(LinuxKernelTest):
+
+ ASSET_ZIMAGE = Asset(
+ 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/sx1/zImage',
+ 'a0271899a8dc2165f9e0adb2d0a57fc839ae3a469722ffc56c77e108a8887615')
+
+ ASSET_INITRD = Asset(
+ 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/sx1/rootfs-armv4.cpio',
+ '35b0721249821aa544cd85b85d3cb8901db4c6d128eed86ab261e5d9e37d58f8')
+
+ ASSET_SD_FS = Asset(
+ 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/sx1/rootfs-armv4.ext2',
+ 'c1db7f43ef92469ebc8605013728c8950e7608439f01d13678994f0ce101c3a8')
+
+ ASSET_FLASH = Asset(
+ 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/sx1/flash',
+ '17e6a2758fa38efd2666be0879d4751fd37d194f25168a8deede420df519b676')
+
+ CONSOLE_ARGS = 'console=ttyS0,115200 earlycon=uart8250,mmio32,0xfffb0000,115200n8'
+
+ def test_arm_sx1_initrd(self):
+ self.set_machine('sx1')
+ zimage_path = self.ASSET_ZIMAGE.fetch()
+ initrd_path = self.ASSET_INITRD.fetch()
+ self.vm.add_args('-append', f'kunit.enable=0 rdinit=/sbin/init {self.CONSOLE_ARGS}')
+ self.vm.add_args('-no-reboot')
+ self.launch_kernel(zimage_path,
+ initrd=initrd_path)
+ self.vm.wait()
+
+ def test_arm_sx1_sd(self):
+ self.set_machine('sx1')
+ zimage_path = self.ASSET_ZIMAGE.fetch()
+ sd_fs_path = self.ASSET_SD_FS.fetch()
+ self.vm.add_args('-append', f'kunit.enable=0 root=/dev/mmcblk0 rootwait {self.CONSOLE_ARGS}')
+ self.vm.add_args('-no-reboot')
+ self.vm.add_args('-snapshot')
+ self.vm.add_args('-drive', f'format=raw,if=sd,file={sd_fs_path}')
+ self.launch_kernel(zimage_path)
+ self.vm.wait()
+
+ def test_arm_sx1_flash(self):
+ self.set_machine('sx1')
+ zimage_path = self.ASSET_ZIMAGE.fetch()
+ flash_path = self.ASSET_FLASH.fetch()
+ self.vm.add_args('-append', f'kunit.enable=0 root=/dev/mtdblock3 rootwait {self.CONSOLE_ARGS}')
+ self.vm.add_args('-no-reboot')
+ self.vm.add_args('-snapshot')
+ self.vm.add_args('-drive', f'format=raw,if=pflash,file={flash_path}')
+ self.launch_kernel(zimage_path)
+ self.vm.wait()
+
+if __name__ == '__main__':
+ LinuxKernelTest.main()