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