2 * ACPI Virtual I/O Translation table implementation
4 * SPDX-License-Identifier: GPL-2.0-or-later
6 #include "qemu/osdep.h"
7 #include "hw/acpi/acpi.h"
8 #include "hw/acpi/aml-build.h"
9 #include "hw/acpi/viot.h"
10 #include "hw/pci/pci.h"
11 #include "hw/pci/pci_host.h"
13 struct viot_pci_host_range
{
18 static void build_pci_host_range(GArray
*table_data
, int min_bus
, int max_bus
,
22 build_append_int_noprefix(table_data
, 1 /* PCI range */, 1);
24 build_append_int_noprefix(table_data
, 0, 1);
26 build_append_int_noprefix(table_data
, 24, 2);
28 build_append_int_noprefix(table_data
, PCI_BUILD_BDF(min_bus
, 0), 4);
29 /* PCI Segment start */
30 build_append_int_noprefix(table_data
, 0, 2);
32 build_append_int_noprefix(table_data
, 0, 2);
34 build_append_int_noprefix(table_data
, PCI_BUILD_BDF(min_bus
, 0), 2);
36 build_append_int_noprefix(table_data
, PCI_BUILD_BDF(max_bus
, 0xff), 2);
38 build_append_int_noprefix(table_data
, output_node
, 2);
40 build_append_int_noprefix(table_data
, 0, 6);
43 /* Build PCI range for a given PCI host bridge */
44 static int enumerate_pci_host_bridges(Object
*obj
, void *opaque
)
46 GArray
*pci_host_ranges
= opaque
;
48 if (object_dynamic_cast(obj
, TYPE_PCI_HOST_BRIDGE
)) {
49 PCIBus
*bus
= PCI_HOST_BRIDGE(obj
)->bus
;
51 if (bus
&& !pci_bus_bypass_iommu(bus
)) {
54 pci_bus_range(bus
, &min_bus
, &max_bus
);
56 const struct viot_pci_host_range pci_host_range
= {
60 g_array_append_val(pci_host_ranges
, pci_host_range
);
67 static gint
pci_host_range_compare(gconstpointer a
, gconstpointer b
)
69 struct viot_pci_host_range
*range_a
= (struct viot_pci_host_range
*)a
;
70 struct viot_pci_host_range
*range_b
= (struct viot_pci_host_range
*)b
;
72 if (range_a
->min_bus
< range_b
->min_bus
) {
74 } else if (range_a
->min_bus
> range_b
->min_bus
) {
82 * Generate a VIOT table with one PCI-based virtio-iommu that manages PCI
85 * Defined in the ACPI Specification (Version TBD)
87 void build_viot(MachineState
*ms
, GArray
*table_data
, BIOSLinker
*linker
,
88 uint16_t virtio_iommu_bdf
, const char *oem_id
,
89 const char *oem_table_id
)
91 /* The virtio-iommu node follows the 48-bytes header */
93 AcpiTable table
= { .sig
= "VIOT", .rev
= 0,
94 .oem_id
= oem_id
, .oem_table_id
= oem_table_id
};
95 GArray
*pci_host_ranges
= g_array_new(false, true,
96 sizeof(struct viot_pci_host_range
));
97 struct viot_pci_host_range
*pci_host_range
;
100 /* Build the list of PCI ranges that this viommu manages */
101 object_child_foreach_recursive(OBJECT(ms
), enumerate_pci_host_bridges
,
104 /* Sort the pci host ranges by min_bus */
105 g_array_sort(pci_host_ranges
, pci_host_range_compare
);
107 /* ACPI table header */
108 acpi_table_begin(&table
, table_data
);
110 build_append_int_noprefix(table_data
, pci_host_ranges
->len
+ 1, 2);
112 build_append_int_noprefix(table_data
, viommu_off
, 2);
114 build_append_int_noprefix(table_data
, 0, 8);
116 /* Virtio-iommu node */
118 build_append_int_noprefix(table_data
, 3 /* virtio-pci IOMMU */, 1);
120 build_append_int_noprefix(table_data
, 0, 1);
122 build_append_int_noprefix(table_data
, 16, 2);
124 build_append_int_noprefix(table_data
, 0, 2);
126 build_append_int_noprefix(table_data
, virtio_iommu_bdf
, 2);
128 build_append_int_noprefix(table_data
, 0, 8);
130 /* PCI ranges found above */
131 for (i
= 0; i
< pci_host_ranges
->len
; i
++) {
132 pci_host_range
= &g_array_index(pci_host_ranges
,
133 struct viot_pci_host_range
, i
);
135 build_pci_host_range(table_data
, pci_host_range
->min_bus
,
136 pci_host_range
->max_bus
, viommu_off
);
139 g_array_free(pci_host_ranges
, true);
141 acpi_table_end(linker
, &table
);