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