1 /*
2 * CXL ACPI Implementation
3 *
4 * Copyright(C) 2020 Intel Corporation.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>
18 */
19
20 #include "qemu/osdep.h"
21 #include "hw/sysbus.h"
22 #include "hw/pci/pci_bridge.h"
23 #include "hw/pci/pci_host.h"
24 #include "hw/cxl/cxl.h"
25 #include "hw/mem/memory-device.h"
26 #include "hw/acpi/acpi.h"
27 #include "hw/acpi/aml-build.h"
28 #include "hw/acpi/bios-linker-loader.h"
29 #include "hw/acpi/cxl.h"
30 #include "qapi/error.h"
31 #include "qemu/uuid.h"
32
build_cxl_dsm_method(Aml * dev)33 void build_cxl_dsm_method(Aml *dev)
34 {
35 Aml *method, *ifctx, *ifctx2;
36
37 method = aml_method("_DSM", 4, AML_SERIALIZED);
38 {
39 Aml *function, *uuid;
40
41 uuid = aml_arg(0);
42 function = aml_arg(2);
43 /* CXL spec v3.0 9.17.3.1 _DSM Function for Retrieving QTG ID */
44 ifctx = aml_if(aml_equal(
45 uuid, aml_touuid("F365F9A6-A7DE-4071-A66A-B40C0B4F8E52")));
46
47 /* Function 0, standard DSM query function */
48 ifctx2 = aml_if(aml_equal(function, aml_int(0)));
49 {
50 uint8_t byte_list[1] = { 0x01 }; /* function 1 only */
51
52 aml_append(ifctx2,
53 aml_return(aml_buffer(sizeof(byte_list), byte_list)));
54 }
55 aml_append(ifctx, ifctx2);
56
57 /*
58 * Function 1
59 * Creating a package with static values. The max supported QTG ID will
60 * be 1 and recommended QTG IDs are 0 and then 1.
61 * The values here are statically created to simplify emulation. Values
62 * from a real BIOS would be determined by the performance of all the
63 * present CXL memory and then assigned.
64 */
65 ifctx2 = aml_if(aml_equal(function, aml_int(1)));
66 {
67 Aml *pak, *pak1;
68
69 /*
70 * Return: A package containing two elements - a WORD that returns
71 * the maximum throttling group that the platform supports, and a
72 * package containing the QTG ID(s) that the platform recommends.
73 * Package {
74 * Max Supported QTG ID
75 * Package {QTG Recommendations}
76 * }
77 *
78 * While the SPEC specified WORD that hints at the value being
79 * 16bit, the ACPI dump of BIOS DSDT table showed that the values
80 * are integers with no specific size specification. aml_int() will
81 * be used for the values.
82 */
83 pak1 = aml_package(2);
84 /* Set QTG ID of 0 */
85 aml_append(pak1, aml_int(0));
86 /* Set QTG ID of 1 */
87 aml_append(pak1, aml_int(1));
88
89 pak = aml_package(2);
90 /* Set Max QTG 1 */
91 aml_append(pak, aml_int(1));
92 aml_append(pak, pak1);
93
94 aml_append(ifctx2, aml_return(pak));
95 }
96 aml_append(ifctx, ifctx2);
97 }
98 aml_append(method, ifctx);
99 aml_append(dev, method);
100 }
101
cedt_build_chbs(GArray * table_data,PXBCXLDev * cxl)102 static void cedt_build_chbs(GArray *table_data, PXBCXLDev *cxl)
103 {
104 PXBDev *pxb = PXB_DEV(cxl);
105 SysBusDevice *sbd = SYS_BUS_DEVICE(cxl->cxl_host_bridge);
106 struct MemoryRegion *mr = sbd->mmio[0].memory;
107
108 /* Type */
109 build_append_int_noprefix(table_data, 0, 1);
110
111 /* Reserved */
112 build_append_int_noprefix(table_data, 0, 1);
113
114 /* Record Length */
115 build_append_int_noprefix(table_data, 32, 2);
116
117 /* UID - currently equal to bus number */
118 build_append_int_noprefix(table_data, pxb->bus_nr, 4);
119
120 /* Version */
121 build_append_int_noprefix(table_data, 1, 4);
122
123 /* Reserved */
124 build_append_int_noprefix(table_data, 0, 4);
125
126 /* Base - subregion within a container that is in PA space */
127 build_append_int_noprefix(table_data, mr->container->addr + mr->addr, 8);
128
129 /* Length */
130 build_append_int_noprefix(table_data, memory_region_size(mr), 8);
131 }
132
133 /*
134 * CFMWS entries in CXL 2.0 ECN: CEDT CFMWS & QTG _DSM.
135 * Interleave ways encoding in CXL 2.0 ECN: 3, 6, 12 and 16-way memory
136 * interleaving.
137 */
cedt_build_cfmws(GArray * table_data,CXLState * cxls)138 static void cedt_build_cfmws(GArray *table_data, CXLState *cxls)
139 {
140 GList *it;
141
142 for (it = cxls->fixed_windows; it; it = it->next) {
143 CXLFixedWindow *fw = it->data;
144 int i;
145
146 /* Type */
147 build_append_int_noprefix(table_data, 1, 1);
148
149 /* Reserved */
150 build_append_int_noprefix(table_data, 0, 1);
151
152 /* Record Length */
153 build_append_int_noprefix(table_data, 36 + 4 * fw->num_targets, 2);
154
155 /* Reserved */
156 build_append_int_noprefix(table_data, 0, 4);
157
158 /* Base HPA */
159 build_append_int_noprefix(table_data, fw->mr.addr, 8);
160
161 /* Window Size */
162 build_append_int_noprefix(table_data, fw->size, 8);
163
164 /* Host Bridge Interleave Ways */
165 build_append_int_noprefix(table_data, fw->enc_int_ways, 1);
166
167 /* Host Bridge Interleave Arithmetic */
168 build_append_int_noprefix(table_data, 0, 1);
169
170 /* Reserved */
171 build_append_int_noprefix(table_data, 0, 2);
172
173 /* Host Bridge Interleave Granularity */
174 build_append_int_noprefix(table_data, fw->enc_int_gran, 4);
175
176 /* Window Restrictions */
177 build_append_int_noprefix(table_data, 0x0f, 2); /* No restrictions */
178
179 /* QTG ID */
180 build_append_int_noprefix(table_data, 0, 2);
181
182 /* Host Bridge List (list of UIDs - currently bus_nr) */
183 for (i = 0; i < fw->num_targets; i++) {
184 g_assert(fw->target_hbs[i]);
185 build_append_int_noprefix(table_data, PXB_DEV(fw->target_hbs[i])->bus_nr, 4);
186 }
187 }
188 }
189
cxl_foreach_pxb_hb(Object * obj,void * opaque)190 static int cxl_foreach_pxb_hb(Object *obj, void *opaque)
191 {
192 Aml *cedt = opaque;
193
194 if (object_dynamic_cast(obj, TYPE_PXB_CXL_DEV)) {
195 cedt_build_chbs(cedt->buf, PXB_CXL_DEV(obj));
196 }
197
198 return 0;
199 }
200
cxl_build_cedt(GArray * table_offsets,GArray * table_data,BIOSLinker * linker,const char * oem_id,const char * oem_table_id,CXLState * cxl_state)201 void cxl_build_cedt(GArray *table_offsets, GArray *table_data,
202 BIOSLinker *linker, const char *oem_id,
203 const char *oem_table_id, CXLState *cxl_state)
204 {
205 Aml *cedt;
206 AcpiTable table = { .sig = "CEDT", .rev = 1, .oem_id = oem_id,
207 .oem_table_id = oem_table_id };
208
209 acpi_add_table(table_offsets, table_data);
210 acpi_table_begin(&table, table_data);
211 cedt = init_aml_allocator();
212
213 /* reserve space for CEDT header */
214
215 object_child_foreach_recursive(object_get_root(), cxl_foreach_pxb_hb, cedt);
216 cedt_build_cfmws(cedt->buf, cxl_state);
217
218 /* copy AML table into ACPI tables blob and patch header there */
219 g_array_append_vals(table_data, cedt->buf->data, cedt->buf->len);
220 free_aml_allocator();
221
222 acpi_table_end(linker, &table);
223 }
224
__build_cxl_osc_method(void)225 static Aml *__build_cxl_osc_method(void)
226 {
227 Aml *method, *if_uuid, *else_uuid, *if_arg1_not_1, *if_cxl, *if_caps_masked;
228 Aml *a_ctrl = aml_local(0);
229 Aml *a_cdw1 = aml_name("CDW1");
230
231 method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
232 /* CDW1 is used for the return value so is present whether or not a match occurs */
233 aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
234
235 /*
236 * Generate shared section between:
237 * CXL 2.0 - 9.14.2.1.4 and
238 * PCI Firmware Specification 3.0
239 * 4.5.1. _OSC Interface for PCI Host Bridge Devices
240 * The _OSC interface for a PCI/PCI-X/PCI Express hierarchy is
241 * identified by the Universal Unique IDentifier (UUID)
242 * 33DB4D5B-1FF7-401C-9657-7441C03DD766
243 * The _OSC interface for a CXL Host bridge is
244 * identified by the UUID 68F2D50B-C469-4D8A-BD3D-941A103FD3FC
245 * A CXL Host bridge is compatible with a PCI host bridge so
246 * for the shared section match both.
247 */
248 if_uuid = aml_if(
249 aml_lor(aml_equal(aml_arg(0),
250 aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766")),
251 aml_equal(aml_arg(0),
252 aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC"))));
253 aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2"));
254 aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
255
256 aml_append(if_uuid, aml_store(aml_name("CDW3"), a_ctrl));
257
258 /*
259 *
260 * Allows OS control for all 5 features:
261 * PCIeHotplug SHPCHotplug PME AER PCIeCapability
262 */
263 aml_append(if_uuid, aml_and(a_ctrl, aml_int(0x1F), a_ctrl));
264
265 /*
266 * Check _OSC revision.
267 * PCI Firmware specification 3.3 and CXL 2.0 both use revision 1
268 * Unknown Revision is CDW1 - BIT (3)
269 */
270 if_arg1_not_1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1))));
271 aml_append(if_arg1_not_1, aml_or(a_cdw1, aml_int(0x08), a_cdw1));
272 aml_append(if_uuid, if_arg1_not_1);
273
274 if_caps_masked = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl)));
275
276 /* Capability bits were masked */
277 aml_append(if_caps_masked, aml_or(a_cdw1, aml_int(0x10), a_cdw1));
278 aml_append(if_uuid, if_caps_masked);
279
280 aml_append(if_uuid, aml_store(aml_name("CDW2"), aml_name("SUPP")));
281 aml_append(if_uuid, aml_store(aml_name("CDW3"), aml_name("CTRL")));
282
283 /* Update DWORD3 (the return value) */
284 aml_append(if_uuid, aml_store(a_ctrl, aml_name("CDW3")));
285
286 /* CXL only section as per CXL 2.0 - 9.14.2.1.4 */
287 if_cxl = aml_if(aml_equal(
288 aml_arg(0), aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC")));
289 /* CXL support field */
290 aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(12), "CDW4"));
291 /* CXL capabilities */
292 aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(16), "CDW5"));
293 aml_append(if_cxl, aml_store(aml_name("CDW4"), aml_name("SUPC")));
294 aml_append(if_cxl, aml_store(aml_name("CDW5"), aml_name("CTRC")));
295
296 /* CXL 2.0 Port/Device Register access */
297 aml_append(if_cxl,
298 aml_or(aml_name("CDW5"), aml_int(0x1), aml_name("CDW5")));
299 aml_append(if_uuid, if_cxl);
300
301 aml_append(if_uuid, aml_return(aml_arg(3)));
302 aml_append(method, if_uuid);
303
304 /*
305 * If no UUID matched, return Unrecognized UUID via Arg3 DWord 1
306 * ACPI 6.4 - 6.2.11
307 * Unrecognised UUID - BIT(2)
308 */
309 else_uuid = aml_else();
310
311 aml_append(else_uuid,
312 aml_or(aml_name("CDW1"), aml_int(0x4), aml_name("CDW1")));
313 aml_append(else_uuid, aml_return(aml_arg(3)));
314 aml_append(method, else_uuid);
315
316 return method;
317 }
318
build_cxl_osc_method(Aml * dev)319 void build_cxl_osc_method(Aml *dev)
320 {
321 aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
322 aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
323 aml_append(dev, aml_name_decl("SUPC", aml_int(0)));
324 aml_append(dev, aml_name_decl("CTRC", aml_int(0)));
325 aml_append(dev, __build_cxl_osc_method());
326 }
327