Merge remote-tracking branch 'remotes/jsnow-gitlab/tags/python-pull-request' into...
[qemu.git] / hw / acpi / viot.c
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