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 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <unistd.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <stdarg.h> 33 #include <string.h> 34 #include <strings.h> 35 #include <limits.h> 36 #include <alloca.h> 37 #include <kstat.h> 38 #include <fcntl.h> 39 #include <errno.h> 40 #include <libnvpair.h> 41 #include <sys/types.h> 42 #include <sys/bitmap.h> 43 #include <sys/processor.h> 44 #include <sys/param.h> 45 #include <sys/fm/protocol.h> 46 #include <sys/systeminfo.h> 47 #include <sys/mc.h> 48 #include <sys/mc_amd.h> 49 #include <fm/topo_mod.h> 50 51 #include "chip.h" 52 53 #ifndef MAX 54 #define MAX(a, b) ((a) > (b) ? (a) : (b)) 55 #endif 56 57 #define MAX_DIMMNUM 7 58 #define MAX_CSNUM 7 59 60 /* 61 * Enumerates the processing chips, or sockets, (as distinct from cores) in a 62 * system. For each chip found, the necessary nodes (one or more cores, and 63 * possibly a memory controller) are constructed underneath. 64 */ 65 66 static int chip_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 67 topo_instance_t, void *); 68 69 static int mem_asru_compute(topo_mod_t *, tnode_t *, topo_version_t, 70 nvlist_t *, nvlist_t **); 71 72 const topo_modinfo_t chip_info = 73 { "chip", CHIP_VERSION, chip_enum, NULL}; 74 75 const topo_method_t rank_methods[] = { 76 { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC, 77 TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, 78 mem_asru_compute }, 79 { NULL } 80 }; 81 82 static const struct debugopt { 83 const char *optname; 84 int optval; 85 } debugopts[] = { 86 { "err", TOPO_DBG_ERR }, 87 { "mod", TOPO_DBG_MOD }, 88 { "log", TOPO_DBG_LOG }, 89 { "walk", TOPO_DBG_WALK }, 90 { "tree", TOPO_DBG_TREE }, 91 { "all", TOPO_DBG_ALL } 92 }; 93 94 static nvlist_t *cs_fmri[MC_CHIP_NCS]; 95 96 static void 97 whinge(topo_mod_t *mod, int *nerr, const char *fmt, ...) 98 { 99 va_list ap; 100 char buf[160]; 101 102 if (nerr != NULL) 103 ++*nerr; 104 105 va_start(ap, fmt); 106 (void) vsnprintf(buf, sizeof (buf), fmt, ap); 107 va_end(ap); 108 109 topo_mod_dprintf(mod, "%s", buf); 110 } 111 112 int 113 _topo_init(topo_mod_t *mod) 114 { 115 const char *debugstr = getenv("TOPOCHPDBG"); 116 chip_t *chip; 117 int i; 118 119 if (debugstr != NULL) { 120 for (i = 0; i < sizeof (debugopts) / sizeof (struct debugopt); 121 i++) { 122 if (strncmp(debugstr, debugopts[i].optname, 4) == 0) { 123 topo_mod_clrdebug(mod); 124 topo_mod_setdebug(mod, debugopts[i].optval); 125 break; /* handle a single option only */ 126 } 127 } 128 } 129 130 topo_mod_dprintf(mod, "initializing chip enumerator\n"); 131 132 if ((chip = topo_mod_zalloc(mod, sizeof (chip_t))) == NULL) 133 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 134 135 if ((chip->chip_kc = kstat_open()) == NULL) { 136 whinge(mod, NULL, "kstat_open failed: %s\n", 137 strerror(errno)); 138 topo_mod_free(mod, chip, sizeof (chip_t)); 139 return (topo_mod_seterrno(mod, errno)); 140 } 141 142 chip->chip_ncpustats = sysconf(_SC_CPUID_MAX); 143 if ((chip->chip_cpustats = topo_mod_zalloc(mod, ( 144 chip->chip_ncpustats + 1) * sizeof (kstat_t *))) == NULL) { 145 (void) kstat_close(chip->chip_kc); 146 topo_mod_free(mod, chip, sizeof (chip_t)); 147 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 148 } 149 150 if (topo_mod_register(mod, &chip_info, (void *)chip) != 0) { 151 whinge(mod, NULL, "failed to register hc: " 152 "%s\n", topo_mod_errmsg(mod)); 153 topo_mod_free(mod, chip->chip_cpustats, 154 (chip->chip_ncpustats + 1) * sizeof (kstat_t *)); 155 (void) kstat_close(chip->chip_kc); 156 topo_mod_free(mod, chip, sizeof (chip_t)); 157 return (-1); /* mod errno set */ 158 } 159 160 return (0); 161 } 162 163 void 164 _topo_fini(topo_mod_t *mod) 165 { 166 chip_t *chip = topo_mod_private(mod); 167 168 if (chip->chip_cpustats != NULL) 169 topo_mod_free(mod, chip->chip_cpustats, 170 (chip->chip_ncpustats + 1) * sizeof (kstat_t *)); 171 172 (void) kstat_close(chip->chip_kc); 173 topo_mod_free(mod, chip, sizeof (chip_t)); 174 175 topo_mod_unregister(mod); 176 } 177 178 static int 179 chip_strprop(tnode_t *cnode, kstat_t *ksp, const char *name) 180 { 181 int err; 182 kstat_named_t *k; 183 184 if ((k = kstat_data_lookup(ksp, (char *)name)) == NULL) 185 return (0); 186 187 (void) topo_prop_set_string(cnode, CHIP_PGROUP, name, 188 TOPO_PROP_SET_ONCE, k->value.str.addr.ptr, &err); 189 190 return (-1); 191 } 192 193 static int 194 chip_longprop(tnode_t *cnode, kstat_t *ksp, const char *name) 195 { 196 int err; 197 kstat_named_t *k; 198 199 if ((k = kstat_data_lookup(ksp, (char *)name)) == NULL) 200 return (0); 201 202 (void) topo_prop_set_int32(cnode, CHIP_PGROUP, name, TOPO_PROP_SET_ONCE, 203 k->value.l, &err); 204 205 return (-1); 206 } 207 208 static int 209 mkrsrc(topo_mod_t *mod, tnode_t *pnode, const char *name, int inst, 210 nvlist_t **nvl) 211 { 212 nvlist_t *args = NULL, *pfmri = NULL; 213 topo_hdl_t *thp = topo_mod_handle(mod); 214 int err; 215 216 if (topo_node_resource(pnode, &pfmri, &err) < 0 || 217 topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0 || 218 nvlist_add_nvlist(args, TOPO_METH_FMRI_ARG_PARENT, pfmri) != 0) { 219 nvlist_free(pfmri); 220 nvlist_free(args); 221 return (-1); 222 } 223 224 *nvl = topo_fmri_create(thp, FM_FMRI_SCHEME_HC, name, inst, args, &err); 225 nvlist_free(pfmri); 226 nvlist_free(args); 227 228 return (nvl != NULL ? 0 : -1); /* caller must free nvlist */ 229 } 230 231 static nvlist_t * 232 cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask) 233 { 234 int err; 235 nvlist_t *asru; 236 237 if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0) 238 return (NULL); 239 240 err = nvlist_add_uint8(asru, FM_VERSION, FM_CPU_SCHEME_VERSION); 241 err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU); 242 err |= nvlist_add_uint32(asru, FM_FMRI_CPU_ID, cpuid); 243 err |= nvlist_add_uint8(asru, FM_FMRI_CPU_MASK, cpumask); 244 if (s != NULL) 245 err |= nvlist_add_string(asru, FM_FMRI_CPU_SERIAL_ID, s); 246 if (err != 0) { 247 nvlist_free(asru); 248 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL); 249 return (NULL); 250 } 251 252 return (asru); 253 } 254 255 static nvlist_t * 256 mem_fmri_create(topo_mod_t *mod) 257 { 258 nvlist_t *asru; 259 260 if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0) 261 return (NULL); 262 263 if (nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0 || 264 nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION) != 0) { 265 nvlist_free(asru); 266 return (NULL); 267 } 268 269 return (asru); 270 } 271 272 static int 273 cpu_create(topo_mod_t *mod, tnode_t *pnode, const char *name, int chipid, 274 chip_t *chip) 275 { 276 kstat_named_t *k; 277 nvlist_t *fmri, *asru; 278 tnode_t *cnode; 279 int err, nerr = 0; 280 int clogid, cpuid; 281 282 if (topo_node_range_create(mod, pnode, name, 0, 283 chip->chip_ncpustats) < 0) 284 return (-1); 285 286 for (cpuid = 0; cpuid <= chip->chip_ncpustats; cpuid++) { 287 if (chip->chip_cpustats[cpuid] == NULL) 288 continue; 289 290 /* 291 * The chip_id in the cpu_info kstat numbers the individual 292 * chips from 0 to #chips - 1. 293 */ 294 if ((k = kstat_data_lookup(chip->chip_cpustats[cpuid], 295 "chip_id")) == NULL) { 296 whinge(mod, &nerr, "cpu_create: chip_id lookup via " 297 "kstats failed\n"); 298 continue; 299 } 300 301 if (k->value.l != chipid) 302 continue; /* not an error */ 303 304 /* 305 * The clog_id in the cpu_info kstat numbers the virtual 306 * processors of a single chip; these may be separate 307 * processor cores, or they may be hardware threads/strands 308 * of individual cores. 309 * 310 * The core_id in the cpu_info kstat tells us which cpus 311 * share the same core - i.e., are hardware strands of the 312 * same core. This enumerator does not distinguish stranded 313 * cores so core_id is unused. 314 */ 315 if ((k = kstat_data_lookup(chip->chip_cpustats[cpuid], 316 "clog_id")) == NULL) { 317 whinge(mod, &nerr, "cpu_create: clog_id lookup via " 318 "kstats failed\n"); 319 continue; 320 } 321 clogid = k->value.l; 322 323 if (mkrsrc(mod, pnode, name, clogid, &fmri) != 0) { 324 whinge(mod, &nerr, "cpu_create: mkrsrc failed\n"); 325 continue; 326 } 327 328 if ((cnode = topo_node_bind(mod, pnode, name, clogid, fmri, 329 NULL)) == NULL) { 330 whinge(mod, &nerr, "cpu_create: node bind failed\n"); 331 nvlist_free(fmri); 332 continue; 333 } 334 nvlist_free(fmri); 335 336 if ((asru = cpu_fmri_create(mod, cpuid, NULL, 0)) != NULL) { 337 (void) topo_node_asru_set(cnode, asru, 0, &err); 338 nvlist_free(asru); 339 } else { 340 whinge(mod, &nerr, "cpu_create: cpu_fmri_create " 341 "failed\n"); 342 } 343 (void) topo_node_fru_set(cnode, NULL, 0, &err); 344 } 345 346 return (nerr == 0 ? 0 : -1); 347 } 348 349 static int 350 nvprop_add(topo_mod_t *mod, nvpair_t *nvp, const char *pgname, tnode_t *node) 351 { 352 int err = 0; 353 char *pname = nvpair_name(nvp); 354 355 switch (nvpair_type(nvp)) { 356 case DATA_TYPE_BOOLEAN_VALUE: { 357 boolean_t val; 358 359 if (nvpair_value_boolean_value(nvp, &val) == 0) { 360 (void) topo_prop_set_string(node, pgname, pname, 361 TOPO_PROP_SET_ONCE, (val ? "true" : "false"), &err); 362 } 363 return (0); 364 } 365 366 case DATA_TYPE_UINT64: { 367 uint64_t val; 368 369 if (nvpair_value_uint64(nvp, &val) == 0) { 370 (void) topo_prop_set_uint64(node, pgname, pname, 371 TOPO_PROP_SET_ONCE, val, &err); 372 } 373 return (0); 374 } 375 376 case DATA_TYPE_STRING: { 377 char *str; 378 379 if (nvpair_value_string(nvp, &str) == 0) 380 (void) topo_prop_set_string(node, pgname, pname, 381 TOPO_PROP_SET_ONCE, str, &err); 382 return (0); 383 } 384 385 default: 386 whinge(mod, &err, "nvprop_add: Can't handle type %d for " 387 "'%s' in property group %s of %s node\n", 388 nvpair_type(nvp), pname, pgname, topo_node_name(node)); 389 return (1); 390 } 391 } 392 393 static int 394 dramchan_create(topo_mod_t *mod, tnode_t *pnode, const char *name) 395 { 396 tnode_t *chnode; 397 nvlist_t *fmri; 398 char *socket; 399 int i, nchan; 400 int err, nerr = 0; 401 402 /* 403 * We will enumerate the number of channels present even if only 404 * channel A is in use (i.e., running in 64-bit mode). Only 405 * the socket 754 package has a single channel. 406 */ 407 if (topo_prop_get_string(pnode, MCT_PGROUP, "socket", 408 &socket, &err) != 0) 409 return (-1); 410 411 if (strcmp(socket, "Socket 754") == 0) 412 nchan = 1; 413 else 414 nchan = 2; 415 416 topo_mod_strfree(mod, socket); 417 418 if (topo_node_range_create(mod, pnode, name, 0, nchan - 1) < 0) 419 return (-1); 420 421 for (i = 0; i < nchan; i++) { 422 if (mkrsrc(mod, pnode, name, i, &fmri) != 0) { 423 whinge(mod, &nerr, "dramchan_create: mkrsrc " 424 "failed\n"); 425 continue; 426 } 427 428 if ((chnode = topo_node_bind(mod, pnode, name, i, fmri, 429 NULL)) == NULL) { 430 nvlist_free(fmri); 431 whinge(mod, &nerr, "dramchan_create: node bind " 432 "failed\n"); 433 continue; 434 } 435 436 nvlist_free(fmri); 437 438 (void) topo_pgroup_create(chnode, CHAN_PGROUP, 439 TOPO_STABILITY_PRIVATE, &err); 440 441 (void) topo_prop_set_string(chnode, CHAN_PGROUP, "channel", 442 TOPO_PROP_SET_ONCE, i == 0 ? "A" : "B", &err); 443 } 444 445 return (nerr == 0 ? 0 : -1); 446 } 447 448 static int 449 cs_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc) 450 { 451 int i, err, nerr = 0; 452 nvpair_t *nvp; 453 tnode_t *csnode; 454 nvlist_t *fmri, **csarr = NULL; 455 uint64_t csnum; 456 uint_t ncs; 457 458 if (nvlist_lookup_nvlist_array(mc, "cslist", &csarr, &ncs) != 0) 459 return (-1); 460 461 if (ncs == 0) 462 return (0); /* no chip-selects configured on this node */ 463 464 if (topo_node_range_create(mod, pnode, name, 0, MAX_CSNUM) < 0) 465 return (-1); 466 467 for (i = 0; i < ncs; i++) { 468 if (nvlist_lookup_uint64(csarr[i], "num", &csnum) != 0) { 469 whinge(mod, &nerr, "cs_create: cs num property " 470 "missing\n"); 471 continue; 472 } 473 474 if (mkrsrc(mod, pnode, name, csnum, &fmri) != 0) { 475 whinge(mod, &nerr, "cs_create: mkrsrc failed\n"); 476 continue; 477 } 478 479 if ((csnode = topo_node_bind(mod, pnode, name, csnum, fmri, 480 NULL)) == NULL) { 481 nvlist_free(fmri); 482 whinge(mod, &nerr, "cs_create: node bind failed\n"); 483 continue; 484 } 485 486 cs_fmri[csnum] = fmri; /* nvlist will be freed in mc_create */ 487 488 (void) topo_node_asru_set(csnode, fmri, 0, &err); 489 490 (void) topo_pgroup_create(csnode, CS_PGROUP, 491 TOPO_STABILITY_PRIVATE, &err); 492 493 for (nvp = nvlist_next_nvpair(csarr[i], NULL); nvp != NULL; 494 nvp = nvlist_next_nvpair(csarr[i], nvp)) { 495 nerr += nvprop_add(mod, nvp, CS_PGROUP, csnode); 496 } 497 } 498 499 return (nerr == 0 ? 0 : -1); 500 } 501 502 /* 503 * Registered method for asru computation for rank nodes. The 'node' 504 * argument identifies the node for which we seek an asru. The 'in' 505 * argument is used to select which asru we will return, as follows: 506 * 507 * - the node name must be "dimm" or "rank" 508 * - if 'in' is NULL then return any statically defined asru for this node 509 * - if 'in' is an "hc" scheme fmri then we construct a "mem" scheme asru 510 * with unum being the hc path to the dimm or rank (this method is called 511 * as part of dynamic asru computation for rank nodes only, but dimm_create 512 * also calls it directly to construct a "mem" scheme asru for a dimm node) 513 * - if 'in' in addition includes an hc-specific member which specifies 514 * asru-physaddr or asru-offset then these are includes in the "mem" scheme 515 * asru as additional membersl physaddr and offset 516 */ 517 /*ARGSUSED*/ 518 static int 519 mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version, 520 nvlist_t *in, nvlist_t **out) 521 { 522 int incl_pa = 0, incl_offset = 0; 523 nvlist_t *hcsp, *asru; 524 uint64_t pa, offset; 525 char *scheme, *unum; 526 int err; 527 528 if (strcmp(topo_node_name(node), RANK_NODE_NAME) != 0 && 529 strcmp(topo_node_name(node), DIMM_NODE_NAME) != 0) 530 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 531 532 if (in == NULL) { 533 if (topo_prop_get_fmri(node, TOPO_PGROUP_PROTOCOL, 534 TOPO_PROP_ASRU, out, &err) == 0) 535 return (0); 536 else 537 return (topo_mod_seterrno(mod, err)); 538 } else { 539 if (nvlist_lookup_string(in, FM_FMRI_SCHEME, &scheme) != 0 || 540 strcmp(scheme, FM_FMRI_SCHEME_HC) != 0) 541 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 542 } 543 544 if (nvlist_lookup_nvlist(in, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) { 545 if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_PHYSADDR, 546 &pa) == 0) 547 incl_pa = 1; 548 549 if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_OFFSET, 550 &offset) == 0) 551 incl_offset = 1; 552 } 553 554 /* use 'in' to obtain resource path; could use node resource */ 555 if (topo_fmri_nvl2str(topo_mod_handle(mod), in, &unum, &err) < 0) 556 return (topo_mod_seterrno(mod, err)); 557 558 if ((asru = mem_fmri_create(mod)) == NULL) { 559 topo_mod_strfree(mod, unum); 560 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 561 } 562 563 err = nvlist_add_string(asru, FM_FMRI_MEM_UNUM, unum); 564 if (incl_pa) 565 err |= nvlist_add_uint64(asru, FM_FMRI_MEM_PHYSADDR, pa); 566 if (incl_offset) 567 err |= nvlist_add_uint64(asru, FM_FMRI_MEM_OFFSET, offset); 568 569 topo_mod_strfree(mod, unum); 570 if (err != 0) { 571 nvlist_free(asru); 572 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 573 } 574 575 *out = asru; 576 return (0); 577 } 578 579 static int 580 rank_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *dimmnvl) 581 { 582 uint64_t *csnumarr; 583 char **csnamearr; 584 uint_t ncs, ncsname; 585 tnode_t *ranknode; 586 nvlist_t *fmri, *pfmri = NULL; 587 uint64_t dsz, rsz; 588 int nerr = 0; 589 int err; 590 int i; 591 592 if (nvlist_lookup_uint64_array(dimmnvl, "csnums", &csnumarr, 593 &ncs) != 0 || nvlist_lookup_string_array(dimmnvl, "csnames", 594 &csnamearr, &ncsname) != 0 || ncs != ncsname) { 595 whinge(mod, &nerr, "rank_create: " 596 "csnums/csnames extraction failed\n"); 597 return (nerr); 598 } 599 600 if (topo_node_resource(pnode, &pfmri, &err) < 0) { 601 whinge(mod, &nerr, "rank_create: parent fmri lookup " 602 "failed\n"); 603 return (nerr); 604 } 605 606 if (topo_node_range_create(mod, pnode, RANK_NODE_NAME, 0, ncs) < 0) { 607 whinge(mod, &nerr, "rank_create: range create failed\n"); 608 nvlist_free(pfmri); 609 return (nerr); 610 } 611 612 if (topo_prop_get_uint64(pnode, DIMM_PGROUP, "size", &dsz, &err) == 0) { 613 rsz = dsz / ncs; 614 } else { 615 whinge(mod, &nerr, "rank_create: parent dimm has no size\n"); 616 return (nerr); 617 } 618 619 for (i = 0; i < ncs; i++) { 620 if (mkrsrc(mod, pnode, RANK_NODE_NAME, i, &fmri) < 0) { 621 whinge(mod, &nerr, "rank_create: mkrsrc failed\n"); 622 continue; 623 } 624 625 if ((ranknode = topo_node_bind(mod, pnode, RANK_NODE_NAME, i, 626 fmri, NULL)) == NULL) { 627 nvlist_free(fmri); 628 whinge(mod, &nerr, "rank_create: node bind " 629 "failed\n"); 630 continue; 631 } 632 633 nvlist_free(fmri); 634 635 (void) topo_node_fru_set(ranknode, pfmri, 0, &err); 636 637 /* 638 * If a rank is faulted the asru is the associated 639 * chip-select, but if a page within a rank is faulted 640 * the asru is just that page. Hence the dual preconstructed 641 * and computed ASRU. 642 */ 643 (void) topo_node_asru_set(ranknode, cs_fmri[csnumarr[i]], 644 TOPO_ASRU_COMPUTE, &err); 645 646 if (topo_method_register(mod, ranknode, rank_methods) < 0) 647 whinge(mod, &nerr, "rank_create: " 648 "topo_method_register failed"); 649 650 (void) topo_pgroup_create(ranknode, RANK_PGROUP, 651 TOPO_STABILITY_PRIVATE, &err); 652 653 (void) topo_prop_set_uint64(ranknode, RANK_PGROUP, "size", 654 TOPO_PROP_SET_ONCE, rsz, &err); 655 656 (void) topo_prop_set_string(ranknode, RANK_PGROUP, "csname", 657 TOPO_PROP_SET_ONCE, csnamearr[i], &err); 658 659 (void) topo_prop_set_uint64(ranknode, RANK_PGROUP, "csnum", 660 TOPO_PROP_SET_ONCE, csnumarr[i], &err); 661 } 662 663 nvlist_free(pfmri); 664 665 return (nerr); 666 } 667 668 static int 669 dimm_create(topo_mod_t *mod, tnode_t *pnode, const char *name, nvlist_t *mc) 670 { 671 int i, err, nerr = 0; 672 nvpair_t *nvp; 673 tnode_t *dimmnode; 674 nvlist_t *fmri, *asru, **dimmarr = NULL; 675 uint64_t num; 676 uint_t ndimm; 677 678 if (nvlist_lookup_nvlist_array(mc, "dimmlist", &dimmarr, &ndimm) != 0) { 679 whinge(mod, NULL, "dimm_create: dimmlist lookup failed\n"); 680 return (-1); 681 } 682 683 if (ndimm == 0) 684 return (0); /* no dimms present on this node */ 685 686 if (topo_node_range_create(mod, pnode, name, 0, MAX_DIMMNUM) < 0) { 687 whinge(mod, NULL, "dimm_create: range create failed\n"); 688 return (-1); 689 } 690 691 for (i = 0; i < ndimm; i++) { 692 if (nvlist_lookup_uint64(dimmarr[i], "num", &num) != 0) { 693 whinge(mod, &nerr, "dimm_create: dimm num property " 694 "missing\n"); 695 continue; 696 } 697 698 if (mkrsrc(mod, pnode, name, num, &fmri) < 0) { 699 whinge(mod, &nerr, "dimm_create: mkrsrc failed\n"); 700 continue; 701 } 702 703 if ((dimmnode = topo_node_bind(mod, pnode, name, num, fmri, 704 NULL)) == NULL) { 705 nvlist_free(fmri); 706 whinge(mod, &nerr, "dimm_create: node bind " 707 "failed\n"); 708 continue; 709 } 710 711 /* 712 * The asru is static but we prefer to publish it in the 713 * "mem" scheme so call the compute method directly to 714 * perform the conversion. 715 */ 716 if (mem_asru_compute(mod, dimmnode, 717 TOPO_METH_ASRU_COMPUTE_VERSION, fmri, &asru) == 0) { 718 (void) topo_node_asru_set(dimmnode, asru, 0, &err); 719 nvlist_free(asru); 720 } else { 721 722 nvlist_free(fmri); 723 whinge(mod, &nerr, "dimm_create: mem_asru_compute " 724 "failed\n"); 725 continue; 726 } 727 728 (void) topo_node_fru_set(dimmnode, fmri, 0, &err); 729 730 nvlist_free(fmri); 731 732 (void) topo_pgroup_create(dimmnode, DIMM_PGROUP, 733 TOPO_STABILITY_PRIVATE, &err); 734 735 for (nvp = nvlist_next_nvpair(dimmarr[i], NULL); nvp != NULL; 736 nvp = nvlist_next_nvpair(dimmarr[i], nvp)) { 737 if (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY && 738 strcmp(nvpair_name(nvp), "csnums") == 0 || 739 nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY && 740 strcmp(nvpair_name(nvp), "csnames") == 0) 741 continue; /* used in rank_create() */ 742 743 nerr += nvprop_add(mod, nvp, DIMM_PGROUP, dimmnode); 744 } 745 746 nerr += rank_create(mod, dimmnode, dimmarr[i]); 747 } 748 749 return (nerr == 0 ? 0 : -1); 750 } 751 752 static nvlist_t * 753 mc_lookup_by_mcid(topo_mod_t *mod, topo_instance_t id) 754 { 755 mc_snapshot_info_t mcs; 756 void *buf = NULL; 757 uint8_t ver; 758 759 nvlist_t *nvl; 760 char path[64]; 761 int fd, err; 762 763 (void) snprintf(path, sizeof (path), "/dev/mc/mc%d", id); 764 fd = open(path, O_RDONLY); 765 766 if (fd == -1) { 767 /* 768 * Some v20z and v40z systems may have had the 3rd-party 769 * NWSnps packagae installed which installs a /dev/mc 770 * link. So try again via /devices. 771 */ 772 (void) snprintf(path, sizeof (path), 773 "/devices/pci@0,0/pci1022,1102@%x,2:mc-amd", 774 MC_AMD_DEV_OFFSET + id); 775 fd = open(path, O_RDONLY); 776 } 777 778 if (fd == -1) { 779 whinge(mod, NULL, "mc failed to open %s: %s\n", 780 path, strerror(errno)); 781 return (NULL); 782 } 783 784 if (ioctl(fd, MC_IOC_SNAPSHOT_INFO, &mcs) == -1 || 785 (buf = topo_mod_alloc(mod, mcs.mcs_size)) == NULL || 786 ioctl(fd, MC_IOC_SNAPSHOT, buf) == -1) { 787 788 whinge(mod, NULL, "mc failed to snapshot %s: %s\n", 789 path, strerror(errno)); 790 791 free(buf); 792 (void) close(fd); 793 return (NULL); 794 } 795 796 (void) close(fd); 797 err = nvlist_unpack(buf, mcs.mcs_size, &nvl, 0); 798 topo_mod_free(mod, buf, mcs.mcs_size); 799 800 if (nvlist_lookup_uint8(nvl, MC_NVLIST_VERSTR, &ver) != 0) { 801 whinge(mod, NULL, "mc nvlist is not versioned\n"); 802 nvlist_free(nvl); 803 return (NULL); 804 } else if (ver != MC_NVLIST_VERS1) { 805 whinge(mod, NULL, "mc nvlist version mismatch\n"); 806 nvlist_free(nvl); 807 return (NULL); 808 } 809 810 return (err ? NULL : nvl); 811 } 812 813 static int 814 mc_create(topo_mod_t *mod, tnode_t *pnode, const char *name) 815 { 816 int err, rc = 0; 817 tnode_t *mcnode; 818 nvlist_t *fmri; 819 nvpair_t *nvp; 820 nvlist_t *mc = NULL; 821 int i; 822 823 if (mkrsrc(mod, pnode, name, 0, &fmri) != 0) { 824 whinge(mod, NULL, "mc_create: mkrsrc failed\n"); 825 return (-1); 826 } 827 828 if (topo_node_range_create(mod, pnode, name, 0, 0) < 0) { 829 nvlist_free(fmri); 830 whinge(mod, NULL, "mc_create: node range create failed\n"); 831 return (-1); 832 } 833 834 /* 835 * Gather and create memory controller topology 836 */ 837 if ((mc = mc_lookup_by_mcid(mod, topo_node_instance(pnode))) == NULL || 838 (mcnode = topo_node_bind(mod, pnode, 839 name, 0, fmri, NULL)) == NULL) { 840 if (mc != NULL) 841 nvlist_free(mc); 842 topo_node_range_destroy(pnode, name); 843 nvlist_free(fmri); 844 whinge(mod, NULL, "mc_create: mc lookup or bind failed\n"); 845 return (-1); 846 } 847 848 (void) topo_node_fru_set(mcnode, NULL, 0, &err); 849 nvlist_free(fmri); 850 851 /* 852 * Add memory controller properties 853 */ 854 (void) topo_pgroup_create(mcnode, MCT_PGROUP, 855 TOPO_STABILITY_PRIVATE, &err); 856 857 for (nvp = nvlist_next_nvpair(mc, NULL); nvp != NULL; 858 nvp = nvlist_next_nvpair(mc, nvp)) { 859 if (nvpair_type(nvp) == DATA_TYPE_NVLIST_ARRAY && 860 (strcmp(nvpair_name(nvp), "cslist") == 0 || 861 strcmp(nvpair_name(nvp), "dimmlist") == 0)) { 862 continue; 863 } else if (nvpair_type(nvp) == DATA_TYPE_UINT8 && 864 strcmp(nvpair_name(nvp), MC_NVLIST_VERSTR) == 0) { 865 continue; 866 } else { 867 if (nvprop_add(mod, nvp, MCT_PGROUP, mcnode) != 0) 868 rc = -1; 869 } 870 } 871 872 if (dramchan_create(mod, mcnode, CHAN_NODE_NAME) != 0 || 873 cs_create(mod, mcnode, CS_NODE_NAME, mc) != 0 || 874 dimm_create(mod, mcnode, DIMM_NODE_NAME, mc) != 0) 875 rc = -1; 876 877 /* 878 * Free the fmris for the chip-selects allocated in cs_create 879 */ 880 for (i = 0; i < MC_CHIP_NCS; i++) { 881 if (cs_fmri[i] != NULL) { 882 nvlist_free(cs_fmri[i]); 883 cs_fmri[i] = NULL; 884 } 885 } 886 887 nvlist_free(mc); 888 return (rc); 889 } 890 891 static int 892 chip_create(topo_mod_t *mod, tnode_t *pnode, const char *name, 893 topo_instance_t min, topo_instance_t max, chip_t *chip) 894 { 895 int i, nerr = 0; 896 kstat_t *ksp; 897 ulong_t *chipmap; 898 tnode_t *cnode; 899 nvlist_t *fmri; 900 901 if ((chipmap = topo_mod_zalloc(mod, BT_BITOUL(max) * 902 sizeof (ulong_t))) == NULL) 903 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 904 905 /* 906 * Read in all cpu_info kstats, for all chip ids. The ks_instance 907 * argument to kstat_lookup is the logical cpu_id - we will use this 908 * in cpu_create. 909 */ 910 for (i = 0; i <= chip->chip_ncpustats; i++) { 911 if ((ksp = kstat_lookup(chip->chip_kc, "cpu_info", i, NULL)) == 912 NULL || kstat_read(chip->chip_kc, ksp, NULL) < 0) 913 continue; 914 915 chip->chip_cpustats[i] = ksp; 916 } 917 918 for (i = 0; i <= chip->chip_ncpustats; i++) { 919 kstat_named_t *k; 920 int err, chipid; 921 922 if ((ksp = chip->chip_cpustats[i]) == NULL) 923 continue; 924 925 if ((k = kstat_data_lookup(ksp, "chip_id")) == NULL) { 926 whinge(mod, &nerr, "chip_create: chip_id lookup " 927 "via kstats failed\n"); 928 continue; 929 } 930 931 chipid = k->value.l; 932 if (BT_TEST(chipmap, chipid)) 933 continue; 934 935 if (chipid < min || chipid > max) 936 continue; 937 938 if (mkrsrc(mod, pnode, name, chipid, &fmri) != 0) { 939 whinge(mod, &nerr, "chip_create: mkrsrc failed\n"); 940 continue; 941 } 942 943 if ((cnode = topo_node_bind(mod, pnode, name, chipid, fmri, 944 NULL)) == NULL) { 945 nvlist_free(fmri); 946 whinge(mod, &nerr, "chip_create: node bind " 947 "failed for chipid %d\n", chipid); 948 continue; 949 } 950 BT_SET(chipmap, chipid); 951 952 (void) topo_node_fru_set(cnode, fmri, 0, &err); 953 954 nvlist_free(fmri); 955 956 (void) topo_pgroup_create(cnode, CHIP_PGROUP, 957 TOPO_STABILITY_PRIVATE, &err); 958 (void) chip_strprop(cnode, ksp, CHIP_VENDOR_ID); 959 (void) chip_longprop(cnode, ksp, CHIP_FAMILY); 960 (void) chip_longprop(cnode, ksp, CHIP_MODEL); 961 (void) chip_longprop(cnode, ksp, CHIP_STEPPING); 962 963 if (cpu_create(mod, cnode, CPU_NODE_NAME, chipid, chip) != 0 || 964 mc_create(mod, cnode, MCT_NODE_NAME) != 0) 965 nerr++; /* have whinged elsewhere */ 966 } 967 968 topo_mod_free(mod, chipmap, BT_BITOUL(max) * sizeof (ulong_t)); 969 970 if (nerr == 0) { 971 return (0); 972 } else { 973 (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM); 974 return (-1); 975 } 976 } 977 978 static int 979 chip_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 980 topo_instance_t min, topo_instance_t max, void *arg) 981 { 982 chip_t *chip = (chip_t *)arg; 983 984 if (strcmp(name, "chip") == 0) 985 return (chip_create(mod, pnode, name, min, max, chip)); 986 987 return (0); 988 } 989