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