xref: /illumos-gate/usr/src/lib/fm/topo/modules/i86pc/chip/chip_subr.c (revision ca9327a6de44d69ddab3668cc1e143ce781387a3)
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 <sys/fm/protocol.h>
37 
38 #include "chip.h"
39 
40 /*
41  * Whinge a debug message via topo_mod_dprintf and increment the
42  * given error counter.
43  */
44 void
45 whinge(topo_mod_t *mod, int *nerr, const char *fmt, ...)
46 {
47 	va_list ap;
48 	char buf[160];
49 
50 	if (nerr != NULL)
51 		++*nerr;
52 
53 	va_start(ap, fmt);
54 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
55 	va_end(ap);
56 
57 	topo_mod_dprintf(mod, "%s", buf);
58 }
59 
60 /*
61  * Given an nvpair of a limited number of data types, extract the property
62  * name and value and add that combination to the given node in the
63  * specified property group using the corresponding topo_prop_set_* function
64  * for the data type.  Return 1 on success, otherwise 0.
65  */
66 int
67 nvprop_add(topo_mod_t *mod, nvpair_t *nvp, const char *pgname, tnode_t *node)
68 {
69 	int success = 0;
70 	int err;
71 	char *pname = nvpair_name(nvp);
72 
73 	switch (nvpair_type(nvp)) {
74 	case DATA_TYPE_BOOLEAN_VALUE: {
75 		boolean_t val;
76 
77 		if (nvpair_value_boolean_value(nvp, &val) == 0 &&
78 		    topo_prop_set_string(node, pgname, pname,
79 		    TOPO_PROP_IMMUTABLE, val ? "true" : "false", &err) == 0)
80 			success = 1;
81 		break;
82 	}
83 
84 	case DATA_TYPE_UINT32: {
85 		uint32_t val;
86 
87 		if (nvpair_value_uint32(nvp, &val) == 0 &&
88 		    topo_prop_set_uint32(node, pgname, pname,
89 		    TOPO_PROP_IMMUTABLE, val, &err) == 0)
90 			success = 1;
91 		break;
92 	}
93 
94 	case DATA_TYPE_UINT64: {
95 		uint64_t val;
96 
97 		if (nvpair_value_uint64(nvp, &val) == 0 &&
98 		    topo_prop_set_uint64(node, pgname, pname,
99 		    TOPO_PROP_IMMUTABLE, val, &err) == 0)
100 			success = 1;
101 		break;
102 	}
103 
104 	case DATA_TYPE_UINT32_ARRAY: {
105 		uint32_t *arrp;
106 		uint_t nelem;
107 
108 		if (nvpair_value_uint32_array(nvp, &arrp, &nelem) == 0 &&
109 		    nelem > 0 && topo_prop_set_uint32_array(node, pgname, pname,
110 		    TOPO_PROP_IMMUTABLE, arrp, nelem, &err) == 0)
111 			success = 1;
112 		break;
113 	}
114 
115 	case DATA_TYPE_STRING: {
116 		char *str;
117 
118 		if (nvpair_value_string(nvp, &str) == 0 &&
119 		    topo_prop_set_string(node, pgname, pname,
120 		    TOPO_PROP_IMMUTABLE, str, &err) == 0)
121 			success = 1;
122 		break;
123 	}
124 
125 	default:
126 		whinge(mod, &err, "nvprop_add: Can't handle type %d for "
127 		    "'%s' in property group %s of %s node\n",
128 		    nvpair_type(nvp), pname, pgname, topo_node_name(node));
129 		break;
130 	}
131 
132 	return (success ? 0 : 1);
133 }
134 
135 /*
136  * Lookup string data named pname in the given kstat_t and add that
137  * as property named pname in the given property group pgname on the indicated
138  * topo node.  Fill pvalp with a pointer to the string value, valid until
139  * kstat_close is called (or the given kstat_t is otherwise invalidated).
140  */
141 int
142 add_kstat_strprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp,
143     const char *pgname, const char *pname, const char **pvalp)
144 {
145 	const char *pval;
146 	kstat_named_t *k;
147 	int err = 0;
148 
149 	if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL)
150 		return (-1);
151 	pval = k->value.str.addr.ptr;
152 
153 	if (topo_prop_set_string(node, pgname, pname,
154 	    TOPO_PROP_IMMUTABLE, pval, &err) == 0) {
155 		if (pvalp)
156 			*pvalp = pval;
157 		return (0);
158 	} else {
159 		whinge(mod, &err, "chip_strprop: failed to add '%s'\n",
160 		    pname);
161 		return (-1);
162 	}
163 }
164 
165 /*
166  * Lookup an int32 item named pname in the given kstat_t and add that
167  * as property named pname in the given property group pgname on the indicated
168  * topo node.  Fill pvalp with the property value.
169  */
170 int
171 add_kstat_longprop(topo_mod_t *mod, tnode_t *node, kstat_t *ksp,
172     const char *pgname, const char *pname, int32_t *pvalp)
173 {
174 	kstat_named_t *k;
175 	int32_t pval;
176 	int err;
177 
178 	if ((k = kstat_data_lookup(ksp, (char *)pname)) == NULL)
179 		return (-1);
180 	pval = k->value.l;
181 
182 	if (topo_prop_set_int32(node, pgname, pname,
183 	    TOPO_PROP_IMMUTABLE, pval, &err) == 0) {
184 		if (pvalp)
185 			*pvalp = pval;
186 		return (0);
187 	} else {
188 		whinge(mod, &err, "chip_longprop: failed to add '%s'\n",
189 		    pname);
190 		return (-1);
191 	}
192 }
193 
194 /*
195  * In a given kstat_t lookup a variable number of int32 properties named in
196  * const char * varargs and each each in the given property group on the
197  * node.  Fill an array of the retrieved values.
198  */
199 int
200 add_kstat_longprops(topo_mod_t *mod, tnode_t *node, kstat_t *ksp,
201     const char *pgname, int32_t *pvalap, ...)
202 {
203 	const char *pname;
204 	va_list ap;
205 	int nerr = 0;
206 
207 	va_start(ap, pvalap);
208 	while ((pname = va_arg(ap, const char *)) != NULL) {
209 		if (add_kstat_longprop(mod, node, ksp, pgname, pname,
210 		    pvalap) != 0)
211 			nerr++;		/* have whinged elsewhere */
212 
213 		if (pvalap != NULL)
214 			++pvalap;
215 	}
216 	va_end(ap);
217 
218 	return (nerr == 0 ? 0 : -1);
219 }
220 
221 /*
222  * Construct an hc scheme resource FMRI for a node named name with
223  * instance number inst, parented by the given parent node pnode.
224  */
225 int
226 mkrsrc(topo_mod_t *mod, tnode_t *pnode, const char *name, int inst,
227     nvlist_t *auth, nvlist_t **nvl)
228 {
229 	*nvl = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, name,
230 	    inst, NULL, auth, NULL, NULL, NULL);
231 	return (nvl != NULL ? 0 : -1);	/* caller must free nvlist */
232 }
233 
234 /*
235  * Construct a cpu scheme FMRI with the given data; the caller must free
236  * the allocated nvlist with nvlist_free().
237  */
238 nvlist_t *
239 cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *s, uint8_t cpumask)
240 {
241 	int err;
242 	nvlist_t *asru;
243 
244 	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
245 		return (NULL);
246 
247 	err = nvlist_add_uint8(asru, FM_VERSION, FM_CPU_SCHEME_VERSION);
248 	err |= nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
249 	err |= nvlist_add_uint32(asru, FM_FMRI_CPU_ID, cpuid);
250 	err |= nvlist_add_uint8(asru, FM_FMRI_CPU_MASK, cpumask);
251 	if (s != NULL)
252 		err |= nvlist_add_string(asru, FM_FMRI_CPU_SERIAL_ID, s);
253 	if (err != 0) {
254 		nvlist_free(asru);
255 		(void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
256 		return (NULL);
257 	}
258 
259 	return (asru);
260 }
261 
262 /*
263  * Construct a mem scheme FMRI for the given unum string; the caller must
264  * free the allocated nvlist with nvlist_free().
265  */
266 nvlist_t *
267 mem_fmri_create(topo_mod_t *mod, const char *unum)
268 {
269 	nvlist_t *asru;
270 
271 	if (topo_mod_nvalloc(mod, &asru, NV_UNIQUE_NAME) != 0)
272 		return (NULL);
273 
274 	if (nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_MEM) != 0 ||
275 	    nvlist_add_uint8(asru, FM_VERSION, FM_MEM_SCHEME_VERSION) != 0 ||
276 	    nvlist_add_string(asru, FM_FMRI_MEM_UNUM, unum) != 0) {
277 		nvlist_free(asru);
278 		return (NULL);
279 	}
280 
281 	return (asru);
282 }
283 
284 /*
285  * Registered method for asru computation for rank nodes.  The 'node'
286  * argument identifies the node for which we seek an asru.  The 'in'
287  * argument is used to select which asru we will return, as follows:
288  *
289  * - the node name must be "dimm" or "rank"
290  * - if 'in' is NULL then return any statically defined asru for this node
291  * - if 'in' is an "hc" scheme fmri then we construct a "mem" scheme asru
292  *   with unum being the hc path to the dimm or rank (this method is called
293  *   as part of dynamic asru computation for rank nodes only, but
294  *   it is also called directly to construct a "mem" scheme asru for a dimm
295  *   node)
296  * - if 'in' in addition includes an hc-specific member which specifies
297  *   asru-physaddr or asru-offset then these are includes in the "mem" scheme
298  *   asru as additional members physaddr and offset
299  */
300 int
301 mem_asru_create(topo_mod_t *mod, nvlist_t *fmri, nvlist_t **asru)
302 {
303 	int incl_pa = 0, incl_offset = 0;
304 	nvlist_t *hcsp, *ap;
305 	char *unum, *scheme;
306 	uint64_t pa, offset;
307 	int err = 0;
308 
309 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0 ||
310 	    strcmp(scheme, FM_FMRI_SCHEME_HC) != 0)
311 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
312 
313 	if (nvlist_lookup_nvlist(fmri, FM_FMRI_HC_SPECIFIC, &hcsp) == 0) {
314 		if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_PHYSADDR,
315 		    &pa) == 0)
316 			incl_pa = 1;
317 
318 		if (nvlist_lookup_uint64(hcsp, "asru-"FM_FMRI_MEM_OFFSET,
319 		    &offset) == 0)
320 			incl_offset = 1;
321 	}
322 
323 	/* use 'fmri' to obtain resource path;  could use node resource */
324 	if (topo_mod_nvl2str(mod, fmri, &unum) < 0)
325 		return (-1);  /* mod errno set */
326 
327 	ap = mem_fmri_create(mod, unum);
328 	topo_mod_strfree(mod, unum);
329 	if (ap == NULL)
330 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
331 
332 	if (incl_pa)
333 		err += nvlist_add_uint64(ap, FM_FMRI_MEM_PHYSADDR, pa) != 0;
334 	if (incl_offset)
335 		err += nvlist_add_uint64(ap, FM_FMRI_MEM_OFFSET, offset) != 0;
336 
337 	if (err != 0) {
338 		nvlist_free(ap);
339 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
340 	}
341 
342 	*asru = ap;
343 
344 	return (0);
345 }
346 
347 /*ARGSUSED*/
348 int
349 mem_asru_compute(topo_mod_t *mod, tnode_t *node, topo_version_t version,
350     nvlist_t *in, nvlist_t **out)
351 {
352 	nvlist_t *asru;
353 	nvlist_t *args, *pargs;
354 	int err;
355 
356 	if (strcmp(topo_node_name(node), RANK_NODE_NAME) != 0 &&
357 	    strcmp(topo_node_name(node), DIMM_NODE_NAME) != 0 &&
358 	    strcmp(topo_node_name(node), CS_NODE_NAME) != 0)
359 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
360 
361 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0)
362 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
363 
364 	if ((err = nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs)) != 0) {
365 		if (err == ENOENT) {
366 			if (topo_mod_nvdup(mod, args, &asru) < 0)
367 				return (topo_mod_seterrno(mod, EMOD_NOMEM));
368 		} else {
369 			return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
370 		}
371 	} else if (mem_asru_create(mod, pargs, &asru) != 0) {
372 		return (-1); /* mod errno already set */
373 	}
374 
375 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
376 		nvlist_free(asru);
377 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
378 	}
379 
380 	err = nvlist_add_string(*out, TOPO_PROP_VAL_NAME, TOPO_PROP_ASRU);
381 	err |= nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_FMRI);
382 	err |= nvlist_add_nvlist(*out, TOPO_PROP_VAL_VAL, asru);
383 	if (err != 0) {
384 		nvlist_free(asru);
385 		nvlist_free(*out);
386 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
387 	}
388 
389 	nvlist_free(asru);
390 
391 	return (0);
392 }
393 
394 /*
395  * If we're getting called then the question of whether this dimm is plugged
396  * in has already been answered.  What we don't know for sure is whether it's
397  * the same dimm or a different one plugged in the same slot.  To check, we
398  * try and compare the serial numbers on the dimm in the current topology with
399  * the serial num from the unum fmri that got passed into this function as the
400  * argument.
401  *
402  * In the event we encounter problems comparing serials or if a comparison isn't
403  * possible, we err on the side of caution and set is_present to TRUE.
404  */
405 /* ARGSUSED */
406 int
407 rank_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
408     nvlist_t *in, nvlist_t **out)
409 {
410 	tnode_t *dimmnode;
411 	int err, is_present = 1;
412 	nvlist_t *unum;
413 	char *curr_serial, *old_serial = NULL;
414 
415 	/*
416 	 * If a serial number for the dimm was available at the time of the
417 	 * fault, it will have been added as a string to the unum nvlist
418 	 */
419 	unum = in;
420 	if (nvlist_lookup_string(unum, FM_FMRI_HC_SERIAL_ID, &old_serial) != 0)
421 		goto done;
422 
423 	/*
424 	 * If the current serial number is available for the DIMM that this rank
425 	 * belongs to, it will be accessible as a property on the parent (dimm)
426 	 * node.
427 	 */
428 	dimmnode = topo_node_parent(node);
429 	if (topo_prop_get_string(dimmnode, TOPO_PGROUP_PROTOCOL,
430 	    FM_FMRI_HC_SERIAL_ID, &curr_serial, &err) != 0) {
431 		if (err != ETOPO_PROP_NOENT) {
432 			whinge(mod, &err, "rank_fmri_present: Unexpected error "
433 			    "retrieving serial from node");
434 			return (topo_mod_seterrno(mod,  EMOD_NVL_INVAL));
435 		} else
436 			goto done;
437 	}
438 
439 	if (strcmp(old_serial, curr_serial) != 0)
440 		is_present = 0;
441 
442 	topo_mod_strfree(mod, curr_serial);
443 done:
444 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) {
445 		whinge(mod, &err,
446 		    "rank_fmri_present: failed to allocate nvlist!");
447 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
448 	}
449 
450 	if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, is_present) != 0) {
451 		nvlist_free(*out);
452 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
453 	}
454 
455 	return (0);
456 }
457