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, int *err) 42 { 43 char *fmtstr; 44 nvlist_t *args; 45 46 topo_mod_dprintf(mod, "get_fmtstr() called\n"); 47 48 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) { 49 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 50 strerror(errno)); 51 *err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 52 return (NULL); 53 } 54 if (nvlist_lookup_string(args, "format", &fmtstr) != 0) { 55 topo_mod_dprintf(mod, "Failed to lookup 'format' arg (%s)\n", 56 strerror(errno)); 57 *err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 58 nvlist_free(args); 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 err; 115 uint32_t offset; 116 nvlist_t *args; 117 118 topo_mod_dprintf(mod, "simple_dimm_label() called\n"); 119 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) { 120 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 121 strerror(errno)); 122 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 123 return (err); 124 } 125 if (nvlist_lookup_uint32(args, "offset", &offset) != 0) { 126 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 127 strerror(errno)); 128 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 129 nvlist_free(args); 130 return (err); 131 } 132 133 if ((fmtstr = get_fmtstr(mod, in, &err)) == NULL) { 134 topo_mod_dprintf(mod, "Failed to retrieve format arg\n"); 135 nvlist_free(args); 136 return (err); 137 } 138 139 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 140 (void) snprintf(buf, BUFSZ, fmtstr, 141 (topo_node_instance(node) + offset)); 142 143 if ((err = store_prop_val(mod, buf, "label", out)) != 0) { 144 topo_mod_dprintf(mod, "Failed to set label\n"); 145 nvlist_free(args); 146 return (err); 147 } 148 149 return (0); 150 } 151 152 153 /* 154 * This is a somewhat generic property method for labelling the dimm slots on 155 * multi-socket x86/x64 platforms. It takes the following two arguments: 156 * 157 * format: a string containing a printf-like format with a two %d tokens 158 * for the cpu and dimm slot label numbers, which this method 159 * computes 160 * 161 * i.e.: CPU %d DIMM %d 162 * 163 * offset: a numeric offset that we'll number the dimms from. This is to 164 * allow for the fact that some systems number the dimm slots 165 * from zero while others may start from one 166 * 167 * order: "reverse" or "forward" - sets the direction of the correlation 168 * between dimm topo node instance number and DIMM slot number 169 * 170 * dimms_per_chip: the number of DIMM slots per chip 171 */ 172 /* ARGSUSED */ 173 int 174 simple_dimm_label_mp(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 175 nvlist_t *in, nvlist_t **out) 176 { 177 char *fmtstr, *order, buf[BUFSZ]; 178 tnode_t *chip; 179 int err; 180 uint32_t offset, dimms_per_chip; 181 nvlist_t *args; 182 183 topo_mod_dprintf(mod, "simple_dimm_label_mp() called\n"); 184 185 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) { 186 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 187 strerror(errno)); 188 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 189 return (err); 190 } 191 if (nvlist_lookup_uint32(args, "offset", &offset) != 0) { 192 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 193 strerror(errno)); 194 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 195 nvlist_free(args); 196 return (err); 197 } 198 if (nvlist_lookup_uint32(args, "dimms_per_chip", &dimms_per_chip) 199 != 0) { 200 topo_mod_dprintf(mod, "Failed to lookup 'dimms_per_chip' arg " 201 "(%s)\n", strerror(errno)); 202 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 203 nvlist_free(args); 204 return (err); 205 } 206 if (nvlist_lookup_string(args, "order", &order) != 0) { 207 topo_mod_dprintf(mod, "Failed to lookup 'order' arg (%s)\n", 208 strerror(errno)); 209 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 210 nvlist_free(args); 211 return (err); 212 } 213 if ((fmtstr = get_fmtstr(mod, in, &err)) == NULL) { 214 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 215 topo_mod_free(mod, order, BUFSZ); 216 nvlist_free(args); 217 return (err); 218 } 219 220 chip = topo_node_parent(topo_node_parent(node)); 221 222 if (strcasecmp(order, "forward") == 0) 223 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 224 (void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip), 225 (topo_node_instance(node) + offset)); 226 else if (strcasecmp(order, "reverse") == 0) 227 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 228 (void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip), 229 (((topo_node_instance(chip) + 1) * dimms_per_chip) 230 - (topo_node_instance(node)) - 1 + offset)); 231 else { 232 topo_mod_dprintf(mod, "Invalid value for order arg\n"); 233 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 234 topo_mod_free(mod, order, BUFSZ); 235 nvlist_free(args); 236 return (err); 237 } 238 239 if ((err = store_prop_val(mod, buf, "label", out)) != 0) { 240 topo_mod_dprintf(mod, "Failed to set label\n"); 241 topo_mod_free(mod, order, BUFSZ); 242 nvlist_free(args); 243 return (err); 244 } 245 246 return (0); 247 } 248 249 /* 250 * This method assumes a correspondence between the dimm topo node instance 251 * number and the dimm slot label number, but unlike simple_chip_label_mp, the 252 * slot numbers aren't reused between CPU's. This method assumes there 253 * are 4 DIMM slots per chip. It takes the following two arguments: 254 * 255 * format: a string containing a printf-like format with a single %d token 256 * which this method computes 257 * 258 * i.e.: DIMM %d 259 * 260 * offset: a numeric offset that we'll number the dimms from. This is to 261 * allow for the fact that some systems number the dimm slots 262 * from zero and others may start from one 263 * 264 * order: "reverse" or "forward" - sets the direction of the correlation 265 * between dimm topo node instance number and DIMM slot number 266 */ 267 /* ARGSUSED */ 268 int 269 seq_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 270 nvlist_t *in, nvlist_t **out) 271 { 272 char *fmtstr, *order, buf[BUFSZ]; 273 int err; 274 uint32_t offset; 275 nvlist_t *args; 276 tnode_t *chip; 277 278 topo_mod_dprintf(mod, "seq_dimm_label() called\n"); 279 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) { 280 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 281 strerror(errno)); 282 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 283 return (err); 284 } 285 if (nvlist_lookup_uint32(args, "offset", &offset) != 0) { 286 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 287 strerror(errno)); 288 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 289 nvlist_free(args); 290 return (err); 291 } 292 if (nvlist_lookup_string(args, "order", &order) != 0) { 293 topo_mod_dprintf(mod, "Failed to lookup 'order' arg (%s)\n", 294 strerror(errno)); 295 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 296 nvlist_free(args); 297 return (err); 298 } 299 300 if ((fmtstr = get_fmtstr(mod, in, &err)) == NULL) { 301 topo_mod_dprintf(mod, "Failed to retrieve 'fmtstr' arg\n"); 302 topo_mod_free(mod, order, BUFSZ); 303 nvlist_free(args); 304 return (err); 305 } 306 307 chip = topo_node_parent(topo_node_parent(node)); 308 309 if (strcasecmp(order, "forward") == 0) 310 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 311 (void) snprintf(buf, BUFSZ, fmtstr, ((topo_node_instance(node)) 312 + (topo_node_instance(chip) * 4) + offset)); 313 else if (strcasecmp(order, "reverse") == 0) 314 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 315 (void) snprintf(buf, BUFSZ, fmtstr, 316 (((topo_node_instance(chip) + 1) * 4) 317 - (topo_node_instance(node)) - 1 + offset)); 318 else { 319 topo_mod_dprintf(mod, "Invalid value for order arg\n"); 320 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 321 topo_mod_free(mod, order, BUFSZ); 322 nvlist_free(args); 323 return (err); 324 } 325 326 if ((err = store_prop_val(mod, buf, "label", out)) != 0) { 327 topo_mod_dprintf(mod, "Failed to set label\n"); 328 topo_mod_free(mod, order, BUFSZ); 329 nvlist_free(args); 330 return (err); 331 } 332 333 return (0); 334 } 335 336 337 /* 338 * This is a somewhat generic property method for labelling the CPU sockets on 339 * x86/x64 platforms. This method assumes a correspondence between 340 * the chip topo node instance number and the CPU socket label number. It takes 341 * the following two arguments: 342 * 343 * format: a string containing a printf-like format with a single %d token 344 * which this method computes 345 * 346 * i.e.: CPU %d 347 * 348 * offset: a numeric offset that we'll number the CPU's from. This is to 349 * allow for the fact that some systems number the CPU sockets 350 * from zero and others start from one (like the X4X00-M2 systems) 351 */ 352 /* ARGSUSED */ 353 int 354 simple_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 355 nvlist_t *in, nvlist_t **out) 356 { 357 char *fmtstr, buf[BUFSZ]; 358 int err; 359 uint32_t offset; 360 nvlist_t *args; 361 362 topo_mod_dprintf(mod, "simple_chip_label() called\n"); 363 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) { 364 topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n", 365 strerror(errno)); 366 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 367 return (err); 368 } 369 if (nvlist_lookup_uint32(args, "offset", &offset) != 0) { 370 topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n", 371 strerror(errno)); 372 err = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 373 nvlist_free(args); 374 return (err); 375 } 376 377 if ((fmtstr = get_fmtstr(mod, in, &err)) == NULL) { 378 topo_mod_dprintf(mod, "Failed to retrieve format arg\n"); 379 nvlist_free(args); 380 return (err); 381 } 382 383 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 384 (void) snprintf(buf, BUFSZ, fmtstr, 385 (topo_node_instance(node) + offset)); 386 387 if ((err = store_prop_val(mod, buf, "label", out)) != 0) { 388 topo_mod_dprintf(mod, "Failed to set label\n"); 389 nvlist_free(args); 390 return (err); 391 } 392 393 return (0); 394 } 395 396 397 /* 398 * This is a custom property method for generating the CPU slot label for the 399 * Galaxy 4E/4F platforms. 400 * 401 * format: a string containing a printf-like format with a single %c token 402 * which this method computes 403 * 404 * i.e.: CPU %c 405 */ 406 /* ARGSUSED */ 407 int 408 g4_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 409 nvlist_t *in, nvlist_t **out) 410 { 411 char *fmtstr, buf[BUFSZ], slot_id; 412 int err, htid, mapidx; 413 uint32_t num_nodes; 414 /* 415 * G4 HT node ID to FRU label translation. The g4map array 416 * is indexed by (number of coherent nodes) / 2 - 1. 417 * The value for a given number of nodes is a char array 418 * indexed by node ID. 419 */ 420 const char *g4map[] = { 421 "AB", /* 2 nodes */ 422 "ADEH", /* 4 nodes */ 423 "ABDEFH", /* 6 nodes */ 424 "ACBDEFGH" /* 8 nodes */ 425 }; 426 427 topo_mod_dprintf(mod, "g4_chip_label() called\n"); 428 if ((fmtstr = get_fmtstr(mod, in, &err)) == NULL) { 429 topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n"); 430 return (err); 431 } 432 433 if (topo_prop_get_uint32(node, "chip-properties", "CoherentNodes", 434 &num_nodes, &err) != 0) { 435 topo_mod_dprintf(mod, "Failed to lookup 'CoherentNodes'" 436 "property\n"); 437 return (err); 438 } 439 440 mapidx = num_nodes / 2 - 1; 441 htid = topo_node_instance(node); 442 443 /* HT nodes must number 0 .. num_nodes - 1 */ 444 if (htid >= num_nodes) { 445 topo_mod_dprintf(mod, "chip node instance range check failed:" 446 "num_nodes=%d, instance=%d\n", num_nodes, htid); 447 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 448 } 449 450 switch (num_nodes) { 451 case (2): 452 case (4): 453 case (6): 454 case (8): 455 /* htid is already range-checked */ 456 mapidx = num_nodes / 2 - 1; 457 slot_id = g4map[mapidx][htid]; 458 break; 459 default: 460 topo_mod_dprintf(mod, "Invalid number of CoherentNodes:" 461 " %d\n", num_nodes); 462 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 463 } 464 465 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 466 (void) snprintf(buf, BUFSZ, fmtstr, slot_id); 467 468 if ((err = store_prop_val(mod, buf, "label", out)) != 0) { 469 topo_mod_dprintf(mod, "Failed to set label\n"); 470 return (err); 471 } 472 473 return (0); 474 } 475