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