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 /*
30  * config.c -- system configuration cache module
31  *
32  * this module caches the system configuration in a format useful
33  * to eft.  the information is loaded into this module by
34  * config_snapshot() at the beginning of each FME.  config_snapshot()
35  * calls the platform-specific platform_config_snapshot() to get
36  * the configuration information loaded up.
37  */
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <string.h>
43 #include <strings.h>
44 #include <fm/topo_hc.h>
45 #include "alloc.h"
46 #include "out.h"
47 #include "literals.h"
48 #include "stable.h"
49 #include "lut.h"
50 #include "tree.h"
51 #include "itree.h"
52 #include "ipath.h"
53 #include "ptree.h"
54 #include "eval.h"
55 #include "config.h"
56 #include "fme.h"
57 #include "platform.h"
58 
59 /*
60  * private data structure for storing config.  all access to
61  * to this information happens using the config.h interfaces.
62  */
63 struct config {
64 	struct config *next;
65 	struct config *child;
66 	struct config *parent;
67 	const char *s;
68 	int num;
69 	struct lut *props;
70 };
71 
72 static const char *config_lastcomp;
73 
74 /*
75  * newcnode -- local function to allocate new config node
76  */
77 static struct config *
78 newcnode(const char *s, int num)
79 {
80 	struct config *retval;
81 
82 	retval = MALLOC(sizeof (struct config));
83 
84 	retval->s = s;
85 	retval->num = num;
86 	retval->next = NULL;
87 	retval->props = NULL;
88 	retval->child = retval->parent = NULL;
89 
90 	return (retval);
91 }
92 
93 /*
94  * If we need to cache certain types of nodes for reverse look-up or
95  * somesuch, do it here.  Currently we need to cache nodes representing
96  * cpus.
97  */
98 static void
99 config_node_cache(struct cfgdata *cdata, struct config *n)
100 {
101 	if (n->s != stable("cpu"))
102 		return;
103 	cdata->cpucache = lut_add(cdata->cpucache,
104 	    (void *)n->num, (void *)n, NULL);
105 }
106 
107 /*
108  * config_lookup -- lookup/add components in configuration cache
109  */
110 struct config *
111 config_lookup(struct config *croot, char *path, int add)
112 {
113 	char *pathbegin = path;
114 	struct config *parent = croot;
115 	struct config *cp;
116 	struct config *lastcp;
117 	struct config *newnode;
118 	char *thiscom;	/* this component */
119 	char *nextcom;	/* next component */
120 	char svdigit;
121 	int len;
122 	int num;
123 	const char *s;
124 	int exists;
125 
126 	if (parent == NULL)
127 		out(O_DIE, "uninitialized configuration");
128 
129 	while (*path) {
130 		if ((nextcom = strchr(path, '/')) != NULL)
131 			*nextcom = '\0';
132 		if ((len = strlen(path)) == 0)
133 			out(O_DIE, "config_lookup: zero length component");
134 		/* start at end of string and work backwards */
135 		thiscom = &path[len - 1];
136 		if (!isdigit(*thiscom))
137 			out(O_DIE, "config_lookup: "
138 			    "component \"%s\" has no number following it",
139 			    path);
140 		while (thiscom > path && isdigit(*thiscom))
141 			thiscom--;
142 		if (thiscom == path && isdigit(*thiscom))
143 			out(O_DIE, "config_lookup: "
144 			    "component \"%s\" has no name part", path);
145 		thiscom++;	/* move to first numeric character */
146 		num = atoi(thiscom);
147 		svdigit = *thiscom;
148 		*thiscom = '\0';
149 		s = stable(path);
150 		if (add)
151 			config_lastcomp = s;
152 		*thiscom = svdigit;
153 
154 		if (nextcom != NULL)
155 			*nextcom++ = '/';
156 
157 		/* now we have s & num, figure out if it exists already */
158 		exists = 0;
159 		lastcp = NULL;
160 		for (cp = parent->child; cp; lastcp = cp, cp = cp->next)
161 			if (cp->s == s && cp->num == num) {
162 				exists = 1;
163 				parent = cp;
164 			}
165 
166 		if (!exists) {
167 			/* creating new node */
168 			if (!add) {
169 				/*
170 				 * indicate component not found by copying
171 				 * it to path (allows better error messages
172 				 * in the caller).
173 				 */
174 				(void) strcpy(pathbegin, s);
175 				return (NULL);
176 			}
177 
178 			newnode = newcnode(s, num);
179 
180 			if (lastcp)
181 				lastcp->next = newnode;
182 			else
183 				parent->child = newnode;
184 
185 			newnode->parent = parent;
186 			parent = newnode;
187 		}
188 
189 		if (nextcom == NULL)
190 			return (parent);	/* all done */
191 
192 		/* move on to next component */
193 		path = nextcom;
194 	}
195 	return (parent);
196 }
197 
198 /*
199  * addconfigprop -- add a config prop to a config cache entry
200  */
201 static void
202 addconfigprop(const char *lhs, struct node *rhs, void *arg)
203 {
204 	struct config *cp = (struct config *)arg;
205 
206 	ASSERT(cp != NULL);
207 	ASSERT(lhs != NULL);
208 	ASSERT(rhs != NULL);
209 	ASSERT(rhs->t == T_QUOTE);
210 
211 	config_setprop(cp, lhs, STRDUP(rhs->u.quote.s));
212 }
213 
214 /*
215  * addconfig -- add a config from parse tree to given configuration cache
216  */
217 /*ARGSUSED*/
218 static void
219 addconfig(struct node *lhs, struct node *rhs, void *arg)
220 {
221 	struct config *parent = (struct config *)arg;
222 	struct config *cp;
223 	const char *s;
224 	int num;
225 	struct config *lastcp;
226 	struct config *newnode;
227 	int exists;
228 	struct lut *lutp;
229 
230 	ASSERT(rhs->t == T_CONFIG);
231 
232 	lutp = rhs->u.stmt.lutp;
233 	rhs = rhs->u.stmt.np;
234 	while (rhs != NULL) {
235 		ASSERT(rhs->t == T_NAME);
236 		ASSERT(rhs->u.name.child->t == T_NUM);
237 		s = rhs->u.name.s;
238 		num = rhs->u.name.child->u.ull;
239 
240 		/* now we have s & num, figure out if it exists already */
241 		exists = 0;
242 		lastcp = NULL;
243 		for (cp = parent->child; cp; lastcp = cp, cp = cp->next)
244 			if (cp->s == s && cp->num == num) {
245 				exists = 1;
246 				parent = cp;
247 			}
248 
249 		if (!exists) {
250 			/* creating new node */
251 
252 			newnode = newcnode(s, num);
253 
254 			if (lastcp)
255 				lastcp->next = newnode;
256 			else
257 				parent->child = newnode;
258 
259 			newnode->parent = parent;
260 			parent = newnode;
261 		}
262 
263 		/* move on to next component */
264 		rhs = rhs->u.name.next;
265 	}
266 
267 	/* add configuration properties */
268 	lut_walk(lutp, (lut_cb)addconfigprop, (void *)parent);
269 }
270 
271 /*
272  * config_cook -- convert raw config strings to eft internal representation
273  */
274 void
275 config_cook(struct cfgdata *cdata)
276 {
277 	struct config *newnode;
278 	char *cfgstr, *equals;
279 	const char *pn, *sv;
280 	char *pv;
281 	const char *ptr;
282 	extern struct lut *Usedprops;
283 	extern struct lut *Usednames;
284 
285 	cdata->cooked_refcnt++;
286 
287 	if (cdata->cooked != NULL)
288 		return;
289 
290 	cdata->cooked = newcnode(NULL, 0);
291 
292 	if ((cfgstr = cdata->begin) == cdata->nextfree) {
293 		out(O_ALTFP|O_VERB, "Platform provided no config data.");
294 		goto eftcfgs;
295 	}
296 
297 	/*
298 	 * add the following properties to the "usedprops" table as they
299 	 * are used internally by eft
300 	 */
301 	ptr = stable("module");
302 	Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
303 	ptr = stable("resource");
304 	Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
305 	ptr = stable("ASRU");
306 	Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
307 	ptr = stable("FRU");
308 	Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL);
309 
310 	out(O_ALTFP|O_VERB3, "Raw config data follows:");
311 	out(O_ALTFP|O_VERB3|O_NONL,
312 	    "nextfree is %p\n%p ", (void *)cdata->nextfree, (void *)cfgstr);
313 	while (cfgstr < cdata->nextfree) {
314 		if (!*cfgstr)
315 			out(O_ALTFP|O_VERB3|O_NONL, "\n%p ",
316 			    (void *)(cfgstr + 1));
317 		else
318 			out(O_ALTFP|O_VERB3|O_NONL, "%c", *cfgstr);
319 		cfgstr++;
320 	}
321 	out(O_ALTFP|O_VERB3, NULL);
322 
323 	cfgstr = cdata->begin;
324 	while (cfgstr < cdata->nextfree) {
325 		while (*cfgstr == '/' && cfgstr < cdata->nextfree) {
326 			out(O_ALTFP|O_VERB3,
327 			    "next string (%p) is %s", (void *)cfgstr, cfgstr);
328 			/* skip the initial slash from libtopo */
329 			newnode = config_lookup(cdata->cooked, cfgstr + 1, 1);
330 			/*
331 			 * Note we'll only cache nodes that have
332 			 * properties on them.  Intermediate nodes
333 			 * will have been added to the config tree,
334 			 * but we don't have easy means of accessing
335 			 * them except if we climb the tree from this
336 			 * newnode to the root.
337 			 *
338 			 * Luckily, the nodes we care to cache
339 			 * (currently just cpus) always have some
340 			 * properties attached to them
341 			 * so we don't bother climbing the tree.
342 			 */
343 			config_node_cache(cdata, newnode);
344 			cfgstr += strlen(cfgstr) + 1;
345 		}
346 
347 		if (cfgstr >= cdata->nextfree)
348 			break;
349 
350 		out(O_ALTFP|O_VERB3, "next string (%p) is %s", (void *)cfgstr,
351 		    cfgstr);
352 		if ((equals = strchr(cfgstr, '=')) == NULL) {
353 			out(O_ALTFP|O_VERB3, "raw config data bad (%p); "
354 			    "property missing equals.\n", (void *)cfgstr);
355 			break;
356 		}
357 
358 		*equals = '\0';
359 		pn = stable(cfgstr);
360 
361 		/*
362 		 * only actually add the props if the rules use them (saves
363 		 * memory)
364 		 */
365 		if (lut_lookup(Usedprops, (void *)pn, NULL) != NULL &&
366 		    lut_lookup(Usednames, (void *)config_lastcomp, NULL) !=
367 		    NULL) {
368 			pv = STRDUP(equals + 1);
369 			out(O_ALTFP|O_VERB3, "add prop (%s) val %p", pn,
370 			    (void *)pv);
371 			config_setprop(newnode, pn, pv);
372 		}
373 
374 		/*
375 		 * If this property is a device path, cache it for quick lookup
376 		 */
377 		if (pn == stable(TOPO_IO_DEV)) {
378 			sv = stable(equals + 1);
379 			out(O_ALTFP|O_VERB3, "caching %s\n", sv);
380 			cdata->devcache = lut_add(cdata->devcache,
381 			    (void *)sv, (void *)newnode, NULL);
382 		}
383 
384 		*equals = '=';
385 		cfgstr += strlen(cfgstr) + 1;
386 	}
387 
388 eftcfgs:
389 	/* now run through Configs table, adding to config cache */
390 	lut_walk(Configs, (lut_cb)addconfig, (void *)cdata->cooked);
391 }
392 
393 /*
394  * config_snapshot -- gather a snapshot of the current configuration
395  */
396 struct cfgdata *
397 config_snapshot(void)
398 {
399 	struct cfgdata *rawcfg;
400 
401 	rawcfg = platform_config_snapshot();
402 	config_cook(rawcfg);
403 	return (rawcfg);
404 }
405 
406 /*
407  * prop_destructor -- free a prop value
408  */
409 /*ARGSUSED*/
410 static void
411 prop_destructor(void *left, void *right, void *arg)
412 {
413 	FREE(right);
414 }
415 
416 /*
417  * structconfig_free -- free a struct config pointer and all its relatives
418  */
419 static void
420 structconfig_free(struct config *cp)
421 {
422 	if (cp == NULL)
423 		return;
424 
425 	structconfig_free(cp->child);
426 	structconfig_free(cp->next);
427 	lut_free(cp->props, prop_destructor, NULL);
428 	FREE(cp);
429 }
430 
431 /*
432  * config_free -- free a configuration snapshot
433  */
434 void
435 config_free(struct cfgdata *cp)
436 {
437 	if (cp == NULL)
438 		return;
439 
440 	if (--cp->cooked_refcnt == 0) {
441 		if (cp->cooked != NULL)
442 			structconfig_free(cp->cooked);
443 		cp->cooked = NULL;
444 		if (cp->devcache != NULL)
445 			lut_free(cp->devcache, NULL, NULL);
446 		cp->devcache = NULL;
447 		if (cp->cpucache != NULL)
448 			lut_free(cp->cpucache, NULL, NULL);
449 		cp->cpucache = NULL;
450 	}
451 
452 	if (--cp->raw_refcnt == 0) {
453 		if (cp->begin != NULL)
454 			FREE(cp->begin);
455 		FREE(cp);
456 	}
457 }
458 
459 /*
460  * config_next -- get the "next" config node
461  */
462 struct config *
463 config_next(struct config *cp)
464 {
465 	ASSERT(cp != NULL);
466 
467 	return ((struct config *)((struct config *)cp)->next);
468 }
469 
470 
471 /*
472  * config_child -- get the "child" of a config node
473  */
474 struct config *
475 config_child(struct config *cp)
476 {
477 	ASSERT(cp != NULL);
478 
479 	return ((struct config *)((struct config *)cp)->child);
480 }
481 
482 /*
483  * config_parent -- get the "parent" of a config node
484  */
485 struct config *
486 config_parent(struct config *cp)
487 {
488 	ASSERT(cp != NULL);
489 
490 	return ((struct config *)((struct config *)cp)->parent);
491 }
492 
493 /*
494  * config_setprop -- add a property to a config node
495  */
496 void
497 config_setprop(struct config *cp, const char *propname, const char *propvalue)
498 {
499 	const char *pn = stable(propname);
500 
501 	cp->props = lut_add(cp->props, (void *)pn, (void *)propvalue, NULL);
502 }
503 
504 /*
505  * config_getprop -- lookup a config property
506  */
507 const char *
508 config_getprop(struct config *cp, const char *propname)
509 {
510 	return (lut_lookup(cp->props, (void *) stable(propname), NULL));
511 }
512 
513 /*
514  * config_getcompname -- get the component name of a config node
515  */
516 void
517 config_getcompname(struct config *cp, char **name, int *inst)
518 {
519 	ASSERT(cp != NULL);
520 
521 	if (name != NULL)
522 		*name = (char *)cp->s;
523 	if (inst != NULL)
524 		*inst = cp->num;
525 }
526 
527 /*
528  * config_nodeize -- convert the config element represented by cp to struct
529  *		     node format
530  */
531 static struct node *
532 config_nodeize(struct config *cp)
533 {
534 	struct node *tmpn, *ptmpn;
535 	struct node *numn;
536 	const char *sname;
537 
538 	if (cp == NULL || cp->s == NULL)
539 		return (NULL);
540 
541 	sname = stable(cp->s);
542 	numn = newnode(T_NUM, NULL, 0);
543 	numn->u.ull = cp->num;
544 
545 	tmpn = tree_name_iterator(tree_name(sname, IT_VERTICAL, NULL, 0), numn);
546 	if ((ptmpn = config_nodeize(cp->parent)) == NULL)
547 		return (tmpn);
548 	return (tree_name_append(ptmpn, tmpn));
549 }
550 
551 /*ARGSUSED*/
552 static void
553 prtdevcache(void *lhs, void *rhs, void *arg)
554 {
555 	out(O_ALTFP|O_VERB3, "%s -> %p", (char *)lhs, rhs);
556 }
557 
558 /*ARGSUSED*/
559 static void
560 prtcpucache(void *lhs, void *rhs, void *arg)
561 {
562 	out(O_ALTFP|O_VERB, "%u -> %p", (uint32_t)lhs, rhs);
563 }
564 
565 /*
566  * config_bydev_lookup -- look up the path in our DEVcache lut.  If we find
567  * it return the config path, but as a struct node.
568  */
569 struct node *
570 config_bydev_lookup(struct cfgdata *fromcfg, const char *path)
571 {
572 	struct config *find;
573 	struct node *np;
574 
575 	out(O_ALTFP|O_VERB3, "Device path cache:");
576 	lut_walk(fromcfg->devcache, (lut_cb)prtdevcache, NULL);
577 
578 	if ((find = lut_lookup(fromcfg->devcache,
579 	    (void *) stable(path), NULL)) == NULL)
580 		return (NULL);
581 
582 	np = config_nodeize(find);
583 	if (np != NULL) {
584 		out(O_ALTFP|O_VERB, "Matching config entry:");
585 		ptree_name_iter(O_ALTFP|O_VERB|O_NONL, np);
586 		out(O_ALTFP|O_VERB, NULL);
587 	}
588 	return (np);
589 }
590 
591 /*
592  * config_bycpuid_lookup -- look up the cpu id in our CPUcache lut.
593  * If we find it return the config path, but as a struct node.
594  */
595 struct node *
596 config_bycpuid_lookup(struct cfgdata *fromcfg, uint32_t id)
597 {
598 	struct config *find;
599 	struct node *np;
600 
601 	out(O_ALTFP|O_VERB, "Cpu cache:");
602 	lut_walk(fromcfg->cpucache, (lut_cb)prtcpucache, NULL);
603 
604 	if ((find = lut_lookup(fromcfg->cpucache,
605 	    (void *)id, NULL)) == NULL)
606 		return (NULL);
607 
608 	np = config_nodeize(find);
609 	if (np != NULL) {
610 		out(O_ALTFP|O_VERB3, "Matching config entry:");
611 		ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, np);
612 		out(O_ALTFP|O_VERB3, NULL);
613 	}
614 	return (np);
615 }
616 
617 /*
618  * printprop -- print prop associated with config node
619  */
620 static void
621 printprop(const char *lhs, const char *rhs, void *arg)
622 {
623 	int flags = (int)arg;
624 
625 	out(flags, "\t%s=%s", lhs, rhs);
626 }
627 
628 /*
629  * pconf -- internal printing function to recurse through the tree
630  */
631 static void
632 pconf(int flags, struct config *cp, char *buf, int offset, int limit)
633 {
634 	char *sep = "/";
635 
636 	if (offset)
637 		sep = "/";
638 	else
639 		sep = "";
640 	(void) snprintf(&buf[offset], limit - offset, "%s%s%d",
641 	    sep, cp->s, cp->num);
642 	if (cp->child == NULL) {
643 		out(flags, "%s", buf);
644 		lut_walk(cp->props, (lut_cb)printprop, (void *)flags);
645 	} else
646 		pconf(flags, cp->child, buf, strlen(buf), limit);
647 	if (cp->next)
648 		pconf(flags, cp->next, buf, offset, limit);
649 }
650 
651 /*
652  * config_print -- spew the current configuration cache
653  */
654 
655 #define	MAXCONFLINE 4096
656 
657 void
658 config_print(int flags, struct config *croot)
659 {
660 	char buf[MAXCONFLINE];
661 
662 	if (croot == NULL)
663 		out(flags, "empty configuration");
664 	else
665 		pconf(flags, croot->child, buf, 0, MAXCONFLINE);
666 }
667