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