Home | History | Annotate | Download | only in chip
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * Support function for the i86pc chip enumerator
     29  */
     30 
     31 #include <sys/types.h>
     32 #include <stdarg.h>
     33 #include <strings.h>
     34 #include <fm/fmd_fmri.h>
     35 #include <sys/systeminfo.h>
     36 #include <sys/fm/protocol.h>
     37 #include <fm/topo_mod.h>
     38 #include <fm/fmd_agent.h>
     39 
     40 #include "chip.h"
     41 
     42 static void fmri_dprint(topo_mod_t *, const char *, uint32_t, nvlist_t *);
     43 static boolean_t is_page_fmri(nvlist_t *);
     44 
     45 /*
     46  * Whinge a debug message via topo_mod_dprintf and increment the
     47  * given error counter.
     48  */
     49 void
     50 whinge(topo_mod_t *mod, int *nerr, const char *fmt, ...)
     51 {
     52 	va_list ap;
     53 	char buf[160];
     54 
     55 	if (nerr != NULL)
     56 		++*nerr;
     57 
     58 	va_start(ap, fmt);
     59 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
     60 	va_end(ap);
     61 
     62 	topo_mod_dprintf(mod, "%s", buf);
     63 }
     64 
     65 /*
     66  * Given an nvpair of a limited number of data types, extract the property
     67  * name and value and add that combination to the given node in the
     68  * specified property group using the corresponding topo_prop_set_* function
     69  * for the data type.  Return 1 on success, otherwise 0.
     70  */
     71 int
     72 nvprop_add(topo_mod_t *mod, nvpair_t *nvp, const char *pgname, tnode_t *node)
     73 {
     74 	int success = 0;
     75 	int err;
     76 	char *pname = nvpair_name(nvp);
     77 
     78 	switch (nvpair_type(nvp)) {
     79 	case DATA_TYPE_BOOLEAN_VALUE: {
     80 		boolean_t val;
     81 
     82 		if (nvpair_value_boolean_value(nvp, &val) == 0 &&
     83 		    topo_prop_set_string(node, pgname, pname,
     84 		    TOPO_PROP_IMMUTABLE, val ? "true" : "false", &err) == 0)
     85 			success = 1;
     86 		break;
     87 	}
     88 
     89 	case DATA_TYPE_UINT32: {
     90 		uint32_t val;
     91 
     92 		if (nvpair_value_uint32(nvp, &val) == 0 &&
     93 		    topo_prop_set_uint32(node, pgname, pname,
     94 		    TOPO_PROP_IMMUTABLE, val, &err) == 0)
     95 			success = 1;
     96 		break;
     97 	}
     98 
     99 	case DATA_TYPE_UINT64: {
    100 		uint64_t val;
    101 
    102 		if (nvpair_value_uint64(nvp, &val) == 0 &&
    103 		    topo_prop_set_uint64(node, pgname, pname,
    104 		    TOPO_PROP_IMMUTABLE, val, &err) == 0)
    105 			success = 1;
    106 		break;
    107 	}
    108 
    109 	case DATA_TYPE_UINT32_ARRAY: {
    110 		uint32_t *arrp;
    111 		uint_t nelem;
    112 
    113 		if (nvpair_value_uint32_array(nvp, &arrp, &nelem) == 0 &&
    114 		    nelem > 0 && topo_prop_set_uint32_array(node, pgname, pname,
    115 		    TOPO_PROP_IMMUTABLE, arrp, nelem, &err) == 0)
    116 			success = 1;
    117 		break;
    118 	}
    119 
    120 	case DATA_TYPE_STRING: {
    121 		char *str;
    122 
    123 		if (nvpair_value_string(nvp, &str) == 0 &&
    124 		    topo_prop_set_string(node, pgname, pname,
    125 		    TOPO_PROP_IMMUTABLE, str, &err) == 0)
    126 			success = 1;
    127 		break;
    128 	}
    129 
    130 	default:
    131 		whinge(mod, &err, "nvprop_add: Can't handle type %d for "
    132 		    "'%s' in property group %s of %s node\n",
    133 		    nvpair_type(nvp), pname, pgname, topo_node_name(node));
    134 		break;
    135 	}
    136 
    137 	return (success ? 0 : 1);
    138 }
    139 
    140 /*
    141  * Lookup string data named pname in the given nvlist and add that
    142  * as property named pname in the given property group pgname on the indicated
    143  * topo node.  Fill pvalp with a pointer to the string value, valid until
    144  * nvlist_free is called.
    145  */
    146 int
    147 add_nvlist_strprop(topo_mod_t *mod, tnode_t *node, nvlist_t *nvl,
    148     const char *pgname, const char *pname, const char **pvalp)
    149 {
    150 	char *pval;
    151 	int err = 0;
    152 
    153 	if (nvlist_lookup_string(nvl, pname, &pval) != 0)
    154 		return (-1);
    155 
    156 	if (topo_prop_set_string(node, pgname, pname,
    157 	    TOPO_PROP_IMMUTABLE, pval, &err) == 0) {
    158 		if (pvalp)
    159 			*pvalp = pval;
    160 		return (0);
    161 	} else {
    162 		whinge(mod, &err, "add_nvlist_strprop: failed to add '%s'\n",
    163 		    pname);
    164 		return (-1);
    165 	}
    166 }
    167 
    168 /*
    169  * Lookup an int32 item named pname in the given nvlist and add that
    170  * as property named pname in the given property group pgname on the indicated
    171  * topo node.  Fill pvalp with the property value.
    172  */
    173 int
    174 add_nvlist_longprop(topo_mod_t *mod, tnode_t *node, nvlist_t *nvl,
    175     const char *pgname, const char *pname, int32_t *pvalp)
    176 {
    177 	int32_t pval;
    178 	int err;
    179 
    180 	if ((nvlist_lookup_int32(nvl, pname, &pval)) != 0)
    181 		return (-1);
    182 
    183 	if (topo_prop_set_int32(node, pgname, pname,
    184 	    TOPO_PROP_IMMUTABLE, pval, &err) == 0) {
    185 		if (pvalp)
    186 			*pvalp = pval;
    187 		return (0);
    188 	} else {
    189 		whinge(mod, &err, "add_nvlist_longprop: failed to add '%s'\n",
    190 		    pname);
    191 		return (-1);
    192 	}
    193 }
    194 
    195 /*
    196  * In a given nvlist lookup a variable number of int32 properties named in
    197  * const char * varargs and each each in the given property group on the
    198  * node.  Fill an array of the retrieved values.
    199  */
    200 int
    201 add_nvlist_longprops(topo_mod_t *mod, tnode_t *node, nvlist_t *nvl,
    202     const char *pgname, int32_t *pvalap, ...)
    203 {
    204 	const char *pname;
    205 	va_list ap;
    206 	int nerr = 0;
    207 
    208 	va_start(ap, pvalap);
    209 	while ((pname = va_arg(ap, const char *)) != NULL) {
    210 		if (add_nvlist_longprop(mod, node, nvl, pgname, pname,
    211 		    pvalap) != 0)
    212 			nerr++;		/* have whinged elsewhere */
    213 
    214 		if (pvalap != NULL)
    215 			++pvalap;
    216 	}
    217 	va_end(ap);
    218 
    219 	return (nerr == 0 ? 0 : -1);
    220 }
    221 
    222 /*
    223  * Construct an hc scheme resource FMRI for a node named name with
    224  * instance number inst, parented by the given parent node pnode.
    225  */
    226 int
    227 mkrsrc(topo_mod_t *mod, tnode_t *pnode, const char *name, int inst,
    228     nvlist_t *auth, nvlist_t **nvl)
    229 {
    230 	*nvl = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, name,
    231 	    inst, NULL, auth, NULL, NULL, NULL);
    232 	return (nvl != NULL ? 0 : -1);	/* caller must free nvlist */
    233 }
    234 
    235 /*
    236  * Construct a cpu scheme FMRI with the given data; the caller must free
    237  * the allocated nvlist with nvlist_free().
    238  */
    239 nvlist_t *
    240 cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask)
    241 {
    242 	int err;
    243 	nvlist_t *asru;
    244 
    245 	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
    246 		return (NULL);
    247 
    248 	err = nvlist_add_uint8(asru, FM_VERSION, FM_CPU_SCHEME_VERSION);
    249 	err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
    250 	err |= nvlist_add_uint32(asru, FM_FMRI_CPU_ID, cpuid);
    251 	err |= nvlist_add_uint8(asru, FM_FMRI_CPU_MASK, cpumask);
    252 	if (s != NULL)
    253 		err |= nvlist_add_string(asru, FM_FMRI_CPU_SERIAL_ID, s);
    254 	if (err != 0) {
    255 		nvlist_free(asru);
    256 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
    257 		return (NULL);
    258 	}
    259 
    260 	return (asru);
    261 }
    262 
    263 /*ARGSUSED*/
    264 int
    265 mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    266     nvlist_t *in, nvlist_t **out)
    267 {
    268 	nvlist_t *asru, *args, *pargs, *hcsp;
    269 	int err;
    270 	uint64_t pa, offset;
    271 
    272 	if (strcmp(topo_node_name(node), RANK_NODE_NAME) != 0 &&
    273 	    strcmp(topo_node_name(node), DIMM_NODE_NAME) != 0 &&
    274 	    strcmp(topo_node_name(node), CS_NODE_NAME) != 0)
    275 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
    276 
    277 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0)
    278 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
    279 
    280 	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs)) != 0) {
    281 		if (err == ENOENT) {
    282 			pargs = args;
    283 		} else {
    284 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
    285 		}
    286 	}
    287 
    288 	if (topo_mod_nvdup(mod, pargs, &asru) != 0)
    289 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
    290 
    291 	err = 0;
    292 
    293 	/*
    294 	 * if 'in' includes an hc-specific member which specifies asru-physaddr
    295 	 * or asru-offset then rename them to asru and physaddr respectively.
    296 	 */
    297 	if (nvlist_lookup_nvlist(asru, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) {
    298 		if (nvlist_lookup_uint64(hcsp,
    299 		    "asru-"FM_FMRI_HC_SPECIFIC_PHYSADDR, &pa) == 0) {
    300 			err += nvlist_remove(hcsp,
    301 			    "asru-"FM_FMRI_HC_SPECIFIC_PHYSADDR,
    302 			    DATA_TYPE_UINT64);
    303 			err += nvlist_add_uint64(hcsp,
    304 			    FM_FMRI_HC_SPECIFIC_PHYSADDR,
    305 			    pa);
    306 		}
    307 
    308 		if (nvlist_lookup_uint64(hcsp,
    309 		    "asru-"FM_FMRI_HC_SPECIFIC_OFFSET, &offset) == 0) {
    310 			err += nvlist_remove(hcsp,
    311 			    "asru-"FM_FMRI_HC_SPECIFIC_OFFSET,
    312 			    DATA_TYPE_UINT64);
    313 			err += nvlist_add_uint64(hcsp,
    314 			    FM_FMRI_HC_SPECIFIC_OFFSET,
    315 			    offset);
    316 		}
    317 	}
    318 
    319 	if (err != 0 || topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
    320 		nvlist_free(asru);
    321 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
    322 	}
    323 
    324 	err = nvlist_add_string(*out, TOPO_PROP_VAL_NAME, TOPO_PROP_ASRU);
    325 	err |= nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_FMRI);
    326 	err |= nvlist_add_nvlist(*out, TOPO_PROP_VAL_VAL, asru);
    327 	if (err != 0) {
    328 		nvlist_free(asru);
    329 		nvlist_free(*out);
    330 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    331 	}
    332 
    333 	nvlist_free(asru);
    334 
    335 	return (0);
    336 }
    337 
    338 static int
    339 set_retnvl(topo_mod_t *mod, nvlist_t **out, const char *retname, uint32_t ret)
    340 {
    341 	nvlist_t *nvl;
    342 
    343 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) < 0)
    344 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
    345 
    346 	if (nvlist_add_uint32(nvl, retname, ret) != 0) {
    347 		nvlist_free(nvl);
    348 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
    349 	}
    350 
    351 	*out = nvl;
    352 	return (0);
    353 }
    354 
    355 /*
    356  * If we're getting called then the question of whether this dimm is plugged
    357  * in has already been answered.  What we don't know for sure is whether it's
    358  * the same dimm or a different one plugged in the same slot.  To check, we
    359  * try and compare the serial numbers on the dimm in the current topology with
    360  * the serial num from the unum fmri that got passed into this function as the
    361  * argument.
    362  *
    363  */
    364 static int
    365 fmri_replaced(topo_mod_t *mod, tnode_t *node, nvlist_t *unum, int *errp)
    366 {
    367 	tnode_t *dimmnode;
    368 	nvlist_t *resource;
    369 	int rc, err;
    370 	char *old_serial, *curr_serial;
    371 	fmd_agent_hdl_t *hdl;
    372 
    373 	/*
    374 	 * If input is a page, return "replaced" if the offset is invalid.
    375 	 */
    376 	if (is_page_fmri(unum) &&
    377 	    (hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) {
    378 		rc = fmd_agent_page_isretired(hdl, unum);
    379 		err = fmd_agent_errno(hdl);
    380 		fmd_agent_close(hdl);
    381 
    382 		if (rc == FMD_AGENT_RETIRE_DONE &&
    383 		    err == EINVAL)
    384 			return (FMD_OBJ_STATE_NOT_PRESENT);
    385 	}
    386 
    387 	/*
    388 	 * If a serial number for the dimm was available at the time of the
    389 	 * fault, it will have been added as a string to the unum nvlist
    390 	 */
    391 	if (nvlist_lookup_string(unum, FM_FMRI_HC_SERIAL_ID, &old_serial))
    392 		return (FMD_OBJ_STATE_UNKNOWN);
    393 
    394 	/*
    395 	 * If the current serial number is available for the DIMM that this rank
    396 	 * belongs to, it will be accessible as a property on the parent (dimm)
    397 	 * node. If there is a serial id in the resource fmri, then use that.
    398 	 * Otherwise fall back to looking for a serial id property in the
    399 	 * protocol group.
    400 	 */
    401 	dimmnode = topo_node_parent(node);
    402 	if (topo_node_resource(dimmnode, &resource, &err) != -1) {
    403 		if (nvlist_lookup_string(resource, FM_FMRI_HC_SERIAL_ID,
    404 		    &curr_serial) == 0) {
    405 			if (strcmp(old_serial, curr_serial) != 0) {
    406 				nvlist_free(resource);
    407 				return (FMD_OBJ_STATE_REPLACED);
    408 			} else {
    409 				nvlist_free(resource);
    410 				return (FMD_OBJ_STATE_STILL_PRESENT);
    411 			}
    412 		}
    413 		nvlist_free(resource);
    414 	}
    415 	if (topo_prop_get_string(dimmnode, TOPO_PGROUP_PROTOCOL,
    416 	    FM_FMRI_HC_SERIAL_ID, &curr_serial, &err) != 0) {
    417 		if (err == ETOPO_PROP_NOENT) {
    418 			return (FMD_OBJ_STATE_UNKNOWN);
    419 		} else {
    420 			*errp = EMOD_NVL_INVAL;
    421 			whinge(mod, NULL, "rank_fmri_present: Unexpected "
    422 			    "error retrieving serial from node");
    423 			return (-1);
    424 		}
    425 	}
    426 
    427 	if (strcmp(old_serial, curr_serial) != 0) {
    428 		topo_mod_strfree(mod, curr_serial);
    429 		return (FMD_OBJ_STATE_REPLACED);
    430 	}
    431 
    432 	topo_mod_strfree(mod, curr_serial);
    433 
    434 	return (FMD_OBJ_STATE_STILL_PRESENT);
    435 }
    436 
    437 /*
    438  * In the event we encounter problems comparing serials or if a comparison isn't
    439  * possible, we err on the side of caution and set is_present to TRUE.
    440  */
    441 int
    442 rank_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    443     nvlist_t *in, nvlist_t **out)
    444 {
    445 	int is_present, err;
    446 
    447 	if (version > TOPO_METH_PRESENT_VERSION)
    448 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    449 
    450 	switch (fmri_replaced(mod, node, in, &err)) {
    451 	case FMD_OBJ_STATE_REPLACED:
    452 	case FMD_OBJ_STATE_NOT_PRESENT:
    453 		is_present = 0;
    454 		break;
    455 
    456 	case FMD_OBJ_STATE_UNKNOWN:
    457 	case FMD_OBJ_STATE_STILL_PRESENT:
    458 		is_present = 1;
    459 		break;
    460 
    461 	default:
    462 		return (topo_mod_seterrno(mod,  err));
    463 	}
    464 
    465 	fmri_dprint(mod, "rank_fmri_present", is_present, in);
    466 
    467 	return (set_retnvl(mod, out, TOPO_METH_PRESENT_RET, is_present));
    468 }
    469 
    470 int
    471 rank_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    472     nvlist_t *in, nvlist_t **out)
    473 {
    474 	int is_replaced, err;
    475 
    476 	if (version > TOPO_METH_REPLACED_VERSION)
    477 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    478 
    479 	is_replaced = fmri_replaced(mod, node, in, &err);
    480 	if (is_replaced == -1)
    481 		return (topo_mod_seterrno(mod,  err));
    482 
    483 	fmri_dprint(mod, "rank_fmri_replaced", is_replaced, in);
    484 
    485 	return (set_retnvl(mod, out, TOPO_METH_REPLACED_RET, is_replaced));
    486 }
    487 
    488 static void
    489 fmri_dprint(topo_mod_t *mod, const char *op, uint32_t rc, nvlist_t *fmri)
    490 {
    491 	char *fmristr;
    492 	const char *status;
    493 
    494 	if (getenv("TOPOCHIPDBG") == NULL)
    495 		return;
    496 
    497 	switch (rc) {
    498 	case FMD_AGENT_RETIRE_DONE:
    499 		status = "sync success";
    500 		break;
    501 	case FMD_AGENT_RETIRE_ASYNC:
    502 		status = "async retiring";
    503 		break;
    504 	case FMD_AGENT_RETIRE_FAIL:
    505 		status = "not retired";
    506 		break;
    507 	default:
    508 		status = "unknown status";
    509 	}
    510 	if (fmri != NULL && topo_mod_nvl2str(mod, fmri, &fmristr) == 0) {
    511 		topo_mod_dprintf(mod, "[%s]: %s => %d (\"%s\")\n", fmristr,
    512 		    op, rc, status);
    513 		topo_mod_strfree(mod, fmristr);
    514 	}
    515 }
    516 
    517 struct strand_walk_data {
    518 	tnode_t		*parent;
    519 	fmd_agent_hdl_t	*hdl;
    520 	int		(*func)(fmd_agent_hdl_t *, int, int, int);
    521 	int		err;
    522 	int		done;
    523 	int		fail;
    524 	int		async;
    525 };
    526 
    527 static int
    528 strand_walker(topo_mod_t *mod, tnode_t *node, void *pdata)
    529 {
    530 	struct strand_walk_data *swdp = pdata;
    531 	int32_t chipid, coreid, strandid;
    532 	int err, rc;
    533 
    534 	/*
    535 	 * Terminate the walk if we reach start-node's sibling
    536 	 */
    537 	if (node != swdp->parent &&
    538 	    topo_node_parent(node) == topo_node_parent(swdp->parent))
    539 		return (TOPO_WALK_TERMINATE);
    540 
    541 	if (strcmp(topo_node_name(node), STRAND) != 0)
    542 		return (TOPO_WALK_NEXT);
    543 
    544 	if (topo_prop_get_int32(node, PGNAME(STRAND), STRAND_CHIP_ID,
    545 	    &chipid, &err) < 0 ||
    546 	    topo_prop_get_int32(node, PGNAME(STRAND), STRAND_CORE_ID,
    547 	    &coreid, &err) < 0) {
    548 		swdp->err++;
    549 		return (TOPO_WALK_NEXT);
    550 	}
    551 	strandid = topo_node_instance(node);
    552 	rc = swdp->func(swdp->hdl, chipid, coreid, strandid);
    553 
    554 	if (rc == FMD_AGENT_RETIRE_DONE)
    555 		swdp->done++;
    556 	else if (rc == FMD_AGENT_RETIRE_FAIL)
    557 		swdp->fail++;
    558 	else if (rc == FMD_AGENT_RETIRE_ASYNC)
    559 		swdp->async++;
    560 	else
    561 		swdp->err++;
    562 
    563 	if (getenv("TOPOCHIPDBG") != NULL) {
    564 		const char *op;
    565 
    566 		if (swdp->func == fmd_agent_cpu_retire)
    567 			op = "retire";
    568 		else if (swdp->func == fmd_agent_cpu_unretire)
    569 			op = "unretire";
    570 		else if (swdp->func == fmd_agent_cpu_isretired)
    571 			op = "check status";
    572 		else
    573 			op = "unknown op";
    574 
    575 		topo_mod_dprintf(mod, "%s cpu (%d:%d:%d): rc = %d, err = %s\n",
    576 		    op, (int)chipid, (int)coreid, (int)strandid, rc,
    577 		    fmd_agent_errmsg(swdp->hdl));
    578 	}
    579 
    580 	return (TOPO_WALK_NEXT);
    581 }
    582 
    583 static int
    584 walk_strands(topo_mod_t *mod, struct strand_walk_data *swdp, tnode_t *parent,
    585     int (*func)(fmd_agent_hdl_t *, int, int, int))
    586 {
    587 	topo_walk_t *twp;
    588 	int err;
    589 
    590 	swdp->parent = parent;
    591 	swdp->func = func;
    592 	swdp->err = swdp->done = swdp->fail = swdp->async = 0;
    593 	if ((swdp->hdl = fmd_agent_open(FMD_AGENT_VERSION)) == NULL) {
    594 		swdp->fail++;
    595 		return (0);
    596 	}
    597 
    598 	twp = topo_mod_walk_init(mod, parent, strand_walker, swdp, &err);
    599 	if (twp == NULL) {
    600 		fmd_agent_close(swdp->hdl);
    601 		return (-1);
    602 	}
    603 
    604 	err = topo_walk_step(twp, TOPO_WALK_CHILD);
    605 	topo_walk_fini(twp);
    606 	fmd_agent_close(swdp->hdl);
    607 
    608 	if (err == TOPO_WALK_ERR || swdp->err > 0)
    609 		return (-1);
    610 
    611 	return (0);
    612 }
    613 
    614 /* ARGSUSED */
    615 int
    616 retire_strands(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    617     nvlist_t *in, nvlist_t **out)
    618 {
    619 	struct strand_walk_data swd;
    620 	uint32_t rc;
    621 
    622 	if (version > TOPO_METH_RETIRE_VERSION)
    623 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    624 
    625 	if (walk_strands(mod, &swd, node, fmd_agent_cpu_retire) == -1)
    626 		return (-1);
    627 
    628 	if (swd.fail > 0)
    629 		rc = FMD_AGENT_RETIRE_FAIL;
    630 	else if (swd.async > 0)
    631 		rc = FMD_AGENT_RETIRE_ASYNC;
    632 	else
    633 		rc = FMD_AGENT_RETIRE_DONE;
    634 
    635 	return (set_retnvl(mod, out, TOPO_METH_RETIRE_RET, rc));
    636 }
    637 
    638 /* ARGSUSED */
    639 int
    640 unretire_strands(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    641     nvlist_t *in, nvlist_t **out)
    642 {
    643 	struct strand_walk_data swd;
    644 	uint32_t rc;
    645 
    646 	if (version > TOPO_METH_UNRETIRE_VERSION)
    647 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    648 
    649 	if (walk_strands(mod, &swd, node, fmd_agent_cpu_unretire) == -1)
    650 		return (-1);
    651 
    652 	if (swd.fail > 0)
    653 		rc = FMD_AGENT_RETIRE_FAIL;
    654 	else if (swd.async > 0)
    655 		rc = FMD_AGENT_RETIRE_ASYNC;
    656 	else
    657 		rc = FMD_AGENT_RETIRE_DONE;
    658 
    659 	return (set_retnvl(mod, out, TOPO_METH_UNRETIRE_RET, rc));
    660 }
    661 
    662 /* ARGSUSED */
    663 int
    664 service_state_strands(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    665     nvlist_t *in, nvlist_t **out)
    666 {
    667 	struct strand_walk_data swd;
    668 	uint32_t rc;
    669 
    670 	if (version > TOPO_METH_SERVICE_STATE_VERSION)
    671 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    672 
    673 	if (walk_strands(mod, &swd, node, fmd_agent_cpu_isretired) == -1)
    674 		return (-1);
    675 
    676 	if (swd.done > 0)
    677 		rc = (swd.fail + swd.async > 0) ? FMD_SERVICE_STATE_DEGRADED :
    678 		    FMD_SERVICE_STATE_UNUSABLE;
    679 	else if (swd.async > 0)
    680 		rc = FMD_SERVICE_STATE_ISOLATE_PENDING;
    681 	else if (swd.fail > 0)
    682 		rc = FMD_SERVICE_STATE_OK;
    683 	else
    684 		rc = FMD_SERVICE_STATE_UNKNOWN;
    685 
    686 	return (set_retnvl(mod, out, TOPO_METH_SERVICE_STATE_RET, rc));
    687 }
    688 
    689 /* ARGSUSED */
    690 int
    691 unusable_strands(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    692     nvlist_t *in, nvlist_t **out)
    693 {
    694 	struct strand_walk_data swd;
    695 	uint32_t rc;
    696 
    697 	if (version > TOPO_METH_UNUSABLE_VERSION)
    698 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    699 
    700 	if (walk_strands(mod, &swd, node, fmd_agent_cpu_isretired) == -1)
    701 		return (-1);
    702 
    703 	rc = (swd.fail + swd.async > 0 || swd.done == 0) ? 0 : 1;
    704 
    705 	return (set_retnvl(mod, out, TOPO_METH_UNUSABLE_RET, rc));
    706 }
    707 
    708 static boolean_t
    709 is_page_fmri(nvlist_t *nvl)
    710 {
    711 	nvlist_t *hcsp;
    712 	uint64_t val;
    713 
    714 	if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC, &hcsp) == 0 &&
    715 	    (nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_OFFSET,
    716 	    &val) == 0 ||
    717 	    nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_OFFSET,
    718 	    &val) == 0 ||
    719 	    nvlist_lookup_uint64(hcsp, FM_FMRI_HC_SPECIFIC_PHYSADDR,
    720 	    &val) == 0 ||
    721 	    nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_PHYSADDR,
    722 	    &val) == 0))
    723 		return (B_TRUE);
    724 
    725 	return (B_FALSE);
    726 }
    727 
    728 /* ARGSUSED */
    729 int
    730 ntv_page_retire(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    731     nvlist_t *in, nvlist_t **out)
    732 {
    733 	fmd_agent_hdl_t *hdl;
    734 	uint32_t rc = FMD_AGENT_RETIRE_FAIL;
    735 
    736 	if (version > TOPO_METH_RETIRE_VERSION)
    737 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    738 	if (is_page_fmri(in)) {
    739 		if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) {
    740 			rc = fmd_agent_page_retire(hdl, in);
    741 			fmd_agent_close(hdl);
    742 		}
    743 	}
    744 	fmri_dprint(mod, "ntv_page_retire", rc, in);
    745 	return (set_retnvl(mod, out, TOPO_METH_RETIRE_RET, rc));
    746 }
    747 
    748 /* ARGSUSED */
    749 int
    750 ntv_page_unretire(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    751     nvlist_t *in, nvlist_t **out)
    752 {
    753 	fmd_agent_hdl_t *hdl;
    754 	uint32_t rc = FMD_AGENT_RETIRE_FAIL;
    755 
    756 	if (version > TOPO_METH_UNRETIRE_VERSION)
    757 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    758 	if (is_page_fmri(in)) {
    759 		if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) {
    760 			rc = fmd_agent_page_unretire(hdl, in);
    761 			fmd_agent_close(hdl);
    762 		}
    763 	}
    764 	fmri_dprint(mod, "ntv_page_unretire", rc, in);
    765 	return (set_retnvl(mod, out, TOPO_METH_UNRETIRE_RET, rc));
    766 }
    767 
    768 /* ARGSUSED */
    769 int
    770 ntv_page_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    771     nvlist_t *in, nvlist_t **out)
    772 {
    773 	fmd_agent_hdl_t *hdl;
    774 	uint32_t rc = FMD_SERVICE_STATE_UNKNOWN;
    775 
    776 	if (version > TOPO_METH_SERVICE_STATE_VERSION)
    777 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    778 	if (is_page_fmri(in)) {
    779 		if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) {
    780 			rc = fmd_agent_page_isretired(hdl, in);
    781 			fmd_agent_close(hdl);
    782 			if (rc == FMD_AGENT_RETIRE_DONE)
    783 				rc = FMD_SERVICE_STATE_UNUSABLE;
    784 			else if (rc == FMD_AGENT_RETIRE_FAIL)
    785 				rc = FMD_SERVICE_STATE_OK;
    786 			else if (rc == FMD_AGENT_RETIRE_ASYNC)
    787 				rc = FMD_SERVICE_STATE_ISOLATE_PENDING;
    788 		}
    789 	}
    790 
    791 	topo_mod_dprintf(mod, "ntv_page_service_state: rc = %u\n", rc);
    792 	return (set_retnvl(mod, out, TOPO_METH_SERVICE_STATE_RET, rc));
    793 }
    794 
    795 /* ARGSUSED */
    796 int
    797 ntv_page_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
    798     nvlist_t *in, nvlist_t **out)
    799 {
    800 	fmd_agent_hdl_t *hdl;
    801 	uint32_t rc = FMD_AGENT_RETIRE_FAIL;
    802 
    803 	if (version > TOPO_METH_UNUSABLE_VERSION)
    804 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
    805 	if (is_page_fmri(in)) {
    806 		if ((hdl = fmd_agent_open(FMD_AGENT_VERSION)) != NULL) {
    807 			rc = fmd_agent_page_isretired(hdl, in);
    808 			fmd_agent_close(hdl);
    809 		}
    810 	}
    811 	topo_mod_dprintf(mod, "ntv_page_unusable: rc = %u\n", rc);
    812 	return (set_retnvl(mod, out, TOPO_METH_UNUSABLE_RET,
    813 	    rc == FMD_AGENT_RETIRE_DONE ? 1 : 0));
    814 }
    815