aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS2
-rw-r--r--VERSION2
-rwxr-xr-xconfigure1
-rw-r--r--docs/about/build-platforms.rst8
-rw-r--r--docs/devel/qapi-domain.rst4
-rw-r--r--docs/igd-assign.txt1
-rw-r--r--docs/system/devices/net.rst16
-rw-r--r--docs/user/index.rst5
-rw-r--r--docs/user/main.rst60
-rw-r--r--hw/core/machine.c2
-rw-r--r--hw/display/qxl-render.c11
-rw-r--r--hw/display/sm501.c1
-rw-r--r--hw/i386/Kconfig2
-rw-r--r--hw/i386/microvm.c2
-rw-r--r--hw/i386/pc_piix.c2
-rw-r--r--hw/i386/pc_q35.c2
-rw-r--r--hw/intc/riscv_aplic.c6
-rw-r--r--hw/net/cadence_gem.c2
-rw-r--r--hw/riscv/virt-acpi-build.c25
-rw-r--r--hw/vfio/cpr.c2
-rw-r--r--hw/vfio/igd.c19
-rw-r--r--hw/vfio/pci.c29
-rw-r--r--hw/vfio/pci.h2
-rw-r--r--hw/vfio/types.h2
-rw-r--r--hw/vfio/vfio-migration-internal.h1
-rw-r--r--hw/xen/xen_pt.c1
-rw-r--r--linux-user/aarch64/signal.c139
-rw-r--r--linux-user/strace.list3
-rw-r--r--migration/meson.build2
-rw-r--r--migration/vfio.c (renamed from migration/target.c)2
-rw-r--r--qapi/accelerator.json4
-rw-r--r--qapi/dump.json2
-rw-r--r--qapi/machine.json2
-rw-r--r--qapi/migration.json4
-rw-r--r--qapi/misc-i386.json2
-rw-r--r--qapi/run-state.json2
-rw-r--r--qapi/sockets.json2
-rw-r--r--roms/Makefile8
-rw-r--r--rust/hw/char/pl011/Cargo.toml3
-rw-r--r--rust/hw/char/pl011/src/device.rs103
-rw-r--r--rust/hw/char/pl011/src/device_class.rs103
-rw-r--r--rust/hw/char/pl011/src/lib.rs1
-rw-r--r--rust/hw/timer/hpet/Cargo.toml3
-rwxr-xr-xscripts/get-wraps-from-cargo-registry.py190
-rw-r--r--system/physmem.c15
-rw-r--r--target/i386/tcg/decode-new.c.inc4
-rw-r--r--target/mips/tcg/system/cp0_helper.c32
-rw-r--r--target/riscv/csr.c15
-rw-r--r--target/riscv/insn_trans/trans_rvv.c.inc323
-rw-r--r--target/riscv/op_helper.c15
-rw-r--r--target/riscv/pmp.c7
-rw-r--r--tests/data/acpi/riscv64/virt/APICbin116 -> 116 bytes
-rw-r--r--tests/data/acpi/riscv64/virt/FACPbin276 -> 276 bytes
-rw-r--r--tests/docker/dockerfiles/debian-all-test-cross.docker34
-rw-r--r--tests/functional/meson.build1
-rw-r--r--tests/functional/qemu_test/testcase.py2
-rwxr-xr-xtests/functional/test_aarch64_kvm.py71
-rw-r--r--tests/qapi-schema/doc-non-first-section.err1
-rw-r--r--tests/qapi-schema/doc-non-first-section.json6
-rw-r--r--tests/qapi-schema/doc-non-first-section.out0
-rw-r--r--tests/tcg/Makefile.target36
-rw-r--r--tests/tcg/multiarch/Makefile.target8
-rw-r--r--tests/tcg/multiarch/system/Makefile.softmmu-target16
63 files changed, 817 insertions, 554 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index f1bd69c..069d77f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3511,6 +3511,7 @@ S: Maintained
F: rust/qemu-api
F: rust/qemu-api-macros
F: rust/rustfmt.toml
+F: scripts/get-wraps-from-cargo-registry.py
Rust-related patches CC here
L: qemu-rust@nongnu.org
@@ -4432,6 +4433,7 @@ F: docs/requirements.txt
F: docs/sphinx/
F: docs/_templates/
F: docs/devel/docs.rst
+F: docs/devel/qapi-domain.rst
Rust build system integration
M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
diff --git a/VERSION b/VERSION
index c52f1cc..8ae905b 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-10.0.90
+10.0.91
diff --git a/configure b/configure
index 95f67c1..825057e 100755
--- a/configure
+++ b/configure
@@ -1800,6 +1800,7 @@ echo "SRC_PATH=$source_path" >> tests/tcg/$config_host_mak
if test "$plugins" = "yes" ; then
echo "CONFIG_PLUGIN=y" >> tests/tcg/$config_host_mak
fi
+echo "PYTHON=$python" >> tests/tcg/$config_host_mak
tcg_tests_targets=
for target in $target_list; do
diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst
index 8ecbd6b..8671c3b 100644
--- a/docs/about/build-platforms.rst
+++ b/docs/about/build-platforms.rst
@@ -127,6 +127,14 @@ Rust build dependencies
(or newer) package. The path to ``rustc`` and ``rustdoc`` must be
provided manually to the configure script.
+ Some distros prefer to avoid vendored crate sources, and instead use
+ local sources from e.g. ``/usr/share/cargo/registry``. QEMU includes a
+ script, ``scripts/get-wraps-from-cargo-registry.py``, that automatically
+ performs this task. The script is meant to be invoked after unpacking
+ the QEMU tarball. QEMU also includes ``rust/Cargo.toml`` and
+ ``rust/Cargo.lock`` files that can be used to compute QEMU's build
+ dependencies, e.g. using ``cargo2rpm -p rust/Cargo.toml buildrequires``.
+
Optional build dependencies
Build components whose absence does not affect the ability to build QEMU
may not be available in distros, or may be too old for our requirements.
diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst
index b71890f..fe540d1 100644
--- a/docs/devel/qapi-domain.rst
+++ b/docs/devel/qapi-domain.rst
@@ -9,7 +9,7 @@ in Sphinx is provided by the QAPI Domain, located in
`Python Domain
<https://www.sphinx-doc.org/en/master/usage/domains/python.html>`_
included with Sphinx, but provides special directives and roles
-speciically for annotating and documenting QAPI definitions
+for annotating and documenting QAPI definitions
specifically.
A `Domain
@@ -101,7 +101,7 @@ without types. The QAPI domain uses this class for features, returns,
and enum values.
TypedField:
- * Creates a grouped, typed field. Multiple adjacent entres will be
+ * Creates a grouped, typed field. Multiple adjacent entries will be
merged into one section, and the content will form a bulleted list.
* *Must* take at least one argument, but supports up to two -
nominally, a name and a type.
diff --git a/docs/igd-assign.txt b/docs/igd-assign.txt
index af4e839..e540403 100644
--- a/docs/igd-assign.txt
+++ b/docs/igd-assign.txt
@@ -48,6 +48,7 @@ Intel document [1] shows how to dump VBIOS to file. For UEFI Option ROM, see
QEMU also provides a "Legacy" mode that implicitly enables full functionality
on IGD, it is automatically enabled when
* IGD generation is 6 to 9 (Sandy Bridge to Comet Lake)
+* IGD claims VGA cycles on host (IGD is VGA controller on host)
* Machine type is i440fx
* IGD is assigned to guest BDF 00:02.0
* ROM BAR or romfile is present
diff --git a/docs/system/devices/net.rst b/docs/system/devices/net.rst
index 4d787c3..7d76fe8 100644
--- a/docs/system/devices/net.rst
+++ b/docs/system/devices/net.rst
@@ -21,11 +21,17 @@ configure it as if it was a real ethernet card.
Linux host
^^^^^^^^^^
-As an example, you can download the ``linux-test-xxx.tar.gz`` archive
-and copy the script ``qemu-ifup`` in ``/etc`` and configure properly
-``sudo`` so that the command ``ifconfig`` contained in ``qemu-ifup`` can
-be executed as root. You must verify that your host kernel supports the
-TAP network interfaces: the device ``/dev/net/tun`` must be present.
+A distribution will generally provide specific helper scripts when it
+packages QEMU. By default these are found at ``/etc/qemu-ifup`` and
+``/etc/qemu-ifdown`` and are called appropriately when QEMU wants to
+change the network state.
+
+If QEMU is being run as a non-privileged user you may need properly
+configure ``sudo`` so that network commands in the scripts can be
+executed as root.
+
+You must verify that your host kernel supports the TAP network
+interfaces: the device ``/dev/net/tun`` must be present.
See :ref:`sec_005finvocation` to have examples of command
lines using the TAP network interfaces.
diff --git a/docs/user/index.rst b/docs/user/index.rst
index 782d27c..2307580 100644
--- a/docs/user/index.rst
+++ b/docs/user/index.rst
@@ -5,8 +5,9 @@ User Mode Emulation
-------------------
This section of the manual is the overall guide for users using QEMU
-for user-mode emulation. In this mode, QEMU can launch
-processes compiled for one CPU on another CPU.
+for user-mode emulation. In this mode, QEMU can launch programs
+compiled for one CPU architecture on the same Operating System (OS)
+but running on a different CPU architecture.
.. toctree::
:maxdepth: 2
diff --git a/docs/user/main.rst b/docs/user/main.rst
index 9a1c604..347bdfa 100644
--- a/docs/user/main.rst
+++ b/docs/user/main.rst
@@ -17,28 +17,44 @@ Features
QEMU user space emulation has the following notable features:
-**System call translation:**
- QEMU includes a generic system call translator. This means that the
- parameters of the system calls can be converted to fix endianness and
- 32/64-bit mismatches between hosts and targets. IOCTLs can be
- converted too.
-
-**POSIX signal handling:**
- QEMU can redirect to the running program all signals coming from the
- host (such as ``SIGALRM``), as well as synthesize signals from
- virtual CPU exceptions (for example ``SIGFPE`` when the program
- executes a division by zero).
-
- QEMU relies on the host kernel to emulate most signal system calls,
- for example to emulate the signal mask. On Linux, QEMU supports both
- normal and real-time signals.
-
-**Threading:**
- On Linux, QEMU can emulate the ``clone`` syscall and create a real
- host thread (with a separate virtual CPU) for each emulated thread.
- Note that not all targets currently emulate atomic operations
- correctly. x86 and Arm use a global lock in order to preserve their
- semantics.
+System call translation
+~~~~~~~~~~~~~~~~~~~~~~~
+
+System calls are the principle interface between user-space and the
+kernel. Generally the same system calls exist on all versions of the
+kernel so QEMU includes a generic system call translator. The
+translator takes care of adjusting endianess, 32/64 bit parameter size
+and then calling the equivalent host system call.
+
+QEMU can also adjust device specific ``ioctl()`` calls in a similar
+fashion.
+
+POSIX signal handling
+~~~~~~~~~~~~~~~~~~~~~
+
+QEMU can redirect to the running program all signals coming from the
+host (such as ``SIGALRM``), as well as synthesize signals from
+virtual CPU exceptions (for example ``SIGFPE`` when the program
+executes a division by zero).
+
+QEMU relies on the host kernel to emulate most signal system calls,
+for example to emulate the signal mask. On Linux, QEMU supports both
+normal and real-time signals.
+
+Threading
+~~~~~~~~~
+
+On Linux, QEMU can emulate the ``clone`` syscall and create a real
+host thread (with a separate virtual CPU) for each emulated thread.
+However as QEMU relies on the system libc to call ``clone`` on its
+behalf we limit the flags accepted to those it uses. Specifically this
+means flags affecting namespaces (e.g. container runtimes) are not
+supported. QEMU user-mode processes can still be run inside containers
+though.
+
+While QEMU does its best to emulate atomic operations properly
+differences between the host and guest memory models can cause issues
+for software that makes assumptions about the memory model.
QEMU was conceived so that ultimately it can emulate itself. Although it
is not very useful, it is an important test to show the power of the
diff --git a/hw/core/machine.c b/hw/core/machine.c
index d6b2240..bd47527 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -41,7 +41,7 @@ GlobalProperty hw_compat_10_0[] = {
{ "scsi-hd", "dpofua", "off" },
{ "vfio-pci", "x-migration-load-config-after-iter", "off" },
{ "ramfb", "use-legacy-x86-rom", "true"},
- { "vfio-pci", "use-legacy-x86-rom", "true" },
+ { "vfio-pci-nohotplug", "use-legacy-x86-rom", "true" },
};
const size_t hw_compat_10_0_len = G_N_ELEMENTS(hw_compat_10_0);
diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
index eda6d3d..c6a9ac1 100644
--- a/hw/display/qxl-render.c
+++ b/hw/display/qxl-render.c
@@ -222,6 +222,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl,
uint32_t max_chunks = 32;
size_t offset = 0;
size_t bytes;
+ QXLPHYSICAL next_chunk_phys = 0;
for (;;) {
bytes = MIN(size - offset, chunk->data_size);
@@ -230,7 +231,15 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl,
if (offset == size) {
return;
}
- chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id,
+ next_chunk_phys = chunk->next_chunk;
+ /* fist time, only get the next chunk's data size */
+ chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id,
+ sizeof(QXLDataChunk));
+ if (!chunk) {
+ return;
+ }
+ /* second time, check data size and get data */
+ chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id,
sizeof(QXLDataChunk) + chunk->data_size);
if (!chunk) {
return;
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index 6d2f186..bc091b3 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -26,6 +26,7 @@
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qapi/error.h"
+#include "qemu/error-report.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "hw/usb/hcd-ohci.h"
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index 14d23e2..5139d23 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -4,7 +4,7 @@ config X86_FW_OVMF
config SEV
bool
select X86_FW_OVMF
- depends on KVM
+ depends on KVM && X86_64
config SGX
bool
diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c
index d90b69a..94d22a2 100644
--- a/hw/i386/microvm.c
+++ b/hw/i386/microvm.c
@@ -635,7 +635,7 @@ GlobalProperty microvm_properties[] = {
*/
{ "pcie-root-port", "io-reserve", "0" },
{ TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" },
- { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" },
+ { TYPE_VFIO_PCI_NOHOTPLUG, "use-legacy-x86-rom", "true" },
};
static void microvm_class_init(ObjectClass *oc, const void *data)
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index ad5caff..c033242 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -80,7 +80,7 @@ static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
static GlobalProperty pc_piix_compat_defaults[] = {
{ TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" },
- { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" },
+ { TYPE_VFIO_PCI_NOHOTPLUG, "use-legacy-x86-rom", "true" },
};
static const size_t pc_piix_compat_defaults_len =
G_N_ELEMENTS(pc_piix_compat_defaults);
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 9b9519f..b309b2b 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -69,7 +69,7 @@
static GlobalProperty pc_q35_compat_defaults[] = {
{ TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "39" },
{ TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" },
- { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" },
+ { TYPE_VFIO_PCI_NOHOTPLUG, "use-legacy-x86-rom", "true" },
};
static const size_t pc_q35_compat_defaults_len =
G_N_ELEMENTS(pc_q35_compat_defaults);
diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c
index 4fa5f75..a1d9fa5 100644
--- a/hw/intc/riscv_aplic.c
+++ b/hw/intc/riscv_aplic.c
@@ -628,7 +628,7 @@ static void riscv_aplic_request(void *opaque, int irq, int level)
static uint64_t riscv_aplic_read(void *opaque, hwaddr addr, unsigned size)
{
- uint32_t irq, word, idc;
+ uint32_t irq, word, idc, sm;
RISCVAPLICState *aplic = opaque;
/* Reads must be 4 byte words */
@@ -696,6 +696,10 @@ static uint64_t riscv_aplic_read(void *opaque, hwaddr addr, unsigned size)
} else if ((APLIC_TARGET_BASE <= addr) &&
(addr < (APLIC_TARGET_BASE + (aplic->num_irqs - 1) * 4))) {
irq = ((addr - APLIC_TARGET_BASE) >> 2) + 1;
+ sm = aplic->sourcecfg[irq] & APLIC_SOURCECFG_SM_MASK;
+ if (sm == APLIC_SOURCECFG_SM_INACTIVE) {
+ return 0;
+ }
return aplic->target[irq];
} else if (!aplic->msimode && (APLIC_IDC_BASE <= addr) &&
(addr < (APLIC_IDC_BASE + aplic->num_harts * APLIC_IDC_SIZE))) {
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index 50025d5..4444666 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -1756,6 +1756,7 @@ static void gem_realize(DeviceState *dev, Error **errp)
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]);
}
+ gem_init_register_masks(s);
qemu_macaddr_default_if_unset(&s->conf.macaddr);
s->nic = qemu_new_nic(&net_gem_info, &s->conf,
@@ -1776,7 +1777,6 @@ static void gem_init(Object *obj)
DB_PRINT("\n");
- gem_init_register_masks(s);
memory_region_init_io(&s->iomem, OBJECT(s), &gem_ops, s,
"enet", sizeof(s->regs));
diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c
index ee1416d..f1406cb 100644
--- a/hw/riscv/virt-acpi-build.c
+++ b/hw/riscv/virt-acpi-build.c
@@ -270,11 +270,8 @@ spcr_setup(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s)
#define RHCT_NODE_ARRAY_OFFSET 56
/*
- * ACPI spec, Revision 6.5+
- * 5.2.36 RISC-V Hart Capabilities Table (RHCT)
- * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/16
- * https://drive.google.com/file/d/1nP3nFiH4jkPMp6COOxP6123DCZKR-tia/view
- * https://drive.google.com/file/d/1sKbOa8m1UZw1JkquZYe3F1zQBN1xXsaf/view
+ * ACPI spec, Revision 6.6
+ * 5.2.37 RISC-V Hart Capabilities Table (RHCT)
*/
static void build_rhct(GArray *table_data,
BIOSLinker *linker,
@@ -421,7 +418,10 @@ static void build_rhct(GArray *table_data,
acpi_table_end(linker, &table);
}
-/* FADT */
+/*
+ * ACPI spec, Revision 6.6
+ * 5.2.9 Fixed ACPI Description Table (MADT)
+ */
static void build_fadt_rev6(GArray *table_data,
BIOSLinker *linker,
RISCVVirtState *s,
@@ -429,7 +429,7 @@ static void build_fadt_rev6(GArray *table_data,
{
AcpiFadtData fadt = {
.rev = 6,
- .minor_ver = 5,
+ .minor_ver = 6,
.flags = 1 << ACPI_FADT_F_HW_REDUCED_ACPI,
.xdsdt_tbl_offset = &dsdt_tbl_offset,
};
@@ -508,11 +508,8 @@ static void build_dsdt(GArray *table_data,
}
/*
- * ACPI spec, Revision 6.5+
+ * ACPI spec, Revision 6.6
* 5.2.12 Multiple APIC Description Table (MADT)
- * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/15
- * https://drive.google.com/file/d/1R6k4MshhN3WTT-hwqAquu5nX6xSEqK2l/view
- * https://drive.google.com/file/d/1oMGPyOD58JaPgMl1pKasT-VKsIKia7zR/view
*/
static void build_madt(GArray *table_data,
BIOSLinker *linker,
@@ -537,7 +534,7 @@ static void build_madt(GArray *table_data,
hart_index_bits = imsic_num_bits(imsic_max_hart_per_socket);
- AcpiTable table = { .sig = "APIC", .rev = 6, .oem_id = s->oem_id,
+ AcpiTable table = { .sig = "APIC", .rev = 7, .oem_id = s->oem_id,
.oem_table_id = s->oem_table_id };
acpi_table_begin(&table, table_data);
@@ -812,10 +809,8 @@ static void build_rimt(GArray *table_data, BIOSLinker *linker,
}
/*
- * ACPI spec, Revision 6.5+
+ * ACPI spec, Revision 6.6
* 5.2.16 System Resource Affinity Table (SRAT)
- * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/25
- * https://drive.google.com/file/d/1YTdDx2IPm5IeZjAW932EYU-tUtgS08tX/view
*/
static void
build_srat(GArray *table_data, BIOSLinker *linker, RISCVVirtState *vms)
diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c
index af0f12a..384b56c 100644
--- a/hw/vfio/cpr.c
+++ b/hw/vfio/cpr.c
@@ -116,6 +116,8 @@ static int vfio_cpr_pci_post_load(void *opaque, int version_id)
PCIDevice *pdev = &vdev->pdev;
int nr_vectors;
+ vfio_sub_page_bar_update_mappings(vdev);
+
if (msix_enabled(pdev)) {
vfio_pci_msix_set_notifiers(vdev);
nr_vectors = vdev->msix->entries;
diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c
index e7a9d1f..ee0767b 100644
--- a/hw/vfio/igd.c
+++ b/hw/vfio/igd.c
@@ -113,6 +113,7 @@ static int igd_gen(VFIOPCIDevice *vdev)
#define IGD_BDSM 0x5c /* Base Data of Stolen Memory */
#define IGD_BDSM_GEN11 0xc0 /* Base Data of Stolen Memory of gen 11 and later */
+#define IGD_GMCH_VGA_DISABLE BIT(1)
#define IGD_GMCH_GEN6_GMS_SHIFT 3 /* SNB_GMCH in i915 */
#define IGD_GMCH_GEN6_GMS_MASK 0x1f
#define IGD_GMCH_GEN8_GMS_SHIFT 8 /* BDW_GMCH in i915 */
@@ -533,12 +534,14 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp)
/*
* For backward compatibility, enable legacy mode when
* - Device geneation is 6 to 9 (including both)
+ * - IGD claims VGA cycles on host
* - Machine type is i440fx (pc_piix)
* - IGD device is at guest BDF 00:02.0
* - Not manually disabled by x-igd-legacy-mode=off
*/
if ((vdev->igd_legacy_mode != ON_OFF_AUTO_OFF) &&
(gen >= 6 && gen <= 9) &&
+ !(gmch & IGD_GMCH_VGA_DISABLE) &&
!strcmp(MACHINE_GET_CLASS(qdev_get_machine())->family, "pc_piix") &&
(&vdev->pdev == pci_find_device(pci_device_root_bus(&vdev->pdev),
0, PCI_DEVFN(0x2, 0)))) {
@@ -568,14 +571,16 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp)
}
/*
- * If IGD VGA Disable is clear (expected) and VGA is not already
- * enabled, try to enable it. Probably shouldn't be using legacy mode
- * without VGA, but also no point in us enabling VGA if disabled in
- * hardware.
+ * If VGA is not already enabled, try to enable it. We shouldn't be
+ * using legacy mode without VGA.
*/
- if (!(gmch & 0x2) && !vdev->vga && !vfio_populate_vga(vdev, &err)) {
- error_setg(&err, "Unable to enable VGA access");
- goto error;
+ if (!vdev->vga) {
+ if (vfio_populate_vga(vdev, &err)) {
+ vfio_pci_config_register_vga(vdev);
+ } else {
+ error_setg(&err, "Unable to enable VGA access");
+ goto error;
+ }
}
/* Enable OpRegion and LPC bridge quirk */
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index e72d514..4fa692c 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -49,8 +49,6 @@
#include "vfio-migration-internal.h"
#include "vfio-helpers.h"
-#define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug"
-
/* Protected by BQL */
static KVMRouteChange vfio_route_change;
@@ -2826,6 +2824,20 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f)
return ret;
}
+void vfio_sub_page_bar_update_mappings(VFIOPCIDevice *vdev)
+{
+ PCIDevice *pdev = &vdev->pdev;
+ int page_size = qemu_real_host_page_size();
+ int bar;
+
+ for (bar = 0; bar < PCI_ROM_SLOT; bar++) {
+ PCIIORegion *r = &pdev->io_regions[bar];
+ if (r->addr != PCI_BAR_UNMAPPED && r->size > 0 && r->size < page_size) {
+ vfio_sub_page_bar_update_mapping(pdev, bar);
+ }
+ }
+}
+
static VFIODeviceOps vfio_pci_ops = {
.vfio_compute_needs_reset = vfio_pci_compute_needs_reset,
.vfio_hot_reset_multi = vfio_pci_hot_reset_multi,
@@ -3150,6 +3162,15 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev)
vdev->req_enabled = false;
}
+void vfio_pci_config_register_vga(VFIOPCIDevice *vdev)
+{
+ assert(vdev->vga != NULL);
+
+ pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem,
+ &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem,
+ &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem);
+}
+
bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp)
{
PCIDevice *pdev = &vdev->pdev;
@@ -3271,9 +3292,7 @@ bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp)
vfio_bars_register(vdev);
if (vdev->vga && vfio_is_vga(vdev)) {
- pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem,
- &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem,
- &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem);
+ vfio_pci_config_register_vga(vdev);
}
return true;
diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h
index 248e5c4..81465a8 100644
--- a/hw/vfio/pci.h
+++ b/hw/vfio/pci.h
@@ -219,6 +219,7 @@ void vfio_pci_write_config(PCIDevice *pdev,
uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size);
void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size);
+void vfio_sub_page_bar_update_mappings(VFIOPCIDevice *vdev);
bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev);
bool vfio_config_quirk_setup(VFIOPCIDevice *vdev, Error **errp);
void vfio_vga_quirk_setup(VFIOPCIDevice *vdev);
@@ -252,6 +253,7 @@ extern const VMStateDescription vfio_display_vmstate;
void vfio_pci_bars_exit(VFIOPCIDevice *vdev);
bool vfio_pci_add_capabilities(VFIOPCIDevice *vdev, Error **errp);
+void vfio_pci_config_register_vga(VFIOPCIDevice *vdev);
bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp);
bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp);
void vfio_pci_intx_eoi(VFIODevice *vbasedev);
diff --git a/hw/vfio/types.h b/hw/vfio/types.h
index fa20c29..c19334f 100644
--- a/hw/vfio/types.h
+++ b/hw/vfio/types.h
@@ -18,4 +18,6 @@
#define TYPE_VFIO_PCI "vfio-pci"
/* TYPE_VFIO_PCI shares struct VFIOPCIDevice. */
+#define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug"
+
#endif /* HW_VFIO_VFIO_TYPES_H */
diff --git a/hw/vfio/vfio-migration-internal.h b/hw/vfio/vfio-migration-internal.h
index 54141e2..814fbd9 100644
--- a/hw/vfio/vfio-migration-internal.h
+++ b/hw/vfio/vfio-migration-internal.h
@@ -13,7 +13,6 @@
#include <linux/vfio.h>
#endif
-#include "qemu/typedefs.h"
#include "qemu/notify.h"
/*
diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c
index 9d16644..006b5b5 100644
--- a/hw/xen/xen_pt.c
+++ b/hw/xen/xen_pt.c
@@ -54,6 +54,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
+#include "qemu/error-report.h"
#include <sys/ioctl.h>
#include "hw/pci/pci.h"
diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c
index d50cab7..668353b 100644
--- a/linux-user/aarch64/signal.c
+++ b/linux-user/aarch64/signal.c
@@ -121,6 +121,30 @@ struct target_za_context {
#define TARGET_ZA_SIG_CONTEXT_SIZE(VQ) \
TARGET_ZA_SIG_ZAV_OFFSET(VQ, VQ * TARGET_SVE_VQ_BYTES)
+#define TARGET_TPIDR2_MAGIC 0x54504902
+
+struct target_tpidr2_context {
+ struct target_aarch64_ctx head;
+ uint64_t tpidr2;
+};
+
+#define TARGET_ZT_MAGIC 0x5a544e01
+
+struct target_zt_context {
+ struct target_aarch64_ctx head;
+ uint16_t nregs;
+ uint16_t reserved[3];
+ /* ZTn register data immediately follows */
+};
+
+#define TARGET_ZT_SIG_REG_BYTES (512 / 8)
+#define TARGET_ZT_SIG_REGS_SIZE(n) (TARGET_ZT_SIG_REG_BYTES * (n))
+#define TARGET_ZT_SIG_CONTEXT_SIZE(n) (sizeof(struct target_zt_context) + \
+ TARGET_ZT_SIG_REGS_SIZE(n))
+#define TARGET_ZT_SIG_REGS_OFFSET sizeof(struct target_zt_context)
+QEMU_BUILD_BUG_ON(TARGET_ZT_SIG_REG_BYTES != \
+ sizeof_field(CPUARMState, za_state.zt0));
+
struct target_rt_sigframe {
struct target_siginfo info;
struct target_ucontext uc;
@@ -253,6 +277,36 @@ static void target_setup_za_record(struct target_za_context *za,
}
}
+static void target_setup_tpidr2_record(struct target_tpidr2_context *tpidr2,
+ CPUARMState *env)
+{
+ __put_user(TARGET_TPIDR2_MAGIC, &tpidr2->head.magic);
+ __put_user(sizeof(struct target_tpidr2_context), &tpidr2->head.size);
+ __put_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2);
+}
+
+static void target_setup_zt_record(struct target_zt_context *zt,
+ CPUARMState *env, int size)
+{
+ uint64_t *z;
+
+ memset(zt, 0, sizeof(*zt));
+ __put_user(TARGET_ZT_MAGIC, &zt->head.magic);
+ __put_user(size, &zt->head.size);
+ /*
+ * The record format allows for multiple ZT regs, but
+ * currently there is only one, ZT0.
+ */
+ __put_user(1, &zt->nregs);
+ assert(size == TARGET_ZT_SIG_CONTEXT_SIZE(1));
+
+ /* ZT0 is the same byte-stream format as SVE regs and ZA */
+ z = (void *)zt + TARGET_ZT_SIG_REGS_OFFSET;
+ for (int i = 0; i < ARRAY_SIZE(env->za_state.zt0); i++) {
+ __put_user_e(env->za_state.zt0[i], z + i, le);
+ }
+}
+
static void target_restore_general_frame(CPUARMState *env,
struct target_rt_sigframe *sf)
{
@@ -403,6 +457,36 @@ static bool target_restore_za_record(CPUARMState *env,
return true;
}
+static void target_restore_tpidr2_record(CPUARMState *env,
+ struct target_tpidr2_context *tpidr2)
+{
+ __get_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2);
+}
+
+static bool target_restore_zt_record(CPUARMState *env,
+ struct target_zt_context *zt, int size,
+ int svcr)
+{
+ uint16_t nregs;
+ uint64_t *z;
+
+ if (!(FIELD_EX64(svcr, SVCR, ZA))) {
+ return false;
+ }
+
+ __get_user(nregs, &zt->nregs);
+
+ if (nregs != 1) {
+ return false;
+ }
+
+ z = (void *)zt + TARGET_ZT_SIG_REGS_OFFSET;
+ for (int i = 0; i < ARRAY_SIZE(env->za_state.zt0); i++) {
+ __get_user_e(env->za_state.zt0[i], z + i, le);
+ }
+ return true;
+}
+
static int target_restore_sigframe(CPUARMState *env,
struct target_rt_sigframe *sf)
{
@@ -410,10 +494,13 @@ static int target_restore_sigframe(CPUARMState *env,
struct target_fpsimd_context *fpsimd = NULL;
struct target_sve_context *sve = NULL;
struct target_za_context *za = NULL;
+ struct target_tpidr2_context *tpidr2 = NULL;
+ struct target_zt_context *zt = NULL;
uint64_t extra_datap = 0;
bool used_extra = false;
int sve_size = 0;
int za_size = 0;
+ int zt_size = 0;
int svcr = 0;
target_restore_general_frame(env, sf);
@@ -460,6 +547,23 @@ static int target_restore_sigframe(CPUARMState *env,
za_size = size;
break;
+ case TARGET_TPIDR2_MAGIC:
+ if (tpidr2 || size != sizeof(struct target_tpidr2_context) ||
+ !cpu_isar_feature(aa64_sme, env_archcpu(env))) {
+ goto err;
+ }
+ tpidr2 = (struct target_tpidr2_context *)ctx;
+ break;
+
+ case TARGET_ZT_MAGIC:
+ if (zt || size != TARGET_ZT_SIG_CONTEXT_SIZE(1) ||
+ !cpu_isar_feature(aa64_sme2, env_archcpu(env))) {
+ goto err;
+ }
+ zt = (struct target_zt_context *)ctx;
+ zt_size = size;
+ break;
+
case TARGET_EXTRA_MAGIC:
if (extra || size != sizeof(struct target_extra_context)) {
goto err;
@@ -497,6 +601,16 @@ static int target_restore_sigframe(CPUARMState *env,
if (za && !target_restore_za_record(env, za, za_size, &svcr)) {
goto err;
}
+ if (tpidr2) {
+ target_restore_tpidr2_record(env, tpidr2);
+ }
+ /*
+ * NB that we must restore ZT after ZA so the check that there's
+ * no ZT record if SVCR.ZA is 0 gets the right value of SVCR.
+ */
+ if (zt && !target_restore_zt_record(env, zt, zt_size, svcr)) {
+ goto err;
+ }
if (env->svcr != svcr) {
env->svcr = svcr;
arm_rebuild_hflags(env);
@@ -568,8 +682,9 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
.total_size = offsetof(struct target_rt_sigframe,
uc.tuc_mcontext.__reserved),
};
- int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0;
- int sve_size = 0, za_size = 0;
+ int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0, tpidr2_ofs = 0;
+ int zt_ofs = 0;
+ int sve_size = 0, za_size = 0, tpidr2_size = 0, zt_size = 0;
struct target_rt_sigframe *frame;
struct target_rt_frame_record *fr;
abi_ulong frame_addr, return_addr;
@@ -585,6 +700,8 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
sve_ofs = alloc_sigframe_space(sve_size, &layout);
}
if (cpu_isar_feature(aa64_sme, env_archcpu(env))) {
+ tpidr2_size = sizeof(struct target_tpidr2_context);
+ tpidr2_ofs = alloc_sigframe_space(tpidr2_size, &layout);
/* ZA state needs saving only if it is enabled. */
if (FIELD_EX64(env->svcr, SVCR, ZA)) {
za_size = TARGET_ZA_SIG_CONTEXT_SIZE(sme_vq(env));
@@ -593,6 +710,12 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
}
za_ofs = alloc_sigframe_space(za_size, &layout);
}
+ if (cpu_isar_feature(aa64_sme2, env_archcpu(env)) &&
+ FIELD_EX64(env->svcr, SVCR, ZA)) {
+ /* If SME ZA storage is enabled, we must also save SME2 ZT0 */
+ zt_size = TARGET_ZT_SIG_CONTEXT_SIZE(1);
+ zt_ofs = alloc_sigframe_space(zt_size, &layout);
+ }
if (layout.extra_ofs) {
/* Reserve space for the extra end marker. The standard end marker
@@ -644,6 +767,12 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
if (za_ofs) {
target_setup_za_record((void *)frame + za_ofs, env, za_size);
}
+ if (tpidr2_ofs) {
+ target_setup_tpidr2_record((void *)frame + tpidr2_ofs, env);
+ }
+ if (zt_ofs) {
+ target_setup_zt_record((void *)frame + zt_ofs, env, zt_size);
+ }
/* Set up the stack frame for unwinding. */
fr = (void *)frame + fr_ofs;
@@ -666,8 +795,12 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
env->btype = 2;
}
- /* Invoke the signal handler with both SM and ZA disabled. */
+ /*
+ * Invoke the signal handler with a clean SME state: both SM and ZA
+ * disabled and TPIDR2_EL0 cleared.
+ */
aarch64_set_svcr(env, 0, R_SVCR_SM_MASK | R_SVCR_ZA_MASK);
+ env->cp15.tpidr2_el0 = 0;
if (info) {
frame->info = *info;
diff --git a/linux-user/strace.list b/linux-user/strace.list
index fdf94ef..ab81835 100644
--- a/linux-user/strace.list
+++ b/linux-user/strace.list
@@ -1716,3 +1716,6 @@
{ TARGET_NR_clock_gettime64, "clock_gettime64" , NULL, print_clock_gettime64,
print_syscall_ret_clock_gettime64 },
#endif
+#ifdef TARGET_NR_riscv_hwprobe
+{ TARGET_NR_riscv_hwprobe, "riscv_hwprobe" , "%s(%p,%d,%d,%d,%d,%d)", NULL, NULL },
+#endif
diff --git a/migration/meson.build b/migration/meson.build
index 9aa48b2..276da3b 100644
--- a/migration/meson.build
+++ b/migration/meson.build
@@ -51,4 +51,4 @@ system_ss.add(when: qatzip, if_true: files('multifd-qatzip.c'))
specific_ss.add(when: 'CONFIG_SYSTEM_ONLY',
if_true: files('ram.c',
- 'target.c'))
+ 'vfio.c'))
diff --git a/migration/target.c b/migration/vfio.c
index 12fd399..0b64e49 100644
--- a/migration/target.c
+++ b/migration/vfio.c
@@ -1,5 +1,5 @@
/*
- * QEMU live migration - functions that need to be compiled target-specific
+ * QEMU live migration - VFIO
*
* This work is licensed under the terms of the GNU GPL, version 2
* or (at your option) any later version.
diff --git a/qapi/accelerator.json b/qapi/accelerator.json
index 28d5ff4..fb28c8d 100644
--- a/qapi/accelerator.json
+++ b/qapi/accelerator.json
@@ -4,7 +4,9 @@
# SPDX-License-Identifier: GPL-2.0-or-later
##
-# = Accelerators
+# ************
+# Accelerators
+# ************
##
{ 'include': 'common.json' }
diff --git a/qapi/dump.json b/qapi/dump.json
index 32c8c1f..726b520 100644
--- a/qapi/dump.json
+++ b/qapi/dump.json
@@ -79,7 +79,7 @@
#
# @detach: if true, QMP will return immediately rather than waiting
# for the dump to finish. The user can track progress using
-# "query-dump". (since 2.6).
+# `query-dump`. (since 2.6).
#
# @begin: if specified, the starting physical address.
#
diff --git a/qapi/machine.json b/qapi/machine.json
index 6f59f70..038eab2 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -2087,7 +2087,7 @@
#
# @deprecated-props: an optional list of properties that are flagged as
# deprecated by the CPU vendor. The list depends on the
-# CpuModelExpansionType: "static" properties are a subset of the
+# `CpuModelExpansionType`: "static" properties are a subset of the
# enabled-properties for the expanded model; "full" properties are
# a set of properties that are deprecated across all models for
# the architecture. (since: 10.1 -- since 9.1 on s390x --).
diff --git a/qapi/migration.json b/qapi/migration.json
index e08a99b..2387c21 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -641,7 +641,7 @@
#
# This mode supports VFIO devices provided the user first puts the
# guest in the suspended runstate, such as by issuing
-# guest-suspend-ram to the QEMU guest agent.
+# `guest-suspend-ram` to the QEMU guest agent.
#
# Best performance is achieved when the memory backend is shared
# and the @x-ignore-shared migration capability is set, but this
@@ -1704,7 +1704,7 @@
#
# .. admonition:: Notes
#
-# 1. The 'query-migrate' command should be used to check
+# 1. The `query-migrate` command should be used to check
# migration's progress and final result (this information is
# provided by the 'status' member).
#
diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json
index c8c91a2..d1ce8ca 100644
--- a/qapi/misc-i386.json
+++ b/qapi/misc-i386.json
@@ -8,7 +8,7 @@
#
# Reset the RTC interrupt reinjection backlog. Can be used if another
# mechanism to synchronize guest time is in effect, for example QEMU
-# guest agent's guest-set-time command.
+# guest agent's `guest-set-time` command.
#
# Use of this command is only applicable for x86 machines with an RTC,
# and on other machines will silently return without performing any
diff --git a/qapi/run-state.json b/qapi/run-state.json
index 54ba5c9..4757947 100644
--- a/qapi/run-state.json
+++ b/qapi/run-state.json
@@ -20,7 +20,7 @@
# @inmigrate: guest is paused waiting for an incoming migration. Note
# that this state does not tell whether the machine will start at
# the end of the migration. This depends on the command-line -S
-# option and any invocation of 'stop' or 'cont' that has happened
+# option and any invocation of `stop` or `cont` that has happened
# since QEMU was started.
#
# @internal-error: An internal error that prevents further guest
diff --git a/qapi/sockets.json b/qapi/sockets.json
index 82046b0..32fac51 100644
--- a/qapi/sockets.json
+++ b/qapi/sockets.json
@@ -143,7 +143,7 @@
#
# @str: decimal is for file descriptor number, otherwise it's a file
# descriptor name. Named file descriptors are permitted in
-# monitor commands, in combination with the 'getfd' command.
+# monitor commands, in combination with the `getfd` command.
# Decimal file descriptors are permitted at startup or other
# contexts where no monitor context is active.
#
diff --git a/roms/Makefile b/roms/Makefile
index beff58d..6af68a9 100644
--- a/roms/Makefile
+++ b/roms/Makefile
@@ -193,12 +193,12 @@ qboot:
cp qboot/build/bios.bin ../pc-bios/qboot.rom
npcm7xx_bootrom:
- $(MAKE) -C vbootrom CROSS_COMPILE=$(arm_cross_prefix)
- cp vbootrom/npcm7xx_bootrom.bin ../pc-bios/npcm7xx_bootrom.bin
+ $(MAKE) -C vbootrom/npcm7xx CROSS_COMPILE=$(arm_cross_prefix)
+ cp vbootrom/npcm7xx/npcm7xx_bootrom.bin ../pc-bios/npcm7xx_bootrom.bin
npcm8xx_bootrom:
- $(MAKE) -C vbootrom CROSS_COMPILE=$(aarch64_cross_prefix)
- cp vbootrom/npcm8xx_bootrom.bin ../pc-bios/npcm8xx_bootrom.bin
+ $(MAKE) -C vbootrom/npcm8xx CROSS_COMPILE=$(aarch64_cross_prefix)
+ cp vbootrom/npcm8xx/npcm8xx_bootrom.bin ../pc-bios/npcm8xx_bootrom.bin
hppa-firmware:
$(MAKE) -C seabios-hppa parisc
diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml
index 003ef96..88ef110 100644
--- a/rust/hw/char/pl011/Cargo.toml
+++ b/rust/hw/char/pl011/Cargo.toml
@@ -12,9 +12,6 @@ license.workspace = true
repository.workspace = true
rust-version.workspace = true
-[lib]
-crate-type = ["staticlib"]
-
[dependencies]
bilge = { version = "0.2.0" }
bilge-impl = { version = "0.2.0" }
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index 5b53f26..ceb71dd 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -2,9 +2,14 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later
-use std::{ffi::CStr, mem::size_of};
+use std::{
+ ffi::{c_int, c_void, CStr},
+ mem::size_of,
+ ptr::NonNull,
+};
use qemu_api::{
+ bindings::{qdev_prop_bool, qdev_prop_chr},
chardev::{CharBackend, Chardev, Event},
impl_vmstate_forward,
irq::{IRQState, InterruptSource},
@@ -18,12 +23,11 @@ use qemu_api::{
sysbus::{SysBusDevice, SysBusDeviceImpl},
uninit_field_mut,
vmstate::VMStateDescription,
+ vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
+ zeroable::Zeroable,
};
-use crate::{
- device_class,
- registers::{self, Interrupt, RegisterOffset},
-};
+use crate::registers::{self, Interrupt, RegisterOffset};
// TODO: You must disable the UART before any of the control registers are
// reprogrammed. When the UART is disabled in the middle of transmission or
@@ -173,10 +177,10 @@ impl ObjectImpl for PL011State {
impl DeviceImpl for PL011State {
fn properties() -> &'static [Property] {
- &device_class::PL011_PROPERTIES
+ &PL011_PROPERTIES
}
fn vmsd() -> Option<&'static VMStateDescription> {
- Some(&device_class::VMSTATE_PL011)
+ Some(&VMSTATE_PL011)
}
const REALIZE: Option<fn(&Self) -> qemu_api::Result<()>> = Some(Self::realize);
}
@@ -712,3 +716,88 @@ impl PL011Impl for PL011Luminary {
impl DeviceImpl for PL011Luminary {}
impl ResettablePhasesImpl for PL011Luminary {}
impl SysBusDeviceImpl for PL011Luminary {}
+
+extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool {
+ let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
+ unsafe { state.as_ref().migrate_clock }
+}
+
+/// Migration subsection for [`PL011State`] clock.
+static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription {
+ name: c"pl011/clock".as_ptr(),
+ version_id: 1,
+ minimum_version_id: 1,
+ needed: Some(pl011_clock_needed),
+ fields: vmstate_fields! {
+ vmstate_clock!(PL011State, clock),
+ },
+ ..Zeroable::ZERO
+};
+
+extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
+ let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
+ let result = unsafe { state.as_ref().post_load(version_id as u32) };
+ if result.is_err() {
+ -1
+ } else {
+ 0
+ }
+}
+
+static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription {
+ name: c"pl011/regs".as_ptr(),
+ version_id: 2,
+ minimum_version_id: 2,
+ fields: vmstate_fields! {
+ vmstate_of!(PL011Registers, flags),
+ vmstate_of!(PL011Registers, line_control),
+ vmstate_of!(PL011Registers, receive_status_error_clear),
+ vmstate_of!(PL011Registers, control),
+ vmstate_of!(PL011Registers, dmacr),
+ vmstate_of!(PL011Registers, int_enabled),
+ vmstate_of!(PL011Registers, int_level),
+ vmstate_of!(PL011Registers, read_fifo),
+ vmstate_of!(PL011Registers, ilpr),
+ vmstate_of!(PL011Registers, ibrd),
+ vmstate_of!(PL011Registers, fbrd),
+ vmstate_of!(PL011Registers, ifl),
+ vmstate_of!(PL011Registers, read_pos),
+ vmstate_of!(PL011Registers, read_count),
+ vmstate_of!(PL011Registers, read_trigger),
+ },
+ ..Zeroable::ZERO
+};
+
+pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
+ name: c"pl011".as_ptr(),
+ version_id: 2,
+ minimum_version_id: 2,
+ post_load: Some(pl011_post_load),
+ fields: vmstate_fields! {
+ vmstate_unused!(core::mem::size_of::<u32>()),
+ vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell<PL011Registers>),
+ },
+ subsections: vmstate_subsections! {
+ VMSTATE_PL011_CLOCK
+ },
+ ..Zeroable::ZERO
+};
+
+qemu_api::declare_properties! {
+ PL011_PROPERTIES,
+ qemu_api::define_property!(
+ c"chardev",
+ PL011State,
+ char_backend,
+ unsafe { &qdev_prop_chr },
+ CharBackend
+ ),
+ qemu_api::define_property!(
+ c"migrate-clk",
+ PL011State,
+ migrate_clock,
+ unsafe { &qdev_prop_bool },
+ bool,
+ default = true
+ ),
+}
diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs
deleted file mode 100644
index d328d84..0000000
--- a/rust/hw/char/pl011/src/device_class.rs
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2024, Linaro Limited
-// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-use std::{
- ffi::{c_int, c_void},
- ptr::NonNull,
-};
-
-use qemu_api::{
- bindings::{qdev_prop_bool, qdev_prop_chr},
- prelude::*,
- vmstate::VMStateDescription,
- vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
- zeroable::Zeroable,
-};
-
-use crate::device::{PL011Registers, PL011State};
-
-extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool {
- let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
- unsafe { state.as_ref().migrate_clock }
-}
-
-/// Migration subsection for [`PL011State`] clock.
-static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription {
- name: c"pl011/clock".as_ptr(),
- version_id: 1,
- minimum_version_id: 1,
- needed: Some(pl011_clock_needed),
- fields: vmstate_fields! {
- vmstate_clock!(PL011State, clock),
- },
- ..Zeroable::ZERO
-};
-
-extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int {
- let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
- let result = unsafe { state.as_ref().post_load(version_id as u32) };
- if result.is_err() {
- -1
- } else {
- 0
- }
-}
-
-static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription {
- name: c"pl011/regs".as_ptr(),
- version_id: 2,
- minimum_version_id: 2,
- fields: vmstate_fields! {
- vmstate_of!(PL011Registers, flags),
- vmstate_of!(PL011Registers, line_control),
- vmstate_of!(PL011Registers, receive_status_error_clear),
- vmstate_of!(PL011Registers, control),
- vmstate_of!(PL011Registers, dmacr),
- vmstate_of!(PL011Registers, int_enabled),
- vmstate_of!(PL011Registers, int_level),
- vmstate_of!(PL011Registers, read_fifo),
- vmstate_of!(PL011Registers, ilpr),
- vmstate_of!(PL011Registers, ibrd),
- vmstate_of!(PL011Registers, fbrd),
- vmstate_of!(PL011Registers, ifl),
- vmstate_of!(PL011Registers, read_pos),
- vmstate_of!(PL011Registers, read_count),
- vmstate_of!(PL011Registers, read_trigger),
- },
- ..Zeroable::ZERO
-};
-
-pub static VMSTATE_PL011: VMStateDescription = VMStateDescription {
- name: c"pl011".as_ptr(),
- version_id: 2,
- minimum_version_id: 2,
- post_load: Some(pl011_post_load),
- fields: vmstate_fields! {
- vmstate_unused!(core::mem::size_of::<u32>()),
- vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell<PL011Registers>),
- },
- subsections: vmstate_subsections! {
- VMSTATE_PL011_CLOCK
- },
- ..Zeroable::ZERO
-};
-
-qemu_api::declare_properties! {
- PL011_PROPERTIES,
- qemu_api::define_property!(
- c"chardev",
- PL011State,
- char_backend,
- unsafe { &qdev_prop_chr },
- CharBackend
- ),
- qemu_api::define_property!(
- c"migrate-clk",
- PL011State,
- migrate_clock,
- unsafe { &qdev_prop_bool },
- bool,
- default = true
- ),
-}
diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs
index 5c4fbc9..2b70d2f 100644
--- a/rust/hw/char/pl011/src/lib.rs
+++ b/rust/hw/char/pl011/src/lib.rs
@@ -13,7 +13,6 @@
//! the [`registers`] module for register types.
mod device;
-mod device_class;
mod registers;
pub use device::pl011_create;
diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml
index 6f07502..ac5df23 100644
--- a/rust/hw/timer/hpet/Cargo.toml
+++ b/rust/hw/timer/hpet/Cargo.toml
@@ -10,9 +10,6 @@ license.workspace = true
repository.workspace = true
rust-version.workspace = true
-[lib]
-crate-type = ["staticlib"]
-
[dependencies]
qemu_api = { path = "../../../qemu-api" }
qemu_api_macros = { path = "../../../qemu-api-macros" }
diff --git a/scripts/get-wraps-from-cargo-registry.py b/scripts/get-wraps-from-cargo-registry.py
new file mode 100755
index 0000000..31eed5c
--- /dev/null
+++ b/scripts/get-wraps-from-cargo-registry.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python3
+
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+"""
+get-wraps-from-cargo-registry.py - Update Meson subprojects from a global registry
+"""
+
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Author: Paolo Bonzini <pbonzini@redhat.com>
+
+import argparse
+import configparser
+import filecmp
+import glob
+import os
+import subprocess
+import sys
+
+
+def get_name_and_semver(namever: str) -> tuple[str, str]:
+ """Split a subproject name into its name and semantic version parts"""
+ parts = namever.rsplit("-", 1)
+ if len(parts) != 2:
+ return namever, ""
+
+ return parts[0], parts[1]
+
+
+class UpdateSubprojects:
+ cargo_registry: str
+ top_srcdir: str
+ dry_run: bool
+ changes: int = 0
+
+ def find_installed_crate(self, namever: str) -> str | None:
+ """Find installed crate matching name and semver prefix"""
+ name, semver = get_name_and_semver(namever)
+
+ # exact version match
+ path = os.path.join(self.cargo_registry, f"{name}-{semver}")
+ if os.path.exists(path):
+ return f"{name}-{semver}"
+
+ # semver match
+ matches = sorted(glob.glob(f"{path}.*"))
+ return os.path.basename(matches[0]) if matches else None
+
+ def compare_build_rs(self, orig_dir: str, registry_namever: str) -> None:
+ """Warn if the build.rs in the original directory differs from the registry version."""
+ orig_build_rs = os.path.join(orig_dir, "build.rs")
+ new_build_rs = os.path.join(self.cargo_registry, registry_namever, "build.rs")
+
+ msg = None
+ if os.path.isfile(orig_build_rs) != os.path.isfile(new_build_rs):
+ if os.path.isfile(orig_build_rs):
+ msg = f"build.rs removed in {registry_namever}"
+ if os.path.isfile(new_build_rs):
+ msg = f"build.rs added in {registry_namever}"
+
+ elif os.path.isfile(orig_build_rs) and not filecmp.cmp(orig_build_rs, new_build_rs):
+ msg = f"build.rs changed from {orig_dir} to {registry_namever}"
+
+ if msg:
+ print(f"⚠️ Warning: {msg}")
+ print(" This may affect the build process - please review the differences.")
+
+ def update_subproject(self, wrap_file: str, registry_namever: str) -> None:
+ """Modify [wrap-file] section to point to self.cargo_registry."""
+ assert wrap_file.endswith("-rs.wrap")
+ wrap_name = wrap_file[:-5]
+
+ env = os.environ.copy()
+ env["MESON_PACKAGE_CACHE_DIR"] = self.cargo_registry
+
+ config = configparser.ConfigParser()
+ config.read(wrap_file)
+ if "wrap-file" not in config:
+ return
+
+ # do not download the wrap, always use the local copy
+ orig_dir = config["wrap-file"]["directory"]
+ if os.path.exists(orig_dir) and orig_dir != registry_namever:
+ self.compare_build_rs(orig_dir, registry_namever)
+
+ if self.dry_run:
+ if orig_dir == registry_namever:
+ print(f"Will install {orig_dir} from registry.")
+ else:
+ print(f"Will replace {orig_dir} with {registry_namever}.")
+ self.changes += 1
+ return
+
+ config["wrap-file"]["directory"] = registry_namever
+ for key in list(config["wrap-file"].keys()):
+ if key.startswith("source"):
+ del config["wrap-file"][key]
+
+ # replace existing directory with installed version
+ if os.path.exists(orig_dir):
+ subprocess.run(
+ ["meson", "subprojects", "purge", "--confirm", wrap_name],
+ cwd=self.top_srcdir,
+ env=env,
+ check=True,
+ )
+
+ with open(wrap_file, "w") as f:
+ config.write(f)
+
+ if orig_dir == registry_namever:
+ print(f"Installing {orig_dir} from registry.")
+ else:
+ print(f"Replacing {orig_dir} with {registry_namever}.")
+ patch_dir = config["wrap-file"]["patch_directory"]
+ patch_dir = os.path.join("packagefiles", patch_dir)
+ _, ver = registry_namever.rsplit("-", 1)
+ subprocess.run(
+ ["meson", "rewrite", "kwargs", "set", "project", "/", "version", ver],
+ cwd=patch_dir,
+ env=env,
+ check=True,
+ )
+
+ subprocess.run(
+ ["meson", "subprojects", "download", wrap_name],
+ cwd=self.top_srcdir,
+ env=env,
+ check=True,
+ )
+ self.changes += 1
+
+ @staticmethod
+ def parse_cmdline() -> argparse.Namespace:
+ parser = argparse.ArgumentParser(
+ description="Replace Meson subprojects with packages in a Cargo registry"
+ )
+ parser.add_argument(
+ "--cargo-registry",
+ default=os.environ.get("CARGO_REGISTRY"),
+ help="Path to Cargo registry (default: CARGO_REGISTRY env var)",
+ )
+ parser.add_argument(
+ "--dry-run",
+ action="store_true",
+ default=False,
+ help="Do not actually replace anything",
+ )
+
+ args = parser.parse_args()
+ if not args.cargo_registry:
+ print("error: CARGO_REGISTRY environment variable not set and --cargo-registry not provided")
+ sys.exit(1)
+
+ return args
+
+ def __init__(self, args: argparse.Namespace):
+ self.cargo_registry = args.cargo_registry
+ self.dry_run = args.dry_run
+ self.top_srcdir = os.getcwd()
+
+ def main(self) -> None:
+ if not os.path.exists("subprojects"):
+ print("'subprojects' directory not found, nothing to do.")
+ return
+
+ os.chdir("subprojects")
+ for wrap_file in sorted(glob.glob("*-rs.wrap")):
+ namever = wrap_file[:-8] # Remove '-rs.wrap'
+
+ registry_namever = self.find_installed_crate(namever)
+ if not registry_namever:
+ print(f"No installed crate found for {wrap_file}")
+ continue
+
+ self.update_subproject(wrap_file, registry_namever)
+
+ if self.changes:
+ if self.dry_run:
+ print("Rerun without --dry-run to apply changes.")
+ else:
+ print(f"✨ {self.changes} subproject(s) updated!")
+ else:
+ print("No changes.")
+
+
+if __name__ == "__main__":
+ args = UpdateSubprojects.parse_cmdline()
+ UpdateSubprojects(args).main()
diff --git a/system/physmem.c b/system/physmem.c
index 130c148..e5dd760 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -165,13 +165,11 @@ static bool ram_is_cpr_compatible(RAMBlock *rb);
* CPUAddressSpace: all the information a CPU needs about an AddressSpace
* @cpu: the CPU whose AddressSpace this is
* @as: the AddressSpace itself
- * @memory_dispatch: its dispatch pointer (cached, RCU protected)
* @tcg_as_listener: listener for tracking changes to the AddressSpace
*/
typedef struct CPUAddressSpace {
CPUState *cpu;
AddressSpace *as;
- struct AddressSpaceDispatch *memory_dispatch;
MemoryListener tcg_as_listener;
} CPUAddressSpace;
@@ -692,7 +690,7 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr orig_addr,
IOMMUTLBEntry iotlb;
int iommu_idx;
hwaddr addr = orig_addr;
- AddressSpaceDispatch *d = cpu->cpu_ases[asidx].memory_dispatch;
+ AddressSpaceDispatch *d = address_space_to_dispatch(cpu->cpu_ases[asidx].as);
for (;;) {
section = address_space_translate_internal(d, addr, &addr, plen, false);
@@ -753,7 +751,7 @@ MemoryRegionSection *iotlb_to_section(CPUState *cpu,
{
int asidx = cpu_asidx_from_attrs(cpu, attrs);
CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx];
- AddressSpaceDispatch *d = cpuas->memory_dispatch;
+ AddressSpaceDispatch *d = address_space_to_dispatch(cpuas->as);
int section_index = index & ~TARGET_PAGE_MASK;
MemoryRegionSection *ret;
@@ -2780,9 +2778,6 @@ static void tcg_log_global_after_sync(MemoryListener *listener)
static void tcg_commit_cpu(CPUState *cpu, run_on_cpu_data data)
{
- CPUAddressSpace *cpuas = data.host_ptr;
-
- cpuas->memory_dispatch = address_space_to_dispatch(cpuas->as);
tlb_flush(cpu);
}
@@ -2798,11 +2793,7 @@ static void tcg_commit(MemoryListener *listener)
cpu = cpuas->cpu;
/*
- * Defer changes to as->memory_dispatch until the cpu is quiescent.
- * Otherwise we race between (1) other cpu threads and (2) ongoing
- * i/o for the current cpu thread, with data cached by mmu_lookup().
- *
- * In addition, queueing the work function will kick the cpu back to
+ * Queueing the work function will kick the cpu back to
* the main loop, which will end the RCU critical section and reclaim
* the memory data structures.
*
diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc
index 853b1c8..5103865 100644
--- a/target/i386/tcg/decode-new.c.inc
+++ b/target/i386/tcg/decode-new.c.inc
@@ -878,10 +878,10 @@ static const X86OpEntry opcodes_0F3A[256] = {
[0x0e] = X86_OP_ENTRY4(VPBLENDW, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66),
[0x0f] = X86_OP_ENTRY4(PALIGNR, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66),
- [0x18] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX) p_66),
+ [0x18] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,dq, vex6 chk(W0) cpuid(AVX) p_66),
[0x19] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 chk(W0) cpuid(AVX) p_66),
- [0x38] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX2) p_66),
+ [0x38] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,dq, vex6 chk(W0) cpuid(AVX2) p_66),
[0x39] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 chk(W0) cpuid(AVX2) p_66),
/* Listed incorrectly as type 4 */
diff --git a/target/mips/tcg/system/cp0_helper.c b/target/mips/tcg/system/cp0_helper.c
index 101b1e6..b69e70d 100644
--- a/target/mips/tcg/system/cp0_helper.c
+++ b/target/mips/tcg/system/cp0_helper.c
@@ -1562,12 +1562,14 @@ target_ulong helper_dvpe(CPUMIPSState *env)
CPUState *other_cs = first_cpu;
target_ulong prev = env->mvp->CP0_MVPControl;
- CPU_FOREACH(other_cs) {
- MIPSCPU *other_cpu = MIPS_CPU(other_cs);
- /* Turn off all VPEs except the one executing the dvpe. */
- if (&other_cpu->env != env) {
- other_cpu->env.mvp->CP0_MVPControl &= ~(1 << CP0MVPCo_EVP);
- mips_vpe_sleep(other_cpu);
+ if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) {
+ CPU_FOREACH(other_cs) {
+ MIPSCPU *other_cpu = MIPS_CPU(other_cs);
+ /* Turn off all VPEs except the one executing the dvpe. */
+ if (&other_cpu->env != env) {
+ other_cpu->env.mvp->CP0_MVPControl &= ~(1 << CP0MVPCo_EVP);
+ mips_vpe_sleep(other_cpu);
+ }
}
}
return prev;
@@ -1578,15 +1580,17 @@ target_ulong helper_evpe(CPUMIPSState *env)
CPUState *other_cs = first_cpu;
target_ulong prev = env->mvp->CP0_MVPControl;
- CPU_FOREACH(other_cs) {
- MIPSCPU *other_cpu = MIPS_CPU(other_cs);
+ if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) {
+ CPU_FOREACH(other_cs) {
+ MIPSCPU *other_cpu = MIPS_CPU(other_cs);
- if (&other_cpu->env != env
- /* If the VPE is WFI, don't disturb its sleep. */
- && !mips_vpe_is_wfi(other_cpu)) {
- /* Enable the VPE. */
- other_cpu->env.mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP);
- mips_vpe_wake(other_cpu); /* And wake it up. */
+ if (&other_cpu->env != env
+ /* If the VPE is WFI, don't disturb its sleep. */
+ && !mips_vpe_is_wfi(other_cpu)) {
+ /* Enable the VPE. */
+ other_cpu->env.mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP);
+ mips_vpe_wake(other_cpu); /* And wake it up. */
+ }
}
}
return prev;
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 8631be9..8842e07 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -374,8 +374,11 @@ static RISCVException aia_smode(CPURISCVState *env, int csrno)
static RISCVException aia_smode32(CPURISCVState *env, int csrno)
{
int ret;
+ int csr_priv = get_field(csrno, 0x300);
- if (!riscv_cpu_cfg(env)->ext_ssaia) {
+ if (csr_priv == PRV_M && !riscv_cpu_cfg(env)->ext_smaia) {
+ return RISCV_EXCP_ILLEGAL_INST;
+ } else if (!riscv_cpu_cfg(env)->ext_ssaia) {
return RISCV_EXCP_ILLEGAL_INST;
}
@@ -5577,7 +5580,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env,
csr_priv = get_field(csrno, 0x300);
if (!env->debugger && (effective_priv < csr_priv)) {
- if (csr_priv == (PRV_S + 1) && env->virt_enabled) {
+ if (csr_priv <= (PRV_S + 1) && env->virt_enabled) {
return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
}
return RISCV_EXCP_ILLEGAL_INST;
@@ -5862,8 +5865,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
NULL, read_mstatus_i128 },
[CSR_MISA] = { "misa", any, read_misa, write_misa,
NULL, read_misa_i128 },
- [CSR_MIDELEG] = { "mideleg", any, NULL, NULL, rmw_mideleg },
- [CSR_MEDELEG] = { "medeleg", any, read_medeleg, write_medeleg },
+ [CSR_MIDELEG] = { "mideleg", smode, NULL, NULL, rmw_mideleg },
+ [CSR_MEDELEG] = { "medeleg", smode, read_medeleg, write_medeleg },
[CSR_MIE] = { "mie", any, NULL, NULL, rmw_mie },
[CSR_MTVEC] = { "mtvec", any, read_mtvec, write_mtvec },
[CSR_MCOUNTEREN] = { "mcounteren", umode, read_mcounteren,
@@ -5871,7 +5874,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_MSTATUSH] = { "mstatush", any32, read_mstatush,
write_mstatush },
- [CSR_MEDELEGH] = { "medelegh", any32, read_zero, write_ignore,
+ [CSR_MEDELEGH] = { "medelegh", smode32, read_zero, write_ignore,
.min_priv_ver = PRIV_VERSION_1_13_0 },
[CSR_HEDELEGH] = { "hedelegh", hmode32, read_hedelegh, write_hedelegh,
.min_priv_ver = PRIV_VERSION_1_13_0 },
@@ -5911,7 +5914,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
[CSR_MVIP] = { "mvip", aia_any, NULL, NULL, rmw_mvip },
/* Machine-Level High-Half CSRs (AIA) */
- [CSR_MIDELEGH] = { "midelegh", aia_any32, NULL, NULL, rmw_midelegh },
+ [CSR_MIDELEGH] = { "midelegh", aia_smode32, NULL, NULL, rmw_midelegh },
[CSR_MIEH] = { "mieh", aia_any32, NULL, NULL, rmw_mieh },
[CSR_MVIENH] = { "mvienh", aia_any32, NULL, NULL, rmw_mvienh },
[CSR_MVIPH] = { "mviph", aia_any32, NULL, NULL, rmw_mviph },
diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc
index 610bf9f..71f98fb 100644
--- a/target/riscv/insn_trans/trans_rvv.c.inc
+++ b/target/riscv/insn_trans/trans_rvv.c.inc
@@ -864,286 +864,32 @@ GEN_VEXT_TRANS(vlm_v, MO_8, vlm_v, ld_us_mask_op, ld_us_mask_check)
GEN_VEXT_TRANS(vsm_v, MO_8, vsm_v, st_us_mask_op, st_us_mask_check)
/*
- * MAXSZ returns the maximum vector size can be operated in bytes,
- * which is used in GVEC IR when vl_eq_vlmax flag is set to true
- * to accelerate vector operation.
- */
-static inline uint32_t MAXSZ(DisasContext *s)
-{
- int max_sz = s->cfg_ptr->vlenb << 3;
- return max_sz >> (3 - s->lmul);
-}
-
-static inline uint32_t get_log2(uint32_t a)
-{
- uint32_t i = 0;
- for (; a > 0;) {
- a >>= 1;
- i++;
- }
- return i;
-}
-
-typedef void gen_tl_ldst(TCGv, TCGv_ptr, tcg_target_long);
-
-/*
- * Simulate the strided load/store main loop:
- *
- * for (i = env->vstart; i < env->vl; env->vstart = ++i) {
- * k = 0;
- * while (k < nf) {
- * if (!vm && !vext_elem_mask(v0, i)) {
- * vext_set_elems_1s(vd, vma, (i + k * max_elems) * esz,
- * (i + k * max_elems + 1) * esz);
- * k++;
- * continue;
- * }
- * target_ulong addr = base + stride * i + (k << log2_esz);
- * ldst(env, adjust_addr(env, addr), i + k * max_elems, vd, ra);
- * k++;
- * }
- * }
- */
-static void gen_ldst_stride_main_loop(DisasContext *s, TCGv dest, uint32_t rs1,
- uint32_t rs2, uint32_t vm, uint32_t nf,
- gen_tl_ldst *ld_fn, gen_tl_ldst *st_fn,
- bool is_load)
-{
- TCGv addr = tcg_temp_new();
- TCGv base = get_gpr(s, rs1, EXT_NONE);
- TCGv stride = get_gpr(s, rs2, EXT_NONE);
-
- TCGv i = tcg_temp_new();
- TCGv i_esz = tcg_temp_new();
- TCGv k = tcg_temp_new();
- TCGv k_esz = tcg_temp_new();
- TCGv k_max = tcg_temp_new();
- TCGv mask = tcg_temp_new();
- TCGv mask_offs = tcg_temp_new();
- TCGv mask_offs_64 = tcg_temp_new();
- TCGv mask_elem = tcg_temp_new();
- TCGv mask_offs_rem = tcg_temp_new();
- TCGv vreg = tcg_temp_new();
- TCGv dest_offs = tcg_temp_new();
- TCGv stride_offs = tcg_temp_new();
-
- uint32_t max_elems = MAXSZ(s) >> s->sew;
-
- TCGLabel *start = gen_new_label();
- TCGLabel *end = gen_new_label();
- TCGLabel *start_k = gen_new_label();
- TCGLabel *inc_k = gen_new_label();
- TCGLabel *end_k = gen_new_label();
-
- MemOp atomicity = MO_ATOM_NONE;
- if (s->sew == 0) {
- atomicity = MO_ATOM_NONE;
- } else {
- atomicity = MO_ATOM_IFALIGN_PAIR;
- }
-
- mark_vs_dirty(s);
-
- tcg_gen_addi_tl(mask, (TCGv)tcg_env, vreg_ofs(s, 0));
-
- /* Start of outer loop. */
- tcg_gen_mov_tl(i, cpu_vstart);
- gen_set_label(start);
- tcg_gen_brcond_tl(TCG_COND_GE, i, cpu_vl, end);
- tcg_gen_shli_tl(i_esz, i, s->sew);
- /* Start of inner loop. */
- tcg_gen_movi_tl(k, 0);
- gen_set_label(start_k);
- tcg_gen_brcond_tl(TCG_COND_GE, k, tcg_constant_tl(nf), end_k);
- /*
- * If we are in mask agnostic regime and the operation is not unmasked we
- * set the inactive elements to 1.
- */
- if (!vm && s->vma) {
- TCGLabel *active_element = gen_new_label();
- /* (i + k * max_elems) * esz */
- tcg_gen_shli_tl(mask_offs, k, get_log2(max_elems << s->sew));
- tcg_gen_add_tl(mask_offs, mask_offs, i_esz);
-
- /*
- * Check whether the i bit of the mask is 0 or 1.
- *
- * static inline int vext_elem_mask(void *v0, int index)
- * {
- * int idx = index / 64;
- * int pos = index % 64;
- * return (((uint64_t *)v0)[idx] >> pos) & 1;
- * }
- */
- tcg_gen_shri_tl(mask_offs_64, mask_offs, 3);
- tcg_gen_add_tl(mask_offs_64, mask_offs_64, mask);
- tcg_gen_ld_i64((TCGv_i64)mask_elem, (TCGv_ptr)mask_offs_64, 0);
- tcg_gen_rem_tl(mask_offs_rem, mask_offs, tcg_constant_tl(8));
- tcg_gen_shr_tl(mask_elem, mask_elem, mask_offs_rem);
- tcg_gen_andi_tl(mask_elem, mask_elem, 1);
- tcg_gen_brcond_tl(TCG_COND_NE, mask_elem, tcg_constant_tl(0),
- active_element);
- /*
- * Set masked-off elements in the destination vector register to 1s.
- * Store instructions simply skip this bit as memory ops access memory
- * only for active elements.
- */
- if (is_load) {
- tcg_gen_shli_tl(mask_offs, mask_offs, s->sew);
- tcg_gen_add_tl(mask_offs, mask_offs, dest);
- st_fn(tcg_constant_tl(-1), (TCGv_ptr)mask_offs, 0);
- }
- tcg_gen_br(inc_k);
- gen_set_label(active_element);
- }
- /*
- * The element is active, calculate the address with stride:
- * target_ulong addr = base + stride * i + (k << log2_esz);
- */
- tcg_gen_mul_tl(stride_offs, stride, i);
- tcg_gen_shli_tl(k_esz, k, s->sew);
- tcg_gen_add_tl(stride_offs, stride_offs, k_esz);
- tcg_gen_add_tl(addr, base, stride_offs);
- /* Calculate the offset in the dst/src vector register. */
- tcg_gen_shli_tl(k_max, k, get_log2(max_elems));
- tcg_gen_add_tl(dest_offs, i, k_max);
- tcg_gen_shli_tl(dest_offs, dest_offs, s->sew);
- tcg_gen_add_tl(dest_offs, dest_offs, dest);
- if (is_load) {
- tcg_gen_qemu_ld_tl(vreg, addr, s->mem_idx, MO_LE | s->sew | atomicity);
- st_fn((TCGv)vreg, (TCGv_ptr)dest_offs, 0);
- } else {
- ld_fn((TCGv)vreg, (TCGv_ptr)dest_offs, 0);
- tcg_gen_qemu_st_tl(vreg, addr, s->mem_idx, MO_LE | s->sew | atomicity);
- }
- /*
- * We don't execute the load/store above if the element was inactive.
- * We jump instead directly to incrementing k and continuing the loop.
- */
- if (!vm && s->vma) {
- gen_set_label(inc_k);
- }
- tcg_gen_addi_tl(k, k, 1);
- tcg_gen_br(start_k);
- /* End of the inner loop. */
- gen_set_label(end_k);
-
- tcg_gen_addi_tl(i, i, 1);
- tcg_gen_mov_tl(cpu_vstart, i);
- tcg_gen_br(start);
-
- /* End of the outer loop. */
- gen_set_label(end);
-
- return;
-}
-
-
-/*
- * Set the tail bytes of the strided loads/stores to 1:
- *
- * for (k = 0; k < nf; ++k) {
- * cnt = (k * max_elems + vl) * esz;
- * tot = (k * max_elems + max_elems) * esz;
- * for (i = cnt; i < tot; i += esz) {
- * store_1s(-1, vd[vl+i]);
- * }
- * }
+ *** stride load and store
*/
-static void gen_ldst_stride_tail_loop(DisasContext *s, TCGv dest, uint32_t nf,
- gen_tl_ldst *st_fn)
-{
- TCGv i = tcg_temp_new();
- TCGv k = tcg_temp_new();
- TCGv tail_cnt = tcg_temp_new();
- TCGv tail_tot = tcg_temp_new();
- TCGv tail_addr = tcg_temp_new();
-
- TCGLabel *start = gen_new_label();
- TCGLabel *end = gen_new_label();
- TCGLabel *start_i = gen_new_label();
- TCGLabel *end_i = gen_new_label();
-
- uint32_t max_elems_b = MAXSZ(s);
- uint32_t esz = 1 << s->sew;
-
- /* Start of the outer loop. */
- tcg_gen_movi_tl(k, 0);
- tcg_gen_shli_tl(tail_cnt, cpu_vl, s->sew);
- tcg_gen_movi_tl(tail_tot, max_elems_b);
- tcg_gen_add_tl(tail_addr, dest, tail_cnt);
- gen_set_label(start);
- tcg_gen_brcond_tl(TCG_COND_GE, k, tcg_constant_tl(nf), end);
- /* Start of the inner loop. */
- tcg_gen_mov_tl(i, tail_cnt);
- gen_set_label(start_i);
- tcg_gen_brcond_tl(TCG_COND_GE, i, tail_tot, end_i);
- /* store_1s(-1, vd[vl+i]); */
- st_fn(tcg_constant_tl(-1), (TCGv_ptr)tail_addr, 0);
- tcg_gen_addi_tl(tail_addr, tail_addr, esz);
- tcg_gen_addi_tl(i, i, esz);
- tcg_gen_br(start_i);
- /* End of the inner loop. */
- gen_set_label(end_i);
- /* Update the counts */
- tcg_gen_addi_tl(tail_cnt, tail_cnt, max_elems_b);
- tcg_gen_addi_tl(tail_tot, tail_cnt, max_elems_b);
- tcg_gen_addi_tl(k, k, 1);
- tcg_gen_br(start);
- /* End of the outer loop. */
- gen_set_label(end);
-
- return;
-}
+typedef void gen_helper_ldst_stride(TCGv_ptr, TCGv_ptr, TCGv,
+ TCGv, TCGv_env, TCGv_i32);
static bool ldst_stride_trans(uint32_t vd, uint32_t rs1, uint32_t rs2,
- uint32_t data, DisasContext *s, bool is_load)
+ uint32_t data, gen_helper_ldst_stride *fn,
+ DisasContext *s)
{
- if (!s->vstart_eq_zero) {
- return false;
- }
-
- TCGv dest = tcg_temp_new();
-
- uint32_t nf = FIELD_EX32(data, VDATA, NF);
- uint32_t vm = FIELD_EX32(data, VDATA, VM);
-
- /* Destination register and mask register */
- tcg_gen_addi_tl(dest, (TCGv)tcg_env, vreg_ofs(s, vd));
-
- /*
- * Select the appropriate load/tore to retrieve data from the vector
- * register given a specific sew.
- */
- static gen_tl_ldst * const ld_fns[4] = {
- tcg_gen_ld8u_tl, tcg_gen_ld16u_tl,
- tcg_gen_ld32u_tl, tcg_gen_ld_tl
- };
-
- static gen_tl_ldst * const st_fns[4] = {
- tcg_gen_st8_tl, tcg_gen_st16_tl,
- tcg_gen_st32_tl, tcg_gen_st_tl
- };
+ TCGv_ptr dest, mask;
+ TCGv base, stride;
+ TCGv_i32 desc;
- gen_tl_ldst *ld_fn = ld_fns[s->sew];
- gen_tl_ldst *st_fn = st_fns[s->sew];
+ dest = tcg_temp_new_ptr();
+ mask = tcg_temp_new_ptr();
+ base = get_gpr(s, rs1, EXT_NONE);
+ stride = get_gpr(s, rs2, EXT_NONE);
+ desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb,
+ s->cfg_ptr->vlenb, data));
- if (ld_fn == NULL || st_fn == NULL) {
- return false;
- }
+ tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd));
+ tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0));
mark_vs_dirty(s);
- gen_ldst_stride_main_loop(s, dest, rs1, rs2, vm, nf, ld_fn, st_fn, is_load);
-
- tcg_gen_movi_tl(cpu_vstart, 0);
-
- /*
- * Set the tail bytes to 1 if tail agnostic:
- */
- if (s->vta != 0 && is_load) {
- gen_ldst_stride_tail_loop(s, dest, nf, st_fn);
- }
+ fn(dest, mask, base, stride, tcg_env, desc);
finalize_rvv_inst(s);
return true;
@@ -1152,6 +898,16 @@ static bool ldst_stride_trans(uint32_t vd, uint32_t rs1, uint32_t rs2,
static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew)
{
uint32_t data = 0;
+ gen_helper_ldst_stride *fn;
+ static gen_helper_ldst_stride * const fns[4] = {
+ gen_helper_vlse8_v, gen_helper_vlse16_v,
+ gen_helper_vlse32_v, gen_helper_vlse64_v
+ };
+
+ fn = fns[eew];
+ if (fn == NULL) {
+ return false;
+ }
uint8_t emul = vext_get_emul(s, eew);
data = FIELD_DP32(data, VDATA, VM, a->vm);
@@ -1159,7 +915,7 @@ static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew)
data = FIELD_DP32(data, VDATA, NF, a->nf);
data = FIELD_DP32(data, VDATA, VTA, s->vta);
data = FIELD_DP32(data, VDATA, VMA, s->vma);
- return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, s, true);
+ return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s);
}
static bool ld_stride_check(DisasContext *s, arg_rnfvm* a, uint8_t eew)
@@ -1177,13 +933,23 @@ GEN_VEXT_TRANS(vlse64_v, MO_64, rnfvm, ld_stride_op, ld_stride_check)
static bool st_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew)
{
uint32_t data = 0;
+ gen_helper_ldst_stride *fn;
+ static gen_helper_ldst_stride * const fns[4] = {
+ /* masked stride store */
+ gen_helper_vsse8_v, gen_helper_vsse16_v,
+ gen_helper_vsse32_v, gen_helper_vsse64_v
+ };
uint8_t emul = vext_get_emul(s, eew);
data = FIELD_DP32(data, VDATA, VM, a->vm);
data = FIELD_DP32(data, VDATA, LMUL, emul);
data = FIELD_DP32(data, VDATA, NF, a->nf);
+ fn = fns[eew];
+ if (fn == NULL) {
+ return false;
+ }
- return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, s, false);
+ return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s);
}
static bool st_stride_check(DisasContext *s, arg_rnfvm* a, uint8_t eew)
@@ -1534,6 +1300,17 @@ GEN_LDST_WHOLE_TRANS(vs8r_v, int8_t, 8, false)
*** Vector Integer Arithmetic Instructions
*/
+/*
+ * MAXSZ returns the maximum vector size can be operated in bytes,
+ * which is used in GVEC IR when vl_eq_vlmax flag is set to true
+ * to accelerate vector operation.
+ */
+static inline uint32_t MAXSZ(DisasContext *s)
+{
+ int max_sz = s->cfg_ptr->vlenb * 8;
+ return max_sz >> (3 - s->lmul);
+}
+
static bool opivv_check(DisasContext *s, arg_rmrr *a)
{
return require_rvv(s) &&
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 15460bf..110292e 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -355,21 +355,22 @@ target_ulong helper_sret(CPURISCVState *env)
}
static void check_ret_from_m_mode(CPURISCVState *env, target_ulong retpc,
- target_ulong prev_priv)
+ target_ulong prev_priv,
+ uintptr_t ra)
{
if (!(env->priv >= PRV_M)) {
- riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+ riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra);
}
if (!riscv_cpu_allow_16bit_insn(&env_archcpu(env)->cfg,
env->priv_ver,
env->misa_ext) && (retpc & 0x3)) {
- riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+ riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, ra);
}
if (riscv_cpu_cfg(env)->pmp &&
!pmp_get_num_rules(env) && (prev_priv != PRV_M)) {
- riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC());
+ riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, ra);
}
}
static target_ulong ssdbltrp_mxret(CPURISCVState *env, target_ulong mstatus,
@@ -394,8 +395,9 @@ target_ulong helper_mret(CPURISCVState *env)
target_ulong retpc = env->mepc & get_xepc_mask(env);
uint64_t mstatus = env->mstatus;
target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP);
+ uintptr_t ra = GETPC();
- check_ret_from_m_mode(env, retpc, prev_priv);
+ check_ret_from_m_mode(env, retpc, prev_priv, ra);
target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV) &&
(prev_priv != PRV_M);
@@ -443,8 +445,9 @@ target_ulong helper_mnret(CPURISCVState *env)
target_ulong retpc = env->mnepc;
target_ulong prev_priv = get_field(env->mnstatus, MNSTATUS_MNPP);
target_ulong prev_virt;
+ uintptr_t ra = GETPC();
- check_ret_from_m_mode(env, retpc, prev_priv);
+ check_ret_from_m_mode(env, retpc, prev_priv, ra);
prev_virt = get_field(env->mnstatus, MNSTATUS_MNPV) &&
(prev_priv != PRV_M);
diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
index 3540327..72f1372 100644
--- a/target/riscv/pmp.c
+++ b/target/riscv/pmp.c
@@ -211,11 +211,12 @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index)
break;
case PMP_AMATCH_TOR:
- sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */
- ea = (this_addr << 2) - 1u;
- if (sa > ea) {
+ if (prev_addr >= this_addr) {
sa = ea = 0u;
+ break;
}
+ sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */
+ ea = (this_addr << 2) - 1u;
break;
case PMP_AMATCH_NA4:
diff --git a/tests/data/acpi/riscv64/virt/APIC b/tests/data/acpi/riscv64/virt/APIC
index 66a25df..3fb5b75 100644
--- a/tests/data/acpi/riscv64/virt/APIC
+++ b/tests/data/acpi/riscv64/virt/APIC
Binary files differ
diff --git a/tests/data/acpi/riscv64/virt/FACP b/tests/data/acpi/riscv64/virt/FACP
index a5276b6..78e1b14 100644
--- a/tests/data/acpi/riscv64/virt/FACP
+++ b/tests/data/acpi/riscv64/virt/FACP
Binary files differ
diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker
index 8ab244e..ef69bbc 100644
--- a/tests/docker/dockerfiles/debian-all-test-cross.docker
+++ b/tests/docker/dockerfiles/debian-all-test-cross.docker
@@ -15,7 +15,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
apt-get update && \
apt-get install -y eatmydata && \
eatmydata apt-get dist-upgrade -y && \
- apt build-dep -yy qemu
+ apt build-dep -yy --arch-only qemu
# Add extra build tools and as many cross compilers as we can for testing
RUN DEBIAN_FRONTEND=noninteractive eatmydata \
@@ -23,7 +23,9 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
bison \
ccache \
clang \
+ dpkg-dev \
flex \
+ gcc \
git \
libclang-rt-dev \
ninja-build \
@@ -33,16 +35,11 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
python3-venv \
python3-wheel
-RUN DEBIAN_FRONTEND=noninteractive eatmydata \
- apt install -y --no-install-recommends \
- gcc-aarch64-linux-gnu \
+# All the generally available compilers
+ENV AVAILABLE_COMPILERS gcc-aarch64-linux-gnu \
libc6-dev-arm64-cross \
gcc-arm-linux-gnueabihf \
libc6-dev-armhf-cross \
- gcc-hppa-linux-gnu \
- libc6-dev-hppa-cross \
- gcc-m68k-linux-gnu \
- libc6-dev-m68k-cross \
gcc-mips-linux-gnu \
libc6-dev-mips-cross \
gcc-mips64-linux-gnuabi64 \
@@ -51,18 +48,25 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
libc6-dev-mips64el-cross \
gcc-mipsel-linux-gnu \
libc6-dev-mipsel-cross \
- gcc-powerpc-linux-gnu \
- libc6-dev-powerpc-cross \
- gcc-powerpc64-linux-gnu \
- libc6-dev-ppc64-cross \
gcc-powerpc64le-linux-gnu \
libc6-dev-ppc64el-cross \
gcc-riscv64-linux-gnu \
libc6-dev-riscv64-cross \
gcc-s390x-linux-gnu \
- libc6-dev-s390x-cross \
- gcc-sparc64-linux-gnu \
- libc6-dev-sparc64-cross && \
+ libc6-dev-s390x-cross
+
+RUN if dpkg-architecture -e amd64; then \
+ export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-hppa-linux-gnu libc6-dev-hppa-cross"; \
+ export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-m68k-linux-gnu libc6-dev-m68k-cross"; \
+ export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-powerpc-linux-gnu libc6-dev-powerpc-cross"; \
+ export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross"; \
+ export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-sparc64-linux-gnu libc6-dev-sparc64-cross"; \
+ fi && \
+ echo "compilers: ${AVAILABLE_COMPILERS}"
+
+RUN DEBIAN_FRONTEND=noninteractive eatmydata \
+ apt install -y --no-install-recommends \
+ ${AVAILABLE_COMPILERS} && \
dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt
diff --git a/tests/functional/meson.build b/tests/functional/meson.build
index 8bebcd4..ecf965a 100644
--- a/tests/functional/meson.build
+++ b/tests/functional/meson.build
@@ -89,6 +89,7 @@ tests_aarch64_system_thorough = [
'aarch64_device_passthrough',
'aarch64_hotplug_pci',
'aarch64_imx8mp_evk',
+ 'aarch64_kvm',
'aarch64_raspi3',
'aarch64_raspi4',
'aarch64_replay',
diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py
index 2a78e73..5caf7b1 100644
--- a/tests/functional/qemu_test/testcase.py
+++ b/tests/functional/qemu_test/testcase.py
@@ -249,7 +249,7 @@ class QemuBaseTest(unittest.TestCase):
tr = pycotap.TAPTestRunner(message_log = pycotap.LogMode.LogToError,
test_output_log = pycotap.LogMode.LogToError)
res = unittest.main(module = None, testRunner = tr, exit = False,
- argv=["__dummy__", path])
+ argv=[sys.argv[0], path] + sys.argv[1:])
for (test, message) in res.result.errors + res.result.failures:
if hasattr(test, "log_filename"):
diff --git a/tests/functional/test_aarch64_kvm.py b/tests/functional/test_aarch64_kvm.py
new file mode 100755
index 0000000..9fb9286
--- /dev/null
+++ b/tests/functional/test_aarch64_kvm.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+#
+# Functional test that runs subsets of kvm-unit-tests on Aarch64.
+# These can run on TCG and any accelerator supporting nested
+# virtualisation.
+#
+# Copyright (c) 2025 Linaro
+#
+# Author:
+# Alex Bennée <alex.bennee@linaro.org>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+from qemu_test import Asset
+from qemu_test import exec_command_and_wait_for_pattern as ec_and_wait
+from qemu_test.linuxkernel import LinuxKernelTest
+
+
+class Aarch64VirtKVMTests(LinuxKernelTest):
+
+ ASSET_KVM_TEST_KERNEL = Asset(
+ 'https://fileserver.linaro.org/s/HmjaxXXYHYSqbes/'
+ 'download?path=%2F&files='
+ 'image-with-kvm-tool-and-unit-tests.gz',
+ '34de4aaea90db5da42729e7d28b77f392c37a2f4da859f889a5234aaf0970696')
+
+ # make it easier to detect successful return to shell
+ PS1 = 'RES=[$?] # '
+ OK_CMD = 'RES=[0] # '
+
+ # base of tests
+ KUT_BASE = "/usr/share/kvm-unit-tests/"
+
+ def _launch_guest(self, kvm_mode="nvhe"):
+
+ self.set_machine('virt')
+ kernel_path = self.ASSET_KVM_TEST_KERNEL.fetch()
+
+ self.vm.set_console()
+ kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE +
+ f"console=ttyAMA0 kvm-arm.mode={kvm_mode}")
+
+ self.vm.add_args("-cpu", "cortex-a72")
+ self.vm.add_args("-machine", "virt,gic-version=3,virtualization=on",
+ '-kernel', kernel_path,
+ '-append', kernel_command_line)
+ self.vm.add_args("-smp", "2", "-m", "320")
+
+ self.vm.launch()
+
+ self.wait_for_console_pattern('buildroot login:')
+ ec_and_wait(self, 'root', '#')
+ ec_and_wait(self, f"export PS1='{self.PS1}'", self.OK_CMD)
+
+ # this is just a smoketest, we don't run all the tests in the image
+ def _smoketest_kvm(self):
+ ec_and_wait(self, f"{self.KUT_BASE}/selftest-setup", self.OK_CMD)
+ ec_and_wait(self, f"{self.KUT_BASE}/selftest-smp", self.OK_CMD)
+ ec_and_wait(self, f"{self.KUT_BASE}/selftest-vectors-kernel", self.OK_CMD)
+ ec_and_wait(self, f"{self.KUT_BASE}/selftest-vectors-user", self.OK_CMD)
+
+ def test_aarch64_nvhe_selftest(self):
+ self._launch_guest("nvhe")
+ self._smoketest_kvm()
+
+ def test_aarch64_vhe_selftest(self):
+ self._launch_guest("vhe")
+ self._smoketest_kvm()
+
+if __name__ == '__main__':
+ LinuxKernelTest.main()
diff --git a/tests/qapi-schema/doc-non-first-section.err b/tests/qapi-schema/doc-non-first-section.err
deleted file mode 100644
index eeced2b..0000000
--- a/tests/qapi-schema/doc-non-first-section.err
+++ /dev/null
@@ -1 +0,0 @@
-doc-non-first-section.json:5:1: '=' heading must come first in a comment block
diff --git a/tests/qapi-schema/doc-non-first-section.json b/tests/qapi-schema/doc-non-first-section.json
deleted file mode 100644
index 1590876..0000000
--- a/tests/qapi-schema/doc-non-first-section.json
+++ /dev/null
@@ -1,6 +0,0 @@
-# = section must be first line
-
-##
-#
-# = Not first
-##
diff --git a/tests/qapi-schema/doc-non-first-section.out b/tests/qapi-schema/doc-non-first-section.out
deleted file mode 100644
index e69de29..0000000
--- a/tests/qapi-schema/doc-non-first-section.out
+++ /dev/null
diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target
index af68f11..18afd5b 100644
--- a/tests/tcg/Makefile.target
+++ b/tests/tcg/Makefile.target
@@ -127,8 +127,14 @@ else
# build options for bare programs are usually pretty different. They
# are expected to provide their own build recipes.
EXTRA_CFLAGS += -ffreestanding -fno-stack-protector
+
+# We skip the multiarch tests if the target hasn't provided a boot.S
+MULTIARCH_SOFTMMU_TARGETS = i386 alpha aarch64 arm loongarch64 s390x x86_64
+
+ifneq ($(filter $(TARGET_NAME),$(MULTIARCH_SOFTMMU_TARGETS)),)
-include $(SRC_PATH)/tests/tcg/minilib/Makefile.target
-include $(SRC_PATH)/tests/tcg/multiarch/system/Makefile.softmmu-target
+endif
-include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.softmmu-target
endif
@@ -155,21 +161,37 @@ VPATH+=$(PLUGIN_LIB)
# For example, libpatch.so only needs to run against the arch-specific patch
# target test, so we explicitly run it in the arch-specific Makefile.
DISABLE_PLUGINS=libpatch.so
+
+# Likewise don't bother with the syscall plugin for softmmu
+ifneq ($(filter %-softmmu, $(TARGET)),)
+DISABLE_PLUGINS += libsyscall.so
+endif
+
PLUGINS=$(filter-out $(DISABLE_PLUGINS), \
$(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c))))
# We need to ensure expand the run-plugin-TEST-with-PLUGIN
# pre-requistes manually here as we can't use stems to handle it. We
# only expand MULTIARCH_TESTS which are common on most of our targets
-# to avoid an exponential explosion as new tests are added. We also
-# add some special helpers the run-plugin- rules can use below.
-# In more, extra tests can be added using ADDITIONAL_PLUGINS_TESTS variable.
+# and rotate the plugins so we don't grow too out of control as new
+# tests are added. Plugins that need to run with a specific test
+# should ensure they add their combination to EXTRA_RUNS.
ifneq ($(MULTIARCH_TESTS),)
-$(foreach p,$(PLUGINS), \
- $(foreach t,$(MULTIARCH_TESTS) $(ADDITIONAL_PLUGINS_TESTS),\
- $(eval run-plugin-$(t)-with-$(p): $t $p) \
- $(eval RUN_TESTS+=run-plugin-$(t)-with-$(p))))
+
+NUM_PLUGINS := $(words $(PLUGINS))
+NUM_TESTS := $(words $(MULTIARCH_TESTS))
+
+define mod_plus_one
+ $(shell $(PYTHON) -c "print( ($(1) % $(2)) + 1 )")
+endef
+
+$(foreach _idx, $(shell seq 1 $(NUM_TESTS)), \
+ $(eval _test := $(word $(_idx), $(MULTIARCH_TESTS))) \
+ $(eval _plugin := $(word $(call mod_plus_one, $(_idx), $(NUM_PLUGINS)), $(PLUGINS))) \
+ $(eval run-plugin-$(_test)-with-$(_plugin): $(_test) $(_plugin)) \
+ $(eval RUN_TESTS+=run-plugin-$(_test)-with-$(_plugin)))
+
endif # MULTIARCH_TESTS
endif # CONFIG_PLUGIN
diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target
index bfdf719..38345ff 100644
--- a/tests/tcg/multiarch/Makefile.target
+++ b/tests/tcg/multiarch/Makefile.target
@@ -189,6 +189,10 @@ run-plugin-semiconsole-with-%:
TESTS += semihosting semiconsole
endif
+test-plugin-mem-access: CFLAGS+=-pthread -O0
+test-plugin-mem-access: LDFLAGS+=-pthread -O0
+
+ifeq ($(CONFIG_PLUGIN),y)
# Test plugin memory access instrumentation
run-plugin-test-plugin-mem-access-with-libmem.so: \
PLUGIN_ARGS=$(COMMA)print-accesses=true
@@ -197,8 +201,8 @@ run-plugin-test-plugin-mem-access-with-libmem.so: \
$(SRC_PATH)/tests/tcg/multiarch/check-plugin-output.sh \
$(QEMU) $<
-test-plugin-mem-access: CFLAGS+=-pthread -O0
-test-plugin-mem-access: LDFLAGS+=-pthread -O0
+EXTRA_RUNS += run-plugin-test-plugin-mem-access-with-libmem.so
+endif
# Update TESTS
TESTS += $(MULTIARCH_TESTS)
diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target
index 07be001..4171b4e 100644
--- a/tests/tcg/multiarch/system/Makefile.softmmu-target
+++ b/tests/tcg/multiarch/system/Makefile.softmmu-target
@@ -6,6 +6,11 @@
# architecture to add to the test dependencies and deal with the
# complications of building.
#
+# To support the multiarch guests the target arch needs to provide a
+# boot.S that jumps to main and provides a __sys_outc functions.
+# Remember to update MULTIARCH_SOFTMMU_TARGETS in the tcg test
+# Makefile.target when this is done.
+#
MULTIARCH_SRC=$(SRC_PATH)/tests/tcg/multiarch
MULTIARCH_SYSTEM_SRC=$(MULTIARCH_SRC)/system
@@ -66,8 +71,11 @@ endif
MULTIARCH_RUNS += run-gdbstub-memory run-gdbstub-interrupt \
run-gdbstub-untimely-packet run-gdbstub-registers
+ifeq ($(CONFIG_PLUGIN),y)
# Test plugin memory access instrumentation
-run-plugin-memory-with-libmem.so: \
- PLUGIN_ARGS=$(COMMA)region-summary=true
-run-plugin-memory-with-libmem.so: \
- CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py $@.out
+run-plugin-memory-with-libmem.so: memory libmem.so
+run-plugin-memory-with-libmem.so: PLUGIN_ARGS=$(COMMA)region-summary=true
+run-plugin-memory-with-libmem.so: CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py $@.out
+
+EXTRA_RUNS += run-plugin-memory-with-libmem.so
+endif