xref: /qemu/hw/acpi/viot.c (revision ca61e750)
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