xref: /onnv-gate/usr/src/lib/libdevinfo/devfsinfo.c (revision 9735:0e6bec37f788)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53337Sas158974  * Common Development and Distribution License (the "License").
63337Sas158974  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*9735SSriman.Bhavanam@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #include <stdio.h>
280Sstevel@tonic-gate #include <string.h>
290Sstevel@tonic-gate #include <strings.h>
300Sstevel@tonic-gate #include <unistd.h>
310Sstevel@tonic-gate #include <stdlib.h>
320Sstevel@tonic-gate #include <thread.h>
330Sstevel@tonic-gate #include <synch.h>
340Sstevel@tonic-gate #include <sys/types.h>
350Sstevel@tonic-gate #include <ctype.h>
360Sstevel@tonic-gate #include <sys/stat.h>
370Sstevel@tonic-gate #include <fcntl.h>
380Sstevel@tonic-gate #include <sys/modctl.h>
390Sstevel@tonic-gate #include <errno.h>
400Sstevel@tonic-gate #include <sys/openpromio.h>
410Sstevel@tonic-gate #include <ftw.h>
420Sstevel@tonic-gate #include <sys/ddi.h>
430Sstevel@tonic-gate #include <sys/sunddi.h>
440Sstevel@tonic-gate #include <limits.h>
450Sstevel@tonic-gate 
460Sstevel@tonic-gate #include "device_info.h"
470Sstevel@tonic-gate 
480Sstevel@tonic-gate /*
490Sstevel@tonic-gate  * #define's
500Sstevel@tonic-gate  */
510Sstevel@tonic-gate 
520Sstevel@tonic-gate /* alias node searching return values */
530Sstevel@tonic-gate #define	NO_MATCH	-1
540Sstevel@tonic-gate #define	EXACT_MATCH	1
550Sstevel@tonic-gate #define	INEXACT_MATCH	2
560Sstevel@tonic-gate 
570Sstevel@tonic-gate /* for prom io operations */
580Sstevel@tonic-gate #define	BUFSIZE		4096
590Sstevel@tonic-gate #define	MAXPROPSIZE	256
600Sstevel@tonic-gate #define	MAXVALSIZE	(BUFSIZE - MAXPROPSIZE - sizeof (uint_t))
610Sstevel@tonic-gate 
623337Sas158974 /* prom_obp_vers() return values */
630Sstevel@tonic-gate #define	OBP_OF			0x4	/* versions OBP 3.x */
643337Sas158974 #define	OBP_NO_ALIAS_NODE	0x8	/* No alias node */
650Sstevel@tonic-gate 
660Sstevel@tonic-gate /* for nftw call */
670Sstevel@tonic-gate #define	FT_DEPTH	15
680Sstevel@tonic-gate 
690Sstevel@tonic-gate /* default logical and physical device name space */
700Sstevel@tonic-gate #define	DEV	"/dev"
710Sstevel@tonic-gate #define	DEVICES	"/devices"
720Sstevel@tonic-gate 
730Sstevel@tonic-gate /* for boot device identification on x86 */
740Sstevel@tonic-gate #define	CREATE_DISKMAP		"/boot/solaris/bin/create_diskmap"
750Sstevel@tonic-gate #define	GRUBDISK_MAP		"/var/run/solaris_grubdisk.map"
760Sstevel@tonic-gate 
770Sstevel@tonic-gate /*
780Sstevel@tonic-gate  * internal structure declarations
790Sstevel@tonic-gate  */
800Sstevel@tonic-gate 
810Sstevel@tonic-gate /* for prom io functions */
820Sstevel@tonic-gate typedef union {
830Sstevel@tonic-gate 	char buf[BUFSIZE];
840Sstevel@tonic-gate 	struct openpromio opp;
850Sstevel@tonic-gate } Oppbuf;
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /* used to manage lists of devices and aliases */
880Sstevel@tonic-gate struct name_list {
890Sstevel@tonic-gate 	char *name;
900Sstevel@tonic-gate 	struct name_list *next;
910Sstevel@tonic-gate };
920Sstevel@tonic-gate 
930Sstevel@tonic-gate /*
940Sstevel@tonic-gate  * internal global data
950Sstevel@tonic-gate  */
960Sstevel@tonic-gate 
970Sstevel@tonic-gate /* global since nftw does not let you pass args to be updated */
980Sstevel@tonic-gate static struct name_list **dev_list;
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate /* global since nftw does not let you pass args to be updated */
1010Sstevel@tonic-gate static struct boot_dev **bootdev_list;
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate /* mutex to protect bootdev_list and dev_list */
1040Sstevel@tonic-gate static mutex_t dev_lists_lk = DEFAULTMUTEX;
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate /*
1070Sstevel@tonic-gate  * internal function prototypes
1080Sstevel@tonic-gate  */
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate static int prom_open(int);
1110Sstevel@tonic-gate static void prom_close(int);
1120Sstevel@tonic-gate static int is_openprom(int);
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate static int prom_dev_to_alias(char *dev, uint_t options, char ***ret_buf);
1153337Sas158974 static int alias_to_prom_dev(char *alias, char *ret_buf);
1160Sstevel@tonic-gate static int prom_srch_aliases_by_def(char *, struct name_list **,
1170Sstevel@tonic-gate     struct name_list **, int);
1183337Sas158974 static int prom_find_aliases_node(int fd);
1190Sstevel@tonic-gate static int prom_compare_devs(char *prom_dev1, char *prom_dev2);
1200Sstevel@tonic-gate static int _prom_strcmp(char *s1, char *s2);
1213337Sas158974 static int prom_srch_node(int fd, char *prop_name, char *ret_buf);
1223337Sas158974 static uint_t prom_next_node(int fd, uint_t node_id);
1233337Sas158974 static uint_t prom_child_node(int fd, uint_t node_id);
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate static int prom_obp_vers(void);
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate static void parse_name(char *, char **, char **, char **);
1280Sstevel@tonic-gate static int process_bootdev(const char *, const char *, struct boot_dev ***);
1290Sstevel@tonic-gate static int process_minor_name(char *dev_path, const char *default_root);
1300Sstevel@tonic-gate static void options_override(char *prom_path, char *alias_name);
1310Sstevel@tonic-gate static int devfs_phys_to_logical(struct boot_dev **bootdev_array,
1320Sstevel@tonic-gate 	const int array_size, const char *default_root);
1330Sstevel@tonic-gate static int check_logical_dev(const char *, const struct stat *, int,
1340Sstevel@tonic-gate 	struct FTW *);
1350Sstevel@tonic-gate static struct boot_dev *alloc_bootdev(char *);
1360Sstevel@tonic-gate static void free_name_list(struct name_list *list, int free_name);
1370Sstevel@tonic-gate static int insert_alias_list(struct name_list **list,
1380Sstevel@tonic-gate 	char *alias_name);
1390Sstevel@tonic-gate static int get_boot_dev_var(struct openpromio *opp);
1400Sstevel@tonic-gate static int set_boot_dev_var(struct openpromio *opp, char *bootdev);
1410Sstevel@tonic-gate static int devfs_prom_to_dev_name(char *prom_path, char *dev_path);
1420Sstevel@tonic-gate static int devfs_dev_to_prom_names(char *dev_path, char *prom_path, size_t len);
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate /*
1450Sstevel@tonic-gate  * frees a list of paths from devfs_get_prom_name_list
1460Sstevel@tonic-gate  */
1470Sstevel@tonic-gate static void
prom_list_free(char ** prom_list)1480Sstevel@tonic-gate prom_list_free(char **prom_list)
1490Sstevel@tonic-gate {
1500Sstevel@tonic-gate 	int i = 0;
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 	if (!prom_list)
1530Sstevel@tonic-gate 		return;
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 	while (prom_list[i]) {
1560Sstevel@tonic-gate 		free(prom_list[i]);
1570Sstevel@tonic-gate 		i++;
1580Sstevel@tonic-gate 	}
1590Sstevel@tonic-gate 	free(prom_list);
1600Sstevel@tonic-gate }
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate static int
devfs_get_prom_name_list(const char * dev_name,char *** prom_list)1630Sstevel@tonic-gate devfs_get_prom_name_list(const char *dev_name, char ***prom_list)
1640Sstevel@tonic-gate {
1650Sstevel@tonic-gate 	char *prom_path = NULL;
1660Sstevel@tonic-gate 	int count = 0;		/* # of slots we will need in prom_list */
1670Sstevel@tonic-gate 	int ret, i, len;
1680Sstevel@tonic-gate 	char **list;
1690Sstevel@tonic-gate 	char *ptr;
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 	if (dev_name == NULL)
1720Sstevel@tonic-gate 		return (DEVFS_INVAL);
1730Sstevel@tonic-gate 	if (*dev_name != '/')
1740Sstevel@tonic-gate 		return (DEVFS_INVAL);
1750Sstevel@tonic-gate 	if (prom_list == NULL)
1760Sstevel@tonic-gate 		return (DEVFS_INVAL);
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 	/*
1790Sstevel@tonic-gate 	 * make sure we are on a machine which supports a prom
1800Sstevel@tonic-gate 	 * and we have permission to use /dev/openprom
1810Sstevel@tonic-gate 	 */
1820Sstevel@tonic-gate 	if ((ret = prom_obp_vers()) < 0)
1830Sstevel@tonic-gate 		return (ret);
1840Sstevel@tonic-gate 	if ((prom_path = (char *)malloc(MAXVALSIZE)) == NULL)
1850Sstevel@tonic-gate 		return (DEVFS_NOMEM);
1860Sstevel@tonic-gate 	/*
1870Sstevel@tonic-gate 	 * get the prom path name
1880Sstevel@tonic-gate 	 */
1890Sstevel@tonic-gate 	ret = devfs_dev_to_prom_names((char *)dev_name, prom_path, MAXVALSIZE);
1900Sstevel@tonic-gate 	if (ret < 0) {
1910Sstevel@tonic-gate 		free(prom_path);
1920Sstevel@tonic-gate 		return (ret);
1930Sstevel@tonic-gate 	}
1940Sstevel@tonic-gate 	/* deal with list of names */
1950Sstevel@tonic-gate 	for (i = 0; i < ret; i++)
1960Sstevel@tonic-gate 		if (prom_path[i] == '\0')
1970Sstevel@tonic-gate 			count++;
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	if ((list = (char **)calloc(count + 1, sizeof (char *))) == NULL) {
2000Sstevel@tonic-gate 		free(prom_path);
2010Sstevel@tonic-gate 		return (DEVFS_NOMEM);
2020Sstevel@tonic-gate 	}
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 	ptr = prom_path;
2050Sstevel@tonic-gate 	for (i = 0; i < count; i++) {
2060Sstevel@tonic-gate 		len = strlen(ptr) + 1;
2070Sstevel@tonic-gate 		if ((list[i] = (char *)malloc(len)) == NULL) {
2080Sstevel@tonic-gate 			free(prom_path);
2090Sstevel@tonic-gate 			free(list);
2100Sstevel@tonic-gate 			return (DEVFS_NOMEM);
2110Sstevel@tonic-gate 		}
2120Sstevel@tonic-gate 		(void) snprintf(list[i], len, "%s", ptr);
2130Sstevel@tonic-gate 		ptr += len;
2140Sstevel@tonic-gate 	}
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate 	free(prom_path);
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	*prom_list = list;
2190Sstevel@tonic-gate 	return (0);
2200Sstevel@tonic-gate }
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate /*
2230Sstevel@tonic-gate  * retrieve the list of prom representations for a given device name
2240Sstevel@tonic-gate  * the list will be sorted in the following order: exact aliases,
2250Sstevel@tonic-gate  * inexact aliases, prom device path name.  If multiple matches occur
2260Sstevel@tonic-gate  * for exact or inexact aliases, then these are sorted in collating
2270Sstevel@tonic-gate  * order. The list is returned in prom_list
2280Sstevel@tonic-gate  *
2290Sstevel@tonic-gate  * the list may be restricted by specifying the correct flags in options.
2300Sstevel@tonic-gate  */
2310Sstevel@tonic-gate int
devfs_get_prom_names(const char * dev_name,uint_t options,char *** prom_list)2320Sstevel@tonic-gate devfs_get_prom_names(const char *dev_name, uint_t options, char ***prom_list)
2330Sstevel@tonic-gate {
2340Sstevel@tonic-gate 	char *prom_path = NULL;
2350Sstevel@tonic-gate 	int count = 0;		/* # of slots we will need in prom_list */
2360Sstevel@tonic-gate 	char **alias_list = NULL;
2370Sstevel@tonic-gate 	char **list;
2380Sstevel@tonic-gate 	int ret;
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	if (dev_name == NULL) {
2410Sstevel@tonic-gate 		return (DEVFS_INVAL);
2420Sstevel@tonic-gate 	}
2430Sstevel@tonic-gate 	if (*dev_name != '/') {
2440Sstevel@tonic-gate 		return (DEVFS_INVAL);
2450Sstevel@tonic-gate 	}
2460Sstevel@tonic-gate 	if (prom_list == NULL) {
2470Sstevel@tonic-gate 		return (DEVFS_INVAL);
2480Sstevel@tonic-gate 	}
2490Sstevel@tonic-gate 	/*
2500Sstevel@tonic-gate 	 * make sure we are on a machine which supports a prom
2510Sstevel@tonic-gate 	 * and we have permission to use /dev/openprom
2520Sstevel@tonic-gate 	 */
2530Sstevel@tonic-gate 	if ((ret = prom_obp_vers()) < 0) {
2540Sstevel@tonic-gate 		return (ret);
2550Sstevel@tonic-gate 	}
2560Sstevel@tonic-gate 	if ((prom_path = (char *)malloc(MAXPATHLEN)) == NULL) {
2570Sstevel@tonic-gate 		return (DEVFS_NOMEM);
2580Sstevel@tonic-gate 	}
2590Sstevel@tonic-gate 	/*
2600Sstevel@tonic-gate 	 * get the prom path name
2610Sstevel@tonic-gate 	 */
2620Sstevel@tonic-gate 	ret = devfs_dev_to_prom_name((char *)dev_name, prom_path);
2630Sstevel@tonic-gate 	if (ret < 0) {
2640Sstevel@tonic-gate 		free(prom_path);
2650Sstevel@tonic-gate 		return (ret);
2660Sstevel@tonic-gate 	}
2670Sstevel@tonic-gate 	/* get the list of aliases (exact and inexact) */
2680Sstevel@tonic-gate 	if ((ret = prom_dev_to_alias(prom_path, options, &alias_list)) < 0) {
2690Sstevel@tonic-gate 		free(prom_path);
2700Sstevel@tonic-gate 		return (ret);
2710Sstevel@tonic-gate 	}
2720Sstevel@tonic-gate 	/* now figure out how big the return array must be */
2730Sstevel@tonic-gate 	if (alias_list != NULL) {
2740Sstevel@tonic-gate 		while (alias_list[count] != NULL) {
2750Sstevel@tonic-gate 			count++;
2760Sstevel@tonic-gate 		}
2770Sstevel@tonic-gate 	}
2780Sstevel@tonic-gate 	if ((options & BOOTDEV_NO_PROM_PATH) == 0) {
2790Sstevel@tonic-gate 		count++;	/* # of slots we will need in prom_list */
2800Sstevel@tonic-gate 	}
2810Sstevel@tonic-gate 	count++;	/* for the null terminator */
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	/* allocate space for the list */
2840Sstevel@tonic-gate 	if ((list = (char **)calloc(count, sizeof (char *))) == NULL) {
2850Sstevel@tonic-gate 		count = 0;
2860Sstevel@tonic-gate 		while ((alias_list) && (alias_list[count] != NULL)) {
2870Sstevel@tonic-gate 			free(alias_list[count]);
2880Sstevel@tonic-gate 			count++;
2890Sstevel@tonic-gate 		}
2900Sstevel@tonic-gate 		free(alias_list);
2910Sstevel@tonic-gate 		free(prom_path);
2920Sstevel@tonic-gate 		return (DEVFS_NOMEM);
2930Sstevel@tonic-gate 	}
2940Sstevel@tonic-gate 	/* fill in the array and free the name list of aliases. */
2950Sstevel@tonic-gate 	count = 0;
2960Sstevel@tonic-gate 	while ((alias_list) && (alias_list[count] != NULL)) {
2970Sstevel@tonic-gate 		list[count] = alias_list[count];
2980Sstevel@tonic-gate 		count++;
2990Sstevel@tonic-gate 	}
3000Sstevel@tonic-gate 	if ((options & BOOTDEV_NO_PROM_PATH) == 0) {
3010Sstevel@tonic-gate 		list[count] = prom_path;
3020Sstevel@tonic-gate 	}
3030Sstevel@tonic-gate 	if (alias_list != NULL) {
3040Sstevel@tonic-gate 		free(alias_list);
3050Sstevel@tonic-gate 	}
3060Sstevel@tonic-gate 	*prom_list = list;
3070Sstevel@tonic-gate 	return (0);
3080Sstevel@tonic-gate }
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate /*
3110Sstevel@tonic-gate  * Get a list prom-path translations for a solaris device.
3120Sstevel@tonic-gate  *
3130Sstevel@tonic-gate  * Returns the number of and all OBP paths and alias variants that
3140Sstevel@tonic-gate  * reference the Solaris device path passed in.
3150Sstevel@tonic-gate  */
3160Sstevel@tonic-gate int
devfs_get_all_prom_names(const char * solaris_path,uint_t flags,struct devfs_prom_path ** paths)3170Sstevel@tonic-gate devfs_get_all_prom_names(const char *solaris_path, uint_t flags,
3180Sstevel@tonic-gate     struct devfs_prom_path **paths)
3190Sstevel@tonic-gate {
3200Sstevel@tonic-gate 	int ret, len, i, count = 0;
3210Sstevel@tonic-gate 	char *ptr, *prom_path;
3220Sstevel@tonic-gate 	struct devfs_prom_path *cur = NULL, *new;
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 	if ((ret = prom_obp_vers()) < 0)
3250Sstevel@tonic-gate 		return (ret);
3260Sstevel@tonic-gate 	if ((prom_path = (char *)malloc(MAXVALSIZE)) == NULL)
3270Sstevel@tonic-gate 		return (DEVFS_NOMEM);
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	if ((ret = devfs_dev_to_prom_names((char *)solaris_path,
3300Sstevel@tonic-gate 	    prom_path, MAXVALSIZE)) < 0) {
3310Sstevel@tonic-gate 		free(prom_path);
3320Sstevel@tonic-gate 		return (ret);
3330Sstevel@tonic-gate 	}
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate 	for (i = 0; i < ret; i++)
3360Sstevel@tonic-gate 		if (prom_path[i] == '\0')
3370Sstevel@tonic-gate 			count++;
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	*paths = NULL;
3400Sstevel@tonic-gate 	ptr = prom_path;
3410Sstevel@tonic-gate 	for (i = 0; i < count; i++) {
3420Sstevel@tonic-gate 		if ((new = (struct devfs_prom_path *)calloc(
3430Sstevel@tonic-gate 		    sizeof (struct devfs_prom_path), 1)) == NULL) {
3440Sstevel@tonic-gate 			free(prom_path);
3450Sstevel@tonic-gate 			devfs_free_all_prom_names(*paths);
3460Sstevel@tonic-gate 			return (DEVFS_NOMEM);
3470Sstevel@tonic-gate 		}
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 		if (cur == NULL)
3500Sstevel@tonic-gate 			*paths = new;
3510Sstevel@tonic-gate 		else
3520Sstevel@tonic-gate 			cur->next = new;
3530Sstevel@tonic-gate 		cur = new;
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 		len = strlen(ptr) + 1;
3560Sstevel@tonic-gate 		if ((cur->obp_path = (char *)calloc(len, 1)) == NULL) {
3570Sstevel@tonic-gate 			free(prom_path);
3580Sstevel@tonic-gate 			devfs_free_all_prom_names(*paths);
3590Sstevel@tonic-gate 			return (DEVFS_NOMEM);
3600Sstevel@tonic-gate 		}
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 		(void) snprintf(cur->obp_path, len, "%s", ptr);
3630Sstevel@tonic-gate 		ptr += len;
3640Sstevel@tonic-gate 		if ((ret = prom_dev_to_alias(cur->obp_path, flags,
3650Sstevel@tonic-gate 		    &(cur->alias_list))) < 0) {
3660Sstevel@tonic-gate 			free(prom_path);
3670Sstevel@tonic-gate 			devfs_free_all_prom_names(*paths);
3680Sstevel@tonic-gate 			return (ret);
3690Sstevel@tonic-gate 		}
3700Sstevel@tonic-gate 	}
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 	free(prom_path);
3730Sstevel@tonic-gate 	return (count);
3740Sstevel@tonic-gate }
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate void
devfs_free_all_prom_names(struct devfs_prom_path * paths)3770Sstevel@tonic-gate devfs_free_all_prom_names(struct devfs_prom_path *paths)
3780Sstevel@tonic-gate {
3790Sstevel@tonic-gate 	int i;
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate 	if (paths == NULL)
3820Sstevel@tonic-gate 		return;
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	devfs_free_all_prom_names(paths->next);
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate 	if (paths->obp_path != NULL)
3870Sstevel@tonic-gate 		free(paths->obp_path);
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 	if (paths->alias_list != NULL) {
3900Sstevel@tonic-gate 		for (i = 0; paths->alias_list[i] != NULL; i++)
3910Sstevel@tonic-gate 			if (paths->alias_list[i] != NULL)
3920Sstevel@tonic-gate 				free(paths->alias_list[i]);
3930Sstevel@tonic-gate 
3940Sstevel@tonic-gate 		free(paths->alias_list);
3950Sstevel@tonic-gate 	}
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 	free(paths);
3980Sstevel@tonic-gate }
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate /*
4010Sstevel@tonic-gate  * Accepts a device name as an input argument.  Uses this to set the
4020Sstevel@tonic-gate  * boot-device (or like) variable
4030Sstevel@tonic-gate  *
4040Sstevel@tonic-gate  * By default, this routine prepends to the list and converts the
4050Sstevel@tonic-gate  * logical device name to its most compact prom representation.
4060Sstevel@tonic-gate  * Available options include: converting the device name to a prom
4070Sstevel@tonic-gate  * path name (but not an alias) or performing no conversion at all;
4080Sstevel@tonic-gate  * overwriting the existing contents of boot-device rather than
4090Sstevel@tonic-gate  * prepending.
4100Sstevel@tonic-gate  */
4110Sstevel@tonic-gate int
devfs_bootdev_set_list(const char * dev_name,const uint_t options)4120Sstevel@tonic-gate devfs_bootdev_set_list(const char *dev_name, const uint_t options)
4130Sstevel@tonic-gate {
4140Sstevel@tonic-gate 	char *prom_path;
4150Sstevel@tonic-gate 	char *new_bootdev;
4160Sstevel@tonic-gate 	char *ptr;
4170Sstevel@tonic-gate 	char **alias_list = NULL;
4180Sstevel@tonic-gate 	char **prom_list = NULL;
4190Sstevel@tonic-gate 	Oppbuf  oppbuf;
4200Sstevel@tonic-gate 	struct openpromio *opp = &(oppbuf.opp);
4210Sstevel@tonic-gate 	int ret, len, i, j;
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 	if (devfs_bootdev_modifiable() != 0) {
4240Sstevel@tonic-gate 		return (DEVFS_NOTSUP);
4250Sstevel@tonic-gate 	}
4260Sstevel@tonic-gate 	if (dev_name == NULL) {
4270Sstevel@tonic-gate 		return (DEVFS_INVAL);
4280Sstevel@tonic-gate 	}
4290Sstevel@tonic-gate 	if (strlen(dev_name) >= MAXPATHLEN)
4300Sstevel@tonic-gate 		return (DEVFS_INVAL);
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 	if ((*dev_name != '/') && !(options & BOOTDEV_LITERAL)) {
4330Sstevel@tonic-gate 		return (DEVFS_INVAL);
4340Sstevel@tonic-gate 	}
4350Sstevel@tonic-gate 	if ((options & BOOTDEV_LITERAL) && (options & BOOTDEV_PROMDEV)) {
4360Sstevel@tonic-gate 		return (DEVFS_INVAL);
4370Sstevel@tonic-gate 	}
4380Sstevel@tonic-gate 	/*
4390Sstevel@tonic-gate 	 * if we are prepending, make sure that this obp rev
4400Sstevel@tonic-gate 	 * supports multiple boot device entries.
4410Sstevel@tonic-gate 	 */
4420Sstevel@tonic-gate 	ret = prom_obp_vers();
4430Sstevel@tonic-gate 	if (ret < 0) {
4440Sstevel@tonic-gate 		return (ret);
4450Sstevel@tonic-gate 	}
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate 	if ((prom_path = (char *)malloc(MAXVALSIZE)) == NULL) {
4480Sstevel@tonic-gate 		return (DEVFS_NOMEM);
4490Sstevel@tonic-gate 	}
4500Sstevel@tonic-gate 	if (options & BOOTDEV_LITERAL) {
4510Sstevel@tonic-gate 		(void) strcpy(prom_path, dev_name);
4520Sstevel@tonic-gate 	} else {
4530Sstevel@tonic-gate 		/* need to convert to prom representation */
4540Sstevel@tonic-gate 		ret = devfs_get_prom_name_list(dev_name, &prom_list);
4550Sstevel@tonic-gate 		if (ret < 0) {
4560Sstevel@tonic-gate 			free(prom_path);
4570Sstevel@tonic-gate 			return (ret);
4580Sstevel@tonic-gate 		}
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate 		len = MAXVALSIZE;
4610Sstevel@tonic-gate 		i = 0;
4620Sstevel@tonic-gate 		ptr = prom_path;
4630Sstevel@tonic-gate 		while (prom_list && prom_list[i]) {
4640Sstevel@tonic-gate 			if (!(options & BOOTDEV_PROMDEV)) {
4650Sstevel@tonic-gate 				ret = prom_dev_to_alias(prom_list[i], 0,
466*9735SSriman.Bhavanam@Sun.COM 				    &alias_list);
4670Sstevel@tonic-gate 				if (ret < 0) {
4680Sstevel@tonic-gate 					free(prom_path);
4690Sstevel@tonic-gate 					prom_list_free(prom_list);
4700Sstevel@tonic-gate 					return (ret);
4710Sstevel@tonic-gate 				}
4720Sstevel@tonic-gate 				if ((alias_list != NULL) &&
4730Sstevel@tonic-gate 				    (alias_list[0] != NULL)) {
4740Sstevel@tonic-gate 					(void) snprintf(ptr, len, "%s ",
4750Sstevel@tonic-gate 					    alias_list[0]);
4760Sstevel@tonic-gate 					for (ret = 0; alias_list[ret] != NULL;
4770Sstevel@tonic-gate 					    ret++)
4780Sstevel@tonic-gate 						free(alias_list[ret]);
4790Sstevel@tonic-gate 				} else {
4800Sstevel@tonic-gate 					(void) snprintf(ptr, len, "%s ",
4810Sstevel@tonic-gate 					    prom_list[i]);
4820Sstevel@tonic-gate 				}
4830Sstevel@tonic-gate 				if (alias_list != NULL)
4840Sstevel@tonic-gate 					free(alias_list);
4850Sstevel@tonic-gate 			} else {
4860Sstevel@tonic-gate 				(void) snprintf(ptr, len, "%s ", prom_list[i]);
4870Sstevel@tonic-gate 			}
4880Sstevel@tonic-gate 			j = strlen(ptr);
4890Sstevel@tonic-gate 			len -= j;
4900Sstevel@tonic-gate 			ptr += j;
4910Sstevel@tonic-gate 			i++;
4920Sstevel@tonic-gate 		}
4930Sstevel@tonic-gate 		ptr--;
4940Sstevel@tonic-gate 		*ptr = NULL;
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 		prom_list_free(prom_list);
4970Sstevel@tonic-gate 	}
4980Sstevel@tonic-gate 	if (options & BOOTDEV_OVERWRITE) {
4990Sstevel@tonic-gate 		new_bootdev = prom_path;
5000Sstevel@tonic-gate 	} else {
5010Sstevel@tonic-gate 		/* retrieve the current value of boot-device */
5020Sstevel@tonic-gate 		ret = get_boot_dev_var(opp);
5030Sstevel@tonic-gate 		if (ret < 0) {
5040Sstevel@tonic-gate 			free(prom_path);
5050Sstevel@tonic-gate 			return (ret);
5060Sstevel@tonic-gate 		}
5070Sstevel@tonic-gate 		/* prepend new entry - deal with duplicates */
5080Sstevel@tonic-gate 		new_bootdev = (char *)malloc(strlen(opp->oprom_array)
5090Sstevel@tonic-gate 		    + strlen(prom_path) + 2);
5100Sstevel@tonic-gate 		if (new_bootdev == NULL) {
5110Sstevel@tonic-gate 			free(prom_path);
5120Sstevel@tonic-gate 			return (DEVFS_NOMEM);
5130Sstevel@tonic-gate 		}
5140Sstevel@tonic-gate 		(void) strcpy(new_bootdev, prom_path);
5150Sstevel@tonic-gate 		if (opp->oprom_size > 0) {
5160Sstevel@tonic-gate 			for (ptr = strtok(opp->oprom_array, " "); ptr != NULL;
5170Sstevel@tonic-gate 			    ptr = strtok(NULL, " ")) {
5180Sstevel@tonic-gate 				/* we strip out duplicates */
5190Sstevel@tonic-gate 				if (strcmp(prom_path, ptr) == 0) {
5200Sstevel@tonic-gate 					continue;
5210Sstevel@tonic-gate 				}
5220Sstevel@tonic-gate 				(void) strcat(new_bootdev, " ");
5230Sstevel@tonic-gate 				(void) strcat(new_bootdev, ptr);
5240Sstevel@tonic-gate 			}
5250Sstevel@tonic-gate 		}
5260Sstevel@tonic-gate 	}
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 	/* now set the new value */
5290Sstevel@tonic-gate 	ret = set_boot_dev_var(opp, new_bootdev);
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 	if (options & BOOTDEV_OVERWRITE) {
5320Sstevel@tonic-gate 		free(prom_path);
5330Sstevel@tonic-gate 	} else {
5340Sstevel@tonic-gate 		free(new_bootdev);
5350Sstevel@tonic-gate 		free(prom_path);
5360Sstevel@tonic-gate 	}
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 	return (ret);
5390Sstevel@tonic-gate }
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate /*
5420Sstevel@tonic-gate  * sets the string bootdev as the new value for boot-device
5430Sstevel@tonic-gate  */
5440Sstevel@tonic-gate static int
set_boot_dev_var(struct openpromio * opp,char * bootdev)5450Sstevel@tonic-gate set_boot_dev_var(struct openpromio *opp, char *bootdev)
5460Sstevel@tonic-gate {
5470Sstevel@tonic-gate 	int prom_fd;
5480Sstevel@tonic-gate 	int i;
5490Sstevel@tonic-gate 	int ret;
5500Sstevel@tonic-gate 	char *valbuf;
5510Sstevel@tonic-gate 	char *save_bootdev;
5520Sstevel@tonic-gate 	char *bootdev_variables[] = {
5530Sstevel@tonic-gate 		"boot-device",
5540Sstevel@tonic-gate 		"bootdev",
5550Sstevel@tonic-gate 		"boot-from",
5560Sstevel@tonic-gate 		NULL
5570Sstevel@tonic-gate 	};
5580Sstevel@tonic-gate 	int found = 0;
5590Sstevel@tonic-gate 	int *ip = (int *)((void *)opp->oprom_array);
5600Sstevel@tonic-gate 
5610Sstevel@tonic-gate 	/* query the prom */
5620Sstevel@tonic-gate 	prom_fd = prom_open(O_RDWR);
5630Sstevel@tonic-gate 	if (prom_fd < 0) {
5640Sstevel@tonic-gate 		return (prom_fd);
5650Sstevel@tonic-gate 	}
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 	/* get the diagnostic-mode? property */
5680Sstevel@tonic-gate 	(void) strcpy(opp->oprom_array, "diagnostic-mode?");
5690Sstevel@tonic-gate 	opp->oprom_size = MAXVALSIZE;
5700Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
5710Sstevel@tonic-gate 		if ((opp->oprom_size > 0) &&
5720Sstevel@tonic-gate 		    (strcmp(opp->oprom_array, "true") == 0)) {
5730Sstevel@tonic-gate 			prom_close(prom_fd);
5740Sstevel@tonic-gate 			return (DEVFS_ERR);
5750Sstevel@tonic-gate 		}
5760Sstevel@tonic-gate 	}
5770Sstevel@tonic-gate 	/* get the diag-switch? property */
5780Sstevel@tonic-gate 	(void) strcpy(opp->oprom_array, "diag-switch?");
5790Sstevel@tonic-gate 	opp->oprom_size = MAXVALSIZE;
5800Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
5810Sstevel@tonic-gate 		if ((opp->oprom_size > 0) &&
5820Sstevel@tonic-gate 		    (strcmp(opp->oprom_array, "true") == 0)) {
5830Sstevel@tonic-gate 			prom_close(prom_fd);
5840Sstevel@tonic-gate 			return (DEVFS_ERR);
5850Sstevel@tonic-gate 		}
5860Sstevel@tonic-gate 	}
5870Sstevel@tonic-gate 	/*
5880Sstevel@tonic-gate 	 * look for one of the following properties in order:
5890Sstevel@tonic-gate 	 *	boot-device
5900Sstevel@tonic-gate 	 *	bootdev
5910Sstevel@tonic-gate 	 *	boot-from
5920Sstevel@tonic-gate 	 *
5930Sstevel@tonic-gate 	 * Use the first one that we find.
5940Sstevel@tonic-gate 	 */
5950Sstevel@tonic-gate 	*ip = 0;
5960Sstevel@tonic-gate 	opp->oprom_size = MAXPROPSIZE;
5970Sstevel@tonic-gate 	while ((opp->oprom_size != 0) && (!found)) {
5980Sstevel@tonic-gate 		opp->oprom_size = MAXPROPSIZE;
5990Sstevel@tonic-gate 		if (ioctl(prom_fd, OPROMNXTOPT, opp) < 0) {
6000Sstevel@tonic-gate 			break;
6010Sstevel@tonic-gate 		}
6020Sstevel@tonic-gate 		for (i = 0; bootdev_variables[i] != NULL; i++) {
6030Sstevel@tonic-gate 			if (strcmp(opp->oprom_array, bootdev_variables[i])
6040Sstevel@tonic-gate 			    == 0) {
6050Sstevel@tonic-gate 				found = 1;
6060Sstevel@tonic-gate 				break;
6070Sstevel@tonic-gate 			}
6080Sstevel@tonic-gate 		}
6090Sstevel@tonic-gate 	}
6100Sstevel@tonic-gate 	if (found) {
6110Sstevel@tonic-gate 		(void) strcpy(opp->oprom_array, bootdev_variables[i]);
6120Sstevel@tonic-gate 		opp->oprom_size = MAXVALSIZE;
6130Sstevel@tonic-gate 		if (ioctl(prom_fd, OPROMGETOPT, opp) < 0) {
6140Sstevel@tonic-gate 			prom_close(prom_fd);
6150Sstevel@tonic-gate 			return (DEVFS_NOTSUP);
6160Sstevel@tonic-gate 		}
6170Sstevel@tonic-gate 	} else {
6180Sstevel@tonic-gate 		prom_close(prom_fd);
6190Sstevel@tonic-gate 		return (DEVFS_NOTSUP);
6200Sstevel@tonic-gate 	}
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	/* save the old copy in case we fail */
6230Sstevel@tonic-gate 	if ((save_bootdev = strdup(opp->oprom_array)) == NULL) {
6240Sstevel@tonic-gate 		prom_close(prom_fd);
6250Sstevel@tonic-gate 		return (DEVFS_NOMEM);
6260Sstevel@tonic-gate 	}
6270Sstevel@tonic-gate 	/* set up the new value of boot-device */
6280Sstevel@tonic-gate 	(void) strcpy(opp->oprom_array, bootdev_variables[i]);
6290Sstevel@tonic-gate 	valbuf = opp->oprom_array + strlen(opp->oprom_array) + 1;
6300Sstevel@tonic-gate 	(void) strcpy(valbuf, bootdev);
6310Sstevel@tonic-gate 
6320Sstevel@tonic-gate 	opp->oprom_size = strlen(valbuf) + strlen(opp->oprom_array) + 2;
6330Sstevel@tonic-gate 
6340Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMSETOPT, opp) < 0) {
6350Sstevel@tonic-gate 		free(save_bootdev);
6360Sstevel@tonic-gate 		prom_close(prom_fd);
6370Sstevel@tonic-gate 		return (DEVFS_ERR);
6380Sstevel@tonic-gate 	}
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	/*
6410Sstevel@tonic-gate 	 * now read it back to make sure it took
6420Sstevel@tonic-gate 	 */
6430Sstevel@tonic-gate 	(void) strcpy(opp->oprom_array, bootdev_variables[i]);
6440Sstevel@tonic-gate 	opp->oprom_size = MAXVALSIZE;
6450Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
6460Sstevel@tonic-gate 		if (_prom_strcmp(opp->oprom_array, bootdev) == 0) {
6470Sstevel@tonic-gate 			/* success */
6480Sstevel@tonic-gate 			free(save_bootdev);
6490Sstevel@tonic-gate 			prom_close(prom_fd);
6500Sstevel@tonic-gate 			return (0);
6510Sstevel@tonic-gate 		}
6520Sstevel@tonic-gate 		/* deal with setting it to "" */
6530Sstevel@tonic-gate 		if ((strlen(bootdev) == 0) && (opp->oprom_size == 0)) {
6540Sstevel@tonic-gate 			/* success */
6550Sstevel@tonic-gate 			free(save_bootdev);
6560Sstevel@tonic-gate 			prom_close(prom_fd);
6570Sstevel@tonic-gate 			return (0);
6580Sstevel@tonic-gate 		}
6590Sstevel@tonic-gate 	}
6600Sstevel@tonic-gate 	/*
6610Sstevel@tonic-gate 	 * something did not take - write out the old value and
6620Sstevel@tonic-gate 	 * hope that we can restore things...
6630Sstevel@tonic-gate 	 *
6640Sstevel@tonic-gate 	 * unfortunately, there is no way for us to differentiate
6650Sstevel@tonic-gate 	 * whether we exceeded the maximum number of characters
6660Sstevel@tonic-gate 	 * allowable.  The limit varies from prom rev to prom
6670Sstevel@tonic-gate 	 * rev, and on some proms, when the limit is
6680Sstevel@tonic-gate 	 * exceeded, whatever was in the
6690Sstevel@tonic-gate 	 * boot-device variable becomes unreadable.
6700Sstevel@tonic-gate 	 *
6710Sstevel@tonic-gate 	 * so if we fail, we will assume we ran out of room.  If we
6720Sstevel@tonic-gate 	 * not able to restore the original setting, then we will
6730Sstevel@tonic-gate 	 * return DEVFS_ERR instead.
6740Sstevel@tonic-gate 	 */
6750Sstevel@tonic-gate 	ret = DEVFS_LIMIT;
6760Sstevel@tonic-gate 	(void) strcpy(opp->oprom_array, bootdev_variables[i]);
6770Sstevel@tonic-gate 	valbuf = opp->oprom_array + strlen(opp->oprom_array) + 1;
6780Sstevel@tonic-gate 	(void) strcpy(valbuf, save_bootdev);
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 	opp->oprom_size = strlen(valbuf) + strlen(opp->oprom_array) + 2;
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMSETOPT, opp) < 0) {
6830Sstevel@tonic-gate 		ret = DEVFS_ERR;
6840Sstevel@tonic-gate 	}
6850Sstevel@tonic-gate 	free(save_bootdev);
6860Sstevel@tonic-gate 	prom_close(prom_fd);
6870Sstevel@tonic-gate 	return (ret);
6880Sstevel@tonic-gate }
6890Sstevel@tonic-gate /*
6900Sstevel@tonic-gate  * retrieve the current value for boot-device
6910Sstevel@tonic-gate  */
6920Sstevel@tonic-gate static int
get_boot_dev_var(struct openpromio * opp)6930Sstevel@tonic-gate get_boot_dev_var(struct openpromio *opp)
6940Sstevel@tonic-gate {
6950Sstevel@tonic-gate 	int prom_fd;
6960Sstevel@tonic-gate 	int i;
6970Sstevel@tonic-gate 	char *bootdev_variables[] = {
6980Sstevel@tonic-gate 		"boot-device",
6990Sstevel@tonic-gate 		"bootdev",
7000Sstevel@tonic-gate 		"boot-from",
7010Sstevel@tonic-gate 		NULL
7020Sstevel@tonic-gate 	};
7030Sstevel@tonic-gate 	int found = 0;
7040Sstevel@tonic-gate 	int *ip = (int *)((void *)opp->oprom_array);
7050Sstevel@tonic-gate 
7060Sstevel@tonic-gate 	/* query the prom */
7070Sstevel@tonic-gate 	prom_fd = prom_open(O_RDONLY);
7080Sstevel@tonic-gate 	if (prom_fd < 0) {
7090Sstevel@tonic-gate 		return (prom_fd);
7100Sstevel@tonic-gate 	}
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate 	/* get the diagnostic-mode? property */
7130Sstevel@tonic-gate 	(void) strcpy(opp->oprom_array, "diagnostic-mode?");
7140Sstevel@tonic-gate 	opp->oprom_size = MAXVALSIZE;
7150Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
7160Sstevel@tonic-gate 		if ((opp->oprom_size > 0) &&
7170Sstevel@tonic-gate 		    (strcmp(opp->oprom_array, "true") == 0)) {
7180Sstevel@tonic-gate 			prom_close(prom_fd);
7190Sstevel@tonic-gate 			return (DEVFS_ERR);
7200Sstevel@tonic-gate 		}
7210Sstevel@tonic-gate 	}
7220Sstevel@tonic-gate 	/* get the diag-switch? property */
7230Sstevel@tonic-gate 	(void) strcpy(opp->oprom_array, "diag-switch?");
7240Sstevel@tonic-gate 	opp->oprom_size = MAXVALSIZE;
7250Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMGETOPT, opp) >= 0) {
7260Sstevel@tonic-gate 		if ((opp->oprom_size > 0) &&
7270Sstevel@tonic-gate 		    (strcmp(opp->oprom_array, "true") == 0)) {
7280Sstevel@tonic-gate 			prom_close(prom_fd);
7290Sstevel@tonic-gate 			return (DEVFS_ERR);
7300Sstevel@tonic-gate 		}
7310Sstevel@tonic-gate 	}
7320Sstevel@tonic-gate 	/*
7330Sstevel@tonic-gate 	 * look for one of the following properties in order:
7340Sstevel@tonic-gate 	 *	boot-device
7350Sstevel@tonic-gate 	 *	bootdev
7360Sstevel@tonic-gate 	 *	boot-from
7370Sstevel@tonic-gate 	 *
7380Sstevel@tonic-gate 	 * Use the first one that we find.
7390Sstevel@tonic-gate 	 */
7400Sstevel@tonic-gate 	*ip = 0;
7410Sstevel@tonic-gate 	opp->oprom_size = MAXPROPSIZE;
7420Sstevel@tonic-gate 	while ((opp->oprom_size != 0) && (!found)) {
7430Sstevel@tonic-gate 		opp->oprom_size = MAXPROPSIZE;
7440Sstevel@tonic-gate 		if (ioctl(prom_fd, OPROMNXTOPT, opp) < 0) {
7450Sstevel@tonic-gate 			break;
7460Sstevel@tonic-gate 		}
7470Sstevel@tonic-gate 		for (i = 0; bootdev_variables[i] != NULL; i++) {
7480Sstevel@tonic-gate 			if (strcmp(opp->oprom_array, bootdev_variables[i])
7490Sstevel@tonic-gate 			    == 0) {
7500Sstevel@tonic-gate 				found = 1;
7510Sstevel@tonic-gate 				break;
7520Sstevel@tonic-gate 			}
7530Sstevel@tonic-gate 		}
7540Sstevel@tonic-gate 	}
7550Sstevel@tonic-gate 	if (found) {
7560Sstevel@tonic-gate 		(void) strcpy(opp->oprom_array, bootdev_variables[i]);
7570Sstevel@tonic-gate 		opp->oprom_size = MAXVALSIZE;
7580Sstevel@tonic-gate 		if (ioctl(prom_fd, OPROMGETOPT, opp) < 0) {
7590Sstevel@tonic-gate 			prom_close(prom_fd);
7600Sstevel@tonic-gate 			return (DEVFS_ERR);
7610Sstevel@tonic-gate 		}
7620Sstevel@tonic-gate 		/* boot-device exists but contains nothing */
7630Sstevel@tonic-gate 		if (opp->oprom_size == 0) {
7640Sstevel@tonic-gate 			*opp->oprom_array = '\0';
7650Sstevel@tonic-gate 		}
7660Sstevel@tonic-gate 	} else {
7670Sstevel@tonic-gate 		prom_close(prom_fd);
7680Sstevel@tonic-gate 		return (DEVFS_NOTSUP);
7690Sstevel@tonic-gate 	}
7700Sstevel@tonic-gate 	prom_close(prom_fd);
7710Sstevel@tonic-gate 	return (0);
7720Sstevel@tonic-gate }
7730Sstevel@tonic-gate 
7740Sstevel@tonic-gate #ifndef __sparc
7750Sstevel@tonic-gate static FILE *
open_diskmap(void)7760Sstevel@tonic-gate open_diskmap(void)
7770Sstevel@tonic-gate {
7780Sstevel@tonic-gate 	FILE *fp;
7790Sstevel@tonic-gate 	char cmd[PATH_MAX];
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	/* make sure we have a map file */
7820Sstevel@tonic-gate 	fp = fopen(GRUBDISK_MAP, "r");
7830Sstevel@tonic-gate 	if (fp == NULL) {
7840Sstevel@tonic-gate 		(void) snprintf(cmd, sizeof (cmd),
7850Sstevel@tonic-gate 		    "%s > /dev/null", CREATE_DISKMAP);
7860Sstevel@tonic-gate 		(void) system(cmd);
7870Sstevel@tonic-gate 		fp = fopen(GRUBDISK_MAP, "r");
7880Sstevel@tonic-gate 	}
7890Sstevel@tonic-gate 	return (fp);
7900Sstevel@tonic-gate }
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate static int
find_x86_boot_device(struct openpromio * opp)7930Sstevel@tonic-gate find_x86_boot_device(struct openpromio *opp)
7940Sstevel@tonic-gate {
7950Sstevel@tonic-gate 	int ret = DEVFS_ERR;
7960Sstevel@tonic-gate 	char *cp, line[MAXVALSIZE + 6];
7970Sstevel@tonic-gate 	FILE *file;
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate 	file = open_diskmap();
8000Sstevel@tonic-gate 	if (file == NULL)
8010Sstevel@tonic-gate 		return (DEVFS_ERR);
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 	while (fgets(line, MAXVALSIZE + 6, file)) {
8040Sstevel@tonic-gate 		if (strncmp(line, "0 ", 2) != 0)
8050Sstevel@tonic-gate 			continue;
8060Sstevel@tonic-gate 		/* drop new-line */
8070Sstevel@tonic-gate 		line[strlen(line) - 1] = '\0';
8080Sstevel@tonic-gate 		/*
8090Sstevel@tonic-gate 		 * an x86 BIOS only boots a disk, not a partition
8100Sstevel@tonic-gate 		 * or a slice, so hard-code :q (p0)
8110Sstevel@tonic-gate 		 */
8120Sstevel@tonic-gate 		cp = strchr(line + 2, ' ');
8130Sstevel@tonic-gate 		if (cp == NULL)
8140Sstevel@tonic-gate 			break;
8150Sstevel@tonic-gate 		(void) snprintf(opp->oprom_array, MAXVALSIZE,
8160Sstevel@tonic-gate 		    "%s:q", cp + 1);
8170Sstevel@tonic-gate 		opp->oprom_size = MAXVALSIZE;
8180Sstevel@tonic-gate 		ret = 0;
8190Sstevel@tonic-gate 		break;
8200Sstevel@tonic-gate 	}
8210Sstevel@tonic-gate 	(void) fclose(file);
8220Sstevel@tonic-gate 	return (ret);
8230Sstevel@tonic-gate }
8240Sstevel@tonic-gate #endif /* ndef __sparc */
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate /*
8270Sstevel@tonic-gate  * retrieve the list of entries in the boot-device configuration
8280Sstevel@tonic-gate  * variable.  An array of boot_dev structs will be created, one entry
8290Sstevel@tonic-gate  * for each device name in the boot-device variable.  Each entry
8300Sstevel@tonic-gate  * in the array will contain the logical device representation of the
8310Sstevel@tonic-gate  * boot-device entry, if any.
8320Sstevel@tonic-gate  *
8330Sstevel@tonic-gate  * default_root. if set, is used to locate logical device entries in
8340Sstevel@tonic-gate  * directories other than /dev
8350Sstevel@tonic-gate  */
8360Sstevel@tonic-gate int
devfs_bootdev_get_list(const char * default_root,struct boot_dev *** bootdev_list)8370Sstevel@tonic-gate devfs_bootdev_get_list(const char *default_root,
8380Sstevel@tonic-gate 	struct boot_dev ***bootdev_list)
8390Sstevel@tonic-gate {
8400Sstevel@tonic-gate 	Oppbuf  oppbuf;
8410Sstevel@tonic-gate 	struct openpromio *opp = &(oppbuf.opp);
8420Sstevel@tonic-gate 	int i;
8430Sstevel@tonic-gate 	struct boot_dev **tmp_list;
8440Sstevel@tonic-gate 
8450Sstevel@tonic-gate 	if (default_root == NULL) {
8460Sstevel@tonic-gate 		default_root = "";
8470Sstevel@tonic-gate 	} else if (*default_root != '/') {
8480Sstevel@tonic-gate 		return (DEVFS_INVAL);
8490Sstevel@tonic-gate 	}
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	if (bootdev_list == NULL) {
8520Sstevel@tonic-gate 		return (DEVFS_INVAL);
8530Sstevel@tonic-gate 	}
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 	/* get the boot-device variable */
8560Sstevel@tonic-gate #if defined(sparc)
8570Sstevel@tonic-gate 	i = get_boot_dev_var(opp);
8580Sstevel@tonic-gate #else
8590Sstevel@tonic-gate 	i = find_x86_boot_device(opp);
8600Sstevel@tonic-gate #endif
8610Sstevel@tonic-gate 	if (i < 0) {
8620Sstevel@tonic-gate 		return (i);
8630Sstevel@tonic-gate 	}
8640Sstevel@tonic-gate 	/* now try to translate each entry to a logical device. */
8650Sstevel@tonic-gate 	i = process_bootdev(opp->oprom_array, default_root, &tmp_list);
8660Sstevel@tonic-gate 	if (i == 0) {
8670Sstevel@tonic-gate 		*bootdev_list = tmp_list;
8680Sstevel@tonic-gate 		return (0);
8690Sstevel@tonic-gate 	} else {
8700Sstevel@tonic-gate 		return (i);
8710Sstevel@tonic-gate 	}
8720Sstevel@tonic-gate }
8730Sstevel@tonic-gate 
8740Sstevel@tonic-gate /*
8750Sstevel@tonic-gate  * loop thru the list of entries in a boot-device configuration
8760Sstevel@tonic-gate  * variable.
8770Sstevel@tonic-gate  */
8780Sstevel@tonic-gate static int
process_bootdev(const char * bootdevice,const char * default_root,struct boot_dev *** list)8790Sstevel@tonic-gate process_bootdev(const char *bootdevice, const char *default_root,
8800Sstevel@tonic-gate 	struct boot_dev ***list)
8810Sstevel@tonic-gate {
8820Sstevel@tonic-gate 	int i;
8830Sstevel@tonic-gate 	char *entry, *ptr;
8840Sstevel@tonic-gate 	char prom_path[MAXPATHLEN];
8850Sstevel@tonic-gate 	char ret_buf[MAXPATHLEN];
8860Sstevel@tonic-gate 	struct boot_dev **bootdev_array;
8870Sstevel@tonic-gate 	int num_entries = 0;
8880Sstevel@tonic-gate 	int found = 0;
8890Sstevel@tonic-gate 	int vers;
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate 	if ((entry = (char *)malloc(strlen(bootdevice) + 1)) == NULL) {
8920Sstevel@tonic-gate 		return (DEVFS_NOMEM);
8930Sstevel@tonic-gate 	}
8940Sstevel@tonic-gate 	/* count the number of entries */
8950Sstevel@tonic-gate 	(void) strcpy(entry, bootdevice);
8960Sstevel@tonic-gate 	for (ptr = strtok(entry, " "); ptr != NULL;
8970Sstevel@tonic-gate 	    ptr = strtok(NULL, " ")) {
8980Sstevel@tonic-gate 		num_entries++;
8990Sstevel@tonic-gate 	}
9000Sstevel@tonic-gate 	(void) strcpy(entry, bootdevice);
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 	bootdev_array = (struct boot_dev **)
9030Sstevel@tonic-gate 	    calloc((size_t)num_entries + 1, sizeof (struct boot_dev *));
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 	if (bootdev_array == NULL) {
9060Sstevel@tonic-gate 		free(entry);
9070Sstevel@tonic-gate 		return (DEVFS_NOMEM);
9080Sstevel@tonic-gate 	}
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate 	vers = prom_obp_vers();
9110Sstevel@tonic-gate 	if (vers < 0) {
9120Sstevel@tonic-gate 		free(entry);
9130Sstevel@tonic-gate 		return (vers);
9140Sstevel@tonic-gate 	}
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 	/* for each entry in boot-device, do... */
9170Sstevel@tonic-gate 	for (ptr = strtok(entry, " "), i = 0; ptr != NULL;
9180Sstevel@tonic-gate 	    ptr = strtok(NULL, " "), i++) {
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate 		if ((bootdev_array[i] = alloc_bootdev(ptr)) == NULL) {
9210Sstevel@tonic-gate 			devfs_bootdev_free_list(bootdev_array);
9220Sstevel@tonic-gate 			free(entry);
9230Sstevel@tonic-gate 			return (DEVFS_NOMEM);
9240Sstevel@tonic-gate 		}
9250Sstevel@tonic-gate 
9263337Sas158974 		/*
9273337Sas158974 		 * prom boot-device may be aliased, so we need to do
9283337Sas158974 		 * the necessary prom alias to dev translation.
9293337Sas158974 		 */
9303337Sas158974 		if (*ptr != '/') {
9313337Sas158974 			if (alias_to_prom_dev(ptr, prom_path) < 0) {
9323337Sas158974 				continue;
9333337Sas158974 			}
9343337Sas158974 		} else {
9353337Sas158974 			(void) strcpy(prom_path, ptr);
9363337Sas158974 		}
9373337Sas158974 
9380Sstevel@tonic-gate 		/* now we have a prom device path - convert to a devfs name */
9390Sstevel@tonic-gate 		if (devfs_prom_to_dev_name(prom_path, ret_buf) < 0) {
940*9735SSriman.Bhavanam@Sun.COM 			continue;
9410Sstevel@tonic-gate 		}
9423337Sas158974 
9430Sstevel@tonic-gate 		/* append any default minor names necessary */
9440Sstevel@tonic-gate 		if (process_minor_name(ret_buf, default_root) < 0) {
945*9735SSriman.Bhavanam@Sun.COM 			continue;
9460Sstevel@tonic-gate 		}
9473337Sas158974 		found = 1;
9480Sstevel@tonic-gate 
9490Sstevel@tonic-gate 		/*
9500Sstevel@tonic-gate 		 * store the physical device path for now - when
9510Sstevel@tonic-gate 		 * we are all done with the entries, we will convert
9520Sstevel@tonic-gate 		 * these to their logical device name equivalents
9530Sstevel@tonic-gate 		 */
9540Sstevel@tonic-gate 		bootdev_array[i]->bootdev_trans[0] = strdup(ret_buf);
9550Sstevel@tonic-gate 	}
9560Sstevel@tonic-gate 	/*
9570Sstevel@tonic-gate 	 * Convert all of the boot-device entries that translated to a
9580Sstevel@tonic-gate 	 * physical device path in /devices to a logical device path
9590Sstevel@tonic-gate 	 * in /dev (note that there may be several logical device paths
9600Sstevel@tonic-gate 	 * associated with a single physical device path - return them all
9610Sstevel@tonic-gate 	 */
9620Sstevel@tonic-gate 	if (found) {
9630Sstevel@tonic-gate 		if (devfs_phys_to_logical(bootdev_array, num_entries,
9640Sstevel@tonic-gate 		    default_root) < 0) {
9650Sstevel@tonic-gate 			devfs_bootdev_free_list(bootdev_array);
9660Sstevel@tonic-gate 			bootdev_array = NULL;
9670Sstevel@tonic-gate 		}
9680Sstevel@tonic-gate 	}
9690Sstevel@tonic-gate 	free(entry);
9700Sstevel@tonic-gate 	*list = bootdev_array;
9710Sstevel@tonic-gate 	return (0);
9720Sstevel@tonic-gate }
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate /*
9750Sstevel@tonic-gate  * We may get a device path from the prom that has no minor name
9760Sstevel@tonic-gate  * information included in it.  Since this device name will not
9770Sstevel@tonic-gate  * correspond directly to a physical device in /devices, we do our
9780Sstevel@tonic-gate  * best to append what the default minor name should be and try this.
9790Sstevel@tonic-gate  *
9800Sstevel@tonic-gate  * For sparc: we append slice 0 (:a).
9810Sstevel@tonic-gate  * For x86: we append fdisk partition 0 (:q).
9820Sstevel@tonic-gate  */
9830Sstevel@tonic-gate static int
process_minor_name(char * dev_path,const char * root)9840Sstevel@tonic-gate process_minor_name(char *dev_path, const char *root)
9850Sstevel@tonic-gate {
9860Sstevel@tonic-gate 	char *cp;
9870Sstevel@tonic-gate #if defined(sparc)
9880Sstevel@tonic-gate 	const char *default_minor_name = "a";
9890Sstevel@tonic-gate #else
9900Sstevel@tonic-gate 	const char *default_minor_name = "q";
9910Sstevel@tonic-gate #endif
9920Sstevel@tonic-gate 	int n;
9930Sstevel@tonic-gate 	struct stat stat_buf;
9940Sstevel@tonic-gate 	char path[MAXPATHLEN];
9950Sstevel@tonic-gate 
9960Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s%s", root, DEVICES, dev_path);
9970Sstevel@tonic-gate 	/*
9980Sstevel@tonic-gate 	 * if the device file already exists as given to us, there
9990Sstevel@tonic-gate 	 * is nothing to do but return.
10000Sstevel@tonic-gate 	 */
10010Sstevel@tonic-gate 	if (stat(path, &stat_buf) == 0) {
10020Sstevel@tonic-gate 		return (0);
10030Sstevel@tonic-gate 	}
10040Sstevel@tonic-gate 	/*
10050Sstevel@tonic-gate 	 * if there is no ':' after the last '/' character, or if there is
10060Sstevel@tonic-gate 	 * a ':' with no specifier, append the default segment specifier
10070Sstevel@tonic-gate 	 * ; if there is a ':' followed by a digit, this indicates
10080Sstevel@tonic-gate 	 * a partition number (which does not map into the /devices name
10090Sstevel@tonic-gate 	 * space), so strip the number and replace it with the letter
10100Sstevel@tonic-gate 	 * that represents the partition index
10110Sstevel@tonic-gate 	 */
10120Sstevel@tonic-gate 	if ((cp = strrchr(dev_path, '/')) != NULL) {
10130Sstevel@tonic-gate 		if ((cp = strchr(cp, ':')) == NULL) {
10140Sstevel@tonic-gate 			(void) strcat(dev_path, ":");
10150Sstevel@tonic-gate 			(void) strcat(dev_path, default_minor_name);
10160Sstevel@tonic-gate 		} else if (*++cp == '\0') {
10170Sstevel@tonic-gate 			(void) strcat(dev_path, default_minor_name);
10180Sstevel@tonic-gate 		} else if (isdigit(*cp)) {
10190Sstevel@tonic-gate 			n = atoi(cp);
10200Sstevel@tonic-gate 			/* make sure to squash the digit */
10210Sstevel@tonic-gate 			*cp = '\0';
10220Sstevel@tonic-gate 			switch (n) {
1023*9735SSriman.Bhavanam@Sun.COM 			case 0:	(void) strcat(dev_path, "q");
10240Sstevel@tonic-gate 					break;
1025*9735SSriman.Bhavanam@Sun.COM 			case 1:	(void) strcat(dev_path, "r");
10260Sstevel@tonic-gate 					break;
1027*9735SSriman.Bhavanam@Sun.COM 			case 2:	(void) strcat(dev_path, "s");
10280Sstevel@tonic-gate 					break;
1029*9735SSriman.Bhavanam@Sun.COM 			case 3:	(void) strcat(dev_path, "t");
10300Sstevel@tonic-gate 					break;
1031*9735SSriman.Bhavanam@Sun.COM 			case 4:	(void) strcat(dev_path, "u");
10320Sstevel@tonic-gate 					break;
1033*9735SSriman.Bhavanam@Sun.COM 			default: (void) strcat(dev_path, "a");
10340Sstevel@tonic-gate 					break;
10350Sstevel@tonic-gate 			}
10360Sstevel@tonic-gate 		}
10370Sstevel@tonic-gate 	}
10380Sstevel@tonic-gate 	/*
10390Sstevel@tonic-gate 	 * see if we can find something now.
10400Sstevel@tonic-gate 	 */
10410Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "%s%s%s", root, DEVICES, dev_path);
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 	if (stat(path, &stat_buf) == 0) {
10440Sstevel@tonic-gate 		return (0);
10450Sstevel@tonic-gate 	} else {
10460Sstevel@tonic-gate 		return (-1);
10470Sstevel@tonic-gate 	}
10480Sstevel@tonic-gate }
10490Sstevel@tonic-gate 
10500Sstevel@tonic-gate /*
10510Sstevel@tonic-gate  * for each entry in bootdev_array, convert the physical device
10520Sstevel@tonic-gate  * representation of the boot-device entry to one or more logical device
10530Sstevel@tonic-gate  * entries.  We use the hammer method - walk through the logical device
10540Sstevel@tonic-gate  * name space looking for matches (/dev).  We use nftw to do this.
10550Sstevel@tonic-gate  */
10560Sstevel@tonic-gate static int
devfs_phys_to_logical(struct boot_dev ** bootdev_array,const int array_size,const char * default_root)10570Sstevel@tonic-gate devfs_phys_to_logical(struct boot_dev **bootdev_array, const int array_size,
10580Sstevel@tonic-gate     const char *default_root)
10590Sstevel@tonic-gate {
10600Sstevel@tonic-gate 	int walk_flags = FTW_PHYS | FTW_MOUNT;
10610Sstevel@tonic-gate 	char *full_path;
10620Sstevel@tonic-gate 	struct name_list *list;
10630Sstevel@tonic-gate 	int count, i;
10640Sstevel@tonic-gate 	char **dev_name_array;
10650Sstevel@tonic-gate 	size_t default_root_len;
10660Sstevel@tonic-gate 	char *dev_dir = DEV;
10670Sstevel@tonic-gate 	int len;
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 	if (array_size < 0) {
10700Sstevel@tonic-gate 		return (-1);
10710Sstevel@tonic-gate 	}
10720Sstevel@tonic-gate 
10730Sstevel@tonic-gate 	if (bootdev_array == NULL) {
10740Sstevel@tonic-gate 		return (-1);
10750Sstevel@tonic-gate 	}
10760Sstevel@tonic-gate 	if (default_root == NULL) {
10770Sstevel@tonic-gate 		return (-1);
10780Sstevel@tonic-gate 	}
10790Sstevel@tonic-gate 	default_root_len = strlen(default_root);
10800Sstevel@tonic-gate 	if ((default_root_len != 0) && (*default_root != '/')) {
10810Sstevel@tonic-gate 		return (-1);
10820Sstevel@tonic-gate 	}
10833337Sas158974 
10840Sstevel@tonic-gate 	/* short cut for an empty array */
10850Sstevel@tonic-gate 	if (*bootdev_array == NULL) {
10860Sstevel@tonic-gate 		return (0);
10870Sstevel@tonic-gate 	}
10880Sstevel@tonic-gate 
10890Sstevel@tonic-gate 	/* tell nftw where to start (default: /dev) */
10900Sstevel@tonic-gate 	len = default_root_len + strlen(dev_dir) + 1;
10910Sstevel@tonic-gate 	if ((full_path = (char *)malloc(len)) == NULL) {
10920Sstevel@tonic-gate 		return (-1);
10930Sstevel@tonic-gate 	}
10943337Sas158974 
10950Sstevel@tonic-gate 	/*
10960Sstevel@tonic-gate 	 * if the default root path is terminated with a /, we have to
10970Sstevel@tonic-gate 	 * make sure we don't end up with one too many slashes in the
10980Sstevel@tonic-gate 	 * path we are building.
10990Sstevel@tonic-gate 	 */
11000Sstevel@tonic-gate 	if ((default_root_len > (size_t)0) &&
11010Sstevel@tonic-gate 	    (default_root[default_root_len - 1] == '/')) {
11020Sstevel@tonic-gate 		(void) snprintf(full_path, len, "%s%s", default_root,
11030Sstevel@tonic-gate 		    &dev_dir[1]);
11040Sstevel@tonic-gate 	} else {
11050Sstevel@tonic-gate 		(void) snprintf(full_path, len, "%s%s", default_root, dev_dir);
11060Sstevel@tonic-gate 	}
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate 	/*
11090Sstevel@tonic-gate 	 * we need to muck with global data to make nftw work
11100Sstevel@tonic-gate 	 * so single thread access
11110Sstevel@tonic-gate 	 */
11120Sstevel@tonic-gate 	(void) mutex_lock(&dev_lists_lk);
11130Sstevel@tonic-gate 
11140Sstevel@tonic-gate 	/*
11150Sstevel@tonic-gate 	 * set the global vars bootdev_list and dev_list for use by nftw
11160Sstevel@tonic-gate 	 * dev_list is an array of lists - one for each boot-device
11170Sstevel@tonic-gate 	 * entry.  The nftw function will create a list of logical device
11180Sstevel@tonic-gate 	 * entries for each boot-device and put all of the lists in
11190Sstevel@tonic-gate 	 * dev_list.
11200Sstevel@tonic-gate 	 */
11210Sstevel@tonic-gate 	dev_list = (struct name_list **)
11220Sstevel@tonic-gate 	    calloc(array_size, sizeof (struct name_list *));
11230Sstevel@tonic-gate 	if (dev_list == NULL) {
11240Sstevel@tonic-gate 		free(full_path);
11250Sstevel@tonic-gate 		(void) mutex_unlock(&dev_lists_lk);
11260Sstevel@tonic-gate 		return (-1);
11270Sstevel@tonic-gate 	}
11280Sstevel@tonic-gate 	bootdev_list = bootdev_array;
11290Sstevel@tonic-gate 
11300Sstevel@tonic-gate 	if (nftw(full_path, check_logical_dev, FT_DEPTH, walk_flags) == -1) {
11310Sstevel@tonic-gate 		bootdev_list = NULL;
11320Sstevel@tonic-gate 		free(full_path);
11330Sstevel@tonic-gate 		for (i = 0; i < array_size; i++) {
11340Sstevel@tonic-gate 			free_name_list(dev_list[i], 1);
11350Sstevel@tonic-gate 		}
11360Sstevel@tonic-gate 		/* don't free dev_list here because it's been handed off */
11370Sstevel@tonic-gate 		dev_list = NULL;
11380Sstevel@tonic-gate 		(void) mutex_unlock(&dev_lists_lk);
11390Sstevel@tonic-gate 		return (-1);
11400Sstevel@tonic-gate 	}
11413337Sas158974 
11420Sstevel@tonic-gate 	/*
11430Sstevel@tonic-gate 	 * now we have a filled in dev_list.  So for each logical device
11440Sstevel@tonic-gate 	 * list in dev_list, count the number of entries in the list,
11450Sstevel@tonic-gate 	 * create an array of strings of logical devices, and save in the
11460Sstevel@tonic-gate 	 * corresponding boot_dev structure.
11470Sstevel@tonic-gate 	 */
11480Sstevel@tonic-gate 	for (i = 0; i < array_size; i++) {
11490Sstevel@tonic-gate 		/* get the next list */
11500Sstevel@tonic-gate 		list = dev_list[i];
11510Sstevel@tonic-gate 		count = 0;
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 		/* count the number of entries in the list */
11540Sstevel@tonic-gate 		while (list != NULL) {
11550Sstevel@tonic-gate 			count++;
11560Sstevel@tonic-gate 			list = list->next;
11570Sstevel@tonic-gate 		}
11580Sstevel@tonic-gate 		if ((dev_name_array =
11590Sstevel@tonic-gate 		    (char **)malloc((count + 1) * sizeof (char *)))
11600Sstevel@tonic-gate 		    == NULL) {
11610Sstevel@tonic-gate 			continue;
11620Sstevel@tonic-gate 		}
11630Sstevel@tonic-gate 		list = dev_list[i];
11640Sstevel@tonic-gate 		count = 0;
11650Sstevel@tonic-gate 
11660Sstevel@tonic-gate 		/* fill in the array */
11670Sstevel@tonic-gate 		while (list != NULL) {
11680Sstevel@tonic-gate 			dev_name_array[count] = list->name;
11690Sstevel@tonic-gate 			count++;
11700Sstevel@tonic-gate 			list = list->next;
11710Sstevel@tonic-gate 		}
11723337Sas158974 
11730Sstevel@tonic-gate 		/*
11740Sstevel@tonic-gate 		 * null terminate the array
11750Sstevel@tonic-gate 		 */
11760Sstevel@tonic-gate 		dev_name_array[count] = NULL;
11773337Sas158974 		if ((bootdev_array[i] != NULL) && (bootdev_array[i]->
11783337Sas158974 		    bootdev_trans[0] != NULL)) {
11790Sstevel@tonic-gate 			free(bootdev_array[i]->bootdev_trans[0]);
11800Sstevel@tonic-gate 		}
11813337Sas158974 		if (bootdev_array[i] != NULL) {
11823337Sas158974 			free(bootdev_array[i]->bootdev_trans);
11833337Sas158974 			bootdev_array[i]->bootdev_trans = dev_name_array;
11843337Sas158974 		}
11850Sstevel@tonic-gate 	}
11860Sstevel@tonic-gate 	bootdev_list = NULL;
11870Sstevel@tonic-gate 	free(full_path);
11880Sstevel@tonic-gate 	for (i = 0; i < array_size; i++) {
11890Sstevel@tonic-gate 		free_name_list(dev_list[i], 0);
11900Sstevel@tonic-gate 	}
11910Sstevel@tonic-gate 	free(dev_list);
11920Sstevel@tonic-gate 	dev_list = NULL;
11930Sstevel@tonic-gate 	(void) mutex_unlock(&dev_lists_lk);
11940Sstevel@tonic-gate 	return (0);
11950Sstevel@tonic-gate }
11960Sstevel@tonic-gate /*
11970Sstevel@tonic-gate  * nftw function
11980Sstevel@tonic-gate  * for a logical dev entry, it walks the list of boot-devices and
11990Sstevel@tonic-gate  * sees if there are any matches.  If so, it saves the logical device
12000Sstevel@tonic-gate  * name off in the appropriate list in dev_list
12010Sstevel@tonic-gate  */
12020Sstevel@tonic-gate /* ARGSUSED */
12030Sstevel@tonic-gate static int
check_logical_dev(const char * node,const struct stat * node_stat,int flags,struct FTW * ftw_info)12040Sstevel@tonic-gate check_logical_dev(const char *node, const struct stat *node_stat, int flags,
12050Sstevel@tonic-gate 	struct FTW *ftw_info)
12060Sstevel@tonic-gate {
12070Sstevel@tonic-gate 	char link_buf[MAXPATHLEN];
12080Sstevel@tonic-gate 	int link_buf_len;
12090Sstevel@tonic-gate 	char *name;
12100Sstevel@tonic-gate 	struct name_list *dev;
12110Sstevel@tonic-gate 	char *physdev;
12120Sstevel@tonic-gate 	int i;
12130Sstevel@tonic-gate 
12140Sstevel@tonic-gate 	if (flags != FTW_SL) {
12150Sstevel@tonic-gate 		return (0);
12160Sstevel@tonic-gate 	}
12170Sstevel@tonic-gate 
12180Sstevel@tonic-gate 	if ((link_buf_len = readlink(node, (void *)link_buf, MAXPATHLEN))
12190Sstevel@tonic-gate 	    == -1) {
12200Sstevel@tonic-gate 		return (0);
12210Sstevel@tonic-gate 	}
12220Sstevel@tonic-gate 	link_buf[link_buf_len] = '\0';
12230Sstevel@tonic-gate 	if ((name = strstr(link_buf, DEVICES)) == NULL) {
12240Sstevel@tonic-gate 		return (0);
12250Sstevel@tonic-gate 	}
12260Sstevel@tonic-gate 	name = (char *)(name + strlen(DEVICES));
12270Sstevel@tonic-gate 
12280Sstevel@tonic-gate 	for (i = 0; bootdev_list[i] != NULL; i++) {
12290Sstevel@tonic-gate 		if (bootdev_list[i]->bootdev_trans[0] == NULL) {
12300Sstevel@tonic-gate 			continue;
12310Sstevel@tonic-gate 		}
12320Sstevel@tonic-gate 		/*
12330Sstevel@tonic-gate 		 * compare the contents of the link with the physical
12340Sstevel@tonic-gate 		 * device representation of this boot device
12350Sstevel@tonic-gate 		 */
12360Sstevel@tonic-gate 		physdev = bootdev_list[i]->bootdev_trans[0];
12370Sstevel@tonic-gate 		if ((strcmp(name, physdev) == 0) &&
12380Sstevel@tonic-gate 		    (strlen(name) == strlen(physdev))) {
12390Sstevel@tonic-gate 			if ((dev = (struct name_list *)
12400Sstevel@tonic-gate 			    malloc(sizeof (struct name_list))) == NULL) {
12410Sstevel@tonic-gate 				return (-1);
12420Sstevel@tonic-gate 			}
12430Sstevel@tonic-gate 			if ((dev->name = strdup(node)) == NULL) {
12440Sstevel@tonic-gate 				free(dev);
12450Sstevel@tonic-gate 				return (-1);
12460Sstevel@tonic-gate 			}
12470Sstevel@tonic-gate 			if (dev_list[i] == NULL) {
12480Sstevel@tonic-gate 				dev_list[i] = dev;
12490Sstevel@tonic-gate 				dev_list[i]->next = NULL;
12500Sstevel@tonic-gate 			} else {
12510Sstevel@tonic-gate 				dev->next = dev_list[i];
12520Sstevel@tonic-gate 				dev_list[i] = dev;
12530Sstevel@tonic-gate 			}
12540Sstevel@tonic-gate 		}
12550Sstevel@tonic-gate 	}
12560Sstevel@tonic-gate 	return (0);
12570Sstevel@tonic-gate }
12580Sstevel@tonic-gate 
12590Sstevel@tonic-gate /*
12600Sstevel@tonic-gate  * frees a list of boot_dev struct pointers
12610Sstevel@tonic-gate  */
12620Sstevel@tonic-gate void
devfs_bootdev_free_list(struct boot_dev ** array)12630Sstevel@tonic-gate devfs_bootdev_free_list(struct boot_dev **array)
12640Sstevel@tonic-gate {
12650Sstevel@tonic-gate 	int i = 0;
12660Sstevel@tonic-gate 	int j;
12670Sstevel@tonic-gate 
12680Sstevel@tonic-gate 	if (array == NULL) {
12690Sstevel@tonic-gate 		return;
12700Sstevel@tonic-gate 	}
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate 	while (array[i] != NULL) {
12730Sstevel@tonic-gate 		free(array[i]->bootdev_element);
12740Sstevel@tonic-gate 		j = 0;
12750Sstevel@tonic-gate 		while (array[i]->bootdev_trans[j] != NULL) {
12760Sstevel@tonic-gate 			free(array[i]->bootdev_trans[j++]);
12770Sstevel@tonic-gate 		}
12780Sstevel@tonic-gate 		free(array[i]->bootdev_trans);
12790Sstevel@tonic-gate 		free(array[i]);
12800Sstevel@tonic-gate 		i++;
12810Sstevel@tonic-gate 	}
12820Sstevel@tonic-gate 	free(array);
12830Sstevel@tonic-gate }
12840Sstevel@tonic-gate /*
12850Sstevel@tonic-gate  * allocates a boot_dev struct and fills in the bootdev_element portion
12860Sstevel@tonic-gate  */
12870Sstevel@tonic-gate static struct boot_dev *
alloc_bootdev(char * entry_name)12880Sstevel@tonic-gate alloc_bootdev(char *entry_name)
12890Sstevel@tonic-gate {
12900Sstevel@tonic-gate 	struct boot_dev *entry;
12910Sstevel@tonic-gate 
12920Sstevel@tonic-gate 	entry = (struct boot_dev *)calloc(1, sizeof (struct boot_dev));
12930Sstevel@tonic-gate 
12940Sstevel@tonic-gate 	if (entry == NULL) {
12950Sstevel@tonic-gate 		return (NULL);
12960Sstevel@tonic-gate 	}
12970Sstevel@tonic-gate 	if ((entry->bootdev_element = strdup(entry_name)) == NULL) {
12980Sstevel@tonic-gate 		free(entry);
12990Sstevel@tonic-gate 		return (NULL);
13000Sstevel@tonic-gate 	}
13010Sstevel@tonic-gate 	/*
13020Sstevel@tonic-gate 	 * Allocate room for 1 name and a null terminator - the caller of
13030Sstevel@tonic-gate 	 * this function will need the first slot right away.
13040Sstevel@tonic-gate 	 */
13050Sstevel@tonic-gate 	if ((entry->bootdev_trans = (char **)calloc(2, sizeof (char *)))
13060Sstevel@tonic-gate 	    == NULL) {
13070Sstevel@tonic-gate 		free(entry->bootdev_element);
13080Sstevel@tonic-gate 		free(entry);
13090Sstevel@tonic-gate 		return (NULL);
13100Sstevel@tonic-gate 	}
13110Sstevel@tonic-gate 	return (entry);
13120Sstevel@tonic-gate }
13130Sstevel@tonic-gate 
13140Sstevel@tonic-gate /*
13150Sstevel@tonic-gate  * will come back with a concatenated list of paths
13160Sstevel@tonic-gate  */
13170Sstevel@tonic-gate int
devfs_dev_to_prom_names(char * dev_path,char * prom_path,size_t len)13180Sstevel@tonic-gate devfs_dev_to_prom_names(char *dev_path, char *prom_path, size_t len)
13190Sstevel@tonic-gate {
13200Sstevel@tonic-gate 	Oppbuf oppbuf;
13210Sstevel@tonic-gate 	struct openpromio *opp = &(oppbuf.opp);
13220Sstevel@tonic-gate 	int prom_fd;
13230Sstevel@tonic-gate 	int ret = DEVFS_INVAL;
13240Sstevel@tonic-gate 	int i;
13250Sstevel@tonic-gate 
13260Sstevel@tonic-gate 	if (prom_path == NULL) {
13270Sstevel@tonic-gate 		return (DEVFS_INVAL);
13280Sstevel@tonic-gate 	}
13290Sstevel@tonic-gate 	if (dev_path == NULL) {
13300Sstevel@tonic-gate 		return (DEVFS_INVAL);
13310Sstevel@tonic-gate 	}
13320Sstevel@tonic-gate 	if (strlen(dev_path) >= MAXPATHLEN)
13330Sstevel@tonic-gate 		return (DEVFS_INVAL);
13340Sstevel@tonic-gate 
13350Sstevel@tonic-gate 	if (*dev_path != '/')
13360Sstevel@tonic-gate 		return (DEVFS_INVAL);
13370Sstevel@tonic-gate 
13380Sstevel@tonic-gate 	prom_fd = prom_open(O_RDONLY);
13390Sstevel@tonic-gate 	if (prom_fd < 0) {
13400Sstevel@tonic-gate 		return (prom_fd);
13410Sstevel@tonic-gate 	}
13420Sstevel@tonic-gate 
13430Sstevel@tonic-gate 	/* query the prom */
13440Sstevel@tonic-gate 	(void) snprintf(opp->oprom_array, MAXVALSIZE, "%s", dev_path);
13450Sstevel@tonic-gate 	opp->oprom_size = MAXVALSIZE;
13460Sstevel@tonic-gate 
13470Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMDEV2PROMNAME, opp) == 0) {
13480Sstevel@tonic-gate 		prom_close(prom_fd);
13490Sstevel@tonic-gate 
13500Sstevel@tonic-gate 		/* return the prom path in prom_path */
13510Sstevel@tonic-gate 
13520Sstevel@tonic-gate 		i = len - opp->oprom_size;
13530Sstevel@tonic-gate 		if (i < 0) {
13540Sstevel@tonic-gate 			bcopy(opp->oprom_array, prom_path, len);
13550Sstevel@tonic-gate 			prom_path[len - 1] = NULL;
13560Sstevel@tonic-gate 			return (len);
13570Sstevel@tonic-gate 		} else {
13580Sstevel@tonic-gate 			bcopy(opp->oprom_array, prom_path, len);
13590Sstevel@tonic-gate 			return (opp->oprom_size);
13600Sstevel@tonic-gate 		}
13610Sstevel@tonic-gate 	}
13620Sstevel@tonic-gate 	/*
13630Sstevel@tonic-gate 	 * either the prom does not support this ioctl or the argument
13640Sstevel@tonic-gate 	 * was invalid.
13650Sstevel@tonic-gate 	 */
13660Sstevel@tonic-gate 	if (errno == ENXIO) {
13670Sstevel@tonic-gate 		ret = DEVFS_NOTSUP;
13680Sstevel@tonic-gate 	}
13690Sstevel@tonic-gate 	prom_close(prom_fd);
13700Sstevel@tonic-gate 	return (ret);
13710Sstevel@tonic-gate }
13720Sstevel@tonic-gate 
13730Sstevel@tonic-gate /*
13740Sstevel@tonic-gate  * Convert a physical or logical device name to a name the prom would
13750Sstevel@tonic-gate  * understand.  Fail if this platform does not support a prom or if
13760Sstevel@tonic-gate  * the device does not correspond to a valid prom device.
13770Sstevel@tonic-gate  *      dev_path should be the name of a device in the logical or
13780Sstevel@tonic-gate  *              physical device namespace.
13790Sstevel@tonic-gate  *      prom_path is the prom version of the device name
13800Sstevel@tonic-gate  *      prom_path must be large enough to contain the result and is
13810Sstevel@tonic-gate  *      supplied by the user.
13820Sstevel@tonic-gate  *
13830Sstevel@tonic-gate  * This routine only supports converting leaf device paths
13840Sstevel@tonic-gate  */
13850Sstevel@tonic-gate int
devfs_dev_to_prom_name(char * dev_path,char * prom_path)13860Sstevel@tonic-gate devfs_dev_to_prom_name(char *dev_path, char *prom_path)
13870Sstevel@tonic-gate {
13880Sstevel@tonic-gate 	int rval;
13890Sstevel@tonic-gate 
13900Sstevel@tonic-gate 	rval = devfs_dev_to_prom_names(dev_path, prom_path, MAXPATHLEN);
13910Sstevel@tonic-gate 
13920Sstevel@tonic-gate 	if (rval < 0)
13930Sstevel@tonic-gate 		return (rval);
13940Sstevel@tonic-gate 	else
13950Sstevel@tonic-gate 		return (0);
13960Sstevel@tonic-gate }
13970Sstevel@tonic-gate 
13980Sstevel@tonic-gate /*
13990Sstevel@tonic-gate  * Use the openprom driver's OPROMPATH2DRV ioctl to convert a devfs
14000Sstevel@tonic-gate  * path to a driver name.
14010Sstevel@tonic-gate  * devfs_path - the pathname of interest.  This must be the physcical device
14020Sstevel@tonic-gate  * path with the mount point prefix (ie. /devices) stripped off.
14030Sstevel@tonic-gate  * drv_buf - user supplied buffer - the driver name will be stored here.
14040Sstevel@tonic-gate  *
14050Sstevel@tonic-gate  * If the prom lookup fails, we return the name of the last component in
14060Sstevel@tonic-gate  * the pathname.  This routine is useful for looking up driver names
14070Sstevel@tonic-gate  * associated with generically named devices.
14080Sstevel@tonic-gate  *
14090Sstevel@tonic-gate  * This routine returns driver names that have aliases resolved.
14100Sstevel@tonic-gate  */
14110Sstevel@tonic-gate int
devfs_path_to_drv(char * devfs_path,char * drv_buf)14120Sstevel@tonic-gate devfs_path_to_drv(char *devfs_path, char *drv_buf)
14130Sstevel@tonic-gate {
14140Sstevel@tonic-gate 	Oppbuf oppbuf;
14150Sstevel@tonic-gate 	struct openpromio *opp = &(oppbuf.opp);
14160Sstevel@tonic-gate 	char *slash, *colon, *dev_addr;
14170Sstevel@tonic-gate 	char driver_path[MAXPATHLEN];
14180Sstevel@tonic-gate 	int prom_fd;
14190Sstevel@tonic-gate 
14200Sstevel@tonic-gate 	if (drv_buf == NULL) {
14210Sstevel@tonic-gate 		return (-1);
14220Sstevel@tonic-gate 	}
14230Sstevel@tonic-gate 	if (devfs_path == NULL) {
14240Sstevel@tonic-gate 		return (-1);
14250Sstevel@tonic-gate 	}
14260Sstevel@tonic-gate 
14270Sstevel@tonic-gate 	if (strlen(devfs_path) >= MAXPATHLEN)
14280Sstevel@tonic-gate 		return (-1);
14290Sstevel@tonic-gate 
14300Sstevel@tonic-gate 	if (*devfs_path != '/')
14310Sstevel@tonic-gate 		return (-1);
14320Sstevel@tonic-gate 
14330Sstevel@tonic-gate 
14340Sstevel@tonic-gate 	/* strip off any minor node info at the end of the path */
14350Sstevel@tonic-gate 	(void) strcpy(driver_path, devfs_path);
14360Sstevel@tonic-gate 	slash = strrchr(driver_path, '/');
14370Sstevel@tonic-gate 	if (slash == NULL)
14380Sstevel@tonic-gate 		return (-1);
14390Sstevel@tonic-gate 	colon = strrchr(slash, ':');
14400Sstevel@tonic-gate 	if (colon != NULL)
14410Sstevel@tonic-gate 		*colon = '\0';
14420Sstevel@tonic-gate 
14430Sstevel@tonic-gate 	/* query the prom */
14440Sstevel@tonic-gate 	if ((prom_fd = prom_open(O_RDONLY)) >= 0) {
14450Sstevel@tonic-gate 		(void) strcpy(opp->oprom_array, driver_path);
14460Sstevel@tonic-gate 		opp->oprom_size = MAXVALSIZE;
14470Sstevel@tonic-gate 
14480Sstevel@tonic-gate 		if (ioctl(prom_fd, OPROMPATH2DRV, opp) == 0) {
14490Sstevel@tonic-gate 			prom_close(prom_fd);
14500Sstevel@tonic-gate 			/* return the driver name in drv_buf */
14510Sstevel@tonic-gate 			(void) strcpy(drv_buf, opp->oprom_array);
14520Sstevel@tonic-gate 			return (0);
14530Sstevel@tonic-gate 		}
14540Sstevel@tonic-gate 		prom_close(prom_fd);
14550Sstevel@tonic-gate 	} else if (prom_fd != DEVFS_NOTSUP)
14560Sstevel@tonic-gate 		return (-1);
14570Sstevel@tonic-gate 	/*
14580Sstevel@tonic-gate 	 * If we get here, then either:
14590Sstevel@tonic-gate 	 *	1. this platform does not support an openprom driver
14600Sstevel@tonic-gate 	 *	2. we were asked to look up a device the prom does
14610Sstevel@tonic-gate 	 *	   not know about (e.g. a pseudo device)
14620Sstevel@tonic-gate 	 * In this case, we use the last component of the devfs path
14630Sstevel@tonic-gate 	 * name and try to derive the driver name
14640Sstevel@tonic-gate 	 */
14650Sstevel@tonic-gate 
14660Sstevel@tonic-gate 	/* use the last component of devfs_path as the driver name */
14670Sstevel@tonic-gate 	if ((dev_addr = strrchr(slash, '@')) != NULL)
14680Sstevel@tonic-gate 		*dev_addr = '\0';
14690Sstevel@tonic-gate 	slash++;
14700Sstevel@tonic-gate 
14710Sstevel@tonic-gate 	/* use opp->oprom_array as a buffer */
14720Sstevel@tonic-gate 	(void) strcpy(opp->oprom_array, slash);
14730Sstevel@tonic-gate 	if (devfs_resolve_aliases(opp->oprom_array) == NULL)
14740Sstevel@tonic-gate 		return (-1);
14750Sstevel@tonic-gate 	(void) strcpy(drv_buf, opp->oprom_array);
14760Sstevel@tonic-gate 	return (0);
14770Sstevel@tonic-gate }
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate /*
14800Sstevel@tonic-gate  * These modctl calls do the equivalent of:
14810Sstevel@tonic-gate  *	ddi_name_to_major()
14820Sstevel@tonic-gate  *	ddi_major_to_name()
14830Sstevel@tonic-gate  * This results in two things:
14840Sstevel@tonic-gate  *	- the driver name must be a valid one
14850Sstevel@tonic-gate  *	- any driver aliases are resolved.
14860Sstevel@tonic-gate  * drv is overwritten with the resulting name.
14870Sstevel@tonic-gate  */
14880Sstevel@tonic-gate char *
devfs_resolve_aliases(char * drv)14890Sstevel@tonic-gate devfs_resolve_aliases(char *drv)
14900Sstevel@tonic-gate {
14910Sstevel@tonic-gate 	major_t maj;
14920Sstevel@tonic-gate 	char driver_name[MAXNAMELEN + 1];
14930Sstevel@tonic-gate 
14940Sstevel@tonic-gate 	if (drv == NULL) {
14950Sstevel@tonic-gate 		return (NULL);
14960Sstevel@tonic-gate 	}
14970Sstevel@tonic-gate 
14980Sstevel@tonic-gate 	if (modctl(MODGETMAJBIND, drv, strlen(drv) + 1, &maj) < 0)
14990Sstevel@tonic-gate 		return (NULL);
15000Sstevel@tonic-gate 	else if (modctl(MODGETNAME, driver_name, sizeof (driver_name), &maj)
15010Sstevel@tonic-gate 	    < 0) {
15020Sstevel@tonic-gate 		return (NULL);
15030Sstevel@tonic-gate 	} else {
15040Sstevel@tonic-gate 		(void) strcpy(drv, driver_name);
15050Sstevel@tonic-gate 		return (drv);
15060Sstevel@tonic-gate 	}
15070Sstevel@tonic-gate }
15080Sstevel@tonic-gate 
15090Sstevel@tonic-gate /*
15100Sstevel@tonic-gate  * open the openprom device.  and verify that we are on an
15110Sstevel@tonic-gate  * OBP/1275 OF machine.  If the prom does not exist, then we
15120Sstevel@tonic-gate  * return an error
15130Sstevel@tonic-gate  */
15140Sstevel@tonic-gate static int
prom_open(int oflag)15150Sstevel@tonic-gate prom_open(int oflag)
15160Sstevel@tonic-gate {
15170Sstevel@tonic-gate 	int prom_fd = -1;
15180Sstevel@tonic-gate 	char *promdev = "/dev/openprom";
15190Sstevel@tonic-gate 
15200Sstevel@tonic-gate 	while (prom_fd < 0) {
15210Sstevel@tonic-gate 		if ((prom_fd = open(promdev, oflag)) < 0)  {
15220Sstevel@tonic-gate 			if (errno == EAGAIN)   {
15230Sstevel@tonic-gate 				(void) sleep(5);
15240Sstevel@tonic-gate 				continue;
15250Sstevel@tonic-gate 			}
15260Sstevel@tonic-gate 			if ((errno == ENXIO) || (errno == ENOENT)) {
15270Sstevel@tonic-gate 				return (DEVFS_NOTSUP);
15280Sstevel@tonic-gate 			}
15290Sstevel@tonic-gate 			if ((errno == EPERM) || (errno == EACCES)) {
15300Sstevel@tonic-gate 				return (DEVFS_PERM);
15310Sstevel@tonic-gate 			}
15320Sstevel@tonic-gate 			return (DEVFS_ERR);
15330Sstevel@tonic-gate 		} else
15340Sstevel@tonic-gate 			break;
15350Sstevel@tonic-gate 	}
15360Sstevel@tonic-gate 	if (is_openprom(prom_fd))
15370Sstevel@tonic-gate 		return (prom_fd);
15380Sstevel@tonic-gate 	else {
15390Sstevel@tonic-gate 		prom_close(prom_fd);
15400Sstevel@tonic-gate 		return (DEVFS_ERR);
15410Sstevel@tonic-gate 	}
15420Sstevel@tonic-gate }
15430Sstevel@tonic-gate 
15440Sstevel@tonic-gate static void
prom_close(int prom_fd)15450Sstevel@tonic-gate prom_close(int prom_fd)
15460Sstevel@tonic-gate {
15470Sstevel@tonic-gate 	(void) close(prom_fd);
15480Sstevel@tonic-gate }
15490Sstevel@tonic-gate 
15500Sstevel@tonic-gate /*
15510Sstevel@tonic-gate  * is this an OBP/1275 OF machine?
15520Sstevel@tonic-gate  */
15530Sstevel@tonic-gate static int
is_openprom(int prom_fd)15540Sstevel@tonic-gate is_openprom(int prom_fd)
15550Sstevel@tonic-gate {
15560Sstevel@tonic-gate 	Oppbuf  oppbuf;
15570Sstevel@tonic-gate 	struct openpromio *opp = &(oppbuf.opp);
15580Sstevel@tonic-gate 	unsigned int i;
15590Sstevel@tonic-gate 
15600Sstevel@tonic-gate 	opp->oprom_size = MAXVALSIZE;
15610Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMGETCONS, opp) < 0)
15620Sstevel@tonic-gate 		return (0);
15630Sstevel@tonic-gate 
15640Sstevel@tonic-gate 	i = (unsigned int)((unsigned char)opp->oprom_array[0]);
15650Sstevel@tonic-gate 	return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM);
15660Sstevel@tonic-gate }
15670Sstevel@tonic-gate 
15680Sstevel@tonic-gate /*
15690Sstevel@tonic-gate  * convert a prom device path name to an equivalent physical device
15700Sstevel@tonic-gate  * path in the kernel.
15710Sstevel@tonic-gate  */
15720Sstevel@tonic-gate static int
devfs_prom_to_dev_name(char * prom_path,char * dev_path)15730Sstevel@tonic-gate devfs_prom_to_dev_name(char *prom_path, char *dev_path)
15740Sstevel@tonic-gate {
15750Sstevel@tonic-gate 	Oppbuf oppbuf;
15760Sstevel@tonic-gate 	struct openpromio *opp = &(oppbuf.opp);
15770Sstevel@tonic-gate 	int prom_fd;
15780Sstevel@tonic-gate 	int ret = DEVFS_INVAL;
15790Sstevel@tonic-gate 
15800Sstevel@tonic-gate 	if (dev_path == NULL) {
15810Sstevel@tonic-gate 		return (DEVFS_INVAL);
15820Sstevel@tonic-gate 	}
15830Sstevel@tonic-gate 	if (prom_path == NULL) {
15840Sstevel@tonic-gate 		return (DEVFS_INVAL);
15850Sstevel@tonic-gate 	}
15860Sstevel@tonic-gate 	if (strlen(prom_path) >= MAXPATHLEN)
15870Sstevel@tonic-gate 		return (DEVFS_INVAL);
15880Sstevel@tonic-gate 
15890Sstevel@tonic-gate 	if (*prom_path != '/') {
15900Sstevel@tonic-gate 		return (DEVFS_INVAL);
15910Sstevel@tonic-gate 	}
15920Sstevel@tonic-gate 
15930Sstevel@tonic-gate 	/* query the prom */
15940Sstevel@tonic-gate 	prom_fd = prom_open(O_RDONLY);
15950Sstevel@tonic-gate 	if (prom_fd < 0) {
15960Sstevel@tonic-gate 		return (prom_fd);
15970Sstevel@tonic-gate 	}
15980Sstevel@tonic-gate 	(void) strcpy(opp->oprom_array, prom_path);
15990Sstevel@tonic-gate 	opp->oprom_size = MAXVALSIZE;
16000Sstevel@tonic-gate 
16010Sstevel@tonic-gate 	if (ioctl(prom_fd, OPROMPROM2DEVNAME, opp) == 0) {
16020Sstevel@tonic-gate 		prom_close(prom_fd);
16030Sstevel@tonic-gate 		/*
16040Sstevel@tonic-gate 		 * success
16050Sstevel@tonic-gate 		 * return the prom path in prom_path
16060Sstevel@tonic-gate 		 */
16070Sstevel@tonic-gate 		(void) strcpy(dev_path, opp->oprom_array);
16080Sstevel@tonic-gate 		return (0);
16090Sstevel@tonic-gate 	}
16100Sstevel@tonic-gate 	/*
16110Sstevel@tonic-gate 	 * either the argument was not a valid name or the openprom
16120Sstevel@tonic-gate 	 * driver does not support this ioctl.
16130Sstevel@tonic-gate 	 */
16140Sstevel@tonic-gate 	if (errno == ENXIO) {
16150Sstevel@tonic-gate 		ret = DEVFS_NOTSUP;
16160Sstevel@tonic-gate 	}
16170Sstevel@tonic-gate 	prom_close(prom_fd);
16180Sstevel@tonic-gate 	return (ret);
16190Sstevel@tonic-gate }
16200Sstevel@tonic-gate /*
16210Sstevel@tonic-gate  * convert a prom device path to a list of equivalent alias names
16220Sstevel@tonic-gate  * If there is no alias node, or there are no aliases that correspond
16230Sstevel@tonic-gate  * to dev, we return empty lists.
16240Sstevel@tonic-gate  */
16250Sstevel@tonic-gate static int
prom_dev_to_alias(char * dev,uint_t options,char *** ret_buf)16260Sstevel@tonic-gate prom_dev_to_alias(char *dev, uint_t options, char ***ret_buf)
16270Sstevel@tonic-gate {
16280Sstevel@tonic-gate 	struct name_list *exact_list;
16290Sstevel@tonic-gate 	struct name_list *inexact_list;
16300Sstevel@tonic-gate 	struct name_list *list;
16310Sstevel@tonic-gate 	char *ptr;
16320Sstevel@tonic-gate 	char **array;
16330Sstevel@tonic-gate 	int prom_fd;
16340Sstevel@tonic-gate 	int count;
16350Sstevel@tonic-gate 	int vers;
16360Sstevel@tonic-gate 
16370Sstevel@tonic-gate 	vers = prom_obp_vers();
16380Sstevel@tonic-gate 	if (vers < 0) {
16390Sstevel@tonic-gate 		return (vers);
16400Sstevel@tonic-gate 	}
16410Sstevel@tonic-gate 
16420Sstevel@tonic-gate 	if (dev == NULL) {
16430Sstevel@tonic-gate 		return (DEVFS_INVAL);
16440Sstevel@tonic-gate 	}
16450Sstevel@tonic-gate 
16460Sstevel@tonic-gate 	if (*dev != '/')
16470Sstevel@tonic-gate 		return (DEVFS_INVAL);
16480Sstevel@tonic-gate 
16490Sstevel@tonic-gate 	if (strlen(dev) >= MAXPATHLEN)
16500Sstevel@tonic-gate 		return (DEVFS_INVAL);
16510Sstevel@tonic-gate 
16520Sstevel@tonic-gate 	if ((ptr = strchr(dev, ':')) != NULL) {
16530Sstevel@tonic-gate 		if (strchr(ptr, '/') != NULL)
16540Sstevel@tonic-gate 			return (DEVFS_INVAL);
16550Sstevel@tonic-gate 	}
16560Sstevel@tonic-gate 	if (ret_buf == NULL) {
16570Sstevel@tonic-gate 		return (DEVFS_INVAL);
16580Sstevel@tonic-gate 	}
16590Sstevel@tonic-gate 
16600Sstevel@tonic-gate 	prom_fd = prom_open(O_RDONLY);
16610Sstevel@tonic-gate 	if (prom_fd < 0) {
16620Sstevel@tonic-gate 		return (prom_fd);
16630Sstevel@tonic-gate 	}
16640Sstevel@tonic-gate 
16650Sstevel@tonic-gate 	(void) prom_srch_aliases_by_def(dev, &exact_list,
16660Sstevel@tonic-gate 	    &inexact_list,  prom_fd);
16670Sstevel@tonic-gate 
16680Sstevel@tonic-gate 	prom_close(prom_fd);
16690Sstevel@tonic-gate 
16700Sstevel@tonic-gate 	if ((options & BOOTDEV_NO_EXACT_ALIAS) != 0) {
16710Sstevel@tonic-gate 		free_name_list(exact_list, 1);
16720Sstevel@tonic-gate 		exact_list = NULL;
16730Sstevel@tonic-gate 	}
16740Sstevel@tonic-gate 
16750Sstevel@tonic-gate 	if ((options & BOOTDEV_NO_INEXACT_ALIAS) != 0) {
16760Sstevel@tonic-gate 		free_name_list(inexact_list, 1);
16770Sstevel@tonic-gate 		inexact_list = NULL;
16780Sstevel@tonic-gate 	}
16790Sstevel@tonic-gate 
16800Sstevel@tonic-gate 	count = 0;
16810Sstevel@tonic-gate 	list = exact_list;
16820Sstevel@tonic-gate 	while (list != NULL) {
16830Sstevel@tonic-gate 		list = list->next;
16840Sstevel@tonic-gate 		count++;
16850Sstevel@tonic-gate 	}
16860Sstevel@tonic-gate 	list = inexact_list;
16870Sstevel@tonic-gate 	while (list != NULL) {
16880Sstevel@tonic-gate 		list = list->next;
16890Sstevel@tonic-gate 		count++;
16900Sstevel@tonic-gate 	}
16910Sstevel@tonic-gate 
16920Sstevel@tonic-gate 	if ((*ret_buf = (char **)malloc((count + 1) * sizeof (char *)))
16930Sstevel@tonic-gate 	    == NULL) {
16940Sstevel@tonic-gate 		free_name_list(inexact_list, 1);
16950Sstevel@tonic-gate 		free_name_list(exact_list, 1);
16960Sstevel@tonic-gate 		return (DEVFS_NOMEM);
16970Sstevel@tonic-gate 	}
16980Sstevel@tonic-gate 
16990Sstevel@tonic-gate 	array = *ret_buf;
17000Sstevel@tonic-gate 	count = 0;
17010Sstevel@tonic-gate 	list = exact_list;
17020Sstevel@tonic-gate 	while (list != NULL) {
17030Sstevel@tonic-gate 		array[count] = list->name;
17040Sstevel@tonic-gate 		list = list->next;
17050Sstevel@tonic-gate 		count++;
17060Sstevel@tonic-gate 	}
17070Sstevel@tonic-gate 	list = inexact_list;
17080Sstevel@tonic-gate 	while (list != NULL) {
17090Sstevel@tonic-gate 		array[count] = list->name;
17100Sstevel@tonic-gate 		list = list->next;
17110Sstevel@tonic-gate 		count++;
17120Sstevel@tonic-gate 	}
17130Sstevel@tonic-gate 	array[count] = NULL;
17140Sstevel@tonic-gate 	free_name_list(inexact_list, 0);
17150Sstevel@tonic-gate 	free_name_list(exact_list, 0);
17160Sstevel@tonic-gate 
17170Sstevel@tonic-gate 	return (0);
17180Sstevel@tonic-gate }
17190Sstevel@tonic-gate 
17200Sstevel@tonic-gate /*
17210Sstevel@tonic-gate  * determine the version of prom we are running on.
17220Sstevel@tonic-gate  * Also include any prom revision specific information.
17230Sstevel@tonic-gate  */
17240Sstevel@tonic-gate static int
prom_obp_vers(void)17250Sstevel@tonic-gate prom_obp_vers(void)
17260Sstevel@tonic-gate {
17270Sstevel@tonic-gate 	Oppbuf  oppbuf;
17280Sstevel@tonic-gate 	struct openpromio *opp = &(oppbuf.opp);
17290Sstevel@tonic-gate 	int prom_fd;
17300Sstevel@tonic-gate 	static int version = 0;
17310Sstevel@tonic-gate 
17320Sstevel@tonic-gate 	/* cache version */
17330Sstevel@tonic-gate 	if (version > 0) {
17340Sstevel@tonic-gate 		return (version);
17350Sstevel@tonic-gate 	}
17360Sstevel@tonic-gate 
17370Sstevel@tonic-gate 	prom_fd = prom_open(O_RDONLY);
17380Sstevel@tonic-gate 	if (prom_fd < 0) {
17390Sstevel@tonic-gate 		return (prom_fd);
17400Sstevel@tonic-gate 	}
17410Sstevel@tonic-gate 
17420Sstevel@tonic-gate 	opp->oprom_size = MAXVALSIZE;
17430Sstevel@tonic-gate 
17440Sstevel@tonic-gate 	if ((ioctl(prom_fd, OPROMGETVERSION, opp)) < 0) {
17450Sstevel@tonic-gate 		prom_close(prom_fd);
17460Sstevel@tonic-gate 		return (DEVFS_ERR);
17470Sstevel@tonic-gate 	}
17480Sstevel@tonic-gate 	prom_close(prom_fd);
17490Sstevel@tonic-gate 
17500Sstevel@tonic-gate 	version |= OBP_OF;
17510Sstevel@tonic-gate 
17520Sstevel@tonic-gate 	return (version);
17530Sstevel@tonic-gate }
17540Sstevel@tonic-gate /*
17550Sstevel@tonic-gate  * search the aliases node by definition - compile a list of
17560Sstevel@tonic-gate  * alias names that are both exact and inexact matches.
17570Sstevel@tonic-gate  */
17580Sstevel@tonic-gate static int
prom_srch_aliases_by_def(char * promdev_def,struct name_list ** exact_list,struct name_list ** inexact_list,int prom_fd)17590Sstevel@tonic-gate prom_srch_aliases_by_def(char *promdev_def, struct name_list **exact_list,
17600Sstevel@tonic-gate     struct name_list **inexact_list, int prom_fd)
17610Sstevel@tonic-gate {
17620Sstevel@tonic-gate 	Oppbuf  oppbuf;
17630Sstevel@tonic-gate 	Oppbuf  propdef_oppbuf;
17640Sstevel@tonic-gate 	struct openpromio *opp = &(oppbuf.opp);
17650Sstevel@tonic-gate 	struct openpromio *propdef_opp = &(propdef_oppbuf.opp);
17660Sstevel@tonic-gate 	int *ip = (int *)((void *)opp->oprom_array);
17670Sstevel@tonic-gate 	int ret;
17680Sstevel@tonic-gate 	struct name_list *inexact_match = *inexact_list = NULL;
17690Sstevel@tonic-gate 	struct name_list *exact_match = *exact_list = NULL;
17700Sstevel@tonic-gate 	char alias_buf[MAXNAMELEN];
17710Sstevel@tonic-gate 	int found = 0;
17720Sstevel@tonic-gate 
1773*9735SSriman.Bhavanam@Sun.COM 	if ((ret = prom_find_aliases_node(prom_fd)) < 0)
1774*9735SSriman.Bhavanam@Sun.COM 		return (0);
1775*9735SSriman.Bhavanam@Sun.COM 
17760Sstevel@tonic-gate 	(void) memset(oppbuf.buf, 0, BUFSIZE);
17770Sstevel@tonic-gate 	opp->oprom_size = MAXPROPSIZE;
17780Sstevel@tonic-gate 	*ip = 0;
17790Sstevel@tonic-gate 
17800Sstevel@tonic-gate 	if ((ret = ioctl(prom_fd, OPROMNXTPROP, opp)) < 0)
17810Sstevel@tonic-gate 		return (0);
17820Sstevel@tonic-gate 	if (opp->oprom_size == 0)
17830Sstevel@tonic-gate 		return (0);
17840Sstevel@tonic-gate 
17850Sstevel@tonic-gate 	while ((ret >= 0) && (opp->oprom_size > 0)) {
17860Sstevel@tonic-gate 		(void) strcpy(propdef_opp->oprom_array, opp->oprom_array);
17870Sstevel@tonic-gate 		opp->oprom_size = MAXPROPSIZE;
17880Sstevel@tonic-gate 		propdef_opp->oprom_size = MAXVALSIZE;
17890Sstevel@tonic-gate 		if ((ioctl(prom_fd, OPROMGETPROP, propdef_opp) < 0) ||
17900Sstevel@tonic-gate 		    (propdef_opp->oprom_size == 0)) {
17910Sstevel@tonic-gate 			ret = ioctl(prom_fd, OPROMNXTPROP, opp);
17920Sstevel@tonic-gate 			continue;
17930Sstevel@tonic-gate 		}
17940Sstevel@tonic-gate 		ret = prom_compare_devs(promdev_def, propdef_opp->oprom_array);
17950Sstevel@tonic-gate 		if (ret == EXACT_MATCH) {
17960Sstevel@tonic-gate 			found++;
17970Sstevel@tonic-gate 			if (insert_alias_list(exact_list, opp->oprom_array)
17980Sstevel@tonic-gate 			    != 0) {
17990Sstevel@tonic-gate 				free_name_list(exact_match, 1);
18000Sstevel@tonic-gate 				free_name_list(inexact_match, 1);
18010Sstevel@tonic-gate 				return (-1);
18020Sstevel@tonic-gate 			}
18030Sstevel@tonic-gate 		}
18040Sstevel@tonic-gate 		if (ret == INEXACT_MATCH) {
18050Sstevel@tonic-gate 			found++;
18060Sstevel@tonic-gate 			(void) strcpy(alias_buf, opp->oprom_array);
18070Sstevel@tonic-gate 			options_override(promdev_def, alias_buf);
18080Sstevel@tonic-gate 			if (insert_alias_list(inexact_list, alias_buf)
18090Sstevel@tonic-gate 			    != 0) {
18100Sstevel@tonic-gate 				free_name_list(exact_match, 1);
18110Sstevel@tonic-gate 				free_name_list(inexact_match, 1);
18120Sstevel@tonic-gate 				return (-1);
18130Sstevel@tonic-gate 			}
18140Sstevel@tonic-gate 		}
18150Sstevel@tonic-gate 		ret = ioctl(prom_fd, OPROMNXTPROP, opp);
18160Sstevel@tonic-gate 	}
18170Sstevel@tonic-gate 	if (found) {
18180Sstevel@tonic-gate 		return (0);
18190Sstevel@tonic-gate 	} else {
18200Sstevel@tonic-gate 		return (-1);
18210Sstevel@tonic-gate 	}
18220Sstevel@tonic-gate }
18230Sstevel@tonic-gate 
18240Sstevel@tonic-gate /*
18250Sstevel@tonic-gate  * free a list of name_list structs and optionally
18260Sstevel@tonic-gate  * free the strings they contain.
18270Sstevel@tonic-gate  */
18280Sstevel@tonic-gate static void
free_name_list(struct name_list * list,int free_name)18290Sstevel@tonic-gate free_name_list(struct name_list *list, int free_name)
18300Sstevel@tonic-gate {
18310Sstevel@tonic-gate 	struct name_list *next = list;
18320Sstevel@tonic-gate 
18330Sstevel@tonic-gate 	while (next != NULL) {
18340Sstevel@tonic-gate 		list = list->next;
18350Sstevel@tonic-gate 		if (free_name)
18360Sstevel@tonic-gate 			free(next->name);
18370Sstevel@tonic-gate 		free(next);
18380Sstevel@tonic-gate 		next = list;
18390Sstevel@tonic-gate 	}
18400Sstevel@tonic-gate }
18410Sstevel@tonic-gate 
18420Sstevel@tonic-gate /*
18430Sstevel@tonic-gate  * insert a new alias in a list of aliases - the list is sorted
18440Sstevel@tonic-gate  * in collating order (ignoring anything that comes after the
18450Sstevel@tonic-gate  * ':' in the name).
18460Sstevel@tonic-gate  */
18470Sstevel@tonic-gate static int
insert_alias_list(struct name_list ** list,char * alias_name)18480Sstevel@tonic-gate insert_alias_list(struct name_list **list, char *alias_name)
18490Sstevel@tonic-gate {
18500Sstevel@tonic-gate 	struct name_list *entry = *list;
18510Sstevel@tonic-gate 	struct name_list *new_entry, *prev_entry;
18520Sstevel@tonic-gate 	int ret;
18530Sstevel@tonic-gate 	char *colon1, *colon2;
18540Sstevel@tonic-gate 
18550Sstevel@tonic-gate 	if ((new_entry =
18560Sstevel@tonic-gate 	    (struct name_list *)malloc(sizeof (struct name_list)))
18570Sstevel@tonic-gate 	    == NULL) {
18580Sstevel@tonic-gate 		return (-1);
18590Sstevel@tonic-gate 	}
18600Sstevel@tonic-gate 	if ((new_entry->name = strdup(alias_name)) == NULL) {
18610Sstevel@tonic-gate 		free(new_entry);
18620Sstevel@tonic-gate 		return (-1);
18630Sstevel@tonic-gate 	}
18640Sstevel@tonic-gate 	new_entry->next = NULL;
18650Sstevel@tonic-gate 
18660Sstevel@tonic-gate 	if (entry == NULL) {
18670Sstevel@tonic-gate 		*list = new_entry;
18680Sstevel@tonic-gate 		return (0);
18690Sstevel@tonic-gate 	}
18700Sstevel@tonic-gate 
18710Sstevel@tonic-gate 	if ((colon1 = strchr(alias_name, ':')) != NULL) {
18720Sstevel@tonic-gate 		*colon1 = '\0';
18730Sstevel@tonic-gate 	}
18740Sstevel@tonic-gate 	prev_entry = NULL;
18750Sstevel@tonic-gate 	while (entry != NULL) {
18760Sstevel@tonic-gate 		if ((colon2 = strchr(entry->name, ':')) != NULL) {
18770Sstevel@tonic-gate 			*colon2 = '\0';
18780Sstevel@tonic-gate 		}
18790Sstevel@tonic-gate 		ret = strcmp(alias_name, entry->name);
18800Sstevel@tonic-gate 		if (colon2 != NULL) {
18810Sstevel@tonic-gate 			*colon2 = ':';
18820Sstevel@tonic-gate 		}
18830Sstevel@tonic-gate 		/* duplicate */
18840Sstevel@tonic-gate 		if (ret == 0) {
18850Sstevel@tonic-gate 			free(new_entry->name);
18860Sstevel@tonic-gate 			free(new_entry);
18870Sstevel@tonic-gate 			if (colon1 != NULL) {
18880Sstevel@tonic-gate 				*colon1 = ':';
18890Sstevel@tonic-gate 			}
18900Sstevel@tonic-gate 			return (0);
18910Sstevel@tonic-gate 		}
18920Sstevel@tonic-gate 		if (ret < 0) {
18930Sstevel@tonic-gate 			new_entry->next = entry;
18940Sstevel@tonic-gate 			if (prev_entry == NULL) {
18950Sstevel@tonic-gate 				/* in beginning of list */
18960Sstevel@tonic-gate 				*list = new_entry;
18970Sstevel@tonic-gate 			} else {
18980Sstevel@tonic-gate 				/* in middle of list */
18990Sstevel@tonic-gate 				prev_entry->next = new_entry;
19000Sstevel@tonic-gate 			}
19010Sstevel@tonic-gate 			if (colon1 != NULL) {
19020Sstevel@tonic-gate 				*colon1 = ':';
19030Sstevel@tonic-gate 			}
19040Sstevel@tonic-gate 			return (0);
19050Sstevel@tonic-gate 		}
19060Sstevel@tonic-gate 		prev_entry = entry;
19070Sstevel@tonic-gate 		entry = entry->next;
19080Sstevel@tonic-gate 	}
19090Sstevel@tonic-gate 	/* at end of list */
19100Sstevel@tonic-gate 	prev_entry->next = new_entry;
19110Sstevel@tonic-gate 	new_entry->next = NULL;
19120Sstevel@tonic-gate 	if (colon1 != NULL) {
19130Sstevel@tonic-gate 		*colon1 = ':';
19140Sstevel@tonic-gate 	}
19150Sstevel@tonic-gate 	return (0);
19160Sstevel@tonic-gate }
19170Sstevel@tonic-gate /*
19180Sstevel@tonic-gate  * append :x to alias_name to override any default minor name options
19190Sstevel@tonic-gate  */
19200Sstevel@tonic-gate static void
options_override(char * prom_path,char * alias_name)19210Sstevel@tonic-gate options_override(char *prom_path, char *alias_name)
19220Sstevel@tonic-gate {
19230Sstevel@tonic-gate 	char *colon;
19240Sstevel@tonic-gate 
19250Sstevel@tonic-gate 	if ((colon = strrchr(alias_name, ':')) != NULL) {
19260Sstevel@tonic-gate 		/*
19270Sstevel@tonic-gate 		 * XXX - should alias names in /aliases ever have a
19280Sstevel@tonic-gate 		 * : embedded in them?
19290Sstevel@tonic-gate 		 * If so we ignore it.
19300Sstevel@tonic-gate 		 */
19310Sstevel@tonic-gate 		*colon = '\0';
19320Sstevel@tonic-gate 	}
19330Sstevel@tonic-gate 
19340Sstevel@tonic-gate 	if ((colon = strrchr(prom_path, ':')) != NULL) {
19350Sstevel@tonic-gate 		(void) strcat(alias_name, colon);
19360Sstevel@tonic-gate 	}
19370Sstevel@tonic-gate }
19380Sstevel@tonic-gate 
19390Sstevel@tonic-gate /*
19400Sstevel@tonic-gate  * compare to prom device names.
19410Sstevel@tonic-gate  * if the device names are not fully qualified. we convert them -
19420Sstevel@tonic-gate  * we only do this as a last resort though since it requires
19430Sstevel@tonic-gate  * jumping into the kernel.
19440Sstevel@tonic-gate  */
19450Sstevel@tonic-gate static int
prom_compare_devs(char * prom_dev1,char * prom_dev2)19460Sstevel@tonic-gate prom_compare_devs(char *prom_dev1, char *prom_dev2)
19470Sstevel@tonic-gate {
19480Sstevel@tonic-gate 	char *dev1, *dev2;
19490Sstevel@tonic-gate 	char *ptr1, *ptr2;
19500Sstevel@tonic-gate 	char *drvname1, *addrname1, *minorname1;
19510Sstevel@tonic-gate 	char *drvname2, *addrname2, *minorname2;
19520Sstevel@tonic-gate 	char component1[MAXNAMELEN], component2[MAXNAMELEN];
19530Sstevel@tonic-gate 	char devname1[MAXPATHLEN], devname2[MAXPATHLEN];
19540Sstevel@tonic-gate 	int unqualified_name = 0;
19550Sstevel@tonic-gate 	int error = EXACT_MATCH;
19560Sstevel@tonic-gate 	int len1, len2;
19570Sstevel@tonic-gate 	char *wildcard = ",0";
19580Sstevel@tonic-gate 
19590Sstevel@tonic-gate 	ptr1 = prom_dev1;
19600Sstevel@tonic-gate 	ptr2 = prom_dev2;
19610Sstevel@tonic-gate 
19620Sstevel@tonic-gate 	if ((ptr1 == NULL) || (*ptr1 != '/')) {
19630Sstevel@tonic-gate 		return (NO_MATCH);
19640Sstevel@tonic-gate 	}
19650Sstevel@tonic-gate 	if ((ptr2 == NULL) || (*ptr2 != '/')) {
19660Sstevel@tonic-gate 		return (NO_MATCH);
19670Sstevel@tonic-gate 	}
19680Sstevel@tonic-gate 
19690Sstevel@tonic-gate 	/*
19700Sstevel@tonic-gate 	 * compare device names one component at a time.
19710Sstevel@tonic-gate 	 */
19720Sstevel@tonic-gate 	while ((ptr1 != NULL) && (ptr2 != NULL)) {
19730Sstevel@tonic-gate 		*ptr1 = *ptr2 = '/';
19740Sstevel@tonic-gate 		dev1 = ptr1 + 1;
19750Sstevel@tonic-gate 		dev2 = ptr2 + 1;
19760Sstevel@tonic-gate 		if ((ptr1 = strchr(dev1, '/')) != NULL)
19770Sstevel@tonic-gate 			*ptr1 = '\0';
19780Sstevel@tonic-gate 		if ((ptr2 = strchr(dev2, '/')) != NULL)
19790Sstevel@tonic-gate 			*ptr2 = '\0';
19800Sstevel@tonic-gate 
19810Sstevel@tonic-gate 		(void) strcpy(component1, dev1);
19820Sstevel@tonic-gate 		(void) strcpy(component2, dev2);
19830Sstevel@tonic-gate 
19840Sstevel@tonic-gate 		parse_name(component1, &drvname1, &addrname1, &minorname1);
19850Sstevel@tonic-gate 		parse_name(component2, &drvname2, &addrname2, &minorname2);
19860Sstevel@tonic-gate 
19870Sstevel@tonic-gate 		if ((drvname1 == NULL) && (addrname1 == NULL)) {
19880Sstevel@tonic-gate 			error = NO_MATCH;
19890Sstevel@tonic-gate 			break;
19900Sstevel@tonic-gate 		}
19910Sstevel@tonic-gate 
19920Sstevel@tonic-gate 		if ((drvname2 == NULL) && (addrname2 == NULL)) {
19930Sstevel@tonic-gate 			error = NO_MATCH;
19940Sstevel@tonic-gate 			break;
19950Sstevel@tonic-gate 		}
19960Sstevel@tonic-gate 
19970Sstevel@tonic-gate 		if (_prom_strcmp(drvname1, drvname2) != 0) {
19980Sstevel@tonic-gate 			error = NO_MATCH;
19990Sstevel@tonic-gate 			break;
20000Sstevel@tonic-gate 		}
20010Sstevel@tonic-gate 
20020Sstevel@tonic-gate 		/*
20030Sstevel@tonic-gate 		 * a possible name is driver_name@address.  The address
20040Sstevel@tonic-gate 		 * portion is optional (i.e. the name is not fully
20050Sstevel@tonic-gate 		 * qualified.).  We have to deal with the case where
20060Sstevel@tonic-gate 		 * the component name is either driver_name or
20070Sstevel@tonic-gate 		 * driver_name@address
20080Sstevel@tonic-gate 		 */
20090Sstevel@tonic-gate 		if ((addrname1 == NULL) ^ (addrname2 == NULL)) {
20100Sstevel@tonic-gate 			unqualified_name = 1;
20110Sstevel@tonic-gate 		} else if (addrname1 &&
20120Sstevel@tonic-gate 		    (_prom_strcmp(addrname1, addrname2) != 0)) {
20130Sstevel@tonic-gate 			/*
20140Sstevel@tonic-gate 			 * check to see if appending a ",0" to the
20150Sstevel@tonic-gate 			 * shorter address causes a match to occur.
20160Sstevel@tonic-gate 			 * If so succeed.
20170Sstevel@tonic-gate 			 */
20180Sstevel@tonic-gate 			len1 = strlen(addrname1);
20190Sstevel@tonic-gate 			len2 = strlen(addrname2);
20200Sstevel@tonic-gate 			if ((len1 < len2) &&
20210Sstevel@tonic-gate 			    (strncmp(addrname1, addrname2, len1) == 0) &&
20220Sstevel@tonic-gate 			    (strcmp(wildcard, &addrname2[len1]) == 0)) {
20230Sstevel@tonic-gate 				continue;
20240Sstevel@tonic-gate 			} else if ((len2 < len1) &&
20250Sstevel@tonic-gate 			    (strncmp(addrname1, addrname2, len2) == 0) &&
20260Sstevel@tonic-gate 			    (strcmp(wildcard, &addrname1[len2]) == 0)) {
20270Sstevel@tonic-gate 				continue;
20280Sstevel@tonic-gate 			}
20290Sstevel@tonic-gate 			error = NO_MATCH;
20300Sstevel@tonic-gate 			break;
20310Sstevel@tonic-gate 		}
20320Sstevel@tonic-gate 	}
20330Sstevel@tonic-gate 
20340Sstevel@tonic-gate 	/*
20350Sstevel@tonic-gate 	 * if either of the two device paths still has more components,
20360Sstevel@tonic-gate 	 * then we do not have a match.
20370Sstevel@tonic-gate 	 */
20380Sstevel@tonic-gate 	if (ptr1 != NULL) {
20390Sstevel@tonic-gate 		*ptr1 = '/';
20400Sstevel@tonic-gate 		error = NO_MATCH;
20410Sstevel@tonic-gate 	}
20420Sstevel@tonic-gate 	if (ptr2 != NULL) {
20430Sstevel@tonic-gate 		*ptr2 = '/';
20440Sstevel@tonic-gate 		error = NO_MATCH;
20450Sstevel@tonic-gate 	}
20460Sstevel@tonic-gate 	if (error == NO_MATCH) {
20470Sstevel@tonic-gate 		return (error);
20480Sstevel@tonic-gate 	}
20490Sstevel@tonic-gate 
20500Sstevel@tonic-gate 	/*
20510Sstevel@tonic-gate 	 * OK - we found a possible match but one or more of the
20520Sstevel@tonic-gate 	 * path components was not fully qualified (did not have any
20530Sstevel@tonic-gate 	 * address information.  So we need to convert it to a form
20540Sstevel@tonic-gate 	 * that is fully qualified and then compare the resulting
20550Sstevel@tonic-gate 	 * strings.
20560Sstevel@tonic-gate 	 */
20570Sstevel@tonic-gate 	if (unqualified_name != 0) {
20580Sstevel@tonic-gate 		if ((devfs_prom_to_dev_name(prom_dev1, devname1) < 0) ||
20590Sstevel@tonic-gate 		    (devfs_prom_to_dev_name(prom_dev2, devname2) < 0)) {
20600Sstevel@tonic-gate 			return (NO_MATCH);
20610Sstevel@tonic-gate 		}
20620Sstevel@tonic-gate 		if ((dev1 = strrchr(devname1, ':')) != NULL) {
20630Sstevel@tonic-gate 			*dev1 = '\0';
20640Sstevel@tonic-gate 		}
20650Sstevel@tonic-gate 		if ((dev2 = strrchr(devname2, ':')) != NULL) {
20660Sstevel@tonic-gate 			*dev2 = '\0';
20670Sstevel@tonic-gate 		}
20680Sstevel@tonic-gate 		if (strcmp(devname1, devname2) != 0) {
20690Sstevel@tonic-gate 			return (NO_MATCH);
20700Sstevel@tonic-gate 		}
20710Sstevel@tonic-gate 	}
20720Sstevel@tonic-gate 	/*
20730Sstevel@tonic-gate 	 * the resulting strings matched.  If the minorname information
20740Sstevel@tonic-gate 	 * matches, then we have an exact match, otherwise an inexact match
20750Sstevel@tonic-gate 	 */
20760Sstevel@tonic-gate 	if (_prom_strcmp(minorname1, minorname2) == 0) {
20770Sstevel@tonic-gate 		return (EXACT_MATCH);
20780Sstevel@tonic-gate 	} else {
20790Sstevel@tonic-gate 		return (INEXACT_MATCH);
20800Sstevel@tonic-gate 	}
20810Sstevel@tonic-gate }
20820Sstevel@tonic-gate 
20830Sstevel@tonic-gate /*
20840Sstevel@tonic-gate  * wrapper or strcmp - deals with null strings.
20850Sstevel@tonic-gate  */
20860Sstevel@tonic-gate static int
_prom_strcmp(char * s1,char * s2)20870Sstevel@tonic-gate _prom_strcmp(char *s1, char *s2)
20880Sstevel@tonic-gate {
20890Sstevel@tonic-gate 	if ((s1 == NULL) && (s2 == NULL))
20900Sstevel@tonic-gate 		return (0);
20910Sstevel@tonic-gate 	if ((s1 == NULL) && (s2 != NULL)) {
20920Sstevel@tonic-gate 		return (-1);
20930Sstevel@tonic-gate 	}
20940Sstevel@tonic-gate 	if ((s1 != NULL) && (s2 == NULL)) {
20950Sstevel@tonic-gate 		return (1);
20960Sstevel@tonic-gate 	}
20970Sstevel@tonic-gate 	return (strcmp(s1, s2));
20980Sstevel@tonic-gate }
20990Sstevel@tonic-gate /*
21000Sstevel@tonic-gate  * break device@a,b:minor into components
21010Sstevel@tonic-gate  */
21020Sstevel@tonic-gate static void
parse_name(char * name,char ** drvname,char ** addrname,char ** minorname)21030Sstevel@tonic-gate parse_name(char *name, char **drvname, char **addrname, char **minorname)
21040Sstevel@tonic-gate {
21050Sstevel@tonic-gate 	char *cp, ch;
21060Sstevel@tonic-gate 
21070Sstevel@tonic-gate 	cp = *drvname = name;
21080Sstevel@tonic-gate 	*addrname = *minorname = NULL;
21090Sstevel@tonic-gate 	if (*name == '@')
21100Sstevel@tonic-gate 		*drvname = NULL;
21110Sstevel@tonic-gate 
21120Sstevel@tonic-gate 	while ((ch = *cp) != '\0') {
21130Sstevel@tonic-gate 		if (ch == '@')
21140Sstevel@tonic-gate 			*addrname = ++cp;
21150Sstevel@tonic-gate 		else if (ch == ':')
21160Sstevel@tonic-gate 			*minorname = ++cp;
21170Sstevel@tonic-gate 		++cp;
21180Sstevel@tonic-gate 	}
21190Sstevel@tonic-gate 	if (*addrname) {
21200Sstevel@tonic-gate 		*((*addrname)-1) = '\0';
21210Sstevel@tonic-gate 	}
21220Sstevel@tonic-gate 	if (*minorname) {
21230Sstevel@tonic-gate 		*((*minorname)-1) = '\0';
21240Sstevel@tonic-gate 	}
21250Sstevel@tonic-gate }
21260Sstevel@tonic-gate 
21270Sstevel@tonic-gate /*
21283337Sas158974  * converts a prom alias to a prom device name.
21293337Sas158974  * if we find no matching device, then we fail since if were
21303337Sas158974  * given a valid alias, then by definition, there must be a
21313337Sas158974  * device pathname associated with it in the /aliases node.
21323337Sas158974  */
21333337Sas158974 static int
alias_to_prom_dev(char * alias,char * ret_buf)21343337Sas158974 alias_to_prom_dev(char *alias, char *ret_buf)
21353337Sas158974 {
21363337Sas158974 	char *options_ptr;
21373337Sas158974 	char alias_buf[MAXNAMELEN];
21383337Sas158974 	char alias_def[MAXPATHLEN];
21393337Sas158974 	char options[16] = "";
21403337Sas158974 	int prom_fd = -1;
21413337Sas158974 	int ret;
21423337Sas158974 	int i;
21433337Sas158974 
21443337Sas158974 	if (strchr(alias, '/') != NULL)
21453337Sas158974 		return (DEVFS_INVAL);
21463337Sas158974 
21473337Sas158974 	if (strlen(alias) > (MAXNAMELEN - 1))
21483337Sas158974 		return (DEVFS_INVAL);
21493337Sas158974 
21503337Sas158974 	if (ret_buf == NULL) {
21513337Sas158974 		return (DEVFS_INVAL);
21523337Sas158974 	}
21533337Sas158974 
21543337Sas158974 	prom_fd = prom_open(O_RDONLY);
21553337Sas158974 	if (prom_fd < 0) {
21563337Sas158974 		return (prom_fd);
21573337Sas158974 	}
21583337Sas158974 
21593337Sas158974 	(void) strlcpy(alias_buf, alias, sizeof (alias_buf));
21603337Sas158974 
21613337Sas158974 	/*
21623337Sas158974 	 * save off any options (minor name info) that is
21633337Sas158974 	 * explicitly called out in the alias name
21643337Sas158974 	 */
21653337Sas158974 	if ((options_ptr = strchr(alias_buf, ':')) != NULL) {
21663337Sas158974 		*options_ptr = '\0';
21673337Sas158974 		(void) strlcpy(options, ++options_ptr, sizeof (options));
21683337Sas158974 	}
21693337Sas158974 
21703337Sas158974 	*alias_def = '\0';
21713337Sas158974 
21723337Sas158974 	ret = prom_find_aliases_node(prom_fd);
21733337Sas158974 	if (ret == 0) {
21743337Sas158974 		/*
21753337Sas158974 		 * we loop because one alias may define another... we have
21763337Sas158974 		 * to work our way down to an actual device definition.
21773337Sas158974 		 */
21783337Sas158974 		for (i = 0; i <= 10; i++) {
21793337Sas158974 			ret = prom_srch_node(prom_fd, alias_buf, alias_def);
21803337Sas158974 			if (ret == -1) {
21813337Sas158974 				break;
21823337Sas158974 			}
21833337Sas158974 			(void) strlcpy(alias_buf, alias_def,
21843337Sas158974 			    sizeof (alias_buf));
21853337Sas158974 			if (*alias_def == '/') {
21863337Sas158974 				break;
21873337Sas158974 			}
21883337Sas158974 
21893337Sas158974 			/*
21903337Sas158974 			 * save off any explicit options (minor name info)
21913337Sas158974 			 * if none has been encountered yet
21923337Sas158974 			 */
21933337Sas158974 			if (options_ptr == NULL) {
21943337Sas158974 				options_ptr = strchr(alias_buf, ':');
21953337Sas158974 				if (options_ptr != NULL) {
2196*9735SSriman.Bhavanam@Sun.COM 					*options_ptr = '\0';
2197*9735SSriman.Bhavanam@Sun.COM 					(void) strlcpy(options, ++options_ptr,
21983337Sas158974 					    sizeof (options));
21993337Sas158974 				}
22003337Sas158974 			}
22013337Sas158974 		}
22023337Sas158974 	}
22033337Sas158974 	prom_close(prom_fd);
22043337Sas158974 
22053337Sas158974 	/* error */
22063337Sas158974 	if (ret == -1) {
22073337Sas158974 		return (ret);
22083337Sas158974 	}
22093337Sas158974 
22103931Sas158974 	(void) strlcpy(ret_buf, alias_def, MAXPATHLEN);
22113337Sas158974 
22123337Sas158974 	/* override minor name information */
22133337Sas158974 	if (options_ptr != NULL) {
22143337Sas158974 		if ((options_ptr = strrchr(ret_buf, ':')) == NULL) {
22153337Sas158974 			(void) strcat(ret_buf, ":");
22163337Sas158974 		} else {
22173337Sas158974 			*(++options_ptr) = '\0';
22183337Sas158974 		}
22193337Sas158974 		(void) strcat(ret_buf, options);
22203337Sas158974 	}
22213337Sas158974 	return (0);
22223337Sas158974 }
22233337Sas158974 
22243337Sas158974 /*
22253337Sas158974  * search a prom node for a property name
22263337Sas158974  */
22273337Sas158974 static int
prom_srch_node(int fd,char * prop_name,char * ret_buf)22283337Sas158974 prom_srch_node(int fd, char *prop_name, char *ret_buf)
22293337Sas158974 {
22303337Sas158974 	Oppbuf  oppbuf;
22313337Sas158974 	struct openpromio *opp = &(oppbuf.opp);
22323337Sas158974 	int *ip = (int *)((void *)opp->oprom_array);
22333337Sas158974 
22343337Sas158974 	(void) memset(oppbuf.buf, 0, BUFSIZE);
22353337Sas158974 	opp->oprom_size = MAXPROPSIZE;
22363337Sas158974 	*ip = 0;
22373337Sas158974 
22383337Sas158974 	if (ioctl(fd, OPROMNXTPROP, opp) < 0)
22393337Sas158974 		return (-1);
22403337Sas158974 	if (opp->oprom_size == 0)
22413337Sas158974 		return (-1);
22423337Sas158974 
22433337Sas158974 	while (strcmp(prop_name, opp->oprom_array) != 0) {
22443337Sas158974 		opp->oprom_size = MAXPROPSIZE;
22453337Sas158974 		if (ioctl(fd, OPROMNXTPROP, opp) < 0)
22463337Sas158974 			return (-1);
22473337Sas158974 		if (opp->oprom_size == 0)
22483337Sas158974 			return (-1);
22493337Sas158974 	}
22503337Sas158974 	opp->oprom_size = MAXVALSIZE;
22513337Sas158974 	if (ioctl(fd, OPROMGETPROP, opp) < 0)
22523337Sas158974 		return (-1);
22533337Sas158974 
22543337Sas158974 	if (opp->oprom_size == 0)
22553337Sas158974 		return (-1);
22563931Sas158974 	(void) strlcpy(ret_buf, opp->oprom_array, MAXPATHLEN);
22573337Sas158974 	return (0);
22583337Sas158974 }
22593337Sas158974 
22603337Sas158974 /*
22613337Sas158974  * return the aliases node.
22623337Sas158974  */
22633337Sas158974 static int
prom_find_aliases_node(int fd)22643337Sas158974 prom_find_aliases_node(int fd)
22653337Sas158974 {
22663337Sas158974 	uint_t child_id;
22673931Sas158974 	char buf[MAXPATHLEN];
22683337Sas158974 
22693337Sas158974 	if ((child_id = prom_next_node(fd, 0)) == 0)
22703337Sas158974 		return (-1);
22713337Sas158974 	if ((child_id = prom_child_node(fd, child_id)) == 0)
22723337Sas158974 		return (-1);
22733337Sas158974 
22743337Sas158974 	while (child_id != 0) {
22753337Sas158974 		if (prom_srch_node(fd, "name", buf) == 0) {
22763337Sas158974 			if (strcmp(buf, "aliases") == 0) {
22773337Sas158974 				return (0);
22783337Sas158974 			}
22793337Sas158974 		}
22803337Sas158974 		child_id = prom_next_node(fd, child_id);
22813337Sas158974 	}
22823337Sas158974 	return (-1);
22833337Sas158974 }
22843337Sas158974 
22853337Sas158974 /*
22863337Sas158974  * get sibling
22873337Sas158974  */
22883337Sas158974 static uint_t
prom_next_node(int fd,uint_t node_id)22893337Sas158974 prom_next_node(int fd, uint_t node_id)
22903337Sas158974 {
22913337Sas158974 	Oppbuf  oppbuf;
22923337Sas158974 	struct openpromio *opp = &(oppbuf.opp);
22933337Sas158974 	uint_t *ip = (uint_t *)((void *)opp->oprom_array);
22943337Sas158974 
22953337Sas158974 	(void) memset(oppbuf.buf, 0, BUFSIZE);
22963337Sas158974 	opp->oprom_size = MAXVALSIZE;
22973337Sas158974 	*ip = node_id;
22983337Sas158974 
22993337Sas158974 	if (ioctl(fd, OPROMNEXT, opp) < 0)
23003337Sas158974 		return (0);
23013337Sas158974 
23023337Sas158974 	return (*(uint_t *)((void *)opp->oprom_array));
23033337Sas158974 }
23043337Sas158974 
23053337Sas158974 /*
23063337Sas158974  * get child
23073337Sas158974  */
23083337Sas158974 static uint_t
prom_child_node(int fd,uint_t node_id)23093337Sas158974 prom_child_node(int fd, uint_t node_id)
23103337Sas158974 {
23113337Sas158974 	Oppbuf  oppbuf;
23123337Sas158974 	struct openpromio *opp = &(oppbuf.opp);
23133337Sas158974 	uint_t *ip = (uint_t *)((void *)opp->oprom_array);
23143337Sas158974 
23153337Sas158974 	(void) memset(oppbuf.buf, 0, BUFSIZE);
23163337Sas158974 	opp->oprom_size = MAXVALSIZE;
23173337Sas158974 	*ip = node_id;
23183337Sas158974 
23193337Sas158974 	if (ioctl(fd, OPROMCHILD, opp) < 0)
23203337Sas158974 		return (0);
23213337Sas158974 
23223337Sas158974 	return (*(uint_t *)((void *)opp->oprom_array));
23233337Sas158974 }
23243337Sas158974 
23253337Sas158974 /*
23260Sstevel@tonic-gate  * only on sparc for now
23270Sstevel@tonic-gate  */
23280Sstevel@tonic-gate int
devfs_bootdev_modifiable(void)23290Sstevel@tonic-gate devfs_bootdev_modifiable(void)
23300Sstevel@tonic-gate {
23310Sstevel@tonic-gate #if defined(sparc)
23320Sstevel@tonic-gate 	return (0);
23330Sstevel@tonic-gate #else
23340Sstevel@tonic-gate 	return (DEVFS_NOTSUP);
23350Sstevel@tonic-gate #endif
23360Sstevel@tonic-gate }
2337