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