blob: f88f450af386ff9be9e1d76ab351f4546e0b9191 [file] [log] [blame]
Wei Yangf13a9442019-05-21 14:28:35 +08001/*
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"
Jonathan Cameronf74e7822024-09-16 18:10:11 +010027#include "qemu/error-report.h"
28#include "qom/object_interfaces.h"
29#include "qapi/error.h"
30#include "hw/boards.h"
Wei Yangf13a9442019-05-21 14:28:35 +080031#include "hw/acpi/aml-build.h"
32#include "hw/acpi/pci.h"
Jonathan Camerona82fe822024-09-16 18:41:22 +010033#include "hw/pci/pci_bridge.h"
Jonathan Cameronf74e7822024-09-16 18:10:11 +010034#include "hw/pci/pci_device.h"
Wei Yangf13a9442019-05-21 14:28:35 +080035#include "hw/pci/pcie_host.h"
36
Igor Mammedov578bc7a2021-09-24 08:27:35 -040037/*
38 * PCI Firmware Specification, Revision 3.0
39 * 4.1.2 MCFG Table Description.
40 */
Marian Postevca602b4582021-01-19 02:32:13 +020041void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info,
42 const char *oem_id, const char *oem_table_id)
Wei Yangf13a9442019-05-21 14:28:35 +080043{
Igor Mammedov578bc7a2021-09-24 08:27:35 -040044 AcpiTable table = { .sig = "MCFG", .rev = 1,
45 .oem_id = oem_id, .oem_table_id = oem_table_id };
Wei Yangf13a9442019-05-21 14:28:35 +080046
Igor Mammedov578bc7a2021-09-24 08:27:35 -040047 acpi_table_begin(&table, table_data);
48
Wei Yange4610782019-05-21 14:28:36 +080049 /* Reserved */
50 build_append_int_noprefix(table_data, 0, 8);
Wei Yange4610782019-05-21 14:28:36 +080051 /*
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);
Wei Yangf13a9442019-05-21 14:28:35 +080065
Igor Mammedov578bc7a2021-09-24 08:27:35 -040066 acpi_table_end(linker, &table);
Wei Yangf13a9442019-05-21 14:28:35 +080067}
Jonathan Cameronf74e7822024-09-16 18:10:11 +010068
69typedef struct AcpiGenericInitiator {
70 /* private */
71 Object parent;
72
73 /* public */
74 char *pci_dev;
Jonathan Cameroncf2181a2024-09-16 18:42:37 +010075 uint32_t node;
Jonathan Cameronf74e7822024-09-16 18:10:11 +010076} AcpiGenericInitiator;
77
78typedef struct AcpiGenericInitiatorClass {
79 ObjectClass parent_class;
80} AcpiGenericInitiatorClass;
81
82#define TYPE_ACPI_GENERIC_INITIATOR "acpi-generic-initiator"
83
84OBJECT_DEFINE_TYPE_WITH_INTERFACES(AcpiGenericInitiator, acpi_generic_initiator,
85 ACPI_GENERIC_INITIATOR, OBJECT,
86 { TYPE_USER_CREATABLE },
87 { NULL })
88
89OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericInitiator, ACPI_GENERIC_INITIATOR)
90
91static 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
99static void acpi_generic_initiator_finalize(Object *obj)
100{
101 AcpiGenericInitiator *gi = ACPI_GENERIC_INITIATOR(obj);
102
103 g_free(gi->pci_dev);
104}
105
106static 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
114static 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
136static 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);
Jonathan Camerondf37d492024-09-16 18:43:21 +0100140 object_class_property_set_description(oc, "pci-dev",
141 "PCI device to associate with the node");
Jonathan Cameronf74e7822024-09-16 18:10:11 +0100142 object_class_property_add(oc, "node", "int", NULL,
143 acpi_generic_initiator_set_node, NULL, NULL);
Jonathan Camerondf37d492024-09-16 18:43:21 +0100144 object_class_property_set_description(oc, "node",
145 "NUMA node associated with the PCI device");
Jonathan Cameronf74e7822024-09-16 18:10:11 +0100146}
147
148static 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
Jonathan Camerona82fe822024-09-16 18:41:22 +0100185typedef struct AcpiGenericPort {
186 /* private */
187 Object parent;
188
189 /* public */
190 char *pci_bus;
191 uint32_t node;
192} AcpiGenericPort;
193
194typedef struct AcpiGenericPortClass {
195 ObjectClass parent_class;
196} AcpiGenericPortClass;
197
198#define TYPE_ACPI_GENERIC_PORT "acpi-generic-port"
199
200OBJECT_DEFINE_TYPE_WITH_INTERFACES(AcpiGenericPort, acpi_generic_port,
201 ACPI_GENERIC_PORT, OBJECT,
202 { TYPE_USER_CREATABLE },
203 { NULL })
204
205OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericPort, ACPI_GENERIC_PORT)
206
207static 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
215static void acpi_generic_port_finalize(Object *obj)
216{
217 AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj);
218
219 g_free(gp->pci_bus);
220}
221
222static 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
230static 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
250static 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
262static 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
296void build_srat_generic_affinity_structures(GArray *table_data)
Jonathan Cameronf74e7822024-09-16 18:10:11 +0100297{
298 object_child_foreach_recursive(object_get_root(),
299 build_acpi_generic_initiator,
300 table_data);
Jonathan Camerona82fe822024-09-16 18:41:22 +0100301 object_child_foreach_recursive(object_get_root(), build_acpi_generic_port,
302 table_data);
Jonathan Cameronf74e7822024-09-16 18:10:11 +0100303}