aboutsummaryrefslogtreecommitdiff
path: root/hw/acpi
diff options
context:
space:
mode:
authorJean-Philippe Brucker <jean-philippe@linaro.org>2021-10-26 19:20:20 +0100
committerMichael S. Tsirkin <mst@redhat.com>2021-11-01 18:49:10 -0400
commit1f85d74ac542d57559babbe0525633837e9fd52f (patch)
treef88acfa66703dd64c243f5a5db8cc9cb64dc7710 /hw/acpi
parente1c1915befbd2a991b20812eef6ad650b5637a36 (diff)
hw/acpi: Add VIOT table
Add a function that generates a Virtual I/O Translation table (VIOT), describing the topology of paravirtual IOMMUs. The table is created if a virtio-iommu device is present. It contains a virtio-iommu node and PCI Range nodes for endpoints managed by the IOMMU. By default, a single node describes all PCI devices. When passing the "default_bus_bypass_iommu" machine option and "bypass_iommu" PXB option, only buses that do not bypass the IOMMU are described by PCI Range nodes. Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org> Message-Id: <20211026182024.2642038-2-jean-philippe@linaro.org> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Diffstat (limited to 'hw/acpi')
-rw-r--r--hw/acpi/Kconfig4
-rw-r--r--hw/acpi/meson.build1
-rw-r--r--hw/acpi/viot.c114
-rw-r--r--hw/acpi/viot.h13
4 files changed, 132 insertions, 0 deletions
diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
index 3b5e118c54..622b0b50b7 100644
--- a/hw/acpi/Kconfig
+++ b/hw/acpi/Kconfig
@@ -51,6 +51,10 @@ config ACPI_VMGENID
default y
depends on PC
+config ACPI_VIOT
+ bool
+ depends on ACPI
+
config ACPI_HW_REDUCED
bool
select ACPI
diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build
index 7d8c0eb43e..adf6347bc4 100644
--- a/hw/acpi/meson.build
+++ b/hw/acpi/meson.build
@@ -20,6 +20,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_APEI', if_true: files('ghes.c'), if_false: files(
acpi_ss.add(when: 'CONFIG_ACPI_PIIX4', if_true: files('piix4.c'))
acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_true: files('pcihp.c'))
acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_false: files('acpi-pci-hotplug-stub.c'))
+acpi_ss.add(when: 'CONFIG_ACPI_VIOT', if_true: files('viot.c'))
acpi_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('ich9.c', 'tco.c'))
acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: files('ipmi-stub.c'))
acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c'))
diff --git a/hw/acpi/viot.c b/hw/acpi/viot.c
new file mode 100644
index 0000000000..c1af75206e
--- /dev/null
+++ b/hw/acpi/viot.c
@@ -0,0 +1,114 @@
+/*
+ * ACPI Virtual I/O Translation table implementation
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/acpi/viot.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
+
+struct viot_pci_ranges {
+ GArray *blob;
+ size_t count;
+ uint16_t output_node;
+};
+
+/* Build PCI range for a given PCI host bridge */
+static int build_pci_range_node(Object *obj, void *opaque)
+{
+ struct viot_pci_ranges *pci_ranges = opaque;
+ GArray *blob = pci_ranges->blob;
+
+ 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);
+
+ /* Type */
+ build_append_int_noprefix(blob, 1 /* PCI range */, 1);
+ /* Reserved */
+ build_append_int_noprefix(blob, 0, 1);
+ /* Length */
+ build_append_int_noprefix(blob, 24, 2);
+ /* Endpoint start */
+ build_append_int_noprefix(blob, PCI_BUILD_BDF(min_bus, 0), 4);
+ /* PCI Segment start */
+ build_append_int_noprefix(blob, 0, 2);
+ /* PCI Segment end */
+ build_append_int_noprefix(blob, 0, 2);
+ /* PCI BDF start */
+ build_append_int_noprefix(blob, PCI_BUILD_BDF(min_bus, 0), 2);
+ /* PCI BDF end */
+ build_append_int_noprefix(blob, PCI_BUILD_BDF(max_bus, 0xff), 2);
+ /* Output node */
+ build_append_int_noprefix(blob, pci_ranges->output_node, 2);
+ /* Reserved */
+ build_append_int_noprefix(blob, 0, 6);
+
+ pci_ranges->count++;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Generate a VIOT table with one PCI-based virtio-iommu that manages PCI
+ * endpoints.
+ *
+ * Defined in the ACPI Specification (Version TBD)
+ */
+void build_viot(MachineState *ms, GArray *table_data, BIOSLinker *linker,
+ uint16_t virtio_iommu_bdf, const char *oem_id,
+ const char *oem_table_id)
+{
+ /* The virtio-iommu node follows the 48-bytes header */
+ int viommu_off = 48;
+ AcpiTable table = { .sig = "VIOT", .rev = 0,
+ .oem_id = oem_id, .oem_table_id = oem_table_id };
+ struct viot_pci_ranges pci_ranges = {
+ .output_node = viommu_off,
+ .blob = g_array_new(false, true /* clear */, 1),
+ };
+
+ /* Build the list of PCI ranges that this viommu manages */
+ object_child_foreach_recursive(OBJECT(ms), build_pci_range_node,
+ &pci_ranges);
+
+ /* ACPI table header */
+ acpi_table_begin(&table, table_data);
+ /* Node count */
+ build_append_int_noprefix(table_data, pci_ranges.count + 1, 2);
+ /* Node offset */
+ build_append_int_noprefix(table_data, viommu_off, 2);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 8);
+
+ /* Virtio-iommu node */
+ /* Type */
+ build_append_int_noprefix(table_data, 3 /* virtio-pci IOMMU */, 1);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 1);
+ /* Length */
+ build_append_int_noprefix(table_data, 16, 2);
+ /* PCI Segment */
+ build_append_int_noprefix(table_data, 0, 2);
+ /* PCI BDF number */
+ build_append_int_noprefix(table_data, virtio_iommu_bdf, 2);
+ /* Reserved */
+ build_append_int_noprefix(table_data, 0, 8);
+
+ /* PCI ranges found above */
+ g_array_append_vals(table_data, pci_ranges.blob->data,
+ pci_ranges.blob->len);
+ g_array_free(pci_ranges.blob, true);
+
+ acpi_table_end(linker, &table);
+}
+
diff --git a/hw/acpi/viot.h b/hw/acpi/viot.h
new file mode 100644
index 0000000000..9fe565bb87
--- /dev/null
+++ b/hw/acpi/viot.h
@@ -0,0 +1,13 @@
+/*
+ * ACPI Virtual I/O Translation Table implementation
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef VIOT_H
+#define VIOT_H
+
+void build_viot(MachineState *ms, GArray *table_data, BIOSLinker *linker,
+ uint16_t virtio_iommu_bdf, const char *oem_id,
+ const char *oem_table_id);
+
+#endif /* VIOT_H */