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 <stdio.h> 30 #include <stdlib.h> 31 #include <stdarg.h> 32 #include <string.h> 33 #include <strings.h> 34 #include <libnvpair.h> 35 #include <sys/types.h> 36 #include <fm/topo_mod.h> 37 38 #define BUFSZ 128 39 40 static char * 41 get_fmtstr(topo_mod_t *mod, nvlist_t *in) 42 { 43 char *fmtstr; 44 nvlist_t *args; 45 int ret; 46 47 topo_mod_dprintf(mod, "get_fmtstr() called\n"); 48 49 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 50 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 51 strerror(ret)); 52 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 53 return (NULL); 54 } 55 if ((ret = nvlist_lookup_string(args, "format", &fmtstr)) != 0) { 56 topo_mod_dprintf(mod, "Failed to lookup 'format' arg (%s)\n", 57 strerror(ret)); 58 (void) topo_mod_seterrno(mod, EMOD_NVL_INVAL); 59 return (NULL); 60 } 61 return (fmtstr); 62 } 63 64 static int 65 store_prop_val(topo_mod_t *mod, char *buf, char *propname, nvlist_t **out) 66 { 67 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) { 68 topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n"); 69 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 70 } 71 if (nvlist_add_string(*out, TOPO_PROP_VAL_NAME, propname) != 0) { 72 topo_mod_dprintf(mod, "Failed to set '%s'\n", 73 TOPO_PROP_VAL_NAME); 74 nvlist_free(*out); 75 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 76 } 77 if (nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_STRING) 78 != 0) { 79 topo_mod_dprintf(mod, "Failed to set '%s'\n", 80 TOPO_PROP_VAL_TYPE); 81 nvlist_free(*out); 82 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 83 } 84 if (nvlist_add_string(*out, TOPO_PROP_VAL_VAL, buf) != 0) { 85 topo_mod_dprintf(mod, "Failed to set '%s'\n", 86 TOPO_PROP_VAL_VAL); 87 nvlist_free(*out); 88 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 89 } 90 return (0); 91 } 92 93 /* 94 * This is a somewhat generic property method for labelling the dimm slots on 95 * uni-socket x86/x64 platforms. This method assumes a direct linear 96 * correlation between the dimm topo node instance number and the dimm slot 97 * label number. It takes the following two arguments: 98 * 99 * format: a string containing a printf-like format with a single %d token 100 * which this method computes 101 * 102 * i.e.: DIMM %d 103 * 104 * offset: a numeric offset that we'll number the dimms from. This is to 105 * allow for the fact that some systems number the dimm slots 106 * from zero and others start from one (like the Ultra 20) 107 */ 108 /* ARGSUSED */ 109 int 110 simple_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 111 nvlist_t *in, nvlist_t **out) 112 { 113 char *fmtstr, buf[BUFSZ]; 114 int ret; 115 uint32_t offset; 116 nvlist_t *args; 117 118 topo_mod_dprintf(mod, "simple_dimm_label() called\n"); 119 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 120 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 121 strerror(ret)); 122 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 123 } 124 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 125 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 126 strerror(ret)); 127 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 128 } 129 130 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 131 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 132 /* topo errno already set */ 133 return (-1); 134 } 135 136 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 137 (void) snprintf(buf, BUFSZ, fmtstr, 138 (topo_node_instance(node) + offset)); 139 140 if (store_prop_val(mod, buf, "label", out) != 0) { 141 topo_mod_dprintf(mod, "Failed to set label\n"); 142 /* topo errno already set */ 143 return (-1); 144 } 145 146 return (0); 147 } 148 149 150 /* 151 * This is a somewhat generic property method for labelling the dimm slots on 152 * multi-socket x86/x64 platforms. It takes the following two arguments: 153 * 154 * format: a string containing a printf-like format with a two %d tokens 155 * for the cpu and dimm slot label numbers, which this method 156 * computes 157 * 158 * i.e.: CPU %d DIMM %d 159 * 160 * offset: a numeric offset that we'll number the dimms from. This is to 161 * allow for the fact that some systems number the dimm slots 162 * from zero while others may start from one 163 * 164 * order: "reverse" or "forward" - sets the direction of the correlation 165 * between dimm topo node instance number and DIMM slot number 166 * 167 * dimms_per_chip: the number of DIMM slots per chip 168 */ 169 /* ARGSUSED */ 170 int 171 simple_dimm_label_mp(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 172 nvlist_t *in, nvlist_t **out) 173 { 174 char *fmtstr, *order, buf[BUFSZ]; 175 tnode_t *chip; 176 int ret; 177 uint32_t offset, dimms_per_chip; 178 nvlist_t *args; 179 180 topo_mod_dprintf(mod, "simple_dimm_label_mp() called\n"); 181 182 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 183 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 184 strerror(ret)); 185 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 186 } 187 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 188 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 189 strerror(ret)); 190 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 191 } 192 if ((ret = nvlist_lookup_uint32(args, "dimms_per_chip", 193 &dimms_per_chip)) != 0) { 194 topo_mod_dprintf(mod, "Failed to lookup 'dimms_per_chip' arg " 195 "(%s)\n", strerror(ret)); 196 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 197 } 198 if ((ret = nvlist_lookup_string(args, "order", &order)) != 0) { 199 topo_mod_dprintf(mod, "Failed to lookup 'order' arg (%s)\n", 200 strerror(ret)); 201 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 202 } 203 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 204 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 205 topo_mod_free(mod, order, BUFSZ); 206 /* topo errno already set */ 207 return (-1); 208 } 209 210 chip = topo_node_parent(topo_node_parent(node)); 211 212 if (strcasecmp(order, "forward") == 0) 213 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 214 (void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip), 215 (topo_node_instance(node) + offset)); 216 else if (strcasecmp(order, "reverse") == 0) 217 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 218 (void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip), 219 (((topo_node_instance(chip) + 1) * dimms_per_chip) 220 - (topo_node_instance(node)) - 1 + offset)); 221 else { 222 topo_mod_dprintf(mod, "Invalid value for order arg\n"); 223 topo_mod_free(mod, order, BUFSZ); 224 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 225 } 226 227 if (store_prop_val(mod, buf, "label", out) != 0) { 228 topo_mod_dprintf(mod, "Failed to set label\n"); 229 topo_mod_free(mod, order, BUFSZ); 230 /* topo errno already set */ 231 return (-1); 232 } 233 234 return (0); 235 } 236 237 /* 238 * This method assumes a correspondence between the dimm topo node instance 239 * number and the dimm slot label number, but unlike simple_chip_label_mp, the 240 * slot numbers aren't reused between CPU's. This method assumes there 241 * are 4 DIMM slots per chip. It takes the following two arguments: 242 * 243 * format: a string containing a printf-like format with a single %d token 244 * which this method computes 245 * 246 * i.e.: DIMM %d 247 * 248 * offset: a numeric offset that we'll number the dimms from. This is to 249 * allow for the fact that some systems number the dimm slots 250 * from zero and others may start from one 251 * 252 * order: "reverse" or "forward" - sets the direction of the correlation 253 * between dimm topo node instance number and DIMM slot number 254 */ 255 /* ARGSUSED */ 256 int 257 seq_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 258 nvlist_t *in, nvlist_t **out) 259 { 260 char *fmtstr, *order, buf[BUFSZ]; 261 int ret; 262 uint32_t offset; 263 nvlist_t *args; 264 tnode_t *chip; 265 266 topo_mod_dprintf(mod, "seq_dimm_label() called\n"); 267 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 268 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 269 strerror(ret)); 270 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 271 } 272 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 273 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 274 strerror(ret)); 275 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 276 } 277 if ((ret = nvlist_lookup_string(args, "order", &order)) != 0) { 278 topo_mod_dprintf(mod, "Failed to lookup 'order' arg (%s)\n", 279 strerror(ret)); 280 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 281 } 282 283 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 284 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 285 topo_mod_free(mod, order, BUFSZ); 286 /* topo errno already set */ 287 return (-1); 288 } 289 290 chip = topo_node_parent(topo_node_parent(node)); 291 292 if (strcasecmp(order, "forward") == 0) 293 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 294 (void) snprintf(buf, BUFSZ, fmtstr, ((topo_node_instance(node)) 295 + (topo_node_instance(chip) * 4) + offset)); 296 else if (strcasecmp(order, "reverse") == 0) 297 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 298 (void) snprintf(buf, BUFSZ, fmtstr, 299 (((topo_node_instance(chip) + 1) * 4) 300 - (topo_node_instance(node)) - 1 + offset)); 301 else { 302 topo_mod_dprintf(mod, "Invalid value for order arg\n"); 303 topo_mod_free(mod, order, BUFSZ); 304 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 305 } 306 307 if (store_prop_val(mod, buf, "label", out) != 0) { 308 topo_mod_dprintf(mod, "Failed to set label\n"); 309 topo_mod_free(mod, order, BUFSZ); 310 /* topo errno already set */ 311 return (-1); 312 } 313 314 return (0); 315 } 316 317 318 /* 319 * This is a somewhat generic property method for labelling the CPU sockets on 320 * x86/x64 platforms. This method assumes a correspondence between 321 * the chip topo node instance number and the CPU socket label number. It takes 322 * the following two arguments: 323 * 324 * format: a string containing a printf-like format with a single %d token 325 * which this method computes 326 * 327 * i.e.: CPU %d 328 * 329 * offset: a numeric offset that we'll number the CPU's from. This is to 330 * allow for the fact that some systems number the CPU sockets 331 * from zero and others start from one (like the X4X00-M2 systems) 332 */ 333 /* ARGSUSED */ 334 int 335 simple_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 336 nvlist_t *in, nvlist_t **out) 337 { 338 char *fmtstr, buf[BUFSZ]; 339 int ret; 340 uint32_t offset; 341 nvlist_t *args; 342 343 topo_mod_dprintf(mod, "simple_chip_label() called\n"); 344 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 345 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 346 strerror(ret)); 347 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 348 } 349 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 350 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 351 strerror(ret)); 352 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 353 } 354 355 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 356 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 357 /* topo errno already set */ 358 return (-1); 359 } 360 361 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 362 (void) snprintf(buf, BUFSZ, fmtstr, 363 (topo_node_instance(node) + offset)); 364 365 if (store_prop_val(mod, buf, "label", out) != 0) { 366 topo_mod_dprintf(mod, "Failed to set label\n"); 367 /* topo errno already set */ 368 return (-1); 369 } 370 371 return (0); 372 } 373 374 375 /* 376 * This is a custom property method for generating the CPU slot label for the 377 * Galaxy 4E/4F platforms. 378 * 379 * format: a string containing a printf-like format with a single %c token 380 * which this method computes 381 * 382 * i.e.: CPU %c 383 */ 384 /* ARGSUSED */ 385 int 386 g4_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 387 nvlist_t *in, nvlist_t **out) 388 { 389 char *fmtstr, buf[BUFSZ], slot_id; 390 int err, htid, mapidx; 391 uint32_t num_nodes; 392 /* 393 * G4 HT node ID to FRU label translation. The g4map array 394 * is indexed by (number of coherent nodes) / 2 - 1. 395 * The value for a given number of nodes is a char array 396 * indexed by node ID. 397 */ 398 const char *g4map[] = { 399 "AB", /* 2 nodes */ 400 "ADEH", /* 4 nodes */ 401 "ABDEFH", /* 6 nodes */ 402 "ACBDEFGH" /* 8 nodes */ 403 }; 404 405 topo_mod_dprintf(mod, "g4_chip_label() called\n"); 406 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 407 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 408 /* topo errno already set */ 409 return (-1); 410 } 411 /* 412 * The chip-properties property will not exist if this platform has 413 * AMD family 0x10 modules. In that case we don't want to treat it as a 414 * fatal error as that will cause calls like topo_prop_getprops to fail 415 * to return any properties on this node. Therefore, if the topo errno 416 * is set to ETOPO_PROP_NOENT, then we'll just set an empty label 417 * and return 0. If the topo errno is set to anything else we'll 418 * return -1. 419 */ 420 if (topo_prop_get_uint32(node, "chip-properties", "CoherentNodes", 421 &num_nodes, &err) != 0) { 422 if (err == ETOPO_PROP_NOENT) { 423 if (store_prop_val(mod, "", "label", out) != 0) { 424 topo_mod_dprintf(mod, "Failed to set label\n"); 425 /* topo errno already set */ 426 return (-1); 427 } 428 return (0); 429 } else { 430 topo_mod_dprintf(mod, "Failed to lookup 'CoherentNodes'" 431 "property\n"); 432 return (topo_mod_seterrno(mod, err)); 433 } 434 } 435 436 mapidx = num_nodes / 2 - 1; 437 htid = topo_node_instance(node); 438 439 /* HT nodes must number 0 .. num_nodes - 1 */ 440 if (htid >= num_nodes) { 441 topo_mod_dprintf(mod, "chip node instance range check failed:" 442 "num_nodes=%d, instance=%d\n", num_nodes, htid); 443 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 444 } 445 446 switch (num_nodes) { 447 case (2): 448 case (4): 449 case (6): 450 case (8): 451 /* htid is already range-checked */ 452 mapidx = num_nodes / 2 - 1; 453 slot_id = g4map[mapidx][htid]; 454 break; 455 default: 456 topo_mod_dprintf(mod, "Invalid number of CoherentNodes:" 457 " %d\n", num_nodes); 458 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 459 } 460 461 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 462 (void) snprintf(buf, BUFSZ, fmtstr, slot_id); 463 464 if (store_prop_val(mod, buf, "label", out) != 0) { 465 topo_mod_dprintf(mod, "Failed to set label\n"); 466 /* topo errno already set */ 467 return (-1); 468 } 469 470 return (0); 471 } 472 473 /* 474 * This is a somewhat generic property method for labelling the chip-select 475 * nodes on multi-socket AMD family 0x10 platforms. This is necessary because 476 * these platforms are not supported by the current AMD memory controller driver 477 * and therefore we're not able to discover the memory topology on AMD family 478 * 0x10 systems. As a result, instead of enumerating the installed dimms and 479 * their ranks, the chip enumerator generically enumerates all of the possible 480 * chip-selects beneath each dram channel. 481 * 482 * When we diagnose a dimm fault, the FRU fmri will be for the chip-select node, 483 * so we need to attach FRU labels to the chip-select nodes. 484 * 485 * format: a string containing a printf-like format with a two %d tokens 486 * for the cpu and dimm slot label numbers, which this method 487 * computes 488 * 489 * i.e.: CPU %d DIMM %d 490 * 491 * offset: a numeric offset that we'll number the dimms from. This is to 492 * allow for the fact that some systems may number the dimm slots 493 * from zero while others may start from one 494 * 495 * This function computes the DIMM slot number using the following formula: 496 * 497 * slot = cs - (cs % 2) + channel + offset 498 */ 499 /* ARGSUSED */ 500 int 501 simple_cs_label_mp(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 502 nvlist_t *in, nvlist_t **out) 503 { 504 char *fmtstr, buf[BUFSZ]; 505 tnode_t *chip, *chan; 506 int dimm_num, ret; 507 uint32_t offset; 508 nvlist_t *args; 509 510 topo_mod_dprintf(mod, "simple_cs_label_mp() called\n"); 511 512 if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) { 513 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 514 strerror(ret)); 515 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 516 } 517 if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) { 518 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 519 strerror(ret)); 520 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 521 } 522 if ((fmtstr = get_fmtstr(mod, in)) == NULL) { 523 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 524 /* topo errno already set */ 525 return (-1); 526 } 527 528 chip = topo_node_parent(topo_node_parent(topo_node_parent(node))); 529 chan = topo_node_parent(node); 530 531 dimm_num = topo_node_instance(node) - (topo_node_instance(node) % 2) 532 + topo_node_instance(chan) + offset; 533 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 534 (void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip), 535 dimm_num); 536 537 if (store_prop_val(mod, buf, "label", out) != 0) { 538 topo_mod_dprintf(mod, "Failed to set label\n"); 539 /* topo errno already set */ 540 return (-1); 541 } 542 543 return (0); 544 } 545