xref: /openbmc/qemu/hw/acpi/pci.c (revision df37d496)
1 /*
2  * Support for generating PCI related ACPI tables and passing them to Guests
3  *
4  * Copyright (C) 2006 Fabrice Bellard
5  * Copyright (C) 2008-2010  Kevin O'Connor <kevin@koconnor.net>
6  * Copyright (C) 2013-2019 Red Hat Inc
7  * Copyright (C) 2019 Intel Corporation
8  *
9  * Author: Wei Yang <richardw.yang@linux.intel.com>
10  * Author: Michael S. Tsirkin <mst@redhat.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21 
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 #include "qemu/osdep.h"
27 #include "qemu/error-report.h"
28 #include "qom/object_interfaces.h"
29 #include "qapi/error.h"
30 #include "hw/boards.h"
31 #include "hw/acpi/aml-build.h"
32 #include "hw/acpi/pci.h"
33 #include "hw/pci/pci_bridge.h"
34 #include "hw/pci/pci_device.h"
35 #include "hw/pci/pcie_host.h"
36 
37 /*
38  * PCI Firmware Specification, Revision 3.0
39  * 4.1.2 MCFG Table Description.
40  */
build_mcfg(GArray * table_data,BIOSLinker * linker,AcpiMcfgInfo * info,const char * oem_id,const char * oem_table_id)41 void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info,
42                 const char *oem_id, const char *oem_table_id)
43 {
44     AcpiTable table = { .sig = "MCFG", .rev = 1,
45                         .oem_id = oem_id, .oem_table_id = oem_table_id };
46 
47     acpi_table_begin(&table, table_data);
48 
49     /* Reserved */
50     build_append_int_noprefix(table_data, 0, 8);
51     /*
52      * Memory Mapped Enhanced Configuration Space Base Address Allocation
53      * Structure
54      */
55     /* Base address, processor-relative */
56     build_append_int_noprefix(table_data, info->base, 8);
57     /* PCI segment group number */
58     build_append_int_noprefix(table_data, 0, 2);
59     /* Starting PCI Bus number */
60     build_append_int_noprefix(table_data, 0, 1);
61     /* Final PCI Bus number */
62     build_append_int_noprefix(table_data, PCIE_MMCFG_BUS(info->size - 1), 1);
63     /* Reserved */
64     build_append_int_noprefix(table_data, 0, 4);
65 
66     acpi_table_end(linker, &table);
67 }
68 
69 typedef struct AcpiGenericInitiator {
70     /* private */
71     Object parent;
72 
73     /* public */
74     char *pci_dev;
75     uint32_t node;
76 } AcpiGenericInitiator;
77 
78 typedef struct AcpiGenericInitiatorClass {
79     ObjectClass parent_class;
80 } AcpiGenericInitiatorClass;
81 
82 #define TYPE_ACPI_GENERIC_INITIATOR "acpi-generic-initiator"
83 
84 OBJECT_DEFINE_TYPE_WITH_INTERFACES(AcpiGenericInitiator, acpi_generic_initiator,
85                    ACPI_GENERIC_INITIATOR, OBJECT,
86                    { TYPE_USER_CREATABLE },
87                    { NULL })
88 
OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericInitiator,ACPI_GENERIC_INITIATOR)89 OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericInitiator, ACPI_GENERIC_INITIATOR)
90 
91 static void acpi_generic_initiator_init(Object *obj)
92 {
93     AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
94 
95     gi->node = MAX_NODES;
96     gi->pci_dev = NULL;
97 }
98 
acpi_generic_initiator_finalize(Object * obj)99 static void acpi_generic_initiator_finalize(Object *obj)
100 {
101     AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
102 
103     g_free(gi->pci_dev);
104 }
105 
acpi_generic_initiator_set_pci_device(Object * obj,const char * val,Error ** errp)106 static void acpi_generic_initiator_set_pci_device(Object *obj, const char *val,
107                                                   Error **errp)
108 {
109     AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
110 
111     gi->pci_dev = g_strdup(val);
112 }
113 
acpi_generic_initiator_set_node(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)114 static void acpi_generic_initiator_set_node(Object *obj, Visitor *v,
115                                             const char *name, void *opaque,
116                                             Error **errp)
117 {
118     AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
119     MachineState *ms = MACHINE(qdev_get_machine());
120     uint32_t value;
121 
122     if (!visit_type_uint32(v, name, &value, errp)) {
123         return;
124     }
125 
126     if (value >= MAX_NODES) {
127         error_printf("%s: Invalid NUMA node specified\n",
128                      TYPE_ACPI_GENERIC_INITIATOR);
129         exit(1);
130     }
131 
132     gi->node = value;
133     ms->numa_state->nodes[gi->node].has_gi = true;
134 }
135 
acpi_generic_initiator_class_init(ObjectClass * oc,void * data)136 static void acpi_generic_initiator_class_init(ObjectClass *oc, void *data)
137 {
138     object_class_property_add_str(oc, "pci-dev", NULL,
139         acpi_generic_initiator_set_pci_device);
140     object_class_property_set_description(oc, "pci-dev",
141         "PCI device to associate with the node");
142     object_class_property_add(oc, "node", "int", NULL,
143         acpi_generic_initiator_set_node, NULL, NULL);
144     object_class_property_set_description(oc, "node",
145         "NUMA node associated with the PCI device");
146 }
147 
build_acpi_generic_initiator(Object * obj,void * opaque)148 static int build_acpi_generic_initiator(Object *obj, void *opaque)
149 {
150     MachineState *ms = MACHINE(qdev_get_machine());
151     AcpiGenericInitiator *gi;
152     GArray *table_data = opaque;
153     int32_t devfn;
154     uint8_t bus;
155     Object *o;
156 
157     if (!object_dynamic_cast(obj, TYPE_ACPI_GENERIC_INITIATOR)) {
158         return 0;
159     }
160 
161     gi = ACPI_GENERIC_INITIATOR(obj);
162     if (gi->node >= ms->numa_state->num_nodes) {
163         error_printf("%s: Specified node %d is invalid.\n",
164                      TYPE_ACPI_GENERIC_INITIATOR, gi->node);
165         exit(1);
166     }
167 
168     o = object_resolve_path_type(gi->pci_dev, TYPE_PCI_DEVICE, NULL);
169     if (!o) {
170         error_printf("%s: Specified device must be a PCI device.\n",
171                      TYPE_ACPI_GENERIC_INITIATOR);
172         exit(1);
173     }
174 
175     bus = object_property_get_uint(o, "busnr", &error_fatal);
176     devfn = object_property_get_uint(o, "addr", &error_fatal);
177     /* devfn is constrained in PCI to be 8 bit but storage is an int32_t */
178     assert(devfn >= 0 && devfn < PCI_DEVFN_MAX);
179 
180     build_srat_pci_generic_initiator(table_data, gi->node, 0, bus, devfn);
181 
182     return 0;
183 }
184 
185 typedef struct AcpiGenericPort {
186     /* private */
187     Object parent;
188 
189     /* public */
190     char *pci_bus;
191     uint32_t node;
192 } AcpiGenericPort;
193 
194 typedef struct AcpiGenericPortClass {
195     ObjectClass parent_class;
196 } AcpiGenericPortClass;
197 
198 #define TYPE_ACPI_GENERIC_PORT "acpi-generic-port"
199 
200 OBJECT_DEFINE_TYPE_WITH_INTERFACES(AcpiGenericPort, acpi_generic_port,
201                    ACPI_GENERIC_PORT, OBJECT,
202                    { TYPE_USER_CREATABLE },
203                    { NULL })
204 
OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericPort,ACPI_GENERIC_PORT)205 OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericPort, ACPI_GENERIC_PORT)
206 
207 static void acpi_generic_port_init(Object *obj)
208 {
209     AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj);
210 
211     gp->node = MAX_NODES;
212     gp->pci_bus = NULL;
213 }
214 
acpi_generic_port_finalize(Object * obj)215 static void acpi_generic_port_finalize(Object *obj)
216 {
217     AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj);
218 
219     g_free(gp->pci_bus);
220 }
221 
acpi_generic_port_set_pci_bus(Object * obj,const char * val,Error ** errp)222 static void acpi_generic_port_set_pci_bus(Object *obj, const char *val,
223                                           Error **errp)
224 {
225     AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj);
226 
227     gp->pci_bus = g_strdup(val);
228 }
229 
acpi_generic_port_set_node(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)230 static void acpi_generic_port_set_node(Object *obj, Visitor *v,
231                                        const char *name, void *opaque,
232                                        Error **errp)
233 {
234     AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj);
235     uint32_t value;
236 
237     if (!visit_type_uint32(v, name, &value, errp)) {
238         return;
239     }
240 
241     if (value >= MAX_NODES) {
242         error_printf("%s: Invalid NUMA node specified\n",
243                      TYPE_ACPI_GENERIC_INITIATOR);
244         exit(1);
245     }
246 
247     gp->node = value;
248 }
249 
acpi_generic_port_class_init(ObjectClass * oc,void * data)250 static void acpi_generic_port_class_init(ObjectClass *oc, void *data)
251 {
252     object_class_property_add_str(oc, "pci-bus", NULL,
253         acpi_generic_port_set_pci_bus);
254     object_class_property_set_description(oc, "pci-bus",
255        "PCI Bus of the host bridge associated with this GP affinity structure");
256     object_class_property_add(oc, "node", "int", NULL,
257         acpi_generic_port_set_node, NULL, NULL);
258     object_class_property_set_description(oc, "node",
259        "The NUMA node like ID to index HMAT/SLIT NUMA properties involving GP");
260 }
261 
build_acpi_generic_port(Object * obj,void * opaque)262 static int build_acpi_generic_port(Object *obj, void *opaque)
263 {
264     MachineState *ms = MACHINE(qdev_get_machine());
265     const char *hid = "ACPI0016";
266     GArray *table_data = opaque;
267     AcpiGenericPort *gp;
268     uint32_t uid;
269     Object *o;
270 
271     if (!object_dynamic_cast(obj, TYPE_ACPI_GENERIC_PORT)) {
272         return 0;
273     }
274 
275     gp = ACPI_GENERIC_PORT(obj);
276 
277     if (gp->node >= ms->numa_state->num_nodes) {
278         error_printf("%s: node %d is invalid.\n",
279                      TYPE_ACPI_GENERIC_PORT, gp->node);
280         exit(1);
281     }
282 
283     o = object_resolve_path_type(gp->pci_bus, TYPE_PXB_CXL_BUS, NULL);
284     if (!o) {
285         error_printf("%s: device must be a CXL host bridge.\n",
286                      TYPE_ACPI_GENERIC_PORT);
287        exit(1);
288     }
289 
290     uid = object_property_get_uint(o, "acpi_uid", &error_fatal);
291     build_srat_acpi_generic_port(table_data, gp->node, hid, uid);
292 
293     return 0;
294 }
295 
build_srat_generic_affinity_structures(GArray * table_data)296 void build_srat_generic_affinity_structures(GArray *table_data)
297 {
298     object_child_foreach_recursive(object_get_root(),
299                                    build_acpi_generic_initiator,
300                                    table_data);
301     object_child_foreach_recursive(object_get_root(), build_acpi_generic_port,
302                                    table_data);
303 }
304