xref: /openbmc/qemu/hw/acpi/viot.c (revision 30796f556790631c86c733ab06756981be0e1def)
11f85d74aSJean-Philippe Brucker /*
21f85d74aSJean-Philippe Brucker  * ACPI Virtual I/O Translation table implementation
31f85d74aSJean-Philippe Brucker  *
41f85d74aSJean-Philippe Brucker  * SPDX-License-Identifier: GPL-2.0-or-later
51f85d74aSJean-Philippe Brucker  */
61f85d74aSJean-Philippe Brucker #include "qemu/osdep.h"
71f85d74aSJean-Philippe Brucker #include "hw/acpi/acpi.h"
81f85d74aSJean-Philippe Brucker #include "hw/acpi/aml-build.h"
91f85d74aSJean-Philippe Brucker #include "hw/acpi/viot.h"
101f85d74aSJean-Philippe Brucker #include "hw/pci/pci.h"
111f85d74aSJean-Philippe Brucker #include "hw/pci/pci_host.h"
121f85d74aSJean-Philippe Brucker 
13e5f73c83SMark Cave-Ayland struct viot_pci_host_range {
14e5f73c83SMark Cave-Ayland     int min_bus;
15e5f73c83SMark Cave-Ayland     int max_bus;
161f85d74aSJean-Philippe Brucker };
171f85d74aSJean-Philippe Brucker 
build_pci_host_range(GArray * table_data,int min_bus,int max_bus,uint16_t output_node)181b805ab5SMark Cave-Ayland static void build_pci_host_range(GArray *table_data, int min_bus, int max_bus,
191b805ab5SMark Cave-Ayland                                  uint16_t output_node)
201b805ab5SMark Cave-Ayland {
211b805ab5SMark Cave-Ayland     /* Type */
221b805ab5SMark Cave-Ayland     build_append_int_noprefix(table_data, 1 /* PCI range */, 1);
231b805ab5SMark Cave-Ayland     /* Reserved */
241b805ab5SMark Cave-Ayland     build_append_int_noprefix(table_data, 0, 1);
251b805ab5SMark Cave-Ayland     /* Length */
261b805ab5SMark Cave-Ayland     build_append_int_noprefix(table_data, 24, 2);
271b805ab5SMark Cave-Ayland     /* Endpoint start */
281b805ab5SMark Cave-Ayland     build_append_int_noprefix(table_data, PCI_BUILD_BDF(min_bus, 0), 4);
291b805ab5SMark Cave-Ayland     /* PCI Segment start */
301b805ab5SMark Cave-Ayland     build_append_int_noprefix(table_data, 0, 2);
311b805ab5SMark Cave-Ayland     /* PCI Segment end */
321b805ab5SMark Cave-Ayland     build_append_int_noprefix(table_data, 0, 2);
331b805ab5SMark Cave-Ayland     /* PCI BDF start */
341b805ab5SMark Cave-Ayland     build_append_int_noprefix(table_data, PCI_BUILD_BDF(min_bus, 0), 2);
351b805ab5SMark Cave-Ayland     /* PCI BDF end */
361b805ab5SMark Cave-Ayland     build_append_int_noprefix(table_data, PCI_BUILD_BDF(max_bus, 0xff), 2);
371b805ab5SMark Cave-Ayland     /* Output node */
381b805ab5SMark Cave-Ayland     build_append_int_noprefix(table_data, output_node, 2);
391b805ab5SMark Cave-Ayland     /* Reserved */
401b805ab5SMark Cave-Ayland     build_append_int_noprefix(table_data, 0, 6);
411b805ab5SMark Cave-Ayland }
421b805ab5SMark Cave-Ayland 
431f85d74aSJean-Philippe Brucker /* Build PCI range for a given PCI host bridge */
enumerate_pci_host_bridges(Object * obj,void * opaque)446164a111SMark Cave-Ayland static int enumerate_pci_host_bridges(Object *obj, void *opaque)
451f85d74aSJean-Philippe Brucker {
46e5f73c83SMark Cave-Ayland     GArray *pci_host_ranges = opaque;
471f85d74aSJean-Philippe Brucker 
481f85d74aSJean-Philippe Brucker     if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) {
491f85d74aSJean-Philippe Brucker         PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus;
501f85d74aSJean-Philippe Brucker 
511f85d74aSJean-Philippe Brucker         if (bus && !pci_bus_bypass_iommu(bus)) {
521f85d74aSJean-Philippe Brucker             int min_bus, max_bus;
531f85d74aSJean-Philippe Brucker 
541f85d74aSJean-Philippe Brucker             pci_bus_range(bus, &min_bus, &max_bus);
551f85d74aSJean-Philippe Brucker 
56e5f73c83SMark Cave-Ayland             const struct viot_pci_host_range pci_host_range = {
57e5f73c83SMark Cave-Ayland                 .min_bus = min_bus,
58e5f73c83SMark Cave-Ayland                 .max_bus = max_bus,
59e5f73c83SMark Cave-Ayland             };
60e5f73c83SMark Cave-Ayland             g_array_append_val(pci_host_ranges, pci_host_range);
611f85d74aSJean-Philippe Brucker         }
621f85d74aSJean-Philippe Brucker     }
631f85d74aSJean-Philippe Brucker 
641f85d74aSJean-Philippe Brucker     return 0;
651f85d74aSJean-Philippe Brucker }
661f85d74aSJean-Philippe Brucker 
pci_host_range_compare(gconstpointer a,gconstpointer b)67*68f14a87SMark Cave-Ayland static gint pci_host_range_compare(gconstpointer a, gconstpointer b)
68*68f14a87SMark Cave-Ayland {
69*68f14a87SMark Cave-Ayland     struct viot_pci_host_range *range_a = (struct viot_pci_host_range *)a;
70*68f14a87SMark Cave-Ayland     struct viot_pci_host_range *range_b = (struct viot_pci_host_range *)b;
71*68f14a87SMark Cave-Ayland 
72*68f14a87SMark Cave-Ayland     if (range_a->min_bus < range_b->min_bus) {
73*68f14a87SMark Cave-Ayland         return -1;
74*68f14a87SMark Cave-Ayland     } else if (range_a->min_bus > range_b->min_bus) {
75*68f14a87SMark Cave-Ayland         return 1;
76*68f14a87SMark Cave-Ayland     } else {
77*68f14a87SMark Cave-Ayland         return 0;
78*68f14a87SMark Cave-Ayland     }
79*68f14a87SMark Cave-Ayland }
80*68f14a87SMark Cave-Ayland 
811f85d74aSJean-Philippe Brucker /*
821f85d74aSJean-Philippe Brucker  * Generate a VIOT table with one PCI-based virtio-iommu that manages PCI
831f85d74aSJean-Philippe Brucker  * endpoints.
841f85d74aSJean-Philippe Brucker  *
851f85d74aSJean-Philippe Brucker  * Defined in the ACPI Specification (Version TBD)
861f85d74aSJean-Philippe Brucker  */
build_viot(MachineState * ms,GArray * table_data,BIOSLinker * linker,uint16_t virtio_iommu_bdf,const char * oem_id,const char * oem_table_id)871f85d74aSJean-Philippe Brucker void build_viot(MachineState *ms, GArray *table_data, BIOSLinker *linker,
881f85d74aSJean-Philippe Brucker                 uint16_t virtio_iommu_bdf, const char *oem_id,
891f85d74aSJean-Philippe Brucker                 const char *oem_table_id)
901f85d74aSJean-Philippe Brucker {
911f85d74aSJean-Philippe Brucker     /* The virtio-iommu node follows the 48-bytes header */
921f85d74aSJean-Philippe Brucker     int viommu_off = 48;
931f85d74aSJean-Philippe Brucker     AcpiTable table = { .sig = "VIOT", .rev = 0,
941f85d74aSJean-Philippe Brucker                         .oem_id = oem_id, .oem_table_id = oem_table_id };
95e5f73c83SMark Cave-Ayland     GArray *pci_host_ranges =  g_array_new(false, true,
96e5f73c83SMark Cave-Ayland                                            sizeof(struct viot_pci_host_range));
97e5f73c83SMark Cave-Ayland     struct viot_pci_host_range *pci_host_range;
98e5f73c83SMark Cave-Ayland     int i;
991f85d74aSJean-Philippe Brucker 
1001f85d74aSJean-Philippe Brucker     /* Build the list of PCI ranges that this viommu manages */
1016164a111SMark Cave-Ayland     object_child_foreach_recursive(OBJECT(ms), enumerate_pci_host_bridges,
102e5f73c83SMark Cave-Ayland                                    pci_host_ranges);
1031f85d74aSJean-Philippe Brucker 
104*68f14a87SMark Cave-Ayland     /* Sort the pci host ranges by min_bus */
105*68f14a87SMark Cave-Ayland     g_array_sort(pci_host_ranges, pci_host_range_compare);
106*68f14a87SMark Cave-Ayland 
1071f85d74aSJean-Philippe Brucker     /* ACPI table header */
1081f85d74aSJean-Philippe Brucker     acpi_table_begin(&table, table_data);
1091f85d74aSJean-Philippe Brucker     /* Node count */
110e5f73c83SMark Cave-Ayland     build_append_int_noprefix(table_data, pci_host_ranges->len + 1, 2);
1111f85d74aSJean-Philippe Brucker     /* Node offset */
1121f85d74aSJean-Philippe Brucker     build_append_int_noprefix(table_data, viommu_off, 2);
1131f85d74aSJean-Philippe Brucker     /* Reserved */
1141f85d74aSJean-Philippe Brucker     build_append_int_noprefix(table_data, 0, 8);
1151f85d74aSJean-Philippe Brucker 
1161f85d74aSJean-Philippe Brucker     /* Virtio-iommu node */
1171f85d74aSJean-Philippe Brucker     /* Type */
1181f85d74aSJean-Philippe Brucker     build_append_int_noprefix(table_data, 3 /* virtio-pci IOMMU */, 1);
1191f85d74aSJean-Philippe Brucker     /* Reserved */
1201f85d74aSJean-Philippe Brucker     build_append_int_noprefix(table_data, 0, 1);
1211f85d74aSJean-Philippe Brucker     /* Length */
1221f85d74aSJean-Philippe Brucker     build_append_int_noprefix(table_data, 16, 2);
1231f85d74aSJean-Philippe Brucker     /* PCI Segment */
1241f85d74aSJean-Philippe Brucker     build_append_int_noprefix(table_data, 0, 2);
1251f85d74aSJean-Philippe Brucker     /* PCI BDF number */
1261f85d74aSJean-Philippe Brucker     build_append_int_noprefix(table_data, virtio_iommu_bdf, 2);
1271f85d74aSJean-Philippe Brucker     /* Reserved */
1281f85d74aSJean-Philippe Brucker     build_append_int_noprefix(table_data, 0, 8);
1291f85d74aSJean-Philippe Brucker 
1301f85d74aSJean-Philippe Brucker     /* PCI ranges found above */
131e5f73c83SMark Cave-Ayland     for (i = 0; i < pci_host_ranges->len; i++) {
132e5f73c83SMark Cave-Ayland         pci_host_range = &g_array_index(pci_host_ranges,
133e5f73c83SMark Cave-Ayland                                         struct viot_pci_host_range, i);
134e5f73c83SMark Cave-Ayland 
135e5f73c83SMark Cave-Ayland         build_pci_host_range(table_data, pci_host_range->min_bus,
136e5f73c83SMark Cave-Ayland                              pci_host_range->max_bus, viommu_off);
137e5f73c83SMark Cave-Ayland     }
138e5f73c83SMark Cave-Ayland 
139e5f73c83SMark Cave-Ayland     g_array_free(pci_host_ranges, true);
1401f85d74aSJean-Philippe Brucker 
1411f85d74aSJean-Philippe Brucker     acpi_table_end(linker, &table);
1421f85d74aSJean-Philippe Brucker }
1431f85d74aSJean-Philippe Brucker 
144