1 2 /* 3 * CDDL HEADER START 4 * 5 * The contents of this file are subject to the terms of the 6 * Common Development and Distribution License (the "License"). 7 * You may not use this file except in compliance with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #include <strings.h> 29 #include <umem.h> 30 #include <fm/topo_mod.h> 31 #include <fm/fmd_fmri.h> 32 #include <fm/fmd_agent.h> 33 #include <sys/fm/protocol.h> 34 35 #include <mem_mdesc.h> 36 37 /* 38 * This enumerator creates mem-schemed nodes for each dimm found in the 39 * sun4v Physical Resource Inventory (PRI). 40 * Each node exports five methods: present(), expand(), unusable(), replaced(), 41 * and contains(). 42 * 43 */ 44 45 #define PLATFORM_MEM_NAME "platform-mem" 46 #define PLATFORM_MEM_VERSION TOPO_VERSION 47 #define MEM_NODE_NAME "mem" 48 49 50 /* Forward declaration */ 51 static int mem_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 52 topo_instance_t, void *, void *); 53 static void mem_release(topo_mod_t *, tnode_t *); 54 static int mem_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 55 nvlist_t **); 56 static int mem_replaced(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 57 nvlist_t **); 58 static int mem_expand(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 59 nvlist_t **); 60 static int mem_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 61 nvlist_t **); 62 static int mem_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 63 nvlist_t **); 64 65 static const topo_modops_t mem_ops = 66 { mem_enum, mem_release }; 67 static const topo_modinfo_t mem_info = 68 { PLATFORM_MEM_NAME, FM_FMRI_SCHEME_MEM, PLATFORM_MEM_VERSION, 69 &mem_ops }; 70 71 static const topo_method_t mem_methods[] = { 72 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, 73 TOPO_METH_PRESENT_VERSION, TOPO_STABILITY_INTERNAL, mem_present }, 74 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC, 75 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, mem_replaced }, 76 { TOPO_METH_EXPAND, TOPO_METH_EXPAND_DESC, 77 TOPO_METH_EXPAND_VERSION, TOPO_STABILITY_INTERNAL, mem_expand }, 78 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, 79 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, mem_unusable }, 80 { TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC, 81 TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, mem_contains }, 82 { NULL } 83 }; 84 85 int 86 _topo_init(topo_mod_t *mod) 87 { 88 md_mem_info_t *mem; 89 90 if (getenv("TOPOPLATFORMMEMDBG")) 91 topo_mod_setdebug(mod); 92 topo_mod_dprintf(mod, "initializing %s enumerator\n", 93 PLATFORM_MEM_NAME); 94 95 if ((mem = topo_mod_zalloc(mod, sizeof (md_mem_info_t))) == NULL) 96 return (-1); 97 98 if (mem_mdesc_init(mod, mem) != 0) { 99 topo_mod_dprintf(mod, "failed to get dimms from the PRI/MD\n"); 100 topo_mod_free(mod, mem, sizeof (md_mem_info_t)); 101 return (-1); 102 } 103 104 topo_mod_setspecific(mod, (void *)mem); 105 106 if (topo_mod_register(mod, &mem_info, TOPO_VERSION) != 0) { 107 topo_mod_dprintf(mod, "failed to register %s: %s\n", 108 PLATFORM_MEM_NAME, topo_mod_errmsg(mod)); 109 mem_mdesc_fini(mod, mem); 110 topo_mod_free(mod, mem, sizeof (md_mem_info_t)); 111 return (-1); 112 } 113 114 topo_mod_dprintf(mod, "%s enumerator inited\n", PLATFORM_MEM_NAME); 115 116 return (0); 117 } 118 119 void 120 _topo_fini(topo_mod_t *mod) 121 { 122 md_mem_info_t *mem; 123 124 mem = (md_mem_info_t *)topo_mod_getspecific(mod); 125 126 mem_mdesc_fini(mod, mem); 127 128 topo_mod_free(mod, mem, sizeof (md_mem_info_t)); 129 130 topo_mod_unregister(mod); 131 132 } 133 134 /*ARGSUSED*/ 135 static int 136 mem_present(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 137 nvlist_t *in, nvlist_t **out) 138 { 139 uint8_t version; 140 char **nvlserids; 141 size_t n, nserids; 142 uint32_t present = 0; 143 md_mem_info_t *mem = (md_mem_info_t *)topo_mod_getspecific(mod); 144 145 /* sun4v platforms all support dimm serial numbers */ 146 147 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || 148 version > FM_MEM_SCHEME_VERSION || 149 nvlist_lookup_string_array(in, FM_FMRI_MEM_SERIAL_ID, 150 &nvlserids, &nserids) != 0) { 151 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 152 } 153 154 /* Find the dimm entry */ 155 for (n = 0; n < nserids; n++) { 156 if (mem_get_dimm_by_sn(nvlserids[n], mem) != NULL) { 157 present = 1; 158 break; 159 } 160 } 161 162 /* return the present status */ 163 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 164 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 165 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) { 166 nvlist_free(*out); 167 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 168 } 169 170 return (0); 171 } 172 173 /*ARGSUSED*/ 174 static int 175 mem_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 176 nvlist_t *in, nvlist_t **out) 177 { 178 uint8_t version; 179 char **nvlserids; 180 size_t n, nserids; 181 uint32_t rval = FMD_OBJ_STATE_NOT_PRESENT; 182 md_mem_info_t *mem = (md_mem_info_t *)topo_mod_getspecific(mod); 183 184 /* sun4v platforms all support dimm serial numbers */ 185 186 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || 187 version > FM_MEM_SCHEME_VERSION || 188 nvlist_lookup_string_array(in, FM_FMRI_MEM_SERIAL_ID, 189 &nvlserids, &nserids) != 0) { 190 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 191 } 192 193 /* Find the dimm entry */ 194 for (n = 0; n < nserids; n++) { 195 if (mem_get_dimm_by_sn(nvlserids[n], mem) != NULL) { 196 rval = FMD_OBJ_STATE_STILL_PRESENT; 197 break; 198 } 199 } 200 201 /* return the replaced status */ 202 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 203 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 204 if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) { 205 nvlist_free(*out); 206 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 207 } 208 209 return (0); 210 } 211 212 void 213 mem_strarray_free(topo_mod_t *mod, char **arr, size_t dim) 214 { 215 int i; 216 217 for (i = 0; i < dim; i++) { 218 if (arr[i] != NULL) 219 topo_mod_strfree(mod, arr[i]); 220 } 221 topo_mod_free(mod, arr, sizeof (char *) * dim); 222 } 223 224 /* 225 * Niagara-1, Niagara-2, and Victoria Falls all have physical address 226 * spaces of 40 bits. 227 */ 228 229 #define MEM_PHYS_ADDRESS_LIMIT 0x10000000000ULL 230 231 /* 232 * The 'mask' argument to extract_bits has 1's in those bit positions of 233 * the physical address used to select the DIMM (or set of DIMMs) which will 234 * store the contents of the physical address. If we extract those bits, ie. 235 * remove them and collapse the holes, the result is the 'address' within the 236 * DIMM or set of DIMMs where the contents are stored. 237 */ 238 239 static uint64_t 240 extract_bits(uint64_t paddr, uint64_t mask) 241 { 242 uint64_t from, to; 243 uint64_t result = 0; 244 245 to = 1; 246 for (from = 1; from <= MEM_PHYS_ADDRESS_LIMIT; from <<= 1) { 247 if ((from & mask) == 0) { 248 if ((from & paddr) != 0) 249 result |= to; 250 to <<= 1; 251 } 252 } 253 return (result); 254 } 255 256 /* 257 * insert_bits is the reverse operation to extract_bits. Where extract_bits 258 * removes from the physical address those bits which select a DIMM or set 259 * of DIMMs, insert_bits reconstitutes a physical address given the DIMM 260 * selection 'mask' and the 'value' for the address bits denoted by 1s in 261 * the 'mask'. 262 */ 263 static uint64_t 264 insert_bits(uint64_t offset, uint64_t mask, uint64_t value) 265 { 266 uint64_t result = 0; 267 uint64_t from, to; 268 269 from = 1; 270 for (to = 1; to <= MEM_PHYS_ADDRESS_LIMIT; to <<= 1) { 271 if ((to & mask) == 0) { 272 if ((offset & from) != 0) 273 result |= to; 274 from <<= 1; 275 } else { 276 result |= to & value; 277 } 278 } 279 return (result); 280 } 281 282 uint64_t 283 calc_phys_addr(mem_seg_map_t *seg, char *ds, uint64_t offset) 284 { 285 mem_bank_map_t *bm; 286 mem_dimm_list_t *dl; 287 288 for (bm = seg->sm_grp->mg_bank; bm != NULL; bm = bm->bm_grp) { 289 dl = bm->bm_dlist; 290 while (dl != NULL) { 291 if (strcmp(dl->dl_dimm->dm_serid, ds) == 0) 292 return (insert_bits(offset<<bm->bm_shift, 293 bm->bm_mask, bm->bm_match)); 294 dl = dl->dl_next; 295 } 296 } 297 return ((uint64_t)-1); 298 } 299 300 void 301 mem_expand_opt(topo_mod_t *mod, nvlist_t *nvl, char **serids) 302 { 303 md_mem_info_t *mem = (md_mem_info_t *)topo_mod_getspecific(mod); 304 mem_seg_map_t *seg; 305 mem_bank_map_t *bm; 306 uint64_t offset, physaddr; 307 308 /* 309 * The following additional expansions are all optional. 310 * Failure to retrieve a data value, or failure to add it 311 * successfully to the FMRI, does NOT cause a failure of 312 * fmd_fmri_expand. All optional expansions will be attempted 313 * once expand_opt is entered. 314 */ 315 316 if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &offset) == 0) { 317 for (seg = mem->mem_seg; seg != NULL; seg = seg->sm_next) { 318 physaddr = calc_phys_addr(seg, *serids, offset); 319 if (physaddr >= seg->sm_base && 320 physaddr < seg->sm_base + seg->sm_size) { 321 (void) nvlist_add_uint64(nvl, 322 FM_FMRI_MEM_PHYSADDR, physaddr); 323 } 324 } 325 } else if (nvlist_lookup_uint64(nvl, 326 FM_FMRI_MEM_PHYSADDR, &physaddr) == 0) { 327 for (seg = mem->mem_seg; seg != NULL; seg = seg->sm_next) { 328 if (physaddr >= seg->sm_base && 329 physaddr < seg->sm_base + seg->sm_size) { 330 bm = seg->sm_grp->mg_bank; 331 /* 332 * The mask & shift values for all banks in a 333 * segment are always the same; only the match 334 * values differ, in order to specify a 335 * dimm-pair. But we already have a full unum. 336 */ 337 offset = extract_bits(physaddr, 338 bm->bm_mask) >> bm->bm_shift; 339 (void) (nvlist_add_uint64(nvl, 340 FM_FMRI_MEM_OFFSET, offset)); 341 } 342 } 343 } 344 } 345 346 /* 347 * The sun4v mem: scheme expand() now assumes that the FMRI -has- serial 348 * numbers, therefore we should never have to call mem_unum_burst again. 349 * Part numbers will be supplied in hc: scheme from the hc: enumeration. 350 * What's left: physical address and offset calculations. 351 */ 352 353 /*ARGSUSED*/ 354 static int 355 mem_expand(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 356 nvlist_t *in, nvlist_t **out) 357 { 358 int rc; 359 uint8_t version; 360 char *unum, **nvlserids; 361 size_t nserids; 362 363 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || 364 version > FM_MEM_SCHEME_VERSION || 365 nvlist_lookup_string(in, FM_FMRI_MEM_UNUM, &unum) != 0) 366 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 367 368 if ((rc = nvlist_lookup_string_array(in, FM_FMRI_MEM_SERIAL_ID, 369 &nvlserids, &nserids)) == 0) { /* already have serial #s */ 370 mem_expand_opt(mod, in, nvlserids); 371 return (0); 372 } else if (rc != ENOENT) 373 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 374 else 375 return (-1); 376 } 377 378 int 379 mem_page_isretired(topo_mod_t *mod, nvlist_t *nvl) 380 { 381 ldom_hdl_t *lhp; 382 int rc; 383 384 if ((lhp = ldom_init(mem_alloc, mem_free)) == NULL) { 385 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 386 errno = ENOMEM; 387 return (FMD_AGENT_RETIRE_FAIL); 388 } 389 390 rc = ldom_fmri_status(lhp, nvl); 391 392 ldom_fini(lhp); 393 errno = rc; 394 395 if (rc == 0 || rc == EINVAL) 396 return (FMD_AGENT_RETIRE_DONE); 397 if (rc == EAGAIN) 398 return (FMD_AGENT_RETIRE_ASYNC); 399 400 return (FMD_AGENT_RETIRE_FAIL); 401 } 402 403 int 404 mem_page_retire(topo_mod_t *mod, nvlist_t *nvl) 405 { 406 ldom_hdl_t *lhp; 407 int rc; 408 409 if ((lhp = ldom_init(mem_alloc, mem_free)) == NULL) { 410 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 411 errno = ENOMEM; 412 return (FMD_AGENT_RETIRE_FAIL); 413 } 414 415 rc = ldom_fmri_retire(lhp, nvl); 416 417 ldom_fini(lhp); 418 errno = rc; 419 420 if (rc == 0 || rc == EIO || rc == EINVAL) 421 return (FMD_AGENT_RETIRE_DONE); 422 if (rc == EAGAIN) 423 return (FMD_AGENT_RETIRE_ASYNC); 424 425 return (FMD_AGENT_RETIRE_FAIL); 426 } 427 428 int 429 mem_page_unretire(topo_mod_t *mod, nvlist_t *nvl) 430 { 431 ldom_hdl_t *lhp; 432 int rc; 433 434 if ((lhp = ldom_init(mem_alloc, mem_free)) == NULL) { 435 (void) topo_mod_seterrno(mod, EMOD_NOMEM); 436 errno = ENOMEM; 437 return (FMD_AGENT_RETIRE_FAIL); 438 } 439 440 rc = ldom_fmri_unretire(lhp, nvl); 441 442 ldom_fini(lhp); 443 errno = rc; 444 445 if (rc == 0 || rc == EIO) 446 return (FMD_AGENT_RETIRE_DONE); 447 448 return (FMD_AGENT_RETIRE_FAIL); 449 450 } 451 452 /*ARGSUSED*/ 453 static int 454 mem_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 455 nvlist_t *in, nvlist_t **out) 456 { 457 int rc = -1; 458 uint8_t version; 459 uint64_t val1, val2; 460 int err1, err2; 461 uint32_t retval; 462 463 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || 464 version > FM_MEM_SCHEME_VERSION) 465 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 466 467 err1 = nvlist_lookup_uint64(in, FM_FMRI_MEM_OFFSET, &val1); 468 err2 = nvlist_lookup_uint64(in, FM_FMRI_MEM_PHYSADDR, &val2); 469 470 if (err1 == ENOENT && err2 == ENOENT) 471 return (0); /* no page, so assume it's still usable */ 472 473 if ((err1 != 0 && err1 != ENOENT) || (err2 != 0 && err2 != ENOENT)) 474 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 475 476 /* 477 * Ask the kernel if the page is retired, using 478 * the original mem FMRI with the specified offset or PA. 479 * Refer to the kernel's page_retire_check() for the error codes. 480 */ 481 rc = mem_page_isretired(mod, in); 482 483 if (rc == FMD_AGENT_RETIRE_FAIL) { 484 /* 485 * The page is not retired and is not scheduled for retirement 486 * (i.e. no request pending and has not seen any errors) 487 */ 488 retval = 0; 489 } else if (rc == FMD_AGENT_RETIRE_DONE || 490 rc == FMD_AGENT_RETIRE_ASYNC) { 491 /* 492 * The page has been retired, is in the process of being 493 * retired, or doesn't exist. The latter is valid if the page 494 * existed in the past but has been DR'd out. 495 */ 496 retval = 1; 497 } else { 498 /* 499 * Errors are only signalled to the caller if they're the 500 * caller's fault. This isn't - it's a failure of the 501 * retirement-check code. We'll whine about it and tell 502 * the caller the page is unusable. 503 */ 504 topo_mod_dprintf(mod, 505 "failed to determine page %s=%llx usability: " 506 "rc=%d errno=%d\n", err1 == 0 ? FM_FMRI_MEM_OFFSET : 507 FM_FMRI_MEM_PHYSADDR, err1 == 0 ? (u_longlong_t)val1 : 508 (u_longlong_t)val2, rc, errno); 509 retval = 1; 510 } 511 512 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 513 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 514 if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, retval) != 0) { 515 nvlist_free(*out); 516 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 517 } 518 return (0); 519 } 520 521 /* ARGSUSED */ 522 static int 523 mem_contains(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 524 nvlist_t *in, nvlist_t **out) 525 { 526 int rc = -1, ret = 1; 527 uint8_t version; 528 unsigned int erx, eex, ersiz, eesiz; 529 nvlist_t *er, *ee; 530 char **ersna, **eesna; 531 532 /* 533 * Unlike the other exported functions, the 'in' argument here is 534 * not a pass-through -- it is a composite of the 'container' and 535 * 'containee' FMRIs. Rather than checking the version of 'in', 536 * check the versions of the container and containee. 537 */ 538 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &er) != 0 || 539 nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &ee) != 0 || 540 nvlist_lookup_uint8(er, FM_VERSION, &version) != 0 || 541 version > FM_MEM_SCHEME_VERSION || 542 nvlist_lookup_uint8(ee, FM_VERSION, &version) != 0 || 543 version > FM_MEM_SCHEME_VERSION || 544 nvlist_lookup_string_array(er, FM_FMRI_MEM_SERIAL_ID, 545 &ersna, &ersiz) != 0 || 546 nvlist_lookup_string_array(ee, FM_FMRI_MEM_SERIAL_ID, 547 &eesna, &eesiz) != 0) 548 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 549 550 /* 551 * Look up each 'ee' serial number in serial number list of 'er'. 552 * If any are not found, return "false"; if all are found, return 553 * "true". 554 */ 555 556 for (eex = 0; eex < eesiz; eex++) { 557 for (erx = 0; erx < ersiz; erx++) { 558 rc = strcmp(ersna[erx], eesna[eex]); 559 if (rc == 0) 560 break; 561 } 562 if (rc != 0) { 563 /* failed -- no containment */ 564 ret = 0; 565 break; 566 } 567 } 568 /* success */ 569 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) { 570 if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET, ret) != 0) { 571 nvlist_free(*out); 572 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 573 } 574 return (0); 575 } 576 return (-1); 577 } 578 579 static nvlist_t * 580 mem_fmri_create(topo_mod_t *mod, char *unum, char *serial) 581 { 582 int err; 583 nvlist_t *fmri; 584 585 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 586 return (NULL); 587 err = nvlist_add_uint8(fmri, FM_VERSION, FM_MEM_SCHEME_VERSION); 588 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM); 589 err |= nvlist_add_string(fmri, FM_FMRI_MEM_UNUM, unum); 590 if (serial != NULL) 591 err |= nvlist_add_string_array(fmri, 592 FM_FMRI_MEM_SERIAL_ID, &serial, 1); 593 if (err != 0) { 594 nvlist_free(fmri); 595 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL); 596 return (NULL); 597 } 598 599 return (fmri); 600 } 601 602 static tnode_t * 603 mem_tnode_create(topo_mod_t *mod, tnode_t *parent, 604 const char *name, topo_instance_t i, char *unum, char *serial, void *priv) 605 { 606 nvlist_t *fmri; 607 tnode_t *ntn; 608 609 fmri = mem_fmri_create(mod, unum, serial); 610 if (fmri == NULL) { 611 topo_mod_dprintf(mod, 612 "Unable to make nvlist for %s bind: %s.\n", 613 name, topo_mod_errmsg(mod)); 614 return (NULL); 615 } 616 617 ntn = topo_node_bind(mod, parent, name, i, fmri); 618 if (ntn == NULL) { 619 topo_mod_dprintf(mod, 620 "topo_node_bind (%s%d/%s%d) failed: %s\n", 621 topo_node_name(parent), topo_node_instance(parent), 622 name, i, 623 topo_strerror(topo_mod_errno(mod))); 624 nvlist_free(fmri); 625 return (NULL); 626 } 627 nvlist_free(fmri); 628 topo_node_setspecific(ntn, priv); 629 630 return (ntn); 631 } 632 633 /*ARGSUSED*/ 634 static int 635 mem_create(topo_mod_t *mod, tnode_t *rnode, const char *name, 636 md_mem_info_t *mem) 637 { 638 int i; 639 int nerr = 0; 640 int ndimms = 0; 641 mem_dimm_map_t *mp; 642 tnode_t *cnode; 643 644 topo_mod_dprintf(mod, "enumerating memory\n"); 645 646 /* 647 * Count the dimms and create a range. The instance numbers 648 * are not meaningful in this context. 649 */ 650 for (mp = mem->mem_dm; mp != NULL; mp = mp->dm_next) { 651 ndimms++; 652 } 653 if (ndimms == 0) 654 return (-1); 655 topo_node_range_destroy(rnode, name); 656 if (topo_node_range_create(mod, rnode, name, 0, ndimms+1) < 0) { 657 topo_mod_dprintf(mod, "failed to create dimm range[0,%d]: %s\n", 658 ndimms, topo_mod_errmsg(mod)); 659 return (-1); 660 } 661 662 /* 663 * Create the dimm nodes 664 */ 665 for (mp = mem->mem_dm, i = 0; mp != NULL; mp = mp->dm_next, i++) { 666 cnode = mem_tnode_create(mod, rnode, name, (topo_instance_t)i, 667 mp->dm_label, mp->dm_serid, NULL); 668 if (cnode == NULL) { 669 topo_mod_dprintf(mod, 670 "failed to create dimm=%d node: %s\n", 671 i, topo_mod_errmsg(mod)); 672 nerr++; 673 } 674 } 675 676 if (nerr != 0) 677 (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM); 678 679 return (0); 680 } 681 682 /*ARGSUSED*/ 683 static int 684 mem_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 685 topo_instance_t min, topo_instance_t max, void *arg, void *notused) 686 { 687 topo_mod_dprintf(mod, "%s enumerating %s\n", PLATFORM_MEM_NAME, name); 688 689 if (topo_method_register(mod, rnode, mem_methods) < 0) { 690 topo_mod_dprintf(mod, "topo_method_register failed: %s\n", 691 topo_strerror(topo_mod_errno(mod))); 692 return (-1); 693 } 694 695 if (strcmp(name, MEM_NODE_NAME) == 0) 696 return (mem_create(mod, rnode, name, (md_mem_info_t *)arg)); 697 698 return (0); 699 } 700 701 /*ARGSUSED*/ 702 static void 703 mem_release(topo_mod_t *mod, tnode_t *node) 704 { 705 topo_method_unregister_all(mod, node); 706 } 707