xref: /titanic_52/usr/src/lib/fm/topo/modules/sun4v/platform-mem/mem.c (revision b69a86957bad202e0d4cd49e4ceb7fea54a83270)
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