1 /* 2 * ACPI Virtual I/O Translation table implementation 3 * 4 * SPDX-License-Identifier: GPL-2.0-or-later 5 */ 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" 12 13 struct viot_pci_ranges { 14 GArray *blob; 15 size_t count; 16 uint16_t output_node; 17 }; 18 19 /* Build PCI range for a given PCI host bridge */ 20 static int build_pci_range_node(Object *obj, void *opaque) 21 { 22 struct viot_pci_ranges *pci_ranges = opaque; 23 GArray *blob = pci_ranges->blob; 24 25 if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) { 26 PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus; 27 28 if (bus && !pci_bus_bypass_iommu(bus)) { 29 int min_bus, max_bus; 30 31 pci_bus_range(bus, &min_bus, &max_bus); 32 33 /* Type */ 34 build_append_int_noprefix(blob, 1 /* PCI range */, 1); 35 /* Reserved */ 36 build_append_int_noprefix(blob, 0, 1); 37 /* Length */ 38 build_append_int_noprefix(blob, 24, 2); 39 /* Endpoint start */ 40 build_append_int_noprefix(blob, PCI_BUILD_BDF(min_bus, 0), 4); 41 /* PCI Segment start */ 42 build_append_int_noprefix(blob, 0, 2); 43 /* PCI Segment end */ 44 build_append_int_noprefix(blob, 0, 2); 45 /* PCI BDF start */ 46 build_append_int_noprefix(blob, PCI_BUILD_BDF(min_bus, 0), 2); 47 /* PCI BDF end */ 48 build_append_int_noprefix(blob, PCI_BUILD_BDF(max_bus, 0xff), 2); 49 /* Output node */ 50 build_append_int_noprefix(blob, pci_ranges->output_node, 2); 51 /* Reserved */ 52 build_append_int_noprefix(blob, 0, 6); 53 54 pci_ranges->count++; 55 } 56 } 57 58 return 0; 59 } 60 61 /* 62 * Generate a VIOT table with one PCI-based virtio-iommu that manages PCI 63 * endpoints. 64 * 65 * Defined in the ACPI Specification (Version TBD) 66 */ 67 void build_viot(MachineState *ms, GArray *table_data, BIOSLinker *linker, 68 uint16_t virtio_iommu_bdf, const char *oem_id, 69 const char *oem_table_id) 70 { 71 /* The virtio-iommu node follows the 48-bytes header */ 72 int viommu_off = 48; 73 AcpiTable table = { .sig = "VIOT", .rev = 0, 74 .oem_id = oem_id, .oem_table_id = oem_table_id }; 75 struct viot_pci_ranges pci_ranges = { 76 .output_node = viommu_off, 77 .blob = g_array_new(false, true /* clear */, 1), 78 }; 79 80 /* Build the list of PCI ranges that this viommu manages */ 81 object_child_foreach_recursive(OBJECT(ms), build_pci_range_node, 82 &pci_ranges); 83 84 /* ACPI table header */ 85 acpi_table_begin(&table, table_data); 86 /* Node count */ 87 build_append_int_noprefix(table_data, pci_ranges.count + 1, 2); 88 /* Node offset */ 89 build_append_int_noprefix(table_data, viommu_off, 2); 90 /* Reserved */ 91 build_append_int_noprefix(table_data, 0, 8); 92 93 /* Virtio-iommu node */ 94 /* Type */ 95 build_append_int_noprefix(table_data, 3 /* virtio-pci IOMMU */, 1); 96 /* Reserved */ 97 build_append_int_noprefix(table_data, 0, 1); 98 /* Length */ 99 build_append_int_noprefix(table_data, 16, 2); 100 /* PCI Segment */ 101 build_append_int_noprefix(table_data, 0, 2); 102 /* PCI BDF number */ 103 build_append_int_noprefix(table_data, virtio_iommu_bdf, 2); 104 /* Reserved */ 105 build_append_int_noprefix(table_data, 0, 8); 106 107 /* PCI ranges found above */ 108 g_array_append_vals(table_data, pci_ranges.blob->data, 109 pci_ranges.blob->len); 110 g_array_free(pci_ranges.blob, true); 111 112 acpi_table_end(linker, &table); 113 } 114 115