Home | History | Annotate | Download | only in common
      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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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 *
    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
    571 node_failed(Prom_node *node)
    572 {
    573 	return (node_status(node, "fail"));
    574 }
    575 
    576 int
    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 *
    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 *
    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
    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 *
    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 *
    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 *
    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 *
    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 *
    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