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 2007 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 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <libnvpair.h>
35 #include <sys/types.h>
36 #include <fm/topo_mod.h>
37 
38 #define	BUFSZ	128
39 
40 static char *
41 get_fmtstr(topo_mod_t *mod, nvlist_t *in, int *err)
42 {
43 	char *fmtstr;
44 	nvlist_t *args;
45 
46 	topo_mod_dprintf(mod, "get_fmtstr() called\n");
47 
48 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) {
49 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
50 		    strerror(errno));
51 		*err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
52 		return (NULL);
53 	}
54 	if (nvlist_lookup_string(args, "format", &fmtstr) != 0) {
55 		topo_mod_dprintf(mod, "Failed to lookup 'format' arg (%s)\n",
56 		    strerror(errno));
57 		*err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
58 		nvlist_free(args);
59 		return (NULL);
60 	}
61 	return (fmtstr);
62 }
63 
64 static int
65 store_prop_val(topo_mod_t *mod, char *buf, char *propname, nvlist_t **out)
66 {
67 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) {
68 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
69 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
70 	}
71 	if (nvlist_add_string(*out, TOPO_PROP_VAL_NAME, propname) != 0) {
72 		topo_mod_dprintf(mod, "Failed to set '%s'\n",
73 		    TOPO_PROP_VAL_NAME);
74 		nvlist_free(*out);
75 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
76 	}
77 	if (nvlist_add_uint32(*out, TOPO_PROP_VAL_TYPE, TOPO_TYPE_STRING)
78 	    != 0) {
79 		topo_mod_dprintf(mod, "Failed to set '%s'\n",
80 		    TOPO_PROP_VAL_TYPE);
81 		nvlist_free(*out);
82 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
83 	}
84 	if (nvlist_add_string(*out, TOPO_PROP_VAL_VAL, buf) != 0) {
85 		topo_mod_dprintf(mod, "Failed to set '%s'\n",
86 		    TOPO_PROP_VAL_VAL);
87 		nvlist_free(*out);
88 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
89 	}
90 	return (0);
91 }
92 
93 /*
94  * This is a somewhat generic property method for labelling the dimm slots on
95  * uni-socket x86/x64 platforms.  This method assumes a direct linear
96  * correlation between the dimm topo node instance number and the dimm slot
97  * label number.  It takes the following two arguments:
98  *
99  * format:	a string containing a printf-like format with a single %d token
100  *              which this method computes
101  *
102  *              i.e.: DIMM %d
103  *
104  * offset:      a numeric offset that we'll number the dimms from.  This is to
105  *              allow for the fact that some systems number the dimm slots
106  *              from zero and others start from one (like the Ultra 20)
107  */
108 /* ARGSUSED */
109 int
110 simple_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
111     nvlist_t *in, nvlist_t **out)
112 {
113 	char *fmtstr, buf[BUFSZ];
114 	int err;
115 	uint32_t offset;
116 	nvlist_t *args;
117 
118 	topo_mod_dprintf(mod, "simple_dimm_label() called\n");
119 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) {
120 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
121 		    strerror(errno));
122 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
123 		return (err);
124 	}
125 	if (nvlist_lookup_uint32(args, "offset", &offset) != 0) {
126 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
127 		    strerror(errno));
128 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
129 		nvlist_free(args);
130 		return (err);
131 	}
132 
133 	if ((fmtstr = get_fmtstr(mod, in, &err)) == NULL) {
134 		topo_mod_dprintf(mod, "Failed to retrieve format arg\n");
135 		nvlist_free(args);
136 		return (err);
137 	}
138 
139 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
140 	(void) snprintf(buf, BUFSZ, fmtstr,
141 	    (topo_node_instance(node) + offset));
142 
143 	if ((err = store_prop_val(mod, buf, "label", out)) != 0) {
144 		topo_mod_dprintf(mod, "Failed to set label\n");
145 		nvlist_free(args);
146 		return (err);
147 	}
148 
149 	return (0);
150 }
151 
152 
153 /*
154  * This is a somewhat generic property method for labelling the dimm slots on
155  * multi-socket x86/x64 platforms.  It takes the following two arguments:
156  *
157  * format:	a string containing a printf-like format with a two %d tokens
158  *              for the cpu and dimm slot label numbers, which this method
159  *              computes
160  *
161  *              i.e.: CPU %d DIMM %d
162  *
163  * offset:      a numeric offset that we'll number the dimms from.  This is to
164  *              allow for the fact that some systems number the dimm slots
165  *              from zero while others may start from one
166  *
167  * order:	"reverse" or "forward" - sets the direction of the correlation
168  *              between dimm topo node instance number and DIMM slot number
169  *
170  * dimms_per_chip:  the number of DIMM slots per chip
171  */
172 /* ARGSUSED */
173 int
174 simple_dimm_label_mp(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
175     nvlist_t *in, nvlist_t **out)
176 {
177 	char *fmtstr, *order, buf[BUFSZ];
178 	tnode_t *chip;
179 	int err;
180 	uint32_t offset, dimms_per_chip;
181 	nvlist_t *args;
182 
183 	topo_mod_dprintf(mod, "simple_dimm_label_mp() called\n");
184 
185 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) {
186 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
187 		    strerror(errno));
188 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
189 		return (err);
190 	}
191 	if (nvlist_lookup_uint32(args, "offset", &offset) != 0) {
192 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
193 		    strerror(errno));
194 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
195 		nvlist_free(args);
196 		return (err);
197 	}
198 	if (nvlist_lookup_uint32(args, "dimms_per_chip", &dimms_per_chip)
199 	    != 0) {
200 		topo_mod_dprintf(mod, "Failed to lookup 'dimms_per_chip' arg "
201 		    "(%s)\n", strerror(errno));
202 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
203 		nvlist_free(args);
204 		return (err);
205 	}
206 	if (nvlist_lookup_string(args, "order", &order) != 0) {
207 		topo_mod_dprintf(mod, "Failed to lookup 'order' arg (%s)\n",
208 		    strerror(errno));
209 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
210 		nvlist_free(args);
211 		return (err);
212 	}
213 	if ((fmtstr = get_fmtstr(mod, in, &err)) == NULL) {
214 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
215 		topo_mod_free(mod, order, BUFSZ);
216 		nvlist_free(args);
217 		return (err);
218 	}
219 
220 	chip = topo_node_parent(topo_node_parent(node));
221 
222 	if (strcasecmp(order, "forward") == 0)
223 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
224 		(void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip),
225 		    (topo_node_instance(node) + offset));
226 	else if (strcasecmp(order, "reverse") == 0)
227 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
228 		(void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip),
229 		    (((topo_node_instance(chip) + 1) * dimms_per_chip)
230 		    - (topo_node_instance(node)) - 1 + offset));
231 	else {
232 		topo_mod_dprintf(mod, "Invalid value for order arg\n");
233 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
234 		topo_mod_free(mod, order, BUFSZ);
235 		nvlist_free(args);
236 		return (err);
237 	}
238 
239 	if ((err = store_prop_val(mod, buf, "label", out)) != 0) {
240 		topo_mod_dprintf(mod, "Failed to set label\n");
241 		topo_mod_free(mod, order, BUFSZ);
242 		nvlist_free(args);
243 		return (err);
244 	}
245 
246 	return (0);
247 }
248 
249 /*
250  * This method assumes a correspondence between the dimm topo node instance
251  * number and the dimm slot label number, but unlike simple_chip_label_mp, the
252  * slot numbers aren't reused between CPU's.  This method assumes there
253  * are 4 DIMM slots per chip.  It takes the following two arguments:
254  *
255  * format:	a string containing a printf-like format with a single %d token
256  *              which this method computes
257  *
258  *              i.e.: DIMM %d
259  *
260  * offset:      a numeric offset that we'll number the dimms from.  This is to
261  *              allow for the fact that some systems number the dimm slots
262  *              from zero and others may start from one
263  *
264  * order:	"reverse" or "forward" - sets the direction of the correlation
265  *              between dimm topo node instance number and DIMM slot number
266  */
267 /* ARGSUSED */
268 int
269 seq_dimm_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
270     nvlist_t *in, nvlist_t **out)
271 {
272 	char *fmtstr, *order, buf[BUFSZ];
273 	int err;
274 	uint32_t offset;
275 	nvlist_t *args;
276 	tnode_t *chip;
277 
278 	topo_mod_dprintf(mod, "seq_dimm_label() called\n");
279 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) {
280 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
281 		    strerror(errno));
282 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
283 		return (err);
284 	}
285 	if (nvlist_lookup_uint32(args, "offset", &offset) != 0) {
286 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
287 		    strerror(errno));
288 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
289 		nvlist_free(args);
290 		return (err);
291 	}
292 	if (nvlist_lookup_string(args, "order", &order) != 0) {
293 		topo_mod_dprintf(mod, "Failed to lookup 'order' arg (%s)\n",
294 		    strerror(errno));
295 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
296 		nvlist_free(args);
297 		return (err);
298 	}
299 
300 	if ((fmtstr = get_fmtstr(mod, in, &err)) == NULL) {
301 		topo_mod_dprintf(mod, "Failed to retrieve 'fmtstr' arg\n");
302 		topo_mod_free(mod, order, BUFSZ);
303 		nvlist_free(args);
304 		return (err);
305 	}
306 
307 	chip = topo_node_parent(topo_node_parent(node));
308 
309 	if (strcasecmp(order, "forward") == 0)
310 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
311 		(void) snprintf(buf, BUFSZ, fmtstr, ((topo_node_instance(node))
312 		    + (topo_node_instance(chip) * 4) + offset));
313 	else if (strcasecmp(order, "reverse") == 0)
314 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
315 		(void) snprintf(buf, BUFSZ, fmtstr,
316 		    (((topo_node_instance(chip) + 1) * 4)
317 		    - (topo_node_instance(node)) - 1 + offset));
318 	else {
319 		topo_mod_dprintf(mod, "Invalid value for order arg\n");
320 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
321 		topo_mod_free(mod, order, BUFSZ);
322 		nvlist_free(args);
323 		return (err);
324 	}
325 
326 	if ((err = store_prop_val(mod, buf, "label", out)) != 0) {
327 		topo_mod_dprintf(mod, "Failed to set label\n");
328 		topo_mod_free(mod, order, BUFSZ);
329 		nvlist_free(args);
330 		return (err);
331 	}
332 
333 	return (0);
334 }
335 
336 
337 /*
338  * This is a somewhat generic property method for labelling the CPU sockets on
339  * x86/x64 platforms.  This method assumes a correspondence between
340  * the chip topo node instance number and the CPU socket label number.  It takes
341  * the following two arguments:
342  *
343  * format:	a string containing a printf-like format with a single %d token
344  *              which this method computes
345  *
346  *              i.e.: CPU %d
347  *
348  * offset:      a numeric offset that we'll number the CPU's from.  This is to
349  *              allow for the fact that some systems number the CPU sockets
350  *              from zero and others start from one (like the X4X00-M2 systems)
351  */
352 /* ARGSUSED */
353 int
354 simple_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
355     nvlist_t *in, nvlist_t **out)
356 {
357 	char *fmtstr, buf[BUFSZ];
358 	int err;
359 	uint32_t offset;
360 	nvlist_t *args;
361 
362 	topo_mod_dprintf(mod, "simple_chip_label() called\n");
363 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) {
364 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
365 		    strerror(errno));
366 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
367 		return (err);
368 	}
369 	if (nvlist_lookup_uint32(args, "offset", &offset) != 0) {
370 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
371 		    strerror(errno));
372 		err = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
373 		nvlist_free(args);
374 		return (err);
375 	}
376 
377 	if ((fmtstr = get_fmtstr(mod, in, &err)) == NULL) {
378 		topo_mod_dprintf(mod, "Failed to retrieve format arg\n");
379 		nvlist_free(args);
380 		return (err);
381 	}
382 
383 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
384 	(void) snprintf(buf, BUFSZ, fmtstr,
385 	    (topo_node_instance(node) + offset));
386 
387 	if ((err = store_prop_val(mod, buf, "label", out)) != 0) {
388 		topo_mod_dprintf(mod, "Failed to set label\n");
389 		nvlist_free(args);
390 		return (err);
391 	}
392 
393 	return (0);
394 }
395 
396 
397 /*
398  * This is a custom property method for generating the CPU slot label for the
399  * Galaxy 4E/4F platforms.
400  *
401  * format:	a string containing a printf-like format with a single %c token
402  *              which this method computes
403  *
404  *              i.e.: CPU %c
405  */
406 /* ARGSUSED */
407 int
408 g4_chip_label(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
409     nvlist_t *in, nvlist_t **out)
410 {
411 	char *fmtstr, buf[BUFSZ], slot_id;
412 	int err, htid, mapidx;
413 	uint32_t num_nodes;
414 	/*
415 	 * G4 HT node ID to FRU label translation.  The g4map array
416 	 * is indexed by (number of coherent nodes) / 2 - 1.
417 	 * The value for a given number of nodes is a char array
418 	 * indexed by node ID.
419 	 */
420 	const char *g4map[] = {
421 	    "AB",	/* 2 nodes */
422 	    "ADEH",	/* 4 nodes */
423 	    "ABDEFH",	/* 6 nodes */
424 	    "ACBDEFGH"	/* 8 nodes */
425 	};
426 
427 	topo_mod_dprintf(mod, "g4_chip_label() called\n");
428 	if ((fmtstr = get_fmtstr(mod, in, &err)) == NULL) {
429 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
430 		return (err);
431 	}
432 
433 	if (topo_prop_get_uint32(node, "chip-properties", "CoherentNodes",
434 	    &num_nodes, &err) != 0) {
435 		topo_mod_dprintf(mod, "Failed to lookup 'CoherentNodes'"
436 		    "property\n");
437 		return (err);
438 	}
439 
440 	mapidx = num_nodes / 2 - 1;
441 	htid = topo_node_instance(node);
442 
443 	/* HT nodes must number 0 .. num_nodes - 1 */
444 	if (htid >= num_nodes) {
445 		topo_mod_dprintf(mod, "chip node instance range check failed:"
446 		    "num_nodes=%d, instance=%d\n", num_nodes, htid);
447 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
448 	}
449 
450 	switch (num_nodes) {
451 		case (2):
452 		case (4):
453 		case (6):
454 		case (8):
455 			/* htid is already range-checked */
456 			mapidx = num_nodes / 2 - 1;
457 			slot_id = g4map[mapidx][htid];
458 			break;
459 		default:
460 			topo_mod_dprintf(mod, "Invalid number of CoherentNodes:"
461 			    " %d\n", num_nodes);
462 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
463 	}
464 
465 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
466 	(void) snprintf(buf, BUFSZ, fmtstr, slot_id);
467 
468 	if ((err = store_prop_val(mod, buf, "label", out)) != 0) {
469 		topo_mod_dprintf(mod, "Failed to set label\n");
470 		return (err);
471 	}
472 
473 	return (0);
474 }
475