1 /* 2 * CXL host parameter parsing routines 3 * 4 * Copyright (c) 2022 Huawei 5 * Modeled loosely on the NUMA options handling in hw/core/numa.c 6 */ 7 8 #include "qemu/osdep.h" 9 #include "qemu/units.h" 10 #include "qemu/bitmap.h" 11 #include "qemu/error-report.h" 12 #include "qapi/error.h" 13 #include "system/qtest.h" 14 #include "hw/boards.h" 15 16 #include "qapi/qapi-visit-machine.h" 17 #include "hw/cxl/cxl.h" 18 #include "hw/cxl/cxl_host.h" 19 #include "hw/pci/pci_bus.h" 20 #include "hw/pci/pci_bridge.h" 21 #include "hw/pci/pci_host.h" 22 #include "hw/pci/pcie_port.h" 23 #include "hw/pci-bridge/pci_expander_bridge.h" 24 25 static void cxl_fixed_memory_window_config(CXLFixedMemoryWindowOptions *object, 26 int index, Error **errp) 27 { 28 ERRP_GUARD(); 29 DeviceState *dev = qdev_new(TYPE_CXL_FMW); 30 CXLFixedWindow *fw = CXL_FMW(dev); 31 strList *target; 32 int i; 33 34 fw->index = index; 35 36 for (target = object->targets; target; target = target->next) { 37 fw->num_targets++; 38 } 39 40 fw->enc_int_ways = cxl_interleave_ways_enc(fw->num_targets, errp); 41 if (*errp) { 42 return; 43 } 44 45 if (object->size % (256 * MiB)) { 46 error_setg(errp, 47 "Size of a CXL fixed memory window must be a multiple of 256MiB"); 48 return; 49 } 50 fw->size = object->size; 51 52 if (object->has_interleave_granularity) { 53 fw->enc_int_gran = 54 cxl_interleave_granularity_enc(object->interleave_granularity, 55 errp); 56 if (*errp) { 57 return; 58 } 59 } else { 60 /* Default to 256 byte interleave */ 61 fw->enc_int_gran = 0; 62 } 63 64 fw->targets = g_malloc0_n(fw->num_targets, sizeof(*fw->targets)); 65 for (i = 0, target = object->targets; target; i++, target = target->next) { 66 /* This link cannot be resolved yet, so stash the name for now */ 67 fw->targets[i] = g_strdup(target->value); 68 } 69 70 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp); 71 } 72 73 static int cxl_fmws_link(Object *obj, void *opaque) 74 { 75 struct CXLFixedWindow *fw; 76 int i; 77 78 if (!object_dynamic_cast(obj, TYPE_CXL_FMW)) { 79 return 0; 80 } 81 fw = CXL_FMW(obj); 82 83 for (i = 0; i < fw->num_targets; i++) { 84 Object *o; 85 bool ambig; 86 87 o = object_resolve_path_type(fw->targets[i], TYPE_PXB_CXL_DEV, 88 &ambig); 89 if (!o) { 90 error_setg(&error_fatal, "Could not resolve CXLFM target %s", 91 fw->targets[i]); 92 return 1; 93 } 94 fw->target_hbs[i] = PXB_CXL_DEV(o); 95 } 96 return 0; 97 } 98 99 void cxl_fmws_link_targets(Error **errp) 100 { 101 /* Order doesn't matter for this, so no need to build list */ 102 object_child_foreach_recursive(object_get_root(), cxl_fmws_link, NULL); 103 } 104 105 static bool cxl_hdm_find_target(uint32_t *cache_mem, hwaddr addr, 106 uint8_t *target) 107 { 108 int hdm_inc = R_CXL_HDM_DECODER1_BASE_LO - R_CXL_HDM_DECODER0_BASE_LO; 109 unsigned int hdm_count; 110 bool found = false; 111 int i; 112 uint32_t cap; 113 114 cap = ldl_le_p(cache_mem + R_CXL_HDM_DECODER_CAPABILITY); 115 hdm_count = cxl_decoder_count_dec(FIELD_EX32(cap, 116 CXL_HDM_DECODER_CAPABILITY, 117 DECODER_COUNT)); 118 for (i = 0; i < hdm_count; i++) { 119 uint32_t ctrl, ig_enc, iw_enc, target_idx; 120 uint32_t low, high; 121 uint64_t base, size; 122 123 low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_LO + i * hdm_inc); 124 high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_BASE_HI + i * hdm_inc); 125 base = (low & 0xf0000000) | ((uint64_t)high << 32); 126 low = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_LO + i * hdm_inc); 127 high = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_SIZE_HI + i * hdm_inc); 128 size = (low & 0xf0000000) | ((uint64_t)high << 32); 129 if (addr < base || addr >= base + size) { 130 continue; 131 } 132 133 ctrl = ldl_le_p(cache_mem + R_CXL_HDM_DECODER0_CTRL + i * hdm_inc); 134 if (!FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, COMMITTED)) { 135 return false; 136 } 137 found = true; 138 ig_enc = FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, IG); 139 iw_enc = FIELD_EX32(ctrl, CXL_HDM_DECODER0_CTRL, IW); 140 target_idx = (addr / cxl_decode_ig(ig_enc)) % (1 << iw_enc); 141 142 if (target_idx < 4) { 143 uint32_t val = ldl_le_p(cache_mem + 144 R_CXL_HDM_DECODER0_TARGET_LIST_LO + 145 i * hdm_inc); 146 *target = extract32(val, target_idx * 8, 8); 147 } else { 148 uint32_t val = ldl_le_p(cache_mem + 149 R_CXL_HDM_DECODER0_TARGET_LIST_HI + 150 i * hdm_inc); 151 *target = extract32(val, (target_idx - 4) * 8, 8); 152 } 153 break; 154 } 155 156 return found; 157 } 158 159 static PCIDevice *cxl_cfmws_find_device(CXLFixedWindow *fw, hwaddr addr) 160 { 161 CXLComponentState *hb_cstate, *usp_cstate; 162 PCIHostState *hb; 163 CXLUpstreamPort *usp; 164 int rb_index; 165 uint32_t *cache_mem; 166 uint8_t target; 167 bool target_found; 168 PCIDevice *rp, *d; 169 170 /* Address is relative to memory region. Convert to HPA */ 171 addr += fw->base; 172 173 rb_index = (addr / cxl_decode_ig(fw->enc_int_gran)) % fw->num_targets; 174 hb = PCI_HOST_BRIDGE(fw->target_hbs[rb_index]->cxl_host_bridge); 175 if (!hb || !hb->bus || !pci_bus_is_cxl(hb->bus)) { 176 return NULL; 177 } 178 179 if (cxl_get_hb_passthrough(hb)) { 180 rp = pcie_find_port_first(hb->bus); 181 if (!rp) { 182 return NULL; 183 } 184 } else { 185 hb_cstate = cxl_get_hb_cstate(hb); 186 if (!hb_cstate) { 187 return NULL; 188 } 189 190 cache_mem = hb_cstate->crb.cache_mem_registers; 191 192 target_found = cxl_hdm_find_target(cache_mem, addr, &target); 193 if (!target_found) { 194 return NULL; 195 } 196 197 rp = pcie_find_port_by_pn(hb->bus, target); 198 if (!rp) { 199 return NULL; 200 } 201 } 202 203 d = pci_bridge_get_sec_bus(PCI_BRIDGE(rp))->devices[0]; 204 if (!d) { 205 return NULL; 206 } 207 208 if (object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3)) { 209 return d; 210 } 211 212 /* 213 * Could also be a switch. Note only one level of switching currently 214 * supported. 215 */ 216 if (!object_dynamic_cast(OBJECT(d), TYPE_CXL_USP)) { 217 return NULL; 218 } 219 usp = CXL_USP(d); 220 221 usp_cstate = cxl_usp_to_cstate(usp); 222 if (!usp_cstate) { 223 return NULL; 224 } 225 226 cache_mem = usp_cstate->crb.cache_mem_registers; 227 228 target_found = cxl_hdm_find_target(cache_mem, addr, &target); 229 if (!target_found) { 230 return NULL; 231 } 232 233 d = pcie_find_port_by_pn(&PCI_BRIDGE(d)->sec_bus, target); 234 if (!d) { 235 return NULL; 236 } 237 238 d = pci_bridge_get_sec_bus(PCI_BRIDGE(d))->devices[0]; 239 if (!d) { 240 return NULL; 241 } 242 243 if (!object_dynamic_cast(OBJECT(d), TYPE_CXL_TYPE3)) { 244 return NULL; 245 } 246 247 return d; 248 } 249 250 static MemTxResult cxl_read_cfmws(void *opaque, hwaddr addr, uint64_t *data, 251 unsigned size, MemTxAttrs attrs) 252 { 253 CXLFixedWindow *fw = opaque; 254 PCIDevice *d; 255 256 d = cxl_cfmws_find_device(fw, addr); 257 if (d == NULL) { 258 *data = 0; 259 /* Reads to invalid address return poison */ 260 return MEMTX_ERROR; 261 } 262 263 return cxl_type3_read(d, addr + fw->base, data, size, attrs); 264 } 265 266 static MemTxResult cxl_write_cfmws(void *opaque, hwaddr addr, 267 uint64_t data, unsigned size, 268 MemTxAttrs attrs) 269 { 270 CXLFixedWindow *fw = opaque; 271 PCIDevice *d; 272 273 d = cxl_cfmws_find_device(fw, addr); 274 if (d == NULL) { 275 /* Writes to invalid address are silent */ 276 return MEMTX_OK; 277 } 278 279 return cxl_type3_write(d, addr + fw->base, data, size, attrs); 280 } 281 282 const MemoryRegionOps cfmws_ops = { 283 .read_with_attrs = cxl_read_cfmws, 284 .write_with_attrs = cxl_write_cfmws, 285 .endianness = DEVICE_LITTLE_ENDIAN, 286 .valid = { 287 .min_access_size = 1, 288 .max_access_size = 8, 289 .unaligned = true, 290 }, 291 .impl = { 292 .min_access_size = 1, 293 .max_access_size = 8, 294 .unaligned = true, 295 }, 296 }; 297 298 static void machine_get_cxl(Object *obj, Visitor *v, const char *name, 299 void *opaque, Error **errp) 300 { 301 CXLState *cxl_state = opaque; 302 bool value = cxl_state->is_enabled; 303 304 visit_type_bool(v, name, &value, errp); 305 } 306 307 static void machine_set_cxl(Object *obj, Visitor *v, const char *name, 308 void *opaque, Error **errp) 309 { 310 CXLState *cxl_state = opaque; 311 bool value; 312 313 if (!visit_type_bool(v, name, &value, errp)) { 314 return; 315 } 316 cxl_state->is_enabled = value; 317 } 318 319 static void machine_get_cfmw(Object *obj, Visitor *v, const char *name, 320 void *opaque, Error **errp) 321 { 322 CXLState *state = opaque; 323 CXLFixedMemoryWindowOptionsList **list = &state->cfmw_list; 324 325 visit_type_CXLFixedMemoryWindowOptionsList(v, name, list, errp); 326 } 327 328 static void machine_set_cfmw(Object *obj, Visitor *v, const char *name, 329 void *opaque, Error **errp) 330 { 331 CXLState *state = opaque; 332 CXLFixedMemoryWindowOptionsList *cfmw_list = NULL; 333 CXLFixedMemoryWindowOptionsList *it; 334 int index; 335 336 visit_type_CXLFixedMemoryWindowOptionsList(v, name, &cfmw_list, errp); 337 if (!cfmw_list) { 338 return; 339 } 340 341 for (it = cfmw_list, index = 0; it; it = it->next, index++) { 342 cxl_fixed_memory_window_config(it->value, index, errp); 343 } 344 state->cfmw_list = cfmw_list; 345 } 346 347 void cxl_machine_init(Object *obj, CXLState *state) 348 { 349 object_property_add(obj, "cxl", "bool", machine_get_cxl, 350 machine_set_cxl, NULL, state); 351 object_property_set_description(obj, "cxl", 352 "Set on/off to enable/disable " 353 "CXL instantiation"); 354 355 object_property_add(obj, "cxl-fmw", "CXLFixedMemoryWindow", 356 machine_get_cfmw, machine_set_cfmw, 357 NULL, state); 358 object_property_set_description(obj, "cxl-fmw", 359 "CXL Fixed Memory Windows (array)"); 360 } 361 362 void cxl_hook_up_pxb_registers(PCIBus *bus, CXLState *state, Error **errp) 363 { 364 /* Walk the pci busses looking for pxb busses to hook up */ 365 if (bus) { 366 QLIST_FOREACH(bus, &bus->child, sibling) { 367 if (!pci_bus_is_root(bus)) { 368 continue; 369 } 370 if (pci_bus_is_cxl(bus)) { 371 if (!state->is_enabled) { 372 error_setg(errp, "CXL host bridges present, but cxl=off"); 373 return; 374 } 375 pxb_cxl_hook_up_registers(state, bus, errp); 376 } 377 } 378 } 379 } 380 381 static int cxl_fmws_find(Object *obj, void *opaque) 382 { 383 GSList **list = opaque; 384 385 if (!object_dynamic_cast(obj, TYPE_CXL_FMW)) { 386 return 0; 387 } 388 *list = g_slist_prepend(*list, obj); 389 390 return 0; 391 } 392 393 static GSList *cxl_fmws_get_all(void) 394 { 395 GSList *list = NULL; 396 397 object_child_foreach_recursive(object_get_root(), cxl_fmws_find, &list); 398 399 return list; 400 } 401 402 static gint cfmws_cmp(gconstpointer a, gconstpointer b, gpointer d) 403 { 404 const struct CXLFixedWindow *ap = a; 405 const struct CXLFixedWindow *bp = b; 406 407 return ap->index > bp->index; 408 } 409 410 GSList *cxl_fmws_get_all_sorted(void) 411 { 412 return g_slist_sort_with_data(cxl_fmws_get_all(), cfmws_cmp, NULL); 413 } 414 415 static int cxl_fmws_mmio_map(Object *obj, void *opaque) 416 { 417 struct CXLFixedWindow *fw; 418 419 if (!object_dynamic_cast(obj, TYPE_CXL_FMW)) { 420 return 0; 421 } 422 fw = CXL_FMW(obj); 423 sysbus_mmio_map(SYS_BUS_DEVICE(fw), 0, fw->base); 424 425 return 0; 426 } 427 428 void cxl_fmws_update_mmio(void) 429 { 430 /* Ordering is not required for this */ 431 object_child_foreach_recursive(object_get_root(), cxl_fmws_mmio_map, NULL); 432 } 433 434 hwaddr cxl_fmws_set_memmap(hwaddr base, hwaddr max_addr) 435 { 436 GSList *cfmws_list, *iter; 437 CXLFixedWindow *fw; 438 439 cfmws_list = cxl_fmws_get_all_sorted(); 440 for (iter = cfmws_list; iter; iter = iter->next) { 441 fw = CXL_FMW(iter->data); 442 if (base + fw->size <= max_addr) { 443 fw->base = base; 444 base += fw->size; 445 } 446 } 447 g_slist_free(cfmws_list); 448 449 return base; 450 } 451 452 static void cxl_fmw_realize(DeviceState *dev, Error **errp) 453 { 454 CXLFixedWindow *fw = CXL_FMW(dev); 455 456 memory_region_init_io(&fw->mr, OBJECT(dev), &cfmws_ops, fw, 457 "cxl-fixed-memory-region", fw->size); 458 sysbus_init_mmio(SYS_BUS_DEVICE(dev), &fw->mr); 459 } 460 461 /* 462 * Note: Fixed memory windows represent fixed address decoders on the host and 463 * as such have no dynamic state to reset or migrate 464 */ 465 static void cxl_fmw_class_init(ObjectClass *klass, const void *data) 466 { 467 DeviceClass *dc = DEVICE_CLASS(klass); 468 469 dc->desc = "CXL Fixed Memory Window"; 470 dc->realize = cxl_fmw_realize; 471 /* Reason - created by machines as tightly coupled to machine memory map */ 472 dc->user_creatable = false; 473 } 474 475 static const TypeInfo cxl_fmw_info = { 476 .name = TYPE_CXL_FMW, 477 .parent = TYPE_SYS_BUS_DEVICE, 478 .instance_size = sizeof(CXLFixedWindow), 479 .class_init = cxl_fmw_class_init, 480 }; 481 482 static void cxl_host_register_types(void) 483 { 484 type_register_static(&cxl_fmw_info); 485 } 486 type_init(cxl_host_register_types) 487