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