xref: /illumos-gate/usr/src/lib/libprtdiag/common/pdevinfo_funcs.c (revision 1e56f352c1c208679012bca47d552e127f5b1072)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <fcntl.h>
31 #include <stdarg.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <sys/utsname.h>
35 #include <sys/openpromio.h>
36 #include <libintl.h>
37 #include "pdevinfo.h"
38 #include "display.h"
39 #include "pdevinfo_sun4u.h"
40 
41 /*
42  * For machines that support the openprom, fetch and print the list
43  * of devices that the kernel has fetched from the prom or conjured up.
44  *
45  */
46 
47 
48 static int prom_fd;
49 extern char *progname;
50 extern char *promdev;
51 extern void getppdata();
52 extern void printppdata();
53 
54 /*
55  * Define DPRINT for run-time debugging printf's...
56  * #define DPRINT	1
57  */
58 
59 #ifdef	DPRINT
60 static	char    vdebug_flag = 1;
61 #define	dprintf	if (vdebug_flag) printf
62 static void dprint_dev_info(caddr_t, dev_info_t *);
63 #endif	/* DPRINT */
64 
65 #if !defined(TEXT_DOMAIN)
66 #define	TEXT_DOMAIN	"SYS_TEST"
67 #endif
68 
69 /*VARARGS1*/
70 int
71 _error(char *fmt, ...)
72 {
73 	int saved_errno;
74 	va_list ap;
75 	extern int errno;
76 	saved_errno = errno;
77 
78 	if (progname)
79 		(void) fprintf(stderr, "%s: ", progname);
80 
81 	va_start(ap, fmt);
82 
83 	(void) vfprintf(stderr, fmt, ap);
84 
85 	va_end(ap);
86 
87 	(void) fprintf(stderr, ": ");
88 	errno = saved_errno;
89 	perror("");
90 
91 	return (2);
92 }
93 
94 int
95 is_openprom(void)
96 {
97 	Oppbuf	oppbuf;
98 	register struct openpromio *opp = &(oppbuf.opp);
99 	register unsigned int i;
100 
101 	opp->oprom_size = MAXVALSIZE;
102 	if (ioctl(prom_fd, OPROMGETCONS, opp) < 0)
103 		exit(_error("OPROMGETCONS"));
104 
105 	i = (unsigned int)((unsigned char)opp->oprom_array[0]);
106 	return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM);
107 }
108 
109 /*
110  * Read all properties and values from nodes.
111  * Copy the properties read into the prom_node passsed in.
112  */
113 void
114 dump_node(Prom_node *node)
115 {
116 	Oppbuf oppbuf;
117 	register struct openpromio *opp = &oppbuf.opp;
118 	Prop *prop = NULL;	/* tail of properties list */
119 	StaticProp *temp;
120 
121 	/* clear out pointers in pnode */
122 	node->props = NULL;
123 
124 	/* get first prop by asking for null string */
125 	(void) memset((void *) oppbuf.buf, 0, BUFSIZE);
126 
127 	/* allocate space for the property */
128 	if ((temp = malloc(sizeof (StaticProp))) == NULL) {
129 		perror("malloc");
130 		exit(1);
131 	}
132 
133 	opp->oprom_size = MAXPROPSIZE;
134 	while (opp->oprom_size != 0) {
135 		Prop *new;
136 		int i;
137 		char *tempp, *newp;
138 
139 		/*
140 		 * get property
141 		 */
142 		opp->oprom_size = MAXPROPSIZE;
143 
144 		if (ioctl(prom_fd, OPROMNXTPROP, opp) < 0)
145 			exit(_error("OPROMNXTPROP"));
146 
147 		if (opp->oprom_size != 0) {
148 			temp->name.opp.oprom_size = opp->oprom_size;
149 			(void) strcpy(temp->name.opp.oprom_array,
150 				opp->oprom_array);
151 
152 			(void) strcpy(temp->value.opp.oprom_array,
153 				temp->name.opp.oprom_array);
154 			getpropval(&temp->value.opp);
155 			temp->size = temp->value.opp.oprom_size;
156 
157 			/* Now copy over temp's data to new. */
158 			if ((new = malloc(sizeof (Prop))) == NULL) {
159 				perror("malloc");
160 				exit(1);
161 			}
162 
163 			/*
164 			 * First copy over temp->name's data. The
165 			 * temp->name.opp.opio_u union always contains char[]
166 			 * (as opposed to an int or int []).
167 			 */
168 			new->name.opp.oprom_size = temp->name.opp.oprom_size;
169 
170 			if ((new->name.opp.oprom_array =
171 			    malloc(new->name.opp.oprom_size)) == NULL) {
172 				perror("malloc");
173 				exit(1);
174 			}
175 			(void) strcpy(new->name.opp.oprom_array,
176 			    temp->name.opp.oprom_array);
177 
178 			new->name.opp.holds_array = 1;
179 
180 			/*
181 			 * Then copy over temp->value's data.
182 			 * temp->value.opp.opio_u could contain char[], int or
183 			 * int []. If *(temp->value.opp.oprom_array) is '\0',
184 			 * this indicates int or int []. int is the norm, but
185 			 * to be safe we assume int [] and copy over
186 			 * OPROM_NODE_SIZE int elements.
187 			 */
188 			new->value.opp.oprom_size = temp->value.opp.oprom_size;
189 
190 			if (*(temp->value.opp.oprom_array) == '\0') {
191 				for (i = 0; i < OPROM_NODE_SIZE; i++)
192 					new->value.opp.oprom_node[i] =
193 					    *(&temp->value.opp.oprom_node+i);
194 
195 				new->value.opp.holds_array = 0;
196 			} else {
197 				if ((new->value.opp.oprom_array =
198 				    malloc(new->value.opp.oprom_size))
199 				    == NULL) {
200 					perror("malloc");
201 					exit(1);
202 				}
203 
204 				/*
205 				 * temp->value.opp.oprom_array can contain one
206 				 * or more embedded NULLs. These trip-up the
207 				 * standard string copying functions, so we do
208 				 * the copy by hand. temp->value.opp.oprom_array
209 				 * will be NULL-terminated. oprom_size includes
210 				 * this terminating NULL.
211 				 */
212 				newp = new->value.opp.oprom_array;
213 				tempp = temp->value.opp.oprom_array;
214 				for (i = new->value.opp.oprom_size; i > 0; i--)
215 					*newp++ = *tempp++;
216 
217 				new->value.opp.holds_array = 1;
218 			}
219 
220 			new->size = temp->size;
221 
222 			/* everything worked so link the property list */
223 			if (node->props == NULL)
224 				node->props = new;
225 			else if (prop != NULL)
226 				prop->next = new;
227 			prop = new;
228 			prop->next = NULL;
229 		}
230 	}
231 	free(temp);
232 }
233 
234 int
235 promopen(int oflag)
236 {
237 	/*CONSTCOND*/
238 	while (1)  {
239 		if ((prom_fd = open(promdev, oflag)) < 0)  {
240 			if (errno == EAGAIN)   {
241 				(void) sleep(5);
242 				continue;
243 			}
244 			if (errno == ENXIO)
245 				return (-1);
246 			exit(_error(dgettext(TEXT_DOMAIN, "cannot open %s"),
247 				promdev));
248 		} else
249 			return (0);
250 	}
251 	/*NOTREACHED*/
252 }
253 
254 void
255 promclose(void)
256 {
257 	if (close(prom_fd) < 0)
258 		exit(_error(dgettext(TEXT_DOMAIN, "close error on %s"),
259 			promdev));
260 }
261 
262 /*
263  * Read the value of the property from the PROM device tree
264  */
265 void
266 getpropval(struct openpromio *opp)
267 {
268 	opp->oprom_size = MAXVALSIZE;
269 
270 	if (ioctl(prom_fd, OPROMGETPROP, opp) < 0)
271 		exit(_error("OPROMGETPROP"));
272 }
273 
274 int
275 next(int id)
276 {
277 	Oppbuf	oppbuf;
278 	register struct openpromio *opp = &(oppbuf.opp);
279 	/* LINTED */
280 	int *ip = (int *)(opp->oprom_array);
281 
282 	(void) memset((void *) oppbuf.buf, 0, BUFSIZE);
283 
284 	opp->oprom_size = MAXVALSIZE;
285 	*ip = id;
286 	if (ioctl(prom_fd, OPROMNEXT, opp) < 0)
287 		return (_error("OPROMNEXT"));
288 	/* LINTED */
289 	return (*(int *)opp->oprom_array);
290 }
291 
292 int
293 child(int id)
294 {
295 	Oppbuf	oppbuf;
296 	register struct openpromio *opp = &(oppbuf.opp);
297 	/* LINTED */
298 	int *ip = (int *)(opp->oprom_array);
299 
300 	(void) memset((void *) oppbuf.buf, 0, BUFSIZE);
301 	opp->oprom_size = MAXVALSIZE;
302 	*ip = id;
303 	if (ioctl(prom_fd, OPROMCHILD, opp) < 0)
304 		return (_error("OPROMCHILD"));
305 	/* LINTED */
306 	return (*(int *)opp->oprom_array);
307 }
308 
309 /*
310  * Check if the Prom node passed in contains a property called
311  * "board#".
312  */
313 int
314 has_board_num(Prom_node *node)
315 {
316 	Prop *prop = node->props;
317 
318 	/*
319 	 * walk thru all properties in this PROM node and look for
320 	 * board# prop
321 	 */
322 	while (prop != NULL) {
323 		if (strcmp(prop->name.opp.oprom_array, "board#") == 0)
324 		    return (1);
325 
326 		prop = prop->next;
327 	}
328 
329 	return (0);
330 }	/* end of has_board_num() */
331 
332 /*
333  * Retrieve the value of the board number property from this Prom
334  * node. It has the type of int.
335  */
336 int
337 get_board_num(Prom_node *node)
338 {
339 	Prop *prop = node->props;
340 
341 	/*
342 	 * walk thru all properties in this PROM node and look for
343 	 * board# prop
344 	 */
345 	while (prop != NULL) {
346 		if (strcmp(prop->name.opp.oprom_array, "board#") == 0)
347 			return (prop->value.opp.oprom_node[0]);
348 
349 		prop = prop->next;
350 	}
351 
352 	return (-1);
353 }	/* end of get_board_num() */
354 
355 /*
356  * Find the requested board struct in the system device tree.
357  */
358 Board_node *
359 find_board(Sys_tree *root, int board)
360 {
361 	Board_node *bnode = root->bd_list;
362 
363 	while ((bnode != NULL) && (board != bnode->board_num))
364 		bnode = bnode->next;
365 
366 	return (bnode);
367 }	/* end of find_board() */
368 
369 /*
370  * Add a board to the system list in order. Initialize all pointer
371  * fields to NULL.
372  */
373 Board_node *
374 insert_board(Sys_tree *root, int board)
375 {
376 	Board_node *bnode;
377 	Board_node *temp = root->bd_list;
378 
379 	if ((bnode = (Board_node *) malloc(sizeof (Board_node))) == NULL) {
380 		perror("malloc");
381 		exit(1);
382 	}
383 	bnode->nodes = NULL;
384 	bnode->next = NULL;
385 	bnode->board_num = board;
386 
387 	if (temp == NULL)
388 		root->bd_list = bnode;
389 	else if (temp->board_num > board) {
390 		bnode->next = temp;
391 		root->bd_list = bnode;
392 	} else {
393 		while ((temp->next != NULL) && (board > temp->next->board_num))
394 			temp = temp->next;
395 		bnode->next = temp->next;
396 		temp->next = bnode;
397 	}
398 	root->board_cnt++;
399 
400 	return (bnode);
401 }	/* end of insert_board() */
402 
403 /*
404  * This function searches through the properties of the node passed in
405  * and returns a pointer to the value of the name property.
406  */
407 char *
408 get_node_name(Prom_node *pnode)
409 {
410 	Prop *prop;
411 
412 	if (pnode == NULL) {
413 		return (NULL);
414 	}
415 
416 	prop = pnode->props;
417 	while (prop != NULL) {
418 		if (strcmp("name", prop->name.opp.oprom_array) == 0)
419 			return (prop->value.opp.oprom_array);
420 		prop = prop->next;
421 	}
422 	return (NULL);
423 }	/* end of get_node_name() */
424 
425 /*
426  * This function searches through the properties of the node passed in
427  * and returns a pointer to the value of the name property.
428  */
429 char *
430 get_node_type(Prom_node *pnode)
431 {
432 	Prop *prop;
433 
434 	if (pnode == NULL) {
435 		return (NULL);
436 	}
437 
438 	prop = pnode->props;
439 	while (prop != NULL) {
440 		if (strcmp("device_type", prop->name.opp.oprom_array) == 0)
441 			return (prop->value.opp.oprom_array);
442 		prop = prop->next;
443 	}
444 	return (NULL);
445 }	/* end of get_node_type() */
446 
447 /*
448  * Do a depth-first walk of a device tree and
449  * return the first node with the name matching.
450  */
451 
452 Prom_node *
453 dev_find_node(Prom_node *root, char *name)
454 {
455 	Prom_node *node;
456 
457 	node = dev_find_node_by_type(root, "name", name);
458 
459 	return (node);
460 }
461 
462 Prom_node *
463 dev_next_node(Prom_node *root, char *name)
464 {
465 	Prom_node *node;
466 
467 	node = dev_next_node_by_type(root, "name", name);
468 
469 	return (node);
470 }
471 
472 /*
473  * Search for and return a node of the required type. If no node is found,
474  * then return NULL.
475  */
476 Prom_node *
477 dev_find_type(Prom_node *root, char *type)
478 {
479 	Prom_node *node;
480 
481 	node = dev_find_node_by_type(root, "device_type", type);
482 
483 	return (node);  /* not found */
484 }
485 
486 /*
487  * Start from the current node and return the next node besides the
488  * current one which has the requested type property.
489  */
490 Prom_node *
491 dev_next_type(Prom_node *root, char *type)
492 {
493 	Prom_node *node;
494 
495 	node = dev_next_node_by_type(root, "device_type", type);
496 
497 	return (node);  /* not found */
498 }
499 
500 /*
501  * Search a device tree and return the first failed node that is found.
502  * (has a 'status' property)
503  */
504 Prom_node *
505 find_failed_node(Prom_node * root)
506 {
507 	Prom_node *pnode;
508 
509 	if (root == NULL)
510 		return (NULL);
511 
512 	if (node_failed(root)) {
513 		return (root);
514 	}
515 
516 	/* search the child */
517 	if ((pnode = find_failed_node(root->child)) != NULL)
518 		return (pnode);
519 
520 	/* search the siblings */
521 	if ((pnode = find_failed_node(root->sibling)) != NULL)
522 		return (pnode);
523 
524 	return (NULL);
525 }	/* end of find_failed_node() */
526 
527 /*
528  * Start from the current node and return the next node besides
529  * the current one which is failed. (has a 'status' property)
530  */
531 Prom_node *
532 next_failed_node(Prom_node * root)
533 {
534 	Prom_node *pnode;
535 	Prom_node *parent;
536 
537 	if (root == NULL)
538 		return (NULL);
539 
540 	/* search the child */
541 	if ((pnode = find_failed_node(root->child)) != NULL) {
542 		return (pnode);
543 	}
544 
545 	/* search the siblings */
546 	if ((pnode = find_failed_node(root->sibling)) != NULL) {
547 		return (pnode);
548 	}
549 
550 	/* backtracking the search up through parents' siblings */
551 	parent = root->parent;
552 	while (parent != NULL) {
553 		if ((pnode = find_failed_node(parent->sibling)) != NULL)
554 			return (pnode);
555 		else
556 			parent = parent->parent;
557 	}
558 
559 	return (NULL);
560 }	/* end of find_failed_node() */
561 
562 /*
563  * node_failed
564  *
565  * This function determines if the current Prom node is failed. This
566  * is defined by having a status property containing the token 'fail'.
567  */
568 int
569 node_failed(Prom_node *node)
570 {
571 	return (node_status(node, "fail"));
572 }
573 
574 int
575 node_status(Prom_node *node, char *status)
576 {
577 	void *value;
578 
579 	if (status == NULL)
580 		return (0);
581 
582 	/* search the local node */
583 	if ((value = get_prop_val(find_prop(node, "status"))) != NULL) {
584 		if ((value != NULL) && strstr((char *)value, status))
585 			return (1);
586 	}
587 	return (0);
588 }
589 
590 /*
591  * Get a property's value. Must be void * since the property can
592  * be any data type. Caller must know the *PROPER* way to use this
593  * data.
594  */
595 void *
596 get_prop_val(Prop *prop)
597 {
598 	if (prop == NULL)
599 		return (NULL);
600 
601 	if (prop->value.opp.holds_array)
602 		return ((void *)(prop->value.opp.oprom_array));
603 	else
604 		return ((void *)(&prop->value.opp.oprom_node[0]));
605 }	/* end of get_prop_val() */
606 
607 /*
608  * Search a Prom node and retrieve the property with the correct
609  * name.
610  */
611 Prop *
612 find_prop(Prom_node *pnode, char *name)
613 {
614 	Prop *prop;
615 
616 	if (pnode  == NULL) {
617 		return (NULL);
618 	}
619 
620 	if (pnode->props == NULL) {
621 		(void) printf("%s", dgettext(TEXT_DOMAIN, "Prom node has "
622 			"no properties\n"));
623 		return (NULL);
624 	}
625 
626 	prop = pnode->props;
627 	while ((prop != NULL) && (strcmp(prop->name.opp.oprom_array, name)))
628 		prop = prop->next;
629 
630 	return (prop);
631 }
632 
633 /*
634  * This function adds a board node to the board structure where that
635  * that node's physical component lives.
636  */
637 void
638 add_node(Sys_tree *root, Prom_node *pnode)
639 {
640 	int board;
641 	Board_node *bnode;
642 	Prom_node *p;
643 
644 	/* add this node to the Board list of the appropriate board */
645 	if ((board = get_board_num(pnode)) == -1) {
646 		/* board is 0 if not on Sunfire */
647 		board = 0;
648 	}
649 
650 	/* find the node with the same board number */
651 	if ((bnode = find_board(root, board)) == NULL) {
652 		bnode = insert_board(root, board);
653 		bnode->board_type = UNKNOWN_BOARD;
654 	}
655 
656 	/* now attach this prom node to the board list */
657 	/* Insert this node at the end of the list */
658 	pnode->sibling = NULL;
659 	if (bnode->nodes == NULL)
660 		bnode->nodes = pnode;
661 	else {
662 		p = bnode->nodes;
663 		while (p->sibling != NULL)
664 			p = p->sibling;
665 		p->sibling = pnode;
666 	}
667 
668 }
669 
670 /*
671  * Find the device on the current board with the requested device ID
672  * and name. If this rountine is passed a NULL pointer, it simply returns
673  * NULL.
674  */
675 Prom_node *
676 find_device(Board_node *board, int id, char *name)
677 {
678 	Prom_node *pnode;
679 	int mask;
680 
681 	/* find the first cpu node */
682 	pnode = dev_find_node(board->nodes, name);
683 
684 	mask = 0x1F;
685 	while (pnode != NULL) {
686 		if ((get_id(pnode) & mask) == id)
687 			return (pnode);
688 
689 		pnode = dev_next_node(pnode, name);
690 	}
691 	return (NULL);
692 }
693 
694 Prom_node *
695 dev_find_node_by_type(Prom_node *root, char *type, char *property)
696 {
697 	Prom_node *node;
698 	char *type_prop;
699 
700 	if (root == NULL || property == NULL)
701 		return (NULL);
702 
703 	type_prop = (char *)get_prop_val(find_prop(root, type));
704 
705 	if (type_prop != NULL) {
706 		if (strcmp(type_prop, property) == 0) {
707 			return (root);
708 		}
709 	}
710 
711 	/* look at your children first */
712 	if ((node = dev_find_node_by_type(root->child, type,
713 	    property)) != NULL)
714 		return (node);
715 
716 	/* now look at your siblings */
717 	if ((node = dev_find_node_by_type(root->sibling, type,
718 	    property)) != NULL)
719 		return (node);
720 
721 	return (NULL);	/* not found */
722 }
723 
724 Prom_node *
725 dev_next_node_by_type(Prom_node *root, char *type, char *property)
726 {
727 	Prom_node *node;
728 
729 	if (root == NULL || property == NULL)
730 		return (NULL);
731 
732 	/* look at your children first */
733 	if ((node = dev_find_node_by_type(root->child, type,
734 	    property)) != NULL)
735 		return (node);
736 
737 	/* now look at your siblings */
738 	if ((node = dev_find_node_by_type(root->sibling, type,
739 	    property)) != NULL)
740 		return (node);
741 
742 	/* now look at papa's siblings */
743 	if ((node = dev_find_node_by_type(root->parent->sibling,
744 	    type, property)) != NULL)
745 		return (node);
746 
747 	return (NULL);  /* not found */
748 }
749 
750 /*
751  * Do a depth-first walk of a device tree and
752  * return the first node with the matching compatible.
753  */
754 Prom_node *
755 dev_find_node_by_compatible(Prom_node *root, char *compatible)
756 {
757 	Prom_node *node;
758 	Prop	*prop;
759 	char	*compatible_array;
760 	int	size, nbytes;
761 
762 	if (root == NULL || compatible == NULL)
763 		return (NULL);
764 
765 	if ((prop = find_prop(root, "compatible")) != NULL &&
766 	    (compatible_array = (char *)get_prop_val(prop)) != NULL) {
767 		/*
768 		 * The Prop structure returned by find_prop() is supposed
769 		 * to contain an indication of how big the value of the
770 		 * compatible property is.  Since it is an array of strings
771 		 * this is our only means of determining just how many
772 		 * strings might be in this property.  However, this size
773 		 * is often left as zero even though there is at least one
774 		 * string present.  When this is the case, all we can do
775 		 * is examine the first string in the compatible property.
776 		 */
777 
778 		for (size = prop->size; size >= 0; size -= nbytes) {
779 			if (strcmp(compatible_array, compatible) == 0)
780 				return (root);		/* found a match */
781 
782 			nbytes = strlen(compatible_array) + 1;
783 			compatible_array += nbytes;
784 		}
785 	}
786 
787 	node = dev_find_node_by_compatible(root->child, compatible);
788 	if (node != NULL)
789 		return (node);
790 
791 	/*
792 	 * Note the very deliberate use of tail recursion here.	 A good
793 	 * compiler (such as Sun's) will recognize this and generate code
794 	 * that does not allocate another stack frame.	Instead, it will
795 	 * overlay the existing stack frame with the new one, the only change
796 	 * having been to replace the original root with its sibling.
797 	 * This has the potential to create some confusion for anyone
798 	 * trying to debug this code from a core dump, since the stack
799 	 * trace will not reveal recursion on siblings, only on children.
800 	 */
801 
802 	return (dev_find_node_by_compatible(root->sibling, compatible));
803 }
804 
805 /*
806  * Start from the current node and return the next node besides
807  * the current one which has the requested compatible property.
808  */
809 Prom_node *
810 dev_next_node_by_compatible(Prom_node *root, char *compatible)
811 {
812 	Prom_node *node;
813 
814 	if (root == NULL || compatible == NULL)
815 		return (NULL);
816 
817 	node = dev_find_node_by_compatible(root->child, compatible);
818 	if (node != NULL)
819 		return (node);
820 
821 	/*
822 	 * More tail recursion.	 Even though it is a different function,
823 	 * this will overlay the current stack frame.  Caveat exterminator.
824 	 */
825 
826 	node = dev_find_node_by_compatible(root->sibling, compatible);
827 	if (node != NULL)
828 		return (node);
829 
830 	return (dev_find_node_by_compatible(root->parent->sibling, compatible));
831 }
832