Home | History | Annotate | Download | only in libdevinfo
      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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include <stdio.h>
     29 #include <string.h>
     30 #include <strings.h>
     31 #include <unistd.h>
     32 #include <stdlib.h>
     33 #include <thread.h>
     34 #include <synch.h>
     35 #include <sys/types.h>
     36 #include <ctype.h>
     37 #include <sys/stat.h>
     38 #include <fcntl.h>
     39 #include <sys/modctl.h>
     40 #include <errno.h>
     41 #include <sys/openpromio.h>
     42 #include <ftw.h>
     43 #include <sys/ddi.h>
     44 #include <sys/sunddi.h>
     45 #include <limits.h>
     46 
     47 #include "device_info.h"
     48 
     49 /*
     50  * #define's
     51  */
     52 
     53 /* alias node searching return values */
     54 #define	NO_MATCH	-1
     55 #define	EXACT_MATCH	1
     56 #define	INEXACT_MATCH	2
     57 
     58 /* for prom io operations */
     59 #define	BUFSIZE		4096
     60 #define	MAXPROPSIZE	256
     61 #define	MAXVALSIZE	(BUFSIZE - MAXPROPSIZE - sizeof (uint_t))
     62 
     63 /* prom_obp_vers() return values */
     64 #define	OBP_OF			0x4	/* versions OBP 3.x */
     65 #define	OBP_NO_ALIAS_NODE	0x8	/* No alias node */
     66 
     67 /* for nftw call */
     68 #define	FT_DEPTH	15
     69 
     70 /* default logical and physical device name space */
     71 #define	DEV	"/dev"
     72 #define	DEVICES	"/devices"
     73 
     74 /* for boot device identification on x86 */
     75 #define	CREATE_DISKMAP		"/boot/solaris/bin/create_diskmap"
     76 #define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
     77 
     78 /*
     79  * internal structure declarations
     80  */
     81 
     82 /* for prom io functions */
     83 typedef union {
     84 	char buf[BUFSIZE];
     85 	struct openpromio opp;
     86 } Oppbuf;
     87 
     88 /* used to manage lists of devices and aliases */
     89 struct name_list {
     90 	char *name;
     91 	struct name_list *next;
     92 };
     93 
     94 /*
     95  * internal global data
     96  */
     97 
     98 /* global since nftw does not let you pass args to be updated */
     99 static struct name_list **dev_list;
    100 
    101 /* global since nftw does not let you pass args to be updated */
    102 static struct boot_dev **bootdev_list;
    103 
    104 /* mutex to protect bootdev_list and dev_list */
    105 static mutex_t dev_lists_lk = DEFAULTMUTEX;
    106 
    107 /*
    108  * internal function prototypes
    109  */
    110 
    111 static int prom_open(int);
    112 static void prom_close(int);
    113 static int is_openprom(int);
    114 
    115 static int prom_dev_to_alias(char *dev, uint_t options, char ***ret_buf);
    116 static int alias_to_prom_dev(char *alias, char *ret_buf);
    117 static int prom_srch_aliases_by_def(char *, struct name_list **,
    118     struct name_list **, int);
    119 static int prom_find_aliases_node(int fd);
    120 static int prom_compare_devs(char *prom_dev1, char *prom_dev2);
    121 static int _prom_strcmp(char *s1, char *s2);
    122 static int prom_srch_node(int fd, char *prop_name, char *ret_buf);
    123 static uint_t prom_next_node(int fd, uint_t node_id);
    124 static uint_t prom_child_node(int fd, uint_t node_id);
    125 
    126 static int prom_obp_vers(void);
    127 
    128 static void parse_name(char *, char **, char **, char **);
    129 static int process_bootdev(const char *, const char *, struct boot_dev ***);
    130 static int process_minor_name(char *dev_path, const char *default_root);
    131 static void options_override(char *prom_path, char *alias_name);
    132 static int devfs_phys_to_logical(struct boot_dev **bootdev_array,
    133 	const int array_size, const char *default_root);
    134 static int check_logical_dev(const char *, const struct stat *, int,
    135 	struct FTW *);
    136 static struct boot_dev *alloc_bootdev(char *);
    137 static void free_name_list(struct name_list *list, int free_name);
    138 static int insert_alias_list(struct name_list **list,
    139 	char *alias_name);
    140 static int get_boot_dev_var(struct openpromio *opp);
    141 static int set_boot_dev_var(struct openpromio *opp, char *bootdev);
    142 static int devfs_prom_to_dev_name(char *prom_path, char *dev_path);
    143 static int devfs_dev_to_prom_names(char *dev_path, char *prom_path, size_t len);
    144 
    145 /*
    146  * frees a list of paths from devfs_get_prom_name_list
    147  */
    148 static void
    149 prom_list_free(char **prom_list)
    150 {
    151 	int i = 0;
    152 
    153 	if (!prom_list)
    154 		return;
    155 
    156 	while (prom_list[i]) {
    157 		free(prom_list[i]);
    158 		i++;
    159 	}
    160 	free(prom_list);
    161 }
    162 
    163 static int
    164 devfs_get_prom_name_list(const char *dev_name, char ***prom_list)
    165 {
    166 	char *prom_path = NULL;
    167 	int count = 0;		/* # of slots we will need in prom_list */
    168 	int ret, i, len;
    169 	char **list;
    170 	char *ptr;
    171 
    172 	if (dev_name == NULL)
    173 		return (DEVFS_INVAL);
    174 	if (*dev_name != '/')
    175 		return (DEVFS_INVAL);
    176 	if (prom_list == NULL)
    177 		return (DEVFS_INVAL);
    178 
    179 	/*
    180 	 * make sure we are on a machine which supports a prom
    181 	 * and we have permission to use /dev/openprom
    182 	 */
    183 	if ((ret = prom_obp_vers()) < 0)
    184 		return (ret);
    185 	if ((prom_path = (char *)malloc(MAXVALSIZE)) == NULL)
    186 		return (DEVFS_NOMEM);
    187 	/*
    188 	 * get the prom path name
    189 	 */
    190 	ret = devfs_dev_to_prom_names((char *)dev_name, prom_path, MAXVALSIZE);
    191 	if (ret < 0) {
    192 		free(prom_path);
    193 		return (ret);
    194 	}
    195 	/* deal with list of names */
    196 	for (i = 0; i < ret; i++)
    197 		if (prom_path[i] == '\0')
    198 			count++;
    199 
    200 	if ((list = (char **)calloc(count + 1, sizeof (char *))) == NULL) {
    201 		free(prom_path);
    202 		return (DEVFS_NOMEM);
    203 	}
    204 
    205 	ptr = prom_path;
    206 	for (i = 0; i < count; i++) {
    207 		len = strlen(ptr) + 1;
    208 		if ((list[i] = (char *)malloc(len)) == NULL) {
    209 			free(prom_path);
    210 			free(list);
    211 			return (DEVFS_NOMEM);
    212 		}
    213 		(void) snprintf(list[i], len, "%s", ptr);
    214 		ptr += len;
    215 	}
    216 
    217 	free(prom_path);
    218 
    219 	*prom_list = list;
    220 	return (0);
    221 }
    222 
    223 /*
    224  * retrieve the list of prom representations for a given device name
    225  * the list will be sorted in the following order: exact aliases,
    226  * inexact aliases, prom device path name.  If multiple matches occur
    227  * for exact or inexact aliases, then these are sorted in collating
    228  * order. The list is returned in prom_list
    229  *
    230  * the list may be restricted by specifying the correct flags in options.
    231  */
    232 int
    233 devfs_get_prom_names(const char *dev_name, uint_t options, char ***prom_list)
    234 {
    235 	char *prom_path = NULL;
    236 	int count = 0;		/* # of slots we will need in prom_list */
    237 	char **alias_list = NULL;
    238 	char **list;
    239 	int ret;
    240 
    241 	if (dev_name == NULL) {
    242 		return (DEVFS_INVAL);
    243 	}
    244 	if (*dev_name != '/') {
    245 		return (DEVFS_INVAL);
    246 	}
    247 	if (prom_list == NULL) {
    248 		return (DEVFS_INVAL);
    249 	}
    250 	/*
    251 	 * make sure we are on a machine which supports a prom
    252 	 * and we have permission to use /dev/openprom
    253 	 */
    254 	if ((ret = prom_obp_vers()) < 0) {
    255 		return (ret);
    256 	}
    257 	if ((prom_path = (char *)malloc(MAXPATHLEN)) == NULL) {
    258 		return (DEVFS_NOMEM);
    259 	}
    260 	/*
    261 	 * get the prom path name
    262 	 */
    263 	ret = devfs_dev_to_prom_name((char *)dev_name, prom_path);
    264 	if (ret < 0) {
    265 		free(prom_path);
    266 		return (ret);
    267 	}
    268 	/* get the list of aliases (exact and inexact) */
    269 	if ((ret = prom_dev_to_alias(prom_path, options, &alias_list)) < 0) {
    270 		free(prom_path);
    271 		return (ret);
    272 	}
    273 	/* now figure out how big the return array must be */
    274 	if (alias_list != NULL) {
    275 		while (alias_list[count] != NULL) {
    276 			count++;
    277 		}
    278 	}
    279 	if ((options & BOOTDEV_NO_PROM_PATH) == 0) {
    280 		count++;	/* # of slots we will need in prom_list */
    281 	}
    282 	count++;	/* for the null terminator */
    283 
    284 	/* allocate space for the list */
    285 	if ((list = (char **)calloc(count, sizeof (char *))) == NULL) {
    286 		count = 0;
    287 		while ((alias_list) && (alias_list[count] != NULL)) {
    288 			free(alias_list[count]);
    289 			count++;
    290 		}
    291 		free(alias_list);
    292 		free(prom_path);
    293 		return (DEVFS_NOMEM);
    294 	}
    295 	/* fill in the array and free the name list of aliases. */
    296 	count = 0;
    297 	while ((alias_list) && (alias_list[count] != NULL)) {
    298 		list[count] = alias_list[count];
    299 		count++;
    300 	}
    301 	if ((options & BOOTDEV_NO_PROM_PATH) == 0) {
    302 		list[count] = prom_path;
    303 	}
    304 	if (alias_list != NULL) {
    305 		free(alias_list);
    306 	}
    307 	*prom_list = list;
    308 	return (0);
    309 }
    310 
    311 /*
    312  * Get a list prom-path translations for a solaris device.
    313  *
    314  * Returns the number of and all OBP paths and alias variants that
    315  * reference the Solaris device path passed in.
    316  */
    317 int
    318 devfs_get_all_prom_names(const char *solaris_path, uint_t flags,
    319     struct devfs_prom_path **paths)
    320 {
    321 	int ret, len, i, count = 0;
    322 	char *ptr, *prom_path;
    323 	struct devfs_prom_path *cur = NULL, *new;
    324 
    325 	if ((ret = prom_obp_vers()) < 0)
    326 		return (ret);
    327 	if ((prom_path = (char *)malloc(MAXVALSIZE)) == NULL)
    328 		return (DEVFS_NOMEM);
    329 
    330 	if ((ret = devfs_dev_to_prom_names((char *)solaris_path,
    331 	    prom_path, MAXVALSIZE)) < 0) {
    332 		free(prom_path);
    333 		return (ret);
    334 	}
    335 
    336 	for (i = 0; i < ret; i++)
    337 		if (prom_path[i] == '\0')
    338 			count++;
    339 
    340 	*paths = NULL;
    341 	ptr = prom_path;
    342 	for (i = 0; i < count; i++) {
    343 		if ((new = (struct devfs_prom_path *)calloc(
    344 		    sizeof (struct devfs_prom_path), 1)) == NULL) {
    345 			free(prom_path);
    346 			devfs_free_all_prom_names(*paths);
    347 			return (DEVFS_NOMEM);
    348 		}
    349 
    350 		if (cur == NULL)
    351 			*paths = new;
    352 		else
    353 			cur->next = new;
    354 		cur = new;
    355 
    356 		len = strlen(ptr) + 1;
    357 		if ((cur->obp_path = (char *)calloc(len, 1)) == NULL) {
    358 			free(prom_path);
    359 			devfs_free_all_prom_names(*paths);
    360 			return (DEVFS_NOMEM);
    361 		}
    362 
    363 		(void) snprintf(cur->obp_path, len, "%s", ptr);
    364 		ptr += len;
    365 		if ((ret = prom_dev_to_alias(cur->obp_path, flags,
    366 		    &(cur->alias_list))) < 0) {
    367 			free(prom_path);
    368 			devfs_free_all_prom_names(*paths);
    369 			return (ret);
    370 		}
    371 	}
    372 
    373 	free(prom_path);
    374 	return (count);
    375 }
    376 
    377 void
    378 devfs_free_all_prom_names(struct devfs_prom_path *paths)
    379 {
    380 	int i;
    381 
    382 	if (paths == NULL)
    383 		return;
    384 
    385 	devfs_free_all_prom_names(paths->next);
    386 
    387 	if (paths->obp_path != NULL)
    388 		free(paths->obp_path);
    389 
    390 	if (paths->alias_list != NULL) {
    391 		for (i = 0; paths->alias_list[i] != NULL; i++)
    392 			if (paths->alias_list[i] != NULL)
    393 				free(paths->alias_list[i]);
    394 
    395 		free(paths->alias_list);
    396 	}
    397 
    398 	free(paths);
    399 }
    400 
    401 /*
    402  * Accepts a device name as an input argument.  Uses this to set the
    403  * boot-device (or like) variable
    404  *
    405  * By default, this routine prepends to the list and converts the
    406  * logical device name to its most compact prom representation.
    407  * Available options include: converting the device name to a prom
    408  * path name (but not an alias) or performing no conversion at all;
    409  * overwriting the existing contents of boot-device rather than
    410  * prepending.
    411  */
    412 int
    413 devfs_bootdev_set_list(const char *dev_name, const uint_t options)
    414 {
    415 	char *prom_path;
    416 	char *new_bootdev;
    417 	char *ptr;
    418 	char **alias_list = NULL;
    419 	char **prom_list = NULL;
    420 	Oppbuf  oppbuf;
    421 	struct openpromio *opp = &(oppbuf.opp);
    422 	int ret, len, i, j;
    423 
    424 	if (devfs_bootdev_modifiable() != 0) {
    425 		return (DEVFS_NOTSUP);
    426 	}
    427 	if (dev_name == NULL) {
    428 		return (DEVFS_INVAL);
    429 	}
    430 	if (strlen(dev_name) >= MAXPATHLEN)
    431 		return (DEVFS_INVAL);
    432 
    433 	if ((*dev_name != '/') && !(options & BOOTDEV_LITERAL)) {
    434 		return (DEVFS_INVAL);
    435 	}
    436 	if ((options & BOOTDEV_LITERAL) && (options & BOOTDEV_PROMDEV)) {
    437 		return (DEVFS_INVAL);
    438 	}
    439 	/*
    440 	 * if we are prepending, make sure that this obp rev
    441 	 * supports multiple boot device entries.
    442 	 */
    443 	ret = prom_obp_vers();
    444 	if (ret < 0) {
    445 		return (ret);
    446 	}
    447 
    448 	if ((prom_path = (char *)malloc(MAXVALSIZE)) == NULL) {
    449 		return (DEVFS_NOMEM);
    450 	}
    451 	if (options & BOOTDEV_LITERAL) {
    452 		(void) strcpy(prom_path, dev_name);
    453 	} else {
    454 		/* need to convert to prom representation */
    455 		ret = devfs_get_prom_name_list(dev_name, &prom_list);
    456 		if (ret < 0) {
    457 			free(prom_path);
    458 			return (ret);
    459 		}
    460 
    461 		len = MAXVALSIZE;
    462 		i = 0;
    463 		ptr = prom_path;
    464 		while (prom_list && prom_list[i]) {
    465 			if (!(options & BOOTDEV_PROMDEV)) {
    466 				ret = prom_dev_to_alias(prom_list[i], 0,
    467 					&alias_list);
    468 				if (ret < 0) {
    469 					free(prom_path);
    470 					prom_list_free(prom_list);
    471 					return (ret);
    472 				}
    473 				if ((alias_list != NULL) &&
    474 				    (alias_list[0] != NULL)) {
    475 					(void) snprintf(ptr, len, "%s ",
    476 					    alias_list[0]);
    477 					for (ret = 0; alias_list[ret] != NULL;
    478 					    ret++)
    479 						free(alias_list[ret]);
    480 				} else {
    481 					(void) snprintf(ptr, len, "%s ",
    482 					    prom_list[i]);
    483 				}
    484 				if (alias_list != NULL)
    485 					free(alias_list);
    486 			} else {
    487 				(void) snprintf(ptr, len, "%s ", prom_list[i]);
    488 			}
    489 			j = strlen(ptr);
    490 			len -= j;
    491 			ptr += j;
    492 			i++;
    493 		}
    494 		ptr--;
    495 		*ptr = NULL;
    496 
    497 		prom_list_free(prom_list);
    498 	}
    499 	if (options & BOOTDEV_OVERWRITE) {
    500 		new_bootdev = prom_path;
    501 	} else {
    502 		/* retrieve the current value of boot-device */
    503 		ret = get_boot_dev_var(opp);
    504 		if (ret < 0) {
    505 			free(prom_path);
    506 			return (ret);
    507 		}
    508 		/* prepend new entry - deal with duplicates */
    509 		new_bootdev = (char *)malloc(strlen(opp->oprom_array)
    510 		    + strlen(prom_path) + 2);
    511 		if (new_bootdev == NULL) {
    512 			free(prom_path);
    513 			return (DEVFS_NOMEM);
    514 		}
    515 		(void) strcpy(new_bootdev, prom_path);
    516 		if (opp->oprom_size > 0) {
    517 			for (ptr = strtok(opp->oprom_array, " "); ptr != NULL;
    518 			    ptr = strtok(NULL, " ")) {
    519 				/* we strip out duplicates */
    520 				if (strcmp(prom_path, ptr) == 0) {
    521 					continue;
    522 				}
    523 				(void) strcat(new_bootdev, " ");
    524 				(void) strcat(new_bootdev, ptr);
    525 			}
    526 		}
    527 	}
    528 
    529 	/* now set the new value */
    530 	ret = set_boot_dev_var(opp, new_bootdev);
    531 
    532 	if (options & BOOTDEV_OVERWRITE) {
    533 		free(prom_path);
    534 	} else {
    535 		free(new_bootdev);
    536 		free(prom_path);
    537 	}
    538 
    539 	return (ret);
    540 }
    541 
    542 /*
    543  * sets the string bootdev as the new value for boot-device
    544  */
    545 static int
    546 set_boot_dev_var(struct openpromio *opp, char *bootdev)
    547 {
    548 	int prom_fd;
    549 	int i;
    550 	int ret;
    551 	char *valbuf;
    552 	char *save_bootdev;
    553 	char *bootdev_variables[] = {
    554 		"boot-device",
    555 		"bootdev",
    556 		"boot-from",
    557 		NULL
    558 	};
    559 	int found = 0;
    560 	int *ip = (int *)((void *)opp->oprom_array);
    561 
    562 	/* query the prom */
    563 	prom_fd = prom_open(O_RDWR);
    564 	if (prom_fd < 0) {
    565 		return (prom_fd);
    566 	}
    567 
    568 	/* get the diagnostic-mode? property */
    569 	(void) strcpy(opp->oprom_array, "diagnostic-mode?");
    570 	opp->oprom_size = MAXVALSIZE;
    571 	if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
    572 		if ((opp->oprom_size > 0) &&
    573 		    (strcmp(opp->oprom_array, "true") == 0)) {
    574 			prom_close(prom_fd);
    575 			return (DEVFS_ERR);
    576 		}
    577 	}
    578 	/* get the diag-switch? property */
    579 	(void) strcpy(opp->oprom_array, "diag-switch?");
    580 	opp->oprom_size = MAXVALSIZE;
    581 	if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
    582 		if ((opp->oprom_size > 0) &&
    583 		    (strcmp(opp->oprom_array, "true") == 0)) {
    584 			prom_close(prom_fd);
    585 			return (DEVFS_ERR);
    586 		}
    587 	}
    588 	/*
    589 	 * look for one of the following properties in order:
    590 	 *	boot-device
    591 	 *	bootdev
    592 	 *	boot-from
    593 	 *
    594 	 * Use the first one that we find.
    595 	 */
    596 	*ip = 0;
    597 	opp->oprom_size = MAXPROPSIZE;
    598 	while ((opp->oprom_size != 0) && (!found)) {
    599 		opp->oprom_size = MAXPROPSIZE;
    600 		if (ioctl(prom_fd, OPROMNXTOPT, opp) < 0) {
    601 			break;
    602 		}
    603 		for (i = 0; bootdev_variables[i] != NULL; i++) {
    604 			if (strcmp(opp->oprom_array, bootdev_variables[i])
    605 			    == 0) {
    606 				found = 1;
    607 				break;
    608 			}
    609 		}
    610 	}
    611 	if (found) {
    612 		(void) strcpy(opp->oprom_array, bootdev_variables[i]);
    613 		opp->oprom_size = MAXVALSIZE;
    614 		if (ioctl(prom_fd, OPROMGETOPT, opp) < 0) {
    615 			prom_close(prom_fd);
    616 			return (DEVFS_NOTSUP);
    617 		}
    618 	} else {
    619 		prom_close(prom_fd);
    620 		return (DEVFS_NOTSUP);
    621 	}
    622 
    623 	/* save the old copy in case we fail */
    624 	if ((save_bootdev = strdup(opp->oprom_array)) == NULL) {
    625 		prom_close(prom_fd);
    626 		return (DEVFS_NOMEM);
    627 	}
    628 	/* set up the new value of boot-device */
    629 	(void) strcpy(opp->oprom_array, bootdev_variables[i]);
    630 	valbuf = opp->oprom_array + strlen(opp->oprom_array) + 1;
    631 	(void) strcpy(valbuf, bootdev);
    632 
    633 	opp->oprom_size = strlen(valbuf) + strlen(opp->oprom_array) + 2;
    634 
    635 	if (ioctl(prom_fd, OPROMSETOPT, opp) < 0) {
    636 		free(save_bootdev);
    637 		prom_close(prom_fd);
    638 		return (DEVFS_ERR);
    639 	}
    640 
    641 	/*
    642 	 * now read it back to make sure it took
    643 	 */
    644 	(void) strcpy(opp->oprom_array, bootdev_variables[i]);
    645 	opp->oprom_size = MAXVALSIZE;
    646 	if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
    647 		if (_prom_strcmp(opp->oprom_array, bootdev) == 0) {
    648 			/* success */
    649 			free(save_bootdev);
    650 			prom_close(prom_fd);
    651 			return (0);
    652 		}
    653 		/* deal with setting it to "" */
    654 		if ((strlen(bootdev) == 0) && (opp->oprom_size == 0)) {
    655 			/* success */
    656 			free(save_bootdev);
    657 			prom_close(prom_fd);
    658 			return (0);
    659 		}
    660 	}
    661 	/*
    662 	 * something did not take - write out the old value and
    663 	 * hope that we can restore things...
    664 	 *
    665 	 * unfortunately, there is no way for us to differentiate
    666 	 * whether we exceeded the maximum number of characters
    667 	 * allowable.  The limit varies from prom rev to prom
    668 	 * rev, and on some proms, when the limit is
    669 	 * exceeded, whatever was in the
    670 	 * boot-device variable becomes unreadable.
    671 	 *
    672 	 * so if we fail, we will assume we ran out of room.  If we
    673 	 * not able to restore the original setting, then we will
    674 	 * return DEVFS_ERR instead.
    675 	 */
    676 	ret = DEVFS_LIMIT;
    677 	(void) strcpy(opp->oprom_array, bootdev_variables[i]);
    678 	valbuf = opp->oprom_array + strlen(opp->oprom_array) + 1;
    679 	(void) strcpy(valbuf, save_bootdev);
    680 
    681 	opp->oprom_size = strlen(valbuf) + strlen(opp->oprom_array) + 2;
    682 
    683 	if (ioctl(prom_fd, OPROMSETOPT, opp) < 0) {
    684 		ret = DEVFS_ERR;
    685 	}
    686 	free(save_bootdev);
    687 	prom_close(prom_fd);
    688 	return (ret);
    689 }
    690 /*
    691  * retrieve the current value for boot-device
    692  */
    693 static int
    694 get_boot_dev_var(struct openpromio *opp)
    695 {
    696 	int prom_fd;
    697 	int i;
    698 	char *bootdev_variables[] = {
    699 		"boot-device",
    700 		"bootdev",
    701 		"boot-from",
    702 		NULL
    703 	};
    704 	int found = 0;
    705 	int *ip = (int *)((void *)opp->oprom_array);
    706 
    707 	/* query the prom */
    708 	prom_fd = prom_open(O_RDONLY);
    709 	if (prom_fd < 0) {
    710 		return (prom_fd);
    711 	}
    712 
    713 	/* get the diagnostic-mode? property */
    714 	(void) strcpy(opp->oprom_array, "diagnostic-mode?");
    715 	opp->oprom_size = MAXVALSIZE;
    716 	if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
    717 		if ((opp->oprom_size > 0) &&
    718 		    (strcmp(opp->oprom_array, "true") == 0)) {
    719 			prom_close(prom_fd);
    720 			return (DEVFS_ERR);
    721 		}
    722 	}
    723 	/* get the diag-switch? property */
    724 	(void) strcpy(opp->oprom_array, "diag-switch?");
    725 	opp->oprom_size = MAXVALSIZE;
    726 	if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
    727 		if ((opp->oprom_size > 0) &&
    728 		    (strcmp(opp->oprom_array, "true") == 0)) {
    729 			prom_close(prom_fd);
    730 			return (DEVFS_ERR);
    731 		}
    732 	}
    733 	/*
    734 	 * look for one of the following properties in order:
    735 	 *	boot-device
    736 	 *	bootdev
    737 	 *	boot-from
    738 	 *
    739 	 * Use the first one that we find.
    740 	 */
    741 	*ip = 0;
    742 	opp->oprom_size = MAXPROPSIZE;
    743 	while ((opp->oprom_size != 0) && (!found)) {
    744 		opp->oprom_size = MAXPROPSIZE;
    745 		if (ioctl(prom_fd, OPROMNXTOPT, opp) < 0) {
    746 			break;
    747 		}
    748 		for (i = 0; bootdev_variables[i] != NULL; i++) {
    749 			if (strcmp(opp->oprom_array, bootdev_variables[i])
    750 			    == 0) {
    751 				found = 1;
    752 				break;
    753 			}
    754 		}
    755 	}
    756 	if (found) {
    757 		(void) strcpy(opp->oprom_array, bootdev_variables[i]);
    758 		opp->oprom_size = MAXVALSIZE;
    759 		if (ioctl(prom_fd, OPROMGETOPT, opp) < 0) {
    760 			prom_close(prom_fd);
    761 			return (DEVFS_ERR);
    762 		}
    763 		/* boot-device exists but contains nothing */
    764 		if (opp->oprom_size == 0) {
    765 			*opp->oprom_array = '\0';
    766 		}
    767 	} else {
    768 		prom_close(prom_fd);
    769 		return (DEVFS_NOTSUP);
    770 	}
    771 	prom_close(prom_fd);
    772 	return (0);
    773 }
    774 
    775 #ifndef __sparc
    776 static FILE *
    777 open_diskmap(void)
    778 {
    779 	FILE *fp;
    780 	char cmd[PATH_MAX];
    781 
    782 	/* make sure we have a map file */
    783 	fp = fopen(GRUBDISK_MAP, "r");
    784 	if (fp == NULL) {
    785 		(void) snprintf(cmd, sizeof (cmd),
    786 		    "%s > /dev/null", CREATE_DISKMAP);
    787 		(void) system(cmd);
    788 		fp = fopen(GRUBDISK_MAP, "r");
    789 	}
    790 	return (fp);
    791 }
    792 
    793 static int
    794 find_x86_boot_device(struct openpromio *opp)
    795 {
    796 	int ret = DEVFS_ERR;
    797 	char *cp, line[MAXVALSIZE + 6];
    798 	FILE *file;
    799 
    800 	file = open_diskmap();
    801 	if (file == NULL)
    802 		return (DEVFS_ERR);
    803 
    804 	while (fgets(line, MAXVALSIZE + 6, file)) {
    805 		if (strncmp(line, "0 ", 2) != 0)
    806 			continue;
    807 		/* drop new-line */
    808 		line[strlen(line) - 1] = '\0';
    809 		/*
    810 		 * an x86 BIOS only boots a disk, not a partition
    811 		 * or a slice, so hard-code :q (p0)
    812 		 */
    813 		cp = strchr(line + 2, ' ');
    814 		if (cp == NULL)
    815 			break;
    816 		(void) snprintf(opp->oprom_array, MAXVALSIZE,
    817 		    "%s:q", cp + 1);
    818 		opp->oprom_size = MAXVALSIZE;
    819 		ret = 0;
    820 		break;
    821 	}
    822 	(void) fclose(file);
    823 	return (ret);
    824 }
    825 #endif /* ndef __sparc */
    826 
    827 /*
    828  * retrieve the list of entries in the boot-device configuration
    829  * variable.  An array of boot_dev structs will be created, one entry
    830  * for each device name in the boot-device variable.  Each entry
    831  * in the array will contain the logical device representation of the
    832  * boot-device entry, if any.
    833  *
    834  * default_root. if set, is used to locate logical device entries in
    835  * directories other than /dev
    836  */
    837 int
    838 devfs_bootdev_get_list(const char *default_root,
    839 	struct boot_dev ***bootdev_list)
    840 {
    841 	Oppbuf  oppbuf;
    842 	struct openpromio *opp = &(oppbuf.opp);
    843 	int i;
    844 	struct boot_dev **tmp_list;
    845 
    846 	if (default_root == NULL) {
    847 		default_root = "";
    848 	} else if (*default_root != '/') {
    849 		return (DEVFS_INVAL);
    850 	}
    851 
    852 	if (bootdev_list == NULL) {
    853 		return (DEVFS_INVAL);
    854 	}
    855 
    856 	/* get the boot-device variable */
    857 #if defined(sparc)
    858 	i = get_boot_dev_var(opp);
    859 #else
    860 	i = find_x86_boot_device(opp);
    861 #endif
    862 	if (i < 0) {
    863 		return (i);
    864 	}
    865 	/* now try to translate each entry to a logical device. */
    866 	i = process_bootdev(opp->oprom_array, default_root, &tmp_list);
    867 	if (i == 0) {
    868 		*bootdev_list = tmp_list;
    869 		return (0);
    870 	} else {
    871 		return (i);
    872 	}
    873 }
    874 
    875 /*
    876  * loop thru the list of entries in a boot-device configuration
    877  * variable.
    878  */
    879 static int
    880 process_bootdev(const char *bootdevice, const char *default_root,
    881 	struct boot_dev ***list)
    882 {
    883 	int i;
    884 	char *entry, *ptr;
    885 	char prom_path[MAXPATHLEN];
    886 	char ret_buf[MAXPATHLEN];
    887 	struct boot_dev **bootdev_array;
    888 	int num_entries = 0;
    889 	int found = 0;
    890 	int vers;
    891 
    892 	if ((entry = (char *)malloc(strlen(bootdevice) + 1)) == NULL) {
    893 		return (DEVFS_NOMEM);
    894 	}
    895 	/* count the number of entries */
    896 	(void) strcpy(entry, bootdevice);
    897 	for (ptr = strtok(entry, " "); ptr != NULL;
    898 	    ptr = strtok(NULL, " ")) {
    899 		num_entries++;
    900 	}
    901 	(void) strcpy(entry, bootdevice);
    902 
    903 	bootdev_array = (struct boot_dev **)
    904 	    calloc((size_t)num_entries + 1, sizeof (struct boot_dev *));
    905 
    906 	if (bootdev_array == NULL) {
    907 		free(entry);
    908 		return (DEVFS_NOMEM);
    909 	}
    910 
    911 	vers = prom_obp_vers();
    912 	if (vers < 0) {
    913 		free(entry);
    914 		return (vers);
    915 	}
    916 
    917 	/* for each entry in boot-device, do... */
    918 	for (ptr = strtok(entry, " "), i = 0; ptr != NULL;
    919 	    ptr = strtok(NULL, " "), i++) {
    920 
    921 		if ((bootdev_array[i] = alloc_bootdev(ptr)) == NULL) {
    922 			devfs_bootdev_free_list(bootdev_array);
    923 			free(entry);
    924 			return (DEVFS_NOMEM);
    925 		}
    926 
    927 		/*
    928 		 * prom boot-device may be aliased, so we need to do
    929 		 * the necessary prom alias to dev translation.
    930 		 */
    931 		if (*ptr != '/') {
    932 			if (alias_to_prom_dev(ptr, prom_path) < 0) {
    933 				continue;
    934 			}
    935 		} else {
    936 			(void) strcpy(prom_path, ptr);
    937 		}
    938 
    939 		/* now we have a prom device path - convert to a devfs name */
    940 		if (devfs_prom_to_dev_name(prom_path, ret_buf) < 0) {
    941 		    continue;
    942 		}
    943 
    944 		/* append any default minor names necessary */
    945 		if (process_minor_name(ret_buf, default_root) < 0) {
    946 		    continue;
    947 		}
    948 		found = 1;
    949 
    950 		/*
    951 		 * store the physical device path for now - when
    952 		 * we are all done with the entries, we will convert
    953 		 * these to their logical device name equivalents
    954 		 */
    955 		bootdev_array[i]->bootdev_trans[0] = strdup(ret_buf);
    956 	}
    957 	/*
    958 	 * Convert all of the boot-device entries that translated to a
    959 	 * physical device path in /devices to a logical device path
    960 	 * in /dev (note that there may be several logical device paths
    961 	 * associated with a single physical device path - return them all
    962 	 */
    963 	if (found) {
    964 		if (devfs_phys_to_logical(bootdev_array, num_entries,
    965 		    default_root) < 0) {
    966 			devfs_bootdev_free_list(bootdev_array);
    967 			bootdev_array = NULL;
    968 		}
    969 	}
    970 	free(entry);
    971 	*list = bootdev_array;
    972 	return (0);
    973 }
    974 
    975 /*
    976  * We may get a device path from the prom that has no minor name
    977  * information included in it.  Since this device name will not
    978  * correspond directly to a physical device in /devices, we do our
    979  * best to append what the default minor name should be and try this.
    980  *
    981  * For sparc: we append slice 0 (:a).
    982  * For x86: we append fdisk partition 0 (:q).
    983  */
    984 static int
    985 process_minor_name(char *dev_path, const char *root)
    986 {
    987 	char *cp;
    988 #if defined(sparc)
    989 	const char *default_minor_name = "a";
    990 #else
    991 	const char *default_minor_name = "q";
    992 #endif
    993 	int n;
    994 	struct stat stat_buf;
    995 	char path[MAXPATHLEN];
    996 
    997 	(void) snprintf(path, sizeof (path), "%s%s%s", root, DEVICES, dev_path);
    998 	/*
    999 	 * if the device file already exists as given to us, there
   1000 	 * is nothing to do but return.
   1001 	 */
   1002 	if (stat(path, &stat_buf) == 0) {
   1003 		return (0);
   1004 	}
   1005 	/*
   1006 	 * if there is no ':' after the last '/' character, or if there is
   1007 	 * a ':' with no specifier, append the default segment specifier
   1008 	 * ; if there is a ':' followed by a digit, this indicates
   1009 	 * a partition number (which does not map into the /devices name
   1010 	 * space), so strip the number and replace it with the letter
   1011 	 * that represents the partition index
   1012 	 */
   1013 	if ((cp = strrchr(dev_path, '/')) != NULL) {
   1014 		if ((cp = strchr(cp, ':')) == NULL) {
   1015 			(void) strcat(dev_path, ":");
   1016 			(void) strcat(dev_path, default_minor_name);
   1017 		} else if (*++cp == '\0') {
   1018 			(void) strcat(dev_path, default_minor_name);
   1019 		} else if (isdigit(*cp)) {
   1020 			n = atoi(cp);
   1021 			/* make sure to squash the digit */
   1022 			*cp = '\0';
   1023 			switch (n) {
   1024 			    case 0:	(void) strcat(dev_path, "q");
   1025 					break;
   1026 			    case 1:	(void) strcat(dev_path, "r");
   1027 					break;
   1028 			    case 2:	(void) strcat(dev_path, "s");
   1029 					break;
   1030 			    case 3:	(void) strcat(dev_path, "t");
   1031 					break;
   1032 			    case 4:	(void) strcat(dev_path, "u");
   1033 					break;
   1034 			    default:	(void) strcat(dev_path, "a");
   1035 					break;
   1036 			}
   1037 		}
   1038 	}
   1039 	/*
   1040 	 * see if we can find something now.
   1041 	 */
   1042 	(void) snprintf(path, sizeof (path), "%s%s%s", root, DEVICES, dev_path);
   1043 
   1044 	if (stat(path, &stat_buf) == 0) {
   1045 		return (0);
   1046 	} else {
   1047 		return (-1);
   1048 	}
   1049 }
   1050 
   1051 /*
   1052  * for each entry in bootdev_array, convert the physical device
   1053  * representation of the boot-device entry to one or more logical device
   1054  * entries.  We use the hammer method - walk through the logical device
   1055  * name space looking for matches (/dev).  We use nftw to do this.
   1056  */
   1057 static int
   1058 devfs_phys_to_logical(struct boot_dev **bootdev_array, const int array_size,
   1059     const char *default_root)
   1060 {
   1061 	int walk_flags = FTW_PHYS | FTW_MOUNT;
   1062 	char *full_path;
   1063 	struct name_list *list;
   1064 	int count, i;
   1065 	char **dev_name_array;
   1066 	size_t default_root_len;
   1067 	char *dev_dir = DEV;
   1068 	int len;
   1069 
   1070 	if (array_size < 0) {
   1071 		return (-1);
   1072 	}
   1073 
   1074 	if (bootdev_array == NULL) {
   1075 		return (-1);
   1076 	}
   1077 	if (default_root == NULL) {
   1078 		return (-1);
   1079 	}
   1080 	default_root_len = strlen(default_root);
   1081 	if ((default_root_len != 0) && (*default_root != '/')) {
   1082 		return (-1);
   1083 	}
   1084 
   1085 	/* short cut for an empty array */
   1086 	if (*bootdev_array == NULL) {
   1087 		return (0);
   1088 	}
   1089 
   1090 	/* tell nftw where to start (default: /dev) */
   1091 	len = default_root_len + strlen(dev_dir) + 1;
   1092 	if ((full_path = (char *)malloc(len)) == NULL) {
   1093 		return (-1);
   1094 	}
   1095 
   1096 	/*
   1097 	 * if the default root path is terminated with a /, we have to
   1098 	 * make sure we don't end up with one too many slashes in the
   1099 	 * path we are building.
   1100 	 */
   1101 	if ((default_root_len > (size_t)0) &&
   1102 	    (default_root[default_root_len - 1] == '/')) {
   1103 		(void) snprintf(full_path, len, "%s%s", default_root,
   1104 		    &dev_dir[1]);
   1105 	} else {
   1106 		(void) snprintf(full_path, len, "%s%s", default_root, dev_dir);
   1107 	}
   1108 
   1109 	/*
   1110 	 * we need to muck with global data to make nftw work
   1111 	 * so single thread access
   1112 	 */
   1113 	(void) mutex_lock(&dev_lists_lk);
   1114 
   1115 	/*
   1116 	 * set the global vars bootdev_list and dev_list for use by nftw
   1117 	 * dev_list is an array of lists - one for each boot-device
   1118 	 * entry.  The nftw function will create a list of logical device
   1119 	 * entries for each boot-device and put all of the lists in
   1120 	 * dev_list.
   1121 	 */
   1122 	dev_list = (struct name_list **)
   1123 	    calloc(array_size, sizeof (struct name_list *));
   1124 	if (dev_list == NULL) {
   1125 		free(full_path);
   1126 		(void) mutex_unlock(&dev_lists_lk);
   1127 		return (-1);
   1128 	}
   1129 	bootdev_list = bootdev_array;
   1130 
   1131 	if (nftw(full_path, check_logical_dev, FT_DEPTH, walk_flags) == -1) {
   1132 		bootdev_list = NULL;
   1133 		free(full_path);
   1134 		for (i = 0; i < array_size; i++) {
   1135 			free_name_list(dev_list[i], 1);
   1136 		}
   1137 		/* don't free dev_list here because it's been handed off */
   1138 		dev_list = NULL;
   1139 		(void) mutex_unlock(&dev_lists_lk);
   1140 		return (-1);
   1141 	}
   1142 
   1143 	/*
   1144 	 * now we