diff options
author | Xingang Wang <wangxingang5@huawei.com> | 2021-07-08 12:55:17 +0000 |
---|---|---|
committer | Michael S. Tsirkin <mst@redhat.com> | 2021-07-16 11:10:45 -0400 |
commit | 26863366b29ab638711bbe0fa741dcf355eddb54 (patch) | |
tree | bc7f6236bcac330d391fe6967c130c65d78d80dd /hw/i386 | |
parent | 42e0f050e3a513f070b1c2e885890ac2095337e8 (diff) | |
download | qemu-26863366b29ab638711bbe0fa741dcf355eddb54.zip qemu-26863366b29ab638711bbe0fa741dcf355eddb54.tar.gz qemu-26863366b29ab638711bbe0fa741dcf355eddb54.tar.bz2 |
hw/i386/acpi-build: Add DMAR support to bypass iommu
In DMAR table, the drhd is set to cover all PCI devices when intel_iommu
is on. To support bypass iommu feature, we need to walk the PCI bus with
bypass_iommu disabled and add explicit scope data in DMAR drhd structure.
/mnt/sdb/wxg/qemu-next/qemu/build/x86_64-softmmu/qemu-system-x86_64 \
-machine q35,accel=kvm,default_bus_bypass_iommu=true \
-cpu host \
-m 16G \
-smp 36,sockets=2,cores=18,threads=1 \
-device pxb-pcie,bus_nr=0x10,id=pci.10,bus=pcie.0,addr=0x3 \
-device pxb-pcie,bus_nr=0x20,id=pci.20,bus=pcie.0,addr=0x4,bypass_iommu=true \
-device pcie-root-port,port=0x1,chassis=1,id=pci.11,bus=pci.10,addr=0x0 \
-device pcie-root-port,port=0x2,chassis=2,id=pci.21,bus=pci.20,addr=0x0 \
-device virtio-scsi-pci,id=scsi0,bus=pci.11,addr=0x0 \
-device virtio-scsi-pci,id=scsi1,bus=pci.21,addr=0x0 \
-drive file=/mnt/sdb/wxg/fedora-48g.qcow2,format=qcow2,if=none,id=drive-scsi0-0-0-0,cache=none,aio=native \
-device scsi-hd,bus=scsi1.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,id=scsi0-0-0-0,bootindex=1 \
-device intel-iommu \
-nographic \
And we get the guest configuration:
~ lspci -vt
-+-[0000:20]---00.0-[21]----00.0 Red Hat, Inc. Virtio SCSI
+-[0000:10]---00.0-[11]----00.0 Red Hat, Inc. Virtio SCSI
\-[0000:00]-+-00.0 Intel Corporation 82G33/G31/P35/P31 Express DRAM Controller
+-01.0 Device 1234:1111
+-02.0 Intel Corporation 82574L Gigabit Network Connection
+-03.0 Red Hat, Inc. QEMU PCIe Expander bridge
+-04.0 Red Hat, Inc. QEMU PCIe Expander bridge
+-1f.0 Intel Corporation 82801IB (ICH9) LPC Interface Controller
+-1f.2 Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode]
\-1f.3 Intel Corporation 82801I (ICH9 Family) SMBus Controller
With bypass_iommu enabled on root bus, the attached devices will bypass iommu:
/sys/class/iommu/dmar0
├── devices
│ ├── 0000:10:00.0 -> ../../../../pci0000:10/0000:10:00.0
│ └── 0000:11:00.0 -> ../../../../pci0000:10/0000:10:00.0/0000:11:00.0
Signed-off-by: Xingang Wang <wangxingang5@huawei.com>
Message-Id: <1625748919-52456-8-git-send-email-wangxingang5@huawei.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Diffstat (limited to 'hw/i386')
-rw-r--r-- | hw/i386/acpi-build.c | 68 |
1 files changed, 66 insertions, 2 deletions
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index bc966a4..7efc628 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2023,6 +2023,56 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) } /* + * Insert DMAR scope for PCI bridges and endpoint devcie + */ +static void +insert_scope(PCIBus *bus, PCIDevice *dev, void *opaque) +{ + GArray *scope_blob = opaque; + AcpiDmarDeviceScope *scope = NULL; + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) { + /* Dmar Scope Type: 0x02 for PCI Bridge */ + build_append_int_noprefix(scope_blob, 0x02, 1); + } else { + /* Dmar Scope Type: 0x01 for PCI Endpoint Device */ + build_append_int_noprefix(scope_blob, 0x01, 1); + } + + /* length */ + build_append_int_noprefix(scope_blob, + sizeof(*scope) + sizeof(scope->path[0]), 1); + /* reserved */ + build_append_int_noprefix(scope_blob, 0, 2); + /* enumeration_id */ + build_append_int_noprefix(scope_blob, 0, 1); + /* bus */ + build_append_int_noprefix(scope_blob, pci_bus_num(bus), 1); + /* device */ + build_append_int_noprefix(scope_blob, PCI_SLOT(dev->devfn), 1); + /* function */ + build_append_int_noprefix(scope_blob, PCI_FUNC(dev->devfn), 1); +} + +/* For a given PCI host bridge, walk and insert DMAR scope */ +static int +dmar_host_bridges(Object *obj, void *opaque) +{ + GArray *scope_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)) { + pci_for_each_device(bus, pci_bus_num(bus), insert_scope, + scope_blob); + } + } + + return 0; +} + +/* * VT-d spec 8.1 DMA Remapping Reporting Structure * (version Oct. 2014 or later) */ @@ -2041,6 +2091,15 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker, const char *oem_id, /* Root complex IOAPIC use one path[0] only */ size_t ioapic_scope_size = sizeof(*scope) + sizeof(scope->path[0]); IntelIOMMUState *intel_iommu = INTEL_IOMMU_DEVICE(iommu); + GArray *scope_blob = g_array_new(false, true, 1); + + /* + * A PCI bus walk, for each PCI host bridge. + * Insert scope for each PCI bridge and endpoint device which + * is attached to a bus with iommu enabled. + */ + object_child_foreach_recursive(object_get_root(), + dmar_host_bridges, scope_blob); assert(iommu); if (x86_iommu_ir_supported(iommu)) { @@ -2054,8 +2113,9 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker, const char *oem_id, /* DMAR Remapping Hardware Unit Definition structure */ drhd = acpi_data_push(table_data, sizeof(*drhd) + ioapic_scope_size); drhd->type = cpu_to_le16(ACPI_DMAR_TYPE_HARDWARE_UNIT); - drhd->length = cpu_to_le16(sizeof(*drhd) + ioapic_scope_size); - drhd->flags = ACPI_DMAR_INCLUDE_PCI_ALL; + drhd->length = + cpu_to_le16(sizeof(*drhd) + ioapic_scope_size + scope_blob->len); + drhd->flags = 0; /* Don't include all pci device */ drhd->pci_segment = cpu_to_le16(0); drhd->address = cpu_to_le64(Q35_HOST_BRIDGE_IOMMU_ADDR); @@ -2069,6 +2129,10 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker, const char *oem_id, scope->path[0].device = PCI_SLOT(Q35_PSEUDO_DEVFN_IOAPIC); scope->path[0].function = PCI_FUNC(Q35_PSEUDO_DEVFN_IOAPIC); + /* Add scope found above */ + g_array_append_vals(table_data, scope_blob->data, scope_blob->len); + g_array_free(scope_blob, true); + if (iommu->dt_supported) { atsr = acpi_data_push(table_data, sizeof(*atsr)); atsr->type = cpu_to_le16(ACPI_DMAR_TYPE_ATSR); |