xref: /titanic_41/usr/src/cmd/fm/schemes/mem/mem.c (revision 4e5b757fbcf21077677360be274461dcd9064106)
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 2007 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 #include <mem.h>
30 #include <fm/fmd_fmri.h>
31 
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <time.h>
37 #include <sys/mem.h>
38 
39 #ifdef	sparc
40 #include <sys/fm/ldom.h>
41 ldom_hdl_t *mem_scheme_lhp;
42 #endif	/* sparc */
43 
44 mem_t mem;
45 
46 #ifdef	sparc
47 
48 extern int mem_update_mdesc(void);
49 
50 /*
51  * Retry values for handling the case where the kernel is not yet ready
52  * to provide DIMM serial ids.  Some platforms acquire DIMM serial id
53  * information from their System Controller via a mailbox interface.
54  * The values chosen are for 10 retries 3 seconds apart to approximate the
55  * possible 30 second timeout length of a mailbox message request.
56  */
57 #define	MAX_MEM_SID_RETRIES	10
58 #define	MEM_SID_RETRY_WAIT	3
59 
60 static mem_dimm_map_t *
61 dm_lookup(const char *name)
62 {
63 	mem_dimm_map_t *dm;
64 
65 	for (dm = mem.mem_dm; dm != NULL; dm = dm->dm_next) {
66 		if (strcmp(name, dm->dm_label) == 0)
67 			return (dm);
68 	}
69 
70 	return (NULL);
71 }
72 
73 /*
74  * Returns 0 with serial numbers if found, -1 (with errno set) for errors.  If
75  * the unum (or a component of same) wasn't found, -1 is returned with errno
76  * set to ENOENT.  If the kernel doesn't have support for serial numbers,
77  * -1 is returned with errno set to ENOTSUP.
78  */
79 static int
80 mem_get_serids_from_kernel(const char *unum, char ***seridsp, size_t *nseridsp)
81 {
82 	char **dimms, **serids;
83 	size_t ndimms, nserids;
84 	int i, rc = 0;
85 	int fd;
86 	int retries = MAX_MEM_SID_RETRIES;
87 	mem_name_t mn;
88 	struct timespec rqt;
89 
90 	if ((fd = open("/dev/mem", O_RDONLY)) < 0)
91 		return (-1);
92 
93 	if (mem_unum_burst(unum, &dimms, &ndimms) < 0) {
94 		(void) close(fd);
95 		return (-1); /* errno is set for us */
96 	}
97 
98 	serids = fmd_fmri_zalloc(sizeof (char *) * ndimms);
99 	nserids = ndimms;
100 
101 	bzero(&mn, sizeof (mn));
102 
103 	for (i = 0; i < ndimms; i++) {
104 		mn.m_namelen = strlen(dimms[i]) + 1;
105 		mn.m_sidlen = MEM_SERID_MAXLEN;
106 
107 		mn.m_name = fmd_fmri_alloc(mn.m_namelen);
108 		mn.m_sid = fmd_fmri_alloc(mn.m_sidlen);
109 
110 		(void) strcpy(mn.m_name, dimms[i]);
111 
112 		do {
113 			rc = ioctl(fd, MEM_SID, &mn);
114 
115 			if (rc >= 0 || errno != EAGAIN)
116 				break;
117 
118 			if (retries == 0) {
119 				errno = ETIMEDOUT;
120 				break;
121 			}
122 
123 			/*
124 			 * EAGAIN indicates the kernel is
125 			 * not ready to provide DIMM serial
126 			 * ids.  Sleep MEM_SID_RETRY_WAIT seconds
127 			 * and try again.
128 			 * nanosleep() is used instead of sleep()
129 			 * to avoid interfering with fmd timers.
130 			 */
131 			rqt.tv_sec = MEM_SID_RETRY_WAIT;
132 			rqt.tv_nsec = 0;
133 			(void) nanosleep(&rqt, NULL);
134 
135 		} while (retries--);
136 
137 		if (rc < 0) {
138 			/*
139 			 * ENXIO can happen if the kernel memory driver
140 			 * doesn't have the MEM_SID ioctl (e.g. if the
141 			 * kernel hasn't been patched to provide the
142 			 * support).
143 			 *
144 			 * If the MEM_SID ioctl is available but the
145 			 * particular platform doesn't support providing
146 			 * serial ids, ENOTSUP will be returned by the ioctl.
147 			 */
148 			if (errno == ENXIO)
149 				errno = ENOTSUP;
150 			fmd_fmri_free(mn.m_name, mn.m_namelen);
151 			fmd_fmri_free(mn.m_sid, mn.m_sidlen);
152 			mem_strarray_free(serids, nserids);
153 			mem_strarray_free(dimms, ndimms);
154 			(void) close(fd);
155 			return (-1);
156 		}
157 
158 		serids[i] = fmd_fmri_strdup(mn.m_sid);
159 
160 		fmd_fmri_free(mn.m_name, mn.m_namelen);
161 		fmd_fmri_free(mn.m_sid, mn.m_sidlen);
162 	}
163 
164 	mem_strarray_free(dimms, ndimms);
165 
166 	(void) close(fd);
167 
168 	*seridsp = serids;
169 	*nseridsp = nserids;
170 
171 	return (0);
172 }
173 
174 /*
175  * Returns 0 with serial numbers if found, -1 (with errno set) for errors.  If
176  * the unum (or a component of same) wasn't found, -1 is returned with errno
177  * set to ENOENT.
178  */
179 static int
180 mem_get_serids_from_cache(const char *unum, char ***seridsp, size_t *nseridsp)
181 {
182 	uint64_t drgen = fmd_fmri_get_drgen();
183 	char **dimms, **serids;
184 	size_t ndimms, nserids;
185 	mem_dimm_map_t *dm;
186 	int i, rc = 0;
187 
188 	if (mem_unum_burst(unum, &dimms, &ndimms) < 0)
189 		return (-1); /* errno is set for us */
190 
191 	serids = fmd_fmri_zalloc(sizeof (char *) * ndimms);
192 	nserids = ndimms;
193 
194 	for (i = 0; i < ndimms; i++) {
195 		if ((dm = dm_lookup(dimms[i])) == NULL) {
196 			rc = fmd_fmri_set_errno(EINVAL);
197 			break;
198 		}
199 
200 		if (*dm->dm_serid == '\0' || dm->dm_drgen != drgen) {
201 			/*
202 			 * We don't have a cached copy, or the copy we've got is
203 			 * out of date.  Look it up again.
204 			 */
205 			if (mem_get_serid(dm->dm_device, dm->dm_serid,
206 			    sizeof (dm->dm_serid)) < 0) {
207 				rc = -1; /* errno is set for us */
208 				break;
209 			}
210 
211 			dm->dm_drgen = drgen;
212 		}
213 
214 		serids[i] = fmd_fmri_strdup(dm->dm_serid);
215 	}
216 
217 	mem_strarray_free(dimms, ndimms);
218 
219 	if (rc == 0) {
220 		*seridsp = serids;
221 		*nseridsp = nserids;
222 	} else {
223 		mem_strarray_free(serids, nserids);
224 	}
225 
226 	return (rc);
227 }
228 
229 /*
230  * Returns 0 with serial numbers if found, -1 (with errno set) for errors.  If
231  * the unum (or a component of same) wasn't found, -1 is returned with errno
232  * set to ENOENT.
233  */
234 static int
235 mem_get_serids_from_mdesc(const char *unum, char ***seridsp, size_t *nseridsp)
236 {
237 	uint64_t drgen = fmd_fmri_get_drgen();
238 	char **dimms, **serids;
239 	size_t ndimms, nserids;
240 	mem_dimm_map_t *dm;
241 	int i, rc = 0;
242 
243 	if (mem_unum_burst(unum, &dimms, &ndimms) < 0)
244 		return (-1); /* errno is set for us */
245 
246 	serids = fmd_fmri_zalloc(sizeof (char *) * ndimms);
247 	nserids = ndimms;
248 
249 	/*
250 	 * first go through dimms and see if dm_drgen entries are outdated
251 	 */
252 	for (i = 0; i < ndimms; i++) {
253 		if ((dm = dm_lookup(dimms[i])) == NULL ||
254 		    dm->dm_drgen != drgen)
255 			break;
256 	}
257 
258 	if (i < ndimms && mem_update_mdesc() != 0) {
259 		mem_strarray_free(dimms, ndimms);
260 		return (-1);
261 	}
262 
263 	/*
264 	 * get to this point if an up-to-date mdesc (and corresponding
265 	 * entries in the global mem list) exists
266 	 */
267 	for (i = 0; i < ndimms; i++) {
268 		if ((dm = dm_lookup(dimms[i])) == NULL) {
269 			rc = fmd_fmri_set_errno(EINVAL);
270 			break;
271 		}
272 
273 		if (dm->dm_drgen != drgen)
274 			dm->dm_drgen = drgen;
275 
276 		/*
277 		 * mdesc and dm entry was updated by an earlier call to
278 		 * mem_update_mdesc, so we go ahead and dup the serid
279 		 */
280 		serids[i] = fmd_fmri_strdup(dm->dm_serid);
281 	}
282 
283 	mem_strarray_free(dimms, ndimms);
284 
285 	if (rc == 0) {
286 		*seridsp = serids;
287 		*nseridsp = nserids;
288 	} else {
289 		mem_strarray_free(serids, nserids);
290 	}
291 
292 	return (rc);
293 }
294 
295 /*
296  * Returns 0 with part numbers if found, returns -1 for errors.
297  */
298 static int
299 mem_get_parts_from_mdesc(const char *unum, char ***partsp, size_t *npartsp)
300 {
301 	uint64_t drgen = fmd_fmri_get_drgen();
302 	char **dimms, **parts;
303 	size_t ndimms, nparts;
304 	mem_dimm_map_t *dm;
305 	int i, rc = 0;
306 
307 	if (mem_unum_burst(unum, &dimms, &ndimms) < 0)
308 		return (-1); /* errno is set for us */
309 
310 	parts = fmd_fmri_zalloc(sizeof (char *) * ndimms);
311 	nparts = ndimms;
312 
313 	/*
314 	 * first go through dimms and see if dm_drgen entries are outdated
315 	 */
316 	for (i = 0; i < ndimms; i++) {
317 		if ((dm = dm_lookup(dimms[i])) == NULL ||
318 		    dm->dm_drgen != drgen)
319 			break;
320 	}
321 
322 	if (i < ndimms && mem_update_mdesc() != 0) {
323 		mem_strarray_free(dimms, ndimms);
324 		mem_strarray_free(parts, nparts);
325 		return (-1);
326 	}
327 
328 	/*
329 	 * get to this point if an up-to-date mdesc (and corresponding
330 	 * entries in the global mem list) exists
331 	 */
332 	for (i = 0; i < ndimms; i++) {
333 		if ((dm = dm_lookup(dimms[i])) == NULL) {
334 			rc = fmd_fmri_set_errno(EINVAL);
335 			break;
336 		}
337 
338 		if (dm->dm_drgen != drgen)
339 			dm->dm_drgen = drgen;
340 
341 		/*
342 		 * mdesc and dm entry was updated by an earlier call to
343 		 * mem_update_mdesc, so we go ahead and dup the part
344 		 */
345 		if (dm->dm_part == NULL) {
346 			rc = -1;
347 			break;
348 		}
349 		parts[i] = fmd_fmri_strdup(dm->dm_part);
350 	}
351 
352 	mem_strarray_free(dimms, ndimms);
353 
354 	if (rc == 0) {
355 		*partsp = parts;
356 		*npartsp = nparts;
357 	} else {
358 		mem_strarray_free(parts, nparts);
359 	}
360 
361 	return (rc);
362 }
363 
364 static int
365 mem_get_parts_by_unum(const char *unum, char ***partp, size_t *npartp)
366 {
367 	if (mem.mem_dm == NULL)
368 		return (-1);
369 	else
370 		return (mem_get_parts_from_mdesc(unum, partp, npartp));
371 }
372 
373 #endif	/* sparc */
374 
375 /*ARGSUSED*/
376 
377 static int
378 mem_get_serids_by_unum(const char *unum, char ***seridsp, size_t *nseridsp)
379 {
380 	/*
381 	 * Some platforms do not support the caching of serial ids by the
382 	 * mem scheme plugin but instead support making serial ids available
383 	 * via the kernel.
384 	 */
385 #ifdef	sparc
386 	if (mem.mem_dm == NULL)
387 		return (mem_get_serids_from_kernel(unum, seridsp, nseridsp));
388 	else if (mem_get_serids_from_mdesc(unum, seridsp, nseridsp) == 0)
389 		return (0);
390 	else
391 		return (mem_get_serids_from_cache(unum, seridsp, nseridsp));
392 #else
393 	errno = ENOTSUP;
394 	return (-1);
395 #endif	/* sparc */
396 }
397 
398 static int
399 mem_fmri_get_unum(nvlist_t *nvl, char **unump)
400 {
401 	uint8_t version;
402 	char *unum;
403 
404 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
405 	    version > FM_MEM_SCHEME_VERSION ||
406 	    nvlist_lookup_string(nvl, FM_FMRI_MEM_UNUM, &unum) != 0)
407 		return (fmd_fmri_set_errno(EINVAL));
408 
409 	*unump = unum;
410 
411 	return (0);
412 }
413 
414 ssize_t
415 fmd_fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
416 {
417 	char format[64];
418 	ssize_t size, presz;
419 	char *rawunum, *preunum, *escunum, *prefix;
420 	uint64_t val;
421 	int i;
422 
423 	if (mem_fmri_get_unum(nvl, &rawunum) < 0)
424 		return (-1); /* errno is set for us */
425 
426 	/*
427 	 * If we have a well-formed unum (hc-FMRI), use the string verbatim
428 	 * to form the initial mem:/// components.  Otherwise use unum=%s.
429 	 */
430 	if (strncmp(rawunum, "hc://", 5) != 0)
431 		prefix = FM_FMRI_MEM_UNUM "=";
432 	else
433 		prefix = "";
434 
435 	/*
436 	 * If we have a DIMM offset, include it in the string.  If we have a PA
437 	 * then use that.  Otherwise just format the unum element.
438 	 */
439 	if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val) == 0) {
440 		(void) snprintf(format, sizeof (format),
441 		    "%s:///%s%%1$s/%s=%%2$llx",
442 		    FM_FMRI_SCHEME_MEM, prefix, FM_FMRI_MEM_OFFSET);
443 	} else if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val) == 0) {
444 		(void) snprintf(format, sizeof (format),
445 		    "%s:///%s%%1$s/%s=%%2$llx",
446 		    FM_FMRI_SCHEME_MEM, prefix, FM_FMRI_MEM_PHYSADDR);
447 	} else {
448 		(void) snprintf(format, sizeof (format),
449 		    "%s:///%s%%1$s", FM_FMRI_SCHEME_MEM, prefix);
450 	}
451 
452 	/*
453 	 * If we have a well-formed unum (hc-FMRI), we skip over the
454 	 * the scheme and authority prefix.
455 	 * Otherwise, the spaces and colons will be escaped,
456 	 * rendering the resulting FMRI pretty much unreadable.
457 	 * We're therefore going to do some escaping of our own first.
458 	 */
459 	if (strncmp(rawunum, "hc://", 5) == 0) {
460 		rawunum += 5;
461 		rawunum = strchr(rawunum, '/');
462 		++rawunum;
463 		/* LINTED: variable format specifier */
464 		size = snprintf(buf, buflen, format, rawunum, val);
465 	} else {
466 		preunum = fmd_fmri_strdup(rawunum);
467 		presz = strlen(preunum) + 1;
468 
469 		for (i = 0; i < presz - 1; i++) {
470 			if (preunum[i] == ':' && preunum[i + 1] == ' ') {
471 				bcopy(preunum + i + 2, preunum + i + 1,
472 				    presz - (i + 2));
473 			} else if (preunum[i] == ' ') {
474 				preunum[i] = ',';
475 			}
476 		}
477 
478 		escunum = fmd_fmri_strescape(preunum);
479 		fmd_fmri_free(preunum, presz);
480 
481 		/* LINTED: variable format specifier */
482 		size = snprintf(buf, buflen, format, escunum, val);
483 		fmd_fmri_strfree(escunum);
484 	}
485 
486 	return (size);
487 }
488 
489 int
490 fmd_fmri_expand(nvlist_t *nvl)
491 {
492 	char *unum, **serids;
493 	uint_t nnvlserids;
494 	size_t nserids;
495 #ifdef sparc
496 	char **parts;
497 	size_t nparts;
498 #endif
499 	int rc;
500 
501 	if (mem_fmri_get_unum(nvl, &unum) < 0)
502 		return (fmd_fmri_set_errno(EINVAL));
503 
504 	if ((rc = nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID,
505 	    &serids, &nnvlserids)) == 0)
506 		return (0); /* fmri is already expanded */
507 	else if (rc != ENOENT)
508 		return (fmd_fmri_set_errno(EINVAL));
509 
510 	if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) {
511 		/* errno is set for us */
512 		if (errno == ENOTSUP)
513 			return (0); /* nothing to add - no s/n support */
514 		else
515 			return (-1);
516 	}
517 
518 	rc = nvlist_add_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, serids,
519 	    nserids);
520 
521 	mem_strarray_free(serids, nserids);
522 
523 	if (rc != 0)
524 		return (fmd_fmri_set_errno(EINVAL));
525 
526 #ifdef sparc
527 	/*
528 	 * Continue with the process if there are no part numbers.
529 	 */
530 	if (mem_get_parts_by_unum(unum, &parts, &nparts) < 0)
531 		return (0);
532 
533 	rc = nvlist_add_string_array(nvl, FM_FMRI_HC_PART, parts, nparts);
534 
535 	mem_strarray_free(parts, nparts);
536 #endif
537 	return (0);
538 }
539 
540 static int
541 serids_eq(char **serids1, uint_t nserids1, char **serids2, uint_t nserids2)
542 {
543 	int i;
544 
545 	if (nserids1 != nserids2)
546 		return (0);
547 
548 	for (i = 0; i < nserids1; i++) {
549 		if (strcmp(serids1[i], serids2[i]) != 0)
550 			return (0);
551 	}
552 
553 	return (1);
554 }
555 
556 int
557 fmd_fmri_present(nvlist_t *nvl)
558 {
559 	char *unum, **nvlserids, **serids;
560 	uint_t nnvlserids;
561 	size_t nserids;
562 	uint64_t memconfig;
563 	int rc;
564 
565 	if (mem_fmri_get_unum(nvl, &unum) < 0)
566 		return (-1); /* errno is set for us */
567 
568 	if (nvlist_lookup_string_array(nvl, FM_FMRI_MEM_SERIAL_ID, &nvlserids,
569 	    &nnvlserids) != 0) {
570 		/*
571 		 * Some mem scheme FMRIs don't have serial ids because
572 		 * either the platform does not support them, or because
573 		 * the FMRI was created before support for serial ids was
574 		 * introduced.  If this is the case, assume it is there.
575 		 */
576 		if (mem.mem_dm == NULL)
577 			return (1);
578 		else
579 			return (fmd_fmri_set_errno(EINVAL));
580 	}
581 
582 	/*
583 	 * Hypervisor will change the memconfig value when the mapping of
584 	 * pages to DIMMs changes, e.g. for change in DIMM size or interleave.
585 	 * If we detect such a change, we discard ereports associated with a
586 	 * previous memconfig value as invalid.
587 	 *
588 	 * The test (mem.mem_memconfig != 0) means we run on a system that
589 	 * actually suplies a memconfig value.
590 	 */
591 
592 	if ((nvlist_lookup_uint64(nvl, FM_FMRI_MEM_MEMCONFIG,
593 	    &memconfig) == 0) && (mem.mem_memconfig != 0) &&
594 	    (memconfig != mem.mem_memconfig))
595 		return (0);
596 
597 	if (mem_get_serids_by_unum(unum, &serids, &nserids) < 0) {
598 		if (errno == ENOTSUP)
599 			return (1); /* assume it's there, no s/n support here */
600 		if (errno != ENOENT) {
601 			/*
602 			 * Errors are only signalled to the caller if they're
603 			 * the caller's fault.  This isn't - it's a failure on
604 			 * our part to burst or read the serial numbers.  We'll
605 			 * whine about it, and tell the caller the named
606 			 * module(s) isn't/aren't there.
607 			 */
608 			fmd_fmri_warn("failed to retrieve serial number for "
609 			    "unum %s", unum);
610 		}
611 		return (0);
612 	}
613 
614 	rc = serids_eq(serids, nserids, nvlserids, nnvlserids);
615 
616 	mem_strarray_free(serids, nserids);
617 
618 	return (rc);
619 }
620 
621 int
622 fmd_fmri_contains(nvlist_t *er, nvlist_t *ee)
623 {
624 	char *erunum, *eeunum;
625 	uint64_t erval = 0, eeval = 0;
626 
627 	if (mem_fmri_get_unum(er, &erunum) < 0 ||
628 	    mem_fmri_get_unum(ee, &eeunum) < 0)
629 		return (-1); /* errno is set for us */
630 
631 	if (mem_unum_contains(erunum, eeunum) <= 0)
632 		return (0); /* can't parse/match, so assume no containment */
633 
634 	if (nvlist_lookup_uint64(er, FM_FMRI_MEM_OFFSET, &erval) == 0) {
635 		return (nvlist_lookup_uint64(ee,
636 		    FM_FMRI_MEM_OFFSET, &eeval) == 0 && erval == eeval);
637 	}
638 
639 	if (nvlist_lookup_uint64(er, FM_FMRI_MEM_PHYSADDR, &erval) == 0) {
640 		return (nvlist_lookup_uint64(ee,
641 		    FM_FMRI_MEM_PHYSADDR, &eeval) == 0 && erval == eeval);
642 	}
643 
644 	return (1);
645 }
646 
647 /*
648  * We can only make a usable/unusable determination for pages.  Mem FMRIs
649  * without page addresses will be reported as usable since Solaris has no
650  * way at present to dynamically disable an entire DIMM or DIMM pair.
651  */
652 int
653 fmd_fmri_unusable(nvlist_t *nvl)
654 {
655 	uint64_t val;
656 	uint8_t version;
657 	int rc, err1, err2;
658 	nvlist_t *nvlcp = NULL;
659 	int retval;
660 
661 	if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
662 	    version > FM_MEM_SCHEME_VERSION)
663 		return (fmd_fmri_set_errno(EINVAL));
664 
665 	err1 = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &val);
666 	err2 = nvlist_lookup_uint64(nvl, FM_FMRI_MEM_PHYSADDR, &val);
667 
668 	if (err1 == ENOENT && err2 == ENOENT)
669 		return (0); /* no page, so assume it's still usable */
670 
671 	if ((err1 != 0 && err1 != ENOENT) || (err2 != 0 && err2 != ENOENT))
672 		return (fmd_fmri_set_errno(EINVAL));
673 
674 	if ((err1 = mem_unum_rewrite(nvl, &nvlcp)) != 0)
675 		return (fmd_fmri_set_errno(err1));
676 
677 	/*
678 	 * Ask the kernel if the page is retired, using either the rewritten
679 	 * hc FMRI or the original mem FMRI with the specified offset or PA.
680 	 * Refer to the kernel's page_retire_check() for the error codes.
681 	 */
682 	rc = mem_page_cmd(MEM_PAGE_FMRI_ISRETIRED, nvlcp ? nvlcp : nvl);
683 
684 	if (rc == -1 && errno == EIO) {
685 		/*
686 		 * The page is not retired and is not scheduled for retirement
687 		 * (i.e. no request pending and has not seen any errors)
688 		 */
689 		retval = 0;
690 	} else if (rc == 0 || errno == EAGAIN || errno == EINVAL) {
691 		/*
692 		 * The page has been retired, is in the process of being
693 		 * retired, or doesn't exist.  The latter is valid if the page
694 		 * existed in the past but has been DR'd out.
695 		 */
696 		retval = 1;
697 	} else {
698 		/*
699 		 * Errors are only signalled to the caller if they're the
700 		 * caller's fault.  This isn't - it's a failure of the
701 		 * retirement-check code.  We'll whine about it and tell
702 		 * the caller the page is unusable.
703 		 */
704 		fmd_fmri_warn("failed to determine page %s=%llx usability: "
705 		    "rc=%d errno=%d\n", err1 == 0 ? FM_FMRI_MEM_OFFSET :
706 		    FM_FMRI_MEM_PHYSADDR, (u_longlong_t)val, rc, errno);
707 		retval = 1;
708 	}
709 
710 	if (nvlcp)
711 		nvlist_free(nvlcp);
712 
713 	return (retval);
714 }
715 
716 int
717 fmd_fmri_init(void)
718 {
719 #ifdef	sparc
720 	mem_scheme_lhp = ldom_init(fmd_fmri_alloc, fmd_fmri_free);
721 #endif	/* sparc */
722 	return (mem_discover());
723 }
724 
725 void
726 fmd_fmri_fini(void)
727 {
728 	mem_dimm_map_t *dm, *em;
729 
730 	for (dm = mem.mem_dm; dm != NULL; dm = em) {
731 		em = dm->dm_next;
732 		fmd_fmri_strfree(dm->dm_label);
733 		fmd_fmri_strfree(dm->dm_device);
734 		fmd_fmri_free(dm, sizeof (mem_dimm_map_t));
735 	}
736 #ifdef	sparc
737 	ldom_fini(mem_scheme_lhp);
738 #endif	/* sparc */
739 }
740