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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <stdarg.h> 30 #include <string.h> 31 #include <strings.h> 32 #include <libnvpair.h> 33 #include <kstat.h> 34 #include <unistd.h> 35 #include <sys/types.h> 36 #include <fm/topo_mod.h> 37 #include <sys/devfm.h> 38 #include <fm/fmd_agent.h> 39 40 #define BUFSZ 128 41 42 char * 43 get_fmtstr(topo_mod_t *mod, nvlist_t *in) 44 { 45 char *fmtstr; 46 nvlist_t *args; 47 int ret; 48 49 topo_mod_dprintf(mod, "get_fmtstr() called\n"); 50 51 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 52 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 53 strerror(ret)); 54 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 55 return (NULL); 56 } 57 if ((ret = nvlist_lookup_string(args, "format", &fmtstr)) != 0) { 58 topo_mod_dprintf(mod, "Failed to lookup 'format' arg (%s)\n", 59 strerror(ret)); 60 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 61 return (NULL); 62 } 63 return (fmtstr); 64 } 65 66 int 67 store_prop_val(topo_mod_t *mod, char *buf, char *propname, nvlist_t **out) 68 { 69 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) { 70 topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n"); 71 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 72 } 73 if (nvlist_add_string(*out, TOPO_PROP_VAL_NAME, propname) != 0) { 74 topo_mod_dprintf(mod, "Failed to set '%s'\n", 75 TOPO_PROP_VAL_NAME); 76 nvlist_free(*out); 77 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 78 } 79 if (nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_STRING) 80 != 0) { 81 topo_mod_dprintf(mod, "Failed to set '%s'\n", 82 TOPO_PROP_VAL_TYPE); 83 nvlist_free(*out); 84 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 85 } 86 if (nvlist_add_string(*out, TOPO_PROP_VAL_VAL, buf) != 0) { 87 topo_mod_dprintf(mod, "Failed to set '%s'\n", 88 TOPO_PROP_VAL_VAL); 89 nvlist_free(*out); 90 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 91 } 92 return (0); 93 } 94 95 /* 96 * This is a somewhat generic property method for labelling the dimm slots on 97 * uni-socket x86/x64 platforms. This method assumes a direct linear 98 * correlation between the dimm topo node instance number and the dimm slot 99 * label number. It takes the following two arguments: 100 * 101 * format: a string containing a printf-like format with a single %d token 102 * which this method computes 103 * 104 * i.e.: DIMM %d 105 * 106 * offset: a numeric offset that we'll number the dimms from. This is to 107 * allow for the fact that some systems number the dimm slots 108 * from zero and others start from one (like the Ultra 20) 109 */ 110 /* ARGSUSED */ 111 int 112 simple_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 113 nvlist_t *in, nvlist_t **out) 114 { 115 char *fmtstr, buf[BUFSZ]; 116 int ret; 117 uint32_t offset; 118 nvlist_t *args; 119 120 topo_mod_dprintf(mod, "simple_dimm_label() called\n"); 121 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 122 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 123 strerror(ret)); 124 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 125 } 126 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 127 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 128 strerror(ret)); 129 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 130 } 131 132 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 133 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 134 /* topo errno already set */ 135 return (-1); 136 } 137 138 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 139 (void) snprintf(buf, BUFSZ, fmtstr, 140 (topo_node_instance(node) + offset)); 141 142 if (store_prop_val(mod, buf, "label", out) != 0) { 143 topo_mod_dprintf(mod, "Failed to set label\n"); 144 /* topo errno already set */ 145 return (-1); 146 } 147 148 return (0); 149 } 150 151 152 /* 153 * This is a somewhat generic property method for labelling the dimm slots on 154 * multi-socket x86/x64 platforms. It takes the following two arguments: 155 * 156 * format: a string containing a printf-like format with a two %d tokens 157 * for the cpu and dimm slot label numbers, which this method 158 * computes 159 * 160 * i.e.: CPU %d DIMM %d 161 * 162 * offset: a numeric offset that we'll number the dimms from. This is to 163 * allow for the fact that some systems number the dimm slots 164 * from zero while others may start from one 165 * 166 * order: "reverse" or "forward" - sets the direction of the correlation 167 * between dimm topo node instance number and DIMM slot number 168 * 169 * dimms_per_chip: the number of DIMM slots per chip 170 */ 171 /* ARGSUSED */ 172 int 173 simple_dimm_label_mp(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 174 nvlist_t *in, nvlist_t **out) 175 { 176 char *fmtstr, *order, buf[BUFSZ]; 177 tnode_t *chip; 178 int ret; 179 uint32_t offset, dimms_per_chip; 180 nvlist_t *args; 181 182 topo_mod_dprintf(mod, "simple_dimm_label_mp() called\n"); 183 184 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 185 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 186 strerror(ret)); 187 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 188 } 189 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 190 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 191 strerror(ret)); 192 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 193 } 194 if ((ret = nvlist_lookup_uint32(args, "dimms_per_chip", 195 &dimms_per_chip)) != 0) { 196 topo_mod_dprintf(mod, "Failed to lookup 'dimms_per_chip' arg " 197 "(%s)\n", strerror(ret)); 198 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 199 } 200 if ((ret = nvlist_lookup_string(args, "order", &order)) != 0) { 201 topo_mod_dprintf(mod, "Failed to lookup 'order' arg (%s)\n", 202 strerror(ret)); 203 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 204 } 205 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 206 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 207 topo_mod_free(mod, order, BUFSZ); 208 /* topo errno already set */ 209 return (-1); 210 } 211 212 chip = topo_node_parent(topo_node_parent(node)); 213 214 if (strcasecmp(order, "forward") == 0) 215 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 216 (void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip), 217 (topo_node_instance(node) + offset)); 218 else if (strcasecmp(order, "reverse") == 0) 219 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 220 (void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip), 221 (((topo_node_instance(chip) + 1) * dimms_per_chip) 222 - (topo_node_instance(node)) - 1 + offset)); 223 else { 224 topo_mod_dprintf(mod, "Invalid value for order arg\n"); 225 topo_mod_free(mod, order, BUFSZ); 226 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 227 } 228 229 if (store_prop_val(mod, buf, "label", out) != 0) { 230 topo_mod_dprintf(mod, "Failed to set label\n"); 231 topo_mod_free(mod, order, BUFSZ); 232 /* topo errno already set */ 233 return (-1); 234 } 235 236 return (0); 237 } 238 239 /* 240 * This method assumes a correspondence between the dimm topo node instance 241 * number and the dimm slot label number, but unlike simple_chip_label_mp, the 242 * slot numbers aren't reused between CPU's. This method assumes there 243 * are 4 DIMM slots per chip. It takes the following two arguments: 244 * 245 * format: a string containing a printf-like format with a single %d token 246 * which this method computes 247 * 248 * i.e.: DIMM %d 249 * 250 * offset: a numeric offset that we'll number the dimms from. This is to 251 * allow for the fact that some systems number the dimm slots 252 * from zero and others may start from one 253 * 254 * order: "reverse" or "forward" - sets the direction of the correlation 255 * between dimm topo node instance number and DIMM slot number 256 */ 257 /* ARGSUSED */ 258 int 259 seq_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 260 nvlist_t *in, nvlist_t **out) 261 { 262 char *fmtstr, *order, buf[BUFSZ]; 263 int ret; 264 uint32_t offset; 265 nvlist_t *args; 266 tnode_t *chip; 267 268 topo_mod_dprintf(mod, "seq_dimm_label() called\n"); 269 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 270 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 271 strerror(ret)); 272 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 273 } 274 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 275 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 276 strerror(ret)); 277 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 278 } 279 if ((ret = nvlist_lookup_string(args, "order", &order)) != 0) { 280 topo_mod_dprintf(mod, "Failed to lookup 'order' arg (%s)\n", 281 strerror(ret)); 282 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 283 } 284 285 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 286 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 287 topo_mod_free(mod, order, BUFSZ); 288 /* topo errno already set */ 289 return (-1); 290 } 291 292 chip = topo_node_parent(topo_node_parent(node)); 293 294 if (strcasecmp(order, "forward") == 0) 295 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 296 (void) snprintf(buf, BUFSZ, fmtstr, ((topo_node_instance(node)) 297 + (topo_node_instance(chip) * 4) + offset)); 298 else if (strcasecmp(order, "reverse") == 0) 299 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 300 (void) snprintf(buf, BUFSZ, fmtstr, 301 (((topo_node_instance(chip) + 1) * 4) 302 - (topo_node_instance(node)) - 1 + offset)); 303 else { 304 topo_mod_dprintf(mod, "Invalid value for order arg\n"); 305 topo_mod_free(mod, order, BUFSZ); 306 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 307 } 308 309 if (store_prop_val(mod, buf, "label", out) != 0) { 310 topo_mod_dprintf(mod, "Failed to set label\n"); 311 topo_mod_free(mod, order, BUFSZ); 312 /* topo errno already set */ 313 return (-1); 314 } 315 316 return (0); 317 } 318 319 320 /* 321 * This is a somewhat generic property method for labelling the CPU sockets on 322 * x86/x64 platforms. This method assumes a correspondence between 323 * the chip topo node instance number and the CPU socket label number. It takes 324 * the following two arguments: 325 * 326 * format: a string containing a printf-like format with a single %d token 327 * which this method computes 328 * 329 * i.e.: CPU %d 330 * 331 * offset: a numeric offset that we'll number the CPU's from. This is to 332 * allow for the fact that some systems number the CPU sockets 333 * from zero and others start from one (like the X4X00-M2 systems) 334 */ 335 /* ARGSUSED */ 336 int 337 simple_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 338 nvlist_t *in, nvlist_t **out) 339 { 340 char *fmtstr, buf[BUFSZ]; 341 int ret; 342 uint32_t offset; 343 nvlist_t *args; 344 345 topo_mod_dprintf(mod, "simple_chip_label() called\n"); 346 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 347 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 348 strerror(ret)); 349 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 350 } 351 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 352 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 353 strerror(ret)); 354 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 355 } 356 357 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 358 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 359 /* topo errno already set */ 360 return (-1); 361 } 362 363 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 364 (void) snprintf(buf, BUFSZ, fmtstr, 365 (topo_node_instance(node) + offset)); 366 367 if (store_prop_val(mod, buf, "label", out) != 0) { 368 topo_mod_dprintf(mod, "Failed to set label\n"); 369 /* topo errno already set */ 370 return (-1); 371 } 372 373 return (0); 374 } 375 376 377 /* 378 * This is a somewhat generic property method for labelling the CPU sockets on 379 * x86/x64 platforms. This method assumes a correspondence between 380 * the chip topo node instance number and the CPU socket label number. It takes 381 * the following argument: 382 * 383 * format: a string containing a printf-like format with a single %d token 384 * which this method computes 385 * 386 * i.e.: CPU %d 387 * 388 * offset: a numeric offset that we'll number the CPU's from. This is to 389 * allow for the fact that some systems number the CPU sockets 390 * from zero and others start from one (like the X8450 systems) 391 */ 392 /* ARGSUSED */ 393 int 394 fsb2_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 395 nvlist_t *in, nvlist_t **out) 396 { 397 char *fmtstr, buf[BUFSZ]; 398 int ret; 399 uint32_t offset; 400 nvlist_t *args; 401 402 topo_mod_dprintf(mod, "fsb2_chip_label() called\n"); 403 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 404 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 405 strerror(ret)); 406 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 407 } 408 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 409 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 410 strerror(ret)); 411 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 412 } 413 414 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 415 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 416 /* topo errno already set */ 417 return (-1); 418 } 419 420 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 421 (void) snprintf(buf, BUFSZ, fmtstr, 422 ((topo_node_instance(node) / 2) + offset)); 423 424 if (store_prop_val(mod, buf, "label", out) != 0) { 425 topo_mod_dprintf(mod, "Failed to set label\n"); 426 /* topo errno already set */ 427 return (-1); 428 } 429 430 return (0); 431 } 432 433 434 /* 435 * This is a custom property method for generating the CPU slot label for the 436 * Galaxy 4E/4F platforms. 437 * 438 * format: a string containing a printf-like format with a single %c token 439 * which this method computes 440 * 441 * i.e.: CPU %c 442 */ 443 /* ARGSUSED */ 444 int 445 g4_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 446 nvlist_t *in, nvlist_t **out) 447 { 448 char *fmtstr, buf[BUFSZ], slot_id; 449 int err, htid, mapidx; 450 uint32_t num_nodes; 451 /* 452 * G4 HT node ID to FRU label translation. The g4map array 453 * is indexed by (number of coherent nodes) / 2 - 1. 454 * The value for a given number of nodes is a char array 455 * indexed by node ID. 456 */ 457 const char *g4map[] = { 458 "AB", /* 2 nodes */ 459 "ADEH", /* 4 nodes */ 460 "ABDEFH", /* 6 nodes */ 461 "ACBDEFGH" /* 8 nodes */ 462 }; 463 464 topo_mod_dprintf(mod, "g4_chip_label() called\n"); 465 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 466 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 467 /* topo errno already set */ 468 return (-1); 469 } 470 /* 471 * The chip-properties property will not exist if this platform has 472 * AMD family 0x10 modules. In that case we don't want to treat it as a 473 * fatal error as that will cause calls like topo_prop_getprops to fail 474 * to return any properties on this node. Therefore, if the topo errno 475 * is set to ETOPO_PROP_NOENT, then we'll just set an empty label 476 * and return 0. If the topo errno is set to anything else we'll 477 * return -1. 478 */ 479 if (topo_prop_get_uint32(node, "chip-properties", "CoherentNodes", 480 &num_nodes, &err) != 0) { 481 if (err == ETOPO_PROP_NOENT) { 482 if (store_prop_val(mod, "", "label", out) != 0) { 483 topo_mod_dprintf(mod, "Failed to set label\n"); 484 /* topo errno already set */ 485 return (-1); 486 } 487 return (0); 488 } else { 489 topo_mod_dprintf(mod, "Failed to lookup 'CoherentNodes'" 490 "property\n"); 491 return (topo_mod_seterrno(mod, err)); 492 } 493 } 494 495 mapidx = num_nodes / 2 - 1; 496 htid = topo_node_instance(node); 497 498 /* HT nodes must number 0 .. num_nodes - 1 */ 499 if (htid >= num_nodes) { 500 topo_mod_dprintf(mod, "chip node instance range check failed:" 501 "num_nodes=%d, instance=%d\n", num_nodes, htid); 502 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 503 } 504 505 switch (num_nodes) { 506 case (2): 507 case (4): 508 case (6): 509 case (8): 510 /* htid is already range-checked */ 511 mapidx = num_nodes / 2 - 1; 512 slot_id = g4map[mapidx][htid]; 513 break; 514 default: 515 topo_mod_dprintf(mod, "Invalid number of CoherentNodes:" 516 " %d\n", num_nodes); 517 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 518 } 519 520 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 521 (void) snprintf(buf, BUFSZ, fmtstr, slot_id); 522 523 if (store_prop_val(mod, buf, "label", out) != 0) { 524 topo_mod_dprintf(mod, "Failed to set label\n"); 525 /* topo errno already set */ 526 return (-1); 527 } 528 529 return (0); 530 } 531 532 /* 533 * Utility function used by a4fplus_chip_label to determine the number of chips 534 * (as opposed to processors) that are installed in the system by counting 535 * the unique chipids. 536 */ 537 static int 538 get_num_chips(topo_mod_t *mod) 539 { 540 fmd_agent_hdl_t *hdl; 541 nvlist_t **cpus; 542 uint_t ncpu; 543 int i, nchip = 0; 544 int32_t chipid; 545 uint64_t bitmap = 0; 546 547 if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL) 548 return (-1); 549 if (fmd_agent_physcpu_info(hdl, &cpus, &ncpu) == -1) { 550 topo_mod_dprintf(mod, "get physcpu info failed:%s\n", 551 fmd_agent_errmsg(hdl)); 552 fmd_agent_close(hdl); 553 return (-1); 554 } 555 fmd_agent_close(hdl); 556 557 for (i = 0; i < ncpu; i++) { 558 if (nvlist_lookup_int32(cpus[i], FM_PHYSCPU_INFO_CHIP_ID, 559 &chipid) != 0 || chipid >= 64) { 560 topo_mod_dprintf(mod, "lookup chipid failed\n"); 561 nchip = -1; 562 break; 563 } 564 if ((bitmap & (1 << chipid)) != 0) { 565 bitmap |= (1 << chipid); 566 nchip++; 567 } 568 } 569 570 for (i = 0; i < ncpu; i++) 571 nvlist_free(cpus[i]); 572 umem_free(cpus, sizeof (nvlist_t *) * ncpu); 573 574 return (nchip); 575 } 576 577 /* 578 * This is a custom property method for generating the CPU slot label for the 579 * Andromeda Fplus platforms. 580 * 581 * format: a string containing a printf-like format with a single %d token 582 * which this method computes 583 * 584 * i.e.: CPU %d 585 */ 586 /* ARGSUSED */ 587 int 588 a4fplus_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 589 nvlist_t *in, nvlist_t **out) 590 { 591 char *fmtstr, buf[BUFSZ]; 592 int num_nodes; 593 594 topo_mod_dprintf(mod, "a4fplus_chip_label() called\n"); 595 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 596 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 597 /* topo errno already set */ 598 return (-1); 599 } 600 601 /* 602 * Normally we'd figure out the total number of chip nodes by looking 603 * at the CoherentNodes property. However, due to the lack of a memory 604 * controller driver for family 0x10, this property wont exist on the 605 * chip nodes on A4Fplus. 606 */ 607 if ((num_nodes = get_num_chips(mod)) < 0) { 608 topo_mod_dprintf(mod, "Failed to determine number of chip " 609 "nodes\n"); 610 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 611 } 612 switch (num_nodes) { 613 case (2): 614 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 615 (void) snprintf(buf, BUFSZ, fmtstr, 616 topo_node_instance(node) + 2); 617 break; 618 case (4): 619 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 620 (void) snprintf(buf, BUFSZ, fmtstr, 621 topo_node_instance(node)); 622 break; 623 default: 624 topo_mod_dprintf(mod, "Invalid number of chip nodes:" 625 " %d\n", num_nodes); 626 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 627 } 628 629 630 if (store_prop_val(mod, buf, "label", out) != 0) { 631 topo_mod_dprintf(mod, "Failed to set label\n"); 632 /* topo errno already set */ 633 return (-1); 634 } 635 636 return (0); 637 } 638 639 /* 640 * This is a somewhat generic property method for labelling the chip-select 641 * nodes on multi-socket AMD family 0x10 platforms. This is necessary because 642 * these platforms are not supported by the current AMD memory controller driver 643 * and therefore we're not able to discover the memory topology on AMD family 644 * 0x10 systems. As a result, instead of enumerating the installed dimms and 645 * their ranks, the chip enumerator generically enumerates all of the possible 646 * chip-selects beneath each dram channel. 647 * 648 * When we diagnose a dimm fault, the FRU fmri will be for the chip-select node, 649 * so we need to attach FRU labels to the chip-select nodes. 650 * 651 * format: a string containing a printf-like format with a two %d tokens 652 * for the cpu and dimm slot label numbers, which this method 653 * computes 654 * 655 * i.e.: CPU %d DIMM %d 656 * 657 * offset: a numeric offset that we'll number the dimms from. This is to 658 * allow for the fact that some systems may number the dimm slots 659 * from zero while others may start from one 660 * 661 * This function computes the DIMM slot number using the following formula: 662 * 663 * slot = cs - (cs % 2) + channel + offset 664 */ 665 /* ARGSUSED */ 666 int 667 simple_cs_label_mp(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 668 nvlist_t *in, nvlist_t **out) 669 { 670 char *fmtstr, buf[BUFSZ]; 671 tnode_t *chip, *chan; 672 int dimm_num, ret; 673 uint32_t offset; 674 nvlist_t *args; 675 676 topo_mod_dprintf(mod, "simple_cs_label_mp() called\n"); 677 678 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 679 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 680 strerror(ret)); 681 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 682 } 683 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 684 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 685 strerror(ret)); 686 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 687 } 688 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 689 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 690 /* topo errno already set */ 691 return (-1); 692 } 693 694 chip = topo_node_parent(topo_node_parent(topo_node_parent(node))); 695 chan = topo_node_parent(node); 696 697 dimm_num = topo_node_instance(node) - (topo_node_instance(node) % 2) 698 + topo_node_instance(chan) + offset; 699 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 700 (void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip), 701 dimm_num); 702 703 if (store_prop_val(mod, buf, "label", out) != 0) { 704 topo_mod_dprintf(mod, "Failed to set label\n"); 705 /* topo errno already set */ 706 return (-1); 707 } 708 709 return (0); 710 } 711 712 /* ARGSUSED */ 713 int 714 g4_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 715 nvlist_t *in, nvlist_t **out) 716 { 717 char *fmtstr, *chip_lbl, buf[BUFSZ]; 718 tnode_t *chip; 719 int ret, err = 0; 720 uint32_t offset; 721 nvlist_t *args; 722 723 topo_mod_dprintf(mod, "g4_dimm_label() called\n"); 724 725 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 726 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 727 strerror(ret)); 728 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 729 } 730 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 731 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 732 strerror(ret)); 733 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 734 } 735 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 736 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 737 /* topo errno already set */ 738 return (-1); 739 } 740 741 /* 742 * The 4600/4600M2 have a weird way of labeling the chip nodes, so 743 * instead of trying to recompute it, we'll simply look it up and 744 * prepend it to our dimm label. 745 */ 746 chip = topo_node_parent(topo_node_parent(node)); 747 if (topo_prop_get_string(chip, TOPO_PGROUP_PROTOCOL, "label", &chip_lbl, 748 &err) != 0) { 749 topo_mod_dprintf(mod, "Failed to lookup label prop on %s=%d\n", 750 topo_node_name(chip), topo_node_instance(chip), 751 topo_strerror(err)); 752 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 753 } 754 755 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 756 (void) snprintf(buf, BUFSZ, fmtstr, chip_lbl, 757 (topo_node_instance(node) + offset)); 758 759 topo_mod_strfree(mod, chip_lbl); 760 761 if (store_prop_val(mod, buf, "label", out) != 0) { 762 topo_mod_dprintf(mod, "Failed to set label\n"); 763 /* topo errno already set */ 764 return (-1); 765 } 766 767 return (0); 768 } 769 770 /* 771 * This method is used to compute the labels for DIMM slots on the Galaxy 1F and 772 * 2F platforms. It results in following dimm node label assignments: 773 * 774 * chip/dimm instances label 775 * ------------------- ----- 776 * chip=0/dimm=0 CPU 1 DIMM A0 777 * chip=0/dimm=1 CPU 1 DIMM B0 778 * chip=0/dimm=2 CPU 1 DIMM A1 779 * chip=0/dimm=3 CPU 1 DIMM B1 780 * 781 * chip=1/dimm=0 CPU 2 DIMM A0 782 * chip=1/dimm=1 CPU 2 DIMM B0 783 * chip=1/dimm=2 CPU 2 DIMM A1 784 * chip=1/dimm=3 CPU 2 DIMM B1 785 */ 786 /* ARGSUSED */ 787 int 788 g12f_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 789 nvlist_t *in, nvlist_t **out) 790 { 791 char *fmtstr, buf[BUFSZ], chan; 792 tnode_t *chip; 793 int ret, dimm_inst, slot_num; 794 nvlist_t *args; 795 796 topo_mod_dprintf(mod, "g12f_dimm_label() called\n"); 797 798 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 799 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 800 strerror(ret)); 801 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 802 } 803 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 804 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 805 /* topo errno already set */ 806 return (-1); 807 } 808 809 chip = topo_node_parent(topo_node_parent(node)); 810 dimm_inst = topo_node_instance(node); 811 chan = dimm_inst == 0 || dimm_inst == 2 ? 'A': 'B'; 812 slot_num = (dimm_inst <= 1 ? 0 : 1); 813 814 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 815 (void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip) + 1, chan, 816 slot_num); 817 818 if (store_prop_val(mod, buf, "label", out) != 0) { 819 topo_mod_dprintf(mod, "Failed to set label\n"); 820 /* topo errno already set */ 821 return (-1); 822 } 823 824 return (0); 825 } 826