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
_error(char * fmt,...)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
is_openprom(void)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
dump_node(Prom_node * node)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
promopen(int oflag)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
promclose(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
getpropval(struct openpromio * opp)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
next(int id)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
child(int id)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
has_board_num(Prom_node * node)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
get_board_num(Prom_node * node)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 *
find_board(Sys_tree * root,int board)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 *
insert_board(Sys_tree * root,int board)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 *
get_node_name(Prom_node * pnode)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 *
get_node_type(Prom_node * pnode)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 *
dev_find_node(Prom_node * root,char * name)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 *
dev_next_node(Prom_node * root,char * name)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 *
dev_find_type(Prom_node * root,char * type)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 *
dev_next_type(Prom_node * root,char * type)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 *
find_failed_node(Prom_node * root)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 *
next_failed_node(Prom_node * root)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
node_failed(Prom_node * node)569 node_failed(Prom_node *node)
570 {
571 return (node_status(node, "fail"));
572 }
573
574 int
node_status(Prom_node * node,char * status)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 *
get_prop_val(Prop * prop)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 *
find_prop(Prom_node * pnode,char * name)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
add_node(Sys_tree * root,Prom_node * pnode)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 *
find_device(Board_node * board,int id,char * name)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 *
dev_find_node_by_type(Prom_node * root,char * type,char * property)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 *
dev_next_node_by_type(Prom_node * root,char * type,char * property)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 *
dev_find_node_by_compatible(Prom_node * root,char * compatible)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 *
dev_next_node_by_compatible(Prom_node * root,char * compatible)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