xref: /onnv-gate/usr/src/lib/fm/topo/modules/sun4v/platform-mem/mem.c (revision 11683:d7b84426fba6)
1*11683STom.Pothier@Sun.COM 
26828Stsien /*
36828Stsien  * CDDL HEADER START
46828Stsien  *
56828Stsien  * The contents of this file are subject to the terms of the
66828Stsien  * Common Development and Distribution License (the "License").
76828Stsien  * You may not use this file except in compliance with the License.
86828Stsien  *
96828Stsien  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
106828Stsien  * or http://www.opensolaris.org/os/licensing.
116828Stsien  * See the License for the specific language governing permissions
126828Stsien  * and limitations under the License.
136828Stsien  *
146828Stsien  * When distributing Covered Code, include this CDDL HEADER in each
156828Stsien  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
166828Stsien  * If applicable, add the following below this CDDL HEADER, with the
176828Stsien  * fields enclosed by brackets "[]" replaced with your own identifying
186828Stsien  * information: Portions Copyright [yyyy] [name of copyright owner]
196828Stsien  *
206828Stsien  * CDDL HEADER END
216828Stsien  */
226828Stsien 
236828Stsien /*
24*11683STom.Pothier@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
256828Stsien  * Use is subject to license terms.
266828Stsien  */
276828Stsien 
286828Stsien #include <strings.h>
296828Stsien #include <umem.h>
306828Stsien #include <fm/topo_mod.h>
317275Sstephh #include <fm/fmd_fmri.h>
327532SSean.Ye@Sun.COM #include <fm/fmd_agent.h>
336828Stsien #include <sys/fm/protocol.h>
346828Stsien 
356828Stsien #include <mem_mdesc.h>
366828Stsien 
376828Stsien /*
386828Stsien  * This enumerator creates mem-schemed nodes for each dimm found in the
396828Stsien  * sun4v Physical Resource Inventory (PRI).
407275Sstephh  * Each node exports five methods: present(), expand(), unusable(), replaced(),
416828Stsien  * and contains().
426828Stsien  *
436828Stsien  */
446828Stsien 
456828Stsien #define	PLATFORM_MEM_NAME	"platform-mem"
466828Stsien #define	PLATFORM_MEM_VERSION	TOPO_VERSION
476828Stsien #define	MEM_NODE_NAME		"mem"
486828Stsien 
496828Stsien 
506828Stsien /* Forward declaration */
516828Stsien static int mem_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
526828Stsien     topo_instance_t, void *, void *);
536828Stsien static void mem_release(topo_mod_t *, tnode_t *);
546828Stsien static int mem_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
556828Stsien     nvlist_t **);
567275Sstephh static int mem_replaced(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
577275Sstephh     nvlist_t **);
586828Stsien static int mem_expand(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
596828Stsien     nvlist_t **);
606828Stsien static int mem_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
616828Stsien     nvlist_t **);
626828Stsien static int mem_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
636828Stsien     nvlist_t **);
646828Stsien 
656828Stsien static const topo_modops_t mem_ops =
666828Stsien 	{ mem_enum, mem_release };
676828Stsien static const topo_modinfo_t mem_info =
686828Stsien 	{ PLATFORM_MEM_NAME, FM_FMRI_SCHEME_MEM, PLATFORM_MEM_VERSION,
696828Stsien 		&mem_ops };
706828Stsien 
716828Stsien static const topo_method_t mem_methods[] = {
726828Stsien 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
736828Stsien 	    TOPO_METH_PRESENT_VERSION, TOPO_STABILITY_INTERNAL, mem_present },
747275Sstephh 	{ TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC,
757275Sstephh 	    TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, mem_replaced },
766828Stsien 	{ TOPO_METH_EXPAND, TOPO_METH_EXPAND_DESC,
776828Stsien 	    TOPO_METH_EXPAND_VERSION, TOPO_STABILITY_INTERNAL, mem_expand },
786828Stsien 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
796828Stsien 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, mem_unusable },
806828Stsien 	{ TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC,
816828Stsien 	    TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, mem_contains },
826828Stsien 	{ NULL }
836828Stsien };
846828Stsien 
856828Stsien int
_topo_init(topo_mod_t * mod)866828Stsien _topo_init(topo_mod_t *mod)
876828Stsien {
886828Stsien 	md_mem_info_t *mem;
896828Stsien 
906828Stsien 	if (getenv("TOPOPLATFORMMEMDBG"))
916828Stsien 		topo_mod_setdebug(mod);
926828Stsien 	topo_mod_dprintf(mod, "initializing %s enumerator\n",
936828Stsien 	    PLATFORM_MEM_NAME);
946828Stsien 
956828Stsien 	if ((mem = topo_mod_zalloc(mod, sizeof (md_mem_info_t))) == NULL)
966828Stsien 		return (-1);
976828Stsien 
986828Stsien 	if (mem_mdesc_init(mod, mem) != 0) {
996828Stsien 		topo_mod_dprintf(mod, "failed to get dimms from the PRI/MD\n");
1006828Stsien 		topo_mod_free(mod, mem, sizeof (md_mem_info_t));
1016828Stsien 		return (-1);
1026828Stsien 	}
1036828Stsien 
1046828Stsien 	topo_mod_setspecific(mod, (void *)mem);
1056828Stsien 
1066828Stsien 	if (topo_mod_register(mod, &mem_info, TOPO_VERSION) != 0) {
1076828Stsien 		topo_mod_dprintf(mod, "failed to register %s: %s\n",
1086828Stsien 		    PLATFORM_MEM_NAME, topo_mod_errmsg(mod));
1096828Stsien 		mem_mdesc_fini(mod, mem);
1106828Stsien 		topo_mod_free(mod, mem, sizeof (md_mem_info_t));
1116828Stsien 		return (-1);
1126828Stsien 	}
1136828Stsien 
1146828Stsien 	topo_mod_dprintf(mod, "%s enumerator inited\n", PLATFORM_MEM_NAME);
1156828Stsien 
1166828Stsien 	return (0);
1176828Stsien }
1186828Stsien 
1196828Stsien void
_topo_fini(topo_mod_t * mod)1206828Stsien _topo_fini(topo_mod_t *mod)
1216828Stsien {
1226828Stsien 	md_mem_info_t *mem;
1236828Stsien 
1246828Stsien 	mem = (md_mem_info_t *)topo_mod_getspecific(mod);
1256828Stsien 
1266828Stsien 	mem_mdesc_fini(mod, mem);
1276828Stsien 
1286828Stsien 	topo_mod_free(mod, mem, sizeof (md_mem_info_t));
1296828Stsien 
1306828Stsien 	topo_mod_unregister(mod);
1316828Stsien 
1326828Stsien }
1336828Stsien 
1346828Stsien /*ARGSUSED*/
1356828Stsien static int
mem_present(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)1366828Stsien mem_present(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
1376828Stsien     nvlist_t *in, nvlist_t **out)
1386828Stsien {
1396828Stsien 	uint8_t version;
1406828Stsien 	char **nvlserids;
1416828Stsien 	size_t n, nserids;
1426828Stsien 	uint32_t present = 0;
1436828Stsien 	md_mem_info_t *mem = (md_mem_info_t *)topo_mod_getspecific(mod);
1446828Stsien 
1456828Stsien 	/* sun4v platforms all support dimm serial numbers */
1466828Stsien 
1476828Stsien 	if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 ||
1486828Stsien 	    version > FM_MEM_SCHEME_VERSION ||
1496828Stsien 	    nvlist_lookup_string_array(in, FM_FMRI_MEM_SERIAL_ID,
1506828Stsien 	    &nvlserids, &nserids) != 0) {
1516828Stsien 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1526828Stsien 	}
1536828Stsien 
1546828Stsien 	/* Find the dimm entry */
1556828Stsien 	for (n = 0; n < nserids; n++) {
1566828Stsien 		if (mem_get_dimm_by_sn(nvlserids[n], mem) != NULL) {
1576828Stsien 			present = 1;
1586828Stsien 			break;
1596828Stsien 		}
1606828Stsien 	}
1616828Stsien 
1626828Stsien 	/* return the present status */
1636828Stsien 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
1646828Stsien 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1656828Stsien 	if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) {
1666828Stsien 		nvlist_free(*out);
1676828Stsien 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1686828Stsien 	}
1696828Stsien 
1706828Stsien 	return (0);
1716828Stsien }
1726828Stsien 
1737275Sstephh /*ARGSUSED*/
1747275Sstephh static int
mem_replaced(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)1757275Sstephh mem_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
1767275Sstephh     nvlist_t *in, nvlist_t **out)
1777275Sstephh {
1787275Sstephh 	uint8_t version;
1797275Sstephh 	char **nvlserids;
1807275Sstephh 	size_t n, nserids;
1817275Sstephh 	uint32_t rval = FMD_OBJ_STATE_NOT_PRESENT;
1827275Sstephh 	md_mem_info_t *mem = (md_mem_info_t *)topo_mod_getspecific(mod);
1837275Sstephh 
1847275Sstephh 	/* sun4v platforms all support dimm serial numbers */
1857275Sstephh 
1867275Sstephh 	if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 ||
1877275Sstephh 	    version > FM_MEM_SCHEME_VERSION ||
1887275Sstephh 	    nvlist_lookup_string_array(in, FM_FMRI_MEM_SERIAL_ID,
1897275Sstephh 	    &nvlserids, &nserids) != 0) {
1907275Sstephh 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1917275Sstephh 	}
1927275Sstephh 
1937275Sstephh 	/* Find the dimm entry */
1947275Sstephh 	for (n = 0; n < nserids; n++) {
1957275Sstephh 		if (mem_get_dimm_by_sn(nvlserids[n], mem) != NULL) {
1967275Sstephh 			rval = FMD_OBJ_STATE_STILL_PRESENT;
1977275Sstephh 			break;
1987275Sstephh 		}
1997275Sstephh 	}
2007275Sstephh 
2017275Sstephh 	/* return the replaced status */
2027275Sstephh 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
2037275Sstephh 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
2047275Sstephh 	if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) {
2057275Sstephh 		nvlist_free(*out);
2067275Sstephh 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
2077275Sstephh 	}
2087275Sstephh 
2097275Sstephh 	return (0);
2107275Sstephh }
2117275Sstephh 
2126828Stsien void
mem_strarray_free(topo_mod_t * mod,char ** arr,size_t dim)2136828Stsien mem_strarray_free(topo_mod_t *mod, char **arr, size_t dim)
2146828Stsien {
2156828Stsien 	int i;
2166828Stsien 
2176828Stsien 	for (i = 0; i < dim; i++) {
2186828Stsien 		if (arr[i] != NULL)
2196828Stsien 			topo_mod_strfree(mod, arr[i]);
2206828Stsien 	}
2216828Stsien 	topo_mod_free(mod, arr, sizeof (char *) * dim);
2226828Stsien }
2236828Stsien 
2246828Stsien /*
2256828Stsien  * Niagara-1, Niagara-2, and Victoria Falls all have physical address
2266828Stsien  * spaces of 40 bits.
2276828Stsien  */
2286828Stsien 
2296828Stsien #define	MEM_PHYS_ADDRESS_LIMIT	0x10000000000ULL
2306828Stsien 
2316828Stsien /*
2326828Stsien  * The 'mask' argument to extract_bits has 1's in those bit positions of
2336828Stsien  * the physical address used to select the DIMM (or set of DIMMs) which will
2346828Stsien  * store the contents of the physical address.  If we extract those bits, ie.
2356828Stsien  * remove them and collapse the holes, the result is the 'address' within the
2366828Stsien  * DIMM or set of DIMMs where the contents are stored.
2376828Stsien  */
2386828Stsien 
2396828Stsien static uint64_t
extract_bits(uint64_t paddr,uint64_t mask)2406828Stsien extract_bits(uint64_t paddr, uint64_t mask)
2416828Stsien {
2426828Stsien 	uint64_t from, to;
2436828Stsien 	uint64_t result = 0;
2446828Stsien 
2456828Stsien 	to = 1;
2466828Stsien 	for (from = 1; from <= MEM_PHYS_ADDRESS_LIMIT; from <<= 1) {
2476828Stsien 		if ((from & mask) == 0) {
2486828Stsien 			if ((from & paddr) != 0)
2496828Stsien 				result |= to;
2506828Stsien 			to <<= 1;
2516828Stsien 		}
2526828Stsien 	}
2536828Stsien 	return (result);
2546828Stsien }
2556828Stsien 
2566828Stsien /*
2576828Stsien  * insert_bits is the reverse operation to extract_bits.  Where extract_bits
2586828Stsien  * removes from the physical address those bits which select a DIMM or set
2596828Stsien  * of DIMMs, insert_bits reconstitutes a physical address given the DIMM
2606828Stsien  * selection 'mask' and the 'value' for the address bits denoted by 1s in
2616828Stsien  * the 'mask'.
2626828Stsien  */
2636828Stsien static uint64_t
insert_bits(uint64_t offset,uint64_t mask,uint64_t value)2646828Stsien insert_bits(uint64_t offset, uint64_t mask, uint64_t value)
2656828Stsien {
2666828Stsien 	uint64_t result = 0;
2676828Stsien 	uint64_t from, to;
2686828Stsien 
2696828Stsien 	from = 1;
2706828Stsien 	for (to = 1; to <= MEM_PHYS_ADDRESS_LIMIT; to <<= 1) {
2716828Stsien 		if ((to & mask) == 0) {
2726828Stsien 			if ((offset & from) != 0)
2736828Stsien 				result |= to;
2746828Stsien 			from <<= 1;
2756828Stsien 		} else {
2766828Stsien 			result |= to & value;
2776828Stsien 		}
2786828Stsien 	}
2796828Stsien 	return (result);
2806828Stsien }
2816828Stsien 
2826828Stsien uint64_t
calc_phys_addr(mem_seg_map_t * seg,char * ds,uint64_t offset)2836828Stsien calc_phys_addr(mem_seg_map_t *seg, char *ds, uint64_t offset)
2846828Stsien {
2856828Stsien 	mem_bank_map_t *bm;
286*11683STom.Pothier@Sun.COM 	mem_dimm_list_t *dl;
2876828Stsien 
2886828Stsien 	for (bm = seg->sm_grp->mg_bank; bm != NULL; bm = bm->bm_grp) {
289*11683STom.Pothier@Sun.COM 		dl = bm->bm_dlist;
290*11683STom.Pothier@Sun.COM 		while (dl != NULL) {
291*11683STom.Pothier@Sun.COM 			if (strcmp(dl->dl_dimm->dm_serid, ds) == 0)
2926828Stsien 				return (insert_bits(offset<<bm->bm_shift,
2936828Stsien 				    bm->bm_mask, bm->bm_match));
294*11683STom.Pothier@Sun.COM 			dl = dl->dl_next;
2956828Stsien 		}
2966828Stsien 	}
2976828Stsien 	return ((uint64_t)-1);
2986828Stsien }
2996828Stsien 
3006828Stsien void
mem_expand_opt(topo_mod_t * mod,nvlist_t * nvl,char ** serids)3016828Stsien mem_expand_opt(topo_mod_t *mod, nvlist_t *nvl, char **serids)
3026828Stsien {
3036828Stsien 	md_mem_info_t *mem = (md_mem_info_t *)topo_mod_getspecific(mod);
3046828Stsien 	mem_seg_map_t *seg;
3056828Stsien 	mem_bank_map_t *bm;
3066828Stsien 	uint64_t offset, physaddr;
3076828Stsien 
3086828Stsien 	/*
3096828Stsien 	 * The following additional expansions are all optional.
3106828Stsien 	 * Failure to retrieve a data value, or failure to add it
3116828Stsien 	 * successfully to the FMRI, does NOT cause a failure of
3126828Stsien 	 * fmd_fmri_expand.  All optional expansions will be attempted
3136828Stsien 	 * once expand_opt is entered.
3146828Stsien 	 */
3156828Stsien 
3166828Stsien 	if (nvlist_lookup_uint64(nvl, FM_FMRI_MEM_OFFSET, &offset) == 0) {
3176828Stsien 		for (seg = mem->mem_seg; seg != NULL; seg = seg->sm_next) {
3186828Stsien 			physaddr = calc_phys_addr(seg, *serids, offset);
3196828Stsien 			if (physaddr >= seg->sm_base &&
3206828Stsien 			    physaddr < seg->sm_base + seg->sm_size) {
3216828Stsien 				(void) nvlist_add_uint64(nvl,
3226828Stsien 				    FM_FMRI_MEM_PHYSADDR, physaddr);
3236828Stsien 			}
3246828Stsien 		}
3256828Stsien 	} else if (nvlist_lookup_uint64(nvl,
3266828Stsien 	    FM_FMRI_MEM_PHYSADDR, &physaddr) == 0) {
3276828Stsien 		for (seg = mem->mem_seg; seg != NULL; seg = seg->sm_next) {
3286828Stsien 			if (physaddr >= seg->sm_base &&
3296828Stsien 			    physaddr < seg->sm_base + seg->sm_size) {
3306828Stsien 				bm = seg->sm_grp->mg_bank;
3316828Stsien 				/*
3326828Stsien 				 * The mask & shift values for all banks in a
3336828Stsien 				 * segment are always the same; only the match
3346828Stsien 				 * values differ, in order to specify a
3356828Stsien 				 * dimm-pair. But we already have a full unum.
3366828Stsien 				 */
3376828Stsien 				offset = extract_bits(physaddr,
3386828Stsien 				    bm->bm_mask) >> bm->bm_shift;
3396828Stsien 				(void) (nvlist_add_uint64(nvl,
3406828Stsien 				    FM_FMRI_MEM_OFFSET, offset));
3416828Stsien 			}
3426828Stsien 		}
3436828Stsien 	}
3446828Stsien }
3456828Stsien 
3466828Stsien /*
3476828Stsien  * The sun4v mem: scheme expand() now assumes that the FMRI -has- serial
3486828Stsien  * numbers, therefore we should never have to call mem_unum_burst again.
3496828Stsien  * Part numbers will be supplied in hc: scheme from the hc: enumeration.
3506828Stsien  * What's left: physical address and offset calculations.
3516828Stsien  */
3526828Stsien 
3536828Stsien /*ARGSUSED*/
3546828Stsien static int
mem_expand(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)3556828Stsien mem_expand(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
3566828Stsien     nvlist_t *in, nvlist_t **out)
3576828Stsien {
3586828Stsien 	int rc;
3596828Stsien 	uint8_t version;
3606828Stsien 	char *unum, **nvlserids;
3616828Stsien 	size_t nserids;
3626828Stsien 
3636828Stsien 	if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 ||
3646828Stsien 	    version > FM_MEM_SCHEME_VERSION ||
3656828Stsien 	    nvlist_lookup_string(in, FM_FMRI_MEM_UNUM, &unum) != 0)
3666828Stsien 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
3676828Stsien 
3686828Stsien 	if ((rc = nvlist_lookup_string_array(in, FM_FMRI_MEM_SERIAL_ID,
3696828Stsien 	    &nvlserids, &nserids)) == 0) { /* already have serial #s */
3706828Stsien 		mem_expand_opt(mod, in, nvlserids);
3716828Stsien 		return (0);
3726828Stsien 	} else if (rc != ENOENT)
3736828Stsien 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
3746828Stsien 	else
3756828Stsien 		return (-1);
3766828Stsien }
3776828Stsien 
3786828Stsien int
mem_page_isretired(topo_mod_t * mod,nvlist_t * nvl)3797532SSean.Ye@Sun.COM mem_page_isretired(topo_mod_t *mod, nvlist_t *nvl)
3806828Stsien {
3817532SSean.Ye@Sun.COM 	ldom_hdl_t *lhp;
3826828Stsien 	int rc;
3836828Stsien 
3846828Stsien 	if ((lhp = ldom_init(mem_alloc, mem_free)) == NULL) {
3857532SSean.Ye@Sun.COM 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
3867532SSean.Ye@Sun.COM 		errno = ENOMEM;
3877532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_FAIL);
3887532SSean.Ye@Sun.COM 	}
3897532SSean.Ye@Sun.COM 
3907532SSean.Ye@Sun.COM 	rc = ldom_fmri_status(lhp, nvl);
3917532SSean.Ye@Sun.COM 
3927532SSean.Ye@Sun.COM 	ldom_fini(lhp);
3937532SSean.Ye@Sun.COM 	errno = rc;
3947532SSean.Ye@Sun.COM 
3957532SSean.Ye@Sun.COM 	if (rc == 0 || rc == EINVAL)
3967532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_DONE);
3977532SSean.Ye@Sun.COM 	if (rc == EAGAIN)
3987532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_ASYNC);
3997532SSean.Ye@Sun.COM 
4007532SSean.Ye@Sun.COM 	return (FMD_AGENT_RETIRE_FAIL);
4017532SSean.Ye@Sun.COM }
4027532SSean.Ye@Sun.COM 
4037532SSean.Ye@Sun.COM int
mem_page_retire(topo_mod_t * mod,nvlist_t * nvl)4047532SSean.Ye@Sun.COM mem_page_retire(topo_mod_t *mod, nvlist_t *nvl)
4057532SSean.Ye@Sun.COM {
4067532SSean.Ye@Sun.COM 	ldom_hdl_t *lhp;
4077532SSean.Ye@Sun.COM 	int rc;
4087532SSean.Ye@Sun.COM 
4097532SSean.Ye@Sun.COM 	if ((lhp = ldom_init(mem_alloc, mem_free)) == NULL) {
4107532SSean.Ye@Sun.COM 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
4117532SSean.Ye@Sun.COM 		errno = ENOMEM;
4127532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_FAIL);
4136828Stsien 	}
4146828Stsien 
4157532SSean.Ye@Sun.COM 	rc = ldom_fmri_retire(lhp, nvl);
4167532SSean.Ye@Sun.COM 
4177532SSean.Ye@Sun.COM 	ldom_fini(lhp);
4187532SSean.Ye@Sun.COM 	errno = rc;
4197532SSean.Ye@Sun.COM 
4207532SSean.Ye@Sun.COM 	if (rc == 0 || rc == EIO || rc == EINVAL)
4217532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_DONE);
4227532SSean.Ye@Sun.COM 	if (rc == EAGAIN)
4237532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_ASYNC);
4247532SSean.Ye@Sun.COM 
4257532SSean.Ye@Sun.COM 	return (FMD_AGENT_RETIRE_FAIL);
4267532SSean.Ye@Sun.COM }
4277532SSean.Ye@Sun.COM 
4287532SSean.Ye@Sun.COM int
mem_page_unretire(topo_mod_t * mod,nvlist_t * nvl)4297532SSean.Ye@Sun.COM mem_page_unretire(topo_mod_t *mod, nvlist_t *nvl)
4307532SSean.Ye@Sun.COM {
4317532SSean.Ye@Sun.COM 	ldom_hdl_t *lhp;
4327532SSean.Ye@Sun.COM 	int rc;
4337532SSean.Ye@Sun.COM 
4347532SSean.Ye@Sun.COM 	if ((lhp = ldom_init(mem_alloc, mem_free)) == NULL) {
4357532SSean.Ye@Sun.COM 		(void) topo_mod_seterrno(mod, EMOD_NOMEM);
4367532SSean.Ye@Sun.COM 		errno = ENOMEM;
4377532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_FAIL);
4386828Stsien 	}
4396828Stsien 
4407532SSean.Ye@Sun.COM 	rc = ldom_fmri_unretire(lhp, nvl);
4416828Stsien 
4426828Stsien 	ldom_fini(lhp);
4437532SSean.Ye@Sun.COM 	errno = rc;
4447532SSean.Ye@Sun.COM 
4457532SSean.Ye@Sun.COM 	if (rc == 0 || rc == EIO)
4467532SSean.Ye@Sun.COM 		return (FMD_AGENT_RETIRE_DONE);
4477532SSean.Ye@Sun.COM 
4487532SSean.Ye@Sun.COM 	return (FMD_AGENT_RETIRE_FAIL);
4497532SSean.Ye@Sun.COM 
4506828Stsien }
4516828Stsien 
4526828Stsien /*ARGSUSED*/
4536828Stsien static int
mem_unusable(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)4546828Stsien mem_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
4556828Stsien     nvlist_t *in, nvlist_t **out)
4566828Stsien {
4576828Stsien 	int rc = -1;
4586828Stsien 	uint8_t version;
45910825SJakub.Jermar@Sun.COM 	uint64_t val1, val2;
4606828Stsien 	int err1, err2;
4616828Stsien 	uint32_t retval;
4626828Stsien 
4636828Stsien 	if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 ||
4646828Stsien 	    version > FM_MEM_SCHEME_VERSION)
4656828Stsien 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
4666828Stsien 
46710825SJakub.Jermar@Sun.COM 	err1 = nvlist_lookup_uint64(in, FM_FMRI_MEM_OFFSET, &val1);
46810825SJakub.Jermar@Sun.COM 	err2 = nvlist_lookup_uint64(in, FM_FMRI_MEM_PHYSADDR, &val2);
4696828Stsien 
4706828Stsien 	if (err1 == ENOENT && err2 == ENOENT)
4716828Stsien 		return (0); /* no page, so assume it's still usable */
4726828Stsien 
4736828Stsien 	if ((err1 != 0 && err1 != ENOENT) || (err2 != 0 && err2 != ENOENT))
4746828Stsien 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
4756828Stsien 
4766828Stsien 	/*
4776828Stsien 	 * Ask the kernel if the page is retired, using
4786828Stsien 	 * the original mem FMRI with the specified offset or PA.
4796828Stsien 	 * Refer to the kernel's page_retire_check() for the error codes.
4806828Stsien 	 */
4817532SSean.Ye@Sun.COM 	rc = mem_page_isretired(mod, in);
4826828Stsien 
4837532SSean.Ye@Sun.COM 	if (rc == FMD_AGENT_RETIRE_FAIL) {
4846828Stsien 		/*
4856828Stsien 		 * The page is not retired and is not scheduled for retirement
4866828Stsien 		 * (i.e. no request pending and has not seen any errors)
4876828Stsien 		 */
4886828Stsien 		retval = 0;
4897532SSean.Ye@Sun.COM 	} else if (rc == FMD_AGENT_RETIRE_DONE ||
4907532SSean.Ye@Sun.COM 	    rc == FMD_AGENT_RETIRE_ASYNC) {
4916828Stsien 		/*
4926828Stsien 		 * The page has been retired, is in the process of being
4936828Stsien 		 * retired, or doesn't exist.  The latter is valid if the page
4946828Stsien 		 * existed in the past but has been DR'd out.
4956828Stsien 		 */
4966828Stsien 		retval = 1;
4976828Stsien 	} else {
4986828Stsien 		/*
4996828Stsien 		 * Errors are only signalled to the caller if they're the
5006828Stsien 		 * caller's fault.  This isn't - it's a failure of the
5016828Stsien 		 * retirement-check code.  We'll whine about it and tell
5026828Stsien 		 * the caller the page is unusable.
5036828Stsien 		 */
5046828Stsien 		topo_mod_dprintf(mod,
5056828Stsien 		    "failed to determine page %s=%llx usability: "
5066828Stsien 		    "rc=%d errno=%d\n", err1 == 0 ? FM_FMRI_MEM_OFFSET :
50710825SJakub.Jermar@Sun.COM 		    FM_FMRI_MEM_PHYSADDR, err1 == 0 ? (u_longlong_t)val1 :
50810825SJakub.Jermar@Sun.COM 		    (u_longlong_t)val2, rc, errno);
5096828Stsien 		retval = 1;
5106828Stsien 	}
5116828Stsien 
5127977SLouis.Tsien@Sun.COM 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
5137977SLouis.Tsien@Sun.COM 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
5146828Stsien 	if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, retval) != 0) {
5156828Stsien 		nvlist_free(*out);
5166828Stsien 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
5176828Stsien 	}
5186828Stsien 	return (0);
5196828Stsien }
5206828Stsien 
5216828Stsien /* ARGSUSED */
5226828Stsien static int
mem_contains(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)5236828Stsien mem_contains(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
5246828Stsien     nvlist_t *in, nvlist_t **out)
5256828Stsien {
5267532SSean.Ye@Sun.COM 	int rc = -1, ret = 1;
5276828Stsien 	uint8_t version;
5286828Stsien 	unsigned int erx, eex, ersiz, eesiz;
5296828Stsien 	nvlist_t *er, *ee;
5306828Stsien 	char **ersna, **eesna;
5316828Stsien 
5326828Stsien 	/*
5336828Stsien 	 * Unlike the other exported functions, the 'in' argument here is
5346828Stsien 	 * not a pass-through -- it is a composite of the 'container' and
5356828Stsien 	 * 'containee' FMRIs.  Rather than checking the version of 'in',
5366828Stsien 	 * check the versions of the container and containee.
5376828Stsien 	 */
5386828Stsien 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &er) != 0 ||
5396828Stsien 	    nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &ee) != 0 ||
5406828Stsien 	    nvlist_lookup_uint8(er, FM_VERSION, &version) != 0 ||
5416828Stsien 	    version > FM_MEM_SCHEME_VERSION ||
5426828Stsien 	    nvlist_lookup_uint8(ee, FM_VERSION, &version) != 0 ||
5436828Stsien 	    version > FM_MEM_SCHEME_VERSION ||
5446828Stsien 	    nvlist_lookup_string_array(er, FM_FMRI_MEM_SERIAL_ID,
5456828Stsien 	    &ersna, &ersiz) != 0 ||
5466828Stsien 	    nvlist_lookup_string_array(ee, FM_FMRI_MEM_SERIAL_ID,
5476828Stsien 	    &eesna, &eesiz) != 0)
5486828Stsien 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
5496828Stsien 
5506828Stsien 	/*
5516828Stsien 	 * Look up each 'ee' serial number in serial number list of 'er'.
5526828Stsien 	 * If any are not found, return "false"; if all are found, return
5536828Stsien 	 * "true".
5546828Stsien 	 */
5556828Stsien 
5566828Stsien 	for (eex = 0; eex < eesiz; eex++) {
5576828Stsien 		for (erx = 0; erx < ersiz; erx++) {
5586828Stsien 			rc = strcmp(ersna[erx], eesna[eex]);
5596828Stsien 			if (rc == 0)
5606828Stsien 				break;
5616828Stsien 		}
5626828Stsien 		if (rc != 0) {
5636828Stsien 			/* failed -- no containment */
5647532SSean.Ye@Sun.COM 			ret = 0;
5657532SSean.Ye@Sun.COM 			break;
5666828Stsien 		}
5676828Stsien 	}
5686828Stsien 	/* success */
5697532SSean.Ye@Sun.COM 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) {
5707532SSean.Ye@Sun.COM 		if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET, ret) != 0) {
5717532SSean.Ye@Sun.COM 			nvlist_free(*out);
5727532SSean.Ye@Sun.COM 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
5737532SSean.Ye@Sun.COM 		}
5747532SSean.Ye@Sun.COM 		return (0);
5756828Stsien 	}
5767532SSean.Ye@Sun.COM 	return (-1);
5776828Stsien }
5786828Stsien 
5796828Stsien static nvlist_t *
mem_fmri_create(topo_mod_t * mod,char * unum,char * serial)5806828Stsien mem_fmri_create(topo_mod_t *mod, char *unum, char *serial)
5816828Stsien {
5826828Stsien 	int err;
5836828Stsien 	nvlist_t *fmri;
5846828Stsien 
5856828Stsien 	if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
5866828Stsien 		return (NULL);
5876828Stsien 	err = nvlist_add_uint8(fmri, FM_VERSION, FM_MEM_SCHEME_VERSION);
5886828Stsien 	err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM);
5896828Stsien 	err |= nvlist_add_string(fmri, FM_FMRI_MEM_UNUM, unum);
5906828Stsien 	if (serial != NULL)
5916828Stsien 		err |= nvlist_add_string_array(fmri,
5926828Stsien 		    FM_FMRI_MEM_SERIAL_ID, &serial, 1);
5936828Stsien 	if (err != 0) {
5946828Stsien 		nvlist_free(fmri);
5956828Stsien 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
5966828Stsien 		return (NULL);
5976828Stsien 	}
5986828Stsien 
5996828Stsien 	return (fmri);
6006828Stsien }
6016828Stsien 
6026828Stsien 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)6036828Stsien mem_tnode_create(topo_mod_t *mod, tnode_t *parent,
6046828Stsien     const char *name, topo_instance_t i, char *unum, char *serial, void *priv)
6056828Stsien {
6066828Stsien 	nvlist_t *fmri;
6076828Stsien 	tnode_t *ntn;
6086828Stsien 
6096828Stsien 	fmri = mem_fmri_create(mod, unum, serial);
6106828Stsien 	if (fmri == NULL) {
6116828Stsien 		topo_mod_dprintf(mod,
6126828Stsien 		    "Unable to make nvlist for %s bind: %s.\n",
6136828Stsien 		    name, topo_mod_errmsg(mod));
6146828Stsien 		return (NULL);
6156828Stsien 	}
6166828Stsien 
6176828Stsien 	ntn = topo_node_bind(mod, parent, name, i, fmri);
6186828Stsien 	if (ntn == NULL) {
6196828Stsien 		topo_mod_dprintf(mod,
6206828Stsien 		    "topo_node_bind (%s%d/%s%d) failed: %s\n",
6216828Stsien 		    topo_node_name(parent), topo_node_instance(parent),
6226828Stsien 		    name, i,
6236828Stsien 		    topo_strerror(topo_mod_errno(mod)));
6246828Stsien 		nvlist_free(fmri);
6256828Stsien 		return (NULL);
6266828Stsien 	}
6276828Stsien 	nvlist_free(fmri);
6286828Stsien 	topo_node_setspecific(ntn, priv);
6296828Stsien 
6306828Stsien 	return (ntn);
6316828Stsien }
6326828Stsien 
6336828Stsien /*ARGSUSED*/
6346828Stsien static int
mem_create(topo_mod_t * mod,tnode_t * rnode,const char * name,md_mem_info_t * mem)6356828Stsien mem_create(topo_mod_t *mod, tnode_t *rnode, const char *name,
6366828Stsien     md_mem_info_t *mem)
6376828Stsien {
6386828Stsien 	int i;
6396828Stsien 	int nerr = 0;
6406828Stsien 	int ndimms = 0;
6416828Stsien 	mem_dimm_map_t *mp;
6426828Stsien 	tnode_t *cnode;
6436828Stsien 
6446828Stsien 	topo_mod_dprintf(mod, "enumerating memory\n");
6456828Stsien 
6466828Stsien 	/*
6476828Stsien 	 * Count the dimms and create a range.  The instance numbers
6486828Stsien 	 * are not meaningful in this context.
6496828Stsien 	 */
6506828Stsien 	for (mp = mem->mem_dm; mp != NULL; mp = mp->dm_next) {
6516828Stsien 		ndimms++;
6526828Stsien 	}
6536828Stsien 	if (ndimms == 0)
6546828Stsien 		return (-1);
6556828Stsien 	topo_node_range_destroy(rnode, name);
6566828Stsien 	if (topo_node_range_create(mod, rnode, name, 0, ndimms+1) < 0) {
6576828Stsien 		topo_mod_dprintf(mod, "failed to create dimm range[0,%d]: %s\n",
6586828Stsien 		    ndimms, topo_mod_errmsg(mod));
6596828Stsien 		return (-1);
6606828Stsien 	}
6616828Stsien 
6626828Stsien 	/*
6636828Stsien 	 * Create the dimm nodes
6646828Stsien 	 */
6656828Stsien 	for (mp = mem->mem_dm, i = 0; mp != NULL; mp = mp->dm_next, i++) {
6666828Stsien 		cnode = mem_tnode_create(mod, rnode, name, (topo_instance_t)i,
6676828Stsien 		    mp->dm_label, mp->dm_serid, NULL);
6686828Stsien 		if (cnode == NULL) {
6696828Stsien 			topo_mod_dprintf(mod,
6706828Stsien 			    "failed to create dimm=%d node: %s\n",
6716828Stsien 			    i, topo_mod_errmsg(mod));
6726828Stsien 			nerr++;
6736828Stsien 		}
6746828Stsien 	}
6756828Stsien 
6766828Stsien 	if (nerr != 0)
6776828Stsien 		(void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
6786828Stsien 
6796828Stsien 	return (0);
6806828Stsien }
6816828Stsien 
6826828Stsien /*ARGSUSED*/
6836828Stsien 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)6846828Stsien mem_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
6856828Stsien     topo_instance_t min, topo_instance_t max, void *arg, void *notused)
6866828Stsien {
6876828Stsien 	topo_mod_dprintf(mod, "%s enumerating %s\n", PLATFORM_MEM_NAME, name);
6886828Stsien 
6896828Stsien 	if (topo_method_register(mod, rnode, mem_methods) < 0) {
6906828Stsien 		topo_mod_dprintf(mod, "topo_method_register failed: %s\n",
6916828Stsien 		    topo_strerror(topo_mod_errno(mod)));
6926828Stsien 		return (-1);
6936828Stsien 	}
6946828Stsien 
6956828Stsien 	if (strcmp(name, MEM_NODE_NAME) == 0)
6966828Stsien 		return (mem_create(mod, rnode, name, (md_mem_info_t *)arg));
6976828Stsien 
6986828Stsien 	return (0);
6996828Stsien }
7006828Stsien 
7016828Stsien /*ARGSUSED*/
7026828Stsien static void
mem_release(topo_mod_t * mod,tnode_t * node)7036828Stsien mem_release(topo_mod_t *mod, tnode_t *node)
7046828Stsien {
7056828Stsien 	topo_method_unregister_all(mod, node);
7066828Stsien }
707