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