]>
Commit | Line | Data |
---|---|---|
1f85d74a JPB |
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 | ||
e5f73c83 MCA |
13 | struct viot_pci_host_range { |
14 | int min_bus; | |
15 | int max_bus; | |
1f85d74a JPB |
16 | }; |
17 | ||
1b805ab5 MCA |
18 | static void build_pci_host_range(GArray *table_data, int min_bus, int max_bus, |
19 | uint16_t output_node) | |
20 | { | |
21 | /* Type */ | |
22 | build_append_int_noprefix(table_data, 1 /* PCI range */, 1); | |
23 | /* Reserved */ | |
24 | build_append_int_noprefix(table_data, 0, 1); | |
25 | /* Length */ | |
26 | build_append_int_noprefix(table_data, 24, 2); | |
27 | /* Endpoint start */ | |
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); | |
31 | /* PCI Segment end */ | |
32 | build_append_int_noprefix(table_data, 0, 2); | |
33 | /* PCI BDF start */ | |
34 | build_append_int_noprefix(table_data, PCI_BUILD_BDF(min_bus, 0), 2); | |
35 | /* PCI BDF end */ | |
36 | build_append_int_noprefix(table_data, PCI_BUILD_BDF(max_bus, 0xff), 2); | |
37 | /* Output node */ | |
38 | build_append_int_noprefix(table_data, output_node, 2); | |
39 | /* Reserved */ | |
40 | build_append_int_noprefix(table_data, 0, 6); | |
41 | } | |
42 | ||
1f85d74a | 43 | /* Build PCI range for a given PCI host bridge */ |
6164a111 | 44 | static int enumerate_pci_host_bridges(Object *obj, void *opaque) |
1f85d74a | 45 | { |
e5f73c83 | 46 | GArray *pci_host_ranges = opaque; |
1f85d74a JPB |
47 | |
48 | if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) { | |
49 | PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus; | |
50 | ||
51 | if (bus && !pci_bus_bypass_iommu(bus)) { | |
52 | int min_bus, max_bus; | |
53 | ||
54 | pci_bus_range(bus, &min_bus, &max_bus); | |
55 | ||
e5f73c83 MCA |
56 | const struct viot_pci_host_range pci_host_range = { |
57 | .min_bus = min_bus, | |
58 | .max_bus = max_bus, | |
59 | }; | |
60 | g_array_append_val(pci_host_ranges, pci_host_range); | |
1f85d74a JPB |
61 | } |
62 | } | |
63 | ||
64 | return 0; | |
65 | } | |
66 | ||
68f14a87 MCA |
67 | static gint pci_host_range_compare(gconstpointer a, gconstpointer b) |
68 | { | |
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; | |
71 | ||
72 | if (range_a->min_bus < range_b->min_bus) { | |
73 | return -1; | |
74 | } else if (range_a->min_bus > range_b->min_bus) { | |
75 | return 1; | |
76 | } else { | |
77 | return 0; | |
78 | } | |
79 | } | |
80 | ||
1f85d74a JPB |
81 | /* |
82 | * Generate a VIOT table with one PCI-based virtio-iommu that manages PCI | |
83 | * endpoints. | |
84 | * | |
85 | * Defined in the ACPI Specification (Version TBD) | |
86 | */ | |
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) | |
90 | { | |
91 | /* The virtio-iommu node follows the 48-bytes header */ | |
92 | int viommu_off = 48; | |
93 | AcpiTable table = { .sig = "VIOT", .rev = 0, | |
94 | .oem_id = oem_id, .oem_table_id = oem_table_id }; | |
e5f73c83 MCA |
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; | |
98 | int i; | |
1f85d74a JPB |
99 | |
100 | /* Build the list of PCI ranges that this viommu manages */ | |
6164a111 | 101 | object_child_foreach_recursive(OBJECT(ms), enumerate_pci_host_bridges, |
e5f73c83 | 102 | pci_host_ranges); |
1f85d74a | 103 | |
68f14a87 MCA |
104 | /* Sort the pci host ranges by min_bus */ |
105 | g_array_sort(pci_host_ranges, pci_host_range_compare); | |
106 | ||
1f85d74a JPB |
107 | /* ACPI table header */ |
108 | acpi_table_begin(&table, table_data); | |
109 | /* Node count */ | |
e5f73c83 | 110 | build_append_int_noprefix(table_data, pci_host_ranges->len + 1, 2); |
1f85d74a JPB |
111 | /* Node offset */ |
112 | build_append_int_noprefix(table_data, viommu_off, 2); | |
113 | /* Reserved */ | |
114 | build_append_int_noprefix(table_data, 0, 8); | |
115 | ||
116 | /* Virtio-iommu node */ | |
117 | /* Type */ | |
118 | build_append_int_noprefix(table_data, 3 /* virtio-pci IOMMU */, 1); | |
119 | /* Reserved */ | |
120 | build_append_int_noprefix(table_data, 0, 1); | |
121 | /* Length */ | |
122 | build_append_int_noprefix(table_data, 16, 2); | |
123 | /* PCI Segment */ | |
124 | build_append_int_noprefix(table_data, 0, 2); | |
125 | /* PCI BDF number */ | |
126 | build_append_int_noprefix(table_data, virtio_iommu_bdf, 2); | |
127 | /* Reserved */ | |
128 | build_append_int_noprefix(table_data, 0, 8); | |
129 | ||
130 | /* PCI ranges found above */ | |
e5f73c83 MCA |
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); | |
134 | ||
135 | build_pci_host_range(table_data, pci_host_range->min_bus, | |
136 | pci_host_range->max_bus, viommu_off); | |
137 | } | |
138 | ||
139 | g_array_free(pci_host_ranges, true); | |
1f85d74a JPB |
140 | |
141 | acpi_table_end(linker, &table); | |
142 | } | |
143 |