[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PULL v3 15/19] hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv
From: |
Michael S. Tsirkin |
Subject: |
[PULL v3 15/19] hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3 |
Date: |
Fri, 16 Jul 2021 11:15:37 -0400 |
From: Xingang Wang <wangxingang5@huawei.com>
When we build IORT table with SMMUv3 and bypass iommu feature enabled,
we can no longer setup one map from RC to SMMUv3 covering the whole RIDs.
We need to walk the PCI bus and check whether the root bus will bypass
iommu, setup RC -> SMMUv3 -> ITS map for RC which will not bypass iommu.
When a SMMUv3 node exist, we setup the idmap from SMMUv3 to ITS
covering the whole RIDs, and only modify the map from RC to SMMUv3.
We build RC -> SMMUv3 -> ITS map for root bus with bypass_iommu
disabled, and build idmap from RC to ITS directly for the rest of
the whole RID space.
For example we run qemu with command line:
qemu/build/aarch64-softmmu/qemu-system-aarch64 \
-kernel arch/arm64/boot/Image \
-enable-kvm \
-cpu host \
-m 8G \
-smp 8,sockets=2,cores=4,threads=1 \
-machine
virt,kernel_irqchip=on,gic-version=3,iommu=smmuv3,default_bus_bypass_iommu=true
\
-drive file=./QEMU_EFI-pflash.raw,if=pflash,format=raw,unit=0,readonly=on \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3.0x1 \
-device
pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x3.0x2,bypass_iommu=true \
-device pcie-root-port,port=0x20,chassis=1,id=pci.1,bus=pcie.0,addr=0x2 \
-device pcie-root-port,port=0x20,chassis=11,id=pci.11,bus=pci.10,addr=0x1 \
-device pcie-root-port,port=0x20,chassis=21,id=pci.21,bus=pci.20,addr=0x1 \
-device virtio-scsi-pci,id=scsi0,bus=pci.1,addr=0x1 \
-device virtio-scsi-pci,id=scsi1,bus=pci.11,addr=0x1 \
-device virtio-scsi-pci,id=scsi2,bus=pci.21,addr=0x1 \
-initrd /mnt/davinci/wxg/kill-linux/rootfs/mfs.cpio.gz \
-nographic \
-append "rdinit=init console=ttyAMA0 earlycon=pl011,0x9000000 nokaslr" \
And we get guest configuration:
-+-[0000:20]---01.0-[21]--
+-[0000:10]---01.0-[11]--
\-[0000:00]-+-00.0 Device 1b36:0008
+-01.0 Device 1af4:1000
\-02.0-[01]--
With bypass_iommu enabled, the attached devices will bypass iommu.
/sys/class/iommu/smmu3.0x0000000009050000/
|-- device -> ../../../arm-smmu-v3.0.auto
|-- devices
| `-- 0000:10:01.0 -> ../../../../../pci0000:10/0000:10:01.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-7-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
---
hw/arm/virt-acpi-build.c | 114 +++++++++++++++++++++++++++++++++++----
1 file changed, 103 insertions(+), 11 deletions(-)
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index f1024843dd..037cc1fd82 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -44,6 +44,7 @@
#include "hw/acpi/tpm.h"
#include "hw/pci/pcie_host.h"
#include "hw/pci/pci.h"
+#include "hw/pci/pci_bus.h"
#include "hw/pci-host/gpex.h"
#include "hw/arm/virt.h"
#include "hw/mem/nvdimm.h"
@@ -239,23 +240,89 @@ static void acpi_dsdt_add_tpm(Aml *scope,
VirtMachineState *vms)
}
#endif
+/* Build the iort ID mapping to SMMUv3 for a given PCI host bridge */
+static int
+iort_host_bridges(Object *obj, void *opaque)
+{
+ GArray *idmap_blob = opaque;
+
+ if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) {
+ PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus;
+
+ if (bus && !pci_bus_bypass_iommu(bus)) {
+ int min_bus, max_bus;
+
+ pci_bus_range(bus, &min_bus, &max_bus);
+
+ AcpiIortIdMapping idmap = {
+ .input_base = min_bus << 8,
+ .id_count = (max_bus - min_bus + 1) << 8,
+ };
+ g_array_append_val(idmap_blob, idmap);
+ }
+ }
+
+ return 0;
+}
+
+static int iort_idmap_compare(gconstpointer a, gconstpointer b)
+{
+ AcpiIortIdMapping *idmap_a = (AcpiIortIdMapping *)a;
+ AcpiIortIdMapping *idmap_b = (AcpiIortIdMapping *)b;
+
+ return idmap_a->input_base - idmap_b->input_base;
+}
+
static void
build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
{
- int nb_nodes, iort_start = table_data->len;
+ int i, nb_nodes, rc_mapping_count, iort_start = table_data->len;
AcpiIortIdMapping *idmap;
AcpiIortItsGroup *its;
AcpiIortTable *iort;
AcpiIortSmmu3 *smmu;
size_t node_size, iort_node_offset, iort_length, smmu_offset = 0;
AcpiIortRC *rc;
+ GArray *smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping));
+ GArray *its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping));
iort = acpi_data_push(table_data, sizeof(*iort));
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
+ AcpiIortIdMapping next_range = {0};
+
+ object_child_foreach_recursive(object_get_root(),
+ iort_host_bridges, smmu_idmaps);
+
+ /* Sort the smmu idmap by input_base */
+ g_array_sort(smmu_idmaps, iort_idmap_compare);
+
+ /*
+ * Split the whole RIDs by mapping from RC to SMMU,
+ * build the ID mapping from RC to ITS directly.
+ */
+ for (i = 0; i < smmu_idmaps->len; i++) {
+ idmap = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i);
+
+ 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;
+ }
+
+ /* Append the last RC -> ITS ID mapping */
+ if (next_range.input_base < 0xFFFF) {
+ next_range.id_count = 0xFFFF - next_range.input_base;
+ g_array_append_val(its_idmaps, next_range);
+ }
+
nb_nodes = 3; /* RC, ITS, SMMUv3 */
+ rc_mapping_count = smmu_idmaps->len + its_idmaps->len;
} else {
nb_nodes = 2; /* RC, ITS */
+ rc_mapping_count = 1;
}
iort_length = sizeof(*iort);
@@ -307,13 +374,13 @@ build_iort(GArray *table_data, BIOSLinker *linker,
VirtMachineState *vms)
}
/* Root Complex Node */
- node_size = sizeof(*rc) + sizeof(*idmap);
+ node_size = sizeof(*rc) + sizeof(*idmap) * rc_mapping_count;
iort_length += node_size;
rc = acpi_data_push(table_data, node_size);
rc->type = ACPI_IORT_NODE_PCI_ROOT_COMPLEX;
rc->length = cpu_to_le16(node_size);
- rc->mapping_count = cpu_to_le32(1);
+ rc->mapping_count = cpu_to_le32(rc_mapping_count);
rc->mapping_offset = cpu_to_le32(sizeof(*rc));
/* fully coherent device */
@@ -321,20 +388,45 @@ build_iort(GArray *table_data, BIOSLinker *linker,
VirtMachineState *vms)
rc->memory_properties.memory_flags = 0x3; /* CCA = CPM = DCAS = 1 */
rc->pci_segment_number = 0; /* MCFG pci_segment */
- /* Identity RID mapping covering the whole input RID range */
- idmap = &rc->id_mapping_array[0];
- idmap->input_base = 0;
- idmap->id_count = cpu_to_le32(0xFFFF);
- idmap->output_base = 0;
-
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
- /* output IORT node is the smmuv3 node */
- idmap->output_reference = cpu_to_le32(smmu_offset);
+ AcpiIortIdMapping *range;
+
+ /* translated RIDs connect to SMMUv3 node: RC -> SMMUv3 -> ITS */
+ for (i = 0; i < smmu_idmaps->len; i++) {
+ idmap = &rc->id_mapping_array[i];
+ range = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i);
+
+ idmap->input_base = cpu_to_le32(range->input_base);
+ idmap->id_count = cpu_to_le32(range->id_count);
+ idmap->output_base = cpu_to_le32(range->input_base);
+ /* output IORT node is the smmuv3 node */
+ idmap->output_reference = cpu_to_le32(smmu_offset);
+ }
+
+ /* bypassed RIDs connect to ITS group node directly: RC -> ITS */
+ for (i = 0; i < its_idmaps->len; i++) {
+ idmap = &rc->id_mapping_array[smmu_idmaps->len + i];
+ range = &g_array_index(its_idmaps, AcpiIortIdMapping, i);
+
+ idmap->input_base = cpu_to_le32(range->input_base);
+ idmap->id_count = cpu_to_le32(range->id_count);
+ idmap->output_base = cpu_to_le32(range->input_base);
+ /* output IORT node is the ITS group node (the first node) */
+ idmap->output_reference = cpu_to_le32(iort_node_offset);
+ }
} else {
+ /* Identity RID mapping covering the whole input RID range */
+ idmap = &rc->id_mapping_array[0];
+ idmap->input_base = cpu_to_le32(0);
+ idmap->id_count = cpu_to_le32(0xFFFF);
+ idmap->output_base = cpu_to_le32(0);
/* output IORT node is the ITS group node (the first node) */
idmap->output_reference = cpu_to_le32(iort_node_offset);
}
+ g_array_free(smmu_idmaps, true);
+ g_array_free(its_idmaps, true);
+
/*
* Update the pointer address in case table_data->data moves during above
* acpi_data_push operations.
--
MST
- [PULL v3 05/19] hw/acpi/ich9: Set ACPI PCI hot-plug as default on Q35, (continued)
- [PULL v3 05/19] hw/acpi/ich9: Set ACPI PCI hot-plug as default on Q35, Michael S. Tsirkin, 2021/07/16
- [PULL v3 07/19] hw/virtio: add boilerplate for vhost-user-i2c device, Michael S. Tsirkin, 2021/07/16
- [PULL v3 09/19] docs: Add '-device intel-iommu' entry, Michael S. Tsirkin, 2021/07/16
- [PULL v3 10/19] hw/pci/pci_host: Allow PCI host to bypass iommu, Michael S. Tsirkin, 2021/07/16
- [PULL v3 06/19] bios-tables-test: Update golden binaries, Michael S. Tsirkin, 2021/07/16
- [PULL v3 08/19] hw/virtio: add vhost-user-i2c-pci boilerplate, Michael S. Tsirkin, 2021/07/16
- [PULL v3 11/19] hw/pxb: Add a bypass iommu property, Michael S. Tsirkin, 2021/07/16
- [PULL v3 12/19] hw/arm/virt: Add default_bus_bypass_iommu machine option, Michael S. Tsirkin, 2021/07/16
- [PULL v3 14/19] hw/pci: Add pci_bus_range() to get PCI bus number range, Michael S. Tsirkin, 2021/07/16
- [PULL v3 13/19] hw/i386: Add a default_bus_bypass_iommu pc machine option, Michael S. Tsirkin, 2021/07/16
- [PULL v3 15/19] hw/arm/virt-acpi-build: Add IORT support to bypass SMMUv3,
Michael S. Tsirkin <=
- [PULL v3 17/19] hw/i386/acpi-build: Add IVRS support to bypass iommu, Michael S. Tsirkin, 2021/07/16
- [PULL v3 18/19] docs: Add documentation for iommu bypass, Michael S. Tsirkin, 2021/07/16
- [PULL v3 19/19] vhost-vsock: SOCK_SEQPACKET feature bit support, Michael S. Tsirkin, 2021/07/16
- [PULL v3 16/19] hw/i386/acpi-build: Add DMAR support to bypass iommu, Michael S. Tsirkin, 2021/07/16
- Re: [PULL v3 00/19] pc,pci,virtio: lots of new features, Peter Maydell, 2021/07/16