Merge tag 'pull-target-arm-20250916' of https://gitlab.com/pm215/qemu into staging

target-arm queue:
 * tests, scripts: Don't import print_function from __future__
 * Implement FEAT_ATS1A
 * Remove deprecated pxa CPU family
 * arm/kvm: report registers we failed to set
 * Expose SME registers to GDB via gdbstub
 * linux-user/aarch64: Generate ESR signal records
 * hw/arm/raspi4b: remove redundant check in raspi_add_memory_node
 * hw/arm/virt: Allow user-creatable SMMUv3 dev instantiation
 * system: drop the -old-param option

# -----BEGIN PGP SIGNATURE-----
#
# iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmjJpt8ZHHBldGVyLm1h
# eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3vRGEACO3VrePiMIA9N7egqlUiGn
# aRQVqIKeuPVj6TRVG7BSNWlAX8qvnOWOKg1yGVHDZv/nLvRje9UyfUAw7pf6jXod
# bzxWBCPJ0J0eOB64Tz87WRCLltKB5pEN+uIG00PtpBcXT1ixYCDgBZXyD3mwuJ4Q
# 5Yc5hEwQzpmh+EycLtfCHbmjKDw3x1ncpVlGceOG4h5fvzIvIhcNcZJXfAHhbhyO
# Y4c5PELrCkCLZaTtSSxd6VJ+vXQ9bNWyKaSZu2KRRnLcMeAqw2Ic7dLPlkzCVyxM
# PTOHy4TuDu+kqCbkxdnhpI6fvq5kcHyfTL6qX6tth8ZZS+qKGtvMEIXnYoy6q1kh
# 4jV5vizK8avx31fSiuTKVpttRv4dC+Aq5QrcgYtIVMeOwtkWHv610D8gcFPmXoG+
# uHX9WdzOjrYOzXVKzJaCZF6b7L31ptSEfOrx7asBC9k2wPRwonFXg4JGNq16Yann
# aAO5TM7NAUvM2IPgqS+Tf1Bk0iQqORxGfqzCyL76OO/QMMgfBy9elKH0UR0G+ePJ
# yjpub1oWIELSXsQGMrdFo1W4/NIpFMTu3DP9W+6XRPu1AvrAx/AsrTuvSvXoeFY9
# d/U3yWAXm5XxRzbCIUg7ke8I8zLwRz924M5PA8vophvSnfDLS3V8CJHLwbz/PqYc
# 0P2KCeI6d2NIhVik4mgEoQ==
# =5tK3
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 16 Sep 2025 11:05:19 AM PDT
# gpg:                using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg:                issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [unknown]
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>" [unknown]
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [unknown]
# gpg:                 aka "Peter Maydell <peter@archaic.org.uk>" [unknown]
# gpg: WARNING: The key's User ID is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* tag 'pull-target-arm-20250916' of https://gitlab.com/pm215/qemu: (36 commits)
  hw/usb/network: Remove hardcoded 0x40 prefix in STRING_ETHADDR response
  qtest/bios-tables-test: Update tables for smmuv3 tests
  qtest/bios-tables-test: Add tests for legacy smmuv3 and smmuv3 device
  bios-tables-test: Allow for smmuv3 test data.
  qemu-options.hx: Document the arm-smmuv3 device
  hw/arm/virt: Allow user-creatable SMMUv3 dev instantiation
  hw/pci: Introduce pci_setup_iommu_per_bus() for per-bus IOMMU ops retrieval
  hw/arm/virt: Add an SMMU_IO_LEN macro
  hw/arm/virt: Factor out common SMMUV3 dt bindings code
  hw/arm/virt-acpi-build: Update IORT for multiple smmuv3 devices
  hw/arm/virt-acpi-build: Re-arrange SMMUv3 IORT build
  hw/arm/smmu-common: Check SMMU has PCIe Root Complex association
  target/arm: Added test case for SME register exposure to GDB
  target/arm: Added support for SME register exposure to GDB
  target/arm: Increase MAX_PACKET_LENGTH for SME ZA remote gdb debugging
  arm/kvm: report registers we failed to set
  system: drop the -old-param option
  target/arm: Drop ARM_FEATURE_IWMMXT handling
  target/arm: Drop ARM_FEATURE_XSCALE handling
  target/arm: Remove iwmmxt helper functions
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson
2025-09-17 11:10:55 -07:00
72 changed files with 1151 additions and 2794 deletions
-1
View File
@@ -86,7 +86,6 @@ static uint32_t get_elf_hwcap(void)
/* probe for the extra features */
/* EDSP is in v5TE and above */
GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP);
GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT);
GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE);
GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON);
GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS);
Vendored
+6
View File
@@ -1839,6 +1839,12 @@ for target in $target_list; do
echo "GDB=$gdb_bin" >> $config_target_mak
fi
if test "${gdb_arches#*$arch}" != "$gdb_arches" && version_ge $gdb_version 14.1; then
echo "GDB_HAS_SME_TILES=y" >> $config_target_mak
else
echo "GDB_HAS_SME_TILES=n" >> $config_target_mak
fi
if test "${gdb_arches#*aarch64}" != "$gdb_arches" && version_ge $gdb_version 15.1; then
echo "GDB_HAS_MTE=y" >> $config_target_mak
fi
-34
View File
@@ -68,19 +68,6 @@ configurations (e.g. -smp drawers=1,books=1,clusters=1 for x86 PC machine) is
marked deprecated since 9.0, users have to ensure that all the topology members
described with -smp are supported by the target machine.
``-old-param`` option for booting Arm kernels via param_struct (since 10.0)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
The ``-old-param`` command line option is specific to Arm targets:
it is used when directly booting a guest kernel to pass it the
command line and other information via the old ``param_struct`` ABI,
rather than the newer ATAGS or DTB mechanisms. This option was only
ever needed to support ancient kernels on some old board types
like the ``akita`` or ``terrier``; it has been deprecated in the
kernel since 2001. None of the board types QEMU supports need
``param_struct`` support, so this option has been deprecated and will
be removed in a future QEMU version.
QEMU Machine Protocol (QMP) commands
------------------------------------
@@ -236,27 +223,6 @@ Keeping 32-bit host support alive is a substantial burden for the
QEMU project. Thus QEMU will in future drop the support for all
32-bit host systems.
linux-user mode CPUs
--------------------
iwMMXt emulation and the ``pxa`` CPUs (since 10.0)
''''''''''''''''''''''''''''''''''''''''''''''''''
The ``pxa`` CPU family (``pxa250``, ``pxa255``, ``pxa260``,
``pxa261``, ``pxa262``, ``pxa270-a0``, ``pxa270-a1``, ``pxa270``,
``pxa270-b0``, ``pxa270-b1``, ``pxa270-c0``, ``pxa270-c5``) are no
longer used in system emulation, because all the machine types which
used these CPUs were removed in the QEMU 9.2 release. These CPUs can
now only be used in linux-user mode, and to do that you would have to
explicitly select one of these CPUs with the ``-cpu`` command line
option or the ``QEMU_CPU`` environment variable.
We don't believe that anybody is using the iwMMXt emulation, and we do
not have any tests to validate it or any real hardware or similar
known-good implementation to test against. GCC is in the process of
dropping their support for iwMMXt codegen. These CPU types are
therefore deprecated in QEMU, and will be removed in a future release.
System emulator CPUs
--------------------
+26
View File
@@ -560,6 +560,18 @@ the options along with the machine models they were intended for.
Use ``-run-with user=..`` instead.
``-old-param`` option for booting Arm kernels via param_struct (removed in 10.2)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
The ``-old-param`` command line option was specific to Arm targets:
it was used when directly booting a guest kernel to pass it the
command line and other information via the old ``param_struct`` ABI,
rather than the newer ATAGS or DTB mechanisms. This option was only
ever needed to support ancient kernels on some old board types
like the ``akita`` or ``terrier``; it has been deprecated in the
kernel since 2001. None of the board types QEMU supports need
``param_struct`` support, so this option has been removed.
User-mode emulator command line arguments
-----------------------------------------
@@ -1138,6 +1150,20 @@ reason the maintainers strongly suspected no one actually used it.
QEMU Nios II architecture was orphan; Intel has EOL'ed the Nios II
processor IP (see `Intel discontinuance notification`_).
iwMMXt emulation and the ``pxa`` CPUs (removed in 10.2)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
The ``pxa`` CPU family (``pxa250``, ``pxa255``, ``pxa260``,
``pxa261``, ``pxa262``, ``pxa270-a0``, ``pxa270-a1``, ``pxa270``,
``pxa270-b0``, ``pxa270-b1``, ``pxa270-c0``, ``pxa270-c5``) were
not available in system emulation, because all the machine types which
used these CPUs were removed in the QEMU 9.2 release. We don't
believe that anybody was using the iwMMXt emulation (which you
would have to explicitly enable on the command line), and we did
not have any tests to validate it or any real hardware or similar
known-good implementation to test against. These CPUs have
therefore been removed in linux-user mode as well.
TCG introspection features
--------------------------
+1
View File
@@ -23,6 +23,7 @@ the following architecture extensions:
- FEAT_AFP (Alternate floating-point behavior)
- FEAT_Armv9_Crypto (Armv9 Cryptographic Extension)
- FEAT_ASID16 (16 bit ASID)
- FEAT_ATS1A (Address Translation operations that ignore stage 1 permissions)
- FEAT_BBM at level 2 (Translation table break-before-make levels)
- FEAT_BF16 (AArch64 BFloat16 instructions)
- FEAT_BTI (Branch Target Identification)
+21 -1
View File
@@ -11,7 +11,27 @@
#include "exec/cpu-common.h"
#define MAX_PACKET_LENGTH 4096
/*
* Most "large" transfers (e.g. memory reads, feature XML
* transfer) have mechanisms in the gdb protocol for splitting
* them. However, register values in particular cannot currently
* be split. This packet size must therefore be at least big enough
* for the worst-case register size. Currently that is Arm SME
* ZA storage with a 256x256 byte value. We also must account
* for the conversion from raw data to hex in gdb_memtohex(),
* which writes 2 * size bytes, and for other protocol overhead
* including command, register number and checksum which add
* another 4 bytes of overhead. However, to be consistent with
* the changes made in gdbserver to address this same requirement,
* we add a total of 32 bytes to account for protocol overhead
* (unclear why specifically 32 bytes), bringing the value of
* MAX_PACKET_LENGTH to 2 * 256 * 256 + 32 = 131104.
*
* The commit making this change for gdbserver can be found here:
* https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=
* b816042e88583f280ad186ff124ab84d31fb592b
*/
#define MAX_PACKET_LENGTH 131104
/*
* Shared structures and definitions
+1 -80
View File
@@ -337,81 +337,6 @@ static void set_kernel_args(const struct arm_boot_info *info, AddressSpace *as)
WRITE_WORD(p, 0);
}
static void set_kernel_args_old(const struct arm_boot_info *info,
AddressSpace *as)
{
hwaddr p;
const char *s;
int initrd_size = info->initrd_size;
hwaddr base = info->loader_start;
/* see linux/include/asm-arm/setup.h */
p = base + KERNEL_ARGS_ADDR;
/* page_size */
WRITE_WORD(p, 4096);
/* nr_pages */
WRITE_WORD(p, info->ram_size / 4096);
/* ramdisk_size */
WRITE_WORD(p, 0);
#define FLAG_READONLY 1
#define FLAG_RDLOAD 4
#define FLAG_RDPROMPT 8
/* flags */
WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT);
/* rootdev */
WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */
/* video_num_cols */
WRITE_WORD(p, 0);
/* video_num_rows */
WRITE_WORD(p, 0);
/* video_x */
WRITE_WORD(p, 0);
/* video_y */
WRITE_WORD(p, 0);
/* memc_control_reg */
WRITE_WORD(p, 0);
/* unsigned char sounddefault */
/* unsigned char adfsdrives */
/* unsigned char bytes_per_char_h */
/* unsigned char bytes_per_char_v */
WRITE_WORD(p, 0);
/* pages_in_bank[4] */
WRITE_WORD(p, 0);
WRITE_WORD(p, 0);
WRITE_WORD(p, 0);
WRITE_WORD(p, 0);
/* pages_in_vram */
WRITE_WORD(p, 0);
/* initrd_start */
if (initrd_size) {
WRITE_WORD(p, info->initrd_start);
} else {
WRITE_WORD(p, 0);
}
/* initrd_size */
WRITE_WORD(p, initrd_size);
/* rd_start */
WRITE_WORD(p, 0);
/* system_rev */
WRITE_WORD(p, 0);
/* system_serial_low */
WRITE_WORD(p, 0);
/* system_serial_high */
WRITE_WORD(p, 0);
/* mem_fclk_21285 */
WRITE_WORD(p, 0);
/* zero unused fields */
while (p < base + KERNEL_ARGS_ADDR + 256 + 1024) {
WRITE_WORD(p, 0);
}
s = info->kernel_cmdline;
if (s) {
address_space_write(as, p, MEMTXATTRS_UNSPECIFIED, s, strlen(s) + 1);
} else {
WRITE_WORD(p, 0);
}
}
static int fdt_add_memory_node(void *fdt, uint32_t acells, hwaddr mem_base,
uint32_t scells, hwaddr mem_len,
int numa_node_id)
@@ -802,11 +727,7 @@ static void do_cpu_reset(void *opaque)
cpu_set_pc(cs, info->loader_start);
if (!have_dtb(info)) {
if (old_param) {
set_kernel_args_old(info, as);
} else {
set_kernel_args(info, as);
}
set_kernel_args(info, as);
}
} else if (info->secondary_cpu_reset_hook) {
info->secondary_cpu_reset_hook(cpu, info);
+9 -13
View File
@@ -36,9 +36,8 @@ struct Raspi4bMachineState {
* (see https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf
* 1.2 Address Map)
*/
static int raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len)
static void raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len)
{
int ret;
uint32_t acells, scells;
char *nodename = g_strdup_printf("/memory@%" PRIx64, mem_base);
@@ -46,19 +45,16 @@ static int raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len)
NULL, &error_fatal);
scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells",
NULL, &error_fatal);
if (acells == 0 || scells == 0) {
fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n");
ret = -1;
} else {
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg",
acells, mem_base,
scells, mem_len);
}
/* validated by arm_load_dtb */
g_assert(acells && scells);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
qemu_fdt_setprop_sized_cells(fdt, nodename, "reg",
acells, mem_base,
scells, mem_len);
g_free(nodename);
return ret;
}
static void raspi4_modify_dtb(const struct arm_boot_info *info, void *fdt)
+34 -3
View File
@@ -20,6 +20,7 @@
#include "trace.h"
#include "exec/target_page.h"
#include "hw/core/cpu.h"
#include "hw/pci/pci_bridge.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
#include "qemu/jhash.h"
@@ -925,6 +926,7 @@ static void smmu_base_realize(DeviceState *dev, Error **errp)
{
SMMUState *s = ARM_SMMU(dev);
SMMUBaseClass *sbc = ARM_SMMU_GET_CLASS(dev);
PCIBus *pci_bus = s->primary_bus;
Error *local_err = NULL;
sbc->parent_realize(dev, &local_err);
@@ -937,11 +939,39 @@ static void smmu_base_realize(DeviceState *dev, Error **errp)
g_free, g_free);
s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL);
if (s->primary_bus) {
pci_setup_iommu(s->primary_bus, &smmu_ops, s);
} else {
if (!pci_bus) {
error_setg(errp, "SMMU is not attached to any PCI bus!");
return;
}
/*
* We only allow default PCIe Root Complex(pcie.0) or pxb-pcie based extra
* root complexes to be associated with SMMU.
*/
if (pci_bus_is_express(pci_bus) && pci_bus_is_root(pci_bus) &&
object_dynamic_cast(OBJECT(pci_bus)->parent, TYPE_PCI_HOST_BRIDGE)) {
/*
* This condition matches either the default pcie.0, pxb-pcie, or
* pxb-cxl. For both pxb-pcie and pxb-cxl, parent_dev will be set.
* Currently, we don't allow pxb-cxl as it requires further
* verification. Therefore, make sure this is indeed pxb-pcie.
*/
if (pci_bus->parent_dev) {
if (!object_dynamic_cast(OBJECT(pci_bus), TYPE_PXB_PCIE_BUS)) {
goto out_err;
}
}
if (s->smmu_per_bus) {
pci_setup_iommu_per_bus(pci_bus, &smmu_ops, s);
} else {
pci_setup_iommu(pci_bus, &smmu_ops, s);
}
return;
}
out_err:
error_setg(errp, "SMMU should be attached to a default PCIe root complex"
"(pcie.0) or a pxb-pcie based root complex");
}
/*
@@ -961,6 +991,7 @@ static void smmu_base_reset_exit(Object *obj, ResetType type)
static const Property smmu_dev_properties[] = {
DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0),
DEFINE_PROP_BOOL("smmu_per_bus", SMMUState, smmu_per_bus, false),
DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus,
TYPE_PCI_BUS, PCIBus *),
};
+2
View File
@@ -1996,6 +1996,8 @@ static void smmuv3_class_init(ObjectClass *klass, const void *data)
device_class_set_parent_realize(dc, smmu_realize,
&c->parent_realize);
device_class_set_props(dc, smmuv3_properties);
dc->hotpluggable = false;
dc->user_creatable = true;
}
static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu,
+156 -45
View File
@@ -45,6 +45,7 @@
#include "hw/acpi/generic_event_device.h"
#include "hw/acpi/tpm.h"
#include "hw/acpi/hmat.h"
#include "hw/arm/smmuv3.h"
#include "hw/cxl/cxl.h"
#include "hw/pci/pcie_host.h"
#include "hw/pci/pci.h"
@@ -305,29 +306,126 @@ static int iort_idmap_compare(gconstpointer a, gconstpointer b)
return idmap_a->input_base - idmap_b->input_base;
}
typedef struct AcpiIortSMMUv3Dev {
int irq;
hwaddr base;
GArray *rc_smmu_idmaps;
/* Offset of the SMMUv3 IORT Node relative to the start of the IORT */
size_t offset;
} AcpiIortSMMUv3Dev;
/*
* Populate the struct AcpiIortSMMUv3Dev for the legacy SMMUv3 and
* return the total number of associated idmaps.
*/
static int populate_smmuv3_legacy_dev(GArray *sdev_blob)
{
VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine());
AcpiIortSMMUv3Dev sdev;
sdev.rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping));
object_child_foreach_recursive(object_get_root(), iort_host_bridges,
sdev.rc_smmu_idmaps);
/*
* There can be only one legacy SMMUv3("iommu=smmuv3") as it is a machine
* wide one. Since it may cover multiple PCIe RCs(based on "bypass_iommu"
* property), may have multiple SMMUv3 idmaps. Sort it by input_base.
*/
g_array_sort(sdev.rc_smmu_idmaps, iort_idmap_compare);
sdev.base = vms->memmap[VIRT_SMMU].base;
sdev.irq = vms->irqmap[VIRT_SMMU] + ARM_SPI_BASE;
g_array_append_val(sdev_blob, sdev);
return sdev.rc_smmu_idmaps->len;
}
static int smmuv3_dev_idmap_compare(gconstpointer a, gconstpointer b)
{
AcpiIortSMMUv3Dev *sdev_a = (AcpiIortSMMUv3Dev *)a;
AcpiIortSMMUv3Dev *sdev_b = (AcpiIortSMMUv3Dev *)b;
AcpiIortIdMapping *map_a = &g_array_index(sdev_a->rc_smmu_idmaps,
AcpiIortIdMapping, 0);
AcpiIortIdMapping *map_b = &g_array_index(sdev_b->rc_smmu_idmaps,
AcpiIortIdMapping, 0);
return map_a->input_base - map_b->input_base;
}
static int iort_smmuv3_devices(Object *obj, void *opaque)
{
VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine());
GArray *sdev_blob = opaque;
AcpiIortIdMapping idmap;
PlatformBusDevice *pbus;
AcpiIortSMMUv3Dev sdev;
int min_bus, max_bus;
SysBusDevice *sbdev;
PCIBus *bus;
if (!object_dynamic_cast(obj, TYPE_ARM_SMMUV3)) {
return 0;
}
bus = PCI_BUS(object_property_get_link(obj, "primary-bus", &error_abort));
pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev);
sbdev = SYS_BUS_DEVICE(obj);
sdev.base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
sdev.base += vms->memmap[VIRT_PLATFORM_BUS].base;
sdev.irq = platform_bus_get_irqn(pbus, sbdev, 0);
sdev.irq += vms->irqmap[VIRT_PLATFORM_BUS];
sdev.irq += ARM_SPI_BASE;
pci_bus_range(bus, &min_bus, &max_bus);
sdev.rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping));
idmap.input_base = min_bus << 8,
idmap.id_count = (max_bus - min_bus + 1) << 8,
g_array_append_val(sdev.rc_smmu_idmaps, idmap);
g_array_append_val(sdev_blob, sdev);
return 0;
}
/*
* Populate the struct AcpiIortSMMUv3Dev for all SMMUv3 devices and
* return the total number of idmaps.
*/
static int populate_smmuv3_dev(GArray *sdev_blob)
{
object_child_foreach_recursive(object_get_root(),
iort_smmuv3_devices, sdev_blob);
/* Sort the smmuv3 devices(if any) by smmu idmap input_base */
g_array_sort(sdev_blob, smmuv3_dev_idmap_compare);
/*
* Since each SMMUv3 dev is assocaited with specific host bridge,
* total number of idmaps equals to total number of smmuv3 devices.
*/
return sdev_blob->len;
}
/* Compute ID ranges (RIDs) from RC that are directed to the ITS Group node */
static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmu_idmaps)
static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmuv3_devs)
{
AcpiIortIdMapping *idmap;
AcpiIortIdMapping next_range = {0};
AcpiIortSMMUv3Dev *sdev;
/*
* Based on the RID ranges that are directed to the SMMU, determine the
* bypassed RID ranges, i.e., the ones that are directed to the ITS Group
* node and do not pass through the SMMU, by subtracting the SMMU-bound
* ranges from the full RID range (0x00000xFFFF).
*/
for (int i = 0; i < smmu_idmaps->len; i++) {
idmap = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i);
for (int i = 0; i < smmuv3_devs->len; i++) {
sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i);
/*
* Based on the RID ranges that are directed to the SMMU, determine the
* bypassed RID ranges, i.e., the ones that are directed to the ITS
* Group node and do not pass through the SMMU, by subtracting the
* SMMU-bound ranges from the full RID range (0x00000xFFFF).
*/
for (int j = 0; j < sdev->rc_smmu_idmaps->len; j++) {
idmap = &g_array_index(sdev->rc_smmu_idmaps, AcpiIortIdMapping, j);
if (next_range.input_base < idmap->input_base) {
next_range.id_count = idmap->input_base - next_range.input_base;
g_array_append_val(its_idmaps, next_range);
if (next_range.input_base < idmap->input_base) {
next_range.id_count = idmap->input_base - next_range.input_base;
g_array_append_val(its_idmaps, next_range);
}
next_range.input_base = idmap->input_base + idmap->id_count;
}
next_range.input_base = idmap->input_base + idmap->id_count;
}
/*
* Append the last RC -> ITS ID mapping.
*
@@ -341,7 +439,6 @@ static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmu_idmaps)
}
}
/*
* Input Output Remapping Table (IORT)
* Conforms to "IO Remapping Table System Software on ARM Platforms",
@@ -351,9 +448,12 @@ static void
build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
{
int i, nb_nodes, rc_mapping_count;
size_t node_size, smmu_offset = 0;
AcpiIortSMMUv3Dev *sdev;
size_t node_size;
int num_smmus = 0;
uint32_t id = 0;
GArray *rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping));
int rc_smmu_idmaps_len = 0;
GArray *smmuv3_devs = g_array_new(false, true, sizeof(AcpiIortSMMUv3Dev));
GArray *rc_its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping));
AcpiTable table = { .sig = "IORT", .rev = 3, .oem_id = vms->oem_id,
@@ -361,22 +461,23 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
/* Table 2 The IORT */
acpi_table_begin(&table, table_data);
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
object_child_foreach_recursive(object_get_root(),
iort_host_bridges, rc_smmu_idmaps);
if (vms->legacy_smmuv3_present) {
rc_smmu_idmaps_len = populate_smmuv3_legacy_dev(smmuv3_devs);
} else {
rc_smmu_idmaps_len = populate_smmuv3_dev(smmuv3_devs);
}
/* Sort the smmu idmap by input_base */
g_array_sort(rc_smmu_idmaps, iort_idmap_compare);
nb_nodes = 2; /* RC and SMMUv3 */
rc_mapping_count = rc_smmu_idmaps->len;
num_smmus = smmuv3_devs->len;
if (num_smmus) {
nb_nodes = num_smmus + 1; /* RC and SMMUv3 */
rc_mapping_count = rc_smmu_idmaps_len;
if (vms->its) {
/*
* Knowing the ID ranges from the RC to the SMMU, it's possible to
* determine the ID ranges from RC that go directly to ITS.
*/
create_rc_its_idmaps(rc_its_idmaps, rc_smmu_idmaps);
create_rc_its_idmaps(rc_its_idmaps, smmuv3_devs);
nb_nodes++; /* ITS */
rc_mapping_count += rc_its_idmaps->len;
@@ -411,9 +512,10 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
build_append_int_noprefix(table_data, 0 /* MADT translation_id */, 4);
}
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
int irq = vms->irqmap[VIRT_SMMU] + ARM_SPI_BASE;
for (i = 0; i < num_smmus; i++) {
sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i);
int smmu_mapping_count, offset_to_id_array;
int irq = sdev->irq;
if (vms->its) {
smmu_mapping_count = 1; /* ITS Group node */
@@ -422,7 +524,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
smmu_mapping_count = 0; /* No ID mappings */
offset_to_id_array = 0; /* No ID mappings array */
}
smmu_offset = table_data->len - table.table_offset;
sdev->offset = table_data->len - table.table_offset;
/* Table 9 SMMUv3 Format */
build_append_int_noprefix(table_data, 4 /* SMMUv3 */, 1); /* Type */
node_size = SMMU_V3_ENTRY_SIZE +
@@ -435,7 +537,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
/* Reference to ID Array */
build_append_int_noprefix(table_data, offset_to_id_array, 4);
/* Base address */
build_append_int_noprefix(table_data, vms->memmap[VIRT_SMMU].base, 8);
build_append_int_noprefix(table_data, sdev->base, 8);
/* Flags */
build_append_int_noprefix(table_data, 1 /* COHACC Override */, 4);
build_append_int_noprefix(table_data, 0, 4); /* Reserved */
@@ -486,21 +588,26 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
build_append_int_noprefix(table_data, 0, 3); /* Reserved */
/* Output Reference */
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
if (num_smmus) {
AcpiIortIdMapping *range;
/*
* Map RIDs (input) from RC to SMMUv3 nodes: RC -> SMMUv3.
*
* N.B.: The mapping from SMMUv3 to ITS Group node (SMMUv3 -> ITS) is
* defined in the SMMUv3 table, where all SMMUv3 IDs are mapped to the
* ITS Group node, if ITS is available.
*/
for (i = 0; i < rc_smmu_idmaps->len; i++) {
range = &g_array_index(rc_smmu_idmaps, AcpiIortIdMapping, i);
/* Output IORT node is the SMMUv3 node. */
build_iort_id_mapping(table_data, range->input_base,
range->id_count, smmu_offset);
for (i = 0; i < num_smmus; i++) {
sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i);
/*
* Map RIDs (input) from RC to SMMUv3 nodes: RC -> SMMUv3.
*
* N.B.: The mapping from SMMUv3 to ITS Group node (SMMUv3 -> ITS)
* is defined in the SMMUv3 table, where all SMMUv3 IDs are mapped
* to the ITS Group node, if ITS is available.
*/
for (int j = 0; j < sdev->rc_smmu_idmaps->len; j++) {
range = &g_array_index(sdev->rc_smmu_idmaps,
AcpiIortIdMapping, j);
/* Output IORT node is the SMMUv3 node. */
build_iort_id_mapping(table_data, range->input_base,
range->id_count, sdev->offset);
}
}
if (vms->its) {
@@ -525,8 +632,12 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
}
acpi_table_end(linker, &table);
g_array_free(rc_smmu_idmaps, true);
g_array_free(rc_its_idmaps, true);
for (i = 0; i < num_smmus; i++) {
sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i);
g_array_free(sdev->rc_smmu_idmaps, true);
}
g_array_free(smmuv3_devs, true);
}
/*
+85 -26
View File
@@ -55,6 +55,7 @@
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci-host/gpex.h"
#include "hw/pci-bridge/pci_expander_bridge.h"
#include "hw/virtio/virtio-pci.h"
@@ -149,6 +150,9 @@ static void arm_virt_compat_set(MachineClass *mc)
#define LEGACY_RAMLIMIT_GB 255
#define LEGACY_RAMLIMIT_BYTES (LEGACY_RAMLIMIT_GB * GiB)
/* MMIO region size for SMMUv3 */
#define SMMU_IO_LEN 0x20000
/* Addresses and sizes of our components.
* 0..128MB is space for a flash device so we can run bootrom code such as UEFI.
* 128MB..256MB is used for miscellaneous device I/O.
@@ -180,7 +184,7 @@ static const MemMapEntry base_memmap[] = {
[VIRT_FW_CFG] = { 0x09020000, 0x00000018 },
[VIRT_GPIO] = { 0x09030000, 0x00001000 },
[VIRT_UART1] = { 0x09040000, 0x00001000 },
[VIRT_SMMU] = { 0x09050000, 0x00020000 },
[VIRT_SMMU] = { 0x09050000, SMMU_IO_LEN },
[VIRT_PCDIMM_ACPI] = { 0x09070000, MEMORY_HOTPLUG_IO_LEN },
[VIRT_ACPI_GED] = { 0x09080000, ACPI_GED_EVT_SEL_LEN },
[VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN},
@@ -1442,19 +1446,66 @@ static void create_pcie_irq_map(const MachineState *ms,
0x7 /* PCI irq */);
}
static void create_smmuv3_dt_bindings(const VirtMachineState *vms, hwaddr base,
hwaddr size, int irq)
{
char *node;
const char compat[] = "arm,smmu-v3";
const char irq_names[] = "eventq\0priq\0cmdq-sync\0gerror";
MachineState *ms = MACHINE(vms);
node = g_strdup_printf("/smmuv3@%" PRIx64, base);
qemu_fdt_add_subnode(ms->fdt, node);
qemu_fdt_setprop(ms->fdt, node, "compatible", compat, sizeof(compat));
qemu_fdt_setprop_sized_cells(ms->fdt, node, "reg", 2, base, 2, size);
qemu_fdt_setprop_cells(ms->fdt, node, "interrupts",
GIC_FDT_IRQ_TYPE_SPI, irq , GIC_FDT_IRQ_FLAGS_EDGE_LO_HI,
GIC_FDT_IRQ_TYPE_SPI, irq + 1, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI,
GIC_FDT_IRQ_TYPE_SPI, irq + 2, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI,
GIC_FDT_IRQ_TYPE_SPI, irq + 3, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
qemu_fdt_setprop(ms->fdt, node, "interrupt-names", irq_names,
sizeof(irq_names));
qemu_fdt_setprop(ms->fdt, node, "dma-coherent", NULL, 0);
qemu_fdt_setprop_cell(ms->fdt, node, "#iommu-cells", 1);
qemu_fdt_setprop_cell(ms->fdt, node, "phandle", vms->iommu_phandle);
g_free(node);
}
static void create_smmuv3_dev_dtb(VirtMachineState *vms,
DeviceState *dev, PCIBus *bus)
{
PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev);
SysBusDevice *sbdev = SYS_BUS_DEVICE(dev);
int irq = platform_bus_get_irqn(pbus, sbdev, 0);
hwaddr base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
MachineState *ms = MACHINE(vms);
if (!(vms->bootinfo.firmware_loaded && virt_is_acpi_enabled(vms)) &&
strcmp("pcie.0", bus->qbus.name)) {
warn_report("SMMUv3 device only supported with pcie.0 for DT");
return;
}
base += vms->memmap[VIRT_PLATFORM_BUS].base;
irq += vms->irqmap[VIRT_PLATFORM_BUS];
vms->iommu_phandle = qemu_fdt_alloc_phandle(ms->fdt);
create_smmuv3_dt_bindings(vms, base, SMMU_IO_LEN, irq);
qemu_fdt_setprop_cells(ms->fdt, vms->pciehb_nodename, "iommu-map",
0x0, vms->iommu_phandle, 0x0, 0x10000);
}
static void create_smmu(const VirtMachineState *vms,
PCIBus *bus)
{
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
char *node;
const char compat[] = "arm,smmu-v3";
int irq = vms->irqmap[VIRT_SMMU];
int i;
hwaddr base = vms->memmap[VIRT_SMMU].base;
hwaddr size = vms->memmap[VIRT_SMMU].size;
const char irq_names[] = "eventq\0priq\0cmdq-sync\0gerror";
DeviceState *dev;
MachineState *ms = MACHINE(vms);
if (vms->iommu != VIRT_IOMMU_SMMUV3 || !vms->iommu_phandle) {
return;
@@ -1473,27 +1524,7 @@ static void create_smmu(const VirtMachineState *vms,
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i,
qdev_get_gpio_in(vms->gic, irq + i));
}
node = g_strdup_printf("/smmuv3@%" PRIx64, base);
qemu_fdt_add_subnode(ms->fdt, node);
qemu_fdt_setprop(ms->fdt, node, "compatible", compat, sizeof(compat));
qemu_fdt_setprop_sized_cells(ms->fdt, node, "reg", 2, base, 2, size);
qemu_fdt_setprop_cells(ms->fdt, node, "interrupts",
GIC_FDT_IRQ_TYPE_SPI, irq , GIC_FDT_IRQ_FLAGS_EDGE_LO_HI,
GIC_FDT_IRQ_TYPE_SPI, irq + 1, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI,
GIC_FDT_IRQ_TYPE_SPI, irq + 2, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI,
GIC_FDT_IRQ_TYPE_SPI, irq + 3, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
qemu_fdt_setprop(ms->fdt, node, "interrupt-names", irq_names,
sizeof(irq_names));
qemu_fdt_setprop(ms->fdt, node, "dma-coherent", NULL, 0);
qemu_fdt_setprop_cell(ms->fdt, node, "#iommu-cells", 1);
qemu_fdt_setprop_cell(ms->fdt, node, "phandle", vms->iommu_phandle);
g_free(node);
create_smmuv3_dt_bindings(vms, base, size, irq);
}
static void create_virtio_iommu_dt_bindings(VirtMachineState *vms)
@@ -1649,6 +1680,7 @@ static void create_pcie(VirtMachineState *vms)
qemu_fdt_setprop_cells(ms->fdt, nodename, "iommu-map",
0x0, vms->iommu_phandle, 0x0, 0x10000);
}
vms->legacy_smmuv3_present = true;
break;
default:
g_assert_not_reached();
@@ -2996,6 +3028,16 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
qlist_append_str(reserved_regions, resv_prop_str);
qdev_prop_set_array(dev, "reserved-regions", reserved_regions);
g_free(resv_prop_str);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_ARM_SMMUV3)) {
if (vms->legacy_smmuv3_present || vms->iommu == VIRT_IOMMU_VIRTIO) {
error_setg(errp, "virt machine already has %s set. "
"Doesn't support incompatible iommus",
(vms->legacy_smmuv3_present) ?
"iommu=smmuv3" : "virtio-iommu");
} else if (vms->iommu == VIRT_IOMMU_NONE) {
/* The new SMMUv3 device is specific to the PCI bus */
object_property_set_bool(OBJECT(dev), "smmu_per_bus", true, NULL);
}
}
}
@@ -3019,6 +3061,22 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
virtio_md_pci_plug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp);
}
if (object_dynamic_cast(OBJECT(dev), TYPE_ARM_SMMUV3)) {
if (!vms->legacy_smmuv3_present && vms->platform_bus_dev) {
PCIBus *bus;
bus = PCI_BUS(object_property_get_link(OBJECT(dev), "primary-bus",
&error_abort));
if (pci_bus_bypass_iommu(bus)) {
error_setg(errp, "Bypass option cannot be set for SMMUv3 "
"associated PCIe RC");
return;
}
create_smmuv3_dev_dtb(vms, dev, bus);
}
}
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
PCIDevice *pdev = PCI_DEVICE(dev);
@@ -3218,6 +3276,7 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data)
mc->max_cpus = 512;
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE);
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS);
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ARM_SMMUV3);
#ifdef CONFIG_TPM
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS);
#endif
+3
View File
@@ -31,6 +31,7 @@
#include "qemu/error-report.h"
#include "system/device_tree.h"
#include "system/tpm.h"
#include "hw/arm/smmuv3.h"
#include "hw/platform-bus.h"
#include "hw/display/ramfb.h"
#include "hw/uefi/var-service-api.h"
@@ -135,6 +136,8 @@ static const BindingEntry bindings[] = {
#ifdef CONFIG_TPM
TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node),
#endif
/* No generic DT support for smmuv3 dev. Support added for arm virt only */
TYPE_BINDING(TYPE_ARM_SMMUV3, no_fdt_node),
TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node),
TYPE_BINDING(TYPE_UEFI_VARS_SYSBUS, add_uefi_vars_node),
TYPE_BINDING("", NULL), /* last element */
-1
View File
@@ -34,7 +34,6 @@ typedef struct PXBBus PXBBus;
DECLARE_INSTANCE_CHECKER(PXBBus, PXB_BUS,
TYPE_PXB_BUS)
#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus"
DECLARE_INSTANCE_CHECKER(PXBBus, PXB_PCIE_BUS,
TYPE_PXB_PCIE_BUS)
+31
View File
@@ -2912,6 +2912,19 @@ static void pci_device_get_iommu_bus_devfn(PCIDevice *dev,
}
}
/*
* When multiple PCI Express Root Buses are defined using pxb-pcie,
* the IOMMU configuration may be specific to each root bus. However,
* pxb-pcie acts as a special root complex whose parent is effectively
* the default root complex(pcie.0). Ensure that we retrieve the
* correct IOMMU ops(if any) in such cases.
*/
if (pci_bus_is_express(iommu_bus) && pci_bus_is_root(iommu_bus)) {
if (parent_bus->iommu_per_bus) {
break;
}
}
iommu_bus = parent_bus;
}
@@ -3172,6 +3185,24 @@ void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque)
bus->iommu_opaque = opaque;
}
/*
* Similar to pci_setup_iommu(), but sets iommu_per_bus to true,
* indicating that the IOMMU is specific to this bus. This is used by
* IOMMU implementations that are tied to a specific PCIe root complex.
*
* In QEMU, pxb-pcie behaves as a special root complex whose parent is
* effectively the default root complex (pcie.0). The iommu_per_bus
* is checked in pci_device_get_iommu_bus_devfn() to ensure the correct
* IOMMU ops are returned, avoiding the use of the parents IOMMU when
* it's not appropriate.
*/
void pci_setup_iommu_per_bus(PCIBus *bus, const PCIIOMMUOps *ops,
void *opaque)
{
pci_setup_iommu(bus, ops, opaque);
bus->iommu_per_bus = true;
}
static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque)
{
Range *range = opaque;
+1 -1
View File
@@ -1383,7 +1383,7 @@ static void usb_net_realize(USBDevice *dev, Error **errp)
qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
snprintf(s->usbstring_mac, sizeof(s->usbstring_mac),
"%02x%02x%02x%02x%02x%02x",
0x40,
s->conf.macaddr.a[0],
s->conf.macaddr.a[1],
s->conf.macaddr.a[2],
s->conf.macaddr.a[3],
+1
View File
@@ -161,6 +161,7 @@ struct SMMUState {
QLIST_HEAD(, SMMUDevice) devices_with_notifiers;
uint8_t bus_num;
PCIBus *primary_bus;
bool smmu_per_bus; /* SMMU is specific to the primary_bus */
};
struct SMMUBaseClass {
+1
View File
@@ -179,6 +179,7 @@ struct VirtMachineState {
char *oem_table_id;
bool ns_el2_virt_timer_irq;
CXLState cxl_devices_state;
bool legacy_smmuv3_present;
};
#define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM)
+2
View File
@@ -773,6 +773,8 @@ int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid,
*/
void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque);
void pci_setup_iommu_per_bus(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque);
pcibus_t pci_bar_address(PCIDevice *d,
int reg, uint8_t type, pcibus_t size);
+1
View File
@@ -104,6 +104,7 @@ typedef struct PXBPCIEDev {
PXBDev parent_obj;
} PXBPCIEDev;
#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus"
#define TYPE_PXB_CXL_BUS "pxb-cxl-bus"
#define TYPE_PXB_DEV "pxb"
OBJECT_DECLARE_SIMPLE_TYPE(PXBDev, PXB_DEV)

Some files were not shown because too many files have changed in this diff Show More