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