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
_topo_init(topo_mod_t * mod)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
_topo_fini(topo_mod_t * mod)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
mem_present(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)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
mem_replaced(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)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
mem_strarray_free(topo_mod_t * mod,char ** arr,size_t dim)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
extract_bits(uint64_t paddr,uint64_t mask)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
insert_bits(uint64_t offset,uint64_t mask,uint64_t value)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
calc_phys_addr(mem_seg_map_t * seg,char * ds,uint64_t offset)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
mem_expand_opt(topo_mod_t * mod,nvlist_t * nvl,char ** serids)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
mem_expand(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)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
mem_page_isretired(topo_mod_t * mod,nvlist_t * nvl)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
mem_page_retire(topo_mod_t * mod,nvlist_t * nvl)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
mem_page_unretire(topo_mod_t * mod,nvlist_t * nvl)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
mem_unusable(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)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
mem_contains(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)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 *
mem_fmri_create(topo_mod_t * mod,char * unum,char * serial)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 *
mem_tnode_create(topo_mod_t * mod,tnode_t * parent,const char * name,topo_instance_t i,char * unum,char * serial,void * priv)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
mem_create(topo_mod_t * mod,tnode_t * rnode,const char * name,md_mem_info_t * mem)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
mem_enum(topo_mod_t * mod,tnode_t * rnode,const char * name,topo_instance_t min,topo_instance_t max,void * arg,void * notused)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
mem_release(topo_mod_t * mod,tnode_t * node)703 mem_release(topo_mod_t *mod, tnode_t *node)
704 {
705 topo_method_unregister_all(mod, node);
706 }
707