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 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Support function for the i86pc chip enumerator 31 */ 32 33 #include <sys/types.h> 34 #include <stdarg.h> 35 #include <strings.h> 36 #include <fm/fmd_fmri.h> 37 #include <sys/fm/protocol.h> 38 39 #include "chip.h" 40 41 /* 42 * Whinge a debug message via topo_mod_dprintf and increment the 43 * given error counter. 44 */ 45 void 46 whinge(topo_mod_t *mod, int *nerr, const char *fmt, ...) 47 { 48 va_list ap; 49 char buf[160]; 50 51 if (nerr != NULL) 52 ++*nerr; 53 54 va_start(ap, fmt); 55 (void) vsnprintf(buf, sizeof (buf), fmt, ap); 56 va_end(ap); 57 58 topo_mod_dprintf(mod, "%s", buf); 59 } 60 61 /* 62 * Given an nvpair of a limited number of data types, extract the property 63 * name and value and add that combination to the given node in the 64 * specified property group using the corresponding topo_prop_set_* function 65 * for the data type. Return 1 on success, otherwise 0. 66 */ 67 int 68 nvprop_add(topo_mod_t *mod, nvpair_t *nvp, const char *pgname, tnode_t *node) 69 { 70 int success = 0; 71 int err; 72 char *pname = nvpair_name(nvp); 73 74 switch (nvpair_type(nvp)) { 75 case DATA_TYPE_BOOLEAN_VALUE: { 76 boolean_t val; 77 78 if (nvpair_value_boolean_value(nvp, &val) == 0 && 79 topo_prop_set_string(node, pgname, pname, 80 TOPO_PROP_IMMUTABLE, val ? "true" : "false", &err) == 0) 81 success = 1; 82 break; 83 } 84 85 case DATA_TYPE_UINT32: { 86 uint32_t val; 87 88 if (nvpair_value_uint32(nvp, &val) == 0 && 89 topo_prop_set_uint32(node, pgname, pname, 90 TOPO_PROP_IMMUTABLE, val, &err) == 0) 91 success = 1; 92 break; 93 } 94 95 case DATA_TYPE_UINT64: { 96 uint64_t val; 97 98 if (nvpair_value_uint64(nvp, &val) == 0 && 99 topo_prop_set_uint64(node, pgname, pname, 100 TOPO_PROP_IMMUTABLE, val, &err) == 0) 101 success = 1; 102 break; 103 } 104 105 case DATA_TYPE_UINT32_ARRAY: { 106 uint32_t *arrp; 107 uint_t nelem; 108 109 if (nvpair_value_uint32_array(nvp, &arrp, &nelem) == 0 && 110 nelem > 0 && topo_prop_set_uint32_array(node, pgname, pname, 111 TOPO_PROP_IMMUTABLE, arrp, nelem, &err) == 0) 112 success = 1; 113 break; 114 } 115 116 case DATA_TYPE_STRING: { 117 char *str; 118 119 if (nvpair_value_string(nvp, &str) == 0 && 120 topo_prop_set_string(node, pgname, pname, 121 TOPO_PROP_IMMUTABLE, str, &err) == 0) 122 success = 1; 123 break; 124 } 125 126 default: 127 whinge(mod, &err, "nvprop_add: Can't handle type %d for " 128 "'%s' in property group %s of %s node\n", 129 nvpair_type(nvp), pname, pgname, topo_node_name(node)); 130 break; 131 } 132 133 return (success ? 0 : 1); 134 } 135 136 /* 137 * Lookup string data named pname in the given kstat_t and add that 138 * as property named pname in the given property group pgname on the indicated 139 * topo node. Fill pvalp with a pointer to the string value, valid until 140 * kstat_close is called (or the given kstat_t is otherwise invalidated). 141 */ 142 int 143 add_kstat_strprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp, 144 const char *pgname, const char *pname, const char **pvalp) 145 { 146 const char *pval; 147 kstat_named_t *k; 148 int err = 0; 149 150 if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL) 151 return (-1); 152 pval = k->value.str.addr.ptr; 153 154 if (topo_prop_set_string(node, pgname, pname, 155 TOPO_PROP_IMMUTABLE, pval, &err) == 0) { 156 if (pvalp) 157 *pvalp = pval; 158 return (0); 159 } else { 160 whinge(mod, &err, "chip_strprop: failed to add '%s'\n", 161 pname); 162 return (-1); 163 } 164 } 165 166 /* 167 * Lookup an int32 item named pname in the given kstat_t and add that 168 * as property named pname in the given property group pgname on the indicated 169 * topo node. Fill pvalp with the property value. 170 */ 171 int 172 add_kstat_longprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp, 173 const char *pgname, const char *pname, int32_t *pvalp) 174 { 175 kstat_named_t *k; 176 int32_t pval; 177 int err; 178 179 if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL) 180 return (-1); 181 pval = k->value.l; 182 183 if (topo_prop_set_int32(node, pgname, pname, 184 TOPO_PROP_IMMUTABLE, pval, &err) == 0) { 185 if (pvalp) 186 *pvalp = pval; 187 return (0); 188 } else { 189 whinge(mod, &err, "chip_longprop: failed to add '%s'\n", 190 pname); 191 return (-1); 192 } 193 } 194 195 /* 196 * In a given kstat_t lookup a variable number of int32 properties named in 197 * const char * varargs and each each in the given property group on the 198 * node. Fill an array of the retrieved values. 199 */ 200 int 201 add_kstat_longprops(topo_mod_t *mod, tnode_t *node, kstat_t *ksp, 202 const char *pgname, int32_t *pvalap, ...) 203 { 204 const char *pname; 205 va_list ap; 206 int nerr = 0; 207 208 va_start(ap, pvalap); 209 while ((pname = va_arg(ap, const char *)) != NULL) { 210 if (add_kstat_longprop(mod, node, ksp, pgname, pname, 211 pvalap) != 0) 212 nerr++; /* have whinged elsewhere */ 213 214 if (pvalap != NULL) 215 ++pvalap; 216 } 217 va_end(ap); 218 219 return (nerr == 0 ? 0 : -1); 220 } 221 222 /* 223 * Construct an hc scheme resource FMRI for a node named name with 224 * instance number inst, parented by the given parent node pnode. 225 */ 226 int 227 mkrsrc(topo_mod_t *mod, tnode_t *pnode, const char *name, int inst, 228 nvlist_t *auth, nvlist_t **nvl) 229 { 230 *nvl = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, name, 231 inst, NULL, auth, NULL, NULL, NULL); 232 return (nvl != NULL ? 0 : -1); /* caller must free nvlist */ 233 } 234 235 /* 236 * Construct a cpu scheme FMRI with the given data; the caller must free 237 * the allocated nvlist with nvlist_free(). 238 */ 239 nvlist_t * 240 cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask) 241 { 242 int err; 243 nvlist_t *asru; 244 245 if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0) 246 return (NULL); 247 248 err = nvlist_add_uint8(asru, FM_VERSION, FM_CPU_SCHEME_VERSION); 249 err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU); 250 err |= nvlist_add_uint32(asru, FM_FMRI_CPU_ID, cpuid); 251 err |= nvlist_add_uint8(asru, FM_FMRI_CPU_MASK, cpumask); 252 if (s != NULL) 253 err |= nvlist_add_string(asru, FM_FMRI_CPU_SERIAL_ID, s); 254 if (err != 0) { 255 nvlist_free(asru); 256 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL); 257 return (NULL); 258 } 259 260 return (asru); 261 } 262 263 /* 264 * Construct a mem scheme FMRI for the given unum string; the caller must 265 * free the allocated nvlist with nvlist_free(). 266 */ 267 nvlist_t * 268 mem_fmri_create(topo_mod_t *mod, const char *unum) 269 { 270 nvlist_t *asru; 271 272 if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0) 273 return (NULL); 274 275 if (nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0 || 276 nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION) != 0 || 277 nvlist_add_string(asru, FM_FMRI_MEM_UNUM, unum) != 0) { 278 nvlist_free(asru); 279 return (NULL); 280 } 281 282 return (asru); 283 } 284 285 /* 286 * Registered method for asru computation for rank nodes. The 'node' 287 * argument identifies the node for which we seek an asru. The 'in' 288 * argument is used to select which asru we will return, as follows: 289 * 290 * - the node name must be "dimm" or "rank" 291 * - if 'in' is NULL then return any statically defined asru for this node 292 * - if 'in' is an "hc" scheme fmri then we construct a "mem" scheme asru 293 * with unum being the hc path to the dimm or rank (this method is called 294 * as part of dynamic asru computation for rank nodes only, but 295 * it is also called directly to construct a "mem" scheme asru for a dimm 296 * node) 297 * - if 'in' in addition includes an hc-specific member which specifies 298 * asru-physaddr or asru-offset then these are includes in the "mem" scheme 299 * asru as additional members physaddr and offset 300 */ 301 int 302 mem_asru_create(topo_mod_t *mod, nvlist_t *fmri, nvlist_t **asru) 303 { 304 int incl_pa = 0, incl_offset = 0; 305 nvlist_t *hcsp, *ap; 306 char *unum, *scheme; 307 uint64_t pa, offset; 308 int err = 0; 309 310 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0 || 311 strcmp(scheme, FM_FMRI_SCHEME_HC) != 0) 312 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 313 314 if (nvlist_lookup_nvlist(fmri, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) { 315 if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_PHYSADDR, 316 &pa) == 0) 317 incl_pa = 1; 318 319 if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_OFFSET, 320 &offset) == 0) 321 incl_offset = 1; 322 } 323 324 /* use 'fmri' to obtain resource path; could use node resource */ 325 if (topo_mod_nvl2str(mod, fmri, &unum) < 0) 326 return (-1); /* mod errno set */ 327 328 ap = mem_fmri_create(mod, unum); 329 topo_mod_strfree(mod, unum); 330 if (ap == NULL) 331 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 332 333 if (incl_pa) 334 err += nvlist_add_uint64(ap, FM_FMRI_MEM_PHYSADDR, pa) != 0; 335 if (incl_offset) 336 err += nvlist_add_uint64(ap, FM_FMRI_MEM_OFFSET, offset) != 0; 337 338 if (err != 0) { 339 nvlist_free(ap); 340 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 341 } 342 343 *asru = ap; 344 345 return (0); 346 } 347 348 /*ARGSUSED*/ 349 int 350 mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version, 351 nvlist_t *in, nvlist_t **out) 352 { 353 nvlist_t *asru; 354 nvlist_t *args, *pargs; 355 int err; 356 357 if (strcmp(topo_node_name(node), RANK_NODE_NAME) != 0 && 358 strcmp(topo_node_name(node), DIMM_NODE_NAME) != 0 && 359 strcmp(topo_node_name(node), CS_NODE_NAME) != 0) 360 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 361 362 if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) 363 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 364 365 if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs)) != 0) { 366 if (err == ENOENT) { 367 if (topo_mod_nvdup(mod, args, &asru) < 0) 368 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 369 } else { 370 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 371 } 372 } else if (mem_asru_create(mod, pargs, &asru) != 0) { 373 return (-1); /* mod errno already set */ 374 } 375 376 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) { 377 nvlist_free(asru); 378 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 379 } 380 381 err = nvlist_add_string(*out, TOPO_PROP_VAL_NAME, TOPO_PROP_ASRU); 382 err |= nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_FMRI); 383 err |= nvlist_add_nvlist(*out, TOPO_PROP_VAL_VAL, asru); 384 if (err != 0) { 385 nvlist_free(asru); 386 nvlist_free(*out); 387 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 388 } 389 390 nvlist_free(asru); 391 392 return (0); 393 } 394 395 /* 396 * If we're getting called then the question of whether this dimm is plugged 397 * in has already been answered. What we don't know for sure is whether it's 398 * the same dimm or a different one plugged in the same slot. To check, we 399 * try and compare the serial numbers on the dimm in the current topology with 400 * the serial num from the unum fmri that got passed into this function as the 401 * argument. 402 * 403 * In the event we encounter problems comparing serials or if a comparison isn't 404 * possible, we err on the side of caution and set is_present to TRUE. 405 */ 406 /* ARGSUSED */ 407 int 408 rank_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, 409 nvlist_t *in, nvlist_t **out) 410 { 411 tnode_t *dimmnode; 412 int err, is_present = 1; 413 nvlist_t *unum; 414 char *curr_serial, *old_serial = NULL; 415 416 /* 417 * If a serial number for the dimm was available at the time of the 418 * fault, it will have been added as a string to the unum nvlist 419 */ 420 unum = in; 421 if (nvlist_lookup_string(unum, FM_FMRI_HC_SERIAL_ID, &old_serial) != 0) 422 goto done; 423 424 /* 425 * If the current serial number is available for the DIMM that this rank 426 * belongs to, it will be accessible as a property on the parent (dimm) 427 * node. 428 */ 429 dimmnode = topo_node_parent(node); 430 if (topo_prop_get_string(dimmnode, TOPO_PGROUP_PROTOCOL, 431 FM_FMRI_HC_SERIAL_ID, &curr_serial, &err) != 0) { 432 if (err != ETOPO_PROP_NOENT) { 433 whinge(mod, &err, "rank_fmri_present: Unexpected error " 434 "retrieving serial from node"); 435 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 436 } else 437 goto done; 438 } 439 440 if (strcmp(old_serial, curr_serial) != 0) 441 is_present = 0; 442 443 topo_mod_strfree(mod, curr_serial); 444 done: 445 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) { 446 whinge(mod, &err, 447 "rank_fmri_present: failed to allocate nvlist!"); 448 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 449 } 450 451 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, is_present) != 0) { 452 nvlist_free(*out); 453 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 454 } 455 456 return (0); 457 } 458 459 /* 460 * If we're getting called then the question of whether this dimm is plugged 461 * in has already been answered. What we don't know for sure is whether it's 462 * the same dimm or a different one plugged in the same slot. To check, we 463 * try and compare the serial numbers on the dimm in the current topology with 464 * the serial num from the unum fmri that got passed into this function as the 465 * argument. 466 * 467 * In the event we encounter problems comparing serials or if a comparison isn't 468 * possible, we err on the side of caution and set is_present to TRUE. 469 */ 470 /* ARGSUSED */ 471 int 472 rank_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version, 473 nvlist_t *in, nvlist_t **out) 474 { 475 tnode_t *dimmnode; 476 int err, rval = FMD_OBJ_STATE_UNKNOWN; 477 nvlist_t *unum; 478 char *curr_serial, *old_serial = NULL; 479 480 /* 481 * If a serial number for the dimm was available at the time of the 482 * fault, it will have been added as a string to the unum nvlist 483 */ 484 unum = in; 485 if (nvlist_lookup_string(unum, FM_FMRI_HC_SERIAL_ID, &old_serial) != 0) 486 goto done; 487 488 /* 489 * If the current serial number is available for the DIMM that this rank 490 * belongs to, it will be accessible as a property on the parent (dimm) 491 * node. 492 */ 493 dimmnode = topo_node_parent(node); 494 if (topo_prop_get_string(dimmnode, TOPO_PGROUP_PROTOCOL, 495 FM_FMRI_HC_SERIAL_ID, &curr_serial, &err) != 0) { 496 if (err != ETOPO_PROP_NOENT) { 497 whinge(mod, &err, "rank_fmri_present: Unexpected error " 498 "retrieving serial from node"); 499 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 500 } else 501 goto done; 502 } 503 504 if (strcmp(old_serial, curr_serial) != 0) 505 rval = FMD_OBJ_STATE_REPLACED; 506 else 507 rval = FMD_OBJ_STATE_STILL_PRESENT; 508 509 topo_mod_strfree(mod, curr_serial); 510 done: 511 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) { 512 whinge(mod, &err, 513 "rank_fmri_present: failed to allocate nvlist!"); 514 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 515 } 516 517 if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) { 518 nvlist_free(*out); 519 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 520 } 521 522 return (0); 523 } 524