2 * CXL ACPI Implementation
4 * Copyright(C) 2020 Intel Corporation.
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.
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.
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/>
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"
33 void build_cxl_dsm_method(Aml
*dev
)
35 Aml
*method
, *ifctx
, *ifctx2
;
37 method
= aml_method("_DSM", 4, AML_SERIALIZED
);
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")));
47 /* Function 0, standard DSM query function */
48 ifctx2
= aml_if(aml_equal(function
, aml_int(0)));
50 uint8_t byte_list
[1] = { 0x01 }; /* function 1 only */
53 aml_return(aml_buffer(sizeof(byte_list
), byte_list
)));
55 aml_append(ifctx
, ifctx2
);
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.
65 ifctx2
= aml_if(aml_equal(function
, aml_int(1)));
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.
74 * Max Supported QTG ID
75 * Package {QTG Recommendations}
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.
83 pak1
= aml_package(2);
85 aml_append(pak1
, aml_int(0));
87 aml_append(pak1
, aml_int(1));
91 aml_append(pak
, aml_int(1));
92 aml_append(pak
, pak1
);
94 aml_append(ifctx2
, aml_return(pak
));
96 aml_append(ifctx
, ifctx2
);
98 aml_append(method
, ifctx
);
99 aml_append(dev
, method
);
102 static void cedt_build_chbs(GArray
*table_data
, PXBCXLDev
*cxl
)
104 PXBDev
*pxb
= PXB_DEV(cxl
);
105 SysBusDevice
*sbd
= SYS_BUS_DEVICE(cxl
->cxl_host_bridge
);
106 struct MemoryRegion
*mr
= sbd
->mmio
[0].memory
;
109 build_append_int_noprefix(table_data
, 0, 1);
112 build_append_int_noprefix(table_data
, 0, 1);
115 build_append_int_noprefix(table_data
, 32, 2);
117 /* UID - currently equal to bus number */
118 build_append_int_noprefix(table_data
, pxb
->bus_nr
, 4);
121 build_append_int_noprefix(table_data
, 1, 4);
124 build_append_int_noprefix(table_data
, 0, 4);
126 /* Base - subregion within a container that is in PA space */
127 build_append_int_noprefix(table_data
, mr
->container
->addr
+ mr
->addr
, 8);
130 build_append_int_noprefix(table_data
, memory_region_size(mr
), 8);
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
138 static void cedt_build_cfmws(GArray
*table_data
, CXLState
*cxls
)
142 for (it
= cxls
->fixed_windows
; it
; it
= it
->next
) {
143 CXLFixedWindow
*fw
= it
->data
;
147 build_append_int_noprefix(table_data
, 1, 1);
150 build_append_int_noprefix(table_data
, 0, 1);
153 build_append_int_noprefix(table_data
, 36 + 4 * fw
->num_targets
, 2);
156 build_append_int_noprefix(table_data
, 0, 4);
159 build_append_int_noprefix(table_data
, fw
->mr
.addr
, 8);
162 build_append_int_noprefix(table_data
, fw
->size
, 8);
164 /* Host Bridge Interleave Ways */
165 build_append_int_noprefix(table_data
, fw
->enc_int_ways
, 1);
167 /* Host Bridge Interleave Arithmetic */
168 build_append_int_noprefix(table_data
, 0, 1);
171 build_append_int_noprefix(table_data
, 0, 2);
173 /* Host Bridge Interleave Granularity */
174 build_append_int_noprefix(table_data
, fw
->enc_int_gran
, 4);
176 /* Window Restrictions */
177 build_append_int_noprefix(table_data
, 0x0f, 2); /* No restrictions */
180 build_append_int_noprefix(table_data
, 0, 2);
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);
190 static int cxl_foreach_pxb_hb(Object
*obj
, void *opaque
)
194 if (object_dynamic_cast(obj
, TYPE_PXB_CXL_DEV
)) {
195 cedt_build_chbs(cedt
->buf
, PXB_CXL_DEV(obj
));
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
)
206 AcpiTable table
= { .sig
= "CEDT", .rev
= 1, .oem_id
= oem_id
,
207 .oem_table_id
= oem_table_id
};
209 acpi_add_table(table_offsets
, table_data
);
210 acpi_table_begin(&table
, table_data
);
211 cedt
= init_aml_allocator();
213 /* reserve space for CEDT header */
215 object_child_foreach_recursive(object_get_root(), cxl_foreach_pxb_hb
, cedt
);
216 cedt_build_cfmws(cedt
->buf
, cxl_state
);
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();
222 acpi_table_end(linker
, &table
);
225 static Aml
*__build_cxl_osc_method(void)
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");
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"));
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.
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"));
256 aml_append(if_uuid
, aml_store(aml_name("CDW3"), a_ctrl
));
260 * Allows OS control for all 5 features:
261 * PCIeHotplug SHPCHotplug PME AER PCIeCapability
263 aml_append(if_uuid
, aml_and(a_ctrl
, aml_int(0x1F), a_ctrl
));
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)
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
);
274 if_caps_masked
= aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl
)));
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
);
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")));
283 /* Update DWORD3 (the return value) */
284 aml_append(if_uuid
, aml_store(a_ctrl
, aml_name("CDW3")));
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")));
296 /* CXL 2.0 Port/Device Register access */
298 aml_or(aml_name("CDW5"), aml_int(0x1), aml_name("CDW5")));
299 aml_append(if_uuid
, if_cxl
);
301 aml_append(if_uuid
, aml_return(aml_arg(3)));
302 aml_append(method
, if_uuid
);
305 * If no UUID matched, return Unrecognized UUID via Arg3 DWord 1
307 * Unrecognised UUID - BIT(2)
309 else_uuid
= aml_else();
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
);
319 void build_cxl_osc_method(Aml
*dev
)
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());