xref: /illumos-gate/usr/src/lib/fm/topo/modules/i86pc/chip/chip_subr.c (revision 2b24ab6b3865caeede9eeb9db6b83e1d89dcd1ea)
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