1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright (c) 2018, Joyent, Inc. 25 * Copyright 2023 Oxide Computer Company 26 */ 27 28 #include <sys/stat.h> 29 #include <sys/types.h> 30 #include <sys/time.h> 31 32 #include <sys/fm/protocol.h> 33 #include <sys/fm/smb/fmsmb.h> 34 #include <sys/devfm.h> 35 36 #include <sys/cpu_module.h> 37 #include <io/amdzen/amdzen_topo.h> 38 39 #define ANY_ID (uint_t)-1 40 41 /* 42 * INIT_HDLS is the initial size of cmi_hdl_t array. We fill the array 43 * during cmi_hdl_walk, if the array overflows, we will reallocate 44 * a new array twice the size of the old one. 45 */ 46 #define INIT_HDLS 16 47 48 typedef struct fm_cmi_walk_t 49 { 50 uint_t chipid; /* chipid to match during walk */ 51 uint_t coreid; /* coreid to match */ 52 uint_t strandid; /* strandid to match */ 53 int (*cbfunc)(cmi_hdl_t, void *, void *); /* callback function */ 54 cmi_hdl_t *hdls; /* allocated array to save the handles */ 55 uint_t nhdl_max; /* allocated array size */ 56 uint_t nhdl; /* handles saved */ 57 } fm_cmi_walk_t; 58 59 extern int x86gentopo_legacy; 60 61 int 62 fm_get_paddr(nvlist_t *nvl, uint64_t *paddr) 63 { 64 uint8_t version; 65 uint64_t pa; 66 char *scheme; 67 int err; 68 69 /* Verify FMRI scheme name and version number */ 70 if ((nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme) != 0) || 71 (strcmp(scheme, FM_FMRI_SCHEME_HC) != 0) || 72 (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0) || 73 version > FM_HC_SCHEME_VERSION) { 74 return (EINVAL); 75 } 76 77 if ((err = cmi_mc_unumtopa(NULL, nvl, &pa)) != CMI_SUCCESS && 78 err != CMIERR_MC_PARTIALUNUMTOPA) 79 return (EINVAL); 80 81 *paddr = pa; 82 return (0); 83 } 84 85 /* 86 * Routines for cmi handles walk. 87 */ 88 89 static void 90 walk_init(fm_cmi_walk_t *wp, uint_t chipid, uint_t coreid, uint_t strandid, 91 int (*cbfunc)(cmi_hdl_t, void *, void *)) 92 { 93 wp->chipid = chipid; 94 wp->coreid = coreid; 95 wp->strandid = strandid; 96 /* 97 * If callback is not set, we allocate an array to save the 98 * cmi handles. 99 */ 100 if ((wp->cbfunc = cbfunc) == NULL) { 101 wp->hdls = kmem_alloc(sizeof (cmi_hdl_t) * INIT_HDLS, KM_SLEEP); 102 wp->nhdl_max = INIT_HDLS; 103 wp->nhdl = 0; 104 } 105 } 106 107 static void 108 walk_fini(fm_cmi_walk_t *wp) 109 { 110 if (wp->cbfunc == NULL) 111 kmem_free(wp->hdls, sizeof (cmi_hdl_t) * wp->nhdl_max); 112 } 113 114 static int 115 select_cmi_hdl(cmi_hdl_t hdl, void *arg1, void *arg2, void *arg3) 116 { 117 fm_cmi_walk_t *wp = (fm_cmi_walk_t *)arg1; 118 119 if (wp->chipid != ANY_ID && wp->chipid != cmi_hdl_chipid(hdl)) 120 return (CMI_HDL_WALK_NEXT); 121 if (wp->coreid != ANY_ID && wp->coreid != cmi_hdl_coreid(hdl)) 122 return (CMI_HDL_WALK_NEXT); 123 if (wp->strandid != ANY_ID && wp->strandid != cmi_hdl_strandid(hdl)) 124 return (CMI_HDL_WALK_NEXT); 125 126 /* 127 * Call the callback function if any exists, otherwise we hold a 128 * reference of the handle and push it to preallocated array. 129 * If the allocated array is going to overflow, reallocate a 130 * bigger one to replace it. 131 */ 132 if (wp->cbfunc != NULL) 133 return (wp->cbfunc(hdl, arg2, arg3)); 134 135 if (wp->nhdl == wp->nhdl_max) { 136 size_t sz = sizeof (cmi_hdl_t) * wp->nhdl_max; 137 cmi_hdl_t *newarray = kmem_alloc(sz << 1, KM_SLEEP); 138 139 bcopy(wp->hdls, newarray, sz); 140 kmem_free(wp->hdls, sz); 141 wp->hdls = newarray; 142 wp->nhdl_max <<= 1; 143 } 144 145 cmi_hdl_hold(hdl); 146 wp->hdls[wp->nhdl++] = hdl; 147 148 return (CMI_HDL_WALK_NEXT); 149 } 150 151 static void 152 populate_cpu(nvlist_t **nvlp, cmi_hdl_t hdl) 153 { 154 uint_t fm_chipid; 155 uint16_t smbios_id; 156 const char *idstr; 157 158 (void) nvlist_alloc(nvlp, NV_UNIQUE_NAME, KM_SLEEP); 159 160 /* 161 * If SMBIOS satisfies FMA Topology needs, gather 162 * more information on the chip's physical roots 163 * like /chassis=x/motherboard=y/cpuboard=z and 164 * set the chip_id to match the SMBIOS' Type 4 165 * ordering & this has to match the ereport's chip 166 * resource instance derived off of SMBIOS. 167 * Multi-Chip-Module support should set the chipid 168 * in terms of the processor package rather than 169 * the die/node in the processor package, for FM. 170 */ 171 172 if (!x86gentopo_legacy) { 173 smbios_id = cmi_hdl_smbiosid(hdl); 174 fm_chipid = cmi_hdl_smb_chipid(hdl); 175 (void) nvlist_add_nvlist(*nvlp, FM_PHYSCPU_INFO_CHIP_ROOTS, 176 cmi_hdl_smb_bboard(hdl)); 177 (void) nvlist_add_uint16(*nvlp, FM_PHYSCPU_INFO_SMBIOS_ID, 178 (uint16_t)smbios_id); 179 } else 180 fm_chipid = cmi_hdl_chipid(hdl); 181 182 fm_payload_set(*nvlp, 183 FM_PHYSCPU_INFO_VENDOR_ID, DATA_TYPE_STRING, 184 cmi_hdl_vendorstr(hdl), 185 FM_PHYSCPU_INFO_FAMILY, DATA_TYPE_INT32, 186 (int32_t)cmi_hdl_family(hdl), 187 FM_PHYSCPU_INFO_MODEL, DATA_TYPE_INT32, 188 (int32_t)cmi_hdl_model(hdl), 189 FM_PHYSCPU_INFO_STEPPING, DATA_TYPE_INT32, 190 (int32_t)cmi_hdl_stepping(hdl), 191 FM_PHYSCPU_INFO_CHIP_ID, DATA_TYPE_INT32, 192 (int32_t)fm_chipid, 193 FM_PHYSCPU_INFO_NPROCNODES, DATA_TYPE_INT32, 194 (int32_t)cmi_hdl_procnodes_per_pkg(hdl), 195 FM_PHYSCPU_INFO_PROCNODE_ID, DATA_TYPE_INT32, 196 (int32_t)cmi_hdl_procnodeid(hdl), 197 FM_PHYSCPU_INFO_CORE_ID, DATA_TYPE_INT32, 198 (int32_t)cmi_hdl_coreid(hdl), 199 FM_PHYSCPU_INFO_STRAND_ID, DATA_TYPE_INT32, 200 (int32_t)cmi_hdl_strandid(hdl), 201 FM_PHYSCPU_INFO_STRAND_APICID, DATA_TYPE_INT32, 202 (int32_t)cmi_hdl_strand_apicid(hdl), 203 FM_PHYSCPU_INFO_CHIP_REV, DATA_TYPE_STRING, 204 cmi_hdl_chiprevstr(hdl), 205 FM_PHYSCPU_INFO_SOCKET_TYPE, DATA_TYPE_UINT32, 206 (uint32_t)cmi_hdl_getsockettype(hdl), 207 FM_PHYSCPU_INFO_CPU_ID, DATA_TYPE_INT32, 208 (int32_t)cmi_hdl_logical_id(hdl), 209 NULL); 210 211 /* 212 * Do this separately so that way if there is no ident string we do not 213 * trigger an error. 214 */ 215 if ((idstr = cmi_hdl_chipident(hdl)) != NULL) { 216 fm_payload_set(*nvlp, 217 FM_PHYSCPU_INFO_CHIP_IDENTSTR, DATA_TYPE_STRING, idstr, 218 NULL); 219 } 220 } 221 222 int 223 fm_ioctl_physcpu_info(int cmd, nvlist_t *invl, nvlist_t **onvlp) 224 { 225 nvlist_t **cpus, *nvl; 226 int i, err; 227 fm_cmi_walk_t wk; 228 229 /* 230 * Do a walk to save all the cmi handles in the array. 231 */ 232 walk_init(&wk, ANY_ID, ANY_ID, ANY_ID, NULL); 233 cmi_hdl_walk(select_cmi_hdl, &wk, NULL, NULL); 234 235 if (wk.nhdl == 0) { 236 walk_fini(&wk); 237 return (ENOENT); 238 } 239 240 cpus = kmem_alloc(sizeof (nvlist_t *) * wk.nhdl, KM_SLEEP); 241 for (i = 0; i < wk.nhdl; i++) { 242 populate_cpu(cpus + i, wk.hdls[i]); 243 cmi_hdl_rele(wk.hdls[i]); 244 } 245 246 walk_fini(&wk); 247 248 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 249 err = nvlist_add_nvlist_array(nvl, FM_PHYSCPU_INFO_CPUS, 250 cpus, wk.nhdl); 251 252 for (i = 0; i < wk.nhdl; i++) 253 nvlist_free(cpus[i]); 254 kmem_free(cpus, sizeof (nvlist_t *) * wk.nhdl); 255 256 if (err != 0) { 257 nvlist_free(nvl); 258 return (err); 259 } 260 261 *onvlp = nvl; 262 return (0); 263 } 264 265 int 266 fm_ioctl_cpu_retire(int cmd, nvlist_t *invl, nvlist_t **onvlp) 267 { 268 int32_t chipid, coreid, strandid; 269 int rc, new_status, old_status; 270 cmi_hdl_t hdl; 271 nvlist_t *nvl; 272 273 switch (cmd) { 274 case FM_IOC_CPU_RETIRE: 275 new_status = P_FAULTED; 276 break; 277 case FM_IOC_CPU_STATUS: 278 new_status = P_STATUS; 279 break; 280 case FM_IOC_CPU_UNRETIRE: 281 new_status = P_ONLINE; 282 break; 283 default: 284 return (ENOTTY); 285 } 286 287 if (nvlist_lookup_int32(invl, FM_CPU_RETIRE_CHIP_ID, &chipid) != 0 || 288 nvlist_lookup_int32(invl, FM_CPU_RETIRE_CORE_ID, &coreid) != 0 || 289 nvlist_lookup_int32(invl, FM_CPU_RETIRE_STRAND_ID, &strandid) != 0) 290 return (EINVAL); 291 292 hdl = cmi_hdl_lookup(CMI_HDL_NEUTRAL, chipid, coreid, strandid); 293 if (hdl == NULL) 294 return (EINVAL); 295 296 rc = cmi_hdl_online(hdl, new_status, &old_status); 297 cmi_hdl_rele(hdl); 298 299 if (rc == 0) { 300 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 301 (void) nvlist_add_int32(nvl, FM_CPU_RETIRE_OLDSTATUS, 302 old_status); 303 *onvlp = nvl; 304 } 305 306 return (rc); 307 } 308 309 /* 310 * Return the value of x86gentopo_legacy variable as an nvpair. 311 * 312 * The caller is responsible for freeing the nvlist. 313 */ 314 int 315 fm_ioctl_gentopo_legacy(int cmd, nvlist_t *invl, nvlist_t **onvlp) 316 { 317 nvlist_t *nvl; 318 319 if (cmd != FM_IOC_GENTOPO_LEGACY) { 320 return (ENOTTY); 321 } 322 323 /* 324 * Inform the caller of the intentions of the ereport generators to 325 * generate either a "generic" or "legacy" x86 topology. 326 */ 327 328 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 329 (void) nvlist_add_int32(nvl, FM_GENTOPO_LEGACY, x86gentopo_legacy); 330 *onvlp = nvl; 331 332 return (0); 333 } 334 335 /* 336 * This is an internal bound on the maximum number of caches that we expect to 337 * encounter to reduce dynamic allocation. 338 */ 339 #define FM_MAX_CACHES 0x10 340 341 static int 342 fm_cmi_cache_err_to_errno(cmi_errno_t cmi) 343 { 344 switch (cmi) { 345 case CMIERR_C_NODATA: 346 return (ENOTSUP); 347 /* 348 * Right now, CMIERR_C_BADCACHENO is explicitly not mapped to EINVAL 349 * (which is what it maps to in cmi_hw.c.). This discrepancy exists 350 * because there's nothing in a user request here that'd end up 351 * resulting in an invalid value, it can only occur because we asked 352 * for a cache that we were told exists, but doesn't actually. If we 353 * returned EINVAL, the user would be wondering what was invalid about 354 * their request. 355 */ 356 case CMIERR_C_BADCACHENO: 357 default: 358 return (EIO); 359 } 360 } 361 362 static int 363 fm_populate_cache(cmi_hdl_t hdl, nvlist_t *nvl, uint_t cpuno) 364 { 365 int ret; 366 cmi_errno_t err; 367 uint32_t ncache; 368 nvlist_t *caches[FM_MAX_CACHES]; 369 char buf[32]; 370 371 err = cmi_cache_ncaches(hdl, &ncache); 372 if (err != CMI_SUCCESS) { 373 return (fm_cmi_cache_err_to_errno(err)); 374 } 375 376 /* 377 * Our promise to userland is that if we skip a value here then there 378 * are no caches. 379 */ 380 if (ncache == 0) { 381 return (0); 382 } else if (ncache > FM_MAX_CACHES) { 383 return (EOVERFLOW); 384 } 385 386 bzero(caches, sizeof (caches)); 387 for (uint32_t i = 0; i < ncache; i++) { 388 x86_cache_t c; 389 fm_cache_info_type_t type = 0; 390 391 (void) nvlist_alloc(&caches[i], NV_UNIQUE_NAME, KM_SLEEP); 392 err = cmi_cache_info(hdl, i, &c); 393 if (err != CMI_SUCCESS) { 394 ret = fm_cmi_cache_err_to_errno(err); 395 goto cleanup; 396 } 397 398 fnvlist_add_uint32(caches[i], FM_CACHE_INFO_LEVEL, c.xc_level); 399 switch (c.xc_type) { 400 case X86_CACHE_TYPE_DATA: 401 type = FM_CACHE_INFO_T_DATA; 402 break; 403 case X86_CACHE_TYPE_INST: 404 type = FM_CACHE_INFO_T_INSTR; 405 break; 406 case X86_CACHE_TYPE_UNIFIED: 407 type = FM_CACHE_INFO_T_DATA | FM_CACHE_INFO_T_INSTR | 408 FM_CACHE_INFO_T_UNIFIED; 409 break; 410 default: 411 break; 412 } 413 fnvlist_add_uint32(caches[i], FM_CACHE_INFO_TYPE, 414 (uint32_t)type); 415 fnvlist_add_uint64(caches[i], FM_CACHE_INFO_NSETS, c.xc_nsets); 416 fnvlist_add_uint32(caches[i], FM_CACHE_INFO_NWAYS, c.xc_nways); 417 fnvlist_add_uint32(caches[i], FM_CACHE_INFO_LINE_SIZE, 418 c.xc_line_size); 419 fnvlist_add_uint64(caches[i], FM_CACHE_INFO_TOTAL_SIZE, 420 c.xc_size); 421 if ((c.xc_flags & X86_CACHE_F_FULL_ASSOC) != 0) { 422 fnvlist_add_boolean(caches[i], 423 FM_CACHE_INFO_FULLY_ASSOC); 424 } 425 fnvlist_add_uint64(caches[i], FM_CACHE_INFO_ID, c.xc_id); 426 fnvlist_add_uint32(caches[i], FM_CACHE_INFO_X86_APIC_SHIFT, 427 c.xc_apic_shift); 428 } 429 430 (void) snprintf(buf, sizeof (buf), "%u", cpuno); 431 fnvlist_add_nvlist_array(nvl, buf, caches, (uint_t)ncache); 432 ret = 0; 433 434 cleanup: 435 for (uint32_t i = 0; i < ncache; i++) { 436 nvlist_free(caches[i]); 437 } 438 return (ret); 439 } 440 441 /* 442 * Gather all of the different per-CPU leaves and return them as a series of 443 * nvlists. 444 */ 445 int 446 fm_ioctl_cache_info(int cmd, nvlist_t *invl, nvlist_t **onvlp) 447 { 448 int ret = 0; 449 fm_cmi_walk_t walk; 450 nvlist_t *nvl; 451 452 if (cmd != FM_IOC_CACHE_INFO) { 453 return (ENOTTY); 454 } 455 456 walk_init(&walk, ANY_ID, ANY_ID, ANY_ID, NULL); 457 cmi_hdl_walk(select_cmi_hdl, &walk, NULL, NULL); 458 if (walk.nhdl == 0) { 459 walk_fini(&walk); 460 return (ENOENT); 461 } 462 463 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 464 fnvlist_add_uint32(nvl, FM_CACHE_INFO_NCPUS, walk.nhdl); 465 466 for (uint_t i = 0; i < walk.nhdl; i++) { 467 if ((ret = fm_populate_cache(walk.hdls[i], nvl, i)) != 0) { 468 break; 469 } 470 cmi_hdl_rele(walk.hdls[i]); 471 } 472 walk_fini(&walk); 473 474 if (ret == 0) { 475 *onvlp = nvl; 476 } else { 477 nvlist_free(nvl); 478 } 479 480 return (ret); 481 } 482 483 /* 484 * For AMD processors, we can ask the amdzen driver for the bus number of the 485 * northbridge of each processor (see the block comment in amdzen.c for more 486 * details) and use this to determine the range of allocated PCI bus numbers. 487 */ 488 static const char *topo_zen_dev = "/devices/pseudo/amdzen@0:topo"; 489 static int 490 fm_physcpu_pci_amd(nvlist_t **onvlp) 491 { 492 extern struct modlinkage devfm_modlinkage; 493 const int ldi_flags = FREAD | FNOCTTY; 494 amdzen_topo_base_t base; 495 nvlist_t *nvl = NULL; 496 ldi_handle_t lh = NULL; 497 ldi_ident_t li; 498 uint32_t ndf = 0; 499 int rval, err = 0; 500 501 VERIFY0(ldi_ident_from_mod(&devfm_modlinkage, &li)); 502 503 err = ldi_open_by_name(topo_zen_dev, ldi_flags, kcred, &lh, li); 504 if (err != 0) { 505 cmn_err(CE_WARN, "!devfm: ldi open of '%s' failed", 506 topo_zen_dev); 507 goto out; 508 } 509 510 err = ldi_ioctl(lh, AMDZEN_TOPO_IOCTL_BASE, (intptr_t)&base, 511 ldi_flags | FKIOCTL, kcred, &rval); 512 if (err != 0) { 513 cmn_err(CE_WARN, "!devfm: failed to get base Zen topology"); 514 goto out; 515 } 516 517 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 518 519 ndf = base.atb_ndf; 520 nvlist_t **dfs = kmem_zalloc(sizeof (nvlist_t *) * ndf, KM_SLEEP); 521 522 for (uint32_t i = 0; i < ndf; i++) { 523 amdzen_topo_df_t df; 524 525 (void) nvlist_alloc(&dfs[i], NV_UNIQUE_NAME, KM_SLEEP); 526 527 df.atd_dfno = i; 528 df.atd_df_buf_nents = 0; 529 df.atd_df_ents = NULL; 530 531 err = ldi_ioctl(lh, AMDZEN_TOPO_IOCTL_DF, (intptr_t)&df, 532 ldi_flags | FKIOCTL, kcred, &rval); 533 if (err != 0) { 534 cmn_err(CE_WARN, "!devfm: failed to get information " 535 "for Zen DF %u - skipping", i); 536 goto out; 537 } 538 539 fm_payload_set(dfs[i], 540 FM_PCI_DATA_CHIP_ID, DATA_TYPE_INT32, 541 (int32_t)df.atd_sockid, 542 FM_PCI_DATA_NB_BUSNO, DATA_TYPE_UINT32, 543 (int32_t)df.atd_nb_busno, 544 NULL); 545 } 546 fnvlist_add_nvlist_array(nvl, FM_PCI_DATA_DFS, 547 dfs, (uint_t)ndf); 548 err = 0; 549 550 out: 551 if (lh != NULL) 552 VERIFY0(ldi_close(lh, ldi_flags, kcred)); 553 ldi_ident_release(li); 554 555 for (uint32_t i = 0; i < ndf; i++) 556 nvlist_free(dfs[i]); 557 558 if (err == 0) 559 *onvlp = nvl; 560 else 561 nvlist_free(nvl); 562 563 return (err); 564 } 565 566 /* 567 * Generate information about the PCI configuration of physical CPUs. This is 568 * intended to be CPU vendor agnostic but is currently only implemented for AMD 569 * processors and returns an array of data fabric information which consists of 570 * the mapping between physical CPUs and their northbridge PCI bus number. The 571 * shape of these data will likely change once support for Intel processors is 572 * added here. 573 */ 574 int 575 fm_ioctl_pci_data(int cmd, nvlist_t *invl __unused, nvlist_t **onvlp) 576 { 577 if (cmd != FM_IOC_PCI_DATA) 578 return (ENOTTY); 579 580 switch (cpuid_getvendor(CPU)) { 581 case X86_VENDOR_AMD: 582 return (fm_physcpu_pci_amd(onvlp)); 583 case X86_VENDOR_Intel: 584 return (ENOTSUP); 585 } 586 587 return (ENOTSUP); 588 } 589