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 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 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 */ 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 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 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 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 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