6874797 Solaris needs to support I/O subtree reparenting
authorVikram Hegde <Vikram.Hegde@Sun.COM>
Fri, 09 Apr 2010 13:39:36 -0700
changeset 12116 ea985fb42600
parent 12115 3655f38d3bea
child 12117 79b43747e259
6874797 Solaris needs to support I/O subtree reparenting
usr/src/cmd/devfsadm/audio_link.c
usr/src/cmd/devfsadm/devfsadm.c
usr/src/cmd/devfsadm/devfsadm.h
usr/src/cmd/devfsadm/port_link.c
usr/src/lib/libdevinfo/devinfo.c
usr/src/lib/libdevinfo/libdevinfo.h
usr/src/lib/libdevinfo/mapfile-vers
usr/src/uts/common/fs/devfs/devfs_subr.c
usr/src/uts/common/io/devinfo.c
usr/src/uts/common/os/devcfg.c
usr/src/uts/common/os/inst_sync.c
usr/src/uts/common/os/instance.c
usr/src/uts/common/sys/ddi_impldefs.h
usr/src/uts/common/sys/devinfo_impl.h
usr/src/uts/common/sys/instance.h
usr/src/uts/common/sys/sunddi.h
usr/src/uts/sun4v/os/fillsysinfo.c
--- a/usr/src/cmd/devfsadm/audio_link.c	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/cmd/devfsadm/audio_link.c	Fri Apr 09 13:39:36 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <regex.h>
@@ -41,7 +40,7 @@
 
 extern int system_labeled;
 
