1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2023 Oxide Computer Company 14 */ 15 16 /* 17 * This file is focused upon building up the tree of information that we need to 18 * build the module's various topology nodes as well as any methods that need to 19 * operate on them. 20 */ 21 22 #include <sys/fm/protocol.h> 23 #include <fm/topo_hc.h> 24 #include <sys/devfm.h> 25 #include <assert.h> 26 27 #include "topo_zen_impl.h" 28 29 static const topo_pgroup_info_t topo_zen_chip_pgroup = { 30 TOPO_PGROUP_CHIP, 31 TOPO_STABILITY_PRIVATE, 32 TOPO_STABILITY_PRIVATE, 33 1 34 }; 35 36 static const topo_pgroup_info_t topo_zen_ccd_pgroup = { 37 TOPO_PGROUP_CCD, 38 TOPO_STABILITY_PRIVATE, 39 TOPO_STABILITY_PRIVATE, 40 1 41 }; 42 43 static const topo_pgroup_info_t topo_zen_ccx_pgroup = { 44 TOPO_PGROUP_CCX, 45 TOPO_STABILITY_PRIVATE, 46 TOPO_STABILITY_PRIVATE, 47 1 48 }; 49 50 static const topo_pgroup_info_t topo_zen_core_pgroup = { 51 TOPO_PGROUP_CORE, 52 TOPO_STABILITY_PRIVATE, 53 TOPO_STABILITY_PRIVATE, 54 1 55 }; 56 57 static const topo_pgroup_info_t topo_zen_strand_pgroup = { 58 TOPO_PGROUP_STRAND, 59 TOPO_STABILITY_PRIVATE, 60 TOPO_STABILITY_PRIVATE, 61 1 62 }; 63 64 static const topo_pgroup_info_t topo_zen_cache_pgroup = { 65 TOPO_PGROUP_CACHE, 66 TOPO_STABILITY_PRIVATE, 67 TOPO_STABILITY_PRIVATE, 68 1 69 }; 70 71 /* 72 * Common interface to create a topo node in our socket and bind it to its 73 * parent. The following properties are commonly shared between all nodes in the 74 * chip: 75 * 76 * o The serial and revision are part of the FMRI. We don't have a good way to 77 * get the orderable OPN for this. The brand string doesn't feel appropriate 78 * for this use case. 79 * o The FRU is always set to the top-level chip. 80 * o We do not set the ASRU given that it will vary from device to device. 81 */ 82 static tnode_t * 83 topo_zen_create_tn(topo_mod_t *mod, zen_topo_enum_sock_t *sock, tnode_t *pnode, 84 topo_instance_t inst, const char *name) 85 { 86 int ret, err; 87 tnode_t *tn = NULL; 88 nvlist_t *fmri = NULL, *auth = NULL; 89 90 auth = topo_mod_auth(mod, pnode); 91 if (auth == NULL) { 92 topo_mod_dprintf(mod, "failed to get auth for %s[%" PRIu64 "]: " 93 "%s", name, inst, topo_mod_errmsg(mod)); 94 return (NULL); 95 } 96 97 fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, name, inst, 98 NULL, auth, NULL, sock->ztes_cpu_rev, sock->ztes_cpu_serial); 99 if (fmri == NULL) { 100 topo_mod_dprintf(mod, "failed to create FMRI for %s[%" PRIu64 101 "]: %s", name, inst, topo_mod_errmsg(mod)); 102 nvlist_free(auth); 103 return (NULL); 104 } 105 106 tn = topo_node_bind(mod, pnode, name, inst, fmri); 107 nvlist_free(auth); 108 if (tn == NULL) { 109 topo_mod_dprintf(mod, "failed to bind node %s[%" PRIu64 "]: %s", 110 name, inst, topo_mod_errmsg(mod)); 111 nvlist_free(fmri); 112 return (NULL); 113 } 114 115 if (sock->ztes_tn == NULL) { 116 ret = topo_node_fru_set(tn, fmri, 0, &err); 117 } else { 118 ret = topo_node_fru_set(tn, NULL, 0, &err); 119 } 120 nvlist_free(fmri); 121 122 if (ret != 0) { 123 topo_mod_dprintf(mod, "failed to set FRU for %s[%" PRIu64 "]: " 124 "%s", name, inst, topo_strerror(err)); 125 (void) topo_mod_seterrno(mod, err); 126 topo_node_unbind(tn); 127 return (NULL); 128 } 129 130 return (tn); 131 } 132 133 static tnode_t * 134 topo_zen_build_cache(topo_mod_t *mod, zen_topo_enum_sock_t *sock, 135 tnode_t *pnode, topo_instance_t inst, nvlist_t *nvl) 136 { 137 int err; 138 uint32_t level, type, ways, line; 139 uint64_t sets, size, id; 140 const char *types[2]; 141 const char *flags[2]; 142 uint_t ntypes = 0, nflags = 0; 143 144 tnode_t *tn = topo_zen_create_tn(mod, sock, pnode, inst, CACHE); 145 if (tn == NULL) { 146 return (NULL); 147 } 148 149 if (nvlist_lookup_pairs(nvl, 0, 150 FM_CACHE_INFO_LEVEL, DATA_TYPE_UINT32, &level, 151 FM_CACHE_INFO_TYPE, DATA_TYPE_UINT32, &type, 152 FM_CACHE_INFO_NWAYS, DATA_TYPE_UINT32, &ways, 153 FM_CACHE_INFO_LINE_SIZE, DATA_TYPE_UINT32, &line, 154 FM_CACHE_INFO_NSETS, DATA_TYPE_UINT64, &sets, 155 FM_CACHE_INFO_TOTAL_SIZE, DATA_TYPE_UINT64, &size, 156 FM_CACHE_INFO_ID, DATA_TYPE_UINT64, &id, NULL) != 0) { 157 topo_mod_dprintf(mod, "internal cache nvlist missing expected " 158 "keys"); 159 goto err; 160 } 161 162 if ((type & FM_CACHE_INFO_T_DATA) != 0) { 163 types[ntypes] = TOPO_PGROUP_CACHE_TYPES_DATA; 164 ntypes++; 165 } 166 167 if ((type & FM_CACHE_INFO_T_INSTR) != 0) { 168 types[ntypes] = TOPO_PGROUP_CACHE_TYPES_INSTR; 169 ntypes++; 170 } 171 172 if ((type & FM_CACHE_INFO_T_UNIFIED) != 0) { 173 flags[nflags] = TOPO_PGROUP_CACHE_FLAGS_UNIFIED; 174 nflags++; 175 } 176 177 if (nvlist_lookup_boolean(nvl, FM_CACHE_INFO_FULLY_ASSOC) == 0) { 178 flags[nflags] = TOPO_PGROUP_CACHE_FLAGS_FA; 179 nflags++; 180 } 181 182 assert(ntypes > 0); 183 if (topo_create_props(mod, tn, TOPO_PROP_IMMUTABLE, 184 &topo_zen_cache_pgroup, 185 TOPO_PGROUP_CACHE_LEVEL, TOPO_TYPE_UINT32, level, 186 TOPO_PGROUP_CACHE_WAYS, TOPO_TYPE_UINT32, ways, 187 TOPO_PGROUP_CACHE_SETS, TOPO_TYPE_UINT64, sets, 188 TOPO_PGROUP_CACHE_LINE_SIZE, TOPO_TYPE_UINT32, line, 189 TOPO_PGROUP_CACHE_SYSTEM_ID, TOPO_TYPE_UINT64, id, 190 TOPO_PGROUP_CACHE_SIZE, TOPO_TYPE_UINT64, size, 191 TOPO_PGROUP_CACHE_TYPES, TOPO_TYPE_STRING_ARRAY, types, ntypes, 192 NULL) != 0) { 193 goto err; 194 } 195 196 if (nflags > 0 && topo_prop_set_string_array(tn, TOPO_PGROUP_CACHE, 197 TOPO_PGROUP_CACHE_FLAGS, TOPO_PROP_IMMUTABLE, flags, nflags, 198 &err) != 0) { 199 topo_mod_dprintf(mod, "failed to create %s property: %s", 200 TOPO_PGROUP_CACHE_FLAGS, topo_strerror(err)); 201 (void) topo_mod_seterrno(mod, err); 202 goto err; 203 } 204 205 return (tn); 206 207 err: 208 topo_node_unbind(tn); 209 return (NULL); 210 211 } 212 213 /* 214 * Build up an FMRI for the CPU scheme for this thread and set that as our ASRU 215 * for the thread. 216 */ 217 static int 218 topo_zen_build_strand_asru(topo_mod_t *mod, zen_topo_enum_sock_t *sock, 219 tnode_t *tn, uint32_t cpuid) 220 { 221 int err, ret; 222 nvlist_t *fmri; 223 224 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) { 225 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 226 } 227 228 if (nvlist_add_uint8(fmri, FM_VERSION, FM_CPU_SCHEME_VERSION) != 0 || 229 nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU) != 0 || 230 nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, cpuid) != 0 || 231 nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID, 232 sock->ztes_cpu_serial) != 0) { 233 topo_mod_dprintf(mod, "failed to construct CPU FMRI\n"); 234 nvlist_free(fmri); 235 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 236 } 237 238 ret = topo_node_asru_set(tn, fmri, 0, &err); 239 nvlist_free(fmri); 240 if (ret != 0) { 241 topo_mod_dprintf(mod, "failed to set ASRU for thread: %s\n", 242 topo_strerror(err)); 243 return (topo_mod_seterrno(mod, err)); 244 } 245 246 return (0); 247 } 248 249 static int 250 topo_zen_build_strand(topo_mod_t *mod, zen_topo_enum_sock_t *sock, 251 const amdzen_topo_core_t *core, zen_topo_enum_core_t *zt_core, uint32_t tid) 252 { 253 uint32_t cpuid; 254 tnode_t *tn; 255 256 tn = topo_zen_create_tn(mod, sock, zt_core->ztcore_tn, tid, STRAND); 257 if (tn == NULL) { 258 return (-1); 259 } 260 261 /* 262 * Strands (hardware threads) have an ASRU that relates to their logical 263 * CPU. Set that up now. We currently only opt to set it on the strand 264 * because if we want to offline the core, it seems like that needs 265 * better semantics and perhaps wants a better way to indicate that in 266 * the scheme. 267 */ 268 if (nvlist_lookup_pairs(zt_core->ztcore_nvls[tid], 0, 269 FM_PHYSCPU_INFO_CPU_ID, DATA_TYPE_INT32, 270 &cpuid, NULL) != 0) { 271 topo_mod_dprintf(mod, "internal thread %u nvlist " 272 "missing expected keys", tid); 273 topo_node_unbind(tn); 274 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 275 } 276 277 if (topo_zen_build_strand_asru(mod, sock, tn, cpuid) != 0) { 278 topo_node_unbind(tn); 279 return (-1); 280 } 281 282 if (topo_create_props(mod, tn, TOPO_PROP_IMMUTABLE, 283 &topo_zen_strand_pgroup, 284 TOPO_PGROUP_STRAND_CPUID, TOPO_TYPE_UINT32, cpuid, 285 TOPO_PGROUP_STRAND_APICID, TOPO_TYPE_UINT32, 286 core->atcore_apicids[tid], NULL) != 0) { 287 topo_mod_dprintf(mod, "failed to set strand properties\n"); 288 topo_node_unbind(tn); 289 return (-1); 290 } 291 292 zt_core->ztcore_thr_tn[tid] = tn; 293 return (0); 294 } 295 296 static int 297 topo_zen_build_core(topo_mod_t *mod, zen_topo_enum_sock_t *sock, 298 tnode_t *ccx_tn, const amdzen_topo_core_t *core, 299 zen_topo_enum_core_t *zt_core) 300 { 301 zt_core->ztcore_tn = topo_zen_create_tn(mod, sock, ccx_tn, 302 core->atcore_phys_no, CORE); 303 if (zt_core->ztcore_tn == NULL) { 304 return (-1); 305 } 306 307 if (topo_create_props(mod, zt_core->ztcore_tn, TOPO_PROP_IMMUTABLE, 308 &topo_zen_core_pgroup, 309 TOPO_PGROUP_CORE_LOGID, TOPO_TYPE_UINT32, core->atcore_log_no, 310 TOPO_PGROUP_CORE_PHYSID, TOPO_TYPE_UINT32, core->atcore_phys_no, 311 NULL) != 0) { 312 return (-1); 313 } 314 315 if (topo_node_range_create(mod, zt_core->ztcore_tn, CACHE, 0, 2) != 0) { 316 topo_mod_dprintf(mod, "failed to create cache range: %s\n", 317 topo_mod_errmsg(mod)); 318 return (-1); 319 } 320 321 if (zt_core->ztcore_l2 != NULL) { 322 zt_core->ztcore_l2_tn = topo_zen_build_cache(mod, sock, 323 zt_core->ztcore_tn, 2, zt_core->ztcore_l2); 324 if (zt_core->ztcore_l2_tn == NULL) { 325 return (-1); 326 } 327 } 328 329 if (zt_core->ztcore_l1i != NULL) { 330 zt_core->ztcore_l1i_tn = topo_zen_build_cache(mod, sock, 331 zt_core->ztcore_tn, 1, zt_core->ztcore_l1i); 332 if (zt_core->ztcore_l1i_tn == NULL) { 333 return (-1); 334 } 335 } 336 337 if (zt_core->ztcore_l1d != NULL) { 338 zt_core->ztcore_l1d_tn = topo_zen_build_cache(mod, sock, 339 zt_core->ztcore_tn, 0, zt_core->ztcore_l1d); 340 if (zt_core->ztcore_l1d_tn == NULL) { 341 return (-1); 342 } 343 } 344 345 if (topo_node_range_create(mod, zt_core->ztcore_tn, STRAND, 0, 346 core->atcore_nthreads - 1) != 0) { 347 topo_mod_dprintf(mod, "failed to create strand range: %s\n", 348 topo_mod_errmsg(mod)); 349 return (-1); 350 } 351 352 for (uint32_t tid = 0; tid < core->atcore_nthreads; tid++) { 353 int ret; 354 355 if (core->atcore_thr_en[tid] == 0) { 356 continue; 357 } 358 359 if ((ret = topo_zen_build_strand(mod, sock, core, zt_core, 360 tid)) != 0) { 361 return (ret); 362 } 363 } 364 365 return (0); 366 } 367 368 static int 369 topo_zen_build_ccx(topo_mod_t *mod, zen_topo_enum_sock_t *sock, tnode_t *ccd_tn, 370 const amdzen_topo_ccx_t *ccx, zen_topo_enum_ccx_t *zt_ccx) 371 { 372 zt_ccx->ztccx_tn = topo_zen_create_tn(mod, sock, ccd_tn, 373 ccx->atccx_phys_no, CCX); 374 if (zt_ccx->ztccx_tn == NULL) { 375 return (-1); 376 } 377 378 if (topo_create_props(mod, zt_ccx->ztccx_tn, TOPO_PROP_IMMUTABLE, 379 &topo_zen_ccx_pgroup, 380 TOPO_PGROUP_CCX_LOGID, TOPO_TYPE_UINT32, ccx->atccx_log_no, 381 TOPO_PGROUP_CCX_PHYSID, TOPO_TYPE_UINT32, ccx->atccx_phys_no, 382 NULL) != 0) { 383 topo_node_unbind(zt_ccx->ztccx_tn); 384 zt_ccx->ztccx_tn = NULL; 385 return (-1); 386 } 387 388 if (topo_node_range_create(mod, zt_ccx->ztccx_tn, CACHE, 0, 0) != 0) { 389 topo_mod_dprintf(mod, "failed to create cache range: %s\n", 390 topo_mod_errmsg(mod)); 391 topo_node_unbind(zt_ccx->ztccx_tn); 392 zt_ccx->ztccx_tn = NULL; 393 return (-1); 394 } 395 396 if (zt_ccx->ztccx_l3 != NULL) { 397 zt_ccx->ztccx_l3_tn = topo_zen_build_cache(mod, sock, 398 zt_ccx->ztccx_tn, 0, zt_ccx->ztccx_l3); 399 if (zt_ccx->ztccx_l3_tn == NULL) { 400 return (-1); 401 } 402 } 403 404 if (topo_node_range_create(mod, zt_ccx->ztccx_tn, CORE, 0, 405 ccx->atccx_nphys_cores - 1) != 0) { 406 topo_mod_dprintf(mod, "failed to create cores range: %s\n", 407 topo_mod_errmsg(mod)); 408 return (-1); 409 } 410 411 for (uint32_t coreno = 0; coreno < ccx->atccx_nphys_cores; coreno++) { 412 int ret; 413 414 if (ccx->atccx_core_en[coreno] == 0) { 415 topo_mod_dprintf(mod, "skipping core %u\n", coreno); 416 continue; 417 } 418 419 if ((ret = topo_zen_build_core(mod, sock, zt_ccx->ztccx_tn, 420 &ccx->atccx_cores[coreno], &zt_ccx->ztccx_core[coreno])) != 421 0) { 422 return (ret); 423 } 424 } 425 426 return (0); 427 } 428 429 static int 430 topo_zen_build_ccds(topo_mod_t *mod, zen_topo_enum_sock_t *sock) 431 { 432 tnode_t *chip = sock->ztes_tn; 433 434 if (topo_node_range_create(mod, chip, CCD, 0, sock->ztes_nccd - 1) != 435 0) { 436 topo_mod_dprintf(mod, "failed to create CCD range: %s\n", 437 topo_mod_errmsg(mod)); 438 return (-1); 439 } 440 441 for (uint32_t ccdno = 0; ccdno < sock->ztes_nccd; ccdno++) { 442 const amdzen_topo_ccd_t *ccd = &sock->ztes_ccd[ccdno]; 443 zen_topo_enum_ccd_t *zt_ccd = &sock->ztes_tn_ccd[ccdno]; 444 445 /* 446 * Make sure we skip any CCDs that don't actually exist. 447 */ 448 if (ccd->atccd_err != AMDZEN_TOPO_CCD_E_OK) { 449 continue; 450 } 451 452 zt_ccd->ztccd_tn = topo_zen_create_tn(mod, sock, chip, ccdno, 453 CCD); 454 if (zt_ccd->ztccd_tn == NULL) { 455 return (-1); 456 } 457 458 if (topo_create_props(mod, zt_ccd->ztccd_tn, 459 TOPO_PROP_IMMUTABLE, &topo_zen_ccd_pgroup, 460 TOPO_PGROUP_CCD_LOGID, TOPO_TYPE_UINT32, ccd->atccd_log_no, 461 TOPO_PGROUP_CCD_PHYSID, TOPO_TYPE_UINT32, 462 ccd->atccd_phys_no, NULL) != 0) { 463 topo_node_unbind(zt_ccd->ztccd_tn); 464 zt_ccd->ztccd_tn = NULL; 465 return (-1); 466 } 467 468 /* 469 * At this point we should go create any additional sensors 470 * (such as the per-CCD Tctl) and probably set some methods, 471 * etc. 472 */ 473 474 if (topo_node_range_create(mod, zt_ccd->ztccd_tn, CCX, 0, 475 ccd->atccd_nphys_ccx) != 0) { 476 topo_mod_dprintf(mod, "failed to create CCD range: " 477 "%s\n", topo_mod_errmsg(mod)); 478 return (-1); 479 } 480 481 for (uint32_t ccxno = 0; ccxno < ccd->atccd_nphys_ccx; 482 ccxno++) { 483 int ret; 484 485 if (ccd->atccd_ccx_en[ccxno] == 0) { 486 continue; 487 } 488 489 if ((ret = topo_zen_build_ccx(mod, sock, 490 zt_ccd->ztccd_tn, &ccd->atccd_ccx[ccxno], 491 &zt_ccd->ztccd_ccx[ccxno])) != 0) { 492 return (ret); 493 } 494 } 495 } 496 497 return (0); 498 } 499 500 int 501 topo_zen_build_chip(topo_mod_t *mod, tnode_t *pnode, topo_instance_t inst, 502 zen_topo_enum_sock_t *sock) 503 { 504 int ret; 505 tnode_t *chip; 506 507 chip = topo_zen_create_tn(mod, sock, pnode, inst, CHIP); 508 if (chip == NULL) { 509 return (-1); 510 } 511 512 if (topo_create_props(mod, chip, TOPO_PROP_IMMUTABLE, 513 &topo_zen_chip_pgroup, 514 TOPO_PGROUP_CHIP_BRAND, TOPO_TYPE_STRING, sock->ztes_cpu_brand, 515 TOPO_PGROUP_CHIP_FAMILY, TOPO_TYPE_INT32, sock->ztes_cpu_fam, 516 TOPO_PGROUP_CHIP_MODEL, TOPO_TYPE_INT32, sock->ztes_cpu_model, 517 TOPO_PGROUP_CHIP_STEPPING, TOPO_TYPE_INT32, sock->ztes_cpu_step, 518 TOPO_PGROUP_CHIP_SOCKET, TOPO_TYPE_STRING, sock->ztes_cpu_sock, 519 TOPO_PGROUP_CHIP_REVISION, TOPO_TYPE_STRING, sock->ztes_cpu_rev, 520 NULL) != 0) { 521 topo_node_unbind(chip); 522 return (-1); 523 } 524 525 sock->ztes_tn = chip; 526 ret = topo_zen_build_ccds(mod, sock); 527 528 /* 529 * At this point we should flesh out the I/O die and all the UMCs, IOMS 530 * instances, and related. we would put the general thermal sensor that 531 * smntemp exposes as procnode.%u under the I/O die when we have it. 532 */ 533 534 return (ret); 535 } 536