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