-static void check_audio_link(char *secondary_link,
+static void check_audio_link(di_node_t anynode, char *secondary_link,
     const char *primary_link_format);
 
 static int audio_process(di_minor_t minor, di_node_t node);
@@ -99,12 +98,22 @@
 
 DEVFSADM_REMOVE_INIT_V0(audio_remove_cbt);
 
+static di_node_t anynode;
+
+int
+minor_init(void)
+{
+	anynode = DI_NODE_NIL;
+	return (DEVFSADM_SUCCESS);
+}
+
 int
 minor_fini(void)
 {
-	check_audio_link("audio", "sound/%d");
-	check_audio_link("audioctl", "sound/%dctl");
-	check_audio_link("dsp", "dsp%d");
+	check_audio_link(anynode, "audio", "sound/%d");
+	check_audio_link(anynode, "audioctl", "sound/%dctl");
+	check_audio_link(anynode, "dsp", "dsp%d");
+	anynode = DI_NODE_NIL;
 	return (DEVFSADM_SUCCESS);
 }
 
@@ -144,6 +153,7 @@
 	char *mn;
 
 	mn = di_minor_name(minor);
+	anynode = node;
 
 	/*
 	 * "Special" handling for /dev/sndstat and /dev/mixer.
@@ -184,6 +194,7 @@
 	if (system_labeled)
 		flags = DA_ADD|DA_AUDIO;
 
+	anynode = node;
 	mn = di_minor_name(minor);
 
 	if ((tmp = di_devfs_path(node)) == NULL) {
@@ -300,14 +311,14 @@
 }
 
 static void
-check_audio_link(char *secondary, const char *primary_format)
+check_audio_link(di_node_t anynode, char *secondary, const char *primary_format)
 {
 	char primary[PATH_MAX + 1];
 	int i;
 	int flags = 0;
 
 	/* if link is present, return */
-	if (devfsadm_link_valid(secondary) == DEVFSADM_TRUE) {
+	if (devfsadm_link_valid(anynode, secondary) == DEVFSADM_TRUE) {
 		return;
 	}
 
@@ -316,7 +327,7 @@
 
 	for (i = 0; i < MAX_AUDIO_LINK; i++) {
 		(void) sprintf(primary, primary_format, i);
-		if (devfsadm_link_valid(primary) == DEVFSADM_TRUE) {
+		if (devfsadm_link_valid(anynode, primary) == DEVFSADM_TRUE) {
 			/* we read link to get it to the master "real" link */
 			(void) devfsadm_secondary_link(secondary,
 			    primary, flags);
--- a/usr/src/cmd/devfsadm/devfsadm.c	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/cmd/devfsadm/devfsadm.c	Fri Apr 09 13:39:36 2010 -0700
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -251,6 +250,8 @@
 static syseventq_t *syseventq_back;
 static void process_syseventq();
 
+static di_node_t devi_root_node = DI_NODE_NIL;
+
 int
 main(int argc, char *argv[])
 {
@@ -1040,6 +1041,7 @@
 		flush_path_to_inst();
 
 	dcip->dci_arg = &mlist;
+	devi_root_node = node;	/* protected by lock_dev() */
 
 	vprint(CHATTY_MID, "walking device tree\n");
 
@@ -1064,6 +1066,7 @@
 		update_devdb = 0;
 	}
 
+	devi_root_node = DI_NODE_NIL;	/* protected by lock_dev() */
 	di_fini(node);
 }
 
@@ -2720,6 +2723,7 @@
 
 	/* Database is not updated when file_mods == FALSE */
 	if (file_mods == FALSE) {
+		/* we want *actual* link contents so no alias redirection */
 		linksize = readlink(devlink, checkcontents, PATH_MAX);
 		if (linksize > 0) {
 			checkcontents[linksize] = '\0';
@@ -2786,6 +2790,13 @@
 
 		case READ_LINK:
 
+			/*
+			 * If there is redirection, new phys path
+			 * and old phys path will not match and the
+			 * link will be created with new phys path
+			 * which is what we want. So we want real
+			 * contents.
+			 */
 			linksize = readlink(devlink, checkcontents, PATH_MAX);
 			if (linksize >= 0) {
 				checkcontents[linksize] = '\0';
@@ -2867,9 +2878,13 @@
 	 * minor node using a snapshot on the physical path
 	 */
 	(void) resolve_link(devlink, NULL, NULL, &devfs_path, 0);
+	/*
+	 * We dont need redirection here - the actual link contents
+	 * whether "alias" or "current" are fine
+	 */
 	if (devfs_path) {
 		di_node_t node;
-		char *drv = NULL;
+		char *drv;
 		struct driver_list *list;
 		char *p;
 
@@ -2887,6 +2902,7 @@
 
 		node = di_init(pwd_buf, DINFOMINOR);
 
+		drv = NULL;
 		if (node) {
 			drv = di_driver_name(node);
 
@@ -2894,7 +2910,6 @@
 				vprint(FILES_MID, "%s: driver is %s\n",
 				    devlink, drv);
 			}
-			di_fini(node);
 		}
 		/* search thru the driver list specified in logindevperm */
 		list = newdev->ldev_driver_list;
@@ -2915,6 +2930,7 @@
 			}
 		}
 		free(devfs_path);
+		di_fini(node);
 	} else {
 		return;
 	}
@@ -3126,20 +3142,31 @@
 
 	vprint(REMOVE_MID, "%s%s\n", fcn, file);
 
-	/* TYPE_LINK split into multiple if's due to excessive indentations */
-	if (file_type == TYPE_LINK) {
-		(void) strcpy(newfile, dev_dir);
-		(void) strcat(newfile, "/");
-		(void) strcat(newfile, file);
-	}
-
-	if ((file_type == TYPE_LINK) && (recurse == TRUE) &&
+	/*
+	 * Note: we don't remove /devices (non-links) entries because they are
+	 *	covered by devfs.
+	 */
+	if (file_type != TYPE_LINK) {
+		return;
+	}
+
+	/* split into multiple if's due to excessive indentations */
+	(void) strcpy(newfile, dev_dir);
+	(void) strcat(newfile, "/");
+	(void) strcat(newfile, file);
+
+	/*
+	 * we dont care about the content of the symlink, so
+	 * redirection is not needed.
+	 */
+	if ((recurse == TRUE) &&
 	    ((linksize = readlink(newfile, contents, PATH_MAX)) > 0)) {
 		contents[linksize] = '\0';
 
-		if (is_minor_node(contents, &ptr) == DEVFSADM_TRUE) {
-			devfsadm_rm_work(++ptr, FALSE, TYPE_DEVICES);
-		} else {
+		/*
+		 * recurse if link points to another link
+		 */
+		if (is_minor_node(contents, &ptr) != DEVFSADM_TRUE) {
 			if (strncmp(contents, DEV "/", strlen(DEV) + 1) == 0) {
 				devfsadm_rm_work(&contents[strlen(DEV) + 1],
 				    TRUE, TYPE_LINK);
@@ -3158,21 +3185,14 @@
 		}
 	}
 
-	if (file_type == TYPE_LINK) {
-		vprint(VERBOSE_MID, DEVFSADM_UNLINK, newfile);
-		if (file_mods == TRUE) {
-			rm_link_from_cache(file);
-			s_unlink(newfile);
-			rm_parent_dir_if_empty(newfile);
-			invalidate_enumerate_cache();
-			(void) di_devlink_rm_link(devlink_cache, file);
-		}
-	}
-
-	/*
-	 * Note: we don't remove /devices entries because they are
-	 *	covered by devfs.
-	 */
+	vprint(VERBOSE_MID, DEVFSADM_UNLINK, newfile);
+	if (file_mods == TRUE) {
+		rm_link_from_cache(file);
+		s_unlink(newfile);
+		rm_parent_dir_if_empty(newfile);
+		invalidate_enumerate_cache();
+		(void) di_devlink_rm_link(devlink_cache, file);
+	}
 }
 
 void
@@ -3284,6 +3304,9 @@
  * dir_re, and then it searches all links in that cache looking for
  * any link whose contents match "valid_link_contents" with a corresponding link
  * which does not match "valid_link".  Any such matches are stale and removed.
+ *
+ * This happens outside the context of a "reparenting" so we dont need
+ * redirection.
  */
 void
 devfsadm_rm_stale_links(char *dir_re, char *valid_link, di_node_t node,
@@ -3421,6 +3444,10 @@
 	(void) strcpy(newlink, devlink);
 
 	do {
+		/*
+		 * None of the consumers of this function need redirection
+		 * so this readlink gets the "current" contents
+		 */
 		linksize = readlink(newlink, contents, PATH_MAX);
 		if (linksize <= 0) {
 			/*
@@ -4429,6 +4456,10 @@
 	if (nfphash_lookup(devpath + norm_len) != NULL)
 		return;
 
+	/*
+	 * Dangling check will work whether "alias" or "current"
+	 * so no need to redirect.
+	 */
 	if (resolve_link(devpath, NULL, NULL, NULL, 1) == TRUE) {
 		if (call_minor_init(cleanup_data->rm->modptr) ==
 		    DEVFSADM_FAILURE) {
@@ -4456,9 +4487,10 @@
 }
 
 int
-devfsadm_read_link(char *link, char **devfs_path)
+devfsadm_read_link(di_node_t anynode, char *link, char **devfs_path)
 {
 	char devlink[PATH_MAX];
+	char *path;
 
 	*devfs_path = NULL;
 
@@ -4468,16 +4500,20 @@
 	(void) strcat(devlink, link);
 
 	/* We *don't* want a stat of the /devices node */
-	(void) resolve_link(devlink, NULL, NULL, devfs_path, 0);
-
+	path = NULL;
+	(void) resolve_link(devlink, NULL, NULL, &path, 0);
+
+	/* redirect if alias to current */
+	*devfs_path = di_alias2curr(anynode, path);
+	free(path);
 	return (*devfs_path ? DEVFSADM_SUCCESS : DEVFSADM_FAILURE);
 }
 
 int
-devfsadm_link_valid(char *link)
+devfsadm_link_valid(di_node_t anynode, char *link)
 {
 	struct stat sb;
-	char devlink[PATH_MAX + 1], *contents = NULL;
+	char devlink[PATH_MAX + 1], *contents, *raw_contents;
 	int rv, type;
 	int instance = 0;
 
@@ -4490,15 +4526,24 @@
 		return (DEVFSADM_FALSE);
 	}
 
-	contents = NULL;
+	raw_contents = NULL;
 	type = 0;
-	if (resolve_link(devlink, &contents, &type, NULL, 1) == TRUE) {
+	if (resolve_link(devlink, &raw_contents, &type, NULL, 1) == TRUE) {
 		rv = DEVFSADM_FALSE;
 	} else {
 		rv = DEVFSADM_TRUE;
 	}
 
 	/*
+	 * resolve alias paths for primary links
+	 */
+	contents = raw_contents;
+	if (type == DI_PRIMARY_LINK) {
+		contents = di_alias2curr(anynode, raw_contents);
+		free(raw_contents);
+	}
+
+	/*
 	 * The link exists. Add it to the database
 	 */
 	(void) di_devlink_add_link(devlink_cache, link, contents, type);
@@ -4524,6 +4569,7 @@
  *	TRUE if dangling
  *	FALSE if not or if caller doesn't care
  * Caller is assumed to have initialized pointer contents to NULL
+ *
  */
 static int
 resolve_link(char *devpath, char **content_p, int *type_p, char **devfs_path,
@@ -4537,6 +4583,10 @@
 	int rv = TRUE;
 	struct stat sb;
 
+	/*
+	 * This routine will return the "raw" contents. It is upto the
+	 * the caller to redirect "alias" to "current" (or vice versa)
+	 */
 	linksize = readlink(devpath, contents, PATH_MAX);
 
 	if (linksize <= 0) {
@@ -5639,6 +5689,7 @@
 	numeral_t *np;
 	int linksize;
 	struct stat sb;
+	char *contents;
 	const char *fcn = "create_cached_numeral";
 
 	assert(index >= 0 && index < setp->re_count);
@@ -5692,14 +5743,21 @@
 	linkbuf[linksize] = '\0';
 
 	/*
+	 * redirect alias path to current path
+	 * devi_root_node is protected by lock_dev()
+	 */
+	contents = di_alias2curr(devi_root_node, linkbuf);
+
+	/*
 	 * the following just points linkptr to the root of the /devices
 	 * node if it is a minor node, otherwise, to the first char of
 	 * linkbuf if it is a link.
 	 */
-	(void) is_minor_node(linkbuf, &linkptr);
+	(void) is_minor_node(contents, &linkptr);
 
 	cmp_str = alloc_cmp_str(linkptr, &rules[index]);
 	if (cmp_str == NULL) {
+		free(contents);
 		return;
 	}
 
@@ -5713,6 +5771,8 @@
 
 	np->next = setp->headnumeral;
 	setp->headnumeral = np;
+
+	free(contents);
 }
 
 
@@ -5832,6 +5892,11 @@
 			return (DEVFSADM_SUCCESS);
 		}
 	} else if ((stat->st_mode & S_IFMT) == S_IFLNK)  {
+		/*
+		 * No need to redirect alias paths. We want a
+		 * true copy. The system on first boot after install
+		 * will redirect paths
+		 */
 		if ((bytes = readlink(file, linkcontents, PATH_MAX)) == -1)  {
 			err_print(READLINK_FAILED, fcn, file, strerror(errno));
 			return (DEVFSADM_SUCCESS);
--- a/usr/src/cmd/devfsadm/devfsadm.h	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/cmd/devfsadm/devfsadm.h	Fri Apr 09 13:39:36 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _DEVFSADM_H
@@ -212,7 +211,7 @@
 
 int devfsadm_noupdate(void);
 const char *devfsadm_root_path(void);
-int devfsadm_link_valid(char *link);
+int devfsadm_link_valid(di_node_t anynode, char *link);
 int devfsadm_mklink(char *link, di_node_t node, di_minor_t minor, int flags);
 int devfsadm_secondary_link(char *link, char *primary_link, int flags);
 void devfsadm_rm_link(char *file);
@@ -241,7 +240,7 @@
  */
 int devfsadm_enumerate_char_start(char *devfs_path, int index,
     char **buf, devfsadm_enumerate_t rules[], int nrules, char *start);
-int devfsadm_read_link(char *link, char **devfs_path);
+int devfsadm_read_link(di_node_t node, char *link, char **devfs_path);
 char *s_strdup(const char *ptr);
 
 /* Private interface between reserve subsystm and disks link generator */
--- a/usr/src/cmd/devfsadm/port_link.c	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/cmd/devfsadm/port_link.c	Fri Apr 09 13:39:36 2010 -0700
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <unistd.h>
@@ -419,7 +418,7 @@
  *	NULL otherwise
  */
 static char *
-check_compat_ports(char *phys_path, char *minor)
+check_compat_ports(di_node_t node, char *phys_path, char *minor)
 {
 	char portid = *minor;
 	char port[PATH_MAX];
@@ -429,7 +428,7 @@
 		return (NULL);
 
 	(void) snprintf(port, sizeof (port), "term/%c", portid);
-	if (devfsadm_read_link(port, &devfs_path) == DEVFSADM_SUCCESS &&
+	if (devfsadm_read_link(node, port, &devfs_path) == DEVFSADM_SUCCESS &&
 	    portcmp(devfs_path, phys_path) != 0) {
 		free(devfs_path);
 		return (NULL);
@@ -438,7 +437,7 @@
 	free(devfs_path);
 
 	(void) snprintf(port, sizeof (port), "cua/%c", portid);
-	if (devfsadm_read_link(port, &devfs_path) == DEVFSADM_SUCCESS &&
+	if (devfsadm_read_link(node, port, &devfs_path) == DEVFSADM_SUCCESS &&
 	    portcmp(devfs_path, phys_path) != 0) {
 		free(devfs_path);
 		return (NULL);
@@ -500,7 +499,7 @@
 	buf = NULL;
 
 #ifdef __i386
-	buf = check_compat_ports(p_path, minor_name);
+	buf = check_compat_ports(node, p_path, minor_name);
 #endif
 
 	/*
@@ -562,7 +561,7 @@
 	buf = NULL;
 
 #ifdef __i386
-	buf = check_compat_ports(p_path, mn);
+	buf = check_compat_ports(node, p_path, mn);
 #endif
 
 	/*
--- a/usr/src/lib/libdevinfo/devinfo.c	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/lib/libdevinfo/devinfo.c	Fri Apr 09 13:39:36 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -3696,8 +3695,53 @@
 	return (0);
 }
 
-di_node_t
-di_lookup_node(di_node_t root, char *devfspath)
+static char *
+alias_to_curr(di_node_t anynode, char *devfspath, di_node_t *nodep)
+{
+	caddr_t		pa;
+	struct di_all	*all;
+	struct di_alias *di_alias;
+	di_node_t	node;
+	char		*curr;
+	char		*cp;
+	char		*alias;
+	di_off_t off;
+	char buf[MAXPATHLEN];
+
+	*nodep = NULL;
+
+	assert(anynode != DI_NODE_NIL);
+
+	pa = (caddr_t)anynode - DI_NODE(anynode)->self;
+	all = DI_ALL(pa);
+
+	di_alias = NULL;
+	for (off = all->aliases; off > 0; off = di_alias->next) {
+		di_alias = DI_ALIAS(pa + off);
+		alias = di_alias->alias;
+		if (strncmp(devfspath, alias, strlen(alias)) == 0) {
+			cp = devfspath + strlen(alias);
+			node = DI_NODE(pa + di_alias->curroff);
+			assert(node != DI_NODE_NIL);
+			if (*cp == '\0') {
+				*nodep = node;
+				return (NULL);
+			} else if (*cp == '/') {
+				curr = di_devfs_path(node);
+				(void) snprintf(buf, sizeof (buf), "%s%s",
+				    curr, cp);
+				di_devfs_path_free(curr);
+				curr = strdup(buf);
+				return (curr);
+			}
+		}
+	}
+
+	return (NULL);
+}
+
+static di_node_t
+di_lookup_node_impl(di_node_t root, char *devfspath)
 {
 	struct di_all *dap;
 	di_node_t node;
@@ -3785,6 +3829,46 @@
 	return (node);
 }
 
+di_node_t
+di_lookup_node(di_node_t root, char *devfspath)
+{
+	di_node_t	node;
+	char		*curr;
+
+	node = di_lookup_node_impl(root, devfspath);
+	if (node != DI_NODE_NIL) {
+		return (node);
+	}
+
+	/* node is already set to DI_NODE_NIL */
+	curr = alias_to_curr(root, devfspath, &node);
+	if (curr == NULL) {
+		/* node may or may node be DI_NODE_NIL */
+		return (node);
+	}
+
+	node = di_lookup_node_impl(root, curr);
+
+	free(curr);
+
+	return (node);
+}
+
+char *
+di_alias2curr(di_node_t anynode, char *alias)
+{
+	di_node_t currnode = DI_NODE_NIL;
+	char *curr = alias_to_curr(anynode, alias, &currnode);
+
+	if (curr == NULL && currnode != DI_NODE_NIL) {
+		return (di_devfs_path(currnode));
+	} else if (curr == NULL) {
+		return (strdup(alias));
+	}
+
+	return (curr);
+}
+
 di_path_t
 di_lookup_path(di_node_t root, char *devfspath)
 {
--- a/usr/src/lib/libdevinfo/libdevinfo.h	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/lib/libdevinfo/libdevinfo.h	Fri Apr 09 13:39:36 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_LIBDEVINFO_H
@@ -559,6 +558,10 @@
 extern char	*di_dim_path_dev(di_dim_t,
 		    char *drv_name, int instance, char *minor_name);
 
+/*
+ * Alias related exported interfaces
+ */
+char *di_alias2curr(di_node_t anynode, char *alias);
 
 #ifdef	__cplusplus
 }
--- a/usr/src/lib/libdevinfo/mapfile-vers	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/lib/libdevinfo/mapfile-vers	Fri Apr 09 13:39:36 2010 -0700
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
 #
@@ -39,6 +38,7 @@
 
 SUNW_1.4 {
     global:
+	di_alias2curr;
 	di_path_bus_addr;
 	di_path_client_devfs_path;
 	di_path_client_next_path;
--- a/usr/src/uts/common/fs/devfs/devfs_subr.c	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/uts/common/fs/devfs/devfs_subr.c	Fri Apr 09 13:39:36 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -1094,6 +1093,23 @@
 		goto notfound;
 	}
 
+	ASSERT(devi);
+
+	/* Check if this is a path alias */
+	if (ddi_aliases_present == B_TRUE && ddi_get_parent(devi) != pdevi) {
+		char *curr = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+		(void) ddi_pathname(devi, curr);
+
+		vp = NULL;
+		if (devfs_lookupname(curr, NULL, &vp) == 0 && vp) {
+			dv = VTODV(vp);
+			kmem_free(curr, MAXPATHLEN);
+			goto found;
+		}
+		kmem_free(curr, MAXPATHLEN);
+	}
+
 	/*
 	 * If we configured a hidden node, consider it notfound.
 	 */
--- a/usr/src/uts/common/io/devinfo.c	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/uts/common/io/devinfo.c	Fri Apr 09 13:39:36 2010 -0700
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -767,12 +766,13 @@
 #endif
 
 	/* Copyin ioctl args, store in the snapshot. */
-	if (copyinstr((void *)arg, all->root_path,
+	if (copyinstr((void *)arg, all->req_path,
 	    sizeof (((struct dinfo_io *)(NULL))->root_path), &size) != 0) {
 		di_freemem(st);
 		(void) di_setstate(st, IOC_IDLE);
 		return (EFAULT);
 	}
+	(void) strcpy(all->root_path, all->req_path);
 	off += size;				/* real length of root_path */
 
 	if ((st->command & DINFOCLEANUP) && !DEVICES_FILES_CLEANABLE(st)) {
@@ -1295,6 +1295,44 @@
 	}
 }
 
+static void
+di_copy_aliases(struct di_state *st, alias_pair_t *apair, di_off_t *offp)
+{
+	di_off_t 		off;
+	struct di_all		*all = DI_ALL_PTR(st);
+	struct di_alias		*di_alias;
+	di_off_t		curroff;
+	dev_info_t		*currdip;
+	size_t			size;
+
+	currdip = NULL;
+	if (resolve_pathname(apair->pair_alias, &currdip, NULL, NULL) != 0) {
+		return;
+	}
+
+	if (di_dip_find(st, currdip, &curroff) != 0) {
+		ndi_rele_devi(currdip);
+		return;
+	}
+	ndi_rele_devi(currdip);
+
+	off = *offp;
+	size = sizeof (struct di_alias);
+	size += strlen(apair->pair_alias) + 1;
+	off = di_checkmem(st, off, size);
+	di_alias = DI_ALIAS(di_mem_addr(st, off));
+
+	di_alias->self = off;
+	di_alias->next = all->aliases;
+	all->aliases = off;
+	(void) strcpy(di_alias->alias, apair->pair_alias);
+	di_alias->curroff = curroff;
+
+	off += size;
+
+	*offp = off;
+}
+
 /*
  * This is the main function that takes a snapshot
  */
@@ -1308,11 +1346,24 @@
 	int		plen;
 	char		*path;
 	vnode_t		*vp;
+	int		i;
 
 	all = DI_ALL_PTR(st);
 	dcmn_err((CE_CONT, "Taking a snapshot of devinfo tree...\n"));
 
 	/*
+	 * Translate requested root path if an alias and snap-root != "/"
+	 */
+	if (ddi_aliases_present == B_TRUE && strcmp(all->root_path, "/") != 0) {
+		/* If there is no redirected alias, use root_path as is */
+		rootnode = ddi_alias_redirect(all->root_path);
+		if (rootnode) {
+			(void) ddi_pathname(rootnode, all->root_path);
+			goto got_root;
+		}
+	}
+
+	/*
 	 * Verify path before entrusting it to e_ddi_hold_devi_by_path because
 	 * some platforms have OBP bugs where executing the NDI_PROMNAME code
 	 * path against an invalid path results in panic.  The lookupnameat
@@ -1341,6 +1392,7 @@
 		return (0);
 	}
 
+got_root:
 	(void) snprintf(buf, sizeof (buf),
 	    "devinfo registered dips (statep=%p)", (void *)st);
 
@@ -1391,6 +1443,15 @@
 		off = di_getlink_data(off, st);
 	}
 
+	all->aliases = 0;
+	if (ddi_aliases_present == B_FALSE)
+		goto done;
+
+	for (i = 0; i < ddi_aliases.dali_num_pairs; i++) {
+		di_copy_aliases(st, &(ddi_aliases.dali_alias_pairs[i]), &off);
+	}
+
+done:
 	/*
 	 * Free up hash tables
 	 */
--- a/usr/src/uts/common/os/devcfg.c	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/uts/common/os/devcfg.c	Fri Apr 09 13:39:36 2010 -0700
@@ -55,14 +55,14 @@
 #include <sys/sunldi.h>
 #include <sys/sunldi_impl.h>
 #include <sys/bootprops.h>
+#include <sys/varargs.h>
+#include <sys/modhash.h>
+#include <sys/instance.h>
 
 #if defined(__amd64) && !defined(__xpv)
 #include <sys/iommulib.h>
 #endif
 
-/* XXX remove before putback */
-boolean_t ddi_err_panic = B_TRUE;
-
 #ifdef DEBUG
 int ddidebug = DDI_AUDIT;
 #else
@@ -170,10 +170,18 @@
 
 int quiesce_debug = 0;
 
+boolean_t ddi_aliases_present = B_FALSE;
+ddi_alias_t ddi_aliases;
+uint_t tsd_ddi_redirect;
+
+#define	DDI_ALIAS_HASH_SIZE	(2700)
+
 static kmem_cache_t *ddi_node_cache;		/* devinfo node cache */
 static devinfo_log_header_t *devinfo_audit_log;	/* devinfo log */
 static int devinfo_log_size;			/* size in pages */
 
+boolean_t ddi_err_panic = B_FALSE;
+
 static int lookup_compatible(dev_info_t *, uint_t);
 static char *encode_composite_string(char **, uint_t, size_t *, uint_t);
 static void link_to_driver_list(dev_info_t *);
@@ -209,6 +217,10 @@
 
 static void quiesce_one_device(dev_info_t *, void *);
 
+dev_info_t *ddi_alias_redirect(char *alias);
+char *ddi_curr_redirect(char *currpath);
+
+
 /*
  * dev_info cache and node management
  */
@@ -5485,21 +5497,30 @@
  * an entire branch.
  */
 int
-ndi_devi_config_one(dev_info_t *dip, char *devnm, dev_info_t **dipp, int flags)
+ndi_devi_config_one(dev_info_t *pdip, char *devnm, dev_info_t **dipp, int flags)
 {
 	int error;
 	int (*f)();
+	char *nmdup;
+	int duplen;
 	int branch_event = 0;
 
+	ASSERT(pdip);
+	ASSERT(devnm);
 	ASSERT(dipp);
-	ASSERT(i_ddi_devi_attached(dip));
+	ASSERT(i_ddi_devi_attached(pdip));
 
 	NDI_CONFIG_DEBUG((CE_CONT,
 	    "ndi_devi_config_one: par = %s%d (%p), child = %s\n",
-	    ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip, devnm));
-
-	if (pm_pre_config(dip, devnm) != DDI_SUCCESS)
+	    ddi_driver_name(pdip), ddi_get_instance(pdip),
+	    (void *)pdip, devnm));
+
+	*dipp = NULL;
+
+	if (pm_pre_config(pdip, devnm) != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "preconfig failed: %s", devnm);
 		return (NDI_FAILURE);
+	}
 
 	if ((flags & (NDI_NO_EVENT | NDI_BRANCH_EVENT_OP)) == 0 &&
 	    (flags & NDI_CONFIG)) {
@@ -5507,17 +5528,47 @@
 		branch_event = 1;
 	}
 
-	if ((DEVI(dip)->devi_ops->devo_bus_ops == NULL) ||
-	    (DEVI(dip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_5) ||
-	    (f = DEVI(dip)->devi_ops->devo_bus_ops->bus_config) == NULL) {
-		error = devi_config_one(dip, devnm, dipp, flags, 0);
+	nmdup = strdup(devnm);
+	duplen = strlen(devnm) + 1;
+
+	if ((DEVI(pdip)->devi_ops->devo_bus_ops == NULL) ||
+	    (DEVI(pdip)->devi_ops->devo_bus_ops->busops_rev < BUSO_REV_5) ||
+	    (f = DEVI(pdip)->devi_ops->devo_bus_ops->bus_config) == NULL) {
+		error = devi_config_one(pdip, devnm, dipp, flags, 0);
 	} else {
 		/* call bus_config entry point */
-		error = (*f)(dip, flags, BUS_CONFIG_ONE, (void *)devnm, dipp);
-	}
-
-	if (error || (flags & NDI_CONFIG) == 0) {
-		pm_post_config(dip, devnm);
+		error = (*f)(pdip, flags, BUS_CONFIG_ONE, (void *)devnm, dipp);
+	}
+
+	if (error) {
+		*dipp = NULL;
+	}
+
+	/*
+	 * if we fail to lookup and this could be an alias, lookup currdip
+	 * To prevent recursive lookups into the same hash table, only
+	 * do the currdip lookups once the hash table init is complete.
+	 * Use tsd so that redirection doesn't recurse
+	 */
+	if (error) {
+		char *alias = kmem_alloc(MAXPATHLEN, KM_NOSLEEP);
+		if (alias == NULL) {
+			ddi_err(DER_PANIC, pdip, "alias alloc failed: %s",
+			    nmdup);
+		}
+		(void) ddi_pathname(pdip, alias);
+		(void) strlcat(alias, "/", MAXPATHLEN);
+		(void) strlcat(alias, nmdup, MAXPATHLEN);
+
+		*dipp = ddi_alias_redirect(alias);
+		error = (*dipp ? NDI_SUCCESS : NDI_FAILURE);
+
+		kmem_free(alias, MAXPATHLEN);
+	}
+	kmem_free(nmdup, duplen);
+
+	if (error || !(flags & NDI_CONFIG)) {
+		pm_post_config(pdip, devnm);
 		return (error);
 	}
 
@@ -5529,7 +5580,7 @@
 	ASSERT(*dipp);
 	error = devi_config_common(*dipp, flags, DDI_MAJOR_T_NONE);
 
-	pm_post_config(dip, devnm);
+	pm_post_config(pdip, devnm);
 
 	if (branch_event)
 		(void) i_log_devfs_branch_add(*dipp);
@@ -5537,7 +5588,6 @@
 	return (error);
 }
 
-
 /*
  * Enumerate and attach a child specified by name 'devnm'.
  * Called during configure the OBP options. This configures
@@ -8553,6 +8603,375 @@
 		mdi_phci_retire_finalize(dip, phci_only, &constraint);
 }
 
+
+#define	VAL_ALIAS(array, x)	(strlen(array[x].pair_alias))
+#define	VAL_CURR(array, x)	(strlen(array[x].pair_curr))
+#define	SWAP(array, x, y)			\
+{						\
+	alias_pair_t tmpair = array[x];		\
+	array[x] = array[y];			\
+	array[y] = tmpair;			\
+}
+
+static int
+partition_curr(alias_pair_t *array, int start, int end)
+{
+	int	i = start - 1;
+	int	j = end + 1;
+	int	pivot = start;
+
+	for (;;) {
+		do {
+			j--;
+		} while (VAL_CURR(array, j) > VAL_CURR(array, pivot));
+
+		do {
+			i++;
+		} while (VAL_CURR(array, i) < VAL_CURR(array, pivot));
+
+		if (i < j)
+			SWAP(array, i, j)
+		else
+			return (j);
+	}
+}
+
+static int
+partition_aliases(alias_pair_t *array, int start, int end)
+{
+	int	i = start - 1;
+	int	j = end + 1;
+	int	pivot = start;
+
+	for (;;) {
+		do {
+			j--;
+		} while (VAL_ALIAS(array, j) > VAL_ALIAS(array, pivot));
+
+		do {
+			i++;
+		} while (VAL_ALIAS(array, i) < VAL_ALIAS(array, pivot));
+
+		if (i < j)
+			SWAP(array, i, j)
+		else
+			return (j);
+	}
+}
+static void
+sort_alias_pairs(alias_pair_t *array, int start, int end)
+{
+	int mid;
+
+	if (start < end) {
+		mid = partition_aliases(array, start, end);
+		sort_alias_pairs(array, start, mid);
+		sort_alias_pairs(array, mid + 1, end);
+	}
+}
+
+static void
+sort_curr_pairs(alias_pair_t *array, int start, int end)
+{
+	int mid;
+
+	if (start < end) {
+		mid = partition_curr(array, start, end);
+		sort_curr_pairs(array, start, mid);
+		sort_curr_pairs(array, mid + 1, end);
+	}
+}
+
+static void
+create_sorted_pairs(plat_alias_t *pali, int npali)
+{
+	int		i;
+	int		j;
+	int		k;
+	int		count;
+
+	count = 0;
+	for (i = 0; i < npali; i++) {
+		count += pali[i].pali_naliases;
+	}
+
+	ddi_aliases.dali_alias_pairs = kmem_zalloc(
+	    (sizeof (alias_pair_t)) * count, KM_NOSLEEP);
+	if (ddi_aliases.dali_alias_pairs == NULL) {
+		cmn_err(CE_PANIC, "alias path-pair alloc failed");
+		/*NOTREACHED*/
+	}
+
+	ddi_aliases.dali_curr_pairs = kmem_zalloc(
+	    (sizeof (alias_pair_t)) * count, KM_NOSLEEP);
+	if (ddi_aliases.dali_curr_pairs == NULL) {
+		cmn_err(CE_PANIC, "curr path-pair alloc failed");
+		/*NOTREACHED*/
+	}
+
+	for (i = 0, k = 0; i < npali; i++) {
+		for (j = 0; j < pali[i].pali_naliases; j++, k++) {
+			ddi_aliases.dali_alias_pairs[k].pair_curr =
+			    ddi_aliases.dali_curr_pairs[k].pair_curr =
+			    pali[i].pali_current;
+			ddi_aliases.dali_alias_pairs[k].pair_alias =
+			    ddi_aliases.dali_curr_pairs[k].pair_alias =
+			    pali[i].pali_aliases[j];
+		}
+	}
+
+	ASSERT(k == count);
+
+	ddi_aliases.dali_num_pairs = count;
+
+	/* Now sort the array based on length of pair_alias */
+	sort_alias_pairs(ddi_aliases.dali_alias_pairs, 0, count - 1);
+	sort_curr_pairs(ddi_aliases.dali_curr_pairs, 0, count - 1);
+}
+
+void
+ddi_register_aliases(plat_alias_t *pali, uint64_t npali)
+{
+
+	ASSERT((pali == NULL) ^ (npali != 0));
+
+	if (npali == 0) {
+		ddi_err(DER_PANIC, NULL, "npali == 0");
+		/*NOTREACHED*/
+	}
+
+	if (ddi_aliases_present == B_TRUE) {
+		ddi_err(DER_PANIC, NULL, "multiple init");
+		/*NOTREACHED*/
+	}
+
+	ddi_aliases.dali_alias_TLB = mod_hash_create_strhash(
+	    "ddi-alias-tlb", DDI_ALIAS_HASH_SIZE, mod_hash_null_valdtor);
+	if (ddi_aliases.dali_alias_TLB == NULL) {
+		ddi_err(DER_PANIC, NULL, "alias TLB hash alloc failed");
+		/*NOTREACHED*/
+	}
+
+	ddi_aliases.dali_curr_TLB = mod_hash_create_strhash(
+	    "ddi-curr-tlb", DDI_ALIAS_HASH_SIZE, mod_hash_null_valdtor);
+	if (ddi_aliases.dali_curr_TLB == NULL) {
+		ddi_err(DER_PANIC, NULL, "curr TLB hash alloc failed");
+		/*NOTREACHED*/
+	}
+
+	create_sorted_pairs(pali, npali);
+
+	tsd_create(&tsd_ddi_redirect, NULL);
+
+	ddi_aliases_present = B_TRUE;
+}
+
+static dev_info_t *
+path_to_dip(char *path)
+{
+	dev_info_t	*currdip;
+	int		error;
+	char		*pdup;
+
+	pdup = ddi_strdup(path, KM_NOSLEEP);
+	if (pdup == NULL) {
+		cmn_err(CE_PANIC, "path strdup failed: %s", path);
+		/*NOTREACHED*/
+	}
+
+	error = resolve_pathname(pdup, &currdip, NULL, NULL);
+
+	kmem_free(pdup, strlen(path) + 1);
+
+	return (error ? NULL : currdip);
+}
+
+dev_info_t *
+ddi_alias_to_currdip(char *alias, int i)
+{
+	alias_pair_t *pair;
+	char *curr;
+	dev_info_t *currdip = NULL;
+	char *aliasdup;
+	int len;
+
+	pair = &(ddi_aliases.dali_alias_pairs[i]);
+	len = strlen(pair->pair_alias);
+
+	curr = NULL;
+	aliasdup = ddi_strdup(alias, KM_NOSLEEP);
+	if (aliasdup == NULL) {
+		cmn_err(CE_PANIC, "aliasdup alloc failed");
+		/*NOTREACHED*/
+	}
+
+	if (strncmp(alias, pair->pair_alias, len)  != 0)
+		goto out;
+
+	if (alias[len] != '/' && alias[len] != '\0')
+		goto out;
+
+
+	curr = kmem_alloc(MAXPATHLEN, KM_NOSLEEP);
+	if (curr == NULL) {
+		cmn_err(CE_PANIC, "curr alloc failed");
+		/*NOTREACHED*/
+	}
+	(void) strlcpy(curr, pair->pair_curr, MAXPATHLEN);
+	if (alias[len] == '/') {
+		(void) strlcat(curr, "/", MAXPATHLEN);
+		(void) strlcat(curr, &alias[len + 1], MAXPATHLEN);
+	}
+
+	currdip = path_to_dip(curr);
+
+out:
+	if (currdip) {
+		(void) mod_hash_insert(ddi_aliases.dali_alias_TLB,
+		    (mod_hash_key_t)aliasdup, (mod_hash_val_t)curr);
+	} else {
+		(void) mod_hash_insert(ddi_aliases.dali_alias_TLB,
+		    (mod_hash_key_t)aliasdup, (mod_hash_val_t)NULL);
+		if (curr)
+			kmem_free(curr, MAXPATHLEN);
+	}
+
+	return (currdip);
+}
+
+char *
+ddi_curr_to_alias(char *curr, int i)
+{
+	alias_pair_t	*pair;
+	char		*alias;
+	char		*currdup;
+	int		len;
+
+	pair = &(ddi_aliases.dali_curr_pairs[i]);
+
+	len = strlen(pair->pair_curr);
+
+	alias = NULL;
+
+	currdup = ddi_strdup(curr, KM_NOSLEEP);
+	if (currdup == NULL) {
+		cmn_err(CE_PANIC, "currdup alloc failed");
+		/*NOTREACHED*/
+	}
+
+	if (strncmp(curr, pair->pair_curr, len) != 0)
+		goto out;
+
+	if (curr[len] != '/' && curr[len] != '\0')
+		goto out;
+
+	alias = kmem_alloc(MAXPATHLEN, KM_NOSLEEP);
+	if (alias == NULL) {
+		cmn_err(CE_PANIC, "alias alloc failed");
+		/*NOTREACHED*/
+	}
+
+	(void) strlcpy(alias, pair->pair_alias, MAXPATHLEN);
+	if (curr[len] == '/') {
+		(void) strlcat(alias, "/", MAXPATHLEN);
+		(void) strlcat(alias, &curr[len + 1], MAXPATHLEN);
+	}
+
+	if (e_ddi_path_to_instance(alias) == NULL) {
+		kmem_free(alias, MAXPATHLEN);
+		alias = NULL;
+	}
+
+out:
+	(void) mod_hash_insert(ddi_aliases.dali_curr_TLB,
+	    (mod_hash_key_t)currdup, (mod_hash_val_t)alias);
+
+	return (alias);
+}
+
+dev_info_t *
+ddi_alias_redirect(char *alias)
+{
+	char		*curr;
+	char		*aliasdup;
+	dev_info_t	*currdip;
+	int		i;
+
+	if (ddi_aliases_present == B_FALSE)
+		return (NULL);
+
+	if (tsd_get(tsd_ddi_redirect))
+		return (NULL);
+
+	(void) tsd_set(tsd_ddi_redirect, (void *)1);
+
+	ASSERT(ddi_aliases.dali_alias_TLB);
+	ASSERT(ddi_aliases.dali_alias_pairs);
+
+	curr = NULL;
+	if (mod_hash_find(ddi_aliases.dali_alias_TLB,
+	    (mod_hash_key_t)alias, (mod_hash_val_t *)&curr) == 0) {
+		currdip = curr ? path_to_dip(curr) : NULL;
+		goto out;
+	}
+
+	aliasdup = ddi_strdup(alias, KM_NOSLEEP);
+	if (aliasdup == NULL) {
+		cmn_err(CE_PANIC, "aliasdup alloc failed");
+		/*NOTREACHED*/
+	}
+
+	/* The TLB has no translation, do it the hard way */
+	currdip = NULL;
+	for (i = ddi_aliases.dali_num_pairs - 1; i >= 0; i--) {
+		currdip = ddi_alias_to_currdip(alias, i);
+		if (currdip)
+			break;
+	}
+out:
+	(void) tsd_set(tsd_ddi_redirect, NULL);
+
+	return (currdip);
+}
+
+char *
+ddi_curr_redirect(char *curr)
+{
+	char 	*alias;
+	int i;
+
+	if (ddi_aliases_present == B_FALSE)
+		return (NULL);
+
+	if (tsd_get(tsd_ddi_redirect))
+		return (NULL);
+
+	(void) tsd_set(tsd_ddi_redirect, (void *)1);
+
+	ASSERT(ddi_aliases.dali_curr_TLB);
+	ASSERT(ddi_aliases.dali_curr_pairs);
+
+	alias = NULL;
+	if (mod_hash_find(ddi_aliases.dali_curr_TLB,
+	    (mod_hash_key_t)curr, (mod_hash_val_t *)&alias) == 0) {
+		goto out;
+	}
+
+
+	/* The TLB has no translation, do it the slow way */
+	alias = NULL;
+	for (i = ddi_aliases.dali_num_pairs - 1; i >= 0; i--) {
+		alias = ddi_curr_to_alias(curr, i);
+		if (alias)
+			break;
+	}
+
+out:
+	(void) tsd_set(tsd_ddi_redirect, NULL);
+
+	return (alias);
+}
+
 void
 ddi_err(ddi_err_t ade, dev_info_t *rdip, const char *fmt, ...)
 {
--- a/usr/src/uts/common/os/inst_sync.c	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/uts/common/os/inst_sync.c	Fri Apr 09 13:39:36 2010 -0700
@@ -19,12 +19,9 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Syscall to write out the instance number data structures to
  * stable storage.
@@ -123,12 +120,18 @@
 
 static int in_write_instance(struct vnode *vp);
 
+static int inst_sync_disable = 0;
+
 static int
 in_sync_sys(char *pathname, uint_t flags)
 {
 	struct vnode *vp;
 	int error;
 
+	/* For debugging/testing */
+	if (inst_sync_disable)
+		return (0);
+
 	/*
 	 * We must have sufficient privilege to do this, since we lock critical
 	 * data structures whilst we're doing it ..
@@ -294,6 +297,9 @@
 
 	for (error = 0; np; np = np->in_sibling) {
 
+		if (np->in_drivers == NULL)
+			continue;
+
 		if (np->in_unit_addr[0] == '\0')
 			(void) sprintf(this, "/%s", np->in_node_name);
 		else
--- a/usr/src/uts/common/os/instance.c	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/uts/common/os/instance.c	Fri Apr 09 13:39:36 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
@@ -48,6 +47,9 @@
 #include <sys/modctl.h>
 #include <sys/console.h>
 #include <sys/cladm.h>
+#include <sys/sysmacros.h>
+#include <sys/crc32.h>
+
 
 static void in_preassign_instance(void);
 static void i_log_devfs_instance_mod(void);
@@ -71,8 +73,12 @@
 static int in_next_instance_block(major_t, int);
 static int in_next_instance(major_t);
 
+#pragma weak plat_ioaliases_init
+
+
 /* external functions */
 extern char *i_binding_to_drv_name(char *bname);
+extern void plat_ioaliases_init(void);
 
 /*
  * This plus devnames defines the entire software state of the instance world.
@@ -87,7 +93,7 @@
 	kmutex_t	ins_serial;
 	kcondvar_t	ins_serial_cv;
 	int		ins_busy;
-	char		ins_dirty;	/* need flush */
+	boolean_t	ins_dirty;	/* instance info needs flush */
 } in_softstate_t;
 
 static in_softstate_t e_ddi_inst_state;
@@ -106,7 +112,7 @@
  * IN_PROVISIONAL:  When a node is assigned an instance number in
  *	e_ddi_assign_instance(), its state is set to IN_PROVISIONAL.
  *	Subsequently, the framework will always call either
- *	e_ddi_keep_instance() which makes the node IN_PERMANENT,
+ *	e_ddi_keep_instance() which makes the node IN_PERMANENT
  *	or e_ddi_free_instance(), which deletes the node.
  * IN_PERMANENT:
  *	If e_ddi_keep_instance() is called on an IN_PROVISIONAL node,
@@ -123,6 +129,7 @@
 #define	PTI_NOT_FOUND	1
 #define	PTI_REBUILD	2
 
+
 /*
  * Path to instance file magic string used for first time boot after
  * an install.  If this is the first string in the file we will
@@ -151,6 +158,12 @@
 	e_ddi_enter_instance();
 
 	/*
+	 * Init the ioaliases if the platform supports it
+	 */
+	if (&plat_ioaliases_init)
+		plat_ioaliases_init();
+
+	/*
 	 * Create the root node, instance zallocs to 0.
 	 * The name and address of this node never get examined, we always
 	 * start searching with its first child.
@@ -192,6 +205,12 @@
 		break;
 
 	case PTI_REBUILD:
+		/*
+		 * path_to_inst has magic str requesting a create
+		 * Convert boot to reconfig boot to ensure /dev is
+		 * in sync with new path_to_inst.
+		 */
+		boothowto |= RB_RECONFIG;
 		cmn_err(CE_CONT,
 		    "?Using default device instance data\n");
 		break;
@@ -486,7 +505,7 @@
 		/* notify devfsadmd to sync of path_to_inst file */
 		mutex_enter(&e_ddi_inst_state.ins_serial);
 		i_log_devfs_instance_mod();
-		e_ddi_inst_state.ins_dirty = 1;
+		e_ddi_inst_state.ins_dirty = B_TRUE;
 		mutex_exit(&e_ddi_inst_state.ins_serial);
 		return (1);
 	}
@@ -549,19 +568,38 @@
 	ASSERT(np == in_devwalk(dip, &ap, NULL));
 
 	/*
+	 * Link the devinfo node and in_node_t
+	 */
+	if (DEVI(dip)->devi_in_node || np->in_devi) {
+		ddi_err(DER_MODE, dip, "devinfo and  instance node (%p) "
+		    "interlink fields are not NULL", (void *)np);
+	}
+	DEVI(dip)->devi_in_node = np;
+	np->in_devi = dip;
+
+	/*
 	 * Look for driver entry, allocate one if not found
 	 */
 	bname = (char *)ddi_driver_name(dip);
 	dp = in_drvwalk(np, bname);
 	if (dp == NULL) {
-		dp = in_alloc_drv(bname);
-		ASSERT(dp != NULL);
-		major = ddi_driver_major(dip);
-		ASSERT(major != DDI_MAJOR_T_NONE);
-		in_endrv(np, dp);
-		in_set_instance(dip, dp, major);
-		dp->ind_state = IN_PROVISIONAL;
-		in_hashdrv(dp);
+
+		if (ddi_aliases_present == B_TRUE) {
+			e_ddi_borrow_instance(dip, np);
+		}
+
+		if ((dp = in_drvwalk(np, bname)) == NULL) {
+			dp = in_alloc_drv(bname);
+			ASSERT(dp != NULL);
+			major = ddi_driver_major(dip);
+			ASSERT(major != DDI_MAJOR_T_NONE);
+			in_endrv(np, dp);
+			in_set_instance(dip, dp, major);
+			dp->ind_state = IN_PROVISIONAL;
+			in_hashdrv(dp);
+		} else {
+			dp->ind_state = IN_BORROWED;
+		}
 	}
 
 	ret = dp->ind_instance;
@@ -738,11 +776,26 @@
 	e_ddi_enter_instance();
 	np = in_devwalk(dip, &ap, addr);
 	ASSERT(np);
+
+	/*
+	 * Break the interlink between dip and np
+	 */
+	if (DEVI(dip)->devi_in_node != np || np->in_devi != dip) {
+		ddi_err(DER_MODE, dip, "devinfo node linked to "
+		    "wrong instance node: %p", (void *)np);
+	}
+	DEVI(dip)->devi_in_node = NULL;
+	np->in_devi = NULL;
+
 	dp = in_drvwalk(np, name);
 	ASSERT(dp);
 	if (dp->ind_state == IN_PROVISIONAL) {
 		in_removedrv(dnp, dp);
 	}
+	if (dp->ind_state == IN_BORROWED) {
+		dp->ind_state = IN_PERMANENT;
+		e_ddi_return_instance(dip, addr, np);
+	}
 	if (np->in_drivers == NULL) {
 		in_removenode(dnp, np, ap);
 	}
@@ -785,10 +838,10 @@
 	ASSERT(dp);
 
 	mutex_enter(&e_ddi_inst_state.ins_serial);
-	if (dp->ind_state == IN_PROVISIONAL) {
+	if (dp->ind_state == IN_PROVISIONAL || dp->ind_state == IN_BORROWED) {
 		dp->ind_state = IN_PERMANENT;
 		i_log_devfs_instance_mod();
-		e_ddi_inst_state.ins_dirty = 1;
+		e_ddi_inst_state.ins_dirty = B_TRUE;
 	}
 	mutex_exit(&e_ddi_inst_state.ins_serial);
 	e_ddi_exit_instance();
@@ -840,6 +893,7 @@
 	in_node_t *np;
 
 	ASSERT(e_ddi_inst_state.ins_busy);
+
 	/*
 	 * Assertion: parents are always instantiated by the framework
 	 * before their children, destroyed after them
@@ -906,6 +960,7 @@
 		}
 		np = np->in_sibling;
 	}
+
 	return (np);
 }
 
@@ -930,7 +985,6 @@
 		cmn_err(CE_WARN,
 		    "invalid instance file entry %s %d",
 		    cp, instance);
-
 		return (0);
 	}
 
@@ -939,11 +993,7 @@
 
 	np = in_make_path(cp);
 	ASSERT(np);
-	if (in_inuse(instance, bname)) {
-		cmn_err(CE_WARN,
-		    "instance already in use: %s %d", cp, instance);
-		return (0);
-	}
+
 	dp = in_drvwalk(np, bname);
 	if (dp != NULL) {
 		cmn_err(CE_WARN,
@@ -952,6 +1002,13 @@
 		    cp, bname, dp->ind_instance);
 		return (0);
 	}
+
+	if (in_inuse(instance, bname)) {
+		cmn_err(CE_WARN,
+		    "instance already in use: %s %d", cp, instance);
+		return (0);
+	}
+
 	dp = in_alloc_drv(bname);
 	in_endrv(np, dp);
 	dp->ind_instance = instance;
@@ -975,8 +1032,10 @@
 	char *cp, *name, *addr;
 
 	ASSERT(e_ddi_inst_state.ins_busy);
+
 	if (path == NULL || path[0] != '/')
 		return (NULL);
+
 	(void) snprintf(buf, sizeof (buf), "%s", path);
 	cp = buf + 1;	/* skip over initial '/' in path */
 	name = in_name_addr(&cp, &addr);
@@ -994,7 +1053,8 @@
 		name = in_name_addr(&cp, &addr);
 
 	ap = e_ddi_inst_state.ins_root;
-	rp = np = e_ddi_inst_state.ins_root->in_child;
+	np = e_ddi_inst_state.ins_root->in_child;
+	rp = np;
 	while (name) {
 		while (name && np) {
 			if (in_eqstr(name, np->in_node_name) &&
@@ -1004,7 +1064,6 @@
 					return (np);
 				ap = np;
 				np = np->in_child;
-				continue;
 			} else {
 				np = np->in_sibling;
 			}
@@ -1016,6 +1075,7 @@
 		np = NULL;	/* can have no children */
 		name = in_name_addr(&cp, &addr);
 	}
+
 	return (rp);
 }
 
@@ -1378,7 +1438,7 @@
 }
 
 void
-e_ddi_enter_instance()
+e_ddi_enter_instance(void)
 {
 	mutex_enter(&e_ddi_inst_state.ins_serial);
 	if (e_ddi_inst_state.ins_thread == curthread)
@@ -1394,7 +1454,7 @@
 }
 
 void
-e_ddi_exit_instance()
+e_ddi_exit_instance(void)
 {
 	mutex_enter(&e_ddi_inst_state.ins_serial);
 	e_ddi_inst_state.ins_busy--;
@@ -1406,19 +1466,19 @@
 }
 
 int
-e_ddi_instance_is_clean()
+e_ddi_instance_is_clean(void)
 {
-	return (e_ddi_inst_state.ins_dirty == 0);
+	return (e_ddi_inst_state.ins_dirty == B_FALSE);
 }
 
 void
-e_ddi_instance_set_clean()
+e_ddi_instance_set_clean(void)
 {
-	e_ddi_inst_state.ins_dirty = 0;
+	e_ddi_inst_state.ins_dirty = B_FALSE;
 }
 
 in_node_t *
-e_ddi_instance_root()
+e_ddi_instance_root(void)
 {
 	return (e_ddi_inst_state.ins_root);
 }
@@ -1450,6 +1510,7 @@
 					break;
 			}
 		}
+
 		if (np->in_child) {
 			rval = in_walk_instances(np->in_child,
 			    path, next, f, arg);
@@ -1480,8 +1541,82 @@
 	e_ddi_enter_instance();
 	root = e_ddi_instance_root();
 	rval = in_walk_instances(root->in_child, path, path, f, arg);
+
 	e_ddi_exit_instance();
 
 	kmem_free(path, MAXPATHLEN);
 	return (rval);
 }
+
+in_node_t *
+e_ddi_path_to_instance(char *path)
+{
+	in_node_t *np;
+
+	np = in_make_path(path);
+	if (np && np->in_drivers && np->in_drivers->ind_state == IN_PERMANENT) {
+		return (np);
+	}
+	return (NULL);
+}
+
+void
+e_ddi_borrow_instance(dev_info_t *cdip, in_node_t *cnp)
+{
+	char		*alias;
+	in_node_t	*anp;
+	char		*curr = kmem_alloc(MAXPATHLEN, KM_NOSLEEP);
+
+	if (curr == NULL) {
+		ddi_err(DER_PANIC, cdip, "curr alloc failed");
+		/*NOTREACHED*/
+	}
+
+	(void) ddi_pathname(cdip, curr);
+
+	if (cnp->in_drivers) {
+		ddi_err(DER_PANIC, cdip, "cnp has instance: %p", cnp);
+		/*NOTREACHED*/
+	}
+
+	alias = ddi_curr_redirect(curr);
+	kmem_free(curr, MAXPATHLEN);
+
+	if (alias && (anp = e_ddi_path_to_instance(alias)) != NULL) {
+		cnp->in_drivers = anp->in_drivers;
+		anp->in_drivers = NULL;
+	}
+}
+
+void
+e_ddi_return_instance(dev_info_t *cdip, char *addr, in_node_t *cnp)
+{
+	in_node_t	*anp;
+	char 		*alias;
+	char		*curr = kmem_alloc(MAXPATHLEN, KM_NOSLEEP);
+
+	if (curr == NULL) {
+		ddi_err(DER_PANIC, cdip, "alloc of curr failed");
+		/*NOTREACHED*/
+	}
+
+	(void) ddi_pathname(cdip, curr);
+	if (addr) {
+		(void) strlcat(curr, "@", MAXPATHLEN);
+		(void) strlcat(curr, addr, MAXPATHLEN);
+
+	}
+	if (cnp->in_drivers == NULL) {
+		ddi_err(DER_PANIC, cdip, "cnp has no inst: %p", cnp);
+		/*NOTREACHED*/
+	}
+
+	alias = ddi_curr_redirect(curr);
+	kmem_free(curr, MAXPATHLEN);
+
+	if (alias && (anp = e_ddi_path_to_instance(alias)) != NULL) {
+		ASSERT(anp->in_drivers == NULL);
+		anp->in_drivers = cnp->in_drivers;
+		cnp->in_drivers = NULL;
+	}
+}
--- a/usr/src/uts/common/sys/ddi_impldefs.h	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/uts/common/sys/ddi_impldefs.h	Fri Apr 09 13:39:36 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _SYS_DDI_IMPLDEFS_H
@@ -123,6 +122,8 @@
 typedef uint8_t	ndi_flavor_t;
 struct ddi_hp_cn_handle;
 
+struct in_node;
+
 struct dev_info  {
 
 	struct dev_info *devi_parent;	/* my parent node in tree	*/
@@ -273,6 +274,8 @@
 
 	/* Owned by hotplug framework */
 	struct ddi_hp_cn_handle *devi_hp_hdlp;   /* hotplug handle list */
+
+	struct in_node  *devi_in_node; /* pointer to devinfo node's in_node_t */
 };
 
 #define	DEVI(dev_info_type)	((struct dev_info *)(dev_info_type))
--- a/usr/src/uts/common/sys/devinfo_impl.h	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/uts/common/sys/devinfo_impl.h	Fri Apr 09 13:39:36 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_SYS_DEVINFO_IMPL_H
@@ -123,6 +122,7 @@
 #define	DI_LNODE(addr)		((struct di_lnode *)((void *)(addr)))
 #define	DI_PRIV_FORMAT(addr)	((struct di_priv_format *)((void *)(addr)))
 #define	DI_HP(addr)		((struct di_hp *)((void *)(addr)))
+#define	DI_ALIAS(addr)		((struct di_alias *)((void *)(addr)))
 
 /*
  * multipath component definitions:  Follows the registered component of
@@ -146,17 +146,19 @@
 	int	generation;	/* reserved for future use */
 	uint32_t	cache_checksum;	/* snapshot checksum */
 	uint64_t	snapshot_time;	/* snapshot timestamp */
-	di_off_t	top_devinfo;
+	di_off_t	top_devinfo;  /* actual top devinfo in snapshot */
 	di_off_t	top_vhci_devinfo;
 	di_off_t	devnames;
 	di_off_t	ppdata_format;	/* parent priv data format array */
 	di_off_t	dpdata_format;	/* driver priv data format array */
+	di_off_t	aliases;	/* offset to alias tree */
 	int	n_ppdata;	/* size of ppdata_format array */
 	int	n_dpdata;	/* size of pddata_format array */
 	int	devcnt;		/* size of devnames array */
 	uint_t	command;	/* same as in di_init() */
 	uint_t	map_size;	/* size of the snapshot */
-	char	root_path[1];	/* path to snapshot root */
+	char	req_path[MAXPATHLEN];	/* path to requested root */
+	char	root_path[1];	/* path to actual snapshot root */
 };
 
 struct di_devnm {
@@ -410,6 +412,17 @@
 	struct di_priv_format *driver;
 };
 
+
+/*
+ * structure for saving alias information
+ */
+struct di_alias {
+	di_off_t	self;		/* make it self addressable */
+	di_off_t	curroff;	/* offset to curr dip's snapshot */
+	di_off_t	next;		/* next alias */
+	char		alias[1];	/* alias path */
+};
+
 /*
  * structure passed in from ioctl
  */
--- a/usr/src/uts/common/sys/instance.h	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/uts/common/sys/instance.h	Fri Apr 09 13:39:36 2010 -0700
@@ -19,15 +19,12 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_SYS_INSTANCE_H
 #define	_SYS_INSTANCE_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Instance number assignment data structures
  */
@@ -35,6 +32,7 @@
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/dditypes.h>
+#include <sys/list.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -43,6 +41,7 @@
 #define	INSTANCE_FILE	"/etc/path_to_inst"
 #define	INSTANCE_FILE_SUFFIX	".old"
 
+
 #if	defined(_KERNEL) || defined(_KMEMUSER)
 
 /*
@@ -60,9 +59,10 @@
 	char		*in_node_name;	/* devi_node_name of this node	*/
 	char		*in_unit_addr;	/* address part of name		*/
 	struct in_node	*in_child;	/* children of this node	*/
-	struct in_node	*in_sibling;	/* "peers" of this node	*/
+	struct in_node	*in_sibling;	/* "peers" of this node		*/
 	struct in_drv	*in_drivers;	/* drivers bound to this node	*/
 	struct in_node	*in_parent;	/* parent of this node		*/
+	dev_info_t	*in_devi;	/* corresponding devinfo	*/
 } in_node_t;
 
 typedef struct in_drv {
@@ -84,6 +84,14 @@
 #define	IN_PROVISIONAL	0x1	/* provisional instance number assigned */
 #define	IN_PERMANENT	0x2	/* instance number has been confirmed */
 #define	IN_UNKNOWN	0x3	/* instance number not yet assigned */
+#define	IN_BORROWED	0x4	/* instance number from alias */
+
+
+/*
+ * Guard for path to instance file
+ */
+#define	PTI_GUARD "#\n#\tCaution! This file contains critical kernel state\n#\n"
+
 
 /*
  * special value for dn_instance
@@ -113,7 +121,12 @@
 
 /* walk the instance tree */
 int e_ddi_walk_instances(int (*)(const char *,
-	in_node_t *, in_drv_t *, void *), void *);
+    in_node_t *, in_drv_t *, void *), void *);
+
+/* for DDI-MP */
+in_node_t *e_ddi_path_to_instance(char *path);
+void e_ddi_borrow_instance(dev_info_t *cdip, in_node_t *cnp);
+void e_ddi_return_instance(dev_info_t *cdip, char *addr, in_node_t *cnp);
 
 /* return values from e_ddi_walk_instances callback */
 #define	INST_WALK_CONTINUE	0
--- a/usr/src/uts/common/sys/sunddi.h	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/uts/common/sys/sunddi.h	Fri Apr 09 13:39:36 2010 -0700
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef	_SYS_SUNDDI_H
@@ -398,10 +397,13 @@
 #define	DDI_MODEL_NATIVE	DATAMODEL_NATIVE
 #define	DDI_MODEL_NONE		DATAMODEL_NONE
 
+/* if set to B_TRUE is DER_MODE is equivalent to DERE_PANIC */
+extern boolean_t ddi_err_panic;
+
 /*
  * Defines for ddi_err().
  */
-typedef enum ddi_err {
+typedef enum {
 	DER_INVALID = 0,	/* must be 0 */
 	DER_CONT = 1,
 	DER_CONS,
@@ -414,10 +416,9 @@
 	DER_DEBUG
 } ddi_err_t;
 
-/* if set to B_TRUE is DER_MODE is equivalent to DERE_PANIC */
-extern boolean_t ddi_err_panic;
 extern void ddi_err(ddi_err_t de, dev_info_t *rdip, const char *fmt, ...);
 
+
 extern char *ddi_strdup(const char *str, int flag);
 extern char *strdup(const char *str);
 extern void strfree(char *str);
@@ -2248,6 +2249,34 @@
 /* Notify DDI of memory added */
 void ddi_mem_update(uint64_t addr, uint64_t size);
 
+/* Path alias interfaces */
+typedef struct plat_alias {
+	char *pali_current;
+	uint64_t pali_naliases;
+	char **pali_aliases;
+} plat_alias_t;
+
+typedef struct alias_pair {
+	char *pair_alias;
+	char *pair_curr;
+} alias_pair_t;
+
+extern boolean_t ddi_aliases_present;
+
+typedef struct ddi_alias {
+	alias_pair_t	*dali_alias_pairs;
+	alias_pair_t	*dali_curr_pairs;
+	int		dali_num_pairs;
+	mod_hash_t	*dali_alias_TLB;
+	mod_hash_t	*dali_curr_TLB;
+} ddi_alias_t;
+
+extern ddi_alias_t ddi_aliases;
+
+void ddi_register_aliases(plat_alias_t *pali, uint64_t npali);
+dev_info_t *ddi_alias_redirect(char *alias);
+char *ddi_curr_redirect(char *curr);
+
 #endif	/* _KERNEL */
 
 #ifdef	__cplusplus
--- a/usr/src/uts/sun4v/os/fillsysinfo.c	Fri Apr 09 13:37:14 2010 -0600
+++ b/usr/src/uts/sun4v/os/fillsysinfo.c	Fri Apr 09 13:39:36 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <sys/errno.h>
@@ -50,6 +49,7 @@
 #include <sys/mmu.h>
 #include <sys/bitmap.h>
 #include <sys/intreg.h>
+#include <sys/instance.h>
 
 struct cpu_node cpunodes[NCPU];
 
@@ -1051,6 +1051,144 @@
 	md_free_scan_dag(mdp, &platlist);
 }
 
+#define	PLAT_MAX_IOALIASES	8
+
+static plat_alias_t *plat_ioaliases;
+static uint64_t plat_num_ioaliases;
+
+/*
+ * split the aliases property into its
+ * component strings for easy searching.
+ */
+static void
+split_alias(plat_alias_t *pali, char *str)
+{
+	char *aliasv[PLAT_MAX_IOALIASES], *p;
+	int i, duplen;
+	char *dup;
+
+	/* skip leading space */
+	str = dup = strdup(str);
+	duplen = strlen(dup) + 1;
+	str += strspn(str, " ");
+	for (i = 0; *str != '\0'; str = p) {
+
+		p = strpbrk(str, " ");
+		if (p != NULL) {
+			*p++ = '\0';
+		}
+
+		VERIFY(i < PLAT_MAX_IOALIASES);
+		aliasv[i++] = strdup(str);
+		if (p == NULL)
+			break;
+		p += strspn(p, " ");
+	}
+
+	kmem_free(dup, duplen);
+
+	if (i == 0) {
+		pali->pali_naliases = 0;
+		pali->pali_aliases = NULL;
+		return;
+	}
+
+	pali->pali_naliases = i;
+	pali->pali_aliases = kmem_alloc(i * sizeof (char *), KM_SLEEP);
+	for (i = 0; i < pali->pali_naliases; i++) {
+		pali->pali_aliases[i] = aliasv[i];
+	}
+}
+
+/*
+ * retrieve the ioalias info from the MD,
+ * and init the ioalias struct.
+ *
+ * NOTE: Assumes that the ioalias info does not change at runtime
+ * This routine is invoked only once at boot time.
+ *
+ * No lock needed as this is called at boot with a DDI lock held
+ */
+void
+plat_ioaliases_init(void)
+{
+	md_t *mdp;
+	mde_cookie_t *ionodes, alinode;
+	plat_alias_t *pali;
+	int nio;
+	int i;
+	int err;
+
+	mdp = md_get_handle();
+	if (mdp == NULL) {
+		cmn_err(CE_PANIC, "no machine description (MD)");
+		/*NOTREACHED*/
+	}
+
+	nio = md_alloc_scan_dag(mdp, md_root_node(mdp),
+	    "ioaliases", "fwd", &ionodes);
+
+
+	/* not all platforms support aliases */
+	if (nio < 1) {
+		(void) md_fini_handle(mdp);
+		return;
+	}
+	if (nio > 1) {
+		cmn_err(CE_PANIC, "multiple ioalias nodes in MD");
+		/*NOTREACHED*/
+	}
+
+	alinode = ionodes[0];
+	md_free_scan_dag(mdp, &ionodes);
+
+	nio = md_alloc_scan_dag(mdp, alinode, "ioalias", "fwd", &ionodes);
+	if (nio <= 0) {
+		cmn_err(CE_PANIC, "MD alias node has no aliases");
+		/*NOTREACHED*/
+	}
+
+	plat_num_ioaliases = nio;
+	plat_ioaliases = pali = kmem_zalloc(nio * sizeof (plat_alias_t),
+	    KM_SLEEP);
+
+	/*
+	 * Each ioalias map will have a composite property of
+	 * aliases and the current valid path.
+	 */
+	for (i = 0; i < nio; i++) {
+		char *str;
+
+		err = md_get_prop_str(mdp, ionodes[i], "current", &str);
+		if (err != 0) {
+			cmn_err(CE_PANIC, "malformed ioalias node");
+			/*NOTREACHED*/
+		}
+		pali->pali_current = strdup(str);
+
+		err = md_get_prop_str(mdp, ionodes[i], "aliases", &str);
+		if (err != 0) {
+			cmn_err(CE_PANIC, "malformed aliases");
+			/*NOTREACHED*/
+		}
+		split_alias(pali, str);
+		pali++;
+	}
+
+	md_free_scan_dag(mdp, &ionodes);
+
+	/*
+	 * Register the io-aliases array with the DDI framework
+	 * The DDI framework assumes that this array and its contents
+	 * will not change post-register. The DDI framework will
+	 * cache this array and is free to access this array at
+	 * any time without any locks.
+	 */
+	ddi_register_aliases(plat_ioaliases, plat_num_ioaliases);
+
+	(void) md_fini_handle(mdp);
+}
+
 /*
  * Number of bits forming a valid context for use in a sun4v TTE and the MMU
  * context registers. Sun4v defines the minimum default value to be 13 if this