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 static void cedt_build_chbs(GArray *table_data, PXBDev *cxl) 34 { 35 SysBusDevice *sbd = SYS_BUS_DEVICE(cxl->cxl.cxl_host_bridge); 36 struct MemoryRegion *mr = sbd->mmio[0].memory; 37 38 /* Type */ 39 build_append_int_noprefix(table_data, 0, 1); 40 41 /* Reserved */ 42 build_append_int_noprefix(table_data, 0, 1); 43 44 /* Record Length */ 45 build_append_int_noprefix(table_data, 32, 2); 46 47 /* UID - currently equal to bus number */ 48 build_append_int_noprefix(table_data, cxl->bus_nr, 4); 49 50 /* Version */ 51 build_append_int_noprefix(table_data, 1, 4); 52 53 /* Reserved */ 54 build_append_int_noprefix(table_data, 0, 4); 55 56 /* Base - subregion within a container that is in PA space */ 57 build_append_int_noprefix(table_data, mr->container->addr + mr->addr, 8); 58 59 /* Length */ 60 build_append_int_noprefix(table_data, memory_region_size(mr), 8); 61 } 62 63 /* 64 * CFMWS entries in CXL 2.0 ECN: CEDT CFMWS & QTG _DSM. 65 * Interleave ways encoding in CXL 2.0 ECN: 3, 6, 12 and 16-way memory 66 * interleaving. 67 */ 68 static void cedt_build_cfmws(GArray *table_data, MachineState *ms) 69 { 70 CXLState *cxls = ms->cxl_devices_state; 71 GList *it; 72 73 for (it = cxls->fixed_windows; it; it = it->next) { 74 CXLFixedWindow *fw = it->data; 75 int i; 76 77 /* Type */ 78 build_append_int_noprefix(table_data, 1, 1); 79 80 /* Reserved */ 81 build_append_int_noprefix(table_data, 0, 1); 82 83 /* Record Length */ 84 build_append_int_noprefix(table_data, 36 + 4 * fw->num_targets, 2); 85 86 /* Reserved */ 87 build_append_int_noprefix(table_data, 0, 4); 88 89 /* Base HPA */ 90 build_append_int_noprefix(table_data, fw->mr.addr, 8); 91 92 /* Window Size */ 93 build_append_int_noprefix(table_data, fw->size, 8); 94 95 /* Host Bridge Interleave Ways */ 96 build_append_int_noprefix(table_data, fw->enc_int_ways, 1); 97 98 /* Host Bridge Interleave Arithmetic */ 99 build_append_int_noprefix(table_data, 0, 1); 100 101 /* Reserved */ 102 build_append_int_noprefix(table_data, 0, 2); 103 104 /* Host Bridge Interleave Granularity */ 105 build_append_int_noprefix(table_data, fw->enc_int_gran, 4); 106 107 /* Window Restrictions */ 108 build_append_int_noprefix(table_data, 0x0f, 2); /* No restrictions */ 109 110 /* QTG ID */ 111 build_append_int_noprefix(table_data, 0, 2); 112 113 /* Host Bridge List (list of UIDs - currently bus_nr) */ 114 for (i = 0; i < fw->num_targets; i++) { 115 g_assert(fw->target_hbs[i]); 116 build_append_int_noprefix(table_data, fw->target_hbs[i]->bus_nr, 4); 117 } 118 } 119 } 120 121 static int cxl_foreach_pxb_hb(Object *obj, void *opaque) 122 { 123 Aml *cedt = opaque; 124 125 if (object_dynamic_cast(obj, TYPE_PXB_CXL_DEVICE)) { 126 cedt_build_chbs(cedt->buf, PXB_CXL_DEV(obj)); 127 } 128 129 return 0; 130 } 131 132 void cxl_build_cedt(MachineState *ms, GArray *table_offsets, GArray *table_data, 133 BIOSLinker *linker, const char *oem_id, 134 const char *oem_table_id) 135 { 136 Aml *cedt; 137 AcpiTable table = { .sig = "CEDT", .rev = 1, .oem_id = oem_id, 138 .oem_table_id = oem_table_id }; 139 140 acpi_add_table(table_offsets, table_data); 141 acpi_table_begin(&table, table_data); 142 cedt = init_aml_allocator(); 143 144 /* reserve space for CEDT header */ 145 146 object_child_foreach_recursive(object_get_root(), cxl_foreach_pxb_hb, cedt); 147 cedt_build_cfmws(cedt->buf, ms); 148 149 /* copy AML table into ACPI tables blob and patch header there */ 150 g_array_append_vals(table_data, cedt->buf->data, cedt->buf->len); 151 free_aml_allocator(); 152 153 acpi_table_end(linker, &table); 154 } 155 156 static Aml *__build_cxl_osc_method(void) 157 { 158 Aml *method, *if_uuid, *else_uuid, *if_arg1_not_1, *if_cxl, *if_caps_masked; 159 Aml *a_ctrl = aml_local(0); 160 Aml *a_cdw1 = aml_name("CDW1"); 161 162 method = aml_method("_OSC", 4, AML_NOTSERIALIZED); 163 /* CDW1 is used for the return value so is present whether or not a match occurs */ 164 aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); 165 166 /* 167 * Generate shared section between: 168 * CXL 2.0 - 9.14.2.1.4 and 169 * PCI Firmware Specification 3.0 170 * 4.5.1. _OSC Interface for PCI Host Bridge Devices 171 * The _OSC interface for a PCI/PCI-X/PCI Express hierarchy is 172 * identified by the Universal Unique IDentifier (UUID) 173 * 33DB4D5B-1FF7-401C-9657-7441C03DD766 174 * The _OSC interface for a CXL Host bridge is 175 * identified by the UUID 68F2D50B-C469-4D8A-BD3D-941A103FD3FC 176 * A CXL Host bridge is compatible with a PCI host bridge so 177 * for the shared section match both. 178 */ 179 if_uuid = aml_if( 180 aml_lor(aml_equal(aml_arg(0), 181 aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766")), 182 aml_equal(aml_arg(0), 183 aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC")))); 184 aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2")); 185 aml_append(if_uuid, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); 186 187 aml_append(if_uuid, aml_store(aml_name("CDW3"), a_ctrl)); 188 189 /* 190 * 191 * Allows OS control for all 5 features: 192 * PCIeHotplug SHPCHotplug PME AER PCIeCapability 193 */ 194 aml_append(if_uuid, aml_and(a_ctrl, aml_int(0x1F), a_ctrl)); 195 196 /* 197 * Check _OSC revision. 198 * PCI Firmware specification 3.3 and CXL 2.0 both use revision 1 199 * Unknown Revision is CDW1 - BIT (3) 200 */ 201 if_arg1_not_1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1)))); 202 aml_append(if_arg1_not_1, aml_or(a_cdw1, aml_int(0x08), a_cdw1)); 203 aml_append(if_uuid, if_arg1_not_1); 204 205 if_caps_masked = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl))); 206 207 /* Capability bits were masked */ 208 aml_append(if_caps_masked, aml_or(a_cdw1, aml_int(0x10), a_cdw1)); 209 aml_append(if_uuid, if_caps_masked); 210 211 aml_append(if_uuid, aml_store(aml_name("CDW2"), aml_name("SUPP"))); 212 aml_append(if_uuid, aml_store(aml_name("CDW3"), aml_name("CTRL"))); 213 214 /* Update DWORD3 (the return value) */ 215 aml_append(if_uuid, aml_store(a_ctrl, aml_name("CDW3"))); 216 217 /* CXL only section as per CXL 2.0 - 9.14.2.1.4 */ 218 if_cxl = aml_if(aml_equal( 219 aml_arg(0), aml_touuid("68F2D50B-C469-4D8A-BD3D-941A103FD3FC"))); 220 /* CXL support field */ 221 aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(12), "CDW4")); 222 /* CXL capabilities */ 223 aml_append(if_cxl, aml_create_dword_field(aml_arg(3), aml_int(16), "CDW5")); 224 aml_append(if_cxl, aml_store(aml_name("CDW4"), aml_name("SUPC"))); 225 aml_append(if_cxl, aml_store(aml_name("CDW5"), aml_name("CTRC"))); 226 227 /* CXL 2.0 Port/Device Register access */ 228 aml_append(if_cxl, 229 aml_or(aml_name("CDW5"), aml_int(0x1), aml_name("CDW5"))); 230 aml_append(if_uuid, if_cxl); 231 232 aml_append(if_uuid, aml_return(aml_arg(3))); 233 aml_append(method, if_uuid); 234 235 /* 236 * If no UUID matched, return Unrecognized UUID via Arg3 DWord 1 237 * ACPI 6.4 - 6.2.11 238 * Unrecognised UUID - BIT(2) 239 */ 240 else_uuid = aml_else(); 241 242 aml_append(else_uuid, 243 aml_or(aml_name("CDW1"), aml_int(0x4), aml_name("CDW1"))); 244 aml_append(else_uuid, aml_return(aml_arg(3))); 245 aml_append(method, else_uuid); 246 247 return method; 248 } 249 250 void build_cxl_osc_method(Aml *dev) 251 { 252 aml_append(dev, aml_name_decl("SUPP", aml_int(0))); 253 aml_append(dev, aml_name_decl("CTRL", aml_int(0))); 254 aml_append(dev, aml_name_decl("SUPC", aml_int(0))); 255 aml_append(dev, aml_name_decl("CTRC", aml_int(0))); 256 aml_append(dev, __build_cxl_osc_method()); 257 } 258