xref: /illumos-gate/usr/src/lib/fm/topo/modules/i86pc/chip/chip_subr.c (revision fe3e2633be44d2f5361a7bba26abeb80fcc04fbc)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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/fm/protocol.h>
38 
39 #include "chip.h"
40 
41 /*
42  * Whinge a debug message via topo_mod_dprintf and increment the
43  * given error counter.
44  */
45 void
46 whinge(topo_mod_t *mod, int *nerr, const char *fmt, ...)
47 {
48 	va_list ap;
49 	char buf[160];
50 
51 	if (nerr != NULL)
52 		++*nerr;
53 
54 	va_start(ap, fmt);
55 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
56 	va_end(ap);
57 
58 	topo_mod_dprintf(mod, "%s", buf);
59 }
60 
61 /*
62  * Given an nvpair of a limited number of data types, extract the property
63  * name and value and add that combination to the given node in the
64  * specified property group using the corresponding topo_prop_set_* function
65  * for the data type.  Return 1 on success, otherwise 0.
66  */
67 int
68 nvprop_add(topo_mod_t *mod, nvpair_t *nvp, const char *pgname, tnode_t *node)
69 {
70 	int success = 0;
71 	int err;
72 	char *pname = nvpair_name(nvp);
73 
74 	switch (nvpair_type(nvp)) {
75 	case DATA_TYPE_BOOLEAN_VALUE: {
76 		boolean_t val;
77 
78 		if (nvpair_value_boolean_value(nvp, &val) == 0 &&
79 		    topo_prop_set_string(node, pgname, pname,
80 		    TOPO_PROP_IMMUTABLE, val ? "true" : "false", &err) == 0)
81 			success = 1;
82 		break;
83 	}
84 
85 	case DATA_TYPE_UINT32: {
86 		uint32_t val;
87 
88 		if (nvpair_value_uint32(nvp, &val) == 0 &&
89 		    topo_prop_set_uint32(node, pgname, pname,
90 		    TOPO_PROP_IMMUTABLE, val, &err) == 0)
91 			success = 1;
92 		break;
93 	}
94 
95 	case DATA_TYPE_UINT64: {
96 		uint64_t val;
97 
98 		if (nvpair_value_uint64(nvp, &val) == 0 &&
99 		    topo_prop_set_uint64(node, pgname, pname,
100 		    TOPO_PROP_IMMUTABLE, val, &err) == 0)
101 			success = 1;
102 		break;
103 	}
104 
105 	case DATA_TYPE_UINT32_ARRAY: {
106 		uint32_t *arrp;
107 		uint_t nelem;
108 
109 		if (nvpair_value_uint32_array(nvp, &arrp, &nelem) == 0 &&
110 		    nelem > 0 && topo_prop_set_uint32_array(node, pgname, pname,
111 		    TOPO_PROP_IMMUTABLE, arrp, nelem, &err) == 0)
112 			success = 1;
113 		break;
114 	}
115 
116 	case DATA_TYPE_STRING: {
117 		char *str;
118 
119 		if (nvpair_value_string(nvp, &str) == 0 &&
120 		    topo_prop_set_string(node, pgname, pname,
121 		    TOPO_PROP_IMMUTABLE, str, &err) == 0)
122 			success = 1;
123 		break;
124 	}
125 
126 	default:
127 		whinge(mod, &err, "nvprop_add: Can't handle type %d for "
128 		    "'%s' in property group %s of %s node\n",
129 		    nvpair_type(nvp), pname, pgname, topo_node_name(node));
130 		break;
131 	}
132 
133 	return (success ? 0 : 1);
134 }
135 
136 /*
137  * Lookup string data named pname in the given kstat_t and add that
138  * as property named pname in the given property group pgname on the indicated
139  * topo node.  Fill pvalp with a pointer to the string value, valid until
140  * kstat_close is called (or the given kstat_t is otherwise invalidated).
141  */
142 int
143 add_kstat_strprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp,
144     const char *pgname, const char *pname, const char **pvalp)
145 {
146 	const char *pval;
147 	kstat_named_t *k;
148 	int err = 0;
149 
150 	if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL)
151 		return (-1);
152 	pval = k->value.str.addr.ptr;
153 
154 	if (topo_prop_set_string(node, pgname, pname,
155 	    TOPO_PROP_IMMUTABLE, pval, &err) == 0) {
156 		if (pvalp)
157 			*pvalp = pval;
158 		return (0);
159 	} else {
160 		whinge(mod, &err, "chip_strprop: failed to add '%s'\n",
161 		    pname);
162 		return (-1);
163 	}
164 }
165 
166 /*
167  * Lookup an int32 item named pname in the given kstat_t and add that
168  * as property named pname in the given property group pgname on the indicated
169  * topo node.  Fill pvalp with the property value.
170  */
171 int
172 add_kstat_longprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp,
173     const char *pgname, const char *pname, int32_t *pvalp)
174 {
175 	kstat_named_t *k;
176 	int32_t pval;
177 	int err;
178 
179 	if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL)
180 		return (-1);
181 	pval = k->value.l;
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, "chip_longprop: failed to add '%s'\n",
190 		    pname);
191 		return (-1);
192 	}
193 }
194 
195 /*
196  * In a given kstat_t 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_kstat_longprops(topo_mod_t *mod, tnode_t *node, kstat_t *ksp,
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_kstat_longprop(mod, node, ksp, 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 /*
264  * Construct a mem scheme FMRI for the given unum string; the caller must
265  * free the allocated nvlist with nvlist_free().
266  */
267 nvlist_t *
268 mem_fmri_create(topo_mod_t *mod, const char *unum)
269 {
270 	nvlist_t *asru;
271 
272 	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
273 		return (NULL);
274 
275 	if (nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0 ||
276 	    nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION) != 0 ||
277 	    nvlist_add_string(asru, FM_FMRI_MEM_UNUM, unum) != 0) {
278 		nvlist_free(asru);
279 		return (NULL);
280 	}
281 
282 	return (asru);
283 }
284 
285 /*
286  * Registered method for asru computation for rank nodes.  The 'node'
287  * argument identifies the node for which we seek an asru.  The 'in'
288  * argument is used to select which asru we will return, as follows:
289  *
290  * - the node name must be "dimm" or "rank"
291  * - if 'in' is NULL then return any statically defined asru for this node
292  * - if 'in' is an "hc" scheme fmri then we construct a "mem" scheme asru
293  *   with unum being the hc path to the dimm or rank (this method is called
294  *   as part of dynamic asru computation for rank nodes only, but
295  *   it is also called directly to construct a "mem" scheme asru for a dimm
296  *   node)
297  * - if 'in' in addition includes an hc-specific member which specifies
298  *   asru-physaddr or asru-offset then these are includes in the "mem" scheme
299  *   asru as additional members physaddr and offset
300  */
301 int
302 mem_asru_create(topo_mod_t *mod, nvlist_t *fmri, nvlist_t **asru)
303 {
304 	int incl_pa = 0, incl_offset = 0;
305 	nvlist_t *hcsp, *ap;
306 	char *unum, *scheme;
307 	uint64_t pa, offset;
308 	int err = 0;
309 
310 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0 ||
311 	    strcmp(scheme, FM_FMRI_SCHEME_HC) != 0)
312 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
313 
314 	if (nvlist_lookup_nvlist(fmri, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) {
315 		if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_PHYSADDR,
316 		    &pa) == 0)
317 			incl_pa = 1;
318 
319 		if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_OFFSET,
320 		    &offset) == 0)
321 			incl_offset = 1;
322 	}
323 
324 	/* use 'fmri' to obtain resource path;  could use node resource */
325 	if (topo_mod_nvl2str(mod, fmri, &unum) < 0)
326 		return (-1);  /* mod errno set */
327 
328 	ap = mem_fmri_create(mod, unum);
329 	topo_mod_strfree(mod, unum);
330 	if (ap == NULL)
331 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
332 
333 	if (incl_pa)
334 		err += nvlist_add_uint64(ap, FM_FMRI_MEM_PHYSADDR, pa) != 0;
335 	if (incl_offset)
336 		err += nvlist_add_uint64(ap, FM_FMRI_MEM_OFFSET, offset) != 0;
337 
338 	if (err != 0) {
339 		nvlist_free(ap);
340 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
341 	}
342 
343 	*asru = ap;
344 
345 	return (0);
346 }
347 
348 /*ARGSUSED*/
349 int
350 mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version,
351     nvlist_t *in, nvlist_t **out)
352 {
353 	nvlist_t *asru;
354 	nvlist_t *args, *pargs;
355 	int err;
356 
357 	if (strcmp(topo_node_name(node), RANK_NODE_NAME) != 0 &&
358 	    strcmp(topo_node_name(node), DIMM_NODE_NAME) != 0 &&
359 	    strcmp(topo_node_name(node), CS_NODE_NAME) != 0)
360 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
361 
362 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0)
363 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
364 
365 	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs)) != 0) {
366 		if (err == ENOENT) {
367 			if (topo_mod_nvdup(mod, args, &asru) < 0)
368 				return (topo_mod_seterrno(mod, EMOD_NOMEM));
369 		} else {
370 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
371 		}
372 	} else if (mem_asru_create(mod, pargs, &asru) != 0) {
373 		return (-1); /* mod errno already set */
374 	}
375 
376 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
377 		nvlist_free(asru);
378 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
379 	}
380 
381 	err = nvlist_add_string(*out, TOPO_PROP_VAL_NAME, TOPO_PROP_ASRU);
382 	err |= nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_FMRI);
383 	err |= nvlist_add_nvlist(*out, TOPO_PROP_VAL_VAL, asru);
384 	if (err != 0) {
385 		nvlist_free(asru);
386 		nvlist_free(*out);
387 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
388 	}
389 
390 	nvlist_free(asru);
391 
392 	return (0);
393 }
394 
395 /*
396  * If we're getting called then the question of whether this dimm is plugged
397  * in has already been answered.  What we don't know for sure is whether it's
398  * the same dimm or a different one plugged in the same slot.  To check, we
399  * try and compare the serial numbers on the dimm in the current topology with
400  * the serial num from the unum fmri that got passed into this function as the
401  * argument.
402  *
403  * In the event we encounter problems comparing serials or if a comparison isn't
404  * possible, we err on the side of caution and set is_present to TRUE.
405  */
406 /* ARGSUSED */
407 int
408 rank_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
409     nvlist_t *in, nvlist_t **out)
410 {
411 	tnode_t *dimmnode;
412 	int err, is_present = 1;
413 	nvlist_t *unum;
414 	char *curr_serial, *old_serial = NULL;
415 
416 	/*
417 	 * If a serial number for the dimm was available at the time of the
418 	 * fault, it will have been added as a string to the unum nvlist
419 	 */
420 	unum = in;
421 	if (nvlist_lookup_string(unum, FM_FMRI_HC_SERIAL_ID, &old_serial) != 0)
422 		goto done;
423 
424 	/*
425 	 * If the current serial number is available for the DIMM that this rank
426 	 * belongs to, it will be accessible as a property on the parent (dimm)
427 	 * node.
428 	 */
429 	dimmnode = topo_node_parent(node);
430 	if (topo_prop_get_string(dimmnode, TOPO_PGROUP_PROTOCOL,
431 	    FM_FMRI_HC_SERIAL_ID, &curr_serial, &err) != 0) {
432 		if (err != ETOPO_PROP_NOENT) {
433 			whinge(mod, &err, "rank_fmri_present: Unexpected error "
434 			    "retrieving serial from node");
435 			return (topo_mod_seterrno(mod,  EMOD_NVL_INVAL));
436 		} else
437 			goto done;
438 	}
439 
440 	if (strcmp(old_serial, curr_serial) != 0)
441 		is_present = 0;
442 
443 	topo_mod_strfree(mod, curr_serial);
444 done:
445 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
446 		whinge(mod, &err,
447 		    "rank_fmri_present: failed to allocate nvlist!");
448 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
449 	}
450 
451 	if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, is_present) != 0) {
452 		nvlist_free(*out);
453 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
454 	}
455 
456 	return (0);
457 }
458 
459 /*
460  * If we're getting called then the question of whether this dimm is plugged
461  * in has already been answered.  What we don't know for sure is whether it's
462  * the same dimm or a different one plugged in the same slot.  To check, we
463  * try and compare the serial numbers on the dimm in the current topology with
464  * the serial num from the unum fmri that got passed into this function as the
465  * argument.
466  *
467  * In the event we encounter problems comparing serials or if a comparison isn't
468  * possible, we err on the side of caution and set is_present to TRUE.
469  */
470 /* ARGSUSED */
471 int
472 rank_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version,
473     nvlist_t *in, nvlist_t **out)
474 {
475 	tnode_t *dimmnode;
476 	int err, rval = FMD_OBJ_STATE_UNKNOWN;
477 	nvlist_t *unum;
478 	char *curr_serial, *old_serial = NULL;
479 
480 	/*
481 	 * If a serial number for the dimm was available at the time of the
482 	 * fault, it will have been added as a string to the unum nvlist
483 	 */
484 	unum = in;
485 	if (nvlist_lookup_string(unum, FM_FMRI_HC_SERIAL_ID, &old_serial) != 0)
486 		goto done;
487 
488 	/*
489 	 * If the current serial number is available for the DIMM that this rank
490 	 * belongs to, it will be accessible as a property on the parent (dimm)
491 	 * node.
492 	 */
493 	dimmnode = topo_node_parent(node);
494 	if (topo_prop_get_string(dimmnode, TOPO_PGROUP_PROTOCOL,
495 	    FM_FMRI_HC_SERIAL_ID, &curr_serial, &err) != 0) {
496 		if (err != ETOPO_PROP_NOENT) {
497 			whinge(mod, &err, "rank_fmri_present: Unexpected error "
498 			    "retrieving serial from node");
499 			return (topo_mod_seterrno(mod,  EMOD_NVL_INVAL));
500 		} else
501 			goto done;
502 	}
503 
504 	if (strcmp(old_serial, curr_serial) != 0)
505 		rval = FMD_OBJ_STATE_REPLACED;
506 	else
507 		rval = FMD_OBJ_STATE_STILL_PRESENT;
508 
509 	topo_mod_strfree(mod, curr_serial);
510 done:
511 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
512 		whinge(mod, &err,
513 		    "rank_fmri_present: failed to allocate nvlist!");
514 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
515 	}
516 
517 	if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) {
518 		nvlist_free(*out);
519 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
520 	}
521 
522 	return (0);
523 }
524