xref: /openbmc/qemu/hw/acpi/cxl.c (revision 2df1eb27)
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 
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 
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  */
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 
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 
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 
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 
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