changeset 10885:b0ef65646c93

Branch merge
author Neale Ferguson <neale@sinenomine.net>
date Mon, 05 Oct 2009 09:17:26 -0400
parents a0be82c748d9 (current diff) f3a7a77cc1a6 (diff)
children c5d0bf73c406
files usr/src/Makefile usr/src/Makefile.lint usr/src/Makefile.master usr/src/Makefile.master.64 usr/src/cmd/Makefile usr/src/cmd/boot/bootadm/bootadm.c usr/src/cmd/cmd-crypto/elfsign/elfsign.c usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c usr/src/cmd/fdisk/fdisk.c usr/src/cmd/format/menu_fdisk.c usr/src/cmd/stmsboot/stmsboot_util.c usr/src/common/devid/devid_scsi.c usr/src/lib/Makefile usr/src/lib/fm/topo/modules/common/disk/disk_common.c usr/src/pkgdefs/SUNWjcom/Makefile usr/src/pkgdefs/SUNWjcom/depend usr/src/pkgdefs/SUNWjcom/pkginfo.tmpl usr/src/pkgdefs/SUNWjcom/prototype_com usr/src/pkgdefs/SUNWjcom/prototype_i386 usr/src/pkgdefs/SUNWjcom/prototype_sparc usr/src/pkgdefs/SUNWjib/Makefile usr/src/pkgdefs/SUNWjib/depend usr/src/pkgdefs/SUNWjib/pkginfo.tmpl usr/src/pkgdefs/SUNWjib/prototype_com usr/src/pkgdefs/SUNWjib/prototype_i386 usr/src/pkgdefs/SUNWjib/prototype_sparc usr/src/pkgdefs/SUNWocf/Makefile usr/src/pkgdefs/SUNWocf/depend usr/src/pkgdefs/SUNWocf/pkginfo.tmpl usr/src/pkgdefs/SUNWocf/preremove.tmpl usr/src/pkgdefs/SUNWocf/prototype_com usr/src/pkgdefs/SUNWocf/prototype_i386 usr/src/pkgdefs/SUNWocf/prototype_sparc usr/src/pkgdefs/SUNWocfd/Makefile usr/src/pkgdefs/SUNWocfd/pkginfo.tmpl usr/src/pkgdefs/SUNWocfd/prototype_com usr/src/pkgdefs/SUNWocfd/prototype_i386 usr/src/pkgdefs/SUNWocfd/prototype_sparc usr/src/pkgdefs/SUNWocfh/Makefile usr/src/pkgdefs/SUNWocfh/copyright.in usr/src/pkgdefs/SUNWocfh/depend usr/src/pkgdefs/SUNWocfh/pkginfo.tmpl usr/src/pkgdefs/SUNWocfh/prototype_com usr/src/pkgdefs/SUNWocfh/prototype_i386 usr/src/pkgdefs/SUNWocfh/prototype_sparc usr/src/pkgdefs/SUNWocfr/Makefile usr/src/pkgdefs/SUNWocfr/pkginfo.tmpl usr/src/pkgdefs/SUNWocfr/postinstall.tmpl usr/src/pkgdefs/SUNWocfr/prototype_com usr/src/pkgdefs/SUNWocfr/prototype_i386 usr/src/pkgdefs/SUNWocfr/prototype_sparc usr/src/pkgdefs/SUNWpamsc/Makefile usr/src/pkgdefs/SUNWpamsc/depend usr/src/pkgdefs/SUNWpamsc/pkginfo.tmpl usr/src/pkgdefs/SUNWpamsc/prototype_com usr/src/pkgdefs/SUNWpamsc/prototype_i386 usr/src/pkgdefs/SUNWpamsc/prototype_sparc usr/src/pkgdefs/SUNWscmhdlr/Makefile usr/src/pkgdefs/SUNWscmhdlr/depend usr/src/pkgdefs/SUNWscmhdlr/pkginfo.tmpl usr/src/pkgdefs/SUNWscmhdlr/prototype_com usr/src/pkgdefs/SUNWscmhdlr/prototype_sparc usr/src/pkgdefs/SUNWstc.u/Makefile usr/src/pkgdefs/SUNWstc.u/pkginfo.tmpl usr/src/pkgdefs/SUNWstc.u/postinstall usr/src/pkgdefs/SUNWstc.u/preremove usr/src/pkgdefs/SUNWstc.u/prototype_com usr/src/pkgdefs/SUNWstc.u/prototype_sparc usr/src/uts/Makefile.uts usr/src/uts/common/Makefile.files usr/src/uts/common/os/clock.c usr/src/uts/common/os/main.c usr/src/uts/common/os/sunddi.c usr/src/uts/common/sys/Makefile usr/src/uts/common/sys/ddi_impldefs.h usr/src/uts/common/sys/systm.h
diffstat 368 files changed, 52937 insertions(+), 7789 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Mon Sep 28 10:38:31 2009 -0400
+++ b/.hgtags	Mon Oct 05 09:17:26 2009 -0400
@@ -106,3 +106,4 @@
 d12a11a8c2b8186d9bec0fd162d01734ee17e202 onnv_122
 a4572ce3df84133c00b9e02f97627feb50fce18e onnv_123
 d591364f873260bb2676ad8b8cc9f3629af0653c onnv_124
+4c75f60a2704a2d3d2b5fc895ebbb3b94a12439f onnv_125
--- a/usr/src/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -268,7 +268,6 @@
 	@cd lib/libsldap; pwd; $(MAKE) EXPORT_SRC
 	@cd lib/libsasl; pwd; $(MAKE) EXPORT_SRC
 	@cd lib/sasl_plugins; pwd; $(MAKE) EXPORT_SRC
-	@cd $(CLOSED)/lib/smartcard;	  pwd; $(MAKE) EXPORT_SRC
 	@cd lib/pam_modules/krb5;   pwd; $(MAKE) EXPORT_SRC
 	@cd lib/libresolv2; pwd; $(MAKE) EXPORT_SRC
 	@cd psm/stand/boot; pwd; $(MAKE) EXPORT_SRC
--- a/usr/src/Makefile.lint	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/Makefile.lint	Mon Oct 05 09:17:26 2009 -0400
@@ -236,6 +236,7 @@
 	cmd/pools \
 	cmd/power \
 	cmd/powertop \
+	cmd/latencytop \
 	cmd/ppgsz \
 	cmd/praudit \
 	cmd/prctl \
@@ -469,11 +470,9 @@
 	$(CLOSED)/cmd/cmd-inet/usr.lib/in.iked \
 	$(CLOSED)/cmd/pax \
 	$(CLOSED)/cmd/sed_xpg4 \
-	$(CLOSED)/cmd/smartcard \
 	$(CLOSED)/cmd/tail \
 	$(CLOSED)/cmd/tr_xpg4 \
-	$(CLOSED)/lib/libc_i18n \
-	$(CLOSED)/lib/smartcard
+	$(CLOSED)/lib/libc_i18n
 
 i386_SUBDIRS= \
 	cmd/biosdev \
--- a/usr/src/Makefile.master	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/Makefile.master	Mon Oct 05 09:17:26 2009 -0400
@@ -137,6 +137,7 @@
 DIFF=		/usr/bin/diff
 GREP=		/usr/bin/grep
 EGREP=		/usr/bin/egrep
+ELFWRAP=	/usr/bin/elfwrap
 KSH93=		/usr/bin/ksh93
 SED=		/usr/bin/sed
 NAWK=		/usr/bin/nawk
@@ -570,6 +571,12 @@
 CCFLAGS64=	$(CCOPTFLAG64) $($(MACH64)_CCFLAGS)
 
 #
+#
+#
+ELFWRAP_FLAGS	=	
+ELFWRAP_FLAGS64	=	-64
+
+#
 # Various mapfiles that are used throughout the build, and delivered to
 # /usr/lib/ld.
 #
@@ -630,6 +637,8 @@
 COMPILE64.s=	$(AS) $(ASFLAGS) $($(MACH64)_AS_XARCH) $(AS_CPPFLAGS)
 COMPILE.d=	$(DTRACE) -G -32
 COMPILE64.d=	$(DTRACE) -G -64
+COMPILE.b=	$(ELFWRAP) $(ELFWRAP_FLAGS$(CLASS))
+COMPILE64.b=	$(ELFWRAP) $(ELFWRAP_FLAGS$(CLASS))
 
 CLASSPATH=	.
 COMPILE.java=	$(JAVAC) $(JAVAFLAGS) -classpath $(CLASSPATH)
@@ -1040,6 +1049,10 @@
 	$(POST_PROCESS_O)
 	$(RM) $*.c
 
+.bin.o:
+	$(COMPILE.b) -o $@ $<
+	$(POST_PROCESS_O)
+
 .java.class:
 	$(COMPILE.java) $<
 
--- a/usr/src/Makefile.master.64	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/Makefile.master.64	Mon Oct 05 09:17:26 2009 -0400
@@ -36,6 +36,7 @@
 COMPILE.cc=	$(COMPILE64.cc)
 COMPILE.s=	$(COMPILE64.s)
 COMPILE.d=	$(COMPILE64.d)
+COMPILE.b=	$(COMPILE64.b)
 LINK.c=		$(LINK64.c)
 LINK.cc=	$(LINK64.cc)
 LINT.c=		$(LINT64.c)
--- a/usr/src/cmd/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -227,6 +227,7 @@
 	kstat		\
 	last		\
 	lastcomm	\
+	latencytop	\
 	ldap		\
 	ldapcachemgr	\
 	lgrpinfo	\
@@ -479,8 +480,7 @@
 	$(CLOSED)/cmd/printf	\
 	$(CLOSED)/cmd/sed	\
 	$(CLOSED)/cmd/sed_xpg4	\
-	$(CLOSED)/cmd/smartcard	\
-	$(CLOSED)/cmd/tail	\
+ODE	$(CLOSED)/cmd/tail	\
 	$(CLOSED)/cmd/tr_xpg4
 
 i386_SUBDIRS=		\
--- a/usr/src/cmd/Makefile.check	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/Makefile.check	Mon Oct 05 09:17:26 2009 -0400
@@ -147,7 +147,6 @@
 	ypcmd/ypupdated
 
 $(CLOSED_BUILD)MANIFEST_SUBDIRS +=		\
-	$(CLOSED)/cmd/smartcard/daemon		\
 	$(CLOSED)/cmd/cmd-inet/usr.lib/in.iked
 
 DTEST_SUBDIRS= \
--- a/usr/src/cmd/boot/bootadm/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/boot/bootadm/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -35,7 +35,8 @@
 
 .KEEP_STATE:
 
-LDLIBS +=	-lnvpair -lgen -ladm -lefi -lscf -lz
+LDLIBS_i386= -lfdisk
+LDLIBS +=	-lnvpair -lgen -ladm -lefi -lscf -lz $(LDLIBS_$(MACH))
 
 # Writing into string literals is incorrect.  We need to match gcc's
 # behavior, which causes us to take SIGSEGV on such a write.
@@ -45,6 +46,7 @@
 
 LINTFLAGS += -erroff=E_INCONS_ARG_DECL2
 LINTFLAGS += -erroff=E_INCONS_VAL_TYPE_DECL2
+LINTFLAGS += -erroff=E_NAME_DEF_NOT_USED2
 
 all: $(PROG)
 
--- a/usr/src/cmd/boot/bootadm/bootadm.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/boot/bootadm/bootadm.c	Mon Oct 05 09:17:26 2009 -0400
@@ -63,6 +63,9 @@
 #include <zlib.h>
 #include <sys/lockfs.h>
 #include <sys/filio.h>
+#ifdef i386
+#include <libfdisk.h>
+#endif
 
 #if !defined(_OPB)
 #include <sys/ucode.h>
@@ -4869,6 +4872,11 @@
 	struct mboot *mboot;
 	char boot_sect[SECTOR_SIZE];
 	char *wholedisk, *slice;
+#ifdef i386
+	ext_part_t *epp;
+	uint32_t secnum, numsec;
+	int rval, pno;
+#endif
 
 	/* form whole disk (p0) */
 	slice = device + strlen(device) - 2;
@@ -4882,12 +4890,34 @@
 
 	/* read boot sector */
 	fd = open(wholedisk, O_RDONLY);
-	free(wholedisk);
 	if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
 		return (partno);
 	}
 	(void) close(fd);
 
+#ifdef i386
+	/* Read/Initialize extended partition information */
+	if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
+	    != FDISK_SUCCESS) {
+		switch (rval) {
+			/*
+			 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
+			 * be considered as soft errors and hence
+			 * we do not return
+			 */
+			case FDISK_EBADLOGDRIVE:
+				break;
+			case FDISK_ENOLOGDRIVE:
+				break;
+			default:
+				free(wholedisk);
+				return (partno);
+				break;
+		}
+	}
+#endif
+	free(wholedisk);
+
 	/* parse fdisk table */
 	mboot = (struct mboot *)((void *)boot_sect);
 	for (i = 0; i < FD_NUMPART; i++) {
@@ -4898,14 +4928,32 @@
 				partno = i;
 				break;
 			}
+#ifdef i386
+		} else if (fdisk_is_dos_extended(part->systid)) {
+			rval = fdisk_get_solaris_part(epp, &pno, &secnum,
+			    &numsec);
+			if (rval == FDISK_SUCCESS) {
+				partno = pno - 1;
+				break;
+			}
+#endif
 		} else {	/* look for solaris partition, old and new */
+#ifdef i386
+			if ((part->systid == SUNIXOS &&
+			    (fdisk_is_linux_swap(epp, part->relsect,
+			    NULL) != 0)) || part->systid == SUNIXOS2) {
+#else
 			if (part->systid == SUNIXOS ||
 			    part->systid == SUNIXOS2) {
+#endif
 				partno = i;
 				break;
 			}
 		}
 	}
+#ifdef i386
+	libfdisk_fini(&epp);
+#endif
 	return (partno);
 }
 
@@ -7226,7 +7274,7 @@
 		if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
 		    strncmp(lp->line, "/dev/rdsk/",
 		    strlen("/dev/rdsk/")) != 0)  {
-			(void) snprintf(dsk, sizeof (dsk), "/dev/dsk/%s",
+			(void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s",
 			    lp->line);
 		} else {
 			(void) strlcpy(dsk, lp->line, sizeof (dsk));
@@ -8936,9 +8984,23 @@
 		    strchr(signbuf, ')') == NULL);
 
 		if (optnum == KERNEL_CMD) {
-			BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn, new_path));
-			entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
-			    signbuf, new_path, NULL, NULL, NULL);
+			if (strcmp(fstype, "zfs") == 0) {
+				new_str_len = strlen(new_path) +
+				    strlen(ZFS_BOOT) + 8;
+				new_arg = s_calloc(1, new_str_len);
+				(void) snprintf(new_arg, new_str_len, "%s %s",
+				    new_path, ZFS_BOOT);
+				BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn,
+				    new_arg));
+				entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
+				    signbuf, new_arg, NULL, NULL, NULL);
+				free(new_arg);
+			} else {
+				BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn,
+				    new_path));
+				entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
+				    signbuf, new_path, NULL, NULL, NULL);
+			}
 		} else {
 			new_str_len = strlen(path) + 8;
 			if (strcmp(fstype, "zfs") == 0) {
--- a/usr/src/cmd/cmd-crypto/elfsign/elfsign.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/cmd-crypto/elfsign/elfsign.c	Mon Oct 05 09:17:26 2009 -0400
@@ -20,12 +20,10 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Developer command for adding the signature section to an ELF object
  * PSARC 2001/488
@@ -212,7 +210,7 @@
 	 *	  because getopt does that for us.
 	 */
 	while (!errflag && (c = getopt(argc - 1, argv + 1, opts)) != EOF) {
-		if (strchr("ceFihkPr", c) != NULL)
+		if (strchr("ceFihkPTr", c) != NULL)
 			cryptodebug("c=%c, '%s'", c, optarg);
 		else
 			cryptodebug("c=%c", c);
@@ -737,6 +735,17 @@
 		return (ret);
 	ret = EXIT_SIGN_FAILED;
 
+	if (cmd_info.token_label &&
+	    !elfcertlib_settoken(cmd_info.ess, cmd_info.token_label)) {
+		es_error(gettext("Unable to access token: %s"),
+		    cmd_info.token_label);
+		ret = EXIT_SIGN_FAILED;
+		goto clean_esa;
+	}
+
+	if ((ret = setcertpath()) != EXIT_OKAY)
+		goto clean_esa;
+
 	/*
 	 * Find the certificate we need to sign the activation file with.
 	 */
@@ -1016,7 +1025,7 @@
 	kmfrv = f; \
 	if (kmfrv != KMF_OK) { \
 		char *e = NULL; \
-		(void) KMF_GetKMFErrorString(kmfrv, &e); \
+		(void) kmf_get_kmf_error_str(kmfrv, &e); \
 		cryptoerror(LOG_STDERR, \
 			gettext("Failed to %s: %s\n"), \
 			s, (e ? e : "unknown error")); \
@@ -1029,17 +1038,23 @@
 {
 	KMF_RETURN kmfrv = KMF_OK;
 	KMF_HANDLE_T kmfhandle = NULL;
-	KMF_CREATEKEYPAIR_PARAMS kp_params;
 	KMF_KEY_HANDLE pubk, prik;
 	KMF_X509_NAME csrSubject;
 	KMF_CSR_DATA csr;
 	KMF_ALGORITHM_INDEX sigAlg = KMF_ALGID_MD5WithRSA;
 	KMF_DATA signedCsr = { NULL, 0 };
-	KMF_CONFIG_PARAMS config;
 	char *err;
+	KMF_ATTRIBUTE	attrlist[16];
+	KMF_ENCODE_FORMAT	format;
+	KMF_KEYSTORE_TYPE	kstype;
+	KMF_KEY_ALG	keytype;
+	uint32_t	keylength;
+	KMF_CREDENTIAL	cred;
+	char	*pin = NULL;
+	int	numattr;
 
-	if ((kmfrv = KMF_Initialize(&kmfhandle, NULL, NULL)) != KMF_OK) {
-		(void) KMF_GetKMFErrorString(kmfrv, &err);
+	if ((kmfrv = kmf_initialize(&kmfhandle, NULL, NULL)) != KMF_OK) {
+		(void) kmf_get_kmf_error_str(kmfrv, &err);
 		cryptoerror(LOG_STDERR,
 		    gettext("Error initializing KMF: %s\n"),
 		    (err ? err : "unknown error"));
@@ -1049,82 +1064,109 @@
 	}
 	(void) memset(&csr, 0, sizeof (csr));
 	(void) memset(&csrSubject, 0, sizeof (csrSubject));
-	(void) memset(&kp_params, 0, sizeof (kp_params));
 
 	if (cmd_info.privpath != NULL) {
-		kp_params.kstype = KMF_KEYSTORE_OPENSSL;
-		kp_params.sslparms.keyfile = cmd_info.privpath;
-		kp_params.sslparms.format = KMF_FORMAT_ASN1;
-	} else if (cmd_info.token_label != NULL) {
+		kstype = KMF_KEYSTORE_OPENSSL;
+		format = KMF_FORMAT_ASN1;
+	} else {
+		boolean_t	readonly;
+		/* args checking verified (cmd_info.token_label != NULL) */
 
 		/* Get a PIN to store the private key in the token */
-		char *pin = getpin();
+		pin = getpin();
 
 		if (pin == NULL) {
-			(void) KMF_Finalize(kmfhandle);
+			(void) kmf_finalize(kmfhandle);
 			return (KMF_ERR_AUTH_FAILED);
 		}
 
-		kp_params.kstype = KMF_KEYSTORE_PK11TOKEN;
-		kp_params.cred.cred = pin;
-		kp_params.cred.credlen = strlen(pin);
+		kstype = KMF_KEYSTORE_PK11TOKEN;
+		readonly = B_FALSE;
 
-		(void) memset(&config, 0, sizeof (config));
-		config.kstype = KMF_KEYSTORE_PK11TOKEN;
-		config.pkcs11config.label = cmd_info.token_label;
-		config.pkcs11config.readonly = FALSE;
-		kmfrv = KMF_ConfigureKeystore(kmfhandle, &config);
+		numattr = 0;
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype));
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_TOKEN_LABEL_ATTR, cmd_info.token_label,
+		    strlen(cmd_info.token_label));
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_READONLY_ATTR, &readonly, sizeof (readonly));
+		kmfrv = kmf_configure_keystore(kmfhandle, numattr, attrlist);
 		if (kmfrv != KMF_OK) {
 			goto cleanup;
 		}
 	}
 
 	/* Create the RSA keypair */
-	kp_params.keytype = KMF_RSA;
-	kp_params.keylength = ES_DEFAULT_KEYSIZE;
+	keytype = KMF_RSA;
+	keylength = ES_DEFAULT_KEYSIZE;
+	(void) memset(&prik, 0, sizeof (prik));
+	(void) memset(&pubk, 0, sizeof (pubk));
 
-	kmfrv = KMF_CreateKeypair(kmfhandle, &kp_params, &prik, &pubk);
+	numattr = 0;
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype));
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_KEYALG_ATTR, &keytype, sizeof (keytype));
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_KEYLENGTH_ATTR, &keylength, sizeof (keylength));
+	if (pin != NULL) {
+		cred.cred = pin;
+		cred.credlen = strlen(pin);
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_CREDENTIAL_ATTR, &cred, sizeof (KMF_CREDENTIAL));
+	}
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_PRIVKEY_HANDLE_ATTR, &prik, sizeof (KMF_KEY_HANDLE));
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_PUBKEY_HANDLE_ATTR, &pubk, sizeof (KMF_KEY_HANDLE));
+	if (kstype == KMF_KEYSTORE_OPENSSL) {
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_KEY_FILENAME_ATTR, cmd_info.privpath,
+		    strlen(cmd_info.privpath));
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_ENCODE_FORMAT_ATTR, &format, sizeof (format));
+	}
+
+	kmfrv = kmf_create_keypair(kmfhandle, numattr, attrlist);
 	if (kmfrv != KMF_OK) {
-		(void) KMF_GetKMFErrorString(kmfrv, &err);
-		if (err != NULL) {
-			cryptoerror(LOG_STDERR,
-			    gettext("Create RSA keypair failed: %s"), err);
-			free(err);
-		}
+		(void) kmf_get_kmf_error_str(kmfrv, &err);
+		cryptoerror(LOG_STDERR,
+		    gettext("Create RSA keypair failed: %s"),
+		    (err ? err : "unknown error"));
+		free(err);
 		goto cleanup;
 	}
 
-	kmfrv = KMF_DNParser(dn, &csrSubject);
+	kmfrv = kmf_dn_parser(dn, &csrSubject);
 	if (kmfrv != KMF_OK) {
-		(void) KMF_GetKMFErrorString(kmfrv, &err);
-		if (err != NULL) {
-			cryptoerror(LOG_STDERR,
-			    gettext("Error parsing subject name: %s\n"), err);
-			free(err);
-		}
+		(void) kmf_get_kmf_error_str(kmfrv, &err);
+		cryptoerror(LOG_STDERR,
+		    gettext("Error parsing subject name: %s\n"),
+		    (err ? err : "unknown error"));
+		free(err);
 		goto cleanup;
 	}
 
-	SET_VALUE(KMF_SetCSRPubKey(kmfhandle, &pubk, &csr), "keypair");
+	SET_VALUE(kmf_set_csr_pubkey(kmfhandle, &pubk, &csr), "keypair");
 
-	SET_VALUE(KMF_SetCSRVersion(&csr, 2), "version number");
+	SET_VALUE(kmf_set_csr_version(&csr, 2), "version number");
 
-	SET_VALUE(KMF_SetCSRSubjectName(&csr, &csrSubject), "subject name");
+	SET_VALUE(kmf_set_csr_subject(&csr, &csrSubject), "subject name");
 
-	SET_VALUE(KMF_SetCSRSignatureAlgorithm(&csr, sigAlg),
-	    "SignatureAlgorithm");
+	SET_VALUE(kmf_set_csr_sig_alg(&csr, sigAlg), "SignatureAlgorithm");
 
-	if ((kmfrv = KMF_SignCSR(kmfhandle, &csr, &prik, &signedCsr)) ==
+	if ((kmfrv = kmf_sign_csr(kmfhandle, &csr, &prik, &signedCsr)) ==
 	    KMF_OK) {
-		kmfrv = KMF_CreateCSRFile(&signedCsr, KMF_FORMAT_PEM,
+		kmfrv = kmf_create_csr_file(&signedCsr, KMF_FORMAT_PEM,
 		    cmd_info.cert);
 	}
 
 cleanup:
-	(void) KMF_FreeKMFKey(kmfhandle, &prik);
-	(void) KMF_FreeData(&signedCsr);
-	(void) KMF_FreeSignedCSR(&csr);
-	(void) KMF_Finalize(kmfhandle);
+	(void) kmf_free_kmf_key(kmfhandle, &prik);
+	(void) kmf_free_data(&signedCsr);
+	(void) kmf_free_signed_csr(&csr);
+	(void) kmf_finalize(kmfhandle);
 
 	return (kmfrv);
 }
--- a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpusers	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpusers	Mon Oct 05 09:17:26 2009 -0400
@@ -13,6 +13,7 @@
 smmsp
 listen
 gdm
+xvm
 mysql
 openldap
 webservd
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h	Mon Oct 05 09:17:26 2009 -0400
@@ -65,8 +65,6 @@
 
 #define	MAXLINE		(1088)		/* max len of detail line */
 
-#define	MAX_HDRTRAILER	(64)		/* max hdr/trailer packet slack */
-
 /*
  * The RPC XID cache structure.
  * When analyzing RPC protocols we
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c	Mon Oct 05 09:17:26 2009 -0400
@@ -69,7 +69,6 @@
 void convert_from_network();
 static void convert_old(struct ohdr *);
 extern sigjmp_buf jmp_env, ojmp_env;
-static dlpi_info_t dlinfo;
 static char *bufp;	/* pointer to read buffer */
 
 static int strioctl(int, int, int, int, void *);
@@ -119,6 +118,7 @@
 	int retval;
 	int flags = DLPI_PASSIVE | DLPI_RAW;
 	dlpi_walk_arg_t dwa;
+	dlpi_info_t dlinfo;
 
 	if (linkname == NULL) {
 		/*
@@ -162,10 +162,6 @@
 		    "not supported\n", dlinfo.di_mactype);
 	}
 
-	/* for backward compatibility, allow known interface mtu_sizes */
-	if (interface->mtu_size > dlinfo.di_max_sdu)
-		dlinfo.di_max_sdu = interface->mtu_size;
-
 	return (interface->try_kernel_filter);
 }
 
@@ -417,19 +413,6 @@
 			goto err;
 		}
 
-		if (nhdrp->sbh_totlen >
-		    (dlinfo.di_max_sdu + MAX_HDRTRAILER)) {
-			if (cap)
-				(void) fprintf(stderr, "(warning) packet length"
-				    " greater than MTU in capture file");
-			else
-				(void) fprintf(stderr, "(warning) packet length"
-				    " greater than MTU in buffer");
-
-			(void) fprintf(stderr, " offset %d: length=%d\n",
-			    bp - buf, nhdrp->sbh_totlen);
-		}
-
 		/*
 		 * Check for incomplete packet.  We are conservative here,
 		 * since we don't know how good the checking is in other
@@ -689,7 +672,6 @@
 		if (mprotect(cap_buffp, cap_len, PROT_READ | PROT_WRITE) < 0)
 			pr_err("mprotect: %s: %m", name);
 	}
-	dlinfo.di_max_sdu = MAXINT;	/* Decode any stored packet. */
 }
 
 void
--- a/usr/src/cmd/devfsadm/Makefile.com	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/devfsadm/Makefile.com	Mon Oct 05 09:17:26 2009 -0400
@@ -99,6 +99,8 @@
 LDLIBS +=		-ldevinfo
 devfsadm :=		LDLIBS += -lgen -lsysevent -lnvpair -lzonecfg -lbsm
 SUNW_md_link.so :=	LDLIBS += -lmeta
+SUNW_disk_link.so :=	LDLIBS += -ldevid
+SUNW_sgen_link.so :=	LDLIBS += -ldevid
 
 # All libraries are built from the same SUNW_%.so rule (see below), and define
 # their own SONAME using -h explicitly.  Null the generic -h macro that gets
--- a/usr/src/cmd/devfsadm/disk_link.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/devfsadm/disk_link.c	Mon Oct 05 09:17:26 2009 -0400
@@ -29,8 +29,11 @@
 #include <stdlib.h>
 #include <limits.h>
 #include <ctype.h>
+#include <sys/int_fmtio.h>
 #include <sys/stat.h>
 #include <bsm/devalloc.h>
+#include <sys/scsi/scsi_address.h>
+#include <sys/libdevid.h>
 
 #define	DISK_SUBPATH_MAX 100
 #define	RM_STALE 0x01
@@ -68,31 +71,31 @@
 
 
 static devfsadm_create_t disk_cbt[] = {
-	{ "disk", "ddi_block", NULL,
+	{ "disk", DDI_NT_BLOCK, NULL,
 	    TYPE_EXACT, ILEVEL_0, disk_callback_nchan
 	},
-	{ "disk", "ddi_block:channel", NULL,
+	{ "disk", DDI_NT_BLOCK_CHAN, NULL,
 	    TYPE_EXACT, ILEVEL_0, disk_callback_chan
 	},
-	{ "disk", "ddi_block:fabric", NULL,
+	{ "disk", DDI_NT_BLOCK_FABRIC, NULL,
 		TYPE_EXACT, ILEVEL_0, disk_callback_fabric
 	},
-	{ "disk", "ddi_block:wwn", NULL,
+	{ "disk", DDI_NT_BLOCK_WWN, NULL,
 	    TYPE_EXACT, ILEVEL_0, disk_callback_wwn
 	},
-	{ "disk", "ddi_block:sas", NULL,
+	{ "disk", DDI_NT_BLOCK_SAS, NULL,
 	    TYPE_EXACT, ILEVEL_0, disk_callback_sas
 	},
-	{ "disk", "ddi_block:cdrom", NULL,
+	{ "disk", DDI_NT_CD, NULL,
 	    TYPE_EXACT, ILEVEL_0, disk_callback_nchan
 	},
-	{ "disk", "ddi_block:cdrom:channel", NULL,
+	{ "disk", DDI_NT_CD_CHAN, NULL,
 	    TYPE_EXACT, ILEVEL_0, disk_callback_chan
 	},
-	{ "disk", "ddi_block:xvmd", NULL,
+	{ "disk", DDI_NT_BLOCK_XVMD, NULL,
 	    TYPE_EXACT, ILEVEL_0, disk_callback_xvmd
 	},
-	{ "disk", "ddi_block:cdrom:xvmd", NULL,
+	{ "disk", DDI_NT_CD_XVMD, NULL,
 	    TYPE_EXACT, ILEVEL_0, disk_callback_xvmd
 	},
 };
@@ -168,11 +171,13 @@
 	int targ;
 	int *intp;
 
-	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "target", &intp) <= 0) {
+	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, SCSI_ADDR_PROP_TARGET,
+	    &intp) <= 0) {
 		return (DEVFSADM_CONTINUE);
 	}
 	targ = *intp;
-	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "lun", &intp) <= 0) {
+	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, SCSI_ADDR_PROP_LUN,
+	    &intp) <= 0) {
 		lun = 0;
 	} else {
 		lun = *intp;
@@ -207,7 +212,7 @@
 	} else if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
 	    "port-wwn", &wwn) > 0) {
 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
-		    "lun", &intp) > 0) {
+		    SCSI_ADDR_PROP_LUN, &intp) > 0) {
 			lun = *intp;
 		} else {
 			lun = 0;
@@ -236,38 +241,72 @@
 disk_callback_sas(di_minor_t minor, di_node_t node)
 {
 	char disk[DISK_SUBPATH_MAX];
-	int lun;
+	int lun64_found = 0;
+	scsi_lun64_t lun64, sl;
+	scsi_lun_t lun;
+	int64_t *lun64p;
+	uint64_t wwn;
 	int *intp;
-	char *str;
-	char *wwn;
+	char *tgt_port;
+	uchar_t addr_method;
 
-	/*
-	 * get LUN property
-	 */
-	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
-	    "lun", &intp) > 0) {
-		lun = *intp;
-	} else {
-		lun = 0;
+	/* Get lun property */
+	if (di_prop_lookup_int64(DDI_DEV_T_ANY, node,
+	    SCSI_ADDR_PROP_LUN64, &lun64p) > 0) {
+		if (*lun64p != SCSI_LUN64_ILLEGAL) {
+			lun64_found = 1;
+			lun64 = (uint64_t)*lun64p;
+		}
 	}
+	if ((!lun64_found) && (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
+	    SCSI_ADDR_PROP_LUN, &intp) > 0)) {
+		lun64 = (uint64_t)*intp;
+	}
+
+	lun = scsi_lun64_to_lun(lun64);
+
+	addr_method = (lun.sl_lun1_msb & SCSI_LUN_AM_MASK);
+
 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
-	    "target-port", &wwn) > 0) {
-		/*
-		 * If the target-port property exist
-		 * we use wwn format naming
-		 */
-		for (str = wwn; *str != '\0'; str++) {
-			*str = DISK_LINK_TO_UPPER(*str);
+	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) > 0) {
+		(void) scsi_wwnstr_to_wwn(tgt_port, &wwn);
+		if ((addr_method == SCSI_LUN_AM_PDEV) &&
+		    (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
+		    (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
+		    (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
+			(void) snprintf(disk, DISK_SUBPATH_MAX,
+			    "t%"PRIX64"d%"PRId64, wwn, lun64);
+		} else if ((addr_method == SCSI_LUN_AM_FLAT) &&
+		    (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
+		    (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
+		    (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
+			sl = (lun.sl_lun1_msb << 8) | lun.sl_lun1_lsb;
+			(void) snprintf(disk, DISK_SUBPATH_MAX,
+			    "t%"PRIX64"d%"PRIX16, wwn, sl);
+		} else {
+			(void) snprintf(disk, DISK_SUBPATH_MAX,
+			    "t%"PRIX64"d%"PRIX64, wwn, lun64);
 		}
-		(void) snprintf(disk, DISK_SUBPATH_MAX, "t%sd%d", wwn, lun);
-
 	} else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
-	    "sata-phy", &intp) > 0) {
-		/*
-		 * For direct attached SATA device without Device Name,
-		 * no wwn exist, we use phy format naming
-		 */
-		(void) snprintf(disk, DISK_SUBPATH_MAX, "t%dd%d", *intp, lun);
+	    SCSI_ADDR_PROP_SATA_PHY, &intp) > 0) {
+		/* Use phy format naming, for SATA devices without wwn */
+		if ((addr_method == SCSI_LUN_AM_PDEV) &&
+		    (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
+		    (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
+		    (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
+			(void) snprintf(disk, DISK_SUBPATH_MAX,
+			    "t%sd%"PRId64, *intp, lun64);
+		} else if ((addr_method == SCSI_LUN_AM_FLAT) &&
+		    (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
+		    (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
+		    (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
+			sl = (lun.sl_lun1_msb << 8) | lun.sl_lun1_lsb;
+			(void) snprintf(disk, DISK_SUBPATH_MAX,
+			    "t%sd%"PRIX16, *intp, sl);
+		} else {
+			(void) snprintf(disk, DISK_SUBPATH_MAX,
+			    "t%sd%"PRIX64, *intp, lun64);
+		}
 	} else {
 		return (DEVFSADM_CONTINUE);
 	}
--- a/usr/src/cmd/devfsadm/sgen_link.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/devfsadm/sgen_link.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,18 +19,19 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <devfsadm.h>
 #include <stdio.h>
 #include <strings.h>
 #include <stdlib.h>
 #include <limits.h>
 #include <ctype.h>
+#include <sys/int_fmtio.h>
+#include <sys/scsi/scsi_address.h>
+#include <sys/libdevid.h>
 
 #define	SGEN_LINK_RE	"^scsi/.+/c[0-9]+t[0-9A-F]+d[0-9]+$"
 #define	SGEN_DIR	"scsi"
@@ -65,17 +65,67 @@
 {
 	char *baddr, *cnum, *tstr;
 	char lpath[PATH_MAX], buf[PATH_MAX];
-	uchar_t *wwn;
+	uchar_t *wwnstr;
+	char *tgt_port;
+
 
 	if ((cnum = find_ctrlr(node, minor)) == NULL)
 		goto done;
 
+	/*
+	 * SCSAv3 attached devices.
+	 */
 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
-	    "client-guid", (char **)&wwn) > 0) {
+	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) > 0) {
+		uint64_t wwn;
+		scsi_lun64_t sl;
+		scsi_lun_t lun;
+		int64_t lun64;
+		int64_t *lun64p;
+		int *intp;
+		uchar_t addr_method;
+
+		/* Get lun property */
+		if ((di_prop_lookup_int64(DDI_DEV_T_ANY, node,
+		    SCSI_ADDR_PROP_LUN64, &lun64p) > 0) &&
+		    (*lun64p != SCSI_LUN64_ILLEGAL)) {
+			lun64 = *lun64p;
+		} else if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
+		    SCSI_ADDR_PROP_LUN, &intp) > 0) {
+			lun64 = (uint64_t)*intp;
+		}
+
+		lun = scsi_lun64_to_lun(lun64);
+
+		addr_method = (lun.sl_lun1_msb & SCSI_LUN_AM_MASK);
+
+		(void) scsi_wwnstr_to_wwn(tgt_port, &wwn);
+		if ((addr_method == SCSI_LUN_AM_PDEV) &&
+		    (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
+		    (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
+		    (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
+			(void) snprintf(lpath, PATH_MAX,
+			    "%s/%s/c%st%"PRIX64"d%"PRId64, SGEN_DIR,
+			    di_minor_name(minor), cnum, wwn, lun64);
+		} else if ((addr_method == SCSI_LUN_AM_FLAT) &&
+		    (lun.sl_lun2_msb == 0) && (lun.sl_lun2_lsb == 0) &&
+		    (lun.sl_lun3_msb == 0) && (lun.sl_lun3_lsb == 0) &&
+		    (lun.sl_lun4_msb == 0) && (lun.sl_lun4_lsb == 0)) {
+			sl = (lun.sl_lun1_msb << 8) | lun.sl_lun1_lsb;
+			(void) snprintf(lpath, PATH_MAX,
+			    "%s/%s/c%st%"PRIX64"d%"PRIX16, SGEN_DIR,
+			    di_minor_name(minor), cnum, wwn, sl);
+		} else {
+			(void) snprintf(lpath, PATH_MAX,
+			    "%s/%s/c%st%"PRIX64"d%"PRIX64, SGEN_DIR,
+			    di_minor_name(minor), cnum, wwn, lun64);
+		}
+	} else if (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
+	    "client-guid", (char **)&wwnstr) > 0) {
 		/*
 		 * MPXIO-enabled devices; lun is always 0.
 		 */
-		if (strlcpy((char *)buf, (char *)wwn, sizeof (buf)) >=
+		if (strlcpy((char *)buf, (char *)wwnstr, sizeof (buf)) >=
 		    sizeof (buf))
 			goto done;
 
@@ -87,7 +137,7 @@
 			goto done;
 
 	} else if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
-	    "port-wwn", &wwn) == 8) {
+	    "port-wwn", &wwnstr) == 8) {
 		/*
 		 * "normal" fibre channel devices
 		 */
@@ -98,7 +148,7 @@
 			lun = 0;
 
 		for (count = 0, tstr = buf; count < 8; count++, tstr += 2)
-			(void) sprintf(tstr, "%02X", wwn[count]);
+			(void) sprintf(tstr, "%02X", wwnstr[count]);
 
 		*tstr = '\0';
 		if (snprintf(lpath, sizeof (lpath), "%s/%s/c%st%sd%d", SGEN_DIR,
--- a/usr/src/cmd/devfsadm/sparc/misc_link_sparc.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/devfsadm/sparc/misc_link_sparc.c	Mon Oct 05 09:17:26 2009 -0400
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <regex.h>
 #include <devfsadm.h>
 #include <stdio.h>
@@ -41,7 +39,6 @@
 static int diskette(di_minor_t minor, di_node_t node);
 static int ecpp_create(di_minor_t minor, di_node_t node);
 static int mc_node(di_minor_t minor, di_node_t node);
-static int ddi_cardreader(di_minor_t minor, di_node_t node);
 static int starcat_sbbc_node(di_minor_t minor, di_node_t node);
 static int lom(di_minor_t minor, di_node_t node);
 static int ntwdt_create(di_minor_t minor, di_node_t node);
@@ -63,9 +60,6 @@
 	{ "printer",  "ddi_printer", NULL,
 	    TYPE_EXACT, ILEVEL_1, ecpp_create
 	},
-	{ "card-reader", "ddi_smartcard_reader", NULL,
-		TYPE_EXACT, ILEVEL_0, ddi_cardreader
-	},
 	{ "pseudo", "ddi_pseudo", "lw8",
 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, lom
 	},
@@ -79,17 +73,6 @@
 
 DEVFSADM_CREATE_INIT_V0(misc_cbt);
 
-/* Smart Card Reader device link */
-#define	CARDREADER_LINK		"^scmi2c[0-9]+$"
-
-/* Rules for removing links */
-static devfsadm_remove_t sparc_remove_cbt[] = {
-	{ "card-reader", CARDREADER_LINK, RM_PRE | RM_ALWAYS,
-		ILEVEL_0, devfsadm_rm_all }
-};
-
-DEVFSADM_REMOVE_INIT_V0(sparc_remove_cbt);
-
 
 /*
  * Handles minor node type "ddi_other"
@@ -213,57 +196,6 @@
 
 
 /*
- * This function is called for Smartcard card reader nodes
- * Handles minor node type "ddi_smartcard_reader"
- * type=ddi_smartcard_reader;name=card-reader   scmi2c\N0
- * Calls enumerate to assign logical card-reader id and then
- * devfsadm_mklink to make the link.
- */
-static int
-ddi_cardreader(di_minor_t minor, di_node_t node)
-{
-	char p_path[PATH_MAX +1], l_path[PATH_MAX +1];
-	char *buf;
-	char *ptr;
-	char *nn, *mn;
-
-	devfsadm_enumerate_t rules[1] = {"^scmi2c([0-9]+)$", 1, MATCH_ALL};
-
-	nn = di_node_name(node);
-	if (strcmp(nn, "card-reader")) {
-		return (DEVFSADM_CONTINUE);
-	}
-
-	if (NULL == (ptr = di_devfs_path(node))) {
-		return (DEVFSADM_CONTINUE);
-	}
-
-	(void) strcpy(p_path, ptr);
-	(void) strcat(p_path, ":");
-
-	mn = di_minor_name(minor);
-
-	(void) strcat(p_path, mn);
-	di_devfs_path_free(ptr);
-
-	if (devfsadm_enumerate_int(p_path, 0, &buf, rules, 1)) {
-		return (DEVFSADM_CONTINUE);
-	}
-	(void) snprintf(l_path, sizeof (l_path), "scmi2c%s", buf);
-	free(buf);
-	(void) devfsadm_mklink(l_path, node, minor, 0);
-
-	return (DEVFSADM_CONTINUE);
-}
-
-
-
-
-
-
-
-
-/*
  * Starcat sbbc node.  We only really care about generating a /dev
  * link for the lone sbbc on the SC (as opposed to the potentially
  * numerous sbbcs on the domain), so only operate on instance 0.
--- a/usr/src/cmd/fdisk/fdisk.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/fdisk/fdisk.c	Mon Oct 05 09:17:26 2009 -0400
@@ -253,6 +253,7 @@
 static char QNX2str[] = "QNX part 2";
 static char QNX3str[] = "QNX part 3";
 static char LINNATstr[] = "Linux native";
+static char LINSWAPstr[] = "Linux swap";
 static char NTFSVOL1str[] = "NT volset 1";
 static char NTFSVOL2str[] = "NT volset 2";
 static char BSDstr[] = "BSD OS";
@@ -695,7 +696,6 @@
 static int ext_read_valid_partition_start(uint32_t *begsec);
 static void ext_read_valid_partition_size(uint32_t begsec, uint32_t *endsec);
 static void ext_part_menu();
-static int is_linux_swap(uint32_t part_start, off_t *lsm_offset);
 static void add_logical_drive();
 static void delete_logical_drive();
 static void ext_print_help_menu();
@@ -1431,57 +1431,6 @@
 		    Table[new_pt].systid != SUNIXOS2)
 			continue;
 
-#ifdef i386
-
-		/*
-		 * Check if a solaris old partition is there in the new table.
-		 * If so, this could potentially have been a linux swap.
-		 * Check to see if the linux swap magic is there, and destroy
-		 * the magic if there is one.
-		 */
-		if (Table[new_pt].systid == SUNIXOS) {
-			off_t lsmo;
-			char *lsm_buf;
-
-			if ((lsm_buf = calloc(1, sectsiz)) == NULL) {
-				fprintf(stderr, "Could not allocate memory\n");
-				exit(1);
-			}
-
-			if (is_linux_swap(Table[new_pt].relsect, &lsmo) == 0) {
-				if (lseek(Dev, lsmo, SEEK_SET) < 0) {
-					fprintf(stderr, "Error seeking on "
-					    "%s\n", Dfltdev);
-					exit(1);
-				}
-
-				if (read(Dev, lsm_buf, sectsiz) < sectsiz) {
-					fprintf(stderr, "Error reading on "
-					    "%s\n", Dfltdev);
-					exit(1);
-				}
-
-				bzero(lsm_buf + sectsiz -
-				    LINUX_SWAP_MAGIC_LENGTH,
-				    LINUX_SWAP_MAGIC_LENGTH);
-
-				if (lseek(Dev, lsmo, SEEK_SET) < 0) {
-					fprintf(stderr, "Error seeking on "
-					    "%s\n", Dfltdev);
-					exit(1);
-				}
-
-				if (write(Dev, lsm_buf, sectsiz) < sectsiz) {
-					fprintf(stderr, "Error writing on "
-					    "%s\n", Dfltdev);
-					exit(1);
-				}
-			}
-			free(lsm_buf);
-		}
-
-#endif
-
 		/* Does the old table have an exact entry for the new entry? */
 		for (old_pt = 0; old_pt < FD_NUMPART; old_pt++) {
 
@@ -2262,6 +2211,11 @@
 				fdisk_add_logical_drive(epp, begsec, endsec,
 				    id);
 				return;
+			} else {
+				(void) fprintf(stderr,
+				    "fdisk: Invalid entry could not be "
+				    "inserted:\n        \"%s\"\n", file);
+				exit(1);
 			}
 		}
 #endif
@@ -2971,6 +2925,17 @@
 			break;
 		case 'e':	/* Extended partition, need extended int13 */
 		case 'E':
+#ifdef i386
+			if (ext_part_present) {
+				printf(Q_LINE);
+				printf(E_LINE);
+				fprintf(stderr,
+				    "Extended partition already exists\n");
+				fprintf(stderr, "Press enter to continue\n");
+				ext_read_input(s);
+				continue;
+			}
+#endif
 			tsystid = FDISK_EXTLBA;
 			break;
 		case 'f':
@@ -3718,6 +3683,11 @@
 			break;
 		case SUNIXOS:
 			type = SUstr;
+#ifdef i386
+			if (fdisk_is_linux_swap(epp, Table[i].relsect,
+			    NULL) == 0)
+				type = LINSWAPstr;
+#endif
 			break;
 		case SUNIXOS2:
 			type = SU2str;
@@ -5469,6 +5439,7 @@
 	int sysid;
 	unsigned int startcyl, endcyl, length, percent, remainder;
 	logical_drive_t *temp;
+	uint32_t part_start;
 	struct ipart *fpart;
 	char namebuff[255];
 	int numcyl = fdisk_get_disk_geom(epp, PHYSGEOM, NCYL);
@@ -5500,7 +5471,22 @@
 		/* Print the logical drive details */
 		fpart = &temp->parts[0];
 		sysid = fpart->systid;
-		id_to_name(sysid, namebuff);
+		/*
+		 * Check if partition id 0x82 is Solaris
+		 * or a Linux swap. Print the string
+		 * accordingly.
+		 */
+		if (sysid == SUNIXOS) {
+			part_start = temp->abs_secnum +
+			    temp->logdrive_offset;
+			if (fdisk_is_linux_swap(epp, part_start,
+			    NULL) == 0)
+				strcpy(namebuff, LINSWAPstr);
+			else
+				strcpy(namebuff, SUstr);
+		} else {
+			id_to_name(sysid, namebuff);
+		}
 		startcyl = temp->begcyl;
 		endcyl = temp->endcyl;
 		if (startcyl == endcyl) {
@@ -5604,59 +5590,3 @@
 	}
 }
 #endif
-
-#ifdef i386
-
-static int
-is_linux_swap(uint32_t part_start, off_t *lsm_offset)
-{
-	int		i;
-	int		rval = -1;
-	off_t		seek_offset;
-	uint32_t	linux_pg_size;
-	char		*buf, *linux_swap_magic;
-	/*
-	 * Known linux kernel page sizes
-	 * The linux swap magic is found as the last 10 bytes of a disk chunk
-	 * at the beginning of the linux swap partition whose size is that of
-	 * kernel page size.
-	 */
-	uint32_t	linux_pg_size_arr[] = {4096, };
-
-	if ((buf = calloc(1, sectsiz)) == NULL) {
-		return (ENOMEM);
-	}
-
-	linux_swap_magic = buf + sectsiz - LINUX_SWAP_MAGIC_LENGTH;
-
-	for (i = 0; i < sizeof (linux_pg_size_arr)/sizeof (uint32_t); i++) {
-		linux_pg_size = linux_pg_size_arr[i];
-		seek_offset = linux_pg_size/sectsiz - 1;
-		seek_offset += part_start;
-		seek_offset *= sectsiz;
-
-		if ((rval = lseek(Dev, seek_offset, SEEK_SET)) < 0) {
-			break;
-		}
-
-		if ((rval = read(Dev, buf, sectsiz)) < sectsiz) {
-			rval = EIO;
-			break;
-		}
-
-		if ((strncmp(linux_swap_magic, "SWAP-SPACE",
-		    LINUX_SWAP_MAGIC_LENGTH) == 0) ||
-		    (strncmp(linux_swap_magic, "SWAPSPACE2",
-		    LINUX_SWAP_MAGIC_LENGTH) == 0)) {
-			/* Found a linux swap */
-			rval = 0;
-			*lsm_offset = seek_offset;
-			break;
-		}
-	}
-
-	free(buf);
-	return (rval);
-}
-
-#endif
--- a/usr/src/cmd/fm/modules/common/zfs-diagnosis/zfs_de.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/fm/modules/common/zfs-diagnosis/zfs_de.c	Mon Oct 05 09:17:26 2009 -0400
@@ -404,18 +404,12 @@
 	}
 
 	/*
-	 * Determine if this ereport corresponds to an open case.  Cases are
-	 * indexed by ENA, since ZFS does all the work of chaining together
-	 * related ereports.
-	 *
-	 * We also detect if an ereport corresponds to an open case by context,
-	 * such as:
-	 *
-	 * 	- An error occurred during an open of a pool with an existing
-	 *	  case.
-	 *
-	 * 	- An error occurred for a device which already has an open
-	 *	  case.
+	 * Determine if this ereport corresponds to an open case.  Previous
+	 * incarnations of this DE used the ENA to chain events together as
+	 * part of the same case.  The problem with this is that we rely on
+	 * global uniqueness of cases based on (pool_guid, vdev_guid) pair when
+	 * generating SERD engines.  Instead, we have a case for each vdev or
+	 * pool, regardless of the ENA.
 	 */
 	(void) nvlist_lookup_uint64(nvl,
 	    FM_EREPORT_PAYLOAD_ZFS_POOL_GUID, &pool_guid);
@@ -427,24 +421,8 @@
 
 	for (zcp = uu_list_first(zfs_cases); zcp != NULL;
 	    zcp = uu_list_next(zfs_cases, zcp)) {
-		/*
-		 * Matches a known ENA.
-		 */
-		if (zcp->zc_data.zc_ena == ena)
-			break;
-
-		/*
-		 * Matches a case involving load errors for this same pool.
-		 */
 		if (zcp->zc_data.zc_pool_guid == pool_guid &&
-		    zcp->zc_data.zc_pool_state == SPA_LOAD_OPEN &&
-		    pool_state == SPA_LOAD_OPEN)
-			break;
-
-		/*
-		 * Device errors for the same device.
-		 */
-		if (vdev_guid != 0 && zcp->zc_data.zc_vdev_guid == vdev_guid)
+		    zcp->zc_data.zc_vdev_guid == vdev_guid)
 			break;
 	}
 
@@ -569,11 +547,7 @@
 		zfs_case_solve(hdl, zcp, "fault.fs.zfs.log_replay", B_TRUE);
 	} else if (fmd_nvl_class_match(hdl, nvl, "ereport.fs.zfs.vdev.*")) {
 		/*
-		 * Device fault.  If this occurred during pool open, then defer
-		 * reporting the fault.  If the pool itself could not be opeend,
-		 * we only report the pool fault, not every device fault that
-		 * may have caused the problem.  If we do not see a pool fault
-		 * within the timeout period, then we'll solve the device case.
+		 * Device fault.
 		 */
 		zfs_case_solve(hdl, zcp, "fault.fs.zfs.device",  B_TRUE);
 	} else if (fmd_nvl_class_match(hdl, nvl,
--- a/usr/src/cmd/format/menu_fdisk.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/format/menu_fdisk.c	Mon Oct 05 09:17:26 2009 -0400
@@ -496,6 +496,9 @@
 	(void) memcpy(&boot_sec, mbr, sizeof (struct mboot));
 	free(mbr);
 
+#ifdef i386
+	(void) extpart_init(&epp);
+#endif
 	for (i = 0; i < FD_NUMPART; i++) {
 		int	ipc;
 
@@ -509,7 +512,6 @@
 		if (fdisk_is_dos_extended(ip.systid) && (ext_part_found == 0)) {
 			/* We support only one extended partition per disk */
 			ext_part_found = 1;
-			(void) extpart_init(&epp);
 			rval = fdisk_get_solaris_part(epp, &pno, &relsec,
 			    &numsec);
 			if (rval == FDISK_SUCCESS) {
@@ -535,7 +537,6 @@
 				    sizeof (struct ipart));
 				bcopy(&ip, ipart, sizeof (struct ipart));
 			}
-			libfdisk_fini(&epp);
 			continue;
 		}
 #endif
@@ -543,9 +544,16 @@
 		/*
 		 * we are interested in Solaris and EFI partition types
 		 */
+#ifdef i386
+		if ((ip.systid == SUNIXOS &&
+		    (fdisk_is_linux_swap(epp, lel(ip.relsect), NULL) != 0)) ||
+		    ip.systid == SUNIXOS2 ||
+		    ip.systid == EFI_PMBR) {
+#else
 		if (ip.systid == SUNIXOS ||
 		    ip.systid == SUNIXOS2 ||
 		    ip.systid == EFI_PMBR) {
+#endif
 			/*
 			 * if the disk has an EFI label, nhead and nsect may
 			 * be zero.  This test protects us from FPE's, and
@@ -569,6 +577,10 @@
 		}
 	}
 
+#ifdef i386
+	libfdisk_fini(&epp);
+#endif
+
 	if (!found) {
 		err_print("Solaris fdisk partition not found\n");
 		return (-1);
@@ -704,6 +716,9 @@
 
 	(void) memcpy(&mboot, mbr, sizeof (struct mboot));
 
+#ifdef i386
+	(void) extpart_init(&epp);
+#endif
 	for (i = 0; i < FD_NUMPART; i++) {
 		int	ipc;
 
@@ -717,7 +732,6 @@
 		if (fdisk_is_dos_extended(ip.systid) && (ext_part_found == 0)) {
 			/* We support only one extended partition per disk */
 			ext_part_found = 1;
-			(void) extpart_init(&epp);
 			rval = fdisk_get_solaris_part(epp, &pno, &relsec,
 			    &numsec);
 			if (rval == FDISK_SUCCESS) {
@@ -738,14 +752,21 @@
 				ip.numsect = numsec;
 				bcopy(&ip, ipart, sizeof (struct ipart));
 			}
-			libfdisk_fini(&epp);
 			continue;
 		}
 #endif
 
+
+#ifdef i386
+		if ((ip.systid == SUNIXOS &&
+		    (fdisk_is_linux_swap(epp, lel(ip.relsect), NULL) != 0)) ||
+		    ip.systid == SUNIXOS2 ||
+		    ip.systid == EFI_PMBR) {
+#else
 		if (ip.systid == SUNIXOS ||
 		    ip.systid == SUNIXOS2 ||
 		    ip.systid == EFI_PMBR) {
+#endif
 			solaris_offset = lel(ip.relsect);
 			bcopy(&ip, ipart, sizeof (struct ipart));
 
@@ -769,6 +790,9 @@
 			break;
 		}
 	}
+#ifdef i386
+	libfdisk_fini(&epp);
+#endif
 
 	(void) close(fd);
 	free(mbr);
@@ -817,6 +841,9 @@
 
 	(void) memcpy(&mboot, mbr, sizeof (struct mboot));
 
+#ifdef i386
+	(void) extpart_init(&epp);
+#endif
 	for (i = 0; i < FD_NUMPART; i++) {
 		int	ipc;
 
@@ -830,7 +857,6 @@
 		if (fdisk_is_dos_extended(ip.systid) && (ext_part_found == 0)) {
 			/* We support only one extended partition per disk */
 			ext_part_found = 1;
-			(void) extpart_init(&epp);
 			rval = fdisk_get_solaris_part(epp, &pno, &relsec,
 			    &numsec);
 			if (rval == FDISK_SUCCESS) {
@@ -848,7 +874,6 @@
 				}
 				solaris_offset = relsec;
 			}
-			libfdisk_fini(&epp);
 			continue;
 		}
 #endif
@@ -858,9 +883,18 @@
 		 * the label may be zero.  This protects us from FPE's, and
 		 * format still seems to work happily
 		 */
+
+
+#ifdef i386
+		if ((ip.systid == SUNIXOS &&
+		    (fdisk_is_linux_swap(epp, lel(ip.relsect), NULL) != 0)) ||
+		    ip.systid == SUNIXOS2 ||
+		    ip.systid == EFI_PMBR) {
+#else
 		if (ip.systid == SUNIXOS ||
 		    ip.systid == SUNIXOS2 ||
 		    ip.systid == EFI_PMBR) {
+#endif
 			if ((label->dkl_nhead != 0) &&
 			    (label->dkl_nsect != 0)) {
 				label->dkl_pcyl = lel(ip.numsect) /
@@ -884,6 +918,9 @@
 		}
 	}
 
+#ifdef i386
+	libfdisk_fini(&epp);
+#endif
 	(void) close(fd);
 	free(mbr);
 	return (0);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+PROG = latencytop
+
+include ../Makefile.cmd
+
+$(64ONLY)SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all	:=	TARGET = all
+install	:=	TARGET = install
+clean	:=	TARGET = clean
+clobber	:=	TARGET = clobber
+lint	:=	TARGET = lint
+
+.KEEP_STATE:
+
+all install clean clobber lint:	$(SUBDIRS)
+
+$(SUBDIRS):	FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/Makefile.com	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,79 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+PROG = latencytop
+OBJS = latencytop.o display.o dwrapper.o klog.o stat.o table.o util.o
+SRCS = $(OBJS:%.o=../common/%.c)
+
+include ../../Makefile.cmd
+
+CFLAGS += $(CCVERBOSE)
+CFLAGS64 += $(CCVERBOSE)
+
+CPPFLAGS += -DEMBED_CONFIGS -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
+C99MODE = $(C99_ENABLE)
+LDLIBS += -lcurses -ldtrace
+all install	:= LDLIBS += -lglib-2.0
+
+LINTFLAGS += -erroff=E_NAME_USED_NOT_DEF2
+LINTFLAGS64 += -erroff=E_NAME_USED_NOT_DEF2
+
+FILEMODE = 0555
+
+ELFWRAP = elfwrap
+WRAPOBJ = latencytop_wrap.o
+
+CLEANFILES += $(OBJS) $(WRAPOBJ) ./latencytop_d ./latencytop_trans
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install:        $(SUBDIRS)
+	-$(RM) $(ROOTPROG)
+	-$(LN) $(ISAEXEC) $(ROOTPROG)
+
+$(PROG): $(OBJS) $(WRAPOBJ)
+	$(LINK.c) -o $@ $(OBJS) $(WRAPOBJ) $(LDLIBS)
+	$(POST_PROCESS)
+
+$(WRAPOBJ): latencytop_d latencytop_trans
+	$(ELFWRAP) $(WRAPOPT) -o $(WRAPOBJ) latencytop_d latencytop_trans
+
+latencytop_d:
+	cp ../common/latencytop.d ./latencytop_d
+
+latencytop_trans:
+	cp ../common/latencytop.trans ./latencytop_trans
+
+clean:
+	$(RM) $(CLEANFILES)
+
+lint:	lint_SRCS
+
+%.o: ../common/%.c
+	$(COMPILE.c) $<
+
+include ../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/amd64/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+include ../Makefile.com
+include ../../Makefile.cmd.64
+
+WRAPOPT = -64
+
+install: all $(ROOTPROG64)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/display.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,1040 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <curses.h>
+#include <time.h>
+#include <wchar.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <signal.h>
+
+#include "latencytop.h"
+
+#define	LT_WINDOW_X			80
+#define	LT_WINDOW_Y			24
+
+#define	LT_COLOR_DEFAULT		1
+#define	LT_COLOR_HEADER			2
+
+/* Windows created by libcurses */
+static WINDOW	*titlebar = NULL;
+static WINDOW	*captionbar = NULL;
+static WINDOW	*sysglobal_window = NULL;
+static WINDOW	*taskbar = NULL;
+static WINDOW	*process_window = NULL;
+static WINDOW	*hintbar = NULL;
+/* Screen dimension */
+static int	screen_width = 1, screen_height = 1;
+/* Is display initialized, i.e. are window pointers set up. */
+static int	display_initialized = FALSE;
+/* Is initscr() called */
+static int	curses_inited = FALSE;
+
+/* To handle user key presses */
+static pid_t selected_pid = INVALID_PID;
+static id_t selected_tid = INVALID_TID;
+static lt_sort_t sort_type = LT_SORT_TOTAL;
+static int thread_mode = FALSE;
+/* Type of list being displayed */
+static int current_list_type = LT_LIST_CAUSE;
+static int show_help = FALSE;
+
+/* Help functions that append/prepend a blank to the given string */
+#define	fill_space_right(a, b, c)	fill_space((a), (b), (c), TRUE)
+#define	fill_space_left(a, b, c)	fill_space((a), (b), (c), FALSE)
+
+static void
+fill_space(char *buffer, int len, int buffer_limit, int is_right)
+{
+	int i = 0;
+	int tofill;
+
+	if (len >= buffer_limit) {
+		len = buffer_limit - 1;
+	}
+
+	i = strlen(buffer);
+
+	if (i >= len) {
+		return;
+	}
+
+	tofill = len - i;
+
+	if (is_right) {
+		(void) memset(&buffer[i], ' ', tofill);
+		buffer[len] = '\0';
+	} else {
+		(void) memmove(&buffer[tofill], buffer, i+1);
+		(void) memset(buffer, ' ', tofill);
+	}
+}
+
+/* Convert the nanosecond value to a human readable string */
+static const char *
+get_time_string(double nanoseconds, char *buffer, int len, int fill_width)
+{
+	const double ONE_USEC = 1000.0;
+	const double ONE_MSEC = 1000000.0;
+	const double ONE_SEC  = 1000000000.0;
+
+	if (nanoseconds < (ONE_USEC - .5)) {
+		(void) snprintf(buffer, len, "%3.1f nsec", nanoseconds);
+	} else if (nanoseconds < (ONE_MSEC - .5 * ONE_USEC)) {
+		(void) snprintf(buffer, len,
+		    "%3.1f usec", nanoseconds / ONE_USEC);
+	} else if (nanoseconds < (ONE_SEC - .5 * ONE_MSEC)) {
+		(void) snprintf(buffer, len,
+		    "%3.1f msec", nanoseconds / ONE_MSEC);
+	} else if (nanoseconds < 999.5 * ONE_SEC) {
+		(void) snprintf(buffer, len,
+		    "%3.1f  sec", nanoseconds / ONE_SEC);
+	} else {
+		(void) snprintf(buffer, len,
+		    "%.0e sec", nanoseconds / ONE_SEC);
+	}
+
+	fill_space_left(buffer, fill_width, len);
+	return (buffer);
+}
+
+/* Used in print_statistics below */
+#define	WIDTH_REASON_STRING	36
+#define	WIDTH_COUNT		12
+#define	WIDTH_AVG		12
+#define	WIDTH_MAX		12
+#define	WIDTH_PCT		8
+#define	BEGIN_COUNT		WIDTH_REASON_STRING
+#define	BEGIN_AVG		(BEGIN_COUNT + WIDTH_COUNT)
+#define	BEGIN_MAX		(BEGIN_AVG + WIDTH_AVG)
+#define	BEGIN_PCT		(BEGIN_MAX + WIDTH_MAX)
+
+/*
+ * Print statistics in global/process pane. Called by print_sysglobal
+ * print_process.
+ *
+ * Parameters:
+ *		window - the global or process statistics window.
+ *		begin_line - where to start printing.
+ *		count - how many lines should be printed.
+ *		list - a stat_list.
+ */
+static void
+print_statistics(WINDOW * window, int begin_line, int nlines, void *list)
+{
+	uint64_t total;
+	int i = 0;
+
+	if (!display_initialized) {
+		return;
+	}
+
+	total = lt_stat_list_get_gtotal(list);
+
+	if (total == 0) {
+		return;
+	}
+
+	while (i < nlines && lt_stat_list_has_item(list, i)) {
+
+		char tmp[WIDTH_REASON_STRING];
+		const char *reason = lt_stat_list_get_reason(list, i);
+		uint64_t count = lt_stat_list_get_count(list, i);
+
+		if (count == 0) {
+			continue;
+		}
+
+		(void) snprintf(tmp, sizeof (tmp), "%s", reason);
+		(void) mvwprintw(window, i + begin_line, 0, "%s", tmp);
+
+		(void) snprintf(tmp, sizeof (tmp), "%llu", count);
+		fill_space_left(tmp, WIDTH_COUNT, sizeof (tmp));
+		(void) mvwprintw(window, i + begin_line, BEGIN_COUNT,
+		    "%s", tmp);
+
+		(void) mvwprintw(window, i + begin_line, BEGIN_AVG,
+		    "%s", get_time_string(
+		    (double)lt_stat_list_get_sum(list, i) / count,
+		    tmp, sizeof (tmp), WIDTH_AVG));
+
+		(void) mvwprintw(window, i + begin_line, BEGIN_MAX,
+		    "%s", get_time_string(
+		    (double)lt_stat_list_get_max(list, i),
+		    tmp, sizeof (tmp), WIDTH_MAX));
+
+		if (LT_LIST_SPECIALS != current_list_type) {
+			(void) snprintf(tmp, sizeof (tmp), "%.1f %%",
+			    (double)lt_stat_list_get_sum(list, i)
+			    / total * 100.0);
+		} else {
+			(void) snprintf(tmp, sizeof (tmp), "--- ");
+		}
+
+		fill_space_left(tmp, WIDTH_PCT, sizeof (tmp));
+
+		(void) mvwprintw(window, i + begin_line, BEGIN_PCT,
+		    "%s", tmp);
+		i++;
+	}
+}
+
+/*
+ * Print statistics in global pane.
+ */
+static void
+print_sysglobal(void)
+{
+	void *list;
+	char header[256];
+
+	if (!display_initialized) {
+		return;
+	}
+
+	(void) werase(sysglobal_window);
+
+	(void) wattron(sysglobal_window, A_REVERSE);
+	(void) snprintf(header, sizeof (header),
+	    "%s", "System wide latencies");
+	fill_space_right(header, screen_width, sizeof (header));
+	(void) mvwprintw(sysglobal_window, 0, 0, "%s", header);
+	(void) wattroff(sysglobal_window, A_REVERSE);
+
+	list = lt_stat_list_create(current_list_type,
+	    LT_LEVEL_GLOBAL, 0, 0, 10, sort_type);
+	print_statistics(sysglobal_window, 1, 10, list);
+	lt_stat_list_free(list);
+
+	(void) wrefresh(sysglobal_window);
+}
+
+/*
+ * Prints current operation mode. Mode is combination of:
+ *
+ * 	"Process or Thread", and "1 or 2 or 3".
+ */
+static void
+print_current_mode()
+{
+	char type;
+
+	if (!display_initialized) {
+		return;
+	}
+
+	switch (current_list_type) {
+	case LT_LIST_CAUSE:
+		type = '1';
+		break;
+	case LT_LIST_SPECIALS:
+		type = '2';
+		break;
+	case LT_LIST_SOBJ:
+		type = '3';
+		break;
+	default:
+		type = '?';
+		break;
+	}
+
+	(void) mvwprintw(process_window, 0, screen_width - 8, "View: %c%c",
+	    type, thread_mode ? 'T' : 'P');
+}
+
+/*
+ * Print per-process statistics in process pane.
+ * This is called when mode of operation is process.
+ */
+static void
+print_process(unsigned int pid)
+{
+	void *list;
+	char header[256];
+	char tmp[30];
+
+	if (!display_initialized) {
+		return;
+	}
+
+	list = lt_stat_list_create(current_list_type, LT_LEVEL_PROCESS,
+	    pid, 0, 8, sort_type);
+
+	(void) werase(process_window);
+	(void) wattron(process_window, A_REVERSE);
+	(void) snprintf(header, sizeof (header), "Process %s (%i), %d threads",
+	    lt_stat_proc_get_name(pid), pid, lt_stat_proc_get_nthreads(pid));
+	fill_space_right(header, screen_width, sizeof (header));
+	(void) mvwprintw(process_window, 0, 0, "%s", header);
+
+	if (current_list_type != LT_LIST_SPECIALS) {
+		(void) mvwprintw(process_window, 0, 48, "Total: %s",
+		    get_time_string((double)lt_stat_list_get_gtotal(list),
+		    tmp, sizeof (tmp), 12));
+	}
+
+	print_current_mode();
+	(void) wattroff(process_window, A_REVERSE);
+	print_statistics(process_window, 1, 8, list);
+	lt_stat_list_free(list);
+
+	(void) wrefresh(process_window);
+}
+
+/*
+ * Display the list of processes that are tracked, in task bar.
+ * This one is called when mode of operation is process.
+ */
+static void
+print_taskbar_process(pid_t *pidlist, int pidlist_len, int pidlist_index)
+{
+	const int ITEM_WIDTH = 8;
+
+	int number_item;
+	int i;
+	int xpos = 0;
+
+	if (!display_initialized) {
+		return;
+	}
+
+	number_item = (screen_width / ITEM_WIDTH) - 1;
+	i = pidlist_index - (pidlist_index % number_item);
+
+	(void) werase(taskbar);
+
+	if (i != 0) {
+		(void) mvwprintw(taskbar, 0, xpos, "<-");
+	}
+
+	xpos = ITEM_WIDTH / 2;
+
+	while (xpos + ITEM_WIDTH <= screen_width && i < pidlist_len) {
+		char str[ITEM_WIDTH+1];
+		int slen;
+		const char *pname = lt_stat_proc_get_name(pidlist[i]);
+
+		if (pname && pname[0]) {
+			(void) snprintf(str, sizeof (str) - 1, "%s", pname);
+		} else {
+			(void) snprintf(str, sizeof (str) - 1,
+			    "<%d>", pidlist[i]);
+		}
+
+		slen = strlen(str);
+
+		if (slen < ITEM_WIDTH) {
+			(void) memset(&str[slen], ' ', ITEM_WIDTH - slen);
+		}
+
+		str[sizeof (str) - 1] = '\0';
+
+		if (i == pidlist_index) {
+			(void) wattron(taskbar, A_REVERSE);
+		}
+
+		(void) mvwprintw(taskbar, 0, xpos, "%s", str);
+
+		if (i == pidlist_index) {
+			(void) wattroff(taskbar, A_REVERSE);
+		}
+
+		xpos += ITEM_WIDTH;
+		i++;
+	}
+
+	if (i != pidlist_len) {
+		(void) mvwprintw(taskbar, 0, screen_width - 2, "->");
+	}
+
+	(void) wrefresh(taskbar);
+}
+
+/*
+ * Display the list of processes that are tracked, in task bar.
+ * This one is called when mode of operation is thread.
+ */
+static void
+print_taskbar_thread(pid_t *pidlist, id_t *tidlist, int list_len,
+    int list_index)
+{
+	const int ITEM_WIDTH = 12;
+
+	int number_item;
+	int i;
+	int xpos = 0;
+	const char *pname = NULL;
+	pid_t last_pid = INVALID_PID;
+
+
+	if (!display_initialized) {
+		return;
+	}
+
+	number_item = (screen_width - 8) / ITEM_WIDTH;
+	i = list_index - (list_index % number_item);
+
+	(void) werase(taskbar);
+
+	if (i != 0) {
+		(void) mvwprintw(taskbar, 0, xpos, "<-");
+	}
+
+	xpos = 4;
+
+	while (xpos + ITEM_WIDTH <= screen_width && i < list_len) {
+		char str[ITEM_WIDTH+1];
+		int slen, tlen;
+
+		if (pidlist[i] != last_pid) {
+			pname = lt_stat_proc_get_name(pidlist[i]);
+			last_pid = pidlist[i];
+		}
+
+		/*
+		 * Calculate length of thread's ID; use shorter process name
+		 * in order to save space on the screen.
+		 */
+		tlen = snprintf(NULL, 0, "_%d", tidlist[i]);
+
+		if (pname && pname[0]) {
+			(void) snprintf(str, sizeof (str) - tlen - 1,
+			    "%s", pname);
+		} else {
+			(void) snprintf(str, sizeof (str) - tlen - 1,
+			    "<%d>", pidlist[i]);
+		}
+
+		slen = strlen(str);
+
+		(void) snprintf(&str[slen], sizeof (str) - slen,
+		    "_%d", tidlist[i]);
+
+		slen += tlen;
+
+		if (slen < ITEM_WIDTH) {
+			(void) memset(&str[slen], ' ', ITEM_WIDTH - slen);
+		}
+
+		str[sizeof (str) - 1] = '\0';
+
+		if (i == list_index) {
+			(void) wattron(taskbar, A_REVERSE);
+		}
+
+		(void) mvwprintw(taskbar, 0, xpos, "%s", str);
+
+		if (i == list_index) {
+			(void) wattroff(taskbar, A_REVERSE);
+		}
+
+		xpos += ITEM_WIDTH;
+		i++;
+	}
+
+	if (i != list_len) {
+		(void) mvwprintw(taskbar, 0, screen_width - 2, "->");
+	}
+
+	(void) wrefresh(taskbar);
+}
+
+/*
+ * Print per-thread statistics in process pane.
+ * This is called when mode of operation is thread.
+ */
+static void
+print_thread(pid_t pid, id_t tid)
+{
+	void *list;
+	char header[256];
+	char tmp[30];
+
+	if (!display_initialized) {
+		return;
+	}
+
+	list = lt_stat_list_create(current_list_type, LT_LEVEL_THREAD,
+	    pid, tid, 8, sort_type);
+
+	(void) werase(process_window);
+	(void) wattron(process_window, A_REVERSE);
+	(void) snprintf(header, sizeof (header),
+	    "Process %s (%i), LWP %d",
+	    lt_stat_proc_get_name(pid), pid, tid);
+	fill_space_right(header, screen_width, sizeof (header));
+	(void) mvwprintw(process_window, 0, 0, "%s", header);
+
+	if (current_list_type != LT_LIST_SPECIALS) {
+		(void) mvwprintw(process_window, 0, 48, "Total: %s",
+		    get_time_string(
+		    (double)lt_stat_list_get_gtotal(list),
+		    tmp, sizeof (tmp), 12));
+	}
+
+	print_current_mode();
+	(void) wattroff(process_window, A_REVERSE);
+	print_statistics(process_window, 1, 8, list);
+	lt_stat_list_free(list);
+	(void) wrefresh(process_window);
+}
+
+/*
+ * Update hint string at the bottom line. The message to print is stored in
+ * hint. If hint is NULL, the function will display its own message.
+ */
+static void
+print_hint(const char *hint)
+{
+	const char *HINTS[] =    {
+		"Press '<' or '>' to switch between processes.",
+		"Press 'q' to exit.",
+		"Press 'r' to refresh immediately.",
+		"Press 't' to toggle Process/Thread display mode.",
+		"Press 'h' for help.",
+		"Use 'c', 'a', 'm', 'p' to change sort criteria."
+		"Use '1', '2', '3' to switch between windows."
+	};
+	const uint64_t update_interval = 5000; /* 5 seconds */
+
+	static int index = 0;
+	static uint64_t next_hint = 0;
+	uint64_t now = lt_millisecond();
+
+	if (!display_initialized) {
+		return;
+	}
+
+	if (hint == NULL) {
+		if (now < next_hint) {
+			return;
+		}
+
+		hint = HINTS[index];
+		index = (index + 1) % (sizeof (HINTS) / sizeof (HINTS[0]));
+		next_hint = now + update_interval;
+	} else {
+		/*
+		 * Important messages are displayed at least every 2 cycles.
+		 */
+		next_hint = now + update_interval * 2;
+	}
+
+	(void) werase(hintbar);
+	(void) mvwprintw(hintbar, 0, (screen_width - strlen(hint)) / 2,
+	    "%s", hint);
+	(void) wrefresh(hintbar);
+}
+
+/*
+ * Create a PID list or a PID/TID list (if operation mode is thread) from
+ * available statistics.
+ */
+static void
+get_plist(pid_t **plist, id_t **tlist, int *list_len, int *list_index)
+{
+	if (!thread_mode) {
+		/* Per-process mode */
+		*list_len = lt_stat_proc_list_create(plist, NULL);
+
+		/* Search for previously selected PID */
+		for (*list_index = 0; *list_index < *list_len &&
+		    (*plist)[*list_index] != selected_pid;
+		    ++*list_index) {
+		}
+
+		if (*list_index >= *list_len) {
+			/*
+			 * The previously selected pid is gone.
+			 * Select the first one.
+			 */
+			*list_index = 0;
+		}
+	} else {
+		/* Per-thread mode */
+		*list_len = lt_stat_proc_list_create(plist, tlist);
+
+		/* Search for previously selected PID & TID */
+		for (*list_index = 0; *list_index < *list_len;
+		    ++*list_index) {
+			if ((*plist)[*list_index] == selected_pid &&
+			    (*tlist)[*list_index] == selected_tid) {
+				break;
+			}
+		}
+
+		if (*list_index >= *list_len) {
+			/*
+			 * The previously selected pid/tid is gone.
+			 * Select the first one.
+			 */
+			for (*list_index = 0;
+			    *list_index < *list_len &&
+			    (*plist)[*list_index] != selected_pid;
+			    ++*list_index) {
+			}
+		}
+
+		if (*list_index >= *list_len) {
+			/*
+			 * The previously selected pid is gone.
+			 * Select the first one
+			 */
+			*list_index = 0;
+		}
+	}
+}
+
+/* Print help message when user presses 'h' hot key */
+static void
+print_help(void)
+{
+	const char *HELP[] =    {
+		TITLE,
+		COPYRIGHT,
+		"",
+		"These single-character commands are available:",
+		"<       - Move to previous process/thread.",
+		">       - Move to next process/thread.",
+		"q       - Exit.",
+		"r       - Refresh.",
+		"t       - Toggle process/thread mode.",
+		"c       - Sort by count.",
+		"a       - Sort by average.",
+		"m       - Sort by maximum.",
+		"p       - Sort by percent.",
+		"1       - Show list by causes.",
+		"2       - Show list of special entries.",
+		"3       - Show list by synchronization objects.",
+		"h       - Show this help.",
+		"",
+		"Press any key to continue..."
+	};
+	int i;
+
+	if (!display_initialized) {
+		return;
+	}
+
+	for (i = 0; i < sizeof (HELP) / sizeof (HELP[0]); ++i) {
+		(void) mvwprintw(stdscr, i, 0, "%s", HELP[i]);
+	}
+
+	(void) refresh();
+}
+
+/*
+ * Print title on screen
+ */
+static void
+print_title(void)
+{
+	if (!display_initialized) {
+		return;
+	}
+
+	(void) wattrset(titlebar, COLOR_PAIR(LT_COLOR_HEADER));
+	(void) wbkgd(titlebar, COLOR_PAIR(LT_COLOR_HEADER));
+	(void) werase(titlebar);
+
+	(void) mvwprintw(titlebar, 0, (screen_width - strlen(TITLE)) / 2,
+	    "%s", TITLE);
+	(void) wrefresh(titlebar);
+
+	(void) werase(captionbar);
+	(void) mvwprintw(captionbar, 0, 0, "%s",
+	    "               Cause                    "
+	    "Count      Average     Maximum   Percent");
+	(void) wrefresh(captionbar);
+
+	(void) wattrset(hintbar, COLOR_PAIR(LT_COLOR_HEADER));
+	(void) wbkgd(hintbar, COLOR_PAIR(LT_COLOR_HEADER));
+}
+
+/*
+ * Handle signal from terminal resize
+ */
+/* ARGSUSED */
+static void
+on_resize(int sig)
+{
+	lt_gpipe_break("r");
+}
+
+/*
+ * Initialize display. Display will be cleared when this function returns.
+ */
+void
+lt_display_init(void)
+{
+	if (display_initialized) {
+		return;
+	}
+
+	/* Window resize signal */
+	(void) signal(SIGWINCH, on_resize);
+
+	/* Initialize curses library */
+	(void) initscr();
+	(void) start_color();
+	(void) keypad(stdscr, TRUE);
+	(void) nonl();
+	(void) cbreak();
+	(void) noecho();
+	(void) curs_set(0);
+
+	/* Set up color pairs */
+	(void) init_pair(LT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK);
+	(void) init_pair(LT_COLOR_HEADER, COLOR_BLACK, COLOR_WHITE);
+
+	curses_inited = TRUE;
+	getmaxyx(stdscr, screen_height, screen_width);
+
+	if (screen_width < LT_WINDOW_X || screen_height < LT_WINDOW_Y) {
+		(void) mvwprintw(stdscr, 0, 0, "Terminal size is too small.");
+		(void) mvwprintw(stdscr, 1, 0,
+		    "Please resize it to 80x24 or larger.");
+		(void) mvwprintw(stdscr, 2, 0, "Press q to quit.");
+		(void) refresh();
+		return;
+	}
+
+	/* Set up all window panes */
+	titlebar = subwin(stdscr, 1, screen_width, 0, 0);
+	captionbar = subwin(stdscr, 1, screen_width, 1, 0);
+	sysglobal_window = subwin(stdscr, screen_height / 2 - 1,
+	    screen_width, 2, 0);
+	process_window = subwin(stdscr, screen_height / 2 - 3,
+	    screen_width, screen_height / 2 + 1, 0);
+	taskbar = subwin(stdscr, 1, screen_width, screen_height - 2, 0);
+	hintbar = subwin(stdscr, 1, screen_width, screen_height - 1, 0);
+	(void) werase(stdscr);
+	(void) refresh();
+
+	display_initialized = TRUE;
+
+	print_title();
+}
+
+/*
+ * The event loop for display. It displays data on screen and handles hotkey
+ * presses.
+ *
+ * Parameter :
+ *		duration - returns after 'duration'
+ *
+ * The function also returns if user presses 'q', 'Ctrl+C' or 'r'.
+ *
+ * Return value:
+ *		0 - main() exits
+ *		1 - main() calls it again
+ */
+int
+lt_display_loop(int duration)
+{
+	uint64_t start;
+	int remaining;
+	struct timeval timeout;
+	fd_set read_fd;
+	int need_refresh = TRUE;
+	pid_t *plist = NULL;
+	id_t *tlist = NULL;
+	int list_len = 0;
+	int list_index = 0;
+	int retval = 1;
+	int next_snap;
+	int gpipe;
+
+	start = lt_millisecond();
+	gpipe = lt_gpipe_readfd();
+
+	if (!show_help) {
+		print_hint(NULL);
+		print_sysglobal();
+	}
+
+	get_plist(&plist, &tlist, &list_len, &list_index);
+
+	for (;;) {
+		if (list_len != 0 && need_refresh && !show_help) {
+			if (!thread_mode) {
+				print_taskbar_process(plist, list_len,
+				    list_index);
+				print_process(plist[list_index]);
+			} else {
+				print_taskbar_thread(plist, tlist,
+				    list_len, list_index);
+				print_thread(plist[list_index],
+				    tlist[list_index]);
+			}
+		}
+
+		need_refresh = TRUE;	/* Usually we need refresh. */
+		remaining = duration - (int)(lt_millisecond() - start);
+
+		if (remaining <= 0) {
+			break;
+		}
+
+		/* Embedded dtrace snap action here. */
+		next_snap = lt_dtrace_work(0);
+
+		if (next_snap == 0) {
+			/*
+			 * Just did a snap, check time for the next one.
+			 */
+			next_snap = lt_dtrace_work(0);
+		}
+
+		if (next_snap > 0 && remaining > next_snap) {
+			remaining = next_snap;
+		}
+
+		timeout.tv_sec = remaining / 1000;
+		timeout.tv_usec = (remaining % 1000) * 1000;
+
+		FD_ZERO(&read_fd);
+		FD_SET(0, &read_fd);
+		FD_SET(gpipe, &read_fd);
+
+		/* Wait for keyboard input, or signal from gpipe */
+		if (select(gpipe + 1, &read_fd, NULL, NULL, &timeout) > 0) {
+			int k = 0;
+
+			if (FD_ISSET(gpipe, &read_fd)) {
+				/* Data from pipe has priority */
+				char ch;
+				(void) read(gpipe, &ch, 1);
+				k = ch; /* Need this for big-endianness */
+			} else {
+				k = getch();
+			}
+
+			/*
+			 * Check if we need to update the hint line whenever we
+			 * get a chance.
+			 * NOTE: current implementation depends on
+			 * g_config.lt_cfg_snap_interval, but it's OK because it
+			 * doesn't have to be precise.
+			 */
+			print_hint(NULL);
+			/*
+			 * If help is on display right now, and a key press
+			 * happens, we need to clear the help and continue.
+			 */
+			if (show_help) {
+				(void) werase(stdscr);
+				(void) refresh();
+				print_title();
+				print_sysglobal();
+				show_help = FALSE;
+				/* Drop this key and continue */
+				continue;
+			}
+
+			switch (k) {
+			case 'Q':
+			case 'q':
+				retval = 0;
+				goto quit;
+			case 'R':
+			case 'r':
+				lt_display_deinit();
+				lt_display_init();
+				goto quit;
+			case 'H':
+			case 'h':
+				show_help = TRUE;
+				(void) werase(stdscr);
+				(void) refresh();
+				print_help();
+				break;
+			case ',':
+			case '<':
+			case KEY_LEFT:
+				--list_index;
+
+				if (list_index < 0) {
+					list_index = 0;
+				}
+
+				break;
+			case '.':
+			case '>':
+			case KEY_RIGHT:
+				++list_index;
+
+				if (list_index >= list_len) {
+					list_index = list_len - 1;
+				}
+
+				break;
+			case 'a':
+			case 'A':
+				sort_type = LT_SORT_AVG;
+				print_sysglobal();
+				break;
+			case 'p':
+			case 'P':
+				sort_type = LT_SORT_TOTAL;
+				print_sysglobal();
+				break;
+			case 'm':
+			case 'M':
+				sort_type = LT_SORT_MAX;
+				print_sysglobal();
+				break;
+			case 'c':
+			case 'C':
+				sort_type = LT_SORT_COUNT;
+				print_sysglobal();
+				break;
+			case 't':
+			case 'T':
+				if (plist != NULL) {
+					selected_pid = plist[list_index];
+				}
+
+				selected_tid = INVALID_TID;
+				thread_mode = !thread_mode;
+				get_plist(&plist, &tlist,
+				    &list_len, &list_index);
+				break;
+			case '1':
+			case '!':
+				current_list_type = LT_LIST_CAUSE;
+				print_sysglobal();
+				break;
+			case '2':
+			case '@':
+				if (g_config.lt_cfg_low_overhead_mode) {
+					lt_display_error("Switching mode is "
+					    "not available for '-f low'.");
+				} else {
+					current_list_type = LT_LIST_SPECIALS;
+					print_sysglobal();
+				}
+
+				break;
+			case '3':
+			case '#':
+				if (g_config.lt_cfg_trace_syncobj) {
+					current_list_type = LT_LIST_SOBJ;
+					print_sysglobal();
+				} else if (g_config.lt_cfg_low_overhead_mode) {
+					lt_display_error("Switching mode is "
+					    "not available for '-f low'.");
+				} else {
+					lt_display_error("Tracing "
+					    "synchronization objects is "
+					    "disabled.");
+				}
+
+				break;
+			default:
+				/* Wake up for nothing; no refresh is needed */
+				need_refresh = FALSE;
+				break;
+			}
+		} else {
+			need_refresh = FALSE;
+		}
+	}
+
+quit:
+	if (plist != NULL) {
+		selected_pid = plist[list_index];
+	}
+
+	if (tlist != NULL) {
+		selected_tid = tlist[list_index];
+	}
+
+	lt_stat_proc_list_free(plist, tlist);
+
+	return (retval);
+}
+
+/*
+ * Clean up display.
+ */
+void
+lt_display_deinit(void)
+{
+	if (curses_inited) {
+		(void) clear();
+		(void) refresh();
+		(void) endwin();
+	}
+
+	titlebar = NULL;
+	captionbar = NULL;
+	sysglobal_window = NULL;
+	taskbar = NULL;
+	process_window = NULL;
+	hintbar = NULL;
+	screen_width = 1;
+	screen_height = 1;
+
+	display_initialized = FALSE;
+	curses_inited = FALSE;
+}
+
+/*
+ * Print message when display error happens.
+ */
+/* ARGSUSED */
+void
+lt_display_error(const char *fmt, ...)
+{
+	va_list vl;
+	char tmp[81];
+	int l;
+
+	va_start(vl, fmt);
+	(void) vsnprintf(tmp, sizeof (tmp), fmt, vl);
+	va_end(vl);
+
+	l = strlen(tmp);
+
+	while (l > 0 && (tmp[l - 1] == '\n' || tmp[l - 1] == '\r')) {
+		tmp[l - 1] = '\0';
+		--l;
+	}
+
+	if (!display_initialized) {
+		(void) fprintf(stderr, "%s\n", tmp);
+	} else if (!show_help) {
+		print_hint(tmp);
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/dwrapper.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,573 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <dtrace.h>
+#include <string.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <limits.h>
+
+#include "latencytop.h"
+
+static dtrace_hdl_t *g_dtp = NULL;	/* dtrace handle */
+static pid_t pid_self = -1;		/* PID of our own process */
+
+/*
+ * Ignore sched if sched is not tracked.
+ * Also ignore ourselves (i.e., latencytop).
+ */
+#define	SHOULD_IGNORE(pid)		\
+	((!g_config.lt_cfg_trace_sched && 0 == (pid)) || pid_self == (pid))
+
+/*
+ * Get an integer value from dtrace record.
+ */
+static uint64_t
+rec_get_value(void *a, size_t b)
+{
+	uint64_t ret = 0;
+
+	switch (b) {
+	case sizeof (uint64_t):
+		ret = *((uint64_t *)(a));
+		break;
+	case sizeof (uint32_t):
+		ret = *((uint32_t *)(a));
+		break;
+	case sizeof (uint16_t):
+		ret = *((uint16_t *)(a));
+		break;
+	case sizeof (uint8_t):
+		ret = *((uint8_t *)(a));
+		break;
+	default:
+		break;
+	}
+
+	return (ret);
+}
+
+/*
+ * Callback to process aggregation lt_call_* (related to on/off cpu
+ * activities) in the snapshot.
+ */
+static int
+aggwalk_call(const dtrace_aggdata_t *data, lt_stat_type_t stat_type)
+{
+	dtrace_aggdesc_t *aggdesc = data->dtada_desc;
+	dtrace_syminfo_t dts;
+	GElf_Sym sym;
+	caddr_t addr;
+	pid_t pid;
+	id_t tid;
+	unsigned int stack_depth;
+	unsigned int pc_size;
+	uint64_t pc;
+	uint64_t agg_value;
+	char *ptr = NULL;
+	char *buffer = NULL;
+	int ptrsize;
+	unsigned int buffersize;
+	char *tag = NULL;
+	unsigned int priority;
+	enum { REC_PID = 1, REC_TID, REC_STACK, REC_TAG, REC_PRIO, REC_AGG,
+	    NREC };
+
+	/* Check action type */
+	if ((aggdesc->dtagd_nrecs < NREC) ||
+	    (aggdesc->dtagd_rec[REC_PID].dtrd_action != DTRACEACT_DIFEXPR) ||
+	    (aggdesc->dtagd_rec[REC_TID].dtrd_action != DTRACEACT_DIFEXPR) ||
+	    (aggdesc->dtagd_rec[REC_TAG].dtrd_action != DTRACEACT_DIFEXPR) ||
+	    (aggdesc->dtagd_rec[REC_PRIO].dtrd_action != DTRACEACT_DIFEXPR) ||
+	    (!DTRACEACT_ISAGG(aggdesc->dtagd_rec[REC_AGG].dtrd_action)) ||
+	    (aggdesc->dtagd_rec[REC_STACK].dtrd_action != DTRACEACT_STACK)) {
+
+		return (-1);
+	}
+
+	pid = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_PID].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_PID].dtrd_size);
+
+	if (SHOULD_IGNORE(pid)) {
+		return (0);
+	}
+
+	tid = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_TID].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_TID].dtrd_size);
+
+	/* Parse stack array from dtagd_rec */
+	stack_depth = aggdesc->dtagd_rec[REC_STACK].dtrd_arg;
+	pc_size = aggdesc->dtagd_rec[REC_STACK].dtrd_size / stack_depth;
+	addr = data->dtada_data + aggdesc->dtagd_rec[REC_STACK].dtrd_offset;
+	buffersize = (stack_depth * (2 * PATH_MAX + 2) + 1) * sizeof (char);
+	buffer = (char *)lt_malloc(buffersize);
+	ptr = buffer;
+	ptrsize = buffersize;
+
+	/* Print the stack */
+	while (stack_depth > 0) {
+		pc = rec_get_value(addr, pc_size);
+
+		if (pc == 0) {
+			break;
+		}
+
+		addr += pc_size;
+
+		if (dtrace_lookup_by_addr(g_dtp, pc, &sym, &dts) == 0) {
+			int len;
+			len = snprintf(ptr, ptrsize,
+			    "%s`%s ", dts.dts_object, dts.dts_name);
+			ptrsize -= len;
+
+			if (ptrsize <= 0) {
+				/*
+				 * snprintf returns "desired" length, so
+				 * reaching here means our buffer is full.
+				 * Move ptr to the last byte of the buffer and
+				 * break.
+				 */
+				ptr = &buffer[buffersize-1];
+				break;
+			} else {
+				ptr += len;
+			}
+		}
+	}
+
+	if (ptr != buffer) {
+		/*
+		 * We have printed something, so it is safe to remove
+		 * the last ' '.
+		 */
+		*(ptr-1) = '\0';
+	}
+
+	tag = (char *)data->dtada_data +
+	    aggdesc->dtagd_rec[REC_TAG].dtrd_offset;
+
+	priority = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_PRIO].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_PRIO].dtrd_size);
+
+	agg_value = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_AGG].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_AGG].dtrd_size);
+
+	lt_stat_update(pid, tid, buffer, tag, priority, stat_type, agg_value);
+
+	if (buffer != NULL)  {
+		free(buffer);
+	}
+
+	return (0);
+}
+
+/*
+ * Callback to process aggregation lt_named_* (related to lock spinning etc.),
+ * in the snapshot.
+ */
+static int
+aggwalk_named(const dtrace_aggdata_t *data, lt_stat_type_t stat_type)
+{
+	dtrace_aggdesc_t *aggdesc = data->dtada_desc;
+	pid_t pid;
+	id_t tid;
+	uint64_t agg_value;
+	int cause_id;
+	char *type = NULL;
+	enum { REC_PID = 1, REC_TID, REC_TYPE, REC_AGG, NREC };
+
+	/* Check action type */
+	if ((aggdesc->dtagd_nrecs < NREC) ||
+	    (aggdesc->dtagd_rec[REC_PID].dtrd_action != DTRACEACT_DIFEXPR) ||
+	    (aggdesc->dtagd_rec[REC_TID].dtrd_action != DTRACEACT_DIFEXPR) ||
+	    (aggdesc->dtagd_rec[REC_TYPE].dtrd_action != DTRACEACT_DIFEXPR) ||
+	    (!DTRACEACT_ISAGG(aggdesc->dtagd_rec[REC_AGG].dtrd_action))) {
+
+		return (-1);
+	}
+
+	pid = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_PID].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_PID].dtrd_size);
+
+	if (SHOULD_IGNORE(pid)) {
+		return (0);
+	}
+
+	tid = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_TID].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_TID].dtrd_size);
+
+	type = (char *)data->dtada_data
+	    + aggdesc->dtagd_rec[REC_TYPE].dtrd_offset;
+	cause_id = lt_table_cause_from_name(type, 1, CAUSE_FLAG_SPECIAL);
+
+	agg_value = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_AGG].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_AGG].dtrd_size);
+
+	lt_stat_update_cause(pid, tid, cause_id, stat_type, agg_value);
+
+	return (0);
+
+}
+
+/*
+ * Callback to process aggregation lt_sync_* (related to synchronization
+ * objects), in the snapshot.
+ */
+static int
+aggwalk_sync(const dtrace_aggdata_t *data, lt_stat_type_t stat_type)
+{
+	dtrace_aggdesc_t *aggdesc = data->dtada_desc;
+	pid_t pid;
+	id_t tid;
+	uint64_t agg_value;
+	int stype;
+	unsigned long long wchan;
+	enum { REC_PID = 1, REC_TID, REC_STYPE, REC_WCHAN, REC_AGG, NREC };
+
+	/* Check action type */
+	if ((aggdesc->dtagd_nrecs < NREC) ||
+	    (aggdesc->dtagd_rec[REC_PID].dtrd_action != DTRACEACT_DIFEXPR) ||
+	    (aggdesc->dtagd_rec[REC_TID].dtrd_action != DTRACEACT_DIFEXPR) ||
+	    (aggdesc->dtagd_rec[REC_STYPE].dtrd_action != DTRACEACT_DIFEXPR) ||
+	    (aggdesc->dtagd_rec[REC_WCHAN].dtrd_action != DTRACEACT_DIFEXPR) ||
+	    (!DTRACEACT_ISAGG(aggdesc->dtagd_rec[REC_AGG].dtrd_action))) {
+
+		return (-1);
+	}
+
+	pid = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_PID].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_PID].dtrd_size);
+
+	if (SHOULD_IGNORE(pid)) {
+		return (0);
+	}
+
+	tid = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_TID].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_TID].dtrd_size);
+
+	stype = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_STYPE].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_STYPE].dtrd_size);
+
+	wchan = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_WCHAN].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_WCHAN].dtrd_size);
+
+	agg_value = rec_get_value(
+	    data->dtada_data + aggdesc->dtagd_rec[REC_AGG].dtrd_offset,
+	    aggdesc->dtagd_rec[REC_AGG].dtrd_size);
+
+	lt_stat_update_sobj(pid, tid, stype, wchan, stat_type, agg_value);
+
+	return (0);
+}
+
+/*
+ * Callback to process various aggregations in the snapshot. Called by
+ * different aggwalk_* functions.
+ */
+/* ARGSUSED */
+static int
+aggwalk(const dtrace_aggdata_t *data, void *arg)
+{
+	char *tmp;
+	char buffer[32];
+	lt_stat_type_t stat_type;
+	int (*func)(const dtrace_aggdata_t *, lt_stat_type_t);
+
+	(void) strncpy(buffer, data->dtada_desc->dtagd_name, sizeof (buffer));
+	buffer[sizeof (buffer) - 1] = '\0';
+	tmp = strtok(buffer, "_");
+
+	if (tmp == NULL || strcmp(tmp, "lt") != 0) {
+		goto done;
+	}
+
+	tmp = strtok(NULL, "_");
+
+	if (tmp == NULL) {
+		goto done;
+	} else if (strcmp(tmp, "call") == 0) {
+		func = aggwalk_call;
+	} else if (strcmp(tmp, "named") == 0) {
+		func = aggwalk_named;
+	} else if (strcmp(tmp, "sync") == 0) {
+		func = aggwalk_sync;
+	} else {
+		goto done;
+	}
+
+	tmp = strtok(NULL, "_");
+
+	if (tmp == NULL) {
+		goto done;
+	} else if (strcmp(tmp, "count") == 0) {
+		stat_type = LT_STAT_COUNT;
+	} else if (strcmp(tmp, "sum") == 0) {
+		stat_type = LT_STAT_SUM;
+	} else if (strcmp(tmp, "max") == 0) {
+		stat_type = LT_STAT_MAX;
+	} else {
+		goto done;
+	}
+
+	(void) func(data, stat_type);
+
+done:
+	/* We have our data, so remove it from DTrace now */
+	return (DTRACE_AGGWALK_REMOVE);
+}
+
+/*
+ * Callback to handle event caused by DTrace dropping data.
+ */
+/*ARGSUSED*/
+static int
+drop_handler(const dtrace_dropdata_t *data, void *user)
+{
+	lt_display_error("Drop: %s\n", data->dtdda_msg);
+
+	/* Pretend nothing happened, so just continue */
+	return (DTRACE_HANDLE_OK);
+}
+
+#ifndef EMBED_CONFIGS
+/*
+ * Copy the content from a "real" file into a temp file.
+ */
+static int
+copy_tmp_file(const char *src, FILE *dst)
+{
+	FILE *tmp = NULL;
+	char buffer[256];
+	int bytes;
+
+	if ((tmp = fopen(src, "r")) == NULL) {
+		return (-1);
+	}
+
+	while ((bytes = fread(buffer, 1, sizeof (buffer), tmp)) > 0) {
+		if (fwrite(buffer, bytes, 1, dst) != 1) {
+			return (-1);
+		}
+	}
+
+	(void) fclose(tmp);
+
+	return (0);
+}
+#endif
+
+/*
+ * DTrace initialization. D script starts running when this function returns.
+ */
+int
+lt_dtrace_init(void)
+{
+	dtrace_prog_t *prog;
+	dtrace_proginfo_t info;
+	int err;
+	FILE *fp_script = NULL;
+
+	pid_self = getpid();
+
+	if ((g_dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) {
+		lt_display_error("Cannot open dtrace library: %s\n",
+		    dtrace_errmsg(NULL, err));
+		return (-1);
+	}
+
+	if (dtrace_handle_drop(g_dtp, &drop_handler, NULL) == -1) {
+		lt_display_error("Cannot install DTrace handle: %s\n",
+		    dtrace_errmsg(NULL, err));
+		return (-1);
+	}
+
+	if (g_config.lt_cfg_enable_filter) {
+		if ((err = dtrace_setopt(g_dtp, "define",
+		    "ENABLE_FILTER")) != 0) {
+			lt_display_error(
+			    "Failed to set option ENABLE_FILTER.\n");
+			return (err);
+		}
+	}
+
+	if (g_config.lt_cfg_trace_syncobj) {
+		if ((err = dtrace_setopt(g_dtp, "define",
+		    "ENABLE_SYNCOBJ")) != 0) {
+			lt_display_error(
+			    "Failed to set option ENABLE_SYNCOBJ.\n");
+			return (err);
+		}
+	}
+
+	if (g_config.lt_cfg_trace_sched) {
+		if ((err = dtrace_setopt(g_dtp, "define",
+		    "ENABLE_SCHED")) != 0) {
+			lt_display_error(
+			    "Failed to set option ENABLE_SYNCOBJ.\n");
+			return (err);
+		}
+	}
+
+	if (g_config.lt_cfg_low_overhead_mode) {
+		if ((err = dtrace_setopt(g_dtp, "define",
+		    "ENABLE_LOW_OVERHEAD")) != 0) {
+			lt_display_error(
+			    "Failed to set option ENABLE_SYNCOBJ.\n");
+			return (err);
+		}
+	}
+
+	/* Create a temp file; libdtrace needs it for cpp(1) */
+	if ((fp_script = tmpfile()) == NULL) {
+		lt_display_error("Cannot create tmp file\n");
+		return (-1);
+	}
+
+	/* Copy the main D script into the temp file */
+#ifdef EMBED_CONFIGS
+	if (fwrite(&latencytop_d_start,
+	    (size_t)(&latencytop_d_end - &latencytop_d_start), 1, fp_script)
+	    != 1) {
+		lt_display_error("Could not copy D script, fwrite() failed\n");
+		(void) fclose(fp_script);
+		return (-1);
+	}
+#else
+	if (copy_tmp_file(DEFAULT_D_SCRIPT_NAME, fp_script) != 0) {
+		lt_display_error("Cannot open script file %s\n",
+		    DEFAULT_D_SCRIPT_NAME);
+		(void) fclose(fp_script);
+		return (-1);
+	}
+#endif	/* EMBED_CONFIGS */
+
+	if (lt_table_append_trans(fp_script) != 0) {
+		(void) fclose(fp_script);
+		return (-1);
+	}
+
+	(void) fseek(fp_script, 0, SEEK_SET);
+
+	if ((prog = dtrace_program_fcompile(g_dtp, fp_script,
+	    DTRACE_C_CPP, 0, NULL)) == NULL) {
+		lt_display_error("Failed to compile D script.\n");
+		(void) fclose(fp_script);
+		return (dtrace_errno(g_dtp));
+	}
+
+	(void) fclose(fp_script);
+
+	/* Execute the D script */
+	if (dtrace_program_exec(g_dtp, prog, &info) == -1) {
+		lt_display_error("Failed to enable probes.\n");
+		return (dtrace_errno(g_dtp));
+	}
+
+	if (dtrace_go(g_dtp) != 0) {
+		lt_display_error("Failed to run D script.\n");
+		return (dtrace_errno(g_dtp));
+	}
+
+	return (0);
+}
+
+/*
+ * Worker function to move aggregate data to user space. Called periodically
+ * to prevent the kernel from running out of memory.
+ */
+int
+lt_dtrace_work(int force)
+{
+	static uint64_t last_snap = 0;
+	uint64_t now = lt_millisecond();
+
+	if (!force && now - last_snap < g_config.lt_cfg_snap_interval) {
+		return (last_snap + g_config.lt_cfg_snap_interval - now);
+	}
+
+	if (dtrace_status(g_dtp) == -1) {
+		lt_display_error("Failed when getting status: %s\n",
+		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
+		return (-1);
+	}
+
+	if (dtrace_aggregate_snap(g_dtp) != 0) {
+		lt_display_error("Failed to snap aggregate: %s\n",
+		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
+		return (-1);
+	}
+
+	last_snap = now;
+	return (0);
+}
+
+/*
+ * Walk through dtrace aggregator and collect data for latencytop to display.
+ * Called immediately before UI update.
+ */
+int
+lt_dtrace_collect(void)
+{
+	if (lt_dtrace_work(1) != 0) {
+		return (-1);
+	}
+
+	if (dtrace_aggregate_walk(g_dtp, aggwalk, NULL) != 0) {
+		lt_display_error("Failed to sort aggregate: %s\n",
+		    dtrace_errmsg(g_dtp, dtrace_errno(g_dtp)));
+		return (-1);
+	}
+
+	/*
+	 * Probably we don't need to clear again, because we have removed
+	 * everything. Paranoid ?
+	 */
+	dtrace_aggregate_clear(g_dtp);
+
+	return (0);
+}
+
+/*
+ * dtrace clean up.
+ */
+void
+lt_dtrace_deinit(void)
+{
+	(void) dtrace_stop(g_dtp);
+	dtrace_close(g_dtp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/klog.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,226 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+#include <procfs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include "latencytop.h"
+
+static GHashTable *proc_table = NULL; /* pid -> char * */
+static GHashTable *klog_table = NULL; /* char * -> uint64_t total */
+static char klog_filename[PATH_MAX] = DEFAULT_KLOG_FILE;
+static int klog_level = LT_KLOG_LEVEL_NONE;
+
+static void
+print_proc(void *key, const char *args, FILE *fp)
+{
+	(void) fprintf(fp, "%-8ld \"%s\"\n", (long)key, args);
+}
+
+static void
+print_stat(const char *key, lt_stat_data_t *log, FILE *fp)
+{
+	(void) fprintf(fp, "%lld, %lld, %lld, %s\n",
+	    (long long)log->lt_s_total,
+	    (long long)log->lt_s_count,
+	    (long long)log->lt_s_max,
+	    key);
+}
+
+/*
+ * Initialization for kernel logging.
+ */
+void
+lt_klog_init(void)
+{
+	if (klog_table != NULL || proc_table != NULL) {
+		return;
+	}
+
+	klog_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+	    (GDestroyNotify)free, (GDestroyNotify)free);
+	lt_check_null(klog_table);
+
+	proc_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+	    NULL, (GDestroyNotify)free);
+	lt_check_null(proc_table);
+}
+
+/*
+ * Set log file path.
+ */
+int
+lt_klog_set_log_file(const char *filename)
+{
+	FILE *fp;
+	int file_exist;
+
+	g_assert(strlen(filename) < sizeof (klog_filename));
+
+	file_exist = lt_file_exist(filename);
+	/* Test if we can write to the file */
+	fp = fopen(filename, "a");
+
+	if (fp == NULL) {
+		return (-2);
+	}
+
+	(void) fclose(fp);
+	/* Don't leave empty file around */
+	if (!file_exist) {
+		(void) unlink(filename);
+	}
+
+	(void) strncpy(klog_filename, filename,
+	    sizeof (klog_filename));
+
+	return (0);
+}
+
+/*
+ * Set log level.
+ */
+int
+lt_klog_set_log_level(int level)
+{
+	if (level < 0 || level > (int)LT_KLOG_LEVEL_ALL) {
+		return (-1);
+	}
+
+	klog_level = level;
+
+	return (0);
+}
+
+/*
+ * Write content to log file.
+ */
+void
+lt_klog_write(void)
+{
+	FILE *fp;
+	char buffer[32];
+
+	if (klog_level == LT_KLOG_LEVEL_NONE) {
+		return;
+	}
+
+	g_assert(klog_table != NULL && proc_table != NULL);
+	fp = fopen(klog_filename, "a");
+
+	if (fp == NULL) {
+		return;
+	}
+
+	lt_time_str(buffer, sizeof (buffer));
+
+	(void) fprintf(fp, "# Log generated at %s by %s\n", buffer, TITLE);
+	(void) fprintf(fp, "# List of processes\n");
+	(void) fprintf(fp, "PID, CMD\n");
+	g_hash_table_foreach(proc_table, (GHFunc)print_proc, fp);
+
+	(void) fprintf(fp, "# Statistics\n");
+	(void) fprintf(fp, "TOTAL, COUNT, MAX, PID, KSTACK\n");
+	g_hash_table_foreach(klog_table, (GHFunc)print_stat, fp);
+
+	(void) fclose(fp);
+}
+
+/*
+ * Clean up. It flushes all log content in memory to log file.
+ */
+void
+lt_klog_deinit(void)
+{
+	if (klog_table != NULL) {
+		g_hash_table_destroy(klog_table);
+		klog_table = NULL;
+	}
+
+	if (proc_table != NULL) {
+		g_hash_table_destroy(proc_table);
+		proc_table = NULL;
+	}
+}
+
+/*
+ * Write a kernel stack and its statistics to log file. Only "total" will
+ * be logged, others are internally discarded.
+ */
+/* ARGSUSED */
+void
+lt_klog_log(int level, pid_t pid, char *stack,
+	lt_stat_type_t type, uint64_t value)
+{
+	lt_stat_data_t *entry = NULL;
+	char *psargs;
+	char *str;
+	int str_len;
+
+	if ((level & klog_level) == 0) {
+		return;
+	}
+
+	g_assert(klog_table != NULL && proc_table != NULL);
+	psargs = (char *)g_hash_table_lookup(proc_table,
+	    LT_INT_TO_POINTER(pid));
+
+	if (psargs == NULL) {
+		psargs = lt_get_proc_field(pid, LT_FIELD_PSARGS);
+
+		if (psargs == NULL) {
+			psargs = lt_get_proc_field(pid, LT_FIELD_FNAME);
+		}
+
+		if (psargs == NULL) {
+			return;
+		}
+
+		g_hash_table_insert(proc_table,
+		    LT_INT_TO_POINTER(pid), psargs);
+	}
+
+	str_len = strlen(stack) + 20;
+	str = lt_malloc(str_len);
+	(void) snprintf(str, str_len, "%ld, \"%s\"", pid, stack);
+	entry = (lt_stat_data_t *)g_hash_table_lookup(klog_table, str);
+
+	if (entry == NULL) {
+		entry = (lt_stat_data_t *)lt_zalloc(sizeof (lt_stat_data_t));
+		g_hash_table_insert(klog_table, str, entry);
+	} else {
+		free(str);
+	}
+
+	lt_update_stat_value(entry, type, value);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/latencytop.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,486 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <unistd.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <libgen.h>
+#include <signal.h>
+#include "latencytop.h"
+
+#define	CMPOPT(a, b)	strncmp((a), (b), sizeof (b))
+
+lt_config_t g_config;
+
+typedef enum {
+	LT_CMDOPT_INTERVAL,
+	LT_CMDOPT_LOG_FILE,
+	LT_CMDOPT_LOG_LEVEL,
+	LT_CMDOPT_LOG_INTERVAL,
+	LT_CMDOPT_CONFIG_FILE,
+	LT_CMDOPT_F_FILTER,
+	LT_CMDOPT_F_SCHED,
+	LT_CMDOPT_F_SOBJ,
+	LT_CMDOPT_F_LOW,
+	LT_CMDOPT__LAST	/* Must be last one */
+} lt_cmd_option_id_t;
+
+/*
+ * Check for duplicate command line options.
+ * Returns TRUE if duplicate options with different values are found,
+ * returns FALSE otherwise.
+ */
+static int
+check_opt_dup(lt_cmd_option_id_t id, uint64_t value) {
+
+	static int opt_set[(int)LT_CMDOPT__LAST];
+	static uint64_t opt_val[(int)LT_CMDOPT__LAST];
+
+	const char *errmsg[] = {
+		"-t is set more than once with different values.",
+		"-o is set more than once.",
+		"-k is set more than once with different values.",
+		"-l is set more than once with different values.",
+		"-c is set more than once.",
+		"-f [no]filter is set more than once with different values.",
+		"-f [no]sched is set more than once with different values.",
+		"-f [no]sobj is set more than once with different values.",
+		"-f [no]low is set more than once with different values.",
+	};
+
+	g_assert(sizeof (errmsg)/sizeof (errmsg[0]) == (int)LT_CMDOPT__LAST);
+
+	if (!opt_set[(int)id]) {
+		opt_set[(int)id] = TRUE;
+		opt_val[(int)id] = value;
+		return (FALSE);
+	}
+
+	if (opt_val[(int)id] != value) {
+		(void) fprintf(stderr, "%s\n", errmsg[(int)id]);
+		return (TRUE);
+	}
+
+	return (FALSE);
+}
+
+/*
+ * Print command-line help message.
+ */
+static void
+print_usage(const char *execname, int long_help)
+{
+	char buffer[PATH_MAX];
+	(void) snprintf(buffer, sizeof (buffer), "%s", execname);
+
+	if (!long_help) {
+		/* Print short help to stderr. */
+		(void) fprintf(stderr, "Usage: %s [option(s)], ",
+		    basename(buffer));
+		(void) fprintf(stderr, "use '%s -h' for details.\n",
+		    basename(buffer));
+		return;
+	}
+
+	(void) printf("Usage: %s [option(s)]\n", basename(buffer));
+	(void) printf("Options:\n"
+	    "    -h, --help\n"
+	    "        Print this help.\n"
+	    "    -t, --interval TIME\n"
+	    "        Set refresh interval to TIME. "
+	    "Valid range [1...60] seconds, default = 5\n"
+	/*
+	 * Option "-c, --config FILE" is not user-visible for now.
+	 * When we have chance to properly document the format of translation
+	 * rules, we'll make it user-visible.
+	 */
+	    "    -o, --output-log-file FILE\n"
+	    "        Output kernel log to FILE. Default = "
+	    DEFAULT_KLOG_FILE "\n"
+	    "    -k, --kernel-log-level LEVEL\n"
+	    "        Set kernel log level to LEVEL.\n"
+	    "        0(default) = None, 1 = Unmapped, 2 = Mapped, 3 = All.\n"
+	    "    -f, --feature [no]feature1,[no]feature2,...\n"
+	    "        Enable/disable features in LatencyTOP.\n"
+	    "        [no]filter:\n"
+	    "        Filter large interruptible latencies, e.g. sleep.\n"
+	    "        [no]sched:\n"
+	    "        Monitors sched (PID=0).\n"
+	    "        [no]sobj:\n"
+	    "        Monitors synchronization objects.\n"
+	    "        [no]low:\n"
+	    "        Lower overhead by sampling small latencies.\n"
+	    "    -l, --log-period TIME\n"
+	    "        Write and restart log every TIME seconds, TIME >= 60\n");
+}
+
+/*
+ * Properly exit latencytop when it receives SIGINT or SIGTERM.
+ */
+/* ARGSUSED */
+static void
+signal_handler(int sig)
+{
+	lt_gpipe_break("q");
+}
+
+/*
+ * Convert string to integer. It returns error if extra characters are found.
+ */
+static int
+to_int(const char *str, int *result)
+{
+	char *tail = NULL;
+	long ret;
+
+	if (str == NULL || result == NULL) {
+		return (-1);
+	}
+
+	ret = strtol(str, &tail, 10);
+
+	if (tail != NULL && *tail != '\0') {
+		return (-1);
+	}
+
+	*result = (int)ret;
+
+	return (0);
+}
+
+/*
+ * The main function.
+ */
+int
+main(int argc, char *argv[])
+{
+	const char *opt_string = "t:o:k:hf:l:c:";
+	struct option const longopts[] = {
+		{"interval", required_argument, NULL, 't'},
+		{"output-log-file", required_argument, NULL, 'o'},
+		{"kernel-log-level", required_argument, NULL, 'k'},
+		{"help", no_argument, NULL, 'h'},
+		{"feature", required_argument, NULL, 'f'},
+		{"log-period", required_argument, NULL, 'l'},
+		{"config", required_argument, NULL, 'c'},
+		{NULL, 0, NULL, 0}
+	};
+
+	int optc;
+	int longind = 0;
+	int running = 1;
+	int unknown_option = FALSE;
+	int refresh_interval = 5;
+	int klog_level = 0;
+	int log_interval = 0;
+	long long last_logged = 0;
+	char *token = NULL;
+	int retval = 0;
+	int gpipe;
+	int err;
+	uint64_t collect_end;
+	uint64_t current_time;
+	uint64_t delta_time;
+	char logfile[PATH_MAX] = "";
+
+	lt_gpipe_init();
+	(void) signal(SIGINT, signal_handler);
+	(void) signal(SIGTERM, signal_handler);
+
+	/* Default global settings */
+	g_config.lt_cfg_enable_filter = 0;
+	g_config.lt_cfg_trace_sched = 0;
+	g_config.lt_cfg_trace_syncobj = 1;
+	g_config.lt_cfg_low_overhead_mode = 0;
+	/* dtrace snapshot every 1 second */
+	g_config.lt_cfg_snap_interval = 1000;
+#ifdef EMBED_CONFIGS
+	g_config.lt_cfg_config_name = NULL;
+#else
+	g_config.lt_cfg_config_name = lt_strdup(DEFAULT_CONFIG_NAME);
+#endif
+
+	/* Parse command line arguments. */
+	while ((optc = getopt_long(argc, argv, opt_string,
+	    longopts, &longind)) != -1) {
+		switch (optc) {
+		case 'h':
+			print_usage(argv[0], TRUE);
+			goto end_none;
+		case 't':
+			if (to_int(optarg, &refresh_interval) != 0 ||
+			    refresh_interval < 1 || refresh_interval > 60) {
+				lt_display_error(
+				    "Invalid refresh interval: %s\n", optarg);
+				unknown_option = TRUE;
+			} else if (check_opt_dup(LT_CMDOPT_INTERVAL,
+			    refresh_interval)) {
+				unknown_option = TRUE;
+			}
+
+			break;
+		case 'k':
+			if (to_int(optarg, &klog_level) != 0 ||
+			    lt_klog_set_log_level(klog_level) != 0) {
+				lt_display_error(
+				    "Invalid log level: %s\n", optarg);
+				unknown_option = TRUE;
+			} else if (check_opt_dup(LT_CMDOPT_LOG_LEVEL,
+			    refresh_interval)) {
+				unknown_option = TRUE;
+			}
+
+			break;
+		case 'o':
+			if (check_opt_dup(LT_CMDOPT_LOG_FILE, optind)) {
+				unknown_option = TRUE;
+			} else if (strlen(optarg) >= sizeof (logfile)) {
+				lt_display_error(
+				    "Log file name is too long: %s\n",
+				    optarg);
+				unknown_option = TRUE;
+			} else {
+				(void) strncpy(logfile, optarg,
+				    sizeof (logfile));
+			}
+
+			break;
+		case 'f':
+			for (token = strtok(optarg, ","); token != NULL;
+			    token = strtok(NULL, ",")) {
+				int v = TRUE;
+
+				if (strncmp(token, "no", 2) == 0) {
+					v = FALSE;
+					token = &token[2];
+				}
+
+				if (CMPOPT(token, "filter") == 0) {
+					if (check_opt_dup(LT_CMDOPT_F_FILTER,
+					    v)) {
+						unknown_option = TRUE;
+					} else {
+						g_config.lt_cfg_enable_filter
+						    = v;
+					}
+				} else if (CMPOPT(token, "sched") == 0) {
+					if (check_opt_dup(LT_CMDOPT_F_SCHED,
+					    v)) {
+						unknown_option = TRUE;
+					} else {
+						g_config.lt_cfg_trace_sched
+						    = v;
+					}
+				} else if (CMPOPT(token, "sobj") == 0) {
+					if (check_opt_dup(LT_CMDOPT_F_SOBJ,
+					    v)) {
+						unknown_option = TRUE;
+					} else {
+						g_config.lt_cfg_trace_syncobj
+						    = v;
+					}
+				} else if (CMPOPT(token, "low") == 0) {
+					if (check_opt_dup(LT_CMDOPT_F_LOW,
+					    v)) {
+						unknown_option = TRUE;
+					} else {
+						g_config.
+						    lt_cfg_low_overhead_mode
+						    = v;
+					}
+				} else {
+					lt_display_error(
+					    "Unknown feature: %s\n", token);
+					unknown_option = TRUE;
+				}
+			}
+
+			break;
+		case 'l':
+			if (to_int(optarg, &log_interval) != 0 ||
+			    log_interval < 60) {
+				lt_display_error(
+				    "Invalid log interval: %s\n", optarg);
+				unknown_option = TRUE;
+			} else if (check_opt_dup(LT_CMDOPT_LOG_INTERVAL,
+			    log_interval)) {
+				unknown_option = TRUE;
+			}
+
+			break;
+		case 'c':
+			if (strlen(optarg) >= PATH_MAX) {
+				lt_display_error(
+				    "Configuration name is too long.\n");
+				unknown_option = TRUE;
+			} else if (check_opt_dup(LT_CMDOPT_CONFIG_FILE,
+			    optind)) {
+				unknown_option = TRUE;
+			} else {
+				g_config.lt_cfg_config_name =
+				    lt_strdup(optarg);
+			}
+
+			break;
+		default:
+			unknown_option = TRUE;
+			break;
+		}
+	}
+
+	if (!unknown_option && strlen(logfile) > 0) {
+		err = lt_klog_set_log_file(logfile);
+
+		if (err == -1) {
+			lt_display_error("Log file name is too long: %s\n",
+			    logfile);
+			unknown_option = TRUE;
+		} else if (err == -2) {
+			lt_display_error("Cannot write to log file: %s\n",
+			    logfile);
+			unknown_option = TRUE;
+		}
+	}
+
+	/* Throw error for invalid/junk arguments */
+	if (optind  < argc) {
+		int tmpind = optind;
+		(void) fprintf(stderr, "Unknown option(s): ");
+
+		while (tmpind < argc) {
+			(void) fprintf(stderr, "%s ", argv[tmpind++]);
+		}
+
+		(void) fprintf(stderr, "\n");
+		unknown_option = TRUE;
+	}
+
+	if (unknown_option) {
+		print_usage(argv[0], FALSE);
+		retval = 1;
+		goto end_none;
+	}
+
+	(void) printf("%s\n%s\n", TITLE, COPYRIGHT);
+
+	/*
+	 * Initialization
+	 */
+	lt_klog_init();
+
+	if (lt_table_init() != 0) {
+		lt_display_error("Unable to load configuration table.\n");
+		retval = 1;
+		goto end_notable;
+	}
+
+	if (lt_dtrace_init() != 0) {
+		lt_display_error("Unable to initialize dtrace.\n");
+		retval = 1;
+		goto end_nodtrace;
+	}
+
+	last_logged = lt_millisecond();
+
+	(void) printf("Collecting data for %d seconds...\n",
+	    refresh_interval);
+
+	gpipe = lt_gpipe_readfd();
+	collect_end = last_logged + refresh_interval * 1000;
+	for (;;) {
+		fd_set read_fd;
+		struct timeval timeout;
+		int tsleep = collect_end - lt_millisecond();
+
+		if (tsleep <= 0) {
+			break;
+		}
+
+		if (tsleep > g_config.lt_cfg_snap_interval * 1000) {
+			tsleep = g_config.lt_cfg_snap_interval * 1000;
+		}
+
+		timeout.tv_sec = tsleep / 1000;
+		timeout.tv_usec = (tsleep % 1000) * 1000;
+
+		FD_ZERO(&read_fd);
+		FD_SET(gpipe, &read_fd);
+
+		if (select(gpipe + 1, &read_fd, NULL, NULL, &timeout) > 0) {
+			goto end_ubreak;
+		}
+
+		(void) lt_dtrace_work(0);
+	}
+
+	lt_display_init();
+
+	do {
+		current_time = lt_millisecond();
+
+		lt_stat_clear_all();
+		(void) lt_dtrace_collect();
+
+		delta_time = current_time;
+		current_time = lt_millisecond();
+		delta_time = current_time - delta_time;
+
+		if (log_interval > 0 &&
+		    current_time - last_logged > log_interval * 1000) {
+			lt_klog_write();
+			last_logged = current_time;
+		}
+
+		running = lt_display_loop(refresh_interval * 1000 -
+		    delta_time);
+	} while (running != 0);
+
+	lt_klog_write();
+
+	/* Cleanup */
+	lt_display_deinit();
+
+end_ubreak:
+	lt_dtrace_deinit();
+	lt_stat_free_all();
+
+end_nodtrace:
+	lt_table_deinit();
+
+end_notable:
+	lt_klog_deinit();
+
+end_none:
+	lt_gpipe_deinit();
+
+	if (g_config.lt_cfg_config_name != NULL) {
+		free(g_config.lt_cfg_config_name);
+	}
+
+	return (retval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/latencytop.d	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,404 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#define	MAX_TAG		8
+#define	MAX_STACK	64
+
+#pragma D option aggsize=8m
+#pragma D option bufsize=16m
+#pragma D option dynvarsize=16m
+#pragma D option aggrate=0
+#pragma D option stackframes=MAX_STACK
+/*
+ * Our D script needs to compile even if some of the TRANSLATE probes cannot
+ * be found. Missing probes can be caused by older kernel, different
+ * architecture, unloaded modules etc.
+ */
+#pragma D option zdefs
+
+#if defined(ENABLE_SCHED)
+#define TRACE_FILTER
+#define TRACE_FILTER_COND(a)	/ (a) /
+#else
+#define TRACE_FILTER	/ pid != 0 /
+#define TRACE_FILTER_COND(a)	/ pid != 0 && (a) /
+#endif
+
+/* Threshold to filter WAKEABLE latencies. */
+#define FILTER_THRESHOLD	5000000
+/* From thread.h */
+#define T_WAKEABLE		2
+
+/*
+ * This array is used to store timestamp of when threads are enqueued
+ * to dispatch queue.
+ * self-> is not accessible when enqueue happens.
+ */
+unsigned long long lt_timestamps[int, int];
+
+self unsigned int lt_is_block_wakeable;
+self unsigned long long lt_sleep_start;
+self unsigned long long lt_sleep_duration;
+self unsigned long long lt_sch_delay;
+self unsigned int lt_counter;		/* only used in low overhead */
+self unsigned long long lt_timestamp;	/* only used in low overhead */
+self unsigned int lt_stackp;
+self unsigned int lt_prio[int];
+self string lt_cause[int];
+
+this unsigned int priority;
+this string cause;
+
+/*
+ * Clean up everything, otherwise we will run out of memory.
+ */
+proc:::lwp-exit
+{
+	lt_timestamps[curpsinfo->pr_pid, curlwpsinfo->pr_lwpid] = 0;
+
+	self->lt_sleep_start = 0;
+	self->lt_is_block_wakeable = 0;
+	self->lt_counter = 0;
+	self->lt_timestamp = 0;
+
+	/*
+	 * Workaround: no way to clear associative array.
+	 * We have to manually clear 0 ~ (MAX_TAG-1).
+	 */
+
+	self->lt_prio[0] = 0;
+	self->lt_prio[1] = 0;
+	self->lt_prio[2] = 0;
+	self->lt_prio[3] = 0;
+	self->lt_prio[4] = 0;
+	self->lt_prio[5] = 0;
+	self->lt_prio[6] = 0;
+	self->lt_prio[7] = 0;
+
+	self->lt_cause[0] = 0;
+	self->lt_cause[1] = 0;
+	self->lt_cause[2] = 0;
+	self->lt_cause[3] = 0;
+	self->lt_cause[4] = 0;
+	self->lt_cause[5] = 0;
+	self->lt_cause[6] = 0;
+	self->lt_cause[7] = 0;
+}
+
+#if !defined(ENABLE_LOW_OVERHEAD)
+/*
+ * Log timestamp when a thread is taken off the CPU.
+ */
+sched::resume:off-cpu
+TRACE_FILTER_COND(curlwpsinfo->pr_state == SSLEEP)
+{
+	self->lt_sleep_start = timestamp;
+	self->lt_is_block_wakeable = curthread->t_flag & T_WAKEABLE;
+
+	lt_timestamps[curpsinfo->pr_pid, curlwpsinfo->pr_lwpid] =
+	    self->lt_sleep_start;
+}
+
+/*
+ * Log timestamp when a thread is put on a dispatch queue and becomes runnable.
+ */
+sched:::enqueue
+/lt_timestamps[args[1]->pr_pid, args[0]->pr_lwpid] != 0/
+{
+	lt_timestamps[args[1]->pr_pid, args[0]->pr_lwpid] = timestamp;
+}
+
+/*
+ * Calculate latency when the thread is actually on the CPU.
+ * This is necessary in order to get the right stack.
+ */
+this unsigned long long end;
+this unsigned long long now;
+sched::resume:on-cpu
+/self->lt_sleep_start != 0/
+{
+	this->end = lt_timestamps[curpsinfo->pr_pid, curlwpsinfo->pr_lwpid];
+	this->now = timestamp;
+	lt_timestamps[curpsinfo->pr_pid, curlwpsinfo->pr_lwpid] = 0;
+	this->end = (this->end != 0 && this->end != self->lt_sleep_start)
+	    ? this->end : this->now;
+
+	self->lt_sch_delay = this->now - this->end;
+	self->lt_sleep_duration = this->end - self->lt_sleep_start;
+	self->lt_sleep_start = 0;
+}
+
+/*
+ * Filter: drop all "large" latency when it is interruptible, i.e., sleep()
+ * etc.
+ */
+#if defined(ENABLE_FILTER)
+sched::resume:on-cpu
+/self->lt_sleep_duration > FILTER_THRESHOLD &&
+  self->lt_is_block_wakeable != 0/
+{
+	self->lt_sch_delay = 0;
+	self->lt_sleep_duration = 0;
+	self->lt_is_block_wakeable = 0;
+}
+#endif /* defined(ENABLE_FILTER) */
+
+/*
+ * Write sleep time to the aggregation.
+ * lt_sleep_duration is the duration between the time when a thread is taken
+ * off the CPU and the time when it is enqueued again.
+ */
+sched::resume:on-cpu
+/self->lt_sleep_duration != 0/
+{
+	this->cause = self->lt_stackp > 0 ?
+	    self->lt_cause[self->lt_stackp - 1] : "";
+	this->priority = self->lt_stackp > 0 ?
+	    self->lt_prio[self->lt_stackp - 1] : 0;
+
+	@lt_call_count[pid, tid, stack(), this->cause,
+	    this->priority] = count();
+	@lt_call_sum[pid, tid, stack(), this->cause,
+	    this->priority] = sum(self->lt_sleep_duration);
+	@lt_call_max[pid, tid, stack(),  this->cause,
+	    this->priority] = max(self->lt_sleep_duration);
+
+	self->lt_is_block_wakeable = 0;	/* Clear the flag to avoid leak */
+	self->lt_sleep_duration = 0;
+}
+
+/*
+ * Write time spent in queue to the aggregation.
+ * lt_sch_delay is the interval between the time when a thread becomes
+ * runnable and the time when it is actually on the CPU.
+ */
+sched::resume:on-cpu
+/self->lt_sch_delay != 0/
+{
+	@lt_named_count[pid, tid, "Wait for available CPU"] = count();
+	@lt_named_sum[pid, tid, "Wait for available CPU"] =
+	    sum(self->lt_sch_delay);
+	@lt_named_max[pid, tid, "Wait for available CPU"] =
+	    max(self->lt_sch_delay);
+
+	self->lt_sch_delay = 0;
+}
+
+/*
+ * Probes to track latency caused by spinning on a lock.
+ */
+lockstat:::adaptive-spin
+TRACE_FILTER
+{
+	@lt_named_count[pid, tid, "Adapt. lock spin"] = count();
+	@lt_named_sum[pid, tid, "Adapt. lock spin"] = sum(arg1);
+	@lt_named_max[pid, tid, "Adapt. lock spin"] = max(arg1);
+}
+
+lockstat:::spin-spin
+TRACE_FILTER
+{
+	@lt_named_count[pid, tid, "Spinlock spin"] = count();
+	@lt_named_sum[pid, tid, "Spinlock spin"] = sum(arg1);
+	@lt_named_max[pid, tid, "Spinlock spin"] = max(arg1);
+}
+
+/*
+ * Probes to track latency caused by blocking on a lock.
+ */
+lockstat:::adaptive-block
+TRACE_FILTER
+{
+	@lt_named_count[pid, tid, "#Adapt. lock block"] = count();
+	@lt_named_sum[pid, tid, "#Adapt. lock block"] = sum(arg1);
+	@lt_named_max[pid, tid, "#Adapt. lock block"] = max(arg1);
+}
+
+lockstat:::rw-block
+TRACE_FILTER
+{
+	@lt_named_count[pid, tid, "#RW. lock block"] = count();
+	@lt_named_sum[pid, tid, "#RW. lock block"] = sum(arg1);
+	@lt_named_max[pid, tid, "#RW. lock block"] = max(arg1);
+}
+
+#if defined(ENABLE_SYNCOBJ)
+/*
+ * Probes to track latency caused by synchronization objects.
+ */
+this int stype;
+this unsigned long long wchan;
+this unsigned long long wtime;
+
+sched:::wakeup
+/*
+ * Currently we are unable to track wakeup from sched, because all its LWP IDs
+ * are zero when we trace it and that makes lt_timestamps unusable.
+ */
+/args[1]->pr_pid != 0 &&
+    lt_timestamps[args[1]->pr_pid, args[0]->pr_lwpid] != 0/
+{
+	this->stype = args[0]->pr_stype;
+	this->wchan = args[0]->pr_wchan;
+	/*
+	 * We can use lt_timestamps[] here, because
+	 * wakeup is always fired before enqueue.
+	 * After enqueue, lt_timestamps[] will be overwritten.
+	 */
+	this->wtime = timestamp - lt_timestamps[args[1]->pr_pid,
+	    args[0]->pr_lwpid];
+
+	@lt_sync_count[args[1]->pr_pid, args[0]->pr_lwpid, this->stype,
+	    this->wchan] = count();
+	@lt_sync_sum[args[1]->pr_pid, args[0]->pr_lwpid, this->stype,
+	    this->wchan] = sum(this->wtime);
+	@lt_sync_max[args[1]->pr_pid, args[0]->pr_lwpid, this->stype,
+	    this->wchan] = max(this->wtime);
+}
+#endif /* defined(ENABLE_SYNCOBJ) */
+
+#else /* !defined(ENABLE_LOW_OVERHEAD) */
+
+/*
+ * This is the low overhead mode.
+ * In order to reduce the number of instructions executed during each
+ * off-cpu and on-cpu event, we do the following:
+ *
+ *	1. Use sampling and update aggregations only roughly 1/100 times
+ *		(SAMPLE_TIMES).
+ *	2. Do not track anything other than what is needed for "main" window.
+ *	3. Use as few thread local variables as possible.
+ */
+
+#define SAMPLE_TIMES		100
+#define SAMPLE_THRESHOLD	50000000
+
+/*
+ * Log timestamp when a thread is off CPU.
+ */
+sched::resume:off-cpu
+TRACE_FILTER_COND(curlwpsinfo->pr_state == SSLEEP)
+{
+	self->lt_timestamp = timestamp;
+#if defined(ENABLE_FILTER)
+	self->lt_is_block_wakeable = curthread->t_flag & T_WAKEABLE;
+#endif /* defined(ENABLE_FILTER) */
+}
+
+/*
+ * Calculate latency when a thread is actually on the CPU.
+ */
+this int need_skip;
+sched::resume:on-cpu
+/self->lt_timestamp != 0/
+{
+	self->lt_timestamp = timestamp - self->lt_timestamp;
+
+#if defined(ENABLE_FILTER)
+	self->lt_timestamp =
+	    (self->lt_timestamp > FILTER_THRESHOLD &&
+	    self->lt_is_block_wakeable != 0) ? 0 : self->lt_timestamp;
+	self->lt_is_block_wakeable = 0;
+#endif /* defined(ENABLE_FILTER) */
+
+	this->need_skip = (self->lt_counter < (SAMPLE_TIMES - 1) &&
+	    self->lt_timestamp <= SAMPLE_THRESHOLD) ? 1 : 0;
+	self->lt_timestamp = this->need_skip ? 0 : self->lt_timestamp;
+	self->lt_counter += this->need_skip;
+}
+
+/*
+ * Track large latency first.
+ */
+sched::resume:on-cpu
+/self->lt_timestamp > SAMPLE_THRESHOLD/
+{
+	this->cause = self->lt_stackp > 0 ?
+	    self->lt_cause[self->lt_stackp - 1] : "";
+	this->priority = self->lt_stackp > 0 ?
+	    self->lt_prio[self->lt_stackp - 1] : 0;
+
+	@lt_call_count[pid, tid, stack(), this->cause,
+	    this->priority] = sum(1);
+	@lt_call_sum[pid, tid, stack(), this->cause,
+	    this->priority] = sum(self->lt_timestamp);
+	@lt_call_max[pid, tid, stack(), this->cause,
+	    this->priority] = max(self->lt_timestamp);
+
+	self->lt_timestamp = 0;
+}
+
+/*
+ * If we fall back to this probe, that means the latency is small and counter
+ * has reached SAMPLE_TIMES.
+ */
+sched::resume:on-cpu
+/self->lt_timestamp != 0/
+{
+	this->cause = self->lt_stackp > 0 ?
+	    self->lt_cause[self->lt_stackp - 1] : "";
+	this->priority = self->lt_stackp > 0 ?
+	    self->lt_prio[self->lt_stackp - 1] : 0;
+
+	/* Need +1 because lt_counter has not been updated in this cycle. */
+	@lt_call_count[pid, tid, stack(), this->cause,
+	    this->priority] = sum(self->lt_counter + 1);
+	@lt_call_sum[pid, tid, stack(), this->cause,
+	    this->priority] = sum((self->lt_counter + 1) * self->lt_timestamp);
+	@lt_call_max[pid, tid, stack(), this->cause,
+	    this->priority] = max(self->lt_timestamp);
+
+	self->lt_timestamp = 0;
+	self->lt_counter = 0;
+}
+
+#endif /* !defined(ENABLE_LOW_OVERHEAD) */
+
+#define	TRANSLATE(entryprobe, returnprobe, cause, priority)		\
+entryprobe								\
+TRACE_FILTER_COND(self->lt_stackp == 0 ||				\
+    (self->lt_stackp < MAX_TAG &&					\
+    self->lt_prio[self->lt_stackp - 1] <= priority) )			\
+{									\
+	self->lt_prio[self->lt_stackp] = priority;			\
+	self->lt_cause[self->lt_stackp] = cause;			\
+	++self->lt_stackp;						\
+}									\
+returnprobe								\
+TRACE_FILTER_COND(self->lt_stackp > 0 &&				\
+    self->lt_cause[self->lt_stackp - 1] == cause)			\
+{									\
+	--self->lt_stackp;						\
+	self->lt_cause[self->lt_stackp] = NULL;				\
+}
+
+/*
+ * Syscalls have a priority of 10. This is to make sure that latency is
+ * traced to one of the syscalls only if nothing else matches.
+ * We put this special probe here because it uses "probefunc" variable instead
+ * of a constant string.
+ */
+
+TRANSLATE(syscall:::entry, syscall:::return, probefunc, 10)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/latencytop.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,269 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#ifndef _LATENCYTOP_H
+#define	_LATENCYTOP_H
+
+#include <sys/types.h>
+
+#include <glib.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Without this lint seems to be confused by glib header file.
+ */
+#ifdef __lint
+#undef g_assert
+#define	g_assert(x)	((void)(x))
+#undef TRUE
+#define	TRUE		1
+#endif
+
+/*
+ * We define our own conversions in order to avoid compiler warnings.
+ */
+#define	LT_INT_TO_POINTER(a)	((void *)(unsigned long)(a))
+
+#define	TITLE			"LatencyTOP for OpenSolaris, version 1.0"
+#define	COPYRIGHT		"Copyright (c) 2008-2009, Intel Corporation."
+#define	DEFAULT_KLOG_FILE	"/var/log/latencytop.log"
+
+#define	INVALID_PID		(~0)
+#define	INVALID_TID		(~0)
+#define	PID_SYS_GLOBAL		INVALID_PID
+#define	INVALID_CAUSE		0
+#define	HIGHER_PRIORITY(a, b)	((a) > (b))
+
+#ifdef EMBED_CONFIGS
+/*
+ * LatencyTOP configuration is embedded in the binary.
+ * Array will be generated by elfwrap.
+ */
+extern char latencytop_d_start;
+extern char latencytop_d_end;
+extern char latencytop_trans_start;
+extern char latencytop_trans_end;
+#else
+/*
+ * LatencyTOP configuration is provided externally by user.
+ */
+#define	DEFAULT_CONFIG_NAME	"./latencytop.trans"
+#define	DEFAULT_D_SCRIPT_NAME   "./latencytop.d"
+#endif
+
+typedef enum {
+	LT_STAT_COUNT,
+	LT_STAT_MAX,
+	LT_STAT_SUM,
+} lt_stat_type_t;
+
+#define	LT_KLOG_LEVEL_NONE	0	/* Log nothing */
+#define	LT_KLOG_LEVEL_UNMAPPED	1	/* Log only stacks not mapped */
+#define	LT_KLOG_LEVEL_MAPPED	2	/* Log only stacks mapped */
+#define	LT_KLOG_LEVEL_ALL	3	/* Log all stacks, mapped or not */
+
+typedef enum {
+	LT_LEVEL_GLOBAL,	/* System wide statistics */
+	LT_LEVEL_PROCESS,	/* Per-process statistics */
+	LT_LEVEL_THREAD,	/* Per-thread statistics */
+} lt_stat_level_t;
+
+typedef enum {
+	LT_SORT_TOTAL,
+	LT_SORT_MAX,
+	LT_SORT_AVG,
+	LT_SORT_COUNT,
+} lt_sort_t;
+
+typedef enum {
+	LT_FIELD_FNAME,
+	LT_FIELD_PSARGS,
+} lt_field_t;
+
+typedef enum {
+	LT_LIST_CAUSE,		/* List latency by causes (default) */
+	LT_LIST_SPECIALS,	/* List only "special" causes */
+	LT_LIST_SOBJ		/* List synchronization objects */
+} lt_list_type_t;
+
+/*
+ * Data structure which contains statistics.
+ */
+typedef struct {
+	uint64_t lt_s_count;
+	uint64_t lt_s_total;
+	uint64_t lt_s_max;
+} lt_stat_data_t;
+
+/*
+ * Data structure that stores statistics along with the name.
+ */
+typedef struct {
+	enum {
+		STAT_CAUSE,
+		STAT_SOBJ
+	} lt_se_type;
+	const char *lt_se_string;
+	lt_stat_data_t lt_se_data;
+	union {
+		struct {
+			int lt_se_c_id;
+			int lt_se_c_flags;
+		} lt_se_t_cause;
+		struct {
+			int lt_se_s_id;
+		} lt_se_t_sobj;
+	} lt_se_tsdata;	/* type specific data */
+} lt_stat_entry_t;
+
+typedef struct {
+	int lt_cfg_enable_filter;
+	int lt_cfg_trace_sched;
+	int lt_cfg_trace_syncobj;
+	int lt_cfg_low_overhead_mode;
+	int lt_cfg_snap_interval;
+	char *lt_cfg_config_name;
+} lt_config_t;
+
+extern lt_config_t g_config;	/* The global settings */
+
+/*
+ * Causes can be disabled through the configuration file.
+ * When disabled, though D script will continue to capture causes, they will
+ * not be counted by LatencyTOP.
+ */
+#define	CAUSE_FLAG_DISABLED		1
+/*
+ * This flag will not show and count causes as part of summary in
+ * "kstack window".
+ */
+#define	CAUSE_FLAG_HIDE_IN_SUMMARY	2
+/*
+ * This is generated from D script (named cause), and is "special".
+ */
+#define	CAUSE_FLAG_SPECIAL		4
+#define	CAUSE_ALL_FLAGS			0xffffffff
+
+/*
+ * These functions collect statistics using DTrace.
+ */
+extern int lt_dtrace_init(void);
+extern int lt_dtrace_work(int);
+extern int lt_dtrace_collect(void);
+extern void lt_dtrace_deinit(void);
+
+/*
+ * These functions maintain configuration, e.g. symbol to cause mapping.
+ */
+extern int lt_table_init(void);
+extern int lt_table_cause_from_stack(const char *, int *, int *);
+extern const char *lt_table_get_cause_name(int);
+extern int lt_table_get_cause_flag(int, int);
+extern int lt_table_cause_from_name(char *, int, int);
+extern int lt_table_append_trans(FILE *fp);
+extern void lt_table_deinit(void);
+
+/*
+ * These functions update statistic of all causes of latency, collected
+ * from DTrace.
+ */
+extern void lt_stat_update(pid_t, id_t, char *, char *, unsigned int,
+    lt_stat_type_t, uint64_t);
+extern void lt_stat_update_cause(pid_t, id_t, int, lt_stat_type_t, uint64_t);
+extern void lt_stat_update_sobj(pid_t, id_t, int, unsigned long long,
+    lt_stat_type_t, uint64_t);
+extern void lt_stat_clear_all(void);
+extern void lt_stat_free_all(void);
+
+/*
+ * These functions produce lists for display panes.
+ * Note: after a call to lt_stat_update_*, the old lists will become invalid.
+ */
+extern void *lt_stat_list_create(lt_list_type_t, lt_stat_level_t,
+    pid_t, id_t, int, lt_sort_t);
+extern int lt_stat_list_has_item(void *, int);
+extern const char *lt_stat_list_get_reason(void *, int);
+extern uint64_t lt_stat_list_get_max(void *, int);
+extern uint64_t lt_stat_list_get_sum(void *, int);
+extern uint64_t lt_stat_list_get_count(void *, int);
+extern uint64_t lt_stat_list_get_gtotal(void *);
+extern void lt_stat_list_free(void *);
+
+/*
+ * These functions produce the process list and the thread list.
+ */
+extern int lt_stat_proc_list_create(pid_t **, id_t **);
+extern void lt_stat_proc_list_free(pid_t *, id_t *);
+extern const char *lt_stat_proc_get_name(pid_t);
+extern int lt_stat_proc_get_nthreads(pid_t);
+
+/*
+ * These functions use ncurses to create console-based display.
+ */
+extern void lt_display_init(void);
+extern int lt_display_loop(int);
+extern void lt_display_error(const char *, ...);
+extern void lt_display_deinit(void);
+
+/*
+ * Write statistics to log file - useful for debugging and offline analysis.
+ */
+extern void lt_klog_init(void);
+extern void lt_klog_deinit(void);
+extern int lt_klog_set_log_file(const char *);
+extern int lt_klog_set_log_level(int);
+extern void lt_klog_write(void);
+extern void lt_klog_log(int, pid_t, char *, lt_stat_type_t,
+    uint64_t);
+
+/*
+ * Utility functions.
+ */
+extern uint64_t lt_millisecond(void);
+extern void *lt_malloc(size_t);
+extern void *lt_zalloc(size_t);
+extern char *lt_strdup(const char *);
+extern void lt_check_null(void *);
+extern void lt_time_str(char *, int);
+extern char *lt_get_proc_field(pid_t, lt_field_t);
+extern void lt_update_stat_value(lt_stat_data_t *, lt_stat_type_t, uint64_t);
+extern int lt_sort_by_total_desc(lt_stat_entry_t *, lt_stat_entry_t *);
+extern int lt_sort_by_max_desc(lt_stat_entry_t *, lt_stat_entry_t *);
+extern int lt_sort_by_count_desc(lt_stat_entry_t *, lt_stat_entry_t *);
+extern int lt_sort_by_avg_desc(lt_stat_entry_t *, lt_stat_entry_t *);
+extern void lt_gpipe_init(void);
+extern void lt_gpipe_deinit(void);
+extern void lt_gpipe_break(const char *);
+extern int lt_gpipe_readfd(void);
+extern int lt_file_exist(const char *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LATENCYTOP_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/latencytop.trans	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+# LatencyTOP 1.0 configuration
+#
+
+#
+# Format:
+# D <priority>    <probe 1>     <probe 2>  ...   <probe n>     <Cause>
+# ; <special_command>  <option value>
+# # comments
+#
+# D: We use dtrace probes to tag latency to different causes. The entry probe
+# pushes the cause and the priority to a thread local stack, and the return
+# probe pops them. These probes are transformed into:
+#     TRANSLATE(entryprobe, returnprobe, cause, priority)
+# and are append to the embedded D script.
+#
+# <special_command>:
+# disable_category <category name> : do not count and display <category name>
+#
+
+# ZFS
+D 60    sdt:zfs:zil_commit_writer:zil-cw1  sdt:zfs:zil_commit_writer:zil-cw4  ZFS ZIL writer I/O
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/stat.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,1050 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/stat.h>
+
+#include "latencytop.h"
+
+/* Statistics for each process/thread. */
+typedef struct _lt_stat_collection lt_stat_collection_t;
+typedef gboolean (*check_child_func_t) (gpointer key,
+    lt_stat_collection_t *stat, void *user);
+
+typedef struct {
+	lt_stat_entry_t lt_grp_summary;
+	/* cause_id -> stat entry */
+	GHashTable *lt_grp_cidlist;
+} lt_datagroup_t;
+
+#define	NGROUPS			2
+#define	GROUP_CAUSE		0
+#define	GROUP_SOBJ		1
+
+/*
+ * A data collection hierarchy involving three entities - system, process
+ * and thread. The hierarchic relationship is as follows :
+ *
+ *		1 system -> 1 or more processes -> 1 or more threads
+ */
+struct _lt_stat_collection {
+	lt_stat_level_t lt_sc_level;
+	unsigned int lt_sc_id;
+	char *lt_sc_name;
+	lt_datagroup_t lt_sc_groups[NGROUPS];
+	/*
+	 * The following fields: lt_sc_parent, lt_sc_children and
+	 * lt_sc_check_child_func maintain the tree structure.
+	 */
+	lt_stat_collection_t *lt_sc_parent;		/* Parent node */
+	GHashTable *lt_sc_children;	/* pid/tid -> lt_stat_collection_t */
+	check_child_func_t lt_sc_check_child_func; /* Release dead children */
+};
+
+/* Internal data structure to back up a stat_list */
+typedef struct _lt_stat_list lt_stat_list_t;
+typedef void (*free_list_func_t)(lt_stat_list_t *);
+struct _lt_stat_list {
+	int lt_sl_entry_count;
+	lt_stat_entry_t **lt_sl_entries;
+	uint64_t lt_sl_gtotal;
+	free_list_func_t lt_sl_free_func;
+};
+
+/* Root of the collection hierarchy: system level statistics */
+static lt_stat_collection_t *stat_system = NULL;
+
+/*
+ * Data structure to hold synchronization objects.
+ * We don't use normal "cause table" because this needs to be cleared
+ * every time we refresh in order to make sure that stale synchronization
+ * objects don't consume memory.
+ */
+typedef struct {
+	int lt_soi_type;
+	unsigned long long lt_soi_addr;
+} lt_sobj_id_t;
+
+typedef struct {
+	lt_sobj_id_t lt_so_oid;
+	int lt_so_cause_id;
+	char lt_so_string[32];	/* Enough to hold "%s: 0x%llX" */
+} lt_sobj_t;
+
+static GHashTable *sobj_table = NULL;
+static int sobj_table_len = 0;
+
+/*
+ * Lower 32-bit of the address of synchronization objects is used to hash
+ * them.
+ */
+static guint
+sobj_id_hash(lt_sobj_id_t *id)
+{
+	g_assert(id != NULL);
+	return (id->lt_soi_addr & 0xFFFFFFFF);
+}
+
+/*
+ * Test if two synchronization objects are the same.
+ */
+static gboolean
+sobj_id_equal(lt_sobj_id_t *a, lt_sobj_id_t *b)
+{
+	g_assert(a != NULL && b != NULL);
+	return (a->lt_soi_type == b->lt_soi_type &&
+	    a->lt_soi_addr == b->lt_soi_addr);
+}
+
+/*
+ * Look up the cause_id of a synchronization object.
+ * Note that this cause_id is only unique in GROUP_SOBJ, and changes after
+ * a refresh.
+ */
+static lt_sobj_t *
+lookup_sobj(lt_sobj_id_t *id)
+{
+	const char *stype_str[] = {
+		"None",
+		"Mutex",
+		"RWLock",
+		"CV",
+		"Sema",
+		"User",
+		"User_PI",
+		"Shuttle"
+	};
+	const int stype_str_len =
+	    sizeof (stype_str) / sizeof (stype_str[0]);
+	lt_sobj_t *ret = NULL;
+	g_assert(id != NULL);
+
+	if (id->lt_soi_type < 0 || id->lt_soi_type >= stype_str_len) {
+		return (NULL);
+	}
+
+	if (sobj_table != NULL) {
+		ret = (lt_sobj_t *)g_hash_table_lookup(sobj_table, id);
+	} else {
+		sobj_table = g_hash_table_new_full(
+		    (GHashFunc)sobj_id_hash, (GEqualFunc)sobj_id_equal,
+		    NULL, (GDestroyNotify)free);
+		lt_check_null(sobj_table);
+	}
+
+	if (ret == NULL) {
+		ret = (lt_sobj_t *)lt_zalloc(sizeof (lt_sobj_t));
+		ret->lt_so_cause_id = ++sobj_table_len;
+		(void) snprintf(ret->lt_so_string, sizeof (ret->lt_so_string),
+		    "%s: 0x%llX", stype_str[id->lt_soi_type], id->lt_soi_addr);
+		ret->lt_so_oid.lt_soi_type = id->lt_soi_type;
+		ret->lt_so_oid.lt_soi_addr = id->lt_soi_addr;
+
+		g_hash_table_insert(sobj_table, &ret->lt_so_oid, ret);
+	}
+
+	return (ret);
+}
+
+/*
+ * Check if a process exists by using /proc/pid
+ */
+/* ARGSUSED */
+static gboolean
+check_process(gpointer key, lt_stat_collection_t *stat, void *user)
+{
+	char name[PATH_MAX];
+
+	(void) snprintf(name, PATH_MAX, "/proc/%u", stat->lt_sc_id);
+	return (lt_file_exist(name) ? FALSE : TRUE);
+}
+
+/*
+ * Check if a thread exists by using /proc/pid/lwp/tid
+ */
+/* ARGSUSED */
+static gboolean
+check_thread(gpointer key, lt_stat_collection_t *stat, void *user)
+{
+	char name[PATH_MAX];
+
+	g_assert(stat->lt_sc_parent != NULL);
+	g_assert(stat->lt_sc_parent->lt_sc_level == LT_LEVEL_PROCESS);
+
+	(void) snprintf(name, PATH_MAX, "/proc/%u/lwp/%u",
+	    stat->lt_sc_parent->lt_sc_id, stat->lt_sc_id);
+	return (lt_file_exist(name) ? FALSE : TRUE);
+}
+
+/*
+ * Helper function to free a stat node.
+ */
+static void
+free_stat(lt_stat_collection_t *stat)
+{
+	int i;
+
+	if (stat == NULL) {
+		return;
+	}
+
+	for (i = 0; i < NGROUPS; ++i) {
+		if (stat->lt_sc_groups[i].lt_grp_cidlist != NULL) {
+			g_hash_table_destroy(stat->lt_sc_groups[i].
+			    lt_grp_cidlist);
+		}
+	}
+
+	if (stat->lt_sc_children != NULL) {
+		g_hash_table_destroy(stat->lt_sc_children);
+	}
+
+	if (stat->lt_sc_name != NULL) {
+		free(stat->lt_sc_name);
+	}
+
+	free(stat);
+}
+
+/*
+ * Helper function to initialize a stat node.
+ */
+/* ARGSUSED */
+static void
+clear_stat(gpointer key, lt_stat_collection_t *stat, void *user)
+{
+	int i;
+
+	g_assert(stat != NULL);
+
+	for (i = 0; i < NGROUPS; ++i) {
+		if (stat->lt_sc_groups[i].lt_grp_cidlist != NULL) {
+			g_hash_table_destroy(stat->lt_sc_groups[i].
+			    lt_grp_cidlist);
+			stat->lt_sc_groups[i].lt_grp_cidlist = NULL;
+		}
+
+		stat->lt_sc_groups[i].lt_grp_summary.lt_se_data.lt_s_count = 0;
+		stat->lt_sc_groups[i].lt_grp_summary.lt_se_data.lt_s_total = 0;
+		stat->lt_sc_groups[i].lt_grp_summary.lt_se_data.lt_s_max = 0;
+	}
+
+	if (stat->lt_sc_children != NULL) {
+		g_hash_table_foreach_remove(stat->lt_sc_children,
+		    (GHRFunc)stat->lt_sc_check_child_func, NULL);
+		g_hash_table_foreach(stat->lt_sc_children,
+		    (GHFunc)clear_stat, NULL);
+	}
+}
+
+/*
+ * Update a collection with the given value.
+ * Recursively update parents in the hierarchy  until the root is reached.
+ */
+static void
+update_stat_entry(lt_stat_collection_t *stat, int cause_id,
+		lt_stat_type_t type, uint64_t value,
+		const char *string, int group_to_use)
+{
+	lt_stat_entry_t *entry = NULL;
+	lt_datagroup_t *group;
+
+	if (group_to_use < 0 || group_to_use >= NGROUPS) {
+		return;
+	}
+
+	group = &(stat->lt_sc_groups[group_to_use]);
+
+	if (group->lt_grp_cidlist != NULL) {
+		entry = (lt_stat_entry_t *)g_hash_table_lookup(
+		    group->lt_grp_cidlist, LT_INT_TO_POINTER(cause_id));
+	} else   {
+		group->lt_grp_cidlist = g_hash_table_new_full(
+		    g_direct_hash, g_direct_equal,
+		    NULL, (GDestroyNotify)free);
+		lt_check_null(group->lt_grp_cidlist);
+	}
+
+	if (entry == NULL) {
+		entry = (lt_stat_entry_t *)lt_zalloc(sizeof (lt_stat_entry_t));
+		entry->lt_se_string = string;
+
+		switch (group_to_use) {
+		case GROUP_CAUSE:
+			entry->lt_se_type = STAT_CAUSE;
+			entry->lt_se_tsdata.lt_se_t_cause.lt_se_c_id = cause_id;
+			entry->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags =
+			    lt_table_get_cause_flag(cause_id, CAUSE_ALL_FLAGS);
+
+			/* hide the first '#' */
+			if ((entry->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags
+			    & CAUSE_FLAG_HIDE_IN_SUMMARY) != 0) {
+				++entry->lt_se_string;
+			}
+
+			break;
+		case GROUP_SOBJ:
+			entry->lt_se_type = STAT_SOBJ;
+			entry->lt_se_tsdata.lt_se_t_sobj.lt_se_s_id = cause_id;
+			break;
+		}
+
+		g_hash_table_insert(group->lt_grp_cidlist,
+		    LT_INT_TO_POINTER(cause_id), entry);
+	}
+
+	lt_update_stat_value(&entry->lt_se_data, type, value);
+
+	if (group_to_use == GROUP_SOBJ ||
+	    (entry->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags &
+	    CAUSE_FLAG_HIDE_IN_SUMMARY) == 0) {
+		lt_update_stat_value(&group->lt_grp_summary.lt_se_data, type,
+		    value);
+	}
+
+	if (stat->lt_sc_parent != NULL) {
+		update_stat_entry(stat->lt_sc_parent, cause_id, type, value,
+		    string, group_to_use);
+	}
+}
+
+/*
+ * Identify the cause of latency from the given stack trace.
+ * Return cause_id.
+ */
+static void
+find_cause(char *stack, int *cause_id, int *cause_priority)
+{
+	int cause_temp;
+	int prio_temp;
+	int cause = INVALID_CAUSE;
+	int priority = 0;
+	int found = 0;
+
+	g_assert(cause_id != NULL);
+	g_assert(cause_priority != NULL);
+
+	while (stack != NULL) {
+		char *sep;
+		sep = strchr(stack, ' ');
+
+		if (sep != NULL) {
+			*sep = '\0';
+		}
+
+		found = lt_table_cause_from_stack(stack, &cause_temp,
+		    &prio_temp);
+
+		if (found && (cause == INVALID_CAUSE ||
+		    HIGHER_PRIORITY(prio_temp, priority))) {
+			cause = cause_temp;
+			priority = prio_temp;
+		}
+
+		if (sep != NULL) {
+			*sep = ' ';
+			stack = sep + 1;
+		} else   {
+			stack = NULL;
+		}
+	}
+
+	*cause_id = cause;
+	*cause_priority = priority;
+}
+
+/*
+ * Create a new collection and hook it to the parent.
+ */
+static lt_stat_collection_t *
+new_collection(lt_stat_level_t level, unsigned int id, char *name,
+    lt_stat_collection_t *parent, check_child_func_t check_child_func)
+{
+	int i;
+	lt_stat_collection_t *ret;
+
+	ret = (lt_stat_collection_t *)
+	    lt_zalloc(sizeof (lt_stat_collection_t));
+
+	ret->lt_sc_level = level;
+	ret->lt_sc_check_child_func = check_child_func;
+	ret->lt_sc_id = id;
+	ret->lt_sc_name = name;
+
+	for (i = 0; i < NGROUPS; ++i) {
+		ret->lt_sc_groups[i].lt_grp_summary.lt_se_string =
+		    (const char *)name;
+	}
+
+	if (parent != NULL) {
+		ret->lt_sc_parent = parent;
+
+		if (parent->lt_sc_children == NULL) {
+			parent->lt_sc_children = g_hash_table_new_full(
+			    g_direct_hash, g_direct_equal,
+			    NULL, (GDestroyNotify)free_stat);
+			lt_check_null(parent->lt_sc_children);
+		}
+
+		g_hash_table_insert(parent->lt_sc_children,
+		    LT_INT_TO_POINTER((int)id), ret);
+	}
+
+	return (ret);
+}
+
+/*
+ * Find the "leaf" in the collection hierarchy, using the given pid and tid.
+ */
+static lt_stat_collection_t *
+get_stat_c(pid_t pid, id_t tid)
+{
+	lt_stat_collection_t *stat_p = NULL;
+	lt_stat_collection_t *stat_t = NULL;
+
+	if (stat_system == NULL) {
+		stat_system = new_collection(LT_LEVEL_GLOBAL,
+		    PID_SYS_GLOBAL, lt_strdup("SYSTEM"), NULL, check_process);
+	} else if (stat_system->lt_sc_children != NULL) {
+		stat_p = (lt_stat_collection_t *)
+		    g_hash_table_lookup(stat_system->lt_sc_children,
+		    LT_INT_TO_POINTER(pid));
+	}
+
+	if (stat_p == NULL) {
+		char *fname;
+		fname = lt_get_proc_field(pid, LT_FIELD_FNAME);
+
+		if (fname == NULL) {
+			/*
+			 * we could not get the executable name of the
+			 * process; the process is probably already dead.
+			 */
+			return (NULL);
+		}
+
+		stat_p = new_collection(LT_LEVEL_PROCESS,
+		    (unsigned int)pid, fname, stat_system, check_thread);
+	} else if (stat_p->lt_sc_children != NULL) {
+		stat_t = (lt_stat_collection_t *)
+		    g_hash_table_lookup(stat_p->lt_sc_children,
+		    LT_INT_TO_POINTER(tid));
+	}
+
+	if (stat_t == NULL) {
+		const int tname_size = 16; /* Enough for "Thread %d" */
+		char *tname;
+
+		tname = (char *)lt_zalloc(tname_size);
+		(void) snprintf(tname, tname_size, "Thread %d", tid);
+
+		stat_t = new_collection(LT_LEVEL_THREAD,
+		    (unsigned int)tid, tname, stat_p, NULL);
+	}
+
+	return (stat_t);
+}
+
+/*
+ * Update statistics with the given cause_id. Values will be added to
+ * internal statistics.
+ */
+void
+lt_stat_update_cause(pid_t pid, id_t tid, int cause_id, lt_stat_type_t type,
+    uint64_t value)
+{
+	const char *string;
+	lt_stat_collection_t *stat_t = NULL;
+
+	if (cause_id < 0 || value == 0) {
+		return;
+	}
+
+	if (lt_table_get_cause_flag(cause_id, CAUSE_FLAG_DISABLED)) {
+		/* Ignore this cause */
+		return;
+	}
+
+	stat_t = get_stat_c(pid, tid);
+
+	if (stat_t == NULL) {
+		/* Process must be dead. */
+		return;
+	}
+
+	string = lt_table_get_cause_name(cause_id);
+
+	update_stat_entry(stat_t, cause_id, type, value, string, GROUP_CAUSE);
+}
+
+/*
+ * Update statistics with the given stack trace.
+ * The stack trace is mapped to a cause and lt_stat_update_cause() is called
+ * to update statistics.
+ */
+void
+lt_stat_update(pid_t pid, id_t tid, char *stack, char *tag,
+    unsigned int tag_priority, lt_stat_type_t type, uint64_t value)
+{
+	int tag_cause_id = INVALID_CAUSE;
+	int stack_cause_id = INVALID_CAUSE;
+	int cause_id = INVALID_CAUSE;
+	int stack_priority = 0;
+
+	if (value == 0) {
+		return;
+	}
+
+	find_cause(stack, &stack_cause_id, &stack_priority);
+
+	if (tag_priority != 0) {
+		tag_cause_id = lt_table_cause_from_name(tag, 0, 0);
+
+		if (tag_cause_id == INVALID_CAUSE) {
+			/* This must be a syscall tag */
+			char tmp[64];
+			(void) snprintf(tmp, sizeof (tmp), "Syscall: %s", tag);
+			tag_cause_id = lt_table_cause_from_name(tmp, 1, 0);
+		}
+	}
+
+	cause_id = (tag_priority > stack_priority) ? tag_cause_id :
+	    stack_cause_id;
+
+	if (cause_id == INVALID_CAUSE) {
+		/*
+		 * We got an unmapped stack. Set SPECIAL flag to display it
+		 * in pane 2. This makes it easier to find the cause.
+		 */
+		cause_id = lt_table_cause_from_name(stack, 1,
+		    CAUSE_FLAG_SPECIAL);
+		lt_klog_log(LT_KLOG_LEVEL_UNMAPPED, pid, stack, type, value);
+	} else   {
+		lt_klog_log(LT_KLOG_LEVEL_MAPPED, pid, stack, type, value);
+	}
+
+	lt_stat_update_cause(pid, tid, cause_id, type, value);
+}
+
+/*
+ * Zero out all statistics, but keep the data structures in memory
+ * to be used to hold new data immediately following.
+ */
+void
+lt_stat_clear_all(void)
+{
+	if (stat_system != NULL) {
+		clear_stat(NULL, stat_system, NULL);
+	}
+
+	if (sobj_table != NULL) {
+		g_hash_table_destroy(sobj_table);
+		sobj_table = NULL;
+	}
+}
+
+/*
+ * Clean up function that frees all memory used for statistics.
+ */
+void
+lt_stat_free_all(void)
+{
+	if (stat_system != NULL) {
+		free_stat(stat_system);
+		stat_system = NULL;
+	}
+
+	if (sobj_table != NULL) {
+		g_hash_table_destroy(sobj_table);
+		sobj_table = NULL;
+	}
+}
+
+/*
+ * Get top N causes of latency for a process. Return handle to a stat_list.
+ * Use pid = PID_SYS_GLOBAL to get global top list.
+ * Call lt_stat_list_free after use to clean up.
+ */
+void *
+lt_stat_list_create(lt_list_type_t list_type, lt_stat_level_t level,
+    pid_t pid, id_t tid, int count, lt_sort_t sort_by)
+{
+	GCompareFunc func;
+	GList *list, *walk;
+	lt_stat_collection_t *stat_c = NULL;
+	lt_stat_list_t *ret;
+	lt_datagroup_t *group;
+
+	if (level == LT_LEVEL_GLOBAL) {
+		/* Use global entry */
+		stat_c = stat_system;
+	} else if (stat_system != NULL && stat_system->lt_sc_children != NULL) {
+		/* Find process entry first */
+		stat_c = (lt_stat_collection_t *)g_hash_table_lookup(
+		    stat_system->lt_sc_children, LT_INT_TO_POINTER(pid));
+
+		if (level == LT_LEVEL_THREAD) {
+			/*
+			 * If thread entry is requested, find it based on
+			 * process entry.
+			 */
+			if (stat_c != NULL && stat_c->lt_sc_children != NULL) {
+				stat_c = (lt_stat_collection_t *)
+				    g_hash_table_lookup(stat_c->lt_sc_children,
+				    LT_INT_TO_POINTER(tid));
+			} else {
+				/*
+				 * Thread entry was not found; set it to NULL,
+				 * so that we can return empty list later.
+				 */
+				stat_c = NULL;
+			}
+		}
+	}
+
+	ret = (lt_stat_list_t *)lt_zalloc(sizeof (lt_stat_list_t));
+	ret->lt_sl_entries = (lt_stat_entry_t **)
+	    lt_zalloc(count * sizeof (lt_stat_entry_t *));
+
+	if (stat_c == NULL) {
+		/* Empty list */
+		return (ret);
+	}
+
+	if (list_type == LT_LIST_SOBJ) {
+		group = &(stat_c->lt_sc_groups[GROUP_SOBJ]);
+	} else {
+		group = &(stat_c->lt_sc_groups[GROUP_CAUSE]);
+	}
+
+	if (group->lt_grp_cidlist == NULL) {
+		/* Empty list */
+		return (ret);
+	}
+
+	ret->lt_sl_gtotal = group->lt_grp_summary.lt_se_data.lt_s_total;
+
+	list = g_hash_table_get_values(group->lt_grp_cidlist);
+
+	switch (sort_by) {
+	case LT_SORT_TOTAL:
+		func = (GCompareFunc)lt_sort_by_total_desc;
+		break;
+	case LT_SORT_MAX:
+		func = (GCompareFunc)lt_sort_by_max_desc;
+		break;
+	case LT_SORT_AVG:
+		func = (GCompareFunc)lt_sort_by_avg_desc;
+		break;
+	case LT_SORT_COUNT:
+		func = (GCompareFunc)lt_sort_by_count_desc;
+		break;
+	}
+	list = g_list_sort(list, func);
+
+	for (walk = list;
+	    walk != NULL && count > 0;
+	    walk = g_list_next(walk), --count) {
+		lt_stat_entry_t *data = (lt_stat_entry_t *)walk->data;
+
+		if (list_type == LT_LIST_CAUSE &&
+		    data->lt_se_type == STAT_CAUSE &&
+		    (data->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags &
+		    CAUSE_FLAG_HIDE_IN_SUMMARY) != 0) {
+			continue;
+		}
+
+		if (list_type == LT_LIST_SPECIALS &&
+		    data->lt_se_type == STAT_CAUSE &&
+		    (data->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags &
+		    CAUSE_FLAG_SPECIAL) == 0) {
+			continue;
+		}
+
+		if (data->lt_se_data.lt_s_count == 0) {
+			break;
+		}
+
+		ret->lt_sl_entries[ret->lt_sl_entry_count++] = data;
+	}
+
+	g_list_free(list);
+
+	return (ret);
+}
+
+/*
+ * Free memory allocated by lt_stat_list_create().
+ */
+void
+lt_stat_list_free(void *ptr)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL) {
+		return;
+	}
+
+	if (list->lt_sl_free_func != NULL) {
+		list->lt_sl_free_func(list);
+	}
+
+	if (list->lt_sl_entries != NULL) {
+		free(list->lt_sl_entries);
+	}
+
+	free(list);
+}
+
+/*
+ * Check if the given list contains the given item.
+ */
+int
+lt_stat_list_has_item(void *ptr, int i)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
+	    list->lt_sl_entries[i] == NULL) {
+		return (0);
+	}
+
+	return (1);
+}
+
+/*
+ * Get display name of the given item i in the given list.
+ */
+const char *
+lt_stat_list_get_reason(void *ptr, int i)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
+	    list->lt_sl_entries[i] == NULL) {
+		return (NULL);
+	}
+
+	g_assert(list->lt_sl_entries[i]->lt_se_string != NULL);
+
+	return (list->lt_sl_entries[i]->lt_se_string);
+}
+
+/*
+ * Get maximum value of the given item i in the given list.
+ */
+uint64_t
+lt_stat_list_get_max(void *ptr, int i)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
+	    list->lt_sl_entries[i] == NULL) {
+		return (0);
+	}
+
+	return (list->lt_sl_entries[i]->lt_se_data.lt_s_max);
+}
+
+/*
+ * Get total value of the given item i in the given list.
+ */
+uint64_t
+lt_stat_list_get_sum(void *ptr, int i)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
+	    list->lt_sl_entries[i] == NULL) {
+		return (0);
+	}
+
+	return (list->lt_sl_entries[i]->lt_se_data.lt_s_total);
+}
+
+/*
+ * Get count value of the given item i in the given list.
+ */
+uint64_t
+lt_stat_list_get_count(void *ptr, int i)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
+	    list->lt_sl_entries[i] == NULL) {
+		return (0);
+	}
+
+	return (list->lt_sl_entries[i]->lt_se_data.lt_s_count);
+}
+
+/*
+ * Get grand total of all latency in the list.
+ */
+uint64_t
+lt_stat_list_get_gtotal(void *ptr)
+{
+	lt_stat_list_t *list = (lt_stat_list_t *)ptr;
+
+	if (list == NULL) {
+		return (0);
+	}
+
+	return (list->lt_sl_gtotal);
+}
+
+/*
+ * ============================================================================
+ * Process and thread list.
+ * They share a lot of the static variables that are used for keeping
+ * statistics, hence they are located in this file.
+ */
+
+/*
+ * Helper function, sort by PID/TID ascend.
+ */
+static int
+sort_id(lt_stat_collection_t *a, lt_stat_collection_t *b)
+{
+	return ((int)(a->lt_sc_id - b->lt_sc_id));
+}
+
+/*
+ * Get the current list of processes. Call lt_stat_proc_list_free after use
+ * to clean up.
+ */
+static int
+plist_create(pid_t ** list)
+{
+	GList *pid_list, *walk;
+	int ret, count;
+
+	ret = g_hash_table_size(stat_system->lt_sc_children);
+	*list = (pid_t *)lt_malloc(sizeof (pid_t) * ret);
+
+	pid_list = g_hash_table_get_values(stat_system->lt_sc_children);
+	pid_list = g_list_sort(pid_list, (GCompareFunc)sort_id);
+
+	for (walk = pid_list, count = 0;
+	    walk != NULL && count < ret;
+	    walk = g_list_next(walk), ++count) {
+		(*list)[count] = (int)
+		    ((lt_stat_collection_t *)(walk->data))->lt_sc_id;
+	}
+
+	g_list_free(pid_list);
+
+	return (ret);
+}
+
+/*
+ * Count the no. of threads currently present in a process.
+ * Only thread that have SSLEEP are counted.
+ */
+/* ARGSUSED */
+static void
+count_threads(gpointer key, lt_stat_collection_t *stat_c, int *ret)
+{
+	g_assert(ret != NULL);
+
+	if (stat_c->lt_sc_children != NULL) {
+		*ret += g_hash_table_size(stat_c->lt_sc_children);
+	}
+}
+
+/*
+ * Get current list of processes and threads.
+ * Call lt_stat_proc_list_free after use to clean up.
+ */
+static int
+tlist_create(pid_t ** plist, id_t ** tlist)
+{
+	GList *pid_list, *walk_p;
+	GList *tid_list, *walk_t;
+	int ret = 0;
+	int count = 0;
+
+	g_hash_table_foreach(stat_system->lt_sc_children,
+	    (GHFunc)count_threads, &ret);
+
+	*plist = (pid_t *)lt_malloc(sizeof (pid_t) * ret);
+	*tlist = (id_t *)lt_malloc(sizeof (id_t) * ret);
+
+	pid_list = g_hash_table_get_values(stat_system->lt_sc_children);
+	pid_list = g_list_sort(pid_list, (GCompareFunc)sort_id);
+
+	for (walk_p = pid_list; walk_p != NULL;
+	    walk_p = g_list_next(walk_p)) {
+		lt_stat_collection_t *stat_p =
+		    (lt_stat_collection_t *)walk_p->data;
+
+		if (stat_p->lt_sc_children == NULL) {
+			continue;
+		}
+
+		tid_list = g_hash_table_get_values(stat_p->lt_sc_children);
+		tid_list = g_list_sort(tid_list, (GCompareFunc)sort_id);
+
+		for (walk_t = tid_list; walk_t != NULL;
+		    walk_t = g_list_next(walk_t)) {
+			lt_stat_collection_t *stat_t =
+			    (lt_stat_collection_t *)walk_t->data;
+
+			(*plist)[count] = (int)stat_p->lt_sc_id;
+			(*tlist)[count] = (int)stat_t->lt_sc_id;
+
+			++count;
+		}
+		g_list_free(tid_list);
+	}
+
+	g_list_free(pid_list);
+	g_assert(count == ret);
+
+	return (ret);
+}
+
+/*
+ * List of processes that are tracked by LatencyTOP.
+ */
+int
+lt_stat_proc_list_create(pid_t ** plist, id_t ** tlist)
+{
+	if (plist == NULL) {
+		return (-1);
+	}
+
+	if (stat_system == NULL || stat_system->lt_sc_children == NULL) {
+		*plist = NULL;
+
+		if (tlist != NULL) {
+			*tlist = NULL;
+		}
+
+		return (0);
+	}
+
+	if (tlist == NULL) {
+		return (plist_create(plist));
+	} else {
+		return (tlist_create(plist, tlist));
+	}
+}
+
+/*
+ * Free memory allocated by lt_stat_proc_list_create().
+ */
+void
+lt_stat_proc_list_free(pid_t *plist, id_t *tlist)
+{
+	if (plist != NULL) {
+		free(plist);
+	}
+
+	if (tlist != NULL) {
+		free(tlist);
+	}
+}
+
+/*
+ * Get executable name of the given process (ID).
+ */
+const char *
+lt_stat_proc_get_name(pid_t pid)
+{
+	lt_stat_collection_t *stat_p = NULL;
+
+	if (stat_system == NULL || stat_system->lt_sc_children == NULL) {
+		return (NULL);
+	}
+
+	stat_p = (lt_stat_collection_t *)g_hash_table_lookup(
+	    stat_system->lt_sc_children, LT_INT_TO_POINTER(pid));
+
+	if (stat_p != NULL) {
+		return (stat_p->lt_sc_name);
+	} else   {
+		return (NULL);
+	}
+}
+
+/*
+ * Get number of threads.
+ */
+int
+lt_stat_proc_get_nthreads(pid_t pid)
+{
+	lt_stat_collection_t *stat_p = NULL;
+
+	if (stat_system == NULL || stat_system->lt_sc_children == NULL) {
+		return (0);
+	}
+
+	stat_p = (lt_stat_collection_t *)g_hash_table_lookup(
+	    stat_system->lt_sc_children, LT_INT_TO_POINTER(pid));
+
+	if (stat_p != NULL) {
+		return (g_hash_table_size(stat_p->lt_sc_children));
+	} else   {
+		return (0);
+	}
+}
+
+/*
+ * Update statistics for synchronization objects.
+ */
+void
+lt_stat_update_sobj(pid_t pid, id_t tid, int stype,
+    unsigned long long wchan,
+    lt_stat_type_t type, uint64_t value)
+{
+	lt_sobj_id_t id;
+	lt_sobj_t *sobj;
+	int cause_id;
+	lt_stat_collection_t *stat_t = NULL;
+
+	stat_t = get_stat_c(pid, tid);
+
+	if (stat_t == NULL) {
+		return;
+	}
+
+	id.lt_soi_type = stype;
+	id.lt_soi_addr = wchan;
+	sobj = lookup_sobj(&id);
+
+	if (sobj == NULL) {
+		return;
+	}
+
+	cause_id = sobj->lt_so_cause_id;
+
+	update_stat_entry(stat_t, cause_id, type, value,
+	    sobj->lt_so_string, GROUP_SOBJ);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/table.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,840 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "latencytop.h"
+
+/*
+ * Structure that holds detail of a cause.
+ */
+typedef struct {
+	int lt_c_cause_id;
+	int lt_c_flags;
+	char *lt_c_name;
+} lt_cause_t;
+
+/*
+ * Structure that represents a matched cause.
+ */
+typedef struct  {
+	int lt_mt_priority;
+	int lt_mt_cause_id;
+} lt_match_t;
+
+/* All lt_cause_t that are created. */
+static GHashTable *cause_lookup = NULL;
+static GPtrArray *causes_array = NULL;
+static int causes_array_len = 0;
+
+/*
+ * This hash table maps a symbol to a cause.
+ * key is of type "char *" and value is of type "lt_match_t *".
+ */
+static GHashTable *symbol_lookup_table = NULL;
+
+/*
+ * The dtrace translation rules we get from the script
+ */
+char *dtrans = NULL;
+
+/*
+ * These structures are only used inside .trans parser.
+ */
+typedef struct {
+	int lt_dm_priority;
+	char *lt_dm_macro;
+} lt_dmacro_t;
+
+typedef struct {
+	GSequence *lt_pr_cmd_disable;
+	GHashTable *lt_pr_dmacro;
+} lt_parser_t;
+
+/* ARGSUSED */
+static void
+free_cause(lt_cause_t *cause, void *user)
+{
+	g_assert(cause != NULL && cause->lt_c_name != NULL);
+
+	free(cause->lt_c_name);
+	free(cause);
+}
+
+static void
+free_dmacro(lt_dmacro_t *d)
+{
+	g_assert(d->lt_dm_macro != NULL);
+	free(d->lt_dm_macro);
+	free(d);
+}
+
+/*
+ * Add a cause.
+ */
+static lt_cause_t *
+new_cause(char *name, int flags)
+{
+	lt_cause_t *entry;
+
+	g_assert(name != NULL);
+
+	entry = (lt_cause_t *)lt_malloc(sizeof (lt_cause_t));
+	entry->lt_c_flags = flags;
+	entry->lt_c_name = name;
+	entry->lt_c_cause_id = causes_array_len;
+
+	g_ptr_array_add(causes_array, entry);
+	++causes_array_len;
+
+	return (entry);
+}
+
+/*
+ * Set a cause to "disabled" state.
+ */
+static void
+disable_cause(char *cause_str, GHashTable *cause_table)
+{
+	lt_cause_t *cause;
+
+	cause = (lt_cause_t *)g_hash_table_lookup(cause_table, cause_str);
+
+	if (cause != NULL) {
+		cause->lt_c_flags |= CAUSE_FLAG_DISABLED;
+	}
+}
+
+/*
+ * Helper functions that reads a line from a character array.
+ */
+static int
+read_line_from_mem(const char *mem, int mem_len, char *line, int line_len,
+    int *index)
+{
+	g_assert(mem != NULL && line != NULL && index != NULL);
+
+	if (line_len <= 0 || mem_len <= 0) {
+		return (0);
+	}
+
+	if (*index >= mem_len) {
+		return (0);
+	}
+
+	while (line_len > 1 && *index < mem_len) {
+		*line = mem[(*index)++];
+		--line_len;
+		++line;
+
+		if (*(line-1) == '\r' || *(line-1) == '\n') {
+			break;
+		}
+	}
+	*line = '\0';
+
+	return (1);
+}
+
+/*
+ * Parse special command from configuration file. Special command
+ * has the following format :
+
+ *	disable_cause <cause name>
+ */
+static int
+parse_config_cmd(char *begin, lt_parser_t *parser)
+{
+	char *tmp;
+	char old_chr = 0;
+
+	/*
+	 * disable_cause  FSFlush Daemon
+	 * ^
+	 */
+	if (*begin == '\0') {
+		return (0);
+	}
+
+	for (tmp = begin;
+	    *tmp != '\0' && !isspace(*tmp);
+	    ++tmp) {
+	}
+	old_chr = *tmp;
+	*tmp = '\0';
+
+	if (strcmp("disable_cause", begin) == 0) {
+		if (old_chr == '\0') {
+			/* Must have an argument */
+			lt_display_error(
+			    "Invalid command format: %s\n",
+			    begin);
+			return (-1);
+		}
+
+		begin = tmp+1;
+		while (isspace(*begin)) {
+			++begin;
+		}
+
+		g_sequence_append(parser->lt_pr_cmd_disable,
+		    lt_strdup(begin));
+	} else   {
+		*tmp = old_chr;
+		lt_display_error(
+		    "Unknown command: %s\n", begin);
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * Parse symbol translation from configuration file. Symbol translation
+ * has the following format :
+ *
+ *	<priority> <symbol name> <cause>
+ *
+ * Finally check if that cause has already been mapped.
+ */
+static int
+parse_sym_trans(char *begin)
+{
+	int priority = 0;
+	char *match;
+	char *match_dup;
+	char *cause_str;
+	lt_cause_t *cause;
+	lt_match_t *match_entry;
+	char *tmp;
+
+	/*
+	 * 10	genunix`pread			Syscall pread
+	 * ^
+	 */
+	priority = strtol(begin, &tmp, 10);
+
+	if (tmp == begin || priority == 0) {
+		return (-1);
+	}
+
+	begin = tmp;
+
+	/*
+	 * 10	genunix`pread			Syscall pread
+	 * --^
+	 */
+
+	if (!isspace(*begin)) {
+		/* At least one space char after <priority> */
+		return (-1);
+	}
+
+	while (isspace(*begin)) {
+		++begin;
+	}
+
+	if (*begin == 0) {
+		return (-1);
+	}
+
+	/*
+	 * 10	genunix`pread			Syscall pread
+	 * -----^
+	 */
+	for (tmp = begin;
+	    *tmp != '\0' && !isspace(*tmp);
+	    ++tmp) {
+	}
+
+	if (*tmp == '\0') {
+		return (-1);
+	}
+
+	*tmp = '\0';
+	match = begin;
+
+	/* Check if we have mapped this function before. */
+	match_entry = (lt_match_t *)
+	    g_hash_table_lookup(symbol_lookup_table, match);
+
+	if (match_entry != NULL &&
+	    HIGHER_PRIORITY(match_entry->lt_mt_priority, priority)) {
+		/* We already have a higher entry. Ignore this. */
+		return (0);
+	}
+
+	begin = tmp + 1;
+
+	/*
+	 * 10	genunix`pread			Syscall pread
+	 * -------------------------------------^
+	 */
+	while (isspace(*begin)) {
+		++begin;
+	}
+
+	if (*begin == 0) {
+		return (-1);
+	}
+
+	cause_str = begin;
+
+	/* Check if we have mapped this cause before. */
+	cause = (lt_cause_t *)
+	    g_hash_table_lookup(cause_lookup, cause_str);
+
+	if (cause == NULL) {
+		char *cause_dup = lt_strdup(cause_str);
+		cause = new_cause(cause_dup, 0);
+		g_hash_table_insert(cause_lookup, cause_dup, cause);
+	}
+
+	match_entry = (lt_match_t *)lt_malloc(sizeof (lt_match_t));
+	match_entry->lt_mt_priority = priority;
+	match_entry->lt_mt_cause_id = cause->lt_c_cause_id;
+	match_dup = lt_strdup(match);
+
+	g_hash_table_insert(symbol_lookup_table, match_dup,
+	    match_entry);
+
+	return (0);
+}
+
+/*
+ * Parse D macro. D macros have the following format :
+ *
+ *	<priority> <entry probe> <return probe> <cause>
+ *
+ * Finally check if that cause has already been mapped.
+ */
+static int
+parse_dmacro(char *begin, lt_parser_t *parser)
+{
+	int priority = 0;
+	char *entryprobe;
+	char *returnprobe;
+	char *cause_str;
+	char buf[512];
+	char probepair[512];
+	char *tmp = NULL;
+	lt_cause_t *cause;
+	lt_dmacro_t *dmacro;
+
+	/*
+	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
+	 * ^
+	 */
+	priority = strtol(begin, &tmp, 10);
+
+	if (tmp == begin || priority == 0) {
+		return (-1);
+	}
+
+	begin = tmp;
+
+	/*
+	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
+	 * --^
+	 */
+	while (isspace(*begin)) {
+		++begin;
+	}
+
+	if (*begin == 0) {
+		return (-1);
+	}
+
+	/*
+	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
+	 * -----^
+	 */
+	for (tmp = begin;
+	    *tmp != '\0' && !isspace(*tmp);
+	    ++tmp) {
+	}
+
+	if (*tmp == '\0') {
+		return (-1);
+	}
+
+	*tmp = '\0';
+	entryprobe = begin;
+	begin = tmp + 1;
+
+	while (isspace(*begin)) {
+		++begin;
+	}
+
+	/*
+	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
+	 * -----------------------------^
+	 */
+	for (tmp = begin;
+	    *tmp != '\0' && !isspace(*tmp);
+	    ++tmp) {
+	}
+
+	if (*tmp == '\0') {
+		return (-1);
+	}
+
+	*tmp = '\0';
+	returnprobe = begin;
+	begin = tmp + 1;
+
+	while (isspace(*begin)) {
+		++begin;
+	}
+
+	/*
+	 * 10	syscall::pread:entry	syscall::pread:return	Syscall pread
+	 * -----------------------------------------------------^
+	 */
+	if (*begin == 0) {
+		return (-1);
+	}
+
+	cause_str = begin;
+
+	dmacro = NULL;
+
+	/* Check if we have mapped this cause before. */
+	cause = (lt_cause_t *)
+	    g_hash_table_lookup(cause_lookup, cause_str);
+
+	if (cause == NULL) {
+		char *cause_dup = lt_strdup(cause_str);
+		cause = new_cause(cause_dup, 0);
+		g_hash_table_insert(cause_lookup, cause_dup, cause);
+	}
+
+	(void) snprintf(buf, sizeof (buf), "\nTRANSLATE(%s, %s, \"%s\", %d)\n",
+	    entryprobe, returnprobe, cause_str, priority);
+
+	(void) snprintf(probepair, sizeof (probepair), "%s %s", entryprobe,
+	    returnprobe);
+
+	g_assert(cause != NULL);
+	g_assert(parser->lt_pr_dmacro != NULL);
+
+	dmacro = g_hash_table_lookup(parser->lt_pr_dmacro, probepair);
+
+	if (dmacro == NULL) {
+		dmacro = (lt_dmacro_t *)lt_malloc(sizeof (lt_dmacro_t));
+		dmacro->lt_dm_priority = priority;
+		dmacro->lt_dm_macro = lt_strdup(buf);
+		g_hash_table_insert(parser->lt_pr_dmacro, lt_strdup(probepair),
+		    dmacro);
+	} else if (dmacro->lt_dm_priority < priority) {
+		free(dmacro->lt_dm_macro);
+		dmacro->lt_dm_priority = priority;
+		dmacro->lt_dm_macro = lt_strdup(buf);
+	}
+
+	return (0);
+}
+
+/*
+ * Helper function to collect TRANSLATE() macros.
+ */
+/* ARGSUSED */
+static void
+genscript(void *key, lt_dmacro_t *dmacro, GString *str)
+{
+	g_string_append(str, dmacro->lt_dm_macro);
+}
+
+/*
+ * Main logic that parses translation rules one line at a time,
+ * and creates a lookup table from it. The syntax for the translation
+ * is as follows :
+ *
+ *	#				<--- comment
+ *	D <D macro rule>		<--- D macro
+ *	S <Symbol translation>		<--- Symbols
+ *	disable_cause <cause>		<--- special command
+ */
+static int
+parse_config(const char *work, int work_len)
+{
+	char line[256];
+	int len;
+	char *begin, *end;
+	int current = 0;
+	lt_parser_t parser;
+	int ret = 0;
+	char flag;
+	GString *script;
+
+	cause_lookup = g_hash_table_new(g_str_hash, g_str_equal);
+	lt_check_null(cause_lookup);
+
+	parser.lt_pr_cmd_disable = g_sequence_new((GDestroyNotify)free);
+	lt_check_null(parser.lt_pr_cmd_disable);
+
+	parser.lt_pr_dmacro = g_hash_table_new_full(g_str_hash,
+	    g_str_equal, (GDestroyNotify)free, (GDestroyNotify)free_dmacro);
+	lt_check_null(parser.lt_pr_dmacro);
+
+	while (read_line_from_mem(work, work_len, line, sizeof (line),
+	    &current)) {
+		len = strlen(line);
+
+		if (line[len-1] != '\n' && line[len-1] != '\r' &&
+		    current < work_len) {
+			lt_display_error("Configuration line too long.\n");
+			goto err;
+		}
+
+		begin = line;
+
+		while (isspace(*begin)) {
+			++begin;
+		}
+
+		if (*begin == '\0') {
+			/* Ignore empty line */
+			continue;
+		}
+
+		/* Delete trailing spaces. */
+		end = begin + strlen(begin) - 1;
+
+		while (isspace(*end)) {
+			--end;
+		}
+
+		end[1] = '\0';
+
+		flag = *begin;
+		++begin;
+
+		switch (flag) {
+		case '#':
+			ret = 0;
+			break;
+		case ';':
+			ret = parse_config_cmd(begin, &parser);
+			break;
+		case 'D':
+		case 'd':
+			if (!isspace(*begin)) {
+				lt_display_error(
+				    "No space after flag char: %s\n", line);
+			}
+			while (isspace(*begin)) {
+				++begin;
+			}
+			ret = parse_dmacro(begin, &parser);
+			break;
+		case 'S':
+		case 's':
+			if (!isspace(*begin)) {
+				lt_display_error(
+				    "No space after flag char: %s\n", line);
+			}
+			while (isspace(*begin)) {
+				++begin;
+			}
+			ret = parse_sym_trans(begin);
+			break;
+		default:
+			ret = -1;
+			break;
+		}
+
+		if (ret != 0) {
+			lt_display_error(
+			    "Invalid configuration line: %s\n", line);
+			goto err;
+		}
+	}
+
+	script = g_string_new(NULL);
+	g_hash_table_foreach(parser.lt_pr_dmacro, (GHFunc)genscript, script);
+	dtrans = g_string_free(script, FALSE);
+
+	if (dtrans != NULL && strlen(dtrans) == 0) {
+		free(dtrans);
+		dtrans = NULL;
+	}
+
+	g_sequence_foreach(parser.lt_pr_cmd_disable, (GFunc)disable_cause,
+	    cause_lookup);
+	g_sequence_free(parser.lt_pr_cmd_disable);
+
+	return (0);
+
+err:
+	g_sequence_free(parser.lt_pr_cmd_disable);
+	g_hash_table_destroy(parser.lt_pr_dmacro);
+	return (-1);
+
+}
+
+/*
+ * Init function, called when latencytop starts.
+ * It loads translation rules from the configuration file. The configuration
+ * file defines some causes and symbols that match those causes.
+ */
+int
+lt_table_init(void)
+{
+	char *config_loaded = NULL;
+	int config_loaded_len = 0;
+	const char *work = NULL;
+	int work_len = 0;
+	lt_cause_t *cause;
+
+#ifdef EMBED_CONFIGS
+	work = &latencytop_trans_start;
+	work_len = (int)(&latencytop_trans_end - &latencytop_trans_start);
+#endif
+
+	if (g_config.lt_cfg_config_name != NULL) {
+		FILE *fp;
+		fp = fopen(g_config.lt_cfg_config_name, "r");
+
+		if (NULL == fp) {
+			lt_display_error(
+			    "Unable to open configuration file.\n");
+			return (-1);
+		}
+
+		(void) fseek(fp, 0, SEEK_END);
+		config_loaded_len = (int)ftell(fp);
+		config_loaded = (char *)lt_malloc(config_loaded_len);
+		(void) fseek(fp, 0, SEEK_SET);
+
+		/* A zero-byte translation is valid */
+		if (config_loaded_len != 0 &&
+		    fread(config_loaded, config_loaded_len, 1, fp) == 0) {
+			lt_display_error(
+			    "Unable to read configuration file.\n");
+			(void) fclose(fp);
+			free(config_loaded);
+			return (-1);
+		}
+
+		(void) fclose(fp);
+		(void) printf("Loaded configuration from %s\n",
+		    g_config.lt_cfg_config_name);
+
+		work = config_loaded;
+		work_len = config_loaded_len;
+	}
+
+	lt_table_deinit();
+	causes_array = g_ptr_array_new();
+	lt_check_null(causes_array);
+
+	/* 0 is not used, but it is kept as a place for bugs etc. */
+	cause = new_cause(lt_strdup("Nothing"), CAUSE_FLAG_DISABLED);
+	g_assert(cause->lt_c_cause_id == INVALID_CAUSE);
+
+	symbol_lookup_table = g_hash_table_new_full(
+	    g_str_hash, g_str_equal,
+	    (GDestroyNotify)free, (GDestroyNotify)free);
+	lt_check_null(symbol_lookup_table);
+
+	if (work_len != 0 && parse_config(work, work_len) != 0) {
+		return (-1);
+	}
+
+	if (config_loaded != NULL) {
+		free(config_loaded);
+	}
+
+	return (0);
+}
+
+/*
+ * Some causes, such as "lock spinning", do not have stack trace. Names
+ * of such causes are explicitly specified in the D script.
+ * This function resolves such causes and dynamically adds them
+ * to the global tables when they are found first. If auto_create is set
+ * to TRUE, the entry will be created if it is not found.
+ * Return cause_id of the cause.
+ */
+int
+lt_table_cause_from_name(char *name, int auto_create, int flags)
+{
+	lt_cause_t *cause = NULL;
+
+	if (cause_lookup == NULL) {
+		cause_lookup = g_hash_table_new(g_str_hash, g_str_equal);
+		lt_check_null(cause_lookup);
+	} else   {
+		cause = (lt_cause_t *)
+		    g_hash_table_lookup(cause_lookup, name);
+	}
+
+	if (cause == NULL && auto_create) {
+		char *cause_dup;
+
+		if (name[0] == '#') {
+			flags |= CAUSE_FLAG_HIDE_IN_SUMMARY;
+		}
+
+		cause_dup = lt_strdup(name);
+		cause = new_cause(cause_dup, flags);
+		g_hash_table_insert(cause_lookup, cause_dup, cause);
+	}
+
+	return (cause == NULL ? INVALID_CAUSE : cause->lt_c_cause_id);
+}
+
+/*
+ * Try to map a symbol on stack to a known cause.
+ * module_func has the format "module_name`function_name".
+ * cause_id and priority will be set if a cause is found.
+ * If cause is found return 1, otherwise return 0.
+ */
+int
+lt_table_cause_from_stack(const char *module_func, int *cause_id, int *priority)
+{
+	lt_match_t *match;
+
+	g_assert(module_func != NULL && cause_id != NULL && priority != NULL);
+
+	if (symbol_lookup_table == NULL) {
+		return (0);
+	}
+
+	match = (lt_match_t *)
+	    g_hash_table_lookup(symbol_lookup_table, module_func);
+
+	if (match == NULL) {
+		char *func = strchr(module_func, '`');
+
+		if (func != NULL) {
+			match = (lt_match_t *)
+			    g_hash_table_lookup(symbol_lookup_table, func);
+		}
+	}
+
+	if (match == NULL) {
+		return (0);
+	} else   {
+		*cause_id = match->lt_mt_cause_id;
+		*priority = match->lt_mt_priority;
+		return (1);
+	}
+}
+
+/*
+ * Get the display name of a cause. cause_id must be valid,
+ * it is usually returned from lt_table_cause_from_stack() or
+ * lt_table_cause_from_name().
+ */
+const char *
+lt_table_get_cause_name(int cause_id)
+{
+	lt_cause_t *cause;
+
+	if (cause_id < 0 || cause_id >= causes_array_len) {
+		return (NULL);
+	}
+
+	cause = (lt_cause_t *)g_ptr_array_index(causes_array, cause_id);
+
+	if (cause == NULL) {
+		return (NULL);
+	} else {
+		return (cause->lt_c_name);
+	}
+}
+
+/*
+ * Check cause flag.
+ * If CAUSE_ALL_FLAGS is passed in, all flags are returned.
+ */
+int
+lt_table_get_cause_flag(int cause_id, int flag)
+{
+	lt_cause_t *cause;
+
+	if (cause_id < 0 || cause_id >= causes_array_len) {
+		return (0);
+	}
+
+	cause = (lt_cause_t *)g_ptr_array_index(causes_array, cause_id);
+
+	if (cause == NULL) {
+		return (0);
+	} else {
+		return (cause->lt_c_flags & flag);
+	}
+}
+
+/*
+ * Append macros to D script, if any.
+ */
+int
+lt_table_append_trans(FILE *fp)
+{
+	if (dtrans != NULL) {
+		if (fwrite(dtrans, strlen(dtrans), 1, fp) != 1) {
+			return (-1);
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * Clean up function.
+ * Free the resources used for symbol table (symbols, causes etc.).
+ */
+void
+lt_table_deinit(void)
+{
+	if (symbol_lookup_table != NULL) {
+		g_hash_table_destroy(symbol_lookup_table);
+		symbol_lookup_table = NULL;
+	}
+
+	if (cause_lookup != NULL) {
+		g_hash_table_destroy(cause_lookup);
+		cause_lookup = NULL;
+	}
+
+	if (causes_array != NULL) {
+		g_ptr_array_foreach(causes_array, (GFunc)free_cause, NULL);
+		g_ptr_array_free(causes_array, TRUE);
+		causes_array = NULL;
+		causes_array_len = 0;
+	}
+
+	if (dtrans != NULL) {
+		g_free(dtrans);
+		dtrans = NULL;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/common/util.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,312 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2008-2009, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include <unistd.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <procfs.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "latencytop.h"
+
+/* Pipe that breaks the event loop (and exits early) */
+static int signal_pipe[2];
+
+/*
+ * Get current system time in milliseconds (1e-3).
+ */
+uint64_t
+lt_millisecond(void)
+{
+	struct timeval p;
+	(void) gettimeofday(&p, NULL);
+	return ((uint64_t)p.tv_sec * 1000 + p.tv_usec / 1000);
+}
+
+/*
+ * Check if we are out of memory.
+ */
+void
+lt_check_null(void *p)
+{
+	if (p == NULL) {
+		(void) fprintf(stderr, "Out of memory!\n");
+		g_assert(0);
+		exit(2);
+	}
+}
+
+/*
+ * Safe malloc.
+ */
+void *
+lt_malloc(size_t size)
+{
+	void *ret = malloc(size);
+
+	lt_check_null(ret);
+
+	return (ret);
+}
+
+/*
+ * Safe alloc with memory cleared.
+ * It is named "zalloc" because its signature is different from
+ * calloc() in stdlib.
+ */
+void *
+lt_zalloc(size_t size)
+{
+	void *ret = calloc(size, 1);
+
+	lt_check_null(ret);
+
+	return (ret);
+}
+
+/*
+ * Safe strdup.
+ */
+char *
+lt_strdup(const char *str)
+{
+	char *ret = strdup(str);
+
+	lt_check_null(ret);
+
+	return (ret);
+}
+
+/*
+ * Get string for current time, e.g. YYYY-MM-DD
+ */
+void
+lt_time_str(char *buffer, int len)
+{
+	struct tm tms;
+	time_t t;
+	int i;
+
+	(void) time(&t);
+	(void) gmtime_r(&t, &tms);
+	(void) asctime_r(&tms, buffer, len);
+
+	for (i = strlen(buffer)-1; i > 0; --i) {
+
+		if (isspace(buffer[i])) {
+			buffer[i] = '\0';
+		} else {
+			break;
+		}
+	}
+}
+
+/*
+ * Retrieves the process's executable name and arguments from /proc.
+ */
+char *
+lt_get_proc_field(pid_t pid, lt_field_t field)
+{
+	char name[PATH_MAX];
+	int fd;
+	int ret;
+	psinfo_t psinfo;
+
+	(void) snprintf(name, PATH_MAX, "/proc/%d/psinfo", (int)pid);
+	fd = open(name, O_RDONLY);
+
+	if (fd == -1) {
+		return (NULL);
+	}
+
+	ret = read(fd, (char *)&psinfo, sizeof (psinfo_t));
+	(void) close(fd);
+
+	if (ret < 0) {
+		return (NULL);
+	}
+
+	switch (field) {
+	case LT_FIELD_FNAME:
+		return (lt_strdup(psinfo.pr_fname));
+	case LT_FIELD_PSARGS:
+		return (lt_strdup(psinfo.pr_psargs));
+	}
+	return (NULL);
+}
+
+/*
+ * Helper function to update the data structure.
+ */
+void
+lt_update_stat_value(lt_stat_data_t *entry,
+    lt_stat_type_t type, uint64_t value)
+{
+	switch (type) {
+	case LT_STAT_COUNT:
+		entry->lt_s_count += value;
+		break;
+	case LT_STAT_SUM:
+		entry->lt_s_total += value;
+		break;
+	case LT_STAT_MAX:
+		if (value > entry->lt_s_max) {
+			entry->lt_s_max = value;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+/*
+ * Helper function to sort on total.
+ */
+int
+lt_sort_by_total_desc(lt_stat_entry_t *a, lt_stat_entry_t *b)
+{
+	g_assert(a != NULL && b != NULL);
+	/*
+	 * lt_s_total is of type int64_t, so we can't simply return
+	 * (b->lt_se_data.lt_s_total - a->lt_se_data.lt_s_total).
+	 */
+	if (b->lt_se_data.lt_s_total > a->lt_se_data.lt_s_total) {
+		return (1);
+	} else if (b->lt_se_data.lt_s_total < a->lt_se_data.lt_s_total) {
+		return (-1);
+	} else {
+		return (0);
+	}
+}
+
+/*
+ * Helper function to sort on max.
+ */
+int
+lt_sort_by_max_desc(lt_stat_entry_t *a, lt_stat_entry_t *b)
+{
+	g_assert(a != NULL && b != NULL);
+
+	if (b->lt_se_data.lt_s_max > a->lt_se_data.lt_s_max) {
+		return (1);
+	} else if (b->lt_se_data.lt_s_max < a->lt_se_data.lt_s_max) {
+		return (-1);
+	} else {
+		return (0);
+	}
+}
+
+/*
+ * Helper function to sort on count.
+ */
+int
+lt_sort_by_count_desc(lt_stat_entry_t *a, lt_stat_entry_t *b)
+{
+	g_assert(a != NULL && b != NULL);
+
+	if (b->lt_se_data.lt_s_count > a->lt_se_data.lt_s_count) {
+		return (1);
+	} else if (b->lt_se_data.lt_s_count < a->lt_se_data.lt_s_count) {
+		return (-1);
+	} else {
+		return (0);
+	}
+}
+
+/*
+ * Helper function to sort on average.
+ */
+int
+lt_sort_by_avg_desc(lt_stat_entry_t *a, lt_stat_entry_t *b)
+{
+	double avg_a, avg_b;
+
+	g_assert(a != NULL && b != NULL);
+
+	avg_a = (double)a->lt_se_data.lt_s_total / a->lt_se_data.lt_s_count;
+	avg_b = (double)b->lt_se_data.lt_s_total / b->lt_se_data.lt_s_count;
+
+	if (avg_b > avg_a) {
+		return (1);
+	} else if (avg_b < avg_a) {
+		return (-1);
+	} else {
+		return (0);
+	}
+}
+
+/*
+ * Create pipe for signal handler and wakeup.
+ */
+void
+lt_gpipe_init(void)
+{
+	(void) pipe(signal_pipe);
+}
+
+/*
+ * Close the pipe used in signal handler.
+ */
+void
+lt_gpipe_deinit(void)
+{
+	(void) close(signal_pipe[0]);
+	(void) close(signal_pipe[1]);
+}
+
+/*
+ * Break early from the main loop.
+ */
+void
+lt_gpipe_break(const char *ch)
+{
+	(void) write(signal_pipe[1], ch, 1);
+}
+
+int
+lt_gpipe_readfd(void)
+{
+	return (signal_pipe[0]);
+}
+
+/*
+ * Check if the given file exists.
+ */
+int
+lt_file_exist(const char *name)
+{
+	struct stat64 st;
+
+	if (stat64(name, &st) == 0) {
+		return (1);
+	} else {
+		return (0);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/i386/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+include ../Makefile.com
+
+WRAPOPT =
+
+install: all $(ROOTPROG32)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/latencytop/sparcv9/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright (c) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+include ../Makefile.com
+include ../../Makefile.cmd.64
+
+WRAPOPT = -64
+
+install: all $(ROOTPROG64)
--- a/usr/src/cmd/mdb/Makefile.common	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/mdb/Makefile.common	Mon Oct 05 09:17:26 2009 -0400
@@ -77,6 +77,7 @@
 	nca \
 	nsctl \
 	nsmb \
+	pmcs \
 	ptm \
 	qlc \
 	random \
--- a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files	Mon Oct 05 09:17:26 2009 -0400
@@ -31,11 +31,13 @@
 GENUNIX_SRCS =		\
 	avl.c		\
 	bio.c		\
+	bitset.c	\
 	combined.c	\
 	contract.c	\
 	cpupart.c	\
 	ctxop.c		\
 	cyclic.c	\
+	damap.c		\
 	devinfo.c	\
 	dist.c		\
 	findstack.c	\
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/genunix/bitset.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,212 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <mdb/mdb_modapi.h>
+#include <sys/bitset.h>
+
+#include "bitset.h"		/* XXX work out ifdef in include file... */
+
+void
+bitset_help(void)
+{
+	mdb_printf("Print the bitset at the address given\n");
+}
+
+static void
+bitset_free(bitset_t *bs)
+{
+	if (bs == NULL)
+		return;
+	if (bs->bs_set && bs->bs_words)
+		mdb_free(bs->bs_set, bs->bs_words * sizeof (ulong_t));
+	mdb_free(bs, sizeof (*bs));
+}
+
+static bitset_t *
+bitset_get(uintptr_t bsaddr)
+{
+	bitset_t	*bs;
+
+	bs = mdb_zalloc(sizeof (*bs), UM_SLEEP);
+	if (mdb_vread(bs, sizeof (*bs), bsaddr) == -1) {
+		mdb_warn("couldn't read bitset 0x%p", bsaddr);
+		bitset_free(bs);
+		return (NULL);
+	}
+
+	bsaddr = (uintptr_t)bs->bs_set;
+	bs->bs_set = mdb_alloc(bs->bs_words * sizeof (ulong_t), UM_SLEEP);
+	if (mdb_vread(bs->bs_set,
+	    bs->bs_words * sizeof (ulong_t), bsaddr) == -1) {
+		mdb_warn("couldn't read bitset bs_set 0x%p", bsaddr);
+		bitset_free(bs);
+		return (NULL);
+	}
+	return (bs);
+
+}
+
+static int
+bitset_highbit(bitset_t *bs)
+{
+	int	high;
+	int	i;
+
+	if ((bs->bs_set == NULL) || (bs->bs_words == 0))
+		return (-1);
+
+	/* move backwards through words */
+	for (i = bs->bs_words; i >= 0; i--)
+		if (bs->bs_set[i])
+			break;
+	if (i < 0)
+		return (-1);
+
+	/* move backwards through bits */
+	high = i << BT_ULSHIFT;
+	for (i = BT_NBIPUL - 1; i; i--)
+		if (BT_TEST(bs->bs_set, high + i))
+			break;
+	return (high + i + 1);
+}
+
+static int
+pow10(int exp)
+{
+	int	res;
+
+	for (res = 1; exp; exp--)
+		res *= 10;
+	return (res);
+}
+
+static int
+log10(int val)
+{
+	int	res = 0;
+
+	do {
+		res++;
+		val /= 10;
+	} while (val);
+	return (res);
+}
+
+/*
+ * The following prints a bitset with a 'ruler' that look like this
+ *
+ *              11111111112222222222333333333344444444445555555555666666666677
+ *    012345678901234567890123456789012345678901234567890123456789012345678901
+ * xx:........................................................................
+ *                                11111111111111111111111111111111111111111111
+ *    777777778888888888999999999900000000001111111111222222222233333333334444
+ *    234567890123456789012345678901234567890123456789012345678901234567890123
+ *    ........................................................................
+ *    111111111111111111111111111111111111111111111111111111112222222222222222
+ *    444444555555555566666666667777777777888888888899999999990000000000111111
+ *    456789012345678901234567890123456789012345678901234567890123456789012345
+ *    ........................................................................
+ *    2222222222
+ *    1111222222
+ *    6789012345
+ *    ..........
+ *
+ * to identify individual bits that are set.
+ */
+static void
+bitset_print(bitset_t *bs, char *label, int width)
+{
+	int	val_start;
+	int	val_max;
+	int	label_width;
+	int	ruler_width;
+	int	v, vm, vi;
+	int	nl, l;
+	int	i;
+	int	p;
+	char	c;
+
+	val_start = 0;
+	val_max = bitset_highbit(bs) + 1;
+	if (val_max <= val_start) {
+		mdb_printf("%s: empty-set", label);
+		return;
+	}
+
+	label_width = strlen(label) + 1;
+	ruler_width = width - label_width;
+
+	for (v = val_start; v < val_max; v = vm) {
+		if ((v + ruler_width) < val_max)
+			vm = v + ruler_width;
+		else
+			vm = val_max;
+
+		nl = log10(vm) - 1;
+		for (l = nl; l >= 0; l--) {
+			p = pow10(l);
+			for (i = 0; i < label_width; i++)
+				mdb_printf(" ");
+
+			for (vi = v; vi < vm; vi++) {
+				c = '0' + ((vi / p) % 10);
+				if ((l == nl) && (c == '0'))
+					c = ' ';
+				mdb_printf("%c", c);
+			}
+
+			mdb_printf("\n");
+		}
+
+		if (v == val_start) {
+			mdb_printf("%s:", label);
+		} else {
+			for (i = 0; i < label_width; i++)
+				mdb_printf(" ");
+		}
+		for (vi = v; vi < vm; vi++) {
+			if (BT_TEST(bs->bs_set, vi))
+				mdb_printf("X");
+			else
+				mdb_printf(".");
+		}
+		mdb_printf("\n");
+	}
+}
+
+/*ARGSUSED*/
+int
+bitset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	bitset_t	*bs;
+
+	bs = bitset_get(addr);
+	if (bs == NULL)
+		return (DCMD_ERR);
+
+	bitset_print(bs, "label", 80);
+	bitset_free(bs);
+	return (DCMD_OK);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/genunix/bitset.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,43 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_MDB_BITSET_H
+#define	_MDB_BITSET_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <mdb/mdb_modapi.h>
+
+extern int bitset(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern void bitset_help(void);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _MDB_BITSET_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/genunix/damap.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,325 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <mdb/mdb_modapi.h>
+#include <sys/sysmacros.h>
+#include <sys/sunddi.h>
+#include <sys/damap_impl.h>
+
+#include "damap.h"
+
+void
+damap_help(void)
+{
+	mdb_printf("Print the damap at the address given.\n");
+	mdb_printf("\n");
+	mdb_printf("EXAMPLE: SCSI: To display the SCSI tgtmap damaps ");
+	mdb_printf("associated with a scsi HBA driver iport dip:\n");
+	mdb_printf("\n");
+	mdb_printf("::devbindings -q <driver_name>\n");
+	mdb_printf("\n");
+	mdb_printf("<iport-dip>::print struct dev_info devi_driver_data|");
+	mdb_printf("::print scsi_hba_tran_t tran_tgtmap|");
+	mdb_printf("::print impl_scsi_tgtmap_t ");
+	mdb_printf("tgtmap_dam[0] tgtmap_dam[1]|::damap\n");
+}
+
+static char *
+local_strdup(const char *s)
+{
+	if (s)
+		return (strcpy(mdb_alloc(strlen(s) + 1, UM_SLEEP), s));
+	else
+		return (NULL);
+}
+
+static void
+local_strfree(const char *s)
+{
+	if (s)
+		mdb_free((void *)s, strlen(s) + 1);
+}
+
+static void
+bitset_free(bitset_t *bs, int embedded)
+{
+	if (bs == NULL)
+		return;
+	if (bs->bs_set && bs->bs_words)
+		mdb_free(bs->bs_set, bs->bs_words * sizeof (ulong_t));
+	if (!embedded)
+		mdb_free(bs, sizeof (*bs));	/* not embedded, free */
+}
+
+static bitset_t *
+bitset_get(uintptr_t bsaddr)
+{
+	bitset_t	*bs;
+
+	bs = mdb_zalloc(sizeof (*bs), UM_SLEEP);
+	if (mdb_vread(bs, sizeof (*bs), bsaddr) == -1) {
+		mdb_warn("couldn't read bitset 0x%p", bsaddr);
+		bitset_free(bs, 0);
+		return (NULL);
+	}
+
+	bsaddr = (uintptr_t)bs->bs_set;
+	bs->bs_set = mdb_alloc(bs->bs_words * sizeof (ulong_t), UM_SLEEP);
+	if (mdb_vread(bs->bs_set,
+	    bs->bs_words * sizeof (ulong_t), bsaddr) == -1) {
+		mdb_warn("couldn't read bitset bs_set 0x%p", bsaddr);
+		bitset_free(bs, 0);
+		return (NULL);
+	}
+	return (bs);
+
+}
+
+static void
+damap_free(struct dam *dam, void **kdamda, int kdamda_n)
+{
+	int			i;
+	struct i_ddi_soft_state *ss;
+	dam_da_t		*da;
+
+	if (dam) {
+		/* free in dam_da_t softstate */
+		ss = (struct i_ddi_soft_state *)dam->dam_da;
+		if (ss) {
+			if (ss->n_items && ss->array) {
+				for (i = 0; i < ss->n_items; i++) {
+					da = ss->array[i];
+					if (da == NULL)
+						continue;
+					local_strfree(da->da_addr);
+					mdb_free(da, sizeof (*da));
+				}
+			}
+
+			mdb_free(ss, sizeof (*ss));
+		}
+
+		/* free dam_active/stable/report_set embedded in dam */
+		bitset_free(&dam->dam_report_set, 1);
+		bitset_free(&dam->dam_stable_set, 1);
+		bitset_free(&dam->dam_active_set, 1);
+
+		/* free dam_name */
+		local_strfree(dam->dam_name);
+
+		/* free dam */
+		mdb_free(dam, sizeof (*dam));
+	}
+
+	if (kdamda)
+		mdb_free(kdamda, kdamda_n * sizeof (void *));
+}
+
+/*
+ * The dam implementation uses a number of different abstractions. Given a
+ * pointer to a damap_t, this function make an mdb instantiation of the dam -
+ * many, but not all, of the different abstractions used in the dam
+ * implementation are also instantiated in mdb. This means that callers of
+ * damap_get can perform some (but not all) types of structure pointer
+ * traversals.
+ */
+struct dam *
+damap_get(uintptr_t damaddr, void ***pkdamda, int *pkdamda_n)
+{
+	/* variables that hold instantiation read from kernel */
+	struct dam		kdam;
+	char			kstring[MAXPATHLEN];
+	struct i_ddi_soft_state kss;
+	void			**kssarray = NULL;
+	int			array_sz = 0;
+
+	/* variables that hold mdb instantiation */
+	struct dam		*dam = NULL;
+	struct i_ddi_soft_state *ss;
+	bitset_t		*bs;
+	dam_da_t		*da;
+
+	int			i;
+
+	/* read kernel: dam */
+	if (mdb_vread(&kdam, sizeof (kdam), damaddr) == -1) {
+		mdb_warn("couldn't read dam 0x%p", damaddr);
+		goto err;
+	}
+
+	/* read kernel: dam->dam_name */
+	mdb_readstr(kstring, sizeof (kstring), (uintptr_t)kdam.dam_name);
+
+	/* read kernel: dam->dam_da (softstate) */
+	if (mdb_vread(&kss, sizeof (kss), (uintptr_t)kdam.dam_da) == -1) {
+		mdb_warn("couldn't read dam dam_da 0x%p",
+		    (uintptr_t)kdam.dam_da);
+		goto err;
+	}
+
+	/* read kernel ((struct i_ddi_soft_state *)dam->dam_da)->array */
+	array_sz = kss.n_items * sizeof (void *);
+	kssarray = mdb_alloc(array_sz, UM_SLEEP);
+	if (mdb_vread(kssarray, array_sz, (uintptr_t)kss.array) == -1) {
+		mdb_warn("couldn't read dam dam_da array 0x%p",
+		    (uintptr_t)kss.array);
+		goto err;
+	}
+
+	/*
+	 * Produce mdb instantiation of kernel data structures.
+	 *
+	 * Structure copy kdam to dam, then clear out pointers in dam (some
+	 * will be filled in by mdb instantiation code below).
+	 */
+	dam = mdb_zalloc(sizeof (*dam), UM_SLEEP);
+	*dam = kdam;
+	dam->dam_name = NULL;
+
+	dam->dam_active_set.bs_set = NULL;
+	dam->dam_stable_set.bs_set = NULL;
+	dam->dam_report_set.bs_set = NULL;
+
+	dam->dam_da = NULL;
+	/* dam_addr_hash, dam_taskqp, dam_kstatp left as kernel addresses */
+
+	/* fill in dam_name */
+	dam->dam_name = local_strdup(kstring);
+
+	/* fill in dam_active/stable/report_set embedded in the dam */
+	bs = bitset_get(damaddr + (offsetof(struct dam, dam_active_set)));
+	if (bs) {
+		dam->dam_active_set = *bs;
+		mdb_free(bs, sizeof (*bs));
+	}
+	bs = bitset_get(damaddr + (offsetof(struct dam, dam_stable_set)));
+	if (bs) {
+		dam->dam_stable_set = *bs;
+		mdb_free(bs, sizeof (*bs));
+	}
+	bs = bitset_get(damaddr + (offsetof(struct dam, dam_report_set)));
+	if (bs) {
+		dam->dam_report_set = *bs;
+		mdb_free(bs, sizeof (*bs));
+	}
+
+	/* fill in dam_da_t softstate */
+	ss = mdb_zalloc(sizeof (struct i_ddi_soft_state), UM_SLEEP);
+	*ss = kss;
+	ss->next = NULL;
+	ss->array = mdb_zalloc(array_sz, UM_SLEEP);
+	dam->dam_da = ss;
+	for (i = 0; i < kss.n_items; i++) {
+		if (kssarray[i] == NULL)
+			continue;
+		da = ss->array[i] = mdb_zalloc(sizeof (*da), UM_SLEEP);
+		if (mdb_vread(da, sizeof (*da), (uintptr_t)kssarray[i]) == -1) {
+			mdb_warn("couldn't read dam dam_da %d 0x%p", i,
+			    (uintptr_t)kss.array);
+			goto err;
+		}
+		/* da_nvl, da_ppriv_rpt, da_nvl_rpt left as kernel addresses */
+
+		/* read kernel: da->da_addr */
+		mdb_readstr(kstring, sizeof (kstring), (uintptr_t)da->da_addr);
+		da->da_addr = local_strdup(kstring);
+	}
+
+	/* return array of kernel dam_da_t pointers associated with each id */
+	*pkdamda = kssarray;
+	*pkdamda_n = array_sz / sizeof (void *);
+
+	/* return pointer to mdb instantiation of the dam */
+	return (dam);
+
+err:	damap_free(dam, kssarray, array_sz / sizeof (void *));
+	*pkdamda = NULL;
+	*pkdamda_n = 0;
+	return (NULL);
+}
+
+/*ARGSUSED*/
+static void
+damap_print(struct dam *dam, void **kdamda, int kdamda_n)
+{
+	struct i_ddi_soft_state	*ss;
+	dam_da_t		*da;
+	int			i;
+
+	mdb_printf("%s:\n", dam->dam_name);
+
+	ss = (struct i_ddi_soft_state *)dam->dam_da;
+	if (ss == NULL)
+		return;
+
+	if ((ss->n_items == 0) || (ss->array == NULL))
+		return;
+
+	for (i = 0; i < ss->n_items; i++) {
+		da = ss->array[i];
+		if (da == NULL)
+			continue;
+
+		/* Print index and address. */
+		mdb_printf("  %3d: %s [", i, da->da_addr);
+
+		/* Print shorthand of Active/Stable/Report set membership */
+		if (BT_TEST(dam->dam_active_set.bs_set, i))
+			mdb_printf("A");
+		else
+			mdb_printf(".");
+		if (BT_TEST(dam->dam_stable_set.bs_set, i))
+			mdb_printf("S");
+		else
+			mdb_printf(".");
+		if (BT_TEST(dam->dam_report_set.bs_set, i))
+			mdb_printf("R");
+		else
+			mdb_printf(".");
+
+		/* Print the reference count and priv */
+		mdb_printf("] %2d %0?lx %0?lx\n",
+		    da->da_ref, da->da_cfg_priv, da->da_ppriv);
+
+		mdb_printf("\t\t\t\t%p::print -ta dam_da_t\n", kdamda[i]);
+	}
+}
+
+/*ARGSUSED*/
+int
+damap(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	struct dam	*dam;
+	void		**kdamda;
+	int		kdamda_n;
+
+	dam = damap_get(addr, &kdamda, &kdamda_n);
+	if (dam == NULL)
+		return (DCMD_ERR);
+
+	damap_print(dam, kdamda, kdamda_n);
+	damap_free(dam, kdamda, kdamda_n);
+	return (DCMD_OK);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/genunix/damap.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,43 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_MDB_DAMAP_H
+#define	_MDB_DAMAP_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <mdb/mdb_modapi.h>
+
+extern int damap(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern void damap_help(void);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _MDB_DAMAP_H */
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c	Mon Oct 05 09:17:26 2009 -0400
@@ -68,43 +68,45 @@
 #include <sys/port_impl.h>
 
 #include "avl.h"
+#include "bio.h"
+#include "bitset.h"
 #include "combined.h"
 #include "contract.h"
 #include "cpupart_mdb.h"
+#include "ctxop.h"
+#include "cyclic.h"
+#include "damap.h"
 #include "devinfo.h"
+#include "findstack.h"
+#include "fm.h"
+#include "group.h"
 #include "irm.h"
+#include "kgrep.h"
+#include "kmem.h"
+#include "ldi.h"
 #include "leaky.h"
 #include "lgrp.h"
-#include "pg.h"
-#include "group.h"
 #include "list.h"
 #include "log.h"
-#include "kgrep.h"
-#include "kmem.h"
-#include "bio.h"
-#include "streams.h"
-#include "cyclic.h"
-#include "findstack.h"
+#include "mdi.h"
+#include "memory.h"
+#include "mmd.h"
+#include "modhash.h"
 #include "ndievents.h"
-#include "mmd.h"
 #include "net.h"
 #include "netstack.h"
 #include "nvpair.h"
-#include "ctxop.h"
-#include "tsd.h"
+#include "pg.h"
+#include "rctl.h"
+#include "sobj.h"
+#include "streams.h"
+#include "sysevent.h"
 #include "thread.h"
-#include "memory.h"
-#include "sobj.h"
-#include "sysevent.h"
-#include "rctl.h"
+#include "tsd.h"
 #include "tsol.h"
 #include "typegraph.h"
-#include "ldi.h"
 #include "vfs.h"
 #include "zone.h"
-#include "modhash.h"
-#include "mdi.h"
-#include "fm.h"
 
 /*
  * Surely this is defined somewhere...
@@ -4329,14 +4331,12 @@
 	{ "whereopen", ":", "given a vnode, dumps procs which have it open",
 	    whereopen },
 
-	/* from zone.c */
-	{ "zone", "?", "display kernel zone(s)", zoneprt },
-	{ "zsd", ":[-v] [zsd_key]", "display zone-specific-data entries for "
-	    "selected zones", zsd },
-
 	/* from bio.c */
 	{ "bufpagefind", ":addr", "find page_t on buf_t list", bufpagefind },
 
+	/* from bitset.c */
+	{ "bitset", ":", "display a bitset", bitset, bitset_help },
+
 	/* from contract.c */
 	{ "contract", "?", "display a contract", cmd_contract },
 	{ "ctevent", ":", "display a contract event", cmd_ctevent },
@@ -4352,6 +4352,9 @@
 	{ "cyclic", ":", "developer information", cyclic },
 	{ "cyctrace", "?", "dump cyclic trace buffer", cyctrace },
 
+	/* from damap.c */
+	{ "damap", ":", "display a damap_t", damap, damap_help },
+
 	/* from devinfo.c */
 	{ "devbindings", "?[-qs] [device-name | major-num]",
 	    "print devinfo nodes bound to device-name or major-num",
@@ -4402,6 +4405,9 @@
 		"print unique kernel thread stacks",
 		stacks, stacks_help },
 
+	/* from group.c */
+	{ "group", "?[-q]", "display a group", group},
+
 	/* from irm.c */
 	{ "irmpools", NULL, "display interrupt pools", irmpools_dcmd },
 	{ "irmreqs", NULL, "display interrupt requests in an interrupt pool",
@@ -4458,6 +4464,22 @@
 	/* from log.c */
 	{ "msgbuf", "?[-v]", "print most recent console messages", msgbuf },
 
+	/* from mdi.c */
+	{ "mdipi", NULL, "given a path, dump mdi_pathinfo "
+		"and detailed pi_prop list", mdipi },
+	{ "mdiprops", NULL, "given a pi_prop, dump the pi_prop list",
+		mdiprops },
+	{ "mdiphci", NULL, "given a phci, dump mdi_phci and "
+		"list all paths", mdiphci },
+	{ "mdivhci", NULL, "given a vhci, dump mdi_vhci and list "
+		"all phcis", mdivhci },
+	{ "mdiclient_paths", NULL, "given a path, walk mdi_pathinfo "
+		"client links", mdiclient_paths },
+	{ "mdiphci_paths", NULL, "given a path, walk through mdi_pathinfo "
+		"phci links", mdiphci_paths },
+	{ "mdiphcis", NULL, "given a phci, walk through mdi_phci ph_next links",
+		mdiphcis },
+
 	/* from memory.c */
 	{ "page", "?", "display a summarized page_t", page },
 	{ "memstat", NULL, "display memory usage summary", memstat },
@@ -4507,10 +4529,7 @@
 
 	/* from pg.c */
 	{ "pg", "?[-q]", "display a pg", pg},
-	/* from group.c */
-	{ "group", "?[-q]", "display a group", group},
-
-	/* from log.c */
+
 	/* from rctl.c */
 	{ "rctl_dict", "?", "print systemwide default rctl definitions",
 		rctl_dict },
@@ -4585,21 +4604,10 @@
 	{ "pfiles", ":[-fp]", "print process file information", pfiles,
 		pfiles_help },
 
-	/* from mdi.c */
-	{ "mdipi", NULL, "given a path, dump mdi_pathinfo "
-		"and detailed pi_prop list", mdipi },
-	{ "mdiprops", NULL, "given a pi_prop, dump the pi_prop list",
-		mdiprops },
-	{ "mdiphci", NULL, "given a phci, dump mdi_phci and "
-		"list all paths", mdiphci },
-	{ "mdivhci", NULL, "given a vhci, dump mdi_vhci and list "
-		"all phcis", mdivhci },
-	{ "mdiclient_paths", NULL, "given a path, walk mdi_pathinfo "
-		"client links", mdiclient_paths },
-	{ "mdiphci_paths", NULL, "given a path, walk through mdi_pathinfo "
-		"phci links", mdiphci_paths },
-	{ "mdiphcis", NULL, "given a phci, walk through mdi_phci ph_next links",
-		mdiphcis },
+	/* from zone.c */
+	{ "zone", "?", "display kernel zone(s)", zoneprt },
+	{ "zsd", ":[-v] [zsd_key]", "display zone-specific-data entries for "
+	    "selected zones", zsd },
 
 	{ NULL }
 };
@@ -4671,12 +4679,6 @@
 	{ AVL_WALK_NAME, AVL_WALK_DESC,
 		avl_walk_init, avl_walk_step, avl_walk_fini },
 
-	/* from zone.c */
-	{ "zone", "walk a list of kernel zones",
-		zone_walk_init, zone_walk_step, NULL },
-	{ "zsd", "walk list of zsd entries for a zone",
-		zsd_walk_init, zsd_walk_step, NULL },
-
 	/* from bio.c */
 	{ "buf", "walk the bio buf hash",
 		buf_walk_init, buf_walk_step, buf_walk_fini },
@@ -4745,6 +4747,10 @@
 		"walk a fault management handle cache active list",
 		devinfo_fmc_walk_init, devinfo_fmc_walk_step, NULL },
 
+	/* from group.c */
+	{ "group", "walk all elements of a group",
+		group_walk_init, group_walk_step, NULL },
+
 	/* from irm.c */
 	{ "irmpools", "walk global list of interrupt pools",
 	    irmpools_walk_init, list_walk_step, list_walk_fini },
@@ -4822,14 +4828,24 @@
 	{ "lgrp_rsrc_cpu", "walk lgroup CPU resources of given lgroup",
 		lgrp_rsrc_cpu_walk_init, lgrp_set_walk_step, NULL },
 
-	/* from group.c */
-	{ "group", "walk all elements of a group",
-		group_walk_init, group_walk_step, NULL },
-
 	/* from list.c */
 	{ LIST_WALK_NAME, LIST_WALK_DESC,
 		list_walk_init, list_walk_step, list_walk_fini },
 
+	/* from mdi.c */
+	{ "mdipi_client_list", "Walker for mdi_pathinfo pi_client_link",
+		mdi_pi_client_link_walk_init,
+		mdi_pi_client_link_walk_step,
+		mdi_pi_client_link_walk_fini },
+	{ "mdipi_phci_list", "Walker for mdi_pathinfo pi_phci_link",
+		mdi_pi_phci_link_walk_init,
+		mdi_pi_phci_link_walk_step,
+		mdi_pi_phci_link_walk_fini },
+	{ "mdiphci_list", "Walker for mdi_phci ph_next link",
+		mdi_phci_ph_next_walk_init,
+		mdi_phci_ph_next_walk_step,
+		mdi_phci_ph_next_walk_fini },
+
 	/* from memory.c */
 	{ "page", "walk all pages, or those from the specified vnode",
 		page_walk_init, page_walk_step, page_walk_fini },
@@ -4875,6 +4891,10 @@
 	{ "udp_stacks", "walk all the udp_stack_t",
 		udp_stacks_walk_init, udp_stacks_walk_step, NULL },
 
+	/* from netstack.c */
+	{ "netstack", "walk a list of kernel netstacks",
+		netstack_walk_init, netstack_walk_step, NULL },
+
 	/* from nvpair.c */
 	{ NVPAIR_WALKER_NAME, NVPAIR_WALKER_DESCR,
 		nvpair_walk_init, nvpair_walk_step, NULL },
@@ -4950,25 +4970,11 @@
 	{ "vfs", "walk file system list",
 		vfs_walk_init, vfs_walk_step },
 
-	/* from mdi.c */
-	{ "mdipi_client_list", "Walker for mdi_pathinfo pi_client_link",
-		mdi_pi_client_link_walk_init,
-		mdi_pi_client_link_walk_step,
-		mdi_pi_client_link_walk_fini },
-
-	{ "mdipi_phci_list", "Walker for mdi_pathinfo pi_phci_link",
-		mdi_pi_phci_link_walk_init,
-		mdi_pi_phci_link_walk_step,
-		mdi_pi_phci_link_walk_fini },
-
-	{ "mdiphci_list", "Walker for mdi_phci ph_next link",
-		mdi_phci_ph_next_walk_init,
-		mdi_phci_ph_next_walk_step,
-		mdi_phci_ph_next_walk_fini },
-
-	/* from netstack.c */
-	{ "netstack", "walk a list of kernel netstacks",
-		netstack_walk_init, netstack_walk_step, NULL },
+	/* from zone.c */
+	{ "zone", "walk a list of kernel zones",
+		zone_walk_init, zone_walk_step, NULL },
+	{ "zsd", "walk list of zsd entries for a zone",
+		zsd_walk_init, zsd_walk_step, NULL },
 
 	{ NULL }
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,1776 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <limits.h>
+#include <sys/mdb_modapi.h>
+#include <sys/sysinfo.h>
+#include <sys/scsi/scsi.h>
+#include <sys/scsi/adapters/pmcs/pmcs.h>
+
+#define	MDB_RD(a, b, c)	mdb_vread(a, b, (uintptr_t)c)
+#define	NOREAD(a, b)	mdb_warn("could not read " #a " at 0x%p", b)
+
+static pmcs_hw_t ss;
+static pmcs_xscsi_t **targets = NULL;
+static int target_idx;
+
+static uint32_t	sas_phys, sata_phys, exp_phys, num_expanders, empty_phys;
+
+static pmcs_phy_t *pmcs_next_sibling(pmcs_phy_t *phyp);
+
+static void
+print_sas_address(pmcs_phy_t *phy)
+{
+	int idx;
+
+	for (idx = 0; idx < 8; idx++) {
+		mdb_printf("%02x", phy->sas_address[idx]);
+	}
+}
+
+/*ARGSUSED*/
+static void
+display_ic(struct pmcs_hw m, int verbose)
+{
+	int msec_per_tick;
+
+	if (mdb_readvar(&msec_per_tick, "msec_per_tick") == -1) {
+		mdb_warn("can't read msec_per_tick");
+		msec_per_tick = 0;
+	}
+
+	mdb_printf("\n");
+	mdb_printf("Interrupt coalescing timer info\n");
+	mdb_printf("-------------------------------\n");
+	if (msec_per_tick == 0) {
+		mdb_printf("Quantum                       : ?? ms\n");
+	} else {
+		mdb_printf("Quantum                       : %d ms\n",
+		    m.io_intr_coal.quantum * msec_per_tick);
+	}
+	mdb_printf("Timer enabled                 : ");
+	if (m.io_intr_coal.timer_on) {
+		mdb_printf("Yes\n");
+		mdb_printf("Coalescing timer value        : %d us\n",
+		    m.io_intr_coal.intr_coal_timer);
+	} else {
+		mdb_printf("No\n");
+	}
+	mdb_printf("Total nsecs between interrupts: %ld\n",
+	    m.io_intr_coal.nsecs_between_intrs);
+	mdb_printf("Time of last I/O interrupt    : %ld\n",
+	    m.io_intr_coal.last_io_comp);
+	mdb_printf("Number of I/O interrupts      : %d\n",
+	    m.io_intr_coal.num_intrs);
+	mdb_printf("Number of I/O completions     : %d\n",
+	    m.io_intr_coal.num_io_completions);
+	mdb_printf("Max I/O completion interrupts : %d\n",
+	    m.io_intr_coal.max_io_completions);
+	mdb_printf("Measured ECHO int latency     : %d ns\n",
+	    m.io_intr_coal.intr_latency);
+	mdb_printf("Interrupt threshold           : %d\n",
+	    m.io_intr_coal.intr_threshold);
+}
+
+/*ARGSUSED*/
+static int
+pmcs_iport_phy_walk_cb(uintptr_t addr, const void *wdata, void *priv)
+{
+	struct pmcs_phy		phy;
+
+	if (mdb_vread(&phy, sizeof (struct pmcs_phy), addr) !=
+	    sizeof (struct pmcs_phy)) {
+		return (DCMD_ERR);
+	}
+
+	mdb_printf("%16p %2d\n", addr, phy.phynum);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+pmcs_iport_walk_cb(uintptr_t addr, const void *wdata, void *priv)
+{
+	struct pmcs_iport	iport;
+	uintptr_t		list_addr;
+	char			*ua_state;
+	char			portid[4];
+	char			unit_address[34];
+
+	if (mdb_vread(&iport, sizeof (struct pmcs_iport), addr) !=
+	    sizeof (struct pmcs_iport)) {
+		return (DCMD_ERR);
+	}
+
+	if (mdb_readstr(unit_address, sizeof (unit_address),
+	    (uintptr_t)(iport.ua)) == -1) {
+		strncpy(unit_address, "Unset", sizeof (unit_address));
+	}
+
+	if (iport.portid == 0xffff) {
+		mdb_snprintf(portid, sizeof (portid), "%s", "-");
+	} else {
+		mdb_snprintf(portid, sizeof (portid), "%d", iport.portid);
+	}
+
+	switch (iport.ua_state) {
+	case UA_INACTIVE:
+		ua_state = "Inactive";
+		break;
+	case UA_PEND_ACTIVATE:
+		ua_state = "PendActivate";
+		break;
+	case UA_ACTIVE:
+		ua_state = "Active";
+		break;
+	case UA_PEND_DEACTIVATE:
+		ua_state = "PendDeactivate";
+		break;
+	default:
+		ua_state = "Unknown";
+	}
+
+	if (strlen(unit_address) < 3) {
+		/* Standard iport unit address */
+		mdb_printf("UA %-16s %16s %8s %8s %16s", "Iport", "UA State",
+		    "PortID", "NumPhys", "DIP\n");
+		mdb_printf("%2s %16p %16s %8s %8d %16p\n", unit_address, addr,
+		    ua_state, portid, iport.nphy, iport.dip);
+	} else {
+		/* Temporary iport unit address */
+		mdb_printf("%-32s %16s %20s %8s %8s %16s", "UA", "Iport",
+		    "UA State", "PortID", "NumPhys", "DIP\n");
+		mdb_printf("%32s %16p %20s %8s %8d %16p\n", unit_address, addr,
+		    ua_state, portid, iport.nphy, iport.dip);
+	}
+
+	if (iport.nphy > 0) {
+		mdb_inc_indent(4);
+		mdb_printf("%-18s %8s", "Phy", "PhyNum\n");
+		mdb_inc_indent(2);
+		list_addr =
+		    (uintptr_t)(addr + offsetof(struct pmcs_iport, phys));
+		if (mdb_pwalk("list", pmcs_iport_phy_walk_cb, NULL,
+		    list_addr) == -1) {
+			mdb_warn("pmcs iport walk failed");
+		}
+		mdb_dec_indent(6);
+		mdb_printf("\n");
+	}
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static void
+display_iport(struct pmcs_hw m, uintptr_t addr, int verbose)
+{
+	uintptr_t	list_addr;
+
+	if (m.iports_attached) {
+		mdb_printf("Iport information:\n");
+		mdb_printf("-----------------\n");
+	} else {
+		mdb_printf("No Iports found.\n\n");
+		return;
+	}
+
+	list_addr = (uintptr_t)(addr + offsetof(struct pmcs_hw, iports));
+
+	if (mdb_pwalk("list", pmcs_iport_walk_cb, NULL, list_addr) == -1) {
+		mdb_warn("pmcs iport walk failed");
+	}
+
+	mdb_printf("\n");
+}
+
+/*ARGSUSED*/
+static void
+display_hwinfo(struct pmcs_hw m, int verbose)
+{
+	struct pmcs_hw	*mp = &m;
+	char		*fwsupport;
+
+	switch (PMCS_FW_TYPE(mp)) {
+	case PMCS_FW_TYPE_RELEASED:
+		fwsupport = "Released";
+		break;
+	case PMCS_FW_TYPE_DEVELOPMENT:
+		fwsupport = "Development";
+		break;
+	case PMCS_FW_TYPE_ALPHA:
+		fwsupport = "Alpha";
+		break;
+	case PMCS_FW_TYPE_BETA:
+		fwsupport = "Beta";
+		break;
+	default:
+		fwsupport = "Special";
+		break;
+	}
+
+	mdb_printf("\nHardware information:\n");
+	mdb_printf("---------------------\n");
+
+	mdb_printf("Chip revision:    %c\n", 'A' + m.chiprev);
+	mdb_printf("SAS WWID:         %"PRIx64"\n", m.sas_wwns[0]);
+	mdb_printf("Firmware version: %x.%x.%x (%s)\n",
+	    PMCS_FW_MAJOR(mp), PMCS_FW_MINOR(mp), PMCS_FW_MICRO(mp),
+	    fwsupport);
+
+	mdb_printf("Number of PHYs:   %d\n", m.nphy);
+	mdb_printf("Maximum commands: %d\n", m.max_cmd);
+	mdb_printf("Maximum devices:  %d\n", m.max_dev);
+	mdb_printf("I/O queue depth:  %d\n", m.ioq_depth);
+	if (m.fwlog == 0) {
+		mdb_printf("Firmware logging: Disabled\n");
+	} else {
+		mdb_printf("Firmware logging: Enabled (%d)\n", m.fwlog);
+	}
+}
+
+static void
+display_targets(struct pmcs_hw m, int verbose, int totals_only)
+{
+	char		*dtype;
+	pmcs_xscsi_t	xs;
+	pmcs_phy_t	phy;
+	uint16_t	max_dev, idx;
+	uint32_t	sas_targets = 0, smp_targets = 0, sata_targets = 0;
+
+	max_dev = m.max_dev;
+
+	if (targets == NULL) {
+		targets = mdb_alloc(sizeof (targets) * max_dev, UM_SLEEP);
+	}
+
+	if (MDB_RD(targets, sizeof (targets) * max_dev, m.targets) == -1) {
+		NOREAD(targets, m.targets);
+		return;
+	}
+
+	if (!totals_only) {
+		mdb_printf("\nTarget information:\n");
+		mdb_printf("---------------------------------------\n");
+		mdb_printf("VTGT %-16s %-16s %-5s %8s %s", "SAS Address",
+		    "PHY Address", "DType", "Active", "DS");
+		mdb_printf("\n");
+	}
+
+	for (idx = 0; idx < max_dev; idx++) {
+		if (targets[idx] == NULL) {
+			continue;
+		}
+
+		if (MDB_RD(&xs, sizeof (xs), targets[idx]) == -1) {
+			NOREAD(pmcs_xscsi_t, targets[idx]);
+			continue;
+		}
+
+		/*
+		 * It has to be one of new, assigned or dying to be of interest.
+		 */
+		if (xs.new == 0 && xs.assigned == 0 && xs.dying == 0) {
+			continue;
+		}
+
+		switch (xs.dtype) {
+		case NOTHING:
+			dtype = "None";
+			break;
+		case SATA:
+			dtype = "SATA";
+			sata_targets++;
+			break;
+		case SAS:
+			dtype = "SAS";
+			sas_targets++;
+			break;
+		case EXPANDER:
+			dtype = "SMP";
+			smp_targets++;
+			break;
+		}
+
+		if (totals_only) {
+			continue;
+		}
+
+		if (xs.phy) {
+			if (MDB_RD(&phy, sizeof (phy), xs.phy) == -1) {
+				NOREAD(pmcs_phy_t, xs.phy);
+				continue;
+			}
+			mdb_printf("%4d ", idx);
+			print_sas_address(&phy);
+			mdb_printf(" %16p", xs.phy);
+		} else {
+			mdb_printf("%4d %16s", idx, "<no phy avail>");
+		}
+		mdb_printf(" %5s", dtype);
+		mdb_printf(" %8d", xs.actv_cnt);
+		mdb_printf(" %2d", xs.dev_state);
+
+		if (verbose) {
+			if (xs.new) {
+				mdb_printf(" new");
+			} else if (xs.dying) {
+				mdb_printf(" dying");
+			} else if (xs.assigned) {
+				mdb_printf(" assigned");
+			}
+			if (xs.draining) {
+				mdb_printf(" draining");
+			}
+			if (xs.reset_wait) {
+				mdb_printf(" reset_wait");
+			}
+			if (xs.resetting) {
+				mdb_printf(" resetting");
+			}
+			if (xs.recover_wait) {
+				mdb_printf(" recover_wait");
+			}
+			if (xs.recovering) {
+				mdb_printf(" recovering");
+			}
+			if (xs.event_recovery) {
+				mdb_printf(" event recovery");
+			}
+			if (xs.special_running) {
+				mdb_printf(" special_active");
+			}
+			if (xs.ncq) {
+				mdb_printf(" ncq_tagmap=0x%x qdepth=%d",
+				    xs.tagmap, xs.qdepth);
+			} else if (xs.pio) {
+				mdb_printf(" pio");
+			}
+		}
+
+		mdb_printf("\n");
+	}
+
+	if (!totals_only) {
+		mdb_printf("\n");
+	}
+
+	mdb_printf("%19s %d (%d SAS + %d SATA + %d SMP)\n",
+	    "Configured targets:", (sas_targets + sata_targets + smp_targets),
+	    sas_targets, sata_targets, smp_targets);
+}
+
+/*ARGSUSED1*/
+static void
+display_work(struct pmcs_hw m, int verbose)
+{
+	int		idx;
+	int		tgt;
+	int		hdrp =  0;
+	pmcs_xscsi_t	xs;
+	pmcs_phy_t	phy;
+	char		buf[16];
+	pmcwork_t	work, *wp = &work;
+	uintptr_t	_wp;
+	char		*state;
+	char		*path;
+
+	mdb_printf("\nActive Work structure information:\n");
+	mdb_printf("----------------------------------\n");
+
+	_wp = (uintptr_t)m.work;
+
+	for (idx = 0; idx < m.max_cmd; idx++, _wp += sizeof (pmcwork_t)) {
+		if (MDB_RD(&work, sizeof (pmcwork_t), _wp) == -1) {
+			NOREAD(pmcwork_t, _wp);
+			continue;
+		}
+		if (wp->htag == PMCS_TAG_TYPE_FREE) {
+			continue;
+		}
+		if (hdrp++ == 0) {
+			mdb_printf("%8s %10s %20s %8s %8s O D\n",
+			    "HTag", "State", "Phy Path", "Target", "Timer");
+		}
+		switch (wp->state) {
+		case PMCS_WORK_STATE_NIL:
+			state = "N/A";
+			break;
+		case PMCS_WORK_STATE_READY:
+			state = "Ready";
+			break;
+		case PMCS_WORK_STATE_ONCHIP:
+			state = "On Chip";
+			break;
+		case PMCS_WORK_STATE_INTR:
+			state = "In Intr";
+			break;
+		case PMCS_WORK_STATE_IOCOMPQ:
+			state = "I/O Comp";
+			break;
+		case PMCS_WORK_STATE_ABORTED:
+			state = "I/O Aborted";
+			break;
+		case PMCS_WORK_STATE_TIMED_OUT:
+			state = "I/O Timed Out";
+			break;
+		default:
+			mdb_snprintf(buf, sizeof (buf), "STATE=%d", wp->state);
+			state = buf;
+			break;
+		}
+		if (wp->ssp_event && wp->ssp_event != 0xffffffff) {
+			mdb_printf("SSP event 0x%x", wp->ssp_event);
+		}
+		tgt = -1;
+		if (wp->xp) {
+			if (MDB_RD(&xs, sizeof (xs), wp->xp) == -1) {
+				NOREAD(pmcs_xscsi_t, wp->xp);
+			} else {
+				tgt = xs.target_num;
+			}
+		}
+		if (wp->phy) {
+			if (MDB_RD(&phy, sizeof (phy), wp->phy) == -1) {
+				NOREAD(pmcs_phy_t, wp->phy);
+				continue;
+			}
+			path = phy.path;
+		} else {
+			path = "????";
+		}
+		mdb_printf("%08x %10s %20s %8d %8u %1d %1d\n",
+		    wp->htag, state, path, tgt, wp->timer,
+		    wp->onwire, wp->dead);
+	}
+}
+
+static void
+print_spcmd(pmcs_cmd_t *sp, void *kaddr, int printhdr, int indent)
+{
+	if (indent)
+		mdb_inc_indent(4);
+	if (printhdr) {
+		mdb_printf("%16s %16s %16s %8s %s\n",
+		    "Command", "SCSA pkt", "DMA Chunks", "HTAG", "SATL Tag");
+	}
+	mdb_printf("%16p %16p %16p %08x %08x\n",
+	    kaddr, sp->cmd_pkt, sp->cmd_clist, sp->cmd_tag, sp->cmd_satltag);
+	if (indent)
+		mdb_dec_indent(4);
+}
+
+/*ARGSUSED1*/
+static void
+display_waitqs(struct pmcs_hw m, int verbose)
+{
+	pmcs_cmd_t	*sp, s;
+	pmcs_xscsi_t	xs;
+	int		first, i;
+	int		max_dev = m.max_dev;
+
+	sp = m.dq.stqh_first;
+	first = 1;
+	while (sp) {
+		if (first) {
+			mdb_printf("\nDead Command Queue:\n");
+			mdb_printf("---------------------------\n");
+		}
+		if (MDB_RD(&s, sizeof (s), sp) == -1) {
+			NOREAD(pmcs_cmd_t, sp);
+			break;
+		}
+		print_spcmd(&s, sp, first, 0);
+		sp = s.cmd_next.stqe_next;
+		first = 0;
+	}
+
+	sp = m.cq.stqh_first;
+	first = 1;
+	while (sp) {
+		if (first) {
+			mdb_printf("\nCompletion Command Queue:\n");
+			mdb_printf("---------------------------\n");
+		}
+		if (MDB_RD(&s, sizeof (s), sp) == -1) {
+			NOREAD(pmcs_cmd_t, sp);
+			break;
+		}
+		print_spcmd(&s, sp, first, 0);
+		sp = s.cmd_next.stqe_next;
+		first = 0;
+	}
+
+
+	if (targets == NULL) {
+		targets = mdb_alloc(sizeof (targets) * max_dev, UM_SLEEP);
+	}
+
+	if (MDB_RD(targets, sizeof (targets) * max_dev, m.targets) == -1) {
+		NOREAD(targets, m.targets);
+		return;
+	}
+
+	for (i = 0; i < max_dev; i++) {
+		if (targets[i] == NULL) {
+			continue;
+		}
+		if (MDB_RD(&xs, sizeof (xs), targets[i]) == -1) {
+			NOREAD(pmcs_xscsi_t, targets[i]);
+			continue;
+		}
+		sp = xs.wq.stqh_first;
+		first = 1;
+		while (sp) {
+			if (first) {
+				mdb_printf("\nTarget %u Wait Queue:\n",
+				    xs.target_num);
+				mdb_printf("---------------------------\n");
+			}
+			if (MDB_RD(&s, sizeof (s), sp) == -1) {
+				NOREAD(pmcs_cmd_t, sp);
+				break;
+			}
+			print_spcmd(&s, sp, first, 0);
+			sp = s.cmd_next.stqe_next;
+			first = 0;
+		}
+		sp = xs.aq.stqh_first;
+		first = 1;
+		while (sp) {
+			if (first) {
+				mdb_printf("\nTarget %u Active Queue:\n",
+				    xs.target_num);
+				mdb_printf("---------------------------\n");
+			}
+			if (MDB_RD(&s, sizeof (s), sp) == -1) {
+				NOREAD(pmcs_cmd_t, sp);
+				break;
+			}
+			print_spcmd(&s, sp, first, 0);
+			sp = s.cmd_next.stqe_next;
+			first = 0;
+		}
+		sp = xs.sq.stqh_first;
+		first = 1;
+		while (sp) {
+			if (first) {
+				mdb_printf("\nTarget %u Special Queue:\n",
+				    xs.target_num);
+				mdb_printf("---------------------------\n");
+			}
+			if (MDB_RD(&s, sizeof (s), sp) == -1) {
+				NOREAD(pmcs_cmd_t, sp);
+				break;
+			}
+			print_spcmd(&s, sp, first, 0);
+			sp = s.cmd_next.stqe_next;
+			first = 0;
+		}
+	}
+}
+
+static char *
+ibq_type(int qnum)
+{
+	if (qnum < 0 || qnum >= PMCS_NIQ) {
+		return ("UNKNOWN");
+	}
+
+	if (qnum < PMCS_IQ_OTHER) {
+		return ("I/O");
+	}
+
+	return ("Other");
+}
+
+static char *
+obq_type(int qnum)
+{
+	switch (qnum) {
+	case PMCS_OQ_IODONE:
+		return ("I/O");
+		break;
+	case PMCS_OQ_GENERAL:
+		return ("General");
+		break;
+	case PMCS_OQ_EVENTS:
+		return ("Events");
+		break;
+	default:
+		return ("UNKNOWN");
+	}
+}
+
+static char *
+iomb_cat(uint32_t cat)
+{
+	switch (cat) {
+	case PMCS_IOMB_CAT_NET:
+		return ("NET");
+		break;
+	case PMCS_IOMB_CAT_FC:
+		return ("FC");
+		break;
+	case PMCS_IOMB_CAT_SAS:
+		return ("SAS");
+		break;
+	case PMCS_IOMB_CAT_SCSI:
+		return ("SCSI");
+		break;
+	default:
+		return ("???");
+	}
+}
+
+static char *
+inbound_iomb_opcode(uint32_t opcode)
+{
+	switch (opcode) {
+	case PMCIN_ECHO:
+		return ("ECHO");
+		break;
+	case PMCIN_GET_INFO:
+		return ("GET_INFO");
+		break;
+	case PMCIN_GET_VPD:
+		return ("GET_VPD");
+		break;
+	case PMCIN_PHY_START:
+		return ("PHY_START");
+		break;
+	case PMCIN_PHY_STOP:
+		return ("PHY_STOP");
+		break;
+	case PMCIN_SSP_INI_IO_START:
+		return ("INI_IO_START");
+		break;
+	case PMCIN_SSP_INI_TM_START:
+		return ("INI_TM_START");
+		break;
+	case PMCIN_SSP_INI_EXT_IO_START:
+		return ("INI_EXT_IO_START");
+		break;
+	case PMCIN_DEVICE_HANDLE_ACCEPT:
+		return ("DEVICE_HANDLE_ACCEPT");
+		break;
+	case PMCIN_SSP_TGT_IO_START:
+		return ("TGT_IO_START");
+		break;
+	case PMCIN_SSP_TGT_RESPONSE_START:
+		return ("TGT_RESPONSE_START");
+		break;
+	case PMCIN_SSP_INI_EDC_EXT_IO_START:
+		return ("INI_EDC_EXT_IO_START");
+		break;
+	case PMCIN_SSP_INI_EDC_EXT_IO_START1:
+		return ("INI_EDC_EXT_IO_START1");
+		break;
+	case PMCIN_SSP_TGT_EDC_IO_START:
+		return ("TGT_EDC_IO_START");
+		break;
+	case PMCIN_SSP_ABORT:
+		return ("SSP_ABORT");
+		break;
+	case PMCIN_DEREGISTER_DEVICE_HANDLE:
+		return ("DEREGISTER_DEVICE_HANDLE");
+		break;
+	case PMCIN_GET_DEVICE_HANDLE:
+		return ("GET_DEVICE_HANDLE");
+		break;
+	case PMCIN_SMP_REQUEST:
+		return ("SMP_REQUEST");
+		break;
+	case PMCIN_SMP_RESPONSE:
+		return ("SMP_RESPONSE");
+		break;
+	case PMCIN_SMP_ABORT:
+		return ("SMP_ABORT");
+		break;
+	case PMCIN_ASSISTED_DISCOVERY:
+		return ("ASSISTED_DISCOVERY");
+		break;
+	case PMCIN_REGISTER_DEVICE:
+		return ("REGISTER_DEVICE");
+		break;
+	case PMCIN_SATA_HOST_IO_START:
+		return ("SATA_HOST_IO_START");
+		break;
+	case PMCIN_SATA_ABORT:
+		return ("SATA_ABORT");
+		break;
+	case PMCIN_LOCAL_PHY_CONTROL:
+		return ("LOCAL_PHY_CONTROL");
+		break;
+	case PMCIN_GET_DEVICE_INFO:
+		return ("GET_DEVICE_INFO");
+		break;
+	case PMCIN_TWI:
+		return ("TWI");
+		break;
+	case PMCIN_FW_FLASH_UPDATE:
+		return ("FW_FLASH_UPDATE");
+		break;
+	case PMCIN_SET_VPD:
+		return ("SET_VPD");
+		break;
+	case PMCIN_GPIO:
+		return ("GPIO");
+		break;
+	case PMCIN_SAS_DIAG_MODE_START_END:
+		return ("SAS_DIAG_MODE_START_END");
+		break;
+	case PMCIN_SAS_DIAG_EXECUTE:
+		return ("SAS_DIAG_EXECUTE");
+		break;
+	case PMCIN_SAW_HW_EVENT_ACK:
+		return ("SAS_HW_EVENT_ACK");
+		break;
+	case PMCIN_GET_TIME_STAMP:
+		return ("GET_TIME_STAMP");
+		break;
+	case PMCIN_PORT_CONTROL:
+		return ("PORT_CONTROL");
+		break;
+	case PMCIN_GET_NVMD_DATA:
+		return ("GET_NVMD_DATA");
+		break;
+	case PMCIN_SET_NVMD_DATA:
+		return ("SET_NVMD_DATA");
+		break;
+	case PMCIN_SET_DEVICE_STATE:
+		return ("SET_DEVICE_STATE");
+		break;
+	case PMCIN_GET_DEVICE_STATE:
+		return ("GET_DEVICE_STATE");
+		break;
+	default:
+		return ("UNKNOWN");
+		break;
+	}
+}
+
+static char *
+outbound_iomb_opcode(uint32_t opcode)
+{
+	switch (opcode) {
+	case PMCOUT_ECHO:
+		return ("ECHO");
+		break;
+	case PMCOUT_GET_INFO:
+		return ("GET_INFO");
+		break;
+	case PMCOUT_GET_VPD:
+		return ("GET_VPD");
+		break;
+	case PMCOUT_SAS_HW_EVENT:
+		return ("SAS_HW_EVENT");
+		break;
+	case PMCOUT_SSP_COMPLETION:
+		return ("SSP_COMPLETION");
+		break;
+	case PMCOUT_SMP_COMPLETION:
+		return ("SMP_COMPLETION");
+		break;
+	case PMCOUT_LOCAL_PHY_CONTROL:
+		return ("LOCAL_PHY_CONTROL");
+		break;
+	case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT:
+		return ("SAS_ASSISTED_DISCOVERY_SENT");
+		break;
+	case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT:
+		return ("SATA_ASSISTED_DISCOVERY_SENT");
+		break;
+	case PMCOUT_DEVICE_REGISTRATION:
+		return ("DEVICE_REGISTRATION");
+		break;
+	case PMCOUT_DEREGISTER_DEVICE_HANDLE:
+		return ("DEREGISTER_DEVICE_HANDLE");
+		break;
+	case PMCOUT_GET_DEVICE_HANDLE:
+		return ("GET_DEVICE_HANDLE");
+		break;
+	case PMCOUT_SATA_COMPLETION:
+		return ("SATA_COMPLETION");
+		break;
+	case PMCOUT_SATA_EVENT:
+		return ("SATA_EVENT");
+		break;
+	case PMCOUT_SSP_EVENT:
+		return ("SSP_EVENT");
+		break;
+	case PMCOUT_DEVICE_HANDLE_ARRIVED:
+		return ("DEVICE_HANDLE_ARRIVED");
+		break;
+	case PMCOUT_SMP_REQUEST_RECEIVED:
+		return ("SMP_REQUEST_RECEIVED");
+		break;
+	case PMCOUT_SSP_REQUEST_RECEIVED:
+		return ("SSP_REQUEST_RECEIVED");
+		break;
+	case PMCOUT_DEVICE_INFO:
+		return ("DEVICE_INFO");
+		break;
+	case PMCOUT_FW_FLASH_UPDATE:
+		return ("FW_FLASH_UPDATE");
+		break;
+	case PMCOUT_SET_VPD:
+		return ("SET_VPD");
+		break;
+	case PMCOUT_GPIO:
+		return ("GPIO");
+		break;
+	case PMCOUT_GPIO_EVENT:
+		return ("GPIO_EVENT");
+		break;
+	case PMCOUT_GENERAL_EVENT:
+		return ("GENERAL_EVENT");
+		break;
+	case PMCOUT_TWI:
+		return ("TWI");
+		break;
+	case PMCOUT_SSP_ABORT:
+		return ("SSP_ABORT");
+		break;
+	case PMCOUT_SATA_ABORT:
+		return ("SATA_ABORT");
+		break;
+	case PMCOUT_SAS_DIAG_MODE_START_END:
+		return ("SAS_DIAG_MODE_START_END");
+		break;
+	case PMCOUT_SAS_DIAG_EXECUTE:
+		return ("SAS_DIAG_EXECUTE");
+		break;
+	case PMCOUT_GET_TIME_STAMP:
+		return ("GET_TIME_STAMP");
+		break;
+	case PMCOUT_SAS_HW_EVENT_ACK_ACK:
+		return ("SAS_HW_EVENT_ACK_ACK");
+		break;
+	case PMCOUT_PORT_CONTROL:
+		return ("PORT_CONTROL");
+		break;
+	case PMCOUT_SKIP_ENTRIES:
+		return ("SKIP_ENTRIES");
+		break;
+	case PMCOUT_SMP_ABORT:
+		return ("SMP_ABORT");
+		break;
+	case PMCOUT_GET_NVMD_DATA:
+		return ("GET_NVMD_DATA");
+		break;
+	case PMCOUT_SET_NVMD_DATA:
+		return ("SET_NVMD_DATA");
+		break;
+	case PMCOUT_DEVICE_HANDLE_REMOVED:
+		return ("DEVICE_HANDLE_REMOVED");
+		break;
+	case PMCOUT_SET_DEVICE_STATE:
+		return ("SET_DEVICE_STATE");
+		break;
+	case PMCOUT_GET_DEVICE_STATE:
+		return ("GET_DEVICE_STATE");
+		break;
+	case PMCOUT_SET_DEVICE_INFO:
+		return ("SET_DEVICE_INFO");
+		break;
+	default:
+		return ("UNKNOWN");
+		break;
+	}
+}
+
+static void
+dump_one_qentry_outbound(uint32_t *qentryp, int idx)
+{
+	int qeidx;
+	uint32_t word0 = LE_32(*qentryp);
+
+	mdb_printf("Entry #%02d\n", idx);
+	mdb_inc_indent(2);
+
+	mdb_printf("Header: 0x%08x (", word0);
+	if (word0 & PMCS_IOMB_VALID) {
+		mdb_printf("VALID, ");
+	}
+	if (word0 & PMCS_IOMB_HIPRI) {
+		mdb_printf("HIPRI, ");
+	}
+	mdb_printf("OBID=%d, ",
+	    (word0 & PMCS_IOMB_OBID_MASK) >> PMCS_IOMB_OBID_SHIFT);
+	mdb_printf("CAT=%s, ",
+	    iomb_cat((word0 & PMCS_IOMB_CAT_MASK) >> PMCS_IOMB_CAT_SHIFT));
+	mdb_printf("OPCODE=%s",
+	    outbound_iomb_opcode(word0 & PMCS_IOMB_OPCODE_MASK));
+	mdb_printf(")\n");
+
+	mdb_printf("Remaining Payload:\n");
+
+	mdb_inc_indent(2);
+	for (qeidx = 1; qeidx < (PMCS_QENTRY_SIZE / 4); qeidx++) {
+		mdb_printf("%08x ", LE_32(*(qentryp + qeidx)));
+	}
+	mdb_printf("\n");
+	mdb_dec_indent(4);
+}
+
+static void
+display_outbound_queues(struct pmcs_hw ss, uint_t verbose)
+{
+	int		idx, qidx;
+	uintptr_t	obqp;
+	uint32_t	*cip;
+	uint32_t	*qentryp = mdb_alloc(PMCS_QENTRY_SIZE, UM_SLEEP);
+	uint32_t	last_consumed, oqpi;
+
+	mdb_printf("\n");
+	mdb_printf("Outbound Queues\n");
+	mdb_printf("---------------\n");
+
+	mdb_inc_indent(2);
+
+	for (qidx = 0; qidx < PMCS_NOQ; qidx++) {
+		obqp = (uintptr_t)ss.oqp[qidx];
+
+		if (obqp == NULL) {
+			mdb_printf("No outbound queue ptr for queue #%d\n",
+			    qidx);
+			continue;
+		}
+
+		mdb_printf("Outbound Queue #%d (Queue Type = %s)\n", qidx,
+		    obq_type(qidx));
+		/*
+		 * Chip is the producer, so read the actual producer index
+		 * and not the driver's version
+		 */
+		cip = (uint32_t *)((void *)ss.cip);
+		if (MDB_RD(&oqpi, 4, cip + OQPI_BASE_OFFSET +
+		    (qidx * 4)) == -1) {
+			mdb_warn("Couldn't read oqpi\n");
+			break;
+		}
+
+		mdb_printf("Producer index: %d  Consumer index: %d\n\n",
+		    LE_32(oqpi), ss.oqci[qidx]);
+		mdb_inc_indent(2);
+
+		if (ss.oqci[qidx] == 0) {
+			last_consumed = ss.ioq_depth - 1;
+		} else {
+			last_consumed = ss.oqci[qidx] - 1;
+		}
+
+
+		if (!verbose) {
+			mdb_printf("Last processed entry:\n");
+			if (MDB_RD(qentryp, PMCS_QENTRY_SIZE,
+			    (obqp + (PMCS_QENTRY_SIZE * last_consumed)))
+			    == -1) {
+				mdb_warn("Couldn't read queue entry at 0x%p\n",
+				    (obqp + (PMCS_QENTRY_SIZE *
+				    last_consumed)));
+				break;
+			}
+			dump_one_qentry_outbound(qentryp, last_consumed);
+			mdb_printf("\n");
+			mdb_dec_indent(2);
+			continue;
+		}
+
+		for (idx = 0; idx < ss.ioq_depth; idx++) {
+			if (MDB_RD(qentryp, PMCS_QENTRY_SIZE,
+			    (obqp + (PMCS_QENTRY_SIZE * idx))) == -1) {
+				mdb_warn("Couldn't read queue entry at 0x%p\n",
+				    (obqp + (PMCS_QENTRY_SIZE * idx)));
+				break;
+			}
+			dump_one_qentry_outbound(qentryp, idx);
+		}
+
+		mdb_printf("\n");
+		mdb_dec_indent(2);
+	}
+
+	mdb_dec_indent(2);
+	mdb_free(qentryp, PMCS_QENTRY_SIZE);
+}
+
+static void
+dump_one_qentry_inbound(uint32_t *qentryp, int idx)
+{
+	int qeidx;
+	uint32_t word0 = LE_32(*qentryp);
+
+	mdb_printf("Entry #%02d\n", idx);
+	mdb_inc_indent(2);
+
+	mdb_printf("Header: 0x%08x (", word0);
+	if (word0 & PMCS_IOMB_VALID) {
+		mdb_printf("VALID, ");
+	}
+	if (word0 & PMCS_IOMB_HIPRI) {
+		mdb_printf("HIPRI, ");
+	}
+	mdb_printf("OBID=%d, ",
+	    (word0 & PMCS_IOMB_OBID_MASK) >> PMCS_IOMB_OBID_SHIFT);
+	mdb_printf("CAT=%s, ",
+	    iomb_cat((word0 & PMCS_IOMB_CAT_MASK) >> PMCS_IOMB_CAT_SHIFT));
+	mdb_printf("OPCODE=%s",
+	    inbound_iomb_opcode(word0 & PMCS_IOMB_OPCODE_MASK));
+	mdb_printf(")\n");
+
+	mdb_printf("HTAG: 0x%08x\n", LE_32(*(qentryp + 1)));
+	mdb_printf("Remaining Payload:\n");
+
+	mdb_inc_indent(2);
+	for (qeidx = 2; qeidx < (PMCS_QENTRY_SIZE / 4); qeidx++) {
+		mdb_printf("%08x ", LE_32(*(qentryp + qeidx)));
+	}
+	mdb_printf("\n");
+	mdb_dec_indent(4);
+}
+
+static void
+display_inbound_queues(struct pmcs_hw ss, uint_t verbose)
+{
+	int		idx, qidx, iqci, last_consumed;
+	uintptr_t	ibqp;
+	uint32_t	*qentryp = mdb_alloc(PMCS_QENTRY_SIZE, UM_SLEEP);
+	uint32_t	*cip;
+
+	mdb_printf("\n");
+	mdb_printf("Inbound Queues\n");
+	mdb_printf("--------------\n");
+
+	mdb_inc_indent(2);
+
+	for (qidx = 0; qidx < PMCS_NIQ; qidx++) {
+		ibqp = (uintptr_t)ss.iqp[qidx];
+
+		if (ibqp == NULL) {
+			mdb_printf("No inbound queue ptr for queue #%d\n",
+			    qidx);
+			continue;
+		}
+
+		mdb_printf("Inbound Queue #%d (Queue Type = %s)\n", qidx,
+		    ibq_type(qidx));
+
+		cip = (uint32_t *)((void *)ss.cip);
+		if (MDB_RD(&iqci, 4, cip + (qidx * 4)) == -1) {
+			mdb_warn("Couldn't read iqci\n");
+			break;
+		}
+		iqci = LE_32(iqci);
+
+		mdb_printf("Producer index: %d  Consumer index: %d\n\n",
+		    ss.shadow_iqpi[qidx], iqci);
+		mdb_inc_indent(2);
+
+		if (iqci == 0) {
+			last_consumed = ss.ioq_depth - 1;
+		} else {
+			last_consumed = iqci - 1;
+		}
+
+		if (!verbose) {
+			mdb_printf("Last processed entry:\n");
+			if (MDB_RD(qentryp, PMCS_QENTRY_SIZE,
+			    (ibqp + (PMCS_QENTRY_SIZE * last_consumed)))
+			    == -1) {
+				mdb_warn("Couldn't read queue entry at 0x%p\n",
+				    (ibqp + (PMCS_QENTRY_SIZE *
+				    last_consumed)));
+				break;
+			}
+			dump_one_qentry_inbound(qentryp, last_consumed);
+			mdb_printf("\n");
+			mdb_dec_indent(2);
+			continue;
+		}
+
+		for (idx = 0; idx < ss.ioq_depth; idx++) {
+			if (MDB_RD(qentryp, PMCS_QENTRY_SIZE,
+			    (ibqp + (PMCS_QENTRY_SIZE * idx))) == -1) {
+				mdb_warn("Couldn't read queue entry at 0x%p\n",
+				    (ibqp + (PMCS_QENTRY_SIZE * idx)));
+				break;
+			}
+			dump_one_qentry_inbound(qentryp, idx);
+		}
+
+		mdb_printf("\n");
+		mdb_dec_indent(2);
+	}
+
+	mdb_dec_indent(2);
+	mdb_free(qentryp, PMCS_QENTRY_SIZE);
+}
+
+static void
+display_phy(struct pmcs_phy phy, int verbose, int totals_only)
+{
+	char		*dtype, *speed;
+	char		*yes = "Yes";
+	char		*no = "No";
+	char		*cfgd = no;
+	char		*apend = no;
+	char		*asent = no;
+	char		*dead = no;
+	char		*changed = no;
+
+	switch (phy.dtype) {
+	case NOTHING:
+		dtype = "None";
+		break;
+	case SATA:
+		dtype = "SATA";
+		if (phy.configured) {
+			++sata_phys;
+		}
+		break;
+	case SAS:
+		dtype = "SAS";
+		if (phy.configured) {
+			++sas_phys;
+		}
+		break;
+	case EXPANDER:
+		dtype = "EXP";
+		if (phy.configured) {
+			++exp_phys;
+		}
+		break;
+	}
+
+	if (phy.dtype == NOTHING) {
+		empty_phys++;
+	} else if ((phy.dtype == EXPANDER) && phy.configured) {
+		num_expanders++;
+	}
+
+	if (totals_only) {
+		return;
+	}
+
+	switch (phy.link_rate) {
+	case SAS_LINK_RATE_1_5GBIT:
+		speed = "1.5Gb/s";
+		break;
+	case SAS_LINK_RATE_3GBIT:
+		speed = "3 Gb/s";
+		break;
+	case SAS_LINK_RATE_6GBIT:
+		speed = "6 Gb/s";
+		break;
+	default:
+		speed = "N/A";
+		break;
+	}
+
+	if ((phy.dtype != NOTHING) || verbose) {
+		print_sas_address(&phy);
+
+		if (phy.device_id != PMCS_INVALID_DEVICE_ID) {
+			mdb_printf(" %3d %4d %6s %4s ",
+			    phy.device_id, phy.phynum, speed, dtype);
+		} else {
+			mdb_printf(" N/A %4d %6s %4s ",
+			    phy.phynum, speed, dtype);
+		}
+
+		if (verbose) {
+			if (phy.abort_sent) {
+				asent = yes;
+			}
+			if (phy.abort_pending) {
+				apend = yes;
+			}
+			if (phy.configured) {
+				cfgd = yes;
+			}
+			if (phy.dead) {
+				dead = yes;
+			}
+			if (phy.changed) {
+				changed = yes;
+			}
+
+			mdb_printf("%-4s %-4s %-4s %-4s %-4s %3d "
+			    "0x%p ", cfgd, apend, asent,
+			    changed, dead, phy.ref_count, phy.phy_lock);
+		}
+
+		mdb_printf("Path: %s\n", phy.path);
+	}
+}
+
+static void
+display_phys(struct pmcs_hw ss, int verbose, struct pmcs_phy *parent, int level,
+    int totals_only)
+{
+	pmcs_phy_t	phy;
+	pmcs_phy_t	*pphy = parent;
+
+	mdb_inc_indent(3);
+
+	if (parent == NULL) {
+		pphy = (pmcs_phy_t *)ss.root_phys;
+	} else {
+		pphy = (pmcs_phy_t *)parent;
+	}
+
+	if (level == 0) {
+		sas_phys = 0;
+		sata_phys = 0;
+		exp_phys = 0;
+		num_expanders = 0;
+		empty_phys = 0;
+	}
+
+	if (!totals_only) {
+		if (level == 0) {
+			mdb_printf("PHY information\n");
+		}
+		mdb_printf("--------\n");
+		mdb_printf("Level %2d\n", level);
+		mdb_printf("--------\n");
+		mdb_printf("SAS Address      Hdl Phy#  Speed Type ");
+
+		if (verbose) {
+			mdb_printf("Cfgd AbtP AbtS Chgd Dead Ref Lock\n");
+		} else {
+			mdb_printf("\n");
+		}
+	}
+
+	while (pphy) {
+		if (MDB_RD(&phy, sizeof (phy), (uintptr_t)pphy) == -1) {
+			NOREAD(pmcs_phy_t, phy);
+			break;
+		}
+
+		display_phy(phy, verbose, totals_only);
+
+		if (phy.children) {
+			display_phys(ss, verbose, phy.children, level + 1,
+			    totals_only);
+			if (!totals_only) {
+				mdb_printf("\n");
+			}
+		}
+
+		pphy = phy.sibling;
+	}
+
+	mdb_dec_indent(3);
+
+	if (level == 0) {
+		if (verbose) {
+			mdb_printf("%19s %d (%d SAS + %d SATA + %d SMP) "
+			    "(+%d subsidiary + %d empty)\n", "Occupied PHYs:",
+			    (sas_phys + sata_phys + num_expanders),
+			    sas_phys, sata_phys, num_expanders,
+			    (exp_phys - num_expanders), empty_phys);
+		} else {
+			mdb_printf("%19s %d (%d SAS + %d SATA + %d SMP)\n",
+			    "Occupied PHYs:",
+			    (sas_phys + sata_phys + num_expanders),
+			    sas_phys, sata_phys, num_expanders);
+		}
+	}
+}
+
+/*
+ * MAX_INST_STRLEN is the largest string size from which we will attempt
+ * to convert to an instance number.  The string will be formed up as
+ * "0t<inst>\0" so that mdb_strtoull can parse it properly.
+ */
+#define	MAX_INST_STRLEN	8
+
+static int
+pmcs_dump_tracelog(boolean_t filter, int instance)
+{
+	pmcs_tbuf_t *tbuf_addr;
+	uint_t tbuf_idx;
+	pmcs_tbuf_t tbuf;
+	boolean_t wrap, elem_filtered;
+	uint_t start_idx, elems_to_print, idx, tbuf_num_elems;
+	char *bufp;
+	char elem_inst[MAX_INST_STRLEN], ei_idx;
+
+	/* Get the address of the first element */
+	if (mdb_readvar(&tbuf_addr, "pmcs_tbuf") == -1) {
+		mdb_warn("can't read pmcs_tbuf");
+		return (DCMD_ERR);
+	}
+
+	/* Get the total number */
+	if (mdb_readvar(&tbuf_num_elems, "pmcs_tbuf_num_elems") == -1) {
+		mdb_warn("can't read pmcs_tbuf_num_elems");
+		return (DCMD_ERR);
+	}
+
+	/* Get the current index */
+	if (mdb_readvar(&tbuf_idx, "pmcs_tbuf_idx") == -1) {
+		mdb_warn("can't read pmcs_tbuf_idx");
+		return (DCMD_ERR);
+	}
+
+	/* Indicator as to whether the buffer has wrapped */
+	if (mdb_readvar(&wrap, "pmcs_tbuf_wrap") == -1) {
+		mdb_warn("can't read pmcs_tbuf_wrap");
+		return (DCMD_ERR);
+	}
+
+	/* Figure out where we start and stop */
+	if (wrap) {
+		start_idx = tbuf_idx;
+		elems_to_print = tbuf_num_elems;
+	} else {
+		start_idx = 0;
+		elems_to_print = tbuf_idx;
+	}
+
+	idx = start_idx;
+
+	/* Dump the buffer contents */
+	while (elems_to_print != 0) {
+		if (MDB_RD(&tbuf, sizeof (pmcs_tbuf_t), (tbuf_addr + idx))
+		    == -1) {
+			NOREAD(tbuf, (tbuf_addr + idx));
+			return (DCMD_ERR);
+		}
+
+		elem_filtered = B_FALSE;
+
+		if (filter) {
+			bufp = tbuf.buf;
+			/* Skip the driver name */
+			while (*bufp < '0' || *bufp > '9') {
+				bufp++;
+			}
+
+			ei_idx = 0;
+			elem_inst[ei_idx++] = '0';
+			elem_inst[ei_idx++] = 't';
+			while (*bufp != ':' && ei_idx < (MAX_INST_STRLEN - 1)) {
+				elem_inst[ei_idx++] = *bufp;
+				bufp++;
+			}
+			elem_inst[ei_idx] = 0;
+
+			/* Get the instance */
+			if ((int)mdb_strtoull(elem_inst) != instance) {
+				elem_filtered = B_TRUE;
+			}
+		}
+
+		if (!elem_filtered) {
+			mdb_printf("%Y.%09ld %s\n", tbuf.timestamp, tbuf.buf);
+		}
+
+		--elems_to_print;
+		if (++idx == tbuf_num_elems) {
+			idx = 0;
+		}
+	}
+
+	return (DCMD_OK);
+}
+
+/*
+ * Walkers
+ */
+static int
+targets_walk_i(mdb_walk_state_t *wsp)
+{
+	if (wsp->walk_addr == NULL) {
+		mdb_warn("Can not perform global walk\n");
+		return (WALK_ERR);
+	}
+
+	/*
+	 * Address provided belongs to HBA softstate.  Get the targets pointer
+	 * to begin the walk.
+	 */
+	if (mdb_vread(&ss, sizeof (pmcs_hw_t), wsp->walk_addr) !=
+	    sizeof (pmcs_hw_t)) {
+		mdb_warn("Unable to read HBA softstate\n");
+		return (WALK_ERR);
+	}
+
+	if (targets == NULL) {
+		targets = mdb_alloc(sizeof (targets) * ss.max_dev, UM_SLEEP);
+	}
+
+	if (MDB_RD(targets, sizeof (targets) * ss.max_dev, ss.targets) == -1) {
+		NOREAD(targets, ss.targets);
+		return (WALK_ERR);
+	}
+
+	target_idx = 0;
+	wsp->walk_addr = (uintptr_t)(targets[0]);
+	wsp->walk_data = mdb_alloc(sizeof (pmcs_xscsi_t), UM_SLEEP);
+
+	return (WALK_NEXT);
+}
+
+static int
+targets_walk_s(mdb_walk_state_t *wsp)
+{
+	int status;
+
+	if (target_idx == ss.max_dev) {
+		return (WALK_DONE);
+	}
+
+	if (mdb_vread(wsp->walk_data, sizeof (pmcs_xscsi_t),
+	    wsp->walk_addr) == -1) {
+		mdb_warn("Failed to read target at %p", (void *)wsp->walk_addr);
+		return (WALK_DONE);
+	}
+
+	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
+	    wsp->walk_cbdata);
+
+	do {
+		wsp->walk_addr = (uintptr_t)(targets[++target_idx]);
+	} while ((wsp->walk_addr == NULL) && (target_idx < ss.max_dev));
+
+	if (target_idx == ss.max_dev) {
+		return (WALK_DONE);
+	}
+
+	return (status);
+}
+
+static void
+targets_walk_f(mdb_walk_state_t *wsp)
+{
+	mdb_free(wsp->walk_data, sizeof (pmcs_xscsi_t));
+}
+
+
+static pmcs_phy_t *
+pmcs_next_sibling(pmcs_phy_t *phyp)
+{
+	pmcs_phy_t parent;
+
+	/*
+	 * First, if this is a root PHY, there are no more siblings
+	 */
+	if (phyp->level == 0) {
+		return (NULL);
+	}
+
+	/*
+	 * Otherwise, next sibling is the parent's sibling
+	 */
+	while (phyp->level > 0) {
+		if (mdb_vread(&parent, sizeof (pmcs_phy_t),
+		    (uintptr_t)phyp->parent) == -1) {
+			mdb_warn("pmcs_next_sibling: Failed to read PHY at %p",
+			    (void *)phyp->parent);
+			return (NULL);
+		}
+
+		if (parent.sibling != NULL) {
+			break;
+		}
+
+		phyp = phyp->parent;
+	}
+
+	return (parent.sibling);
+}
+
+static int
+phy_walk_i(mdb_walk_state_t *wsp)
+{
+	if (wsp->walk_addr == NULL) {
+		mdb_warn("Can not perform global walk\n");
+		return (WALK_ERR);
+	}
+
+	/*
+	 * Address provided belongs to HBA softstate.  Get the targets pointer
+	 * to begin the walk.
+	 */
+	if (mdb_vread(&ss, sizeof (pmcs_hw_t), wsp->walk_addr) !=
+	    sizeof (pmcs_hw_t)) {
+		mdb_warn("Unable to read HBA softstate\n");
+		return (WALK_ERR);
+	}
+
+	wsp->walk_addr = (uintptr_t)(ss.root_phys);
+	wsp->walk_data = mdb_alloc(sizeof (pmcs_phy_t), UM_SLEEP);
+
+	return (WALK_NEXT);
+}
+
+static int
+phy_walk_s(mdb_walk_state_t *wsp)
+{
+	pmcs_phy_t *phyp, *nphyp;
+	int status;
+
+	if (mdb_vread(wsp->walk_data, sizeof (pmcs_phy_t),
+	    wsp->walk_addr) == -1) {
+		mdb_warn("phy_walk_s: Failed to read PHY at %p",
+		    (void *)wsp->walk_addr);
+		return (WALK_DONE);
+	}
+
+	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
+	    wsp->walk_cbdata);
+
+	phyp = (pmcs_phy_t *)wsp->walk_data;
+	if (phyp->children) {
+		wsp->walk_addr = (uintptr_t)(phyp->children);
+	} else {
+		wsp->walk_addr = (uintptr_t)(phyp->sibling);
+	}
+
+	if (wsp->walk_addr == NULL) {
+		/*
+		 * We reached the end of this sibling list.  Trudge back up
+		 * to the parent and find the next sibling after the expander
+		 * we just finished traversing, if there is one.
+		 */
+		nphyp = pmcs_next_sibling(phyp);
+
+		if (nphyp == NULL) {
+			return (WALK_DONE);
+		}
+
+		wsp->walk_addr = (uintptr_t)nphyp;
+	}
+
+	return (status);
+}
+
+static void
+phy_walk_f(mdb_walk_state_t *wsp)
+{
+	mdb_free(wsp->walk_data, sizeof (pmcs_phy_t));
+}
+
+static int
+pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	struct	pmcs_hw		ss;
+	uint_t			verbose = FALSE;
+	uint_t			phy_info = FALSE;
+	uint_t			hw_info = FALSE;
+	uint_t			target_info = FALSE;
+	uint_t			work_info = FALSE;
+	uint_t			ic_info = FALSE;
+	uint_t			iport_info = FALSE;
+	uint_t			waitqs_info = FALSE;
+	uint_t			tracelog = FALSE;
+	uint_t			ibq = FALSE;
+	uint_t			obq = FALSE;
+	uint_t			tgt_phy_count = FALSE;
+	int			rv = DCMD_OK;
+	void			*pmcs_state;
+	char			*state_str;
+	struct dev_info		dip;
+
+	if (!(flags & DCMD_ADDRSPEC)) {
+		pmcs_state = NULL;
+		if (mdb_readvar(&pmcs_state, "pmcs_softc_state") == -1) {
+			mdb_warn("can't read pmcs_softc_state");
+			return (DCMD_ERR);
+		}
+		if (mdb_pwalk_dcmd("genunix`softstate", "pmcs`pmcs", argc, argv,
+		    (uintptr_t)pmcs_state) == -1) {
+			mdb_warn("mdb_pwalk_dcmd failed");
+			return (DCMD_ERR);
+		}
+		return (DCMD_OK);
+	}
+
+	if (mdb_getopts(argc, argv,
+	    'h', MDB_OPT_SETBITS, TRUE, &hw_info,
+	    'i', MDB_OPT_SETBITS, TRUE, &ic_info,
+	    'I', MDB_OPT_SETBITS, TRUE, &iport_info,
+	    'l', MDB_OPT_SETBITS, TRUE, &tracelog,
+	    'p', MDB_OPT_SETBITS, TRUE, &phy_info,
+	    'q', MDB_OPT_SETBITS, TRUE, &ibq,
+	    'Q', MDB_OPT_SETBITS, TRUE, &obq,
+	    't', MDB_OPT_SETBITS, TRUE, &target_info,
+	    'T', MDB_OPT_SETBITS, TRUE, &tgt_phy_count,
+	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
+	    'w', MDB_OPT_SETBITS, TRUE, &work_info,
+	    'W', MDB_OPT_SETBITS, TRUE, &waitqs_info,
+	    NULL) != argc)
+		return (DCMD_USAGE);
+
+	if (MDB_RD(&ss, sizeof (ss), addr) == -1) {
+		NOREAD(pmcs_hw_t, addr);
+		return (DCMD_ERR);
+	}
+
+	if (MDB_RD(&dip, sizeof (struct dev_info), ss.dip) == -1) {
+		NOREAD(pmcs_hw_t, addr);
+		return (DCMD_ERR);
+	}
+
+	/*
+	 * Dumping the trace log is special.  It's global, not per-HBA.
+	 * Thus, a provided address is ignored.  In addition, other options
+	 * cannot be specified at the same time.
+	 */
+
+	if (tracelog) {
+		if (hw_info || ic_info || iport_info || phy_info || work_info ||
+		    target_info || waitqs_info || ibq || obq || tgt_phy_count) {
+			return (DCMD_USAGE);
+		}
+
+		if ((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) {
+			return (pmcs_dump_tracelog(B_TRUE, dip.devi_instance));
+		} else if (flags & DCMD_LOOPFIRST) {
+			return (pmcs_dump_tracelog(B_FALSE, 0));
+		} else {
+			return (DCMD_OK);
+		}
+	}
+
+	/* processing completed */
+
+	if (((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) ||
+	    (flags & DCMD_LOOPFIRST) || phy_info || target_info || hw_info ||
+	    work_info || waitqs_info || ibq || obq || tgt_phy_count) {
+		if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST))
+			mdb_printf("\n");
+		mdb_printf("%16s %9s %4s B C  WorkFlags wserno DbgMsk %16s\n",
+		    "Address", "State", "Inst", "DIP");
+		mdb_printf("================================="
+		    "============================================\n");
+	}
+
+	switch (ss.state) {
+	case STATE_NIL:
+		state_str = "Invalid";
+		break;
+	case STATE_PROBING:
+		state_str = "Probing";
+		break;
+	case STATE_RUNNING:
+		state_str = "Running";
+		break;
+	case STATE_UNPROBING:
+		state_str = "Unprobing";
+		break;
+	case STATE_DEAD:
+		state_str = "Dead";
+		break;
+	}
+
+	mdb_printf("%16p %9s %4d %1d %1d 0x%08x 0x%04x 0x%04x %16p\n", addr,
+	    state_str, dip.devi_instance, ss.blocked, ss.configuring,
+	    ss.work_flags, ss.wserno, ss.debug_mask, ss.dip);
+	mdb_printf("\n");
+
+	mdb_inc_indent(4);
+
+	if (waitqs_info)
+		display_waitqs(ss, verbose);
+
+	if (hw_info)
+		display_hwinfo(ss, verbose);
+
+	if (phy_info || tgt_phy_count)
+		display_phys(ss, verbose, NULL, 0, tgt_phy_count);
+
+	if (target_info || tgt_phy_count)
+		display_targets(ss, verbose, tgt_phy_count);
+
+	if (work_info)
+		display_work(ss, verbose);
+
+	if (ic_info)
+		display_ic(ss, verbose);
+
+	if (ibq)
+		display_inbound_queues(ss, verbose);
+
+	if (obq)
+		display_outbound_queues(ss, verbose);
+
+	if (iport_info)
+		display_iport(ss, addr, verbose);
+
+	mdb_dec_indent(4);
+
+	return (rv);
+}
+
+void
+pmcs_help()
+{
+	mdb_printf("Prints summary information about each pmcs instance.\n"
+	    "    -h: Print more detailed hardware information\n"
+	    "    -i: Print interrupt coalescing information\n"
+	    "    -I: Print information about each iport\n"
+	    "    -l: Dump the trace log (cannot be used with other options)\n"
+	    "    -p: Print information about each attached PHY\n"
+	    "    -q: Dump inbound queues\n"
+	    "    -Q: Dump outbound queues\n"
+	    "    -t: Print information about each known target\n"
+	    "    -T: Print target and PHY count summary\n"
+	    "    -w: Dump work structures\n"
+	    "    -W: List pmcs cmds waiting on various queues\n"
+	    "    -v: Add verbosity to the above options\n");
+}
+
+static const mdb_dcmd_t dcmds[] = {
+	{ "pmcs", "?[-hiIpQqtTwWv] | -l", "print pmcs information",
+	    pmcs_dcmd, pmcs_help
+	},
+	{ NULL }
+};
+
+static const mdb_walker_t walkers[] = {
+	{ "pmcs_targets", "walk target structures",
+		targets_walk_i, targets_walk_s, targets_walk_f },
+	{ "pmcs_phys", "walk PHY structures",
+		phy_walk_i, phy_walk_s, phy_walk_f },
+	{ NULL }
+};
+
+static const mdb_modinfo_t modinfo = {
+	MDB_API_VERSION, dcmds, walkers
+};
+
+const mdb_modinfo_t *
+_mdb_init(void)
+{
+	return (&modinfo);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/intel/amd64/pmcs/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,38 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+MODULE = pmcs.so
+MDBTGT = kvm
+
+MODSRCS = pmcs.c
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/Makefile.cmd.64
+include $(SRC)/cmd/mdb/intel/Makefile.amd64
+include $(SRC)/cmd/mdb/Makefile.module
+
+C99MODE = -xc99=%all
+
+CPPFLAGS += -I$(SRC)/uts/common
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/intel/ia32/pmcs/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+MODULE = pmcs.so
+MDBTGT = kvm
+
+MODSRCS = pmcs.c
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/mdb/intel/Makefile.ia32
+include $(SRC)/cmd/mdb/Makefile.module
+
+C99MODE = -xc99=%all
+
+CPPFLAGS += -I$(SRC)/uts/common
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/sparc/v9/pmcs/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,38 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+MODULE = pmcs.so
+MDBTGT = kvm
+
+MODSRCS = pmcs.c
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/Makefile.cmd.64
+include $(SRC)/cmd/mdb/sparc/Makefile.sparcv9
+include $(SRC)/cmd/mdb/Makefile.module
+
+C99MODE = -xc99=%all
+
+CPPFLAGS += -I$(SRC)/uts/common
--- a/usr/src/cmd/mpathadm/mpathadm.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/mpathadm/mpathadm.c	Mon Oct 05 09:17:26 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -228,8 +228,7 @@
 	MP_OID_LIST				*pPluginOidList;
 	boolean_t				shown = B_FALSE;
 	/* number of plugins listed */
-	int					i,
-						op;
+	int					i, op;
 
 	if ((mpstatus = MP_GetPluginOidList(&pPluginOidList))
 	    != MP_STATUS_SUCCESS) {
@@ -276,11 +275,12 @@
 						    TEXT_LB_MPATH_SUPPORT),
 						    pluginProps.fileName);
 					} else {
-					/* LINTED E_SEC_PRINTF_VAR_FMT */
-						(void) fprintf(stderr,
-						    getTextString(
+				/* begin back-up indentation */
+				/* LINTED E_SEC_PRINTF_VAR_FMT */
+				(void) fprintf(stderr, getTextString(
 				    ERR_CANT_FIND_MPATH_SUPPORT_WITH_NAME),
-						    operand[op]);
+				    operand[op]);
+				/* end back-up indentation */
 						(void) printf("\n");
 					}
 				}
@@ -311,9 +311,7 @@
 	MP_OID_LIST				*deviceOidListArray;
 	MP_DEVICE_PRODUCT_PROPERTIES		devProps;
 	boolean_t				bListIt = B_FALSE;
-	int					op,
-						i,
-						j;
+	int					op, i, j;
 	MP_LOAD_BALANCE_TYPE 			lb;
 
 
@@ -330,7 +328,7 @@
 	}
 
 	for (op = 0; op < operandLen; op++) {
-	    bListIt = B_FALSE;
+		bListIt = B_FALSE;
 
 		for (i = 0; i < pPluginOidList->oidCount; i++) {
 
@@ -377,11 +375,11 @@
 			    getTextString(TEXT_LB_DEFAULT_LB));
 			/* don't ignore load balance type none. */
 			if (pluginProps.defaultloadBalanceType == 0) {
-			    (void) printf("%s",
-			    getTextString(TEXT_LBTYPE_NONE));
+				(void) printf("%s",
+				    getTextString(TEXT_LBTYPE_NONE));
 			} else {
-			    displayLoadBalanceString(
-			    pluginProps.defaultloadBalanceType);
+				displayLoadBalanceString(
+				    pluginProps.defaultloadBalanceType);
 			}
 			(void) printf("\n");
 
@@ -391,19 +389,20 @@
 			/* check each bit, display string if found set */
 			if (pluginProps.supportedLoadBalanceTypes == 0) {
 				(void) printf("\t\t%s\n",
-					getTextString(TEXT_LBTYPE_NONE));
+				    getTextString(TEXT_LBTYPE_NONE));
 			} else {
-			    lb = 1;
-			    do {
-				if (0 != (lb &
-				    pluginProps.supportedLoadBalanceTypes)) {
-					(void) printf("\t\t");
-					displayLoadBalanceString(lb &
-					pluginProps.supportedLoadBalanceTypes);
-					(void) printf("\n");
-				}
-				lb = lb<<1;
-			    } while (lb < 0x80000000);
+				lb = 1;
+				do {
+					if (0 != (lb & pluginProps.
+					    supportedLoadBalanceTypes)) {
+						(void) printf("\t\t");
+						displayLoadBalanceString(lb &
+						    pluginProps.
+						    supportedLoadBalanceTypes);
+						(void) printf("\n");
+					}
+					lb = lb<<1;
+				} while (lb < 0x80000000);
 			}
 
 			(void) printf("\t%s  %s\n",
@@ -493,9 +492,10 @@
 
 				for (j = 0; j < deviceOidListArray->oidCount;
 				    j++) {
-					(void) memset(&devProps, 0,
-					sizeof (MP_DEVICE_PRODUCT_PROPERTIES));
-
+				/* begin backup indentation */
+				(void) memset(&devProps, 0,
+				    sizeof (MP_DEVICE_PRODUCT_PROPERTIES));
+				/* end backup indentation */
 					if ((mpstatus =
 					    MP_GetDeviceProductProperties(\
 					    deviceOidListArray->oids[j],
@@ -520,24 +520,24 @@
 						(void) printf("\n\t\t%s\n",
 						    getTextString(
 						    TEXT_LB_SUPPORTED_LB));
-			if (devProps.supportedLoadBalanceTypes == 0) {
-				(void) printf("\t\t\t%s\n",
-					getTextString(TEXT_LBTYPE_NONE));
-			} else {
-			    lb = 1;
-			    do {
+		/* begin back-up indentation */
+		if (devProps.supportedLoadBalanceTypes == 0) {
+			(void) printf("\t\t\t%s\n",
+			    getTextString(TEXT_LBTYPE_NONE));
+		} else {
+			lb = 1;
+			do {
 				if (0 != (lb &
 				    devProps.supportedLoadBalanceTypes)) {
 					(void) printf("\t\t\t");
 					displayLoadBalanceString(lb &
-					devProps.supportedLoadBalanceTypes);
+					    devProps.supportedLoadBalanceTypes);
 					(void) printf("\n");
 				}
 				lb = lb<<1;
-			    } while (lb < 0x80000000);
-			}
-
-
+			} while (lb < 0x80000000);
+		}
+		/* end back-up indentation */
 						(void) printf("\n");
 
 					} else {
@@ -582,18 +582,14 @@
 int
 modifyMpathSupport(int operandLen, char *operand[], cmdOptions_t *options)
 {
-	MP_STATUS				mpstatus = MP_STATUS_SUCCESS;
-	MP_PLUGIN_PROPERTIES			pluginProps;
-	MP_OID_LIST				*pPluginOidList;
-	boolean_t				bFoundIt = B_FALSE;
-	MP_OID					pluginOid;
-	cmdOptions_t 				*optionList = options;
-	char					*cmdStr =
-						    getTextString(
-						    TEXT_UNKNOWN);
-	int					op,
-						i,
-						lbValue;
+	MP_STATUS		mpstatus = MP_STATUS_SUCCESS;
+	MP_PLUGIN_PROPERTIES	pluginProps;
+	MP_OID_LIST		*pPluginOidList;
+	boolean_t		bFoundIt = B_FALSE;
+	MP_OID			pluginOid;
+	cmdOptions_t 		*optionList = options;
+	char			*cmdStr = getTextString(TEXT_UNKNOWN);
+	int			op, i, lbValue;
 
 	if ((mpstatus = MP_GetPluginOidList(&pPluginOidList))
 	    != MP_STATUS_SUCCESS) {
@@ -645,8 +641,8 @@
 				mpstatus =
 				    MP_EnableAutoFailback(pluginOid);
 			} else if (0 ==
-				strcasecmp(optionList->optarg,
-				    getTextString(TEXT_OFF))) {
+			    strcasecmp(optionList->optarg,
+			    getTextString(TEXT_OFF))) {
 				mpstatus =
 				    MP_DisableAutoFailback(pluginOid);
 			} else {
@@ -667,8 +663,8 @@
 				mpstatus =
 				    MP_EnableAutoProbing(pluginOid);
 			} else if (0 ==
-				strcasecmp(optionList->optarg,
-				    getTextString(TEXT_OFF))) {
+			    strcasecmp(optionList->optarg,
+			    getTextString(TEXT_OFF))) {
 				mpstatus =
 				    MP_DisableAutoProbing(pluginOid);
 			} else {
@@ -738,29 +734,18 @@
 int
 listLogicalUnit(int operandLen, char *operand[], cmdOptions_t *options)
 {
-	MP_STATUS				mpstatus = MP_STATUS_SUCCESS;
-	MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES	luProps;
-	MP_PLUGIN_PROPERTIES			pluginProps;
-	MP_TARGET_PORT_PROPERTIES		tportProps;
-	MP_OID_LIST				*pPluginOidList,
-						*pLogicalUnitOidList,
-						*pTpgOidListArray,
-						*pTportOidListArray;
-	boolean_t				bListIt = B_FALSE,
-						bFoundOperand = B_FALSE,
-						*bFoundOption,
-						bContinue = B_FALSE;
-	MP_OID					luOid;
-	cmdOptions_t 				*optionList = options;
-	int					opListCount = 0,
-						i = 0,
-						lu = 0,
-						tpg = 0,
-						opoffset = 0,
-						j = 0,
-						opStart = 0,
-						opEnd = 0,
-						opIndex;
+	MP_STATUS mpstatus = MP_STATUS_SUCCESS;
+	MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES luProps;
+	MP_PLUGIN_PROPERTIES pluginProps;
+	MP_TARGET_PORT_PROPERTIES tportProps;
+	MP_OID_LIST *pPluginOidList, *pLogicalUnitOidList,
+	    *pTpgOidListArray, *pTportOidListArray;
+	boolean_t bListIt = B_FALSE, bFoundOperand = B_FALSE,
+	    *bFoundOption, bContinue = B_FALSE;
+	MP_OID luOid;
+	cmdOptions_t *optionList = options;
+	int opListCount = 0, i = 0, lu = 0, tpg = 0, opoffset = 0, j = 0,
+	    opStart = 0, opEnd = 0, opIndex;
 
 	/* count number of options */
 	for (; optionList->optval; optionList++) {
@@ -819,9 +804,11 @@
 			}
 
 			for (lu = 0; lu < pLogicalUnitOidList->oidCount; lu++) {
-				/* get lu properties so we can check the name */
-				(void) memset(&luProps, 0,
-				sizeof (MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES));
+			/* begin backup indentation */
+			/* get lu properties so we can check the name */
+			(void) memset(&luProps, 0,
+			    sizeof (MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES));
+			/* end backup indentation */
 				mpstatus =
 				    MP_GetMPLogicalUnitProperties(
 				    pLogicalUnitOidList->oids[lu],
@@ -903,9 +890,11 @@
 				    (lu < pLogicalUnitOidList->oidCount);
 				    lu++) {
 					bListIt = B_FALSE;
-					/* get lu props & check the name */
-					(void) memset(&luProps, 0,
-				sizeof (MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES));
+			/* begin backup indentation */
+			/* get lu props & check the name */
+			(void) memset(&luProps, 0,
+			    sizeof (MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES));
+			/* end backup indentation */
 					mpstatus =
 					    MP_GetMPLogicalUnitProperties(
 					    pLogicalUnitOidList->oids[lu],
@@ -968,7 +957,7 @@
 		    (B_FALSE == bListIt); tpg++) {
 			mpstatus =
 			    MP_GetTargetPortOidList(pTpgOidListArray->oids[tpg],
-				&pTportOidListArray);
+			    &pTportOidListArray);
 			if (mpstatus != MP_STATUS_SUCCESS) {
 				(void) fprintf(stderr, "%s:  %s\n",
 				    cmdName,
@@ -1148,8 +1137,7 @@
 	MP_PATH_LOGICAL_UNIT_PROPERTIES		pathProps;
 	MP_OID_LIST				*pPathOidListArray;
 	MP_STATUS				mpstatus = MP_STATUS_SUCCESS;
-	int					numOperationalPaths,
-						pa;
+	int					numOperationalPaths, pa;
 
 	(void) printf("\t");
 	displayArray(luProps.deviceFileName, sizeof (luProps.deviceFileName));
@@ -1161,7 +1149,7 @@
 		/* LINTED E_SEC_PRINTF_VAR_FMT */
 		(void) fprintf(stderr,
 		    getTextString(ERR_NO_LU_PATH_INFO_WITH_MISSING_LU_STR),
-		getStringArray(luProps.deviceFileName,
+		    getStringArray(luProps.deviceFileName,
 		    sizeof (luProps.deviceFileName)));
 		(void) fprintf(stderr, "\n");
 		return (mpstatus);
@@ -1213,8 +1201,7 @@
 	MP_STATUS				mpstatus = MP_STATUS_SUCCESS;
 	MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES	luProps;
 	MP_PLUGIN_PROPERTIES			pluginProps;
-	MP_OID					luOid,
-						pluginOid;
+	MP_OID					luOid, pluginOid;
 
 	int					op;
 
@@ -1292,21 +1279,18 @@
 	MP_TARGET_PORT_GROUP_PROPERTIES		tpgProps;
 	MP_TARGET_PORT_PROPERTIES 		tportProps;
 	MP_INITIATOR_PORT_PROPERTIES 		initProps;
-	MP_OID_LIST				*pPathOidListArray,
-						*pTPGOidListArray,
-						*pTportOidListArray;
+	MP_OID_LIST	*pPathOidListArray, *pTPGOidListArray,
+	    *pTportOidListArray;
 	MP_STATUS				mpstatus = MP_STATUS_SUCCESS;
 	boolean_t				showTportLabel = B_TRUE;
 
-	int					pa,
-						tpg,
-						tport;
+	int					pa, tpg, tport;
 
 	(void) printf("%s  ", getTextString(TEXT_LB_LOGICAL_UNIT));
 	displayArray(luProps.deviceFileName, sizeof (luProps.deviceFileName));
 	(void) printf("\n");
 	(void) printf("\t%s  %s\n", getTextString(TEXT_LB_MPATH_SUPPORT),
-		pluginProps.fileName);
+	    pluginProps.fileName);
 
 	(void) printf("\t%s  ", getTextString(TEXT_LB_VENDOR));
 	displayArray(luProps.vendor,
@@ -1467,14 +1451,16 @@
 					    getAccessStateStr(
 					    tpgProps.accessState));
 					    /* display label for each tpg. */
-					    (void) printf("\t\t%s\n",
-						getTextString(TEXT_TPORT_LIST));
+					(void) printf("\t\t%s\n",
+					    getTextString(TEXT_TPORT_LIST));
 				} else {
 					/* display label once for symmetric. */
 					if (B_TRUE == showTportLabel) {
-						(void) printf("\t%s\n",
-						getTextString(TEXT_TPORT_LIST));
-						showTportLabel = B_FALSE;
+					/* begin back-up indentation */
+					(void) printf("\t%s\n",
+					    getTextString(TEXT_TPORT_LIST));
+					showTportLabel = B_FALSE;
+					/* end back-up indentation */
 					}
 				}
 
@@ -1496,28 +1482,28 @@
 		    MP_GetTargetPortProperties(pTportOidListArray->oids[tport],
 		    &tportProps)) != MP_STATUS_SUCCESS) {
 			(void) fprintf(stderr, "%s:  %s",
-			cmdName, getTextString(ERR_NO_PROPERTIES));
+			    cmdName, getTextString(ERR_NO_PROPERTIES));
 		} else {
 			if (MP_TRUE == luProps.asymmetric) {
 				(void) printf("\t\t\t%s  ",
-				getTextString(TEXT_LB_NAME));
+				    getTextString(TEXT_LB_NAME));
 				displayArray(tportProps.portID,
-					sizeof (tportProps.portID));
+				    sizeof (tportProps.portID));
 				(void) printf("\n\t\t\t%s  %d\n",
-				getTextString(TEXT_LB_RELATIVE_ID),
-				tportProps.relativePortID);
+				    getTextString(TEXT_LB_RELATIVE_ID),
+				    tportProps.relativePortID);
 			} else {
 				(void) printf("\t\t%s  ",
-				getTextString(TEXT_LB_NAME));
+				    getTextString(TEXT_LB_NAME));
 				displayArray(tportProps.portID,
-					sizeof (tportProps.portID));
+				    sizeof (tportProps.portID));
 				(void) printf("\n\t\t%s  %d\n",
-				getTextString(TEXT_LB_RELATIVE_ID),
-				tportProps.relativePortID);
+				    getTextString(TEXT_LB_RELATIVE_ID),
+				    tportProps.relativePortID);
 			}
 			/* insert blank line if not the last target port. */
 			if (!(tport == (pTportOidListArray->oidCount - 1))) {
-			    (void) printf("\n");
+				(void) printf("\n");
 			}
 		}
 	} /* for each target port */
@@ -1551,9 +1537,7 @@
 	MP_STATUS				mpstatus = MP_STATUS_SUCCESS;
 	MP_OID					luOid;
 	cmdOptions_t 				*optionList = options;
-	char					*cmdStr =
-						    getTextString(
-						    TEXT_UNKNOWN);
+	char	*cmdStr = getTextString(TEXT_UNKNOWN);
 	int					op;
 
 	for (op = 0; op < operandLen; op++) {
@@ -1580,14 +1564,15 @@
 					mpstatus =
 					    MP_DisableAutoFailback(luOid);
 				} else {
-					/* LINTED E_SEC_PRINTF_VAR_FMT */
-					(void) fprintf(stderr, getTextString(
+				/* begin back-up indentation */
+				/* LINTED E_SEC_PRINTF_VAR_FMT */
+				(void) fprintf(stderr, getTextString(
 				    ERR_FAILED_TO_CHANGE_OPTION_WITH_REASON),
-					    cmdStr,
-					    getTextString(
-					    TEXT_ILLEGAL_ARGUMENT));
-					(void) printf("\n");
-					return (ERROR_CLI_FAILED);
+				    cmdStr, getTextString(
+				    TEXT_ILLEGAL_ARGUMENT));
+				(void) printf("\n");
+				return (ERROR_CLI_FAILED);
+				/* start back-up indentation */
 				}
 				break;
 			case 'p':
@@ -1598,18 +1583,19 @@
 					mpstatus =
 					    MP_EnableAutoProbing(luOid);
 				} else if (0 == strcasecmp(optionList->optarg,
-					getTextString(TEXT_OFF))) {
+				    getTextString(TEXT_OFF))) {
 					mpstatus =
 					    MP_DisableAutoProbing(luOid);
 				} else {
-					/* LINTED E_SEC_PRINTF_VAR_FMT */
-					(void) fprintf(stderr, getTextString(
+				/* begin back-up indentation */
+				/* LINTED E_SEC_PRINTF_VAR_FMT */
+				(void) fprintf(stderr, getTextString(
 				    ERR_FAILED_TO_CHANGE_OPTION_WITH_REASON),
-					    cmdStr,
-					    getTextString(
-					    TEXT_ILLEGAL_ARGUMENT));
-					(void) printf("\n");
-					return (ERROR_CLI_FAILED);
+				    cmdStr, getTextString(
+				    TEXT_ILLEGAL_ARGUMENT));
+				(void) printf("\n");
+				return (ERROR_CLI_FAILED);
+				/* end back-up indentation */
 				}
 				break;
 			case 'b':
@@ -1689,7 +1675,7 @@
 	    MP_GetAssociatedTPGOidList(luOid, &pTpgOidListArray);
 	if (mpstatus != MP_STATUS_SUCCESS) {
 		(void) fprintf(stderr, "%s:  %s\n",
-		cmdName, getTextString(ERR_NO_ASSOC_TPGS));
+		    cmdName, getTextString(ERR_NO_ASSOC_TPGS));
 		return (mpstatus);
 	}
 
@@ -1709,7 +1695,7 @@
 		}
 		if (MP_FALSE == tpgProps.explicitFailover) {
 			(void) fprintf(stderr, "%s:  %s\n",
-			cmdName, getTextString(ERR_NO_FAILOVER_ALLOWED));
+			    cmdName, getTextString(ERR_NO_FAILOVER_ALLOWED));
 			return (ERROR_CLI_FAILED);
 		}
 
@@ -1726,12 +1712,14 @@
 			mpstatus =
 			    MP_SetTPGAccess(luOid, 1, &tpgStatePair);
 			if (MP_STATUS_SUCCESS != mpstatus) {
-				/* LINTED E_SEC_PRINTF_VAR_FMT */
-				(void) fprintf(stderr, getTextString(
+			/* begin back-up indentation */
+			/* LINTED E_SEC_PRINTF_VAR_FMT */
+			(void) fprintf(stderr, getTextString(
 			    ERR_FAILED_TO_FAILOVER_WITH_REASON),
 			    getMpStatusStr(mpstatus));
-				(void) printf("\n");
-				return (mpstatus);
+			(void) printf("\n");
+			return (mpstatus);
+			/* end back-up indentation */
 			}
 		}
 
@@ -1765,15 +1753,13 @@
 	MP_STATUS				mpstatus = MP_STATUS_SUCCESS;
 	MP_MULTIPATH_LOGICAL_UNIT_PROPERTIES	luProps;
 	MP_PLUGIN_PROPERTIES			pluginProps;
-	MP_OID_LIST				*pPluginOidList,
-						*pLogicalUnitOidList;
+	MP_OID_LIST	*pPluginOidList, *pLogicalUnitOidList;
 	boolean_t				foundIt = B_FALSE;
 
-	int					i,
-						lu;
+	int					i, lu;
 
 	int 					fd1, fd2;
-	ddi_devid_t				devid1 = NULL, devid2 = NULL;
+	ddi_devid_t				devid1, devid2;
 
 	if (NULL == pluOid) {
 		/* print some kind of error msg here - should never happen */
@@ -1788,14 +1774,14 @@
 	pluOid->ownerId = 0;
 
 	if ((mpstatus = MP_GetPluginOidList(&pPluginOidList))
-		!= MP_STATUS_SUCCESS) {
+	    != MP_STATUS_SUCCESS) {
 		(void) fprintf(stderr, "%s: %s\n", cmdName,
 		    getTextString(ERR_NO_MPATH_SUPPORT_LIST));
 		return (B_FALSE);
 	}
 	if ((NULL == pPluginOidList) || (pPluginOidList->oidCount < 1)) {
 		(void) fprintf(stderr, "%s: %s\n", cmdName,
-		getTextString(ERR_NO_MPATH_SUPPORT_LIST));
+		    getTextString(ERR_NO_MPATH_SUPPORT_LIST));
 		return (ERROR_CLI_FAILED);
 	}
 	for (i = 0; i < pPluginOidList->oidCount; i++) {
@@ -1805,9 +1791,9 @@
 		if ((mpstatus =
 		    MP_GetPluginProperties(pPluginOidList->oids[i],
 		    &pluginProps)) != MP_STATUS_SUCCESS) {
-				(void) fprintf(stderr, "%s:  %s\n",
-				    cmdName, getTextString(ERR_NO_PROPERTIES));
-				return (B_FALSE);
+			(void) fprintf(stderr, "%s:  %s\n",
+			    cmdName, getTextString(ERR_NO_PROPERTIES));
+			return (B_FALSE);
 		}
 
 		/* attempt to find this logical unit */
@@ -1834,7 +1820,8 @@
 				return (B_FALSE);
 			}
 
-			if (0 == strcmp(luFileName, luProps.deviceFileName)) {
+			if (compareLUName(luFileName, luProps.deviceFileName)
+			    == B_TRUE) {
 				foundIt = B_TRUE;
 			} else {
 				/* user input didn't match, try via devid */
@@ -1845,10 +1832,11 @@
 				 */
 
 				fd1 = fd2 = -1;
+				devid1 = devid2 = NULL;
 				if (((fd1 = open(luFileName,
-					O_RDONLY|O_NDELAY)) >= 0) &&
+				    O_RDONLY|O_NDELAY)) >= 0) &&
 				    ((fd2 = open(luProps.deviceFileName,
-					O_RDONLY|O_NDELAY)) >= 0) &&
+				    O_RDONLY|O_NDELAY)) >= 0) &&
 				    (devid_get(fd1, &devid1) == 0) &&
 				    (devid_get(fd2, &devid2) == 0) &&
 				    ((NULL != devid1) && (NULL != devid2))) {
@@ -1905,14 +1893,11 @@
 {
 	MP_STATUS				mpstatus = MP_STATUS_SUCCESS;
 	MP_INITIATOR_PORT_PROPERTIES 		initProps;
-	MP_OID_LIST				*pPluginOidList,
-						*pInitOidList;
+	MP_OID_LIST	*pPluginOidList, *pInitOidList;
 	boolean_t				bListIt = B_FALSE;
 	boolean_t				*foundOp;
 
-	int					ol,
-						i,
-						iport;
+	int		ol, i, iport;
 
 	foundOp = malloc((sizeof (boolean_t)) * operandLen);
 	if (NULL == foundOp) {
@@ -1926,7 +1911,7 @@
 	}
 
 	if ((mpstatus = MP_GetPluginOidList(&pPluginOidList))
-		!= MP_STATUS_SUCCESS) {
+	    != MP_STATUS_SUCCESS) {
 		(void) fprintf(stderr, "%s: %s\n", cmdName,
 		    getTextString(ERR_NO_MPATH_SUPPORT_LIST));
 		return (mpstatus);
@@ -1948,7 +1933,7 @@
 			    getMpStatusStr(mpstatus));
 			(void) printf("\n");
 		} else if ((NULL == pInitOidList) ||
-			(pInitOidList->oidCount < 1)) {
+		    (pInitOidList->oidCount < 1)) {
 			(void) fprintf(stderr, "%s: %s\n", cmdName,
 			    getTextString(ERR_NO_INIT_PORTS));
 		} else {
@@ -2032,7 +2017,7 @@
 
 	(void) printf("%s  ", getTextString(TEXT_LB_INITATOR_PORT));
 	displayArray(initProps.portID,
-		sizeof (initProps.portID));
+	    sizeof (initProps.portID));
 	(void) printf("\n");
 
 	return (mpstatus);
@@ -2056,16 +2041,12 @@
 {
 	MP_STATUS				mpstatus = MP_STATUS_SUCCESS;
 	MP_INITIATOR_PORT_PROPERTIES 		initProps;
-	MP_OID_LIST				*pPluginOidList,
-						*pInitOidList;
-	boolean_t				bListIt = B_FALSE,
-						bFoundIt = B_FALSE;
-	int					op,
-						i,
-						iport;
+	MP_OID_LIST	*pPluginOidList, *pInitOidList;
+	boolean_t	bListIt = B_FALSE, bFoundIt = B_FALSE;
+	int		op, i, iport;
 
 	if ((mpstatus = MP_GetPluginOidList(&pPluginOidList))
-		!= MP_STATUS_SUCCESS) {
+	    != MP_STATUS_SUCCESS) {
 		(void) fprintf(stderr, "%s: %s\n", cmdName,
 		    getTextString(ERR_NO_MPATH_SUPPORT_LIST));
 		return (mpstatus);
@@ -2107,9 +2088,11 @@
 					    pInitOidList->oids[iport],
 					    &initProps))
 					    != MP_STATUS_SUCCESS) {
-						(void) fprintf(stderr,
-						    "%s: %s\n", cmdName,
+					/* begin back-up indentation */
+					(void) fprintf(stderr,
+					    "%s: %s\n", cmdName,
 					    getTextString(ERR_NO_PROPERTIES));
+					/* end back-up indentation */
 					} else {
 						if (0 == strcmp(operand[op],
 						    initProps.portID)) {
@@ -2120,8 +2103,8 @@
 
 					if (B_TRUE == bListIt) {
 						mpstatus =
-						showIndividualInitiatorPort(
-						initProps);
+						    showIndividualInitiatorPort(
+						    initProps);
 						if (0 != mpstatus) {
 							return (mpstatus);
 						}
@@ -2199,9 +2182,7 @@
 	MP_OID					pathOid;
 
 	cmdOptions_t 				*optionList = options;
-	boolean_t				bHaveInit = B_FALSE,
-						bHaveTarg = B_FALSE,
-						bHaveLu = B_FALSE;
+	boolean_t   bHaveInit = B_FALSE, bHaveTarg = B_FALSE, bHaveLu = B_FALSE;
 
 	for (; optionList->optval; optionList++) {
 		switch (optionList->optval) {
@@ -2284,9 +2265,8 @@
 	MP_OID					pathOid;
 
 	cmdOptions_t 				*optionList = options;
-	boolean_t				bHaveInit = B_FALSE,
-						bHaveTarg = B_FALSE,
-						bHaveLu = B_FALSE;
+	boolean_t	bHaveInit = B_FALSE, bHaveTarg = B_FALSE,
+	    bHaveLu = B_FALSE;
 
 	for (; optionList->optval; optionList++) {
 		switch (optionList->optval) {
@@ -2471,24 +2451,18 @@
 	MP_INITIATOR_PORT_PROPERTIES		initProps;
 	MP_TARGET_PORT_PROPERTIES		targProps;
 
-	MP_OID_LIST				*pPluginOidList,
-						*pLogicalUnitOidList,
-						*pathOidListArray;
+	MP_OID_LIST	*pPluginOidList, *pLogicalUnitOidList,
+	    *pathOidListArray;
 
 	boolean_t				bFoundIt = B_FALSE;
 	MP_CHAR					initPortID[256];
 	MP_CHAR					targetPortID[256];
 	MP_CHAR					luDeviceFileName[256];
-	boolean_t				bHaveTarg = B_FALSE,
-						bHaveLu = B_FALSE,
-						bHaveInit = B_FALSE;
-
-
+	boolean_t	bHaveTarg = B_FALSE, bHaveLu = B_FALSE,
+	    bHaveInit = B_FALSE;
 	cmdOptions_t 				*optionList = options;
 
-	int					i,
-						lu,
-						pa;
+	int					i, lu, pa;
 	if (NULL == pPathOid) {
 		return (B_FALSE);
 	}
@@ -2550,7 +2524,7 @@
 		}
 
 		for (lu = 0; (lu < pLogicalUnitOidList->oidCount) &&
-			(B_FALSE == bFoundIt); lu++) {
+		    (B_FALSE == bFoundIt); lu++) {
 
 			/* get lu properties so we can check the name */
 			(void) memset(&luProps, 0,
@@ -2563,8 +2537,8 @@
 				    cmdName, getTextString(ERR_NO_PROPERTIES));
 				return (B_FALSE);
 			}
-			if (0 == strcmp(luDeviceFileName,
-			    luProps.deviceFileName)) {
+			if (compareLUName(luDeviceFileName,
+			    luProps.deviceFileName) == B_TRUE) {
 				/* get paths for this LU and search from here */
 				mpstatus =
 				    MP_GetAssociatedPathOidList(
@@ -2580,8 +2554,8 @@
 				}
 
 				for (pa = 0;
-					(pa < pathOidListArray->oidCount) &&
-					(B_FALSE == bFoundIt); pa++) {
+				    (pa < pathOidListArray->oidCount) &&
+				    (B_FALSE == bFoundIt); pa++) {
 					mpstatus =
 					    MP_GetPathLogicalUnitProperties
 					    (pathOidListArray->oids[pa],
@@ -2767,7 +2741,7 @@
 			break;
 		case MP_LU_NAME_TYPE_DEVICE_SPECIFIC:
 			typeString =
-			getTextString(TEXT_NAME_TYPE_DEVICE_SPECIFIC);
+			    getTextString(TEXT_NAME_TYPE_DEVICE_SPECIFIC);
 			break;
 		default:
 			typeString = getTextString(TEXT_UNKNOWN);
@@ -3203,7 +3177,7 @@
 			break;
 		default:
 			(void) fprintf(stderr, "%s: %s\n",
-		    cmdName, getTextString(TEXT_UNKNOWN_OBJECT));
+			    cmdName, getTextString(TEXT_UNKNOWN_OBJECT));
 			ret = 1;
 			break;
 	}
--- a/usr/src/cmd/mv/mv.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/mv/mv.c	Mon Oct 05 09:17:26 2009 -0400
@@ -18,6 +18,7 @@
  *
  * CDDL HEADER END
  */
+
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
@@ -97,7 +98,6 @@
 static int		use_stdin(void);
 static int		copyattributes(char *, char *);
 static int		copy_sysattr(char *, char *);
-static void		timestruc_to_timeval(timestruc_t *, struct timeval *);
 static tree_node_t	*create_tnode(dev_t, ino_t);
 
 static struct stat 	s1, s2, s3, s4;
@@ -1341,23 +1341,23 @@
  *
  * Try to preserve modification and access time.
  * If 1) pflg is not set, or 2) pflg is set and this is the Solaris version,
- * don't report a utime() failure.
- * If this is the XPG4 version and utime fails, if 1) pflg is set (cp -p)
+ * don't report a utimensat() failure.
+ * If this is the XPG4 version and utimensat fails, if 1) pflg is set (cp -p)
  * or 2) we are doing a mv, print a diagnostic message; arrange for a non-zero
  * exit status only if pflg is set.
- * utimes(2) is being used to achieve granularity in
- * microseconds while setting file times.
+ * utimensat(2) is being used to achieve granularity in nanoseconds
+ * (if supported by the underlying file system) while setting file times.
  */
 static int
 chg_time(char *to, struct stat ss)
 {
-	struct timeval times[2];
+	struct timespec times[2];
 	int rc;
 
-	timestruc_to_timeval(&ss.st_atim, times);
-	timestruc_to_timeval(&ss.st_mtim, times + 1);
+	times[0] = ss.st_atim;
+	times[1] = ss.st_mtim;
 
-	rc = utimes(to, times);
+	rc = utimensat(AT_FDCWD, to, times, 0);
 #ifdef XPG4
 	if ((pflg || mve) && rc != 0) {
 		(void) fprintf(stderr,
@@ -1630,7 +1630,7 @@
 	int clearflg = 0;
 	acl_t *xacl = NULL;
 	acl_t *attrdiracl = NULL;
-	struct timeval times[2];
+	struct timespec times[2];
 
 
 	if (pathconf(source,  _PC_XATTR_EXISTS) != 1)
@@ -1677,13 +1677,11 @@
 		}
 		/*
 		 * Now that we are the owner we can update st_ctime by calling
-		 * futimesat.
+		 * utimensat.
 		 */
-		times[0].tv_sec = attrdir.st_atime;
-		times[0].tv_usec = 0;
-		times[1].tv_sec = attrdir.st_mtime;
-		times[1].tv_usec = 0;
-		if (futimesat(targetdirfd, ".", times) < 0) {
+		times[0] = attrdir.st_atim;
+		times[1] = attrdir.st_mtim;
+		if (utimensat(targetdirfd, ".", times, 0) < 0) {
 			if (!attrsilent) {
 				(void) fprintf(stderr,
 				    gettext("%s: cannot set attribute times"
@@ -1792,10 +1790,9 @@
 					++clearflg;
 				}
 			}
-			/* tv_usec were cleared above */
-			times[0].tv_sec = s3.st_atime;
-			times[1].tv_sec = s3.st_mtime;
-			if (futimesat(targetdirfd, dp->d_name, times) < 0) {
+			times[0] = s3.st_atim;
+			times[1] = s3.st_mtim;
+			if (utimensat(targetdirfd, dp->d_name, times, 0) < 0) {
 				if (!attrsilent) {
 					(void) fprintf(stderr,
 					    gettext("%s: cannot set attribute"
@@ -1973,16 +1970,6 @@
 	return (error == 0 ? 0 : 1);
 }
 
-/*
- * nanoseconds are rounded off to microseconds by flooring.
- */
-static void
-timestruc_to_timeval(timestruc_t *ts, struct timeval *tv)
-{
-	tv->tv_sec = ts->tv_sec;
-	tv->tv_usec = ts->tv_nsec / 1000;
-}
-
 /* Open the source file */
 
 int
--- a/usr/src/cmd/pcidr/plugins/default/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/pcidr/plugins/default/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -19,10 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
 #
 
 .PARALLEL:
@@ -41,7 +40,8 @@
 include $(SRC)/cmd/pcidr/Makefile.com
 #############################################################################
 
-THISDIR = $(TOP)/plugins/default
+THISDIR = ../../plugins/default
+USR_SRC_BASE = ../../../..
 
 # SRCS is used by the lintcheck rule and is defined as
 # $(OBJECTS:%.o=$(SRCDIR)/%.c) where SRCDIR is this directory; set SRCS to a
@@ -49,7 +49,7 @@
 #
 
 SRCDIR = .
-SRCS = $(TOPOBJECTS:%.o=$(TOP)/%.c) $(LOCOBJECTS:%.o=$(SRCDIR)/%.c)
+SRCS = $(TOPOBJECTS:%.o=$(USR_SRC_BASE)/cmd/pcidr/%.c) $(LOCOBJECTS:%.o=$(SRCDIR)/%.c) 
 
 LIBS = $(DYNLIB)
 
@@ -75,16 +75,7 @@
 
 $(ROOTLIBS): all
 
-# Note that we can't do:
-#	$(TOPOBJECTS:%=objs/%) := SRCDIR = $(TOP)
-# and let the make do the rest because the man page states:
-# "Notice that if a conditional macro is referred to in a  dependency list,
-#  the $ must be delayed (use $$ instead)."
-#
-# So we must add new targets for our TOPOBJECTS items but follow
-# the existing pic/*.o and objs/*.o rules in lib/Makefile.targ
-#
-objs/%.o pics/%.o: $(TOP)/%.c
+objs/%.o pics/%.o: $(USR_SRC_BASE)/cmd/pcidr/%.c
 	$(COMPILE.c) -o $@ $<
 	$(POST_PROCESS_O)
 
--- a/usr/src/cmd/prtconf/pdevinfo.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/prtconf/pdevinfo.c	Mon Oct 05 09:17:26 2009 -0400
@@ -770,7 +770,6 @@
 		(void) printf(" (retired)");
 	} else if (di_state(node) & DI_DRIVER_DETACHED)
 		(void) printf(" (driver not attached)");
-
 	(void) printf("\n");
 
 	if (opts.o_verbose)  {
@@ -1030,6 +1029,13 @@
 		return;
 
 	while ((pi = di_path_client_next_path(node, pi)) != DI_PATH_NIL) {
+
+		/* It is not really a path if we failed to capture the pHCI */
+		phci_node = di_path_phci_node(pi);
+		if (phci_node == DI_NODE_NIL)
+			continue;
+
+		/* Print header for the first path */
 		if (firsttime) {
 			indent_to_level(ilev);
 			firsttime = 0;
@@ -1042,10 +1048,9 @@
 		 * the same as the /devices devifo path had the device been
 		 * enumerated under pHCI.
 		 */
-		phci_node = di_path_phci_node(pi);
 		phci_path = di_devfs_path(phci_node);
-		path_instance = di_path_instance(pi);
 		if (phci_path) {
+			path_instance = di_path_instance(pi);
 			if (path_instance > 0) {
 				indent_to_level(ilev);
 				(void) printf("Path %d: %s/%s@%s\n",
@@ -1060,6 +1065,7 @@
 		indent_to_level(ilev);
 		(void) printf("%s#%d (%s)\n", di_driver_name(phci_node),
 		    di_instance(phci_node), path_state_name(di_path_state(pi)));
+
 		(void) dump_prop_list(&pathprop_dumpops, NULL, ilev + 1,
 		    pi, DDI_DEV_T_ANY, NULL);
 	}
--- a/usr/src/cmd/rpcsvc/net_files/rpc	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/rpcsvc/net_files/rpc	Mon Oct 05 09:17:26 2009 -0400
@@ -1,5 +1,5 @@
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # CDDL HEADER START
@@ -21,8 +21,6 @@
 #
 # CDDL HEADER END
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 #	rpc
 #
 rpcbind		100000	portmap sunrpc rpcbind
@@ -86,7 +84,6 @@
 nispasswd	100303	rpc.nispasswdd
 ufsd		100233	ufsd
 pcnfsd		150001
-ocfserv		100150
 metad		100229	metad
 metamhd		100230	metamhd
 metamedd	100242	metamedd
--- a/usr/src/cmd/stmsboot/stmsboot.sh	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/stmsboot/stmsboot.sh	Mon Oct 05 09:17:26 2009 -0400
@@ -39,7 +39,7 @@
 BOOTDEVICES=$SAVEDIR/boot-devices
 RECOVERFILE=$SAVEDIR/recover_instructions
 SVCCFG_RECOVERY=$SAVEDIR/svccfg_recover
-SUPPORTED_DRIVERS="fp|mpt|mpt_sas"
+SUPPORTED_DRIVERS="fp|mpt|mpt_sas|pmcs"
 USAGE=`gettext "Usage: stmsboot [-D $SUPPORTED_DRIVERS] -e | -d | -u | -L | -l controller_number"`
 TEXTDOMAIN=SUNW_OST_OSCMD
 export TEXTDOMAIN
@@ -290,7 +290,7 @@
 # Returns 0 on success, 1 on failure.
 #
 # Args: $cmd = {enable | disable}
-#	$d = {fp | mpt | mpt_sas}
+#	$d = {fp | mpt | mpt_sas | pmcs}
 #
 # the global variable $DRVLIST is used
 #
@@ -417,7 +417,7 @@
 # Emit a warning message to the user that by default we
 # operate on all multipath-capable controllers that are
 # attached to the system, and that if they want to operate
-# on only a specific controller type (fp|mpt|mpt_sas|....) then 
+# on only a specific controller type (fp|mpt|mpt_sas|pmcs|....) then 
 # they need to re-invoke stmsboot with "-D $driver" in
 # their argument list
 #
@@ -437,7 +437,7 @@
 	
 	echo ""
 	gettext "If you do NOT wish to operate on these controllers, please quit stmsboot\n"
-	gettext "and re-invoke with -D { fp | mpt | mpt_sas} to specify which controllers you wish\n"
+	gettext "and re-invoke with -D { fp | mpt | mpt_sas | pmcs} to specify which controllers you wish\n"
 	gettext "to modify your multipathing configuration for.\n"
 
 	echo ""
@@ -480,7 +480,7 @@
 fi
 
 if [ "x$DRV" = "x" ]; then
-	DRVLIST="fp mpt mpt_sas"
+	DRVLIST="fp mpt mpt_sas pmcs"
 else
 	DRVLIST=$DRV
 fi
--- a/usr/src/cmd/stmsboot/stmsboot_util.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/stmsboot/stmsboot_util.c	Mon Oct 05 09:17:26 2009 -0400
@@ -143,6 +143,8 @@
 			    devinfo_root));
 			print_mpx_capable(di_drv_first_node("mpt_sas",
 			    devinfo_root));
+			print_mpx_capable(di_drv_first_node("pmcs",
+			    devinfo_root));
 		}
 		di_fini(devinfo_root);
 		return (0);
@@ -401,7 +403,8 @@
 			/* update this if adding support for a new driver */
 			if ((strncmp(drvlimit, "fp", 2) == NULL) &&
 			    (strncmp(drvlimit, "mpt", 3) == NULL) &&
-			    (strncmp(drvlimit, "mpt_sas", 7) == NULL)) {
+			    (strncmp(drvlimit, "mpt_sas", 7) == NULL) &&
+			    (strncmp(drvlimit, "pmcs", 4) == NULL)) {
 				logmsg(MSG_ERROR,
 				    gettext("invalid parent driver (%s) "
 				    "specified"), drvlimit);
--- a/usr/src/cmd/svc/profile/generic_limited_net.xml	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/svc/profile/generic_limited_net.xml	Mon Oct 05 09:17:26 2009 -0400
@@ -299,9 +299,6 @@
   <!--
 	non-default inetd(1M) RPC services disabled
   -->
-  <service name='network/rpc/ocfserv' version='1' type='service'>
-    <instance name='default' enabled='false'/>
-  </service>
   <service name='network/rpc/rex' version='1' type='service'>
     <instance name='default' enabled='false'/>
   </service>
--- a/usr/src/cmd/svc/shell/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/svc/shell/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -32,6 +32,7 @@
 	routing_include.sh \
 	sendmail_include.sh \
 	smf_include.sh \
+	mfsthistory \
 	manifest_cleanup.ksh
 
 SCRIPTS = $(SRCS:%=$(ROOT)/lib/svc/share/%)
--- a/usr/src/cmd/svc/shell/manifest_cleanup.ksh	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/svc/shell/manifest_cleanup.ksh	Mon Oct 05 09:17:26 2009 -0400
@@ -36,6 +36,7 @@
 MFSTSCAN=/lib/svc/bin/mfstscan
 MCLEANUPFILE=/etc/svc/volatile/mcleanup.$$
 IGNORELIST="system/install-discovery smf/manifest"
+MFSTHISTORY=/lib/svc/share/mfsthistory
 UPLIST=0
 
 #
@@ -58,9 +59,6 @@
 
 
 			eval $cl_invent=\"\$$cl_invent $cl_mfile\"
-			# XXX - remove this line at some point "
-			# I clears up the above escaped quote throwing
-			# off my color scheme in vim.
 		done
 	done
 	UPLIST=1
@@ -152,13 +150,17 @@
 	#
 	for mf in ${ps_mfiles[@]}
 	do
+		#
+		# This is an unsupported service just return
+		# skipping the service.
+		#
+		[ ${mf%/var/svc/manifest*} ] && return
+
 		if [ ! -f $mf ]; then
 			ps_mfiles_tmp="$ps_mfiles_tmp $mf"
 			continue
 		fi
 
-		[ ${mf%/var/svc/manifest*} ] && continue
-
 		inst=`get_instances $mf`
 
 		set -A ps_inst_list
@@ -328,6 +330,12 @@
 	if [ "$1" == "FALSE" ]; then
 		am_lisnap=1
 		am_inst=`svcs -H -oFMRI $am_service 2>/dev/null`
+
+		#
+		# Check for a last-import snapshot, if there is not
+		# one then the service was hand crafted and the support
+		# should be set to false.
+		#
 		if [ $? -eq 0 ]; then
 			for i in $am_inst
 			do
@@ -336,16 +344,33 @@
 			done
 		fi
 
-		if [ $am_lisnap -eq 0 ]; then
+		if [ $am_lisnap -ne 0 ]; then
+			$SVCCFG -s $am_service setprop $MFSTPG/support = boolean: 0
+
+			return
+		fi
+
+		#
+		# If the service was not hand crafted then check to see if
+		# the service has ever been installed in the /var/svc/manifest
+		# directory and therefore a known removed service.
+		#
+		grep "$am_service " $MFSTHISTORY | grep -v "^#" > /dev/null 2>&1
+		if [ $? -eq 0 ]; then
+			echo "$SVCCFG delete $am_service"
 			$SVCCFG delete -f $am_service
 		else
+			#
+			# Do not know where the service came from so set
+			# it to false.
+			#
 			$SVCCFG -s $am_service setprop $MFSTPG/support = boolean: 0
 		fi
 	else
 		for am_mfile in $@
 		do
 			CF=${am_mfile#/*}
-			CF=`echo $CF | sed -e 's/[-\/\,\.]/_/g'`
+			CF=`echo $CF | sed -e 's/[\/\,\.]/_/g'`
 			$SVCCFG -s $am_service setprop $MFSTPG/$CF = astring: $am_mfile
 		done
 	fi
@@ -434,9 +459,11 @@
 	do
 		svcprop -q -p $MFSTPG $service
 		if [ $? -ne 0 ]; then
-			if [[ $IGNORELIST == $ps_service ]]; then
+			mc_igchk=`eval expr \"$IGNORELIST \" : "'.*\($service \)'"`
+			if [[ -n $mc_igchk ]]; then
 				echo "add_manifest $service FALSE"
 				add_manifest $service FALSE
+				continue
 			fi
 			
 			[ $UPLIST -eq 0 ] && create_list
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svc/shell/mfsthistory	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,662 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# 
+# This file is for historical purposes only, and should not be modified.
+# 
+svc:/platform/sun4us/fjsvdrd:default var/svc/manifest/platform/sun4us/fjsvdrd.xml
+svc:/platform/sun4us/fjsvdrd var/svc/manifest/platform/sun4us/fjsvdrd.xml
+svc:/system/sar:default var/svc/manifest/system/sar.xml
+svc:/system/sar var/svc/manifest/system/sar.xml
+svc:/system/sysidtool:system var/svc/manifest/system/sysidtool.xml
+svc:/system/sysidtool:net var/svc/manifest/system/sysidtool.xml
+svc:/system/sysidtool var/svc/manifest/system/sysidtool.xml
+svc:/network/http:apache2 var/svc/manifest/network/http-apache2.xml
+svc:/network/http var/svc/manifest/network/http-apache2.xml
+svc:/network/apocd/udp:default var/svc/manifest/network/apocd.xml
+svc:/network/apocd/udp var/svc/manifest/network/apocd.xml
+svc:/system/filesystem/autofs:default var/svc/manifest/system/filesystem/autofs.xml
+svc:/system/filesystem/autofs var/svc/manifest/system/filesystem/autofs.xml
+svc:/network/dns/server:bind9 var/svc/manifest/network/dns/bind9-server.xml
+svc:/network/dns/server var/svc/manifest/network/dns/bind9-server.xml
+svc:/network/uucp:default var/svc/manifest/network/uucp.xml
+svc:/network/uucp var/svc/manifest/network/uucp.xml
+svc:/network/rarp:default var/svc/manifest/network/rarp.xml
+svc:/network/rarp var/svc/manifest/network/rarp.xml
+svc:/network/rpc/bootparams:default var/svc/manifest/network/rpc/bootparams.xml
+svc:/network/rpc/bootparams var/svc/manifest/network/rpc/bootparams.xml
+svc:/system/device/fc-fabric:default var/svc/manifest/system/device/devices-fc-fabric.xml
+svc:/system/device/fc-fabric var/svc/manifest/system/device/devices-fc-fabric.xml
+svc:/system/dumpadm:default var/svc/manifest/system/dumpadm.xml
+svc:/system/dumpadm var/svc/manifest/system/dumpadm.xml
+svc:/system/fmd:default var/svc/manifest/system/fmd.xml
+svc:/system/fmd var/svc/manifest/system/fmd.xml
+svc:/network/chargen:stream var/svc/manifest/network/chargen.xml
+svc:/network/chargen:dgram var/svc/manifest/network/chargen.xml
+svc:/network/chargen var/svc/manifest/network/chargen.xml
+svc:/network/daytime:stream var/svc/manifest/network/daytime.xml
+svc:/network/daytime:dgram var/svc/manifest/network/daytime.xml
+svc:/network/daytime var/svc/manifest/network/daytime.xml
+svc:/network/discard:stream var/svc/manifest/network/discard.xml
+svc:/network/discard:dgram var/svc/manifest/network/discard.xml
+svc:/network/discard var/svc/manifest/network/discard.xml
+svc:/network/echo:stream var/svc/manifest/network/echo.xml
+svc:/network/echo:dgram var/svc/manifest/network/echo.xml
+svc:/network/echo var/svc/manifest/network/echo.xml
+svc:/network/time:stream var/svc/manifest/network/time.xml
+svc:/network/time:dgram var/svc/manifest/network/time.xml
+svc:/network/time var/svc/manifest/network/time.xml
+svc:/system/sysevent:default var/svc/manifest/system/sysevent.xml
+svc:/system/sysevent var/svc/manifest/system/sysevent.xml
+svc:/milestone/multi-user-server:default var/svc/manifest/milestone/multi-user-server.xml
+svc:/milestone/multi-user-server var/svc/manifest/milestone/multi-user-server.xml
+svc:/milestone/multi-user:default var/svc/manifest/milestone/multi-user.xml
+svc:/milestone/multi-user var/svc/manifest/milestone/multi-user.xml
+svc:/milestone/name-services:default var/svc/manifest/milestone/name-services.xml
+svc:/milestone/name-services var/svc/manifest/milestone/name-services.xml
+svc:/milestone/network:default var/svc/manifest/milestone/network.xml
+svc:/milestone/network var/svc/manifest/milestone/network.xml
+svc:/milestone/single-user:default var/svc/manifest/milestone/single-user.xml
+svc:/milestone/single-user var/svc/manifest/milestone/single-user.xml
+svc:/network/dns/client:default var/svc/manifest/network/dns/client.xml
+svc:/network/dns/client var/svc/manifest/network/dns/client.xml
+svc:/network/dns/server:default var/svc/manifest/network/dns/server.xml
+svc:/network/dns/server var/svc/manifest/network/dns/server.xml
+svc:/network/inetd-upgrade:default var/svc/manifest/network/inetd-upgrade.xml
+svc:/network/inetd-upgrade var/svc/manifest/network/inetd-upgrade.xml
+svc:/network/inetd:default var/svc/manifest/network/inetd.xml
+svc:/network/inetd var/svc/manifest/network/inetd.xml
+svc:/network/ldap/client:default var/svc/manifest/network/ldap/client.xml
+svc:/network/ldap/client var/svc/manifest/network/ldap/client.xml
+svc:/network/initial:default var/svc/manifest/network/network-initial.xml
+svc:/network/initial var/svc/manifest/network/network-initial.xml
+svc:/network/loopback:default var/svc/manifest/network/network-loopback.xml
+svc:/network/loopback var/svc/manifest/network/network-loopback.xml
+svc:/network/physical:default var/svc/manifest/network/network-physical.xml
+svc:/network/physical var/svc/manifest/network/network-physical.xml
+svc:/network/service:default var/svc/manifest/network/network-service.xml
+svc:/network/service var/svc/manifest/network/network-service.xml
+svc:/network/rpc/bind:default var/svc/manifest/network/rpc/bind.xml
+svc:/network/rpc/bind var/svc/manifest/network/rpc/bind.xml
+svc:/network/rpc/keyserv:default var/svc/manifest/network/rpc/keyserv.xml
+svc:/network/rpc/keyserv var/svc/manifest/network/rpc/keyserv.xml
+svc:/platform/sun4u/mpxio-upgrade:default var/svc/manifest/platform/sun4u/mpxio-upgrade.xml
+svc:/platform/sun4u/mpxio-upgrade var/svc/manifest/platform/sun4u/mpxio-upgrade.xml
+svc:/system/consadm:default var/svc/manifest/system/consadm.xml
+svc:/system/consadm var/svc/manifest/system/consadm.xml
+svc:/system/console-login:default var/svc/manifest/system/console-login.xml
+svc:/system/console-login var/svc/manifest/system/console-login.xml
+svc:/system/coreadm:default var/svc/manifest/system/coreadm.xml
+svc:/system/coreadm var/svc/manifest/system/coreadm.xml
+svc:/system/cron:default var/svc/manifest/system/cron.xml
+svc:/system/cron var/svc/manifest/system/cron.xml
+svc:/system/cryptosvc:default var/svc/manifest/system/cryptosvc.xml
+svc:/system/cryptosvc var/svc/manifest/system/cryptosvc.xml
+svc:/system/device/local:default var/svc/manifest/system/device/devices-local.xml
+svc:/milestone/devices:default var/svc/manifest/system/device/devices-local.xml
+svc:/system/device/local var/svc/manifest/system/device/devices-local.xml
+svc:/milestone/devices var/svc/manifest/system/device/devices-local.xml
+svc:/system/filesystem/local:default var/svc/manifest/system/filesystem/local-fs.xml
+svc:/system/filesystem/local var/svc/manifest/system/filesystem/local-fs.xml
+svc:/system/filesystem/minimal:default var/svc/manifest/system/filesystem/minimal-fs.xml
+svc:/system/filesystem/minimal var/svc/manifest/system/filesystem/minimal-fs.xml
+svc:/system/filesystem/root:default var/svc/manifest/system/filesystem/root-fs.xml
+svc:/system/filesystem/root var/svc/manifest/system/filesystem/root-fs.xml
+svc:/system/filesystem/usr:default var/svc/manifest/system/filesystem/usr-fs.xml
+svc:/system/filesystem/usr var/svc/manifest/system/filesystem/usr-fs.xml
+svc:/system/identity:domain var/svc/manifest/system/identity.xml
+svc:/system/identity:node var/svc/manifest/system/identity.xml
+svc:/system/identity var/svc/manifest/system/identity.xml
+svc:/system/keymap:default var/svc/manifest/system/keymap.xml
+svc:/system/keymap var/svc/manifest/system/keymap.xml
+svc:/system/manifest-import:default var/svc/manifest/system/manifest-import.xml
+svc:/system/manifest-import var/svc/manifest/system/manifest-import.xml
+svc:/system/name-service-cache:default var/svc/manifest/system/name-service-cache.xml
+svc:/system/name-service-cache var/svc/manifest/system/name-service-cache.xml
+svc:/system/rmtmpfiles:default var/svc/manifest/system/rmtmpfiles.xml
+svc:/system/rmtmpfiles var/svc/manifest/system/rmtmpfiles.xml
+svc:/system/sac:default var/svc/manifest/system/sac.xml
+svc:/system/sac var/svc/manifest/system/sac.xml
+svc:/system/svc/restarter:default var/svc/manifest/system/svc/restarter.xml
+svc:/system/svc/restarter var/svc/manifest/system/svc/restarter.xml
+svc:/system/system-log:default var/svc/manifest/system/system-log.xml
+svc:/system/system-log var/svc/manifest/system/system-log.xml
+svc:/system/utmp:default var/svc/manifest/system/utmp.xml
+svc:/system/utmp var/svc/manifest/system/utmp.xml
+svc:/system/cvc:default var/svc/manifest/system/cvc.xml
+svc:/system/cvc var/svc/manifest/system/cvc.xml
+svc:/platform/sun4u/dcs:default var/svc/manifest/platform/sun4u/dcs.xml
+svc:/platform/sun4u/dcs var/svc/manifest/platform/sun4u/dcs.xml
+svc:/network/dhcp-server:default var/svc/manifest/network/dhcp-server.xml
+svc:/network/dhcp-server var/svc/manifest/network/dhcp-server.xml
+svc:/platform/sun4u/efdaemon:default var/svc/manifest/platform/sun4u/efdaemon.xml
+svc:/platform/sun4u/efdaemon var/svc/manifest/platform/sun4u/efdaemon.xml
+svc:/application/font/fc-cache:default var/svc/manifest/application/font/fc-cache.xml
+svc:/application/font/fc-cache var/svc/manifest/application/font/fc-cache.xml
+svc:/network/ftp:default var/svc/manifest/network/ftp.xml
+svc:/network/ftp var/svc/manifest/network/ftp.xml
+svc:/application/gdm2-login:default var/svc/manifest/application/gdm2-login.xml
+svc:/application/gdm2-login var/svc/manifest/application/gdm2-login.xml
+svc:/network/rpc/gss:default var/svc/manifest/network/rpc/gss.xml
+svc:/network/rpc/gss var/svc/manifest/network/rpc/gss.xml
+svc:/network/ipfilter:default var/svc/manifest/network/ipfilter.xml
+svc:/network/ipfilter var/svc/manifest/network/ipfilter.xml
+svc:/network/pfil:default var/svc/manifest/network/pfil.xml
+svc:/network/pfil var/svc/manifest/network/pfil.xml
+svc:/application/print/ipp-listener:default var/svc/manifest/application/print/ipp-listener.xml
+svc:/application/print/ipp-listener var/svc/manifest/application/print/ipp-listener.xml
+svc:/network/security/krb5_prop:default var/svc/manifest/network/security/krb5_prop.xml
+svc:/network/security/krb5_prop var/svc/manifest/network/security/krb5_prop.xml
+svc:/network/security/kadmin:default var/svc/manifest/network/security/kadmin.xml
+svc:/network/security/kadmin var/svc/manifest/network/security/kadmin.xml
+svc:/network/security/krb5kdc:default var/svc/manifest/network/security/krb5kdc.xml
+svc:/network/security/krb5kdc var/svc/manifest/network/security/krb5kdc.xml
+svc:/network/security/ktkt_warn:default var/svc/manifest/network/security/ktkt_warn.xml
+svc:/network/security/ktkt_warn var/svc/manifest/network/security/ktkt_warn.xml
+svc:/network/rpc/mdcomm:default var/svc/manifest/network/rpc/mdcomm.xml
+svc:/network/rpc/mdcomm var/svc/manifest/network/rpc/mdcomm.xml
+svc:/network/rpc/meta:default var/svc/manifest/network/rpc/meta.xml
+svc:/network/rpc/meta var/svc/manifest/network/rpc/meta.xml
+svc:/network/rpc/metamed:default var/svc/manifest/network/rpc/metamed.xml
+svc:/network/rpc/metamed var/svc/manifest/network/rpc/metamed.xml
+svc:/network/rpc/metamh:default var/svc/manifest/network/rpc/metamh.xml
+svc:/network/rpc/metamh var/svc/manifest/network/rpc/metamh.xml
+svc:/system/mdmonitor:default var/svc/manifest/system/mdmonitor.xml
+svc:/system/mdmonitor var/svc/manifest/system/mdmonitor.xml
+svc:/system/metainit:default var/svc/manifest/system/metainit.xml
+svc:/system/metainit var/svc/manifest/system/metainit.xml
+svc:/network/nfs/cbd:default var/svc/manifest/network/nfs/cbd.xml
+svc:/network/nfs/cbd var/svc/manifest/network/nfs/cbd.xml
+svc:/network/nfs/client:default var/svc/manifest/network/nfs/client.xml
+svc:/network/nfs/client var/svc/manifest/network/nfs/client.xml
+svc:/network/nfs/mapid:default var/svc/manifest/network/nfs/mapid.xml
+svc:/network/nfs/mapid var/svc/manifest/network/nfs/mapid.xml
+svc:/network/nfs/nlockmgr:default var/svc/manifest/network/nfs/nlockmgr.xml
+svc:/network/nfs/nlockmgr var/svc/manifest/network/nfs/nlockmgr.xml
+svc:/network/nfs/status:default var/svc/manifest/network/nfs/status.xml
+svc:/network/nfs/status var/svc/manifest/network/nfs/status.xml
+svc:/network/nfs/rquota:default var/svc/manifest/network/nfs/rquota.xml
+svc:/network/nfs/rquota var/svc/manifest/network/nfs/rquota.xml
+svc:/network/nfs/server:default var/svc/manifest/network/nfs/server.xml
+svc:/network/nfs/server var/svc/manifest/network/nfs/server.xml
+svc:/network/nis/client:default var/svc/manifest/network/nis/client.xml
+svc:/network/nis/client var/svc/manifest/network/nis/client.xml
+svc:/network/rpc/nisplus:default var/svc/manifest/network/rpc/nisplus.xml
+svc:/network/rpc/nisplus var/svc/manifest/network/rpc/nisplus.xml
+svc:/network/rpc/rex:default var/svc/manifest/network/rpc/rex.xml
+svc:/network/rpc/rex var/svc/manifest/network/rpc/rex.xml
+svc:/network/ntp:default var/svc/manifest/network/ntp.xml
+svc:/network/ntp var/svc/manifest/network/ntp.xml
+svc:/network/rpc/ocfserv:default var/svc/manifest/network/rpc/ocfserv.xml
+svc:/network/rpc/ocfserv var/svc/manifest/network/rpc/ocfserv.xml
+svc:/application/print/cleanup:default var/svc/manifest/application/print/cleanup.xml
+svc:/application/print/cleanup var/svc/manifest/application/print/cleanup.xml
+svc:/application/print/rfc1179:default var/svc/manifest/application/print/rfc1179.xml
+svc:/application/print/rfc1179 var/svc/manifest/application/print/rfc1179.xml
+svc:/system/picl:default var/svc/manifest/system/picl.xml
+svc:/system/picl var/svc/manifest/system/picl.xml
+svc:/system/power:default var/svc/manifest/system/power.xml
+svc:/system/power var/svc/manifest/system/power.xml
+svc:/application/print/server:default var/svc/manifest/application/print/server.xml
+svc:/application/print/server var/svc/manifest/application/print/server.xml
+svc:/system/rcap:default var/svc/manifest/system/rcap.xml
+svc:/system/rcap var/svc/manifest/system/rcap.xml
+svc:/network/comsat:default var/svc/manifest/network/comsat.xml
+svc:/network/comsat var/svc/manifest/network/comsat.xml
+svc:/network/finger:default var/svc/manifest/network/finger.xml
+svc:/network/finger var/svc/manifest/network/finger.xml
+svc:/network/login:eklogin var/svc/manifest/network/login.xml
+svc:/network/login:klogin var/svc/manifest/network/login.xml
+svc:/network/login:rlogin var/svc/manifest/network/login.xml
+svc:/network/login var/svc/manifest/network/login.xml
+svc:/network/rexec:default var/svc/manifest/network/rexec.xml
+svc:/network/rexec var/svc/manifest/network/rexec.xml
+svc:/network/rpc/rstat:default var/svc/manifest/network/rpc/rstat.xml
+svc:/network/rpc/rstat var/svc/manifest/network/rpc/rstat.xml
+svc:/network/rpc/rusers:default var/svc/manifest/network/rpc/rusers.xml
+svc:/network/rpc/rusers var/svc/manifest/network/rpc/rusers.xml
+svc:/network/rpc/spray:default var/svc/manifest/network/rpc/spray.xml
+svc:/network/rpc/spray var/svc/manifest/network/rpc/spray.xml
+svc:/network/rpc/wall:default var/svc/manifest/network/rpc/wall.xml
+svc:/network/rpc/wall var/svc/manifest/network/rpc/wall.xml
+svc:/network/shell:kshell var/svc/manifest/network/shell.xml
+svc:/network/shell:default var/svc/manifest/network/shell.xml
+svc:/network/shell var/svc/manifest/network/shell.xml
+svc:/network/talk:default var/svc/manifest/network/talk.xml
+svc:/network/talk var/svc/manifest/network/talk.xml
+svc:/platform/sun4u/sf880drd:default var/svc/manifest/platform/sun4u/sf880drd.xml
+svc:/platform/sun4u/sf880drd var/svc/manifest/platform/sun4u/sf880drd.xml
+svc:/network/slp:default var/svc/manifest/network/slp.xml
+svc:/network/slp var/svc/manifest/network/slp.xml
+svc:/network/smtp:sendmail var/svc/manifest/network/smtp-sendmail.xml
+svc:/network/smtp var/svc/manifest/network/smtp-sendmail.xml
+svc:/network/ssh:default var/svc/manifest/network/ssh.xml
+svc:/network/ssh var/svc/manifest/network/ssh.xml
+svc:/application/font/stfsloader:default var/svc/manifest/application/font/stfsloader.xml
+svc:/application/font/stfsloader var/svc/manifest/application/font/stfsloader.xml
+svc:/network/tname:default var/svc/manifest/network/tname.xml
+svc:/network/tname var/svc/manifest/network/tname.xml
+svc:/network/telnet:default var/svc/manifest/network/telnet.xml
+svc:/network/telnet var/svc/manifest/network/telnet.xml
+svc:/network/rpc/smserver:default var/svc/manifest/network/rpc/smserver.xml
+svc:/network/rpc/smserver var/svc/manifest/network/rpc/smserver.xml
+svc:/application/management/webmin:default var/svc/manifest/application/management/webmin.xml
+svc:/application/management/webmin var/svc/manifest/application/management/webmin.xml
+svc:/application/x11/x11-server var/svc/manifest/application/x11/x11-server.xml
+svc:/application/x11/xfs:default var/svc/manifest/application/x11/xfs.xml
+svc:/application/x11/xfs var/svc/manifest/application/x11/xfs.xml
+svc:/network/nis/passwd:default var/svc/manifest/network/nis/passwd.xml
+svc:/network/nis/passwd var/svc/manifest/network/nis/passwd.xml
+svc:/network/nis/server:default var/svc/manifest/network/nis/server.xml
+svc:/network/nis/server var/svc/manifest/network/nis/server.xml
+svc:/network/nis/update:default var/svc/manifest/network/nis/update.xml
+svc:/network/nis/update var/svc/manifest/network/nis/update.xml
+svc:/network/nis/xfr:default var/svc/manifest/network/nis/xfr.xml
+svc:/network/nis/xfr var/svc/manifest/network/nis/xfr.xml
+svc:/system/zones:default var/svc/manifest/system/zones.xml
+svc:/system/zones var/svc/manifest/system/zones.xml
+svc:/milestone/sysconfig:default var/svc/manifest/milestone/sysconfig.xml
+svc:/milestone/sysconfig var/svc/manifest/milestone/sysconfig.xml
+svc:/network/iscsi_initiator:default var/svc/manifest/network/iscsi_initiator.xml
+svc:/network/iscsi_initiator var/svc/manifest/network/iscsi_initiator.xml
+svc:/network/http:apache22 var/svc/manifest/network/http-apache22.xml
+svc:/network/http var/svc/manifest/network/http-apache22.xml
+svc:/system/avahi-bridge-dsd:default var/svc/manifest/system/avahi-bridge-dsd.xml
+svc:/system/avahi-bridge-dsd var/svc/manifest/system/avahi-bridge-dsd.xml
+svc:/system/basicreg:default var/svc/manifest/system/br.xml
+svc:/system/basicreg var/svc/manifest/system/br.xml
+svc:/system/intrd:default var/svc/manifest/system/intrd.xml
+svc:/system/intrd var/svc/manifest/system/intrd.xml
+svc:/system/scheduler:default var/svc/manifest/system/scheduler.xml
+svc:/system/scheduler var/svc/manifest/system/scheduler.xml
+svc:/network/datalink-management:default var/svc/manifest/network/dlmgmt.xml
+svc:/network/datalink-management var/svc/manifest/network/dlmgmt.xml
+svc:/network/ipv4-forwarding:default var/svc/manifest/network/forwarding.xml
+svc:/network/ipv6-forwarding:default var/svc/manifest/network/forwarding.xml
+svc:/network/ipv4-forwarding var/svc/manifest/network/forwarding.xml
+svc:/network/ipv6-forwarding var/svc/manifest/network/forwarding.xml
+svc:/network/ipsec/ike:default var/svc/manifest/network/ipsec/ike.xml
+svc:/network/ipsec/ike var/svc/manifest/network/ipsec/ike.xml
+svc:/network/ipsec/ipsecalgs:default var/svc/manifest/network/ipsec/ipsecalgs.xml
+svc:/network/ipsec/ipsecalgs var/svc/manifest/network/ipsec/ipsecalgs.xml
+svc:/network/ipsec/manual-key:default var/svc/manifest/network/ipsec/manual-key.xml
+svc:/network/ipsec/manual-key var/svc/manifest/network/ipsec/manual-key.xml
+svc:/network/ipsec/policy:default var/svc/manifest/network/ipsec/policy.xml
+svc:/network/ipsec/policy var/svc/manifest/network/ipsec/policy.xml
+svc:/network/physical:nwam var/svc/manifest/network/network-physical.xml
+svc:/network/routing-setup:default var/svc/manifest/network/network-routing-setup.xml
+svc:/network/routing-setup var/svc/manifest/network/network-routing-setup.xml
+svc:/network/routing/legacy-routing:ipv6 var/svc/manifest/network/routing/legacy-routing.xml
+svc:/network/routing/legacy-routing:ipv4 var/svc/manifest/network/routing/legacy-routing.xml
+svc:/network/routing/legacy-routing var/svc/manifest/network/routing/legacy-routing.xml
+svc:/network/shares/group:default var/svc/manifest/network/shares/group.xml
+svc:/network/shares/group var/svc/manifest/network/shares/group.xml
+svc:/network/ssl/proxy var/svc/manifest/network/ssl/kssl-proxy.xml
+svc:/system/auditd:default var/svc/manifest/system/auditd.xml
+svc:/system/auditd var/svc/manifest/system/auditd.xml
+svc:/system/boot-archive-update:default var/svc/manifest/system/boot-archive-update.xml
+svc:/system/boot-archive-update var/svc/manifest/system/boot-archive-update.xml
+svc:/system/boot-archive:default var/svc/manifest/system/boot-archive.xml
+svc:/system/boot-archive var/svc/manifest/system/boot-archive.xml
+svc:/system/console-login:vt6 var/svc/manifest/system/console-login.xml
+svc:/system/console-login:vt5 var/svc/manifest/system/console-login.xml
+svc:/system/console-login:vt4 var/svc/manifest/system/console-login.xml
+svc:/system/console-login:vt3 var/svc/manifest/system/console-login.xml
+svc:/system/console-login:vt2 var/svc/manifest/system/console-login.xml
+svc:/system/device/mpxio-upgrade:default var/svc/manifest/system/device/mpxio-upgrade.xml
+svc:/system/device/mpxio-upgrade var/svc/manifest/system/device/mpxio-upgrade.xml
+svc:/system/extended-accounting:task var/svc/manifest/system/extended-accounting.xml
+svc:/system/extended-accounting:process var/svc/manifest/system/extended-accounting.xml
+svc:/system/extended-accounting:flow var/svc/manifest/system/extended-accounting.xml
+svc:/system/extended-accounting var/svc/manifest/system/extended-accounting.xml
+svc:/system/idmap:default var/svc/manifest/system/idmap.xml
+svc:/system/idmap var/svc/manifest/system/idmap.xml
+svc:/system/vtdaemon:default var/svc/manifest/system/vtdaemon.xml
+svc:/system/vtdaemon var/svc/manifest/system/vtdaemon.xml
+svc:/application/cups/scheduler:default var/svc/manifest/application/cups.xml
+svc:/application/cups/in-lpd:default var/svc/manifest/application/cups.xml
+svc:/application/cups/scheduler var/svc/manifest/application/cups.xml
+svc:/application/cups/in-lpd var/svc/manifest/application/cups.xml
+svc:/system/dbus:default var/svc/manifest/system/dbus.xml
+svc:/system/dbus var/svc/manifest/system/dbus.xml
+svc:/platform/sun4u/oplhpd:default var/svc/manifest/platform/sun4u/oplhpd.xml
+svc:/platform/sun4u/oplhpd var/svc/manifest/platform/sun4u/oplhpd.xml
+svc:/platform/sun4u/dscp:default var/svc/manifest/platform/sun4u/dscp.xml
+svc:/platform/sun4u/dscp var/svc/manifest/platform/sun4u/dscp.xml
+svc:/network/dns/multicast:default var/svc/manifest/network/dns/multicast.xml
+svc:/network/dns/multicast var/svc/manifest/network/dns/multicast.xml
+svc:/application/cde-printinfo:default var/svc/manifest/application/cde-printinfo.xml
+svc:/application/cde-printinfo var/svc/manifest/application/cde-printinfo.xml
+svc:/network/cde-spc:default var/svc/manifest/network/cde-spc.xml
+svc:/network/cde-spc var/svc/manifest/network/cde-spc.xml
+svc:/network/rpc/cde-calendar-manager:default var/svc/manifest/network/rpc/cde-cmsd.xml
+svc:/network/rpc/cde-calendar-manager var/svc/manifest/network/rpc/cde-cmsd.xml
+svc:/network/rpc/cde-ttdbserver:tcp var/svc/manifest/network/rpc/cde-ttdbserverd.xml
+svc:/network/rpc/cde-ttdbserver var/svc/manifest/network/rpc/cde-ttdbserverd.xml
+svc:/application/graphical-login/cde-login:default var/svc/manifest/application/graphical-login/cde-login.xml
+svc:/application/graphical-login/cde-login var/svc/manifest/application/graphical-login/cde-login.xml
+svc:/platform/sun4v/efdaemon:default var/svc/manifest/platform/sun4v/efdaemon.xml
+svc:/platform/sun4v/efdaemon var/svc/manifest/platform/sun4v/efdaemon.xml
+svc:/network/npiv_config:default var/svc/manifest/network/npiv_config.xml
+svc:/network/npiv_config var/svc/manifest/network/npiv_config.xml
+svc:/system/fpsd:default var/svc/manifest/system/fpsd.xml
+svc:/system/fpsd var/svc/manifest/system/fpsd.xml
+svc:/application/time-slider:default var/svc/manifest/application/time-slider.xml
+svc:/application/time-slider var/svc/manifest/application/time-slider.xml
+svc:/network/device-discovery/printers:snmp var/svc/manifest/network/network-discovery.xml
+svc:/network/device-discovery/printers var/svc/manifest/network/network-discovery.xml
+svc:/system/hal:default var/svc/manifest/system/hal.xml
+svc:/system/hal var/svc/manifest/system/hal.xml
+svc:/system/nws_ii:default var/svc/manifest/system/nws_ii.xml
+svc:/system/nws_ii var/svc/manifest/system/nws_ii.xml
+svc:/network/ipmievd:default var/svc/manifest/network/ipmievd.xml
+svc:/network/ipmievd var/svc/manifest/network/ipmievd.xml
+svc:/system/iscsitgt:default var/svc/manifest/system/iscsi_target.xml
+svc:/system/iscsitgt var/svc/manifest/system/iscsi_target.xml
+svc:/network/isns_server:default var/svc/manifest/network/isns_server.xml
+svc:/network/isns_server var/svc/manifest/network/isns_server.xml
+svc:/application/wnn8/server:default var/svc/manifest/application/wnn8/wnn8-server.xml
+svc:/application/wnn8/server var/svc/manifest/application/wnn8/wnn8-server.xml
+svc:/platform/sun4v/drd:default var/svc/manifest/platform/sun4v/drd.xml
+svc:/platform/sun4v/drd var/svc/manifest/platform/sun4v/drd.xml
+svc:/ldoms/vntsd:default var/svc/manifest/platform/sun4v/vntsd.xml
+svc:/ldoms/vntsd var/svc/manifest/platform/sun4v/vntsd.xml
+svc:/network/http:lighttpd14 var/svc/manifest/network/http-lighttpd14.xml
+svc:/network/http var/svc/manifest/network/http-lighttpd14.xml
+svc:/system/webconsole:console var/svc/manifest/system/webconsole.xml
+svc:/system/webconsole var/svc/manifest/system/webconsole.xml
+svc:/system/metasync:default var/svc/manifest/system/metasync.xml
+svc:/system/metasync var/svc/manifest/system/metasync.xml
+svc:/application/database/memcached:default var/svc/manifest/application/database/memcached.xml
+svc:/application/database/memcached var/svc/manifest/application/database/memcached.xml
+svc:/application/management/mms:mm var/svc/manifest/application/management/mms.xml
+svc:/application/management/mms:db var/svc/manifest/application/management/mms.xml
+svc:/application/management/mms:wcr var/svc/manifest/application/management/mms.xml
+svc:/application/management/mms var/svc/manifest/application/management/mms.xml
+svc:/application/database/mysql:version_50 var/svc/manifest/application/database/mysql.xml
+svc:/application/database/mysql var/svc/manifest/application/database/mysql.xml
+svc:/system/ndmpd:default var/svc/manifest/system/ndmp.xml
+svc:/system/ndmpd var/svc/manifest/system/ndmp.xml
+svc:/application/database/pgbouncer:default var/svc/manifest/application/database/pgbouncer.xml
+svc:/application/database/pgbouncer var/svc/manifest/application/database/pgbouncer.xml
+svc:/system/pools/dynamic:default var/svc/manifest/system/poold.xml
+svc:/system/pools/dynamic var/svc/manifest/system/poold.xml
+svc:/system/pools:default var/svc/manifest/system/pools.xml
+svc:/system/pools var/svc/manifest/system/pools.xml
+svc:/application/database/postgresql:version_82_64bit var/svc/manifest/application/database/postgresql.xml
+svc:/application/database/postgresql:version_82 var/svc/manifest/application/database/postgresql.xml
+svc:/application/database/postgresql:version_81 var/svc/manifest/application/database/postgresql.xml
+svc:/application/database/postgresql var/svc/manifest/application/database/postgresql.xml
+svc:/application/database/postgresql_83:default_64bit var/svc/manifest/application/database/postgresql_83.xml
+svc:/application/database/postgresql_83:default_32bit var/svc/manifest/application/database/postgresql_83.xml
+svc:/application/database/postgresql_83 var/svc/manifest/application/database/postgresql_83.xml
+svc:/system/postrun:default var/svc/manifest/system/postrun.xml
+svc:/system/postrun var/svc/manifest/system/postrun.xml
+svc:/system/installupdates:default var/svc/manifest/system/installupdates.xml
+svc:/system/installupdates var/svc/manifest/system/installupdates.xml
+svc:/network/http:privoxy var/svc/manifest/network/http-privoxy.xml
+svc:/network/http var/svc/manifest/network/http-privoxy.xml
+svc:/application/print/ppd-cache-update:default var/svc/manifest/application/print/ppd-cache-update.xml
+svc:/application/print/ppd-cache-update var/svc/manifest/application/print/ppd-cache-update.xml
+svc:/network/routing/zebra:quagga var/svc/manifest/network/routing/quagga.xml
+svc:/network/routing/rip:quagga var/svc/manifest/network/routing/quagga.xml
+svc:/network/routing/ripng:quagga var/svc/manifest/network/routing/quagga.xml
+svc:/network/routing/ospf:quagga var/svc/manifest/network/routing/quagga.xml
+svc:/network/routing/ospf6:quagga var/svc/manifest/network/routing/quagga.xml
+svc:/network/routing/bgp:quagga var/svc/manifest/network/routing/quagga.xml
+svc:/network/routing/zebra var/svc/manifest/network/routing/quagga.xml
+svc:/network/routing/rip var/svc/manifest/network/routing/quagga.xml
+svc:/network/routing/ripng var/svc/manifest/network/routing/quagga.xml
+svc:/network/routing/ospf var/svc/manifest/network/routing/quagga.xml
+svc:/network/routing/ospf6 var/svc/manifest/network/routing/quagga.xml
+svc:/network/routing/bgp var/svc/manifest/network/routing/quagga.xml
+svc:/system/nws_rdc:default var/svc/manifest/system/nws_rdc.xml
+svc:/system/nws_rdc var/svc/manifest/system/nws_rdc.xml
+svc:/system/nws_rdcsyncd:default var/svc/manifest/system/nws_rdcsyncd.xml
+svc:/system/nws_rdcsyncd var/svc/manifest/system/nws_rdcsyncd.xml
+svc:/system/filesystem/rmvolmgr:default var/svc/manifest/system/filesystem/rmvolmgr.xml
+svc:/system/filesystem/rmvolmgr var/svc/manifest/system/filesystem/rmvolmgr.xml
+svc:/network/routing/ndp:default var/svc/manifest/network/routing/ndp.xml
+svc:/network/routing/ndp var/svc/manifest/network/routing/ndp.xml
+svc:/network/routing/rdisc:default var/svc/manifest/network/routing/rdisc.xml
+svc:/network/routing/rdisc var/svc/manifest/network/routing/rdisc.xml
+svc:/network/routing/ripng:default var/svc/manifest/network/routing/ripng.xml
+svc:/network/routing/ripng var/svc/manifest/network/routing/ripng.xml
+svc:/network/routing/route:default var/svc/manifest/network/routing/route.xml
+svc:/network/routing/route var/svc/manifest/network/routing/route.xml
+svc:/application/management/snmpdx:default var/svc/manifest/application/management/snmpdx.xml
+svc:/application/management/snmpdx var/svc/manifest/application/management/snmpdx.xml
+svc:/platform/sun4u/sckmd:default var/svc/manifest/platform/sun4u/sckmd.xml
+svc:/platform/sun4u/sckmd var/svc/manifest/platform/sun4u/sckmd.xml
+svc:/system/nws_scm:default var/svc/manifest/system/nws_scm.xml
+svc:/system/nws_scm var/svc/manifest/system/nws_scm.xml
+svc:/network/stdiscover:default var/svc/manifest/network/stdiscover.xml
+svc:/network/stdiscover var/svc/manifest/network/stdiscover.xml
+svc:/network/stlisten:default var/svc/manifest/network/stlisten.xml
+svc:/network/stlisten var/svc/manifest/network/stlisten.xml
+svc:/network/samba:default var/svc/manifest/network/samba.xml
+svc:/network/samba var/svc/manifest/network/samba.xml
+svc:/network/swat:default var/svc/manifest/network/swat.xml
+svc:/network/swat var/svc/manifest/network/swat.xml
+svc:/network/winbind:default var/svc/manifest/network/winbind.xml
+svc:/network/winbind var/svc/manifest/network/winbind.xml
+svc:/network/wins:default var/svc/manifest/network/wins.xml
+svc:/network/wins var/svc/manifest/network/wins.xml
+svc:/network/smb/client:default var/svc/manifest/network/smb/client.xml
+svc:/network/smb/client var/svc/manifest/network/smb/client.xml
+svc:/network/smb/server:default var/svc/manifest/network/smb/server.xml
+svc:/network/smb/server var/svc/manifest/network/smb/server.xml
+svc:/application/management/seaport:default var/svc/manifest/application/management/seaport.xml
+svc:/application/management/seaport var/svc/manifest/application/management/seaport.xml
+svc:/application/management/sma:default var/svc/manifest/application/management/sma.xml
+svc:/application/management/sma var/svc/manifest/application/management/sma.xml
+svc:/system/nws_sv:default var/svc/manifest/system/nws_sv.xml
+svc:/system/nws_sv var/svc/manifest/system/nws_sv.xml
+svc:/network/http:squid var/svc/manifest/network/http-squid.xml
+svc:/network/http var/svc/manifest/network/http-squid.xml
+svc:/system/stmf:default var/svc/manifest/system/stmf.xml
+svc:/system/stmf var/svc/manifest/system/stmf.xml
+svc:/application/stosreg:default var/svc/manifest/application/stosreg.xml
+svc:/application/stosreg var/svc/manifest/application/stosreg.xml
+svc:/network/ssl/stunnel:default var/svc/manifest/network/ssl/stunnel.xml
+svc:/network/ssl/stunnel var/svc/manifest/network/ssl/stunnel.xml
+svc:/network/tnctl:default var/svc/manifest/network/tnctl.xml
+svc:/network/tnctl var/svc/manifest/network/tnctl.xml
+svc:/network/tnd:default var/svc/manifest/network/tnd.xml
+svc:/network/tnd var/svc/manifest/network/tnd.xml
+svc:/system/labeld:default var/svc/manifest/system/labeld.xml
+svc:/system/labeld var/svc/manifest/system/labeld.xml
+svc:/system/tsol-zones:default var/svc/manifest/system/tsol-zones.xml
+svc:/system/tsol-zones var/svc/manifest/system/tsol-zones.xml
+svc:/system/filesystem/vscan:icap var/svc/manifest/system/filesystem/vscan.xml
+svc:/system/filesystem/vscan var/svc/manifest/system/filesystem/vscan.xml
+svc:/application/management/wbem:default var/svc/manifest/application/management/wbem.xml
+svc:/application/management/wbem var/svc/manifest/application/management/wbem.xml
+svc:/network/wpa var/svc/manifest/network/wpa.xml
+svc:/application/x11/xvnc-inetd:default var/svc/manifest/application/x11/xvnc-inetd.xml
+svc:/application/x11/xvnc-inetd var/svc/manifest/application/x11/xvnc-inetd.xml
+svc:/system/filesystem/zfs/auto-snapshot:daily var/svc/manifest/system/filesystem/auto-snapshot-daily.xml
+svc:/system/filesystem/zfs/auto-snapshot:default var/svc/manifest/system/filesystem/auto-snapshot-daily.xml
+svc:/system/filesystem/zfs/auto-snapshot var/svc/manifest/system/filesystem/auto-snapshot-daily.xml
+svc:/system/filesystem/zfs/auto-snapshot:frequent var/svc/manifest/system/filesystem/auto-snapshot-frequent.xml
+svc:/system/filesystem/zfs/auto-snapshot:default var/svc/manifest/system/filesystem/auto-snapshot-frequent.xml
+svc:/system/filesystem/zfs/auto-snapshot var/svc/manifest/system/filesystem/auto-snapshot-frequent.xml
+svc:/system/filesystem/zfs/auto-snapshot:hourly var/svc/manifest/system/filesystem/auto-snapshot-hourly.xml
+svc:/system/filesystem/zfs/auto-snapshot:default var/svc/manifest/system/filesystem/auto-snapshot-hourly.xml
+svc:/system/filesystem/zfs/auto-snapshot var/svc/manifest/system/filesystem/auto-snapshot-hourly.xml
+svc:/system/filesystem/zfs/auto-snapshot:monthly var/svc/manifest/system/filesystem/auto-snapshot-monthly.xml
+svc:/system/filesystem/zfs/auto-snapshot:default var/svc/manifest/system/filesystem/auto-snapshot-monthly.xml
+svc:/system/filesystem/zfs/auto-snapshot var/svc/manifest/system/filesystem/auto-snapshot-monthly.xml
+svc:/system/filesystem/zfs/auto-snapshot:weekly var/svc/manifest/system/filesystem/auto-snapshot-weekly.xml
+svc:/system/filesystem/zfs/auto-snapshot:default var/svc/manifest/system/filesystem/auto-snapshot-weekly.xml
+svc:/system/filesystem/zfs/auto-snapshot var/svc/manifest/system/filesystem/auto-snapshot-weekly.xml
+svc:/system/filesystem/zfssnap-roleadd:default var/svc/manifest/system/filesystem/zfs-auto-snapshot-roleadd.xml
+svc:/system/filesystem/zfssnap-roleadd var/svc/manifest/system/filesystem/zfs-auto-snapshot-roleadd.xml
+svc:/system/filesystem/zfs/auto-snapshot var/svc/manifest/system/filesystem/zfs-auto-snapshot.xml
+svc:/system/resource-mgmt:default var/svc/manifest/system/resource-mgmt.xml
+svc:/system/resource-mgmt var/svc/manifest/system/resource-mgmt.xml
+svc:/network/routing/isis:quagga var/svc/manifest/network/routing/quagga.xml
+svc:/network/routing/isis var/svc/manifest/network/routing/quagga.xml
+svc:/system/filesystem/zfssnap-roleadd:default var/svc/manifest/system/filesystem/auto-snapshot-roleadd.xml
+svc:/system/filesystem/zfssnap-roleadd var/svc/manifest/system/filesystem/auto-snapshot-roleadd.xml
+svc:/system/filesystem/zfs/auto-snapshot var/svc/manifest/system/filesystem/auto-snapshot.xml
+svc:/system/filesystem/zfs/auto-snapshot:daily var/svc/manifest/system/filesystem/zfs-auto-snapshot-daily.xml
+svc:/system/filesystem/zfs/auto-snapshot:default var/svc/manifest/system/filesystem/zfs-auto-snapshot-daily.xml
+svc:/system/filesystem/zfs/auto-snapshot var/svc/manifest/system/filesystem/zfs-auto-snapshot-daily.xml
+svc:/system/filesystem/zfs/auto-snapshot:frequent var/svc/manifest/system/filesystem/zfs-auto-snapshot-frequent.xml
+svc:/system/filesystem/zfs/auto-snapshot:default var/svc/manifest/system/filesystem/zfs-auto-snapshot-frequent.xml
+svc:/system/filesystem/zfs/auto-snapshot var/svc/manifest/system/filesystem/zfs-auto-snapshot-frequent.xml
+svc:/system/filesystem/zfs/auto-snapshot:hourly var/svc/manifest/system/filesystem/zfs-auto-snapshot-hourly.xml
+svc:/system/filesystem/zfs/auto-snapshot:default var/svc/manifest/system/filesystem/zfs-auto-snapshot-hourly.xml
+svc:/system/filesystem/zfs/auto-snapshot var/svc/manifest/system/filesystem/zfs-auto-snapshot-hourly.xml
+svc:/system/filesystem/zfs/auto-snapshot:monthly var/svc/manifest/system/filesystem/zfs-auto-snapshot-monthly.xml
+svc:/system/filesystem/zfs/auto-snapshot:default var/svc/manifest/system/filesystem/zfs-auto-snapshot-monthly.xml
+svc:/system/filesystem/zfs/auto-snapshot var/svc/manifest/system/filesystem/zfs-auto-snapshot-monthly.xml
+svc:/system/filesystem/zfs/auto-snapshot:weekly var/svc/manifest/system/filesystem/zfs-auto-snapshot-weekly.xml
+svc:/system/filesystem/zfs/auto-snapshot:default var/svc/manifest/system/filesystem/zfs-auto-snapshot-weekly.xml
+svc:/system/filesystem/zfs/auto-snapshot var/svc/manifest/system/filesystem/zfs-auto-snapshot-weekly.xml
+svc:/application/database/javadb:default var/svc/manifest/application/database/javadb.xml
+svc:/application/database/javadb var/svc/manifest/application/database/javadb.xml
+svc:/system/svc/global:default var/svc/manifest/system/svc/global.xml
+svc:/system/svc/global var/svc/manifest/system/svc/global.xml
+svc:/system/nws_ii:default var/svc/manifest/system/nws-ii.xml
+svc:/system/nws_ii var/svc/manifest/system/nws-ii.xml
+svc:/network/ldap/server:openldap_24 var/svc/manifest/network/ldap/ldap-olslapd.xml
+svc:/network/ldap/server var/svc/manifest/network/ldap/ldap-olslapd.xml
+svc:/network/iscsi/target:default var/svc/manifest/network/iscsi/iscsi-target.xml
+svc:/network/iscsi/target var/svc/manifest/network/iscsi/iscsi-target.xml
+svc:/system/filesystem/zfs/auto-snapshot:monthly var/svc/manifest/system/filesystem/auto-snapshot.xml
+svc:/system/filesystem/zfs/auto-snapshot:weekly var/svc/manifest/system/filesystem/auto-snapshot.xml
+svc:/system/filesystem/zfs/auto-snapshot:daily var/svc/manifest/system/filesystem/auto-snapshot.xml
+svc:/system/filesystem/zfs/auto-snapshot:hourly var/svc/manifest/system/filesystem/auto-snapshot.xml
+svc:/system/filesystem/zfs/auto-snapshot:frequent var/svc/manifest/system/filesystem/auto-snapshot.xml
+svc:/network/cimserver:default var/svc/manifest/network/cimserver.xml
+svc:/network/cimserver var/svc/manifest/network/cimserver.xml
+svc:/system/extended-accounting:net var/svc/manifest/system/extended-accounting.xml
+svc:/network/http:tomcat6 var/svc/manifest/network/http-tomcat6.xml
+svc:/network/http var/svc/manifest/network/http-tomcat6.xml
+svc:/network/sendmail-client:default var/svc/manifest/network/sendmail-client.xml
+svc:/network/sendmail-client var/svc/manifest/network/sendmail-client.xml
+svc:/application/database/mysql:version_51 var/svc/manifest/application/database/mysql_51.xml
+svc:/application/database/mysql var/svc/manifest/application/database/mysql_51.xml
+svc:/network/openwsmand:default var/svc/manifest/network/openwsmand.xml
+svc:/network/openwsmand var/svc/manifest/network/openwsmand.xml
+svc:/network/fcoe_config:default var/svc/manifest/network/fcoe_config.xml
+svc:/network/fcoe_config var/svc/manifest/network/fcoe_config.xml
+svc:/network/iscsi/initiator:default var/svc/manifest/network/iscsi/iscsi-initiator.xml
+svc:/network/iscsi/initiator var/svc/manifest/network/iscsi/iscsi-initiator.xml
+svc:/network/loadbalancer/pen:http var/svc/manifest/network/loadbalancer/pen.xml
+svc:/network/loadbalancer/pen:default var/svc/manifest/network/loadbalancer/pen.xml
+svc:/network/loadbalancer/pen var/svc/manifest/network/loadbalancer/pen.xml
+svc:/system/wusbd:default var/svc/manifest/system/wusb.xml
+svc:/system/wusbd var/svc/manifest/system/wusb.xml
+svc:/system/device/audio:default var/svc/manifest/system/device/devices-audio.xml
+svc:/system/device/audio var/svc/manifest/system/device/devices-audio.xml
+svc:/application/desktop-cache/desktop-mime-cache:default var/svc/manifest/application/desktop-cache/desktop-mime-cache.xml
+svc:/application/desktop-cache/desktop-mime-cache var/svc/manifest/application/desktop-cache/desktop-mime-cache.xml
+svc:/application/desktop-cache/gconf-cache:default var/svc/manifest/application/desktop-cache/gconf-cache.xml
+svc:/application/desktop-cache/gconf-cache var/svc/manifest/application/desktop-cache/gconf-cache.xml
+svc:/application/desktop-cache/icon-cache:default var/svc/manifest/application/desktop-cache/icon-cache.xml
+svc:/application/desktop-cache/icon-cache var/svc/manifest/application/desktop-cache/icon-cache.xml
+svc:/application/desktop-cache/input-method-cache:default var/svc/manifest/application/desktop-cache/input-method-cache.xml
+svc:/application/desktop-cache/input-method-cache var/svc/manifest/application/desktop-cache/input-method-cache.xml
+svc:/application/desktop-cache/mime-types-cache:default var/svc/manifest/application/desktop-cache/mime-types-cache.xml
+svc:/application/desktop-cache/mime-types-cache var/svc/manifest/application/desktop-cache/mime-types-cache.xml
+svc:/application/desktop-cache/pixbuf-loaders-installer:default var/svc/manifest/application/desktop-cache/pixbuf-loaders-installer.xml
+svc:/application/desktop-cache/pixbuf-loaders-installer var/svc/manifest/application/desktop-cache/pixbuf-loaders-installer.xml
+svc:/system/ibsrp/target:default var/svc/manifest/system/ibsrp/target.xml
+svc:/system/ibsrp/target var/svc/manifest/system/ibsrp/target.xml
+svc:/network/tor:default var/svc/manifest/network/tor.xml
+svc:/network/tor var/svc/manifest/network/tor.xml
+svc:/system/conmand:default var/svc/manifest/system/conmand.xml
+svc:/system/conmand var/svc/manifest/system/conmand.xml
+svc:/application/security/tcsd:default var/svc/manifest/application/security/tcsd.xml
+svc:/application/security/tcsd var/svc/manifest/application/security/tcsd.xml
+svc:/system/rbac:default var/svc/manifest/system/rbac.xml
+svc:/system/rbac var/svc/manifest/system/rbac.xml
+svc:/network/xmpp:ejabberd var/svc/manifest/network/xmpp-ejabberd.xml
+svc:/network/xmpp var/svc/manifest/network/xmpp-ejabberd.xml
+svc:/system/fcoe_target:default var/svc/manifest/system/fcoe_target.xml
+svc:/system/fcoe_target var/svc/manifest/system/fcoe_target.xml
+svc:/network/snort:default var/svc/manifest/network/snort.xml
+svc:/network/snort var/svc/manifest/network/snort.xml
+svc:/network/aggregation:default var/svc/manifest/network/aggregation.xml
+svc:/network/aggregation var/svc/manifest/network/aggregation.xml
+svc:/network/datalink-init:default var/svc/manifest/network/datalink-init.xml
+svc:/network/datalink-init var/svc/manifest/network/datalink-init.xml
+svc:/network/datalink:default var/svc/manifest/network/datalink.xml
+svc:/network/datalink var/svc/manifest/network/datalink.xml
+svc:/system/boot-config:default var/svc/manifest/system/boot-config.xml
+svc:/system/boot-config var/svc/manifest/system/boot-config.xml
+svc:/application/management/net-snmp:default var/svc/manifest/application/management/net-snmp.xml
+svc:/application/management/net-snmp var/svc/manifest/application/management/net-snmp.xml
+svc:/system/fcoe_initiator:default var/svc/manifest/system/fcoe_initiator.xml
+svc:/system/fcoe_initiator var/svc/manifest/system/fcoe_initiator.xml
+svc:/system/freeipmi/bmc-watchdog:default var/svc/manifest/system/bmc-watchdog.xml
+svc:/system/freeipmi/bmc-watchdog var/svc/manifest/system/bmc-watchdog.xml
+svc:/system/freeipmi/ipmidetectd:default var/svc/manifest/system/ipmidetectd.xml
+svc:/system/freeipmi/ipmidetectd var/svc/manifest/system/ipmidetectd.xml
+svc:/system/boot-archive:default var/svc/manifest/system/filesystem/boot-archive.xml
+svc:/system/boot-archive var/svc/manifest/system/filesystem/boot-archive.xml
+svc:/application/dtprintinfo:default var/svc/manifest/application/dtprintinfo.xml
+svc:/application/dtprintinfo var/svc/manifest/application/dtprintinfo.xml
+svc:/network/dtspc:default var/svc/manifest/network/dtspc.xml
+svc:/network/dtspc var/svc/manifest/network/dtspc.xml
+svc:/network/rpc/calendar-manager:udp var/svc/manifest/network/rpc/cmsd.xml
+svc:/network/rpc/calendar-manager var/svc/manifest/network/rpc/cmsd.xml
+svc:/network/rpc/ttdbserver:tcp var/svc/manifest/network/rpc/ttdbserverd.xml
+svc:/network/rpc/ttdbserver var/svc/manifest/network/rpc/ttdbserverd.xml
+svc:/application/graphical-login/dtlogin:default var/svc/manifest/application/graphical-login/dtlogin.xml
+svc:/application/graphical-login/dtlogin var/svc/manifest/application/graphical-login/dtlogin.xml
+svc:/system/filesystem/volfs:default var/svc/manifest/system/filesystem/volfs.xml
+svc:/system/filesystem/volfs var/svc/manifest/system/filesystem/volfs.xml
+svc:/application/smcwbem:default var/svc/manifest/application/smcwbem.xml
+svc:/application/smcwbem var/svc/manifest/application/smcwbem.xml
+svc:/application/graphical-login/gdm:default var/svc/manifest/application/graphical-login/gdm.xml
+svc:/application/graphical-login/gdm var/svc/manifest/application/graphical-login/gdm.xml
+svc:/network/http:apache22 var/svc/manifest/network/http-apache2.xml
+svc:/platform/i86pc/eeprom:default var/svc/manifest/platform/i86pc/eeprom.xml
+svc:/platform/i86pc/eeprom var/svc/manifest/platform/i86pc/eeprom.xml
+svc:/platform/i86pc/kdmconfig:default var/svc/manifest/platform/i86pc/kdmconfig.xml
+svc:/platform/i86pc/kdmconfig var/svc/manifest/platform/i86pc/kdmconfig.xml
+svc:/system/hostid:default var/svc/manifest/system/hostid.xml
+svc:/system/hostid var/svc/manifest/system/hostid.xml
+svc:/system/xvm/virtd:default var/svc/manifest/system/xvm/virtd.xml
+svc:/system/xvm/virtd var/svc/manifest/system/xvm/virtd.xml
+svc:/system/xvm/console:default var/svc/manifest/system/xvm/console.xml
+svc:/system/xvm/console var/svc/manifest/system/xvm/console.xml
+svc:/system/xvm/domains:default var/svc/manifest/system/xvm/domains.xml
+svc:/system/xvm/domains var/svc/manifest/system/xvm/domains.xml
+svc:/system/xvm/store:default var/svc/manifest/system/xvm/store.xml
+svc:/system/xvm/store var/svc/manifest/system/xvm/store.xml
+svc:/system/xvm/xend:default var/svc/manifest/system/xvm/xend.xml
+svc:/system/xvm/xend var/svc/manifest/system/xvm/xend.xml
+svc:/application/opengl/ogl-select:default var/svc/manifest/application/opengl/ogl-select.xml
+svc:/application/opengl/ogl-select var/svc/manifest/application/opengl/ogl-select.xml
+svc:/network/rpc/bootparams var/svc/manifest/network/rpc/bootparams.xml
+svc:/system/pkgserv:default var/svc/manifest/system/pkgserv.xml
+svc:/system/pkgserv var/svc/manifest/system/pkgserv.xml
+svc:/system/xvm/ipagent:default var/svc/manifest/system/ipagent.xml 
+svc:/system/xvm/ipagent var/svc/manifest/system/ipagent.xml
+svc:/network/lms:default var/svc/manifest/network/lms.xml
+svc:/network/lms var/svc/manifest/network/lms.xml
--- a/usr/src/cmd/svc/svccfg/svccfg_libscf.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/svc/svccfg/svccfg_libscf.c	Mon Oct 05 09:17:26 2009 -0400
@@ -3303,13 +3303,14 @@
  *
  */
 static int
-upgrade_manifestfiles(const entity_t *ient, const scf_snaplevel_t *running,
-    void *ent)
-{
-	scf_propertygroup_t *ud_run_mfsts_pg = NULL;
-	scf_property_t *ud_run_prop = NULL;
+upgrade_manifestfiles(pgroup_t *pg, const entity_t *ient,
+    const scf_snaplevel_t *running, void *ent)
+{
+	scf_propertygroup_t *ud_mfsts_pg = NULL;
+	scf_property_t *ud_prop = NULL;
 	scf_iter_t *ud_prop_iter;
 	scf_value_t *fname_value;
+	scf_callback_t cbdata;
 	pgroup_t *mfst_pgroup;
 	property_t *mfst_prop;
 	property_t *old_prop;
@@ -3317,6 +3318,7 @@
 	char *fval;
 	char *old_pname;
 	char *old_fval;
+	int no_upgrade_pg;
 	int mfst_seen;
 	int r;
 
@@ -3327,22 +3329,19 @@
 	 * path, and the fact that the manifests pg is a service
 	 * level property group only.
 	 */
-	ud_run_mfsts_pg = scf_pg_create(g_hndl);
-	ud_run_prop = scf_property_create(g_hndl);
+	ud_mfsts_pg = scf_pg_create(g_hndl);
+	ud_prop = scf_property_create(g_hndl);
 	ud_prop_iter = scf_iter_create(g_hndl);
 	fname_value = scf_value_create(g_hndl);
 
-	/* Fetch the running version of the "manifests" property group */
-	if (running != NULL)
-		r = scf_snaplevel_get_pg(running, SCF_PG_MANIFESTFILES,
-		    ud_run_mfsts_pg);
-	else
-		r = entity_get_pg(ent, issvc, SCF_PG_MANIFESTFILES,
-		    ud_run_mfsts_pg);
-
+	/* Fetch the "manifests" property group */
+	no_upgrade_pg = 0;
+	r = entity_get_pg(ent, issvc, SCF_PG_MANIFESTFILES,
+	    ud_mfsts_pg);
 	if (r != 0) {
 		switch (scf_error()) {
 		case SCF_ERROR_NOT_FOUND:
+			no_upgrade_pg = 1;
 			break;
 
 		case SCF_ERROR_DELETED:
@@ -3359,6 +3358,20 @@
 		}
 	}
 
+	if (no_upgrade_pg) {
+		cbdata.sc_handle = g_hndl;
+		cbdata.sc_parent = ent;
+		cbdata.sc_service = issvc;
+		cbdata.sc_flags = SCI_FORCE;
+		cbdata.sc_source_fmri = ient->sc_fmri;
+		cbdata.sc_target_fmri = ient->sc_fmri;
+
+		if (entity_pgroup_import(pg, &cbdata) != UU_WALK_NEXT)
+			return (cbdata.sc_err);
+
+		return (0);
+	}
+
 	/* Fetch the new manifests property group */
 	for (mfst_pgroup = uu_list_first(ient->sc_pgroups);
 	    mfst_pgroup != NULL;
@@ -3368,13 +3381,13 @@
 			break;
 	}
 
-	if (scf_iter_pg_properties(ud_prop_iter, ud_run_mfsts_pg) !=
+	if ((r = scf_iter_pg_properties(ud_prop_iter, ud_mfsts_pg)) !=
 	    SCF_SUCCESS)
 		return (-1);
 
-	while ((r = scf_iter_next_property(ud_prop_iter, ud_run_prop)) == 1) {
+	while ((r = scf_iter_next_property(ud_prop_iter, ud_prop)) == 1) {
 		mfst_seen = 0;
-		if (scf_property_get_name(ud_run_prop, pname, MAXPATHLEN) < 0)
+		if (scf_property_get_name(ud_prop, pname, MAXPATHLEN) < 0)
 			continue;
 
 		for (mfst_prop = uu_list_first(mfst_pgroup->sc_pgroup_props);
@@ -3395,11 +3408,11 @@
 
 			/*
 			 * If we cannot get the value then there is no
-			 * reasont to attempt to attach the value to
+			 * reason to attempt to attach the value to
 			 * the property group
 			 */
 			if (fval != NULL &&
-			    prop_get_val(ud_run_prop, fname_value) == 0 &&
+			    prop_get_val(ud_prop, fname_value) == 0 &&
 			    scf_value_get_astring(fname_value, fval,
 			    MAXPATHLEN) != -1)  {
 				/*
@@ -3410,6 +3423,7 @@
 					free(fval);
 					continue;
 				}
+
 				old_pname = safe_strdup(pname);
 				old_fval = safe_strdup(fval);
 				old_prop = internal_property_create(old_pname,
@@ -3425,6 +3439,16 @@
 		}
 	}
 
+	cbdata.sc_handle = g_hndl;
+	cbdata.sc_parent = ent;
+	cbdata.sc_service = issvc;
+	cbdata.sc_flags = SCI_FORCE;
+	cbdata.sc_source_fmri = ient->sc_fmri;
+	cbdata.sc_target_fmri = ient->sc_fmri;
+
+	if (entity_pgroup_import(mfst_pgroup, &cbdata) != UU_WALK_NEXT)
+		return (cbdata.sc_err);
+
 	return (r);
 }
 
@@ -4840,8 +4864,7 @@
 		return (upgrade_dependents(lipg, imp_snpl, ient, running, ent));
 
 	if (strcmp(imp_str, SCF_PG_MANIFESTFILES) == 0)
-		if ((r = upgrade_manifestfiles(ient, running, ent)) != 0)
-			return (r);
+		return (upgrade_manifestfiles(NULL, ient, running, ent));
 
 	if (mpg == NULL || mpg->sc_pgroup_delete) {
 		/* property group was deleted from manifest */
@@ -5378,12 +5401,39 @@
 			continue;
 		}
 
-		if (running != NULL)
+		if (strcmp(pg->sc_pgroup_name, SCF_PG_MANIFESTFILES) == 0) {
+			r = upgrade_manifestfiles(pg, ient, running, ent);
+			switch (r) {
+			case 0:
+				break;
+
+			case ECONNABORTED:
+			case ENOMEM:
+			case ENOSPC:
+			case ECANCELED:
+			case ENODEV:
+			case EBADF:
+			case EBUSY:
+			case EINVAL:
+			case EPERM:
+			case EROFS:
+			case EACCES:
+			case EEXIST:
+				return (r);
+
+			default:
+				bad_error("upgrade_manifestfiles", r);
+			}
+			continue;
+		}
+
+		if (running != NULL) {
 			r = scf_snaplevel_get_pg(running, pg->sc_pgroup_name,
 			    imp_pg);
-		else
+		} else {
 			r = entity_get_pg(ent, issvc, pg->sc_pgroup_name,
 			    imp_pg);
+		}
 		if (r != 0) {
 			scf_callback_t cbdata;
 
--- a/usr/src/cmd/zdb/zdb.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/zdb/zdb.c	Mon Oct 05 09:17:26 2009 -0400
@@ -100,6 +100,7 @@
 	(void) fprintf(stderr, "	-u uberblock\n");
 	(void) fprintf(stderr, "	-d datasets\n");
 	(void) fprintf(stderr, "        -C cached pool configuration\n");
+	(void) fprintf(stderr, "        -h pool history\n");
 	(void) fprintf(stderr, "	-i intent logs\n");
 	(void) fprintf(stderr, "	-b block statistics\n");
 	(void) fprintf(stderr, "	-m metaslabs\n");
@@ -504,7 +505,7 @@
 	char *name[DTL_TYPES] = { "missing", "partial", "scrub", "outage" };
 	char prefix[256];
 
-	spa_vdev_state_enter(spa);
+	spa_vdev_state_enter(spa, SCL_NONE);
 	required = vdev_dtl_required(vd);
 	(void) spa_vdev_state_exit(spa, NULL, 0);
 
@@ -534,6 +535,67 @@
 		dump_dtl(vd->vdev_child[c], indent + 4);
 }
 
+static void
+dump_history(spa_t *spa)
+{
+	nvlist_t **events = NULL;
+	char buf[SPA_MAXBLOCKSIZE];
+	uint64_t resid, off = 0;
+	uint64_t len = sizeof (buf);
+	uint_t num = 0;
+	int error;
+	time_t tsec;
+	struct tm t;
+	char tbuf[30];
+	char internalstr[MAXPATHLEN];
+
+	do {
+		if ((error = spa_history_get(spa, &off, &len, buf)) != 0) {
+			(void) fprintf(stderr, "Unable to read history: "
+			    "error %d\n", error);
+			return;
+		}
+
+		if (zpool_history_unpack(buf, len, &resid, &events, &num) != 0)
+			break;
+
+		off -= resid;
+	} while (len != 0);
+
+	(void) printf("\nHistory:\n");
+	for (int i = 0; i < num; i++) {
+		uint64_t time, txg, ievent;
+		char *cmd, *intstr;
+
+		if (nvlist_lookup_uint64(events[i], ZPOOL_HIST_TIME,
+		    &time) != 0)
+			continue;
+		if (nvlist_lookup_string(events[i], ZPOOL_HIST_CMD,
+		    &cmd) != 0) {
+			if (nvlist_lookup_uint64(events[i],
+			    ZPOOL_HIST_INT_EVENT, &ievent) != 0)
+				continue;
+			verify(nvlist_lookup_uint64(events[i],
+			    ZPOOL_HIST_TXG, &txg) == 0);
+			verify(nvlist_lookup_string(events[i],
+			    ZPOOL_HIST_INT_STR, &intstr) == 0);
+			if (ievent >= LOG_END)
+				continue;
+
+			(void) snprintf(internalstr,
+			    sizeof (internalstr),
+			    "[internal %s txg:%lld] %s",
+			    hist_event_table[ievent], txg,
+			    intstr);
+			cmd = internalstr;
+		}
+		tsec = time;
+		(void) localtime_r(&tsec, &t);
+		(void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t);
+		(void) printf("%s %s\n", tbuf, cmd);
+	}
+}
+
 /*ARGSUSED*/
 static void
 dump_dnode(objset_t *os, uint64_t object, void *data, size_t size)
@@ -1791,6 +1853,9 @@
 	if (dump_opt['s'])
 		show_pool_stats(spa);
 
+	if (dump_opt['h'])
+		dump_history(spa);
+
 	if (rc != 0)
 		exit(rc);
 }
@@ -2256,11 +2321,12 @@
 
 	dprintf_setup(&argc, argv);
 
-	while ((c = getopt(argc, argv, "udibcmsvCLS:U:lRep:t:")) != -1) {
+	while ((c = getopt(argc, argv, "udhibcmsvCLS:U:lRep:t:")) != -1) {
 		switch (c) {
 		case 'u':
 		case 'd':
 		case 'i':
+		case 'h':
 		case 'b':
 		case 'c':
 		case 'm':
@@ -2415,6 +2481,23 @@
 			    B_TRUE, FTAG, &os);
 		} else {
 			error = spa_open(argv[0], &spa, FTAG);
+			if (error) {
+				/*
+				 * If we're missing the log device then
+				 * try opening the pool after clearing the
+				 * log state.
+				 */
+				mutex_enter(&spa_namespace_lock);
+				if ((spa = spa_lookup(argv[0])) != NULL &&
+				    spa->spa_log_state == SPA_LOG_MISSING) {
+					spa->spa_log_state = SPA_LOG_CLEAR;
+					error = 0;
+				}
+				mutex_exit(&spa_namespace_lock);
+
+				if (!error)
+					error = spa_open(argv[0], &spa, FTAG);
+			}
 		}
 	}
 
--- a/usr/src/cmd/zinject/zinject.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/zinject/zinject.c	Mon Oct 05 09:17:26 2009 -0400
@@ -227,11 +227,15 @@
 	    "\t\tfunctions which call spa_vdev_config_exit(), or \n"
 	    "\t\tspa_vdev_exit() will trigger a panic.\n"
 	    "\n"
-	    "\tzinject -d device [-e errno] [-L <nvlist|uber>] [-F] pool\n"
+	    "\tzinject -d device [-e errno] [-L <nvlist|uber>] [-F]\n"
+	    "\t    [-T <read|write|free|claim|all> pool\n"
 	    "\t\tInject a fault into a particular device or the device's\n"
 	    "\t\tlabel.  Label injection can either be 'nvlist' or 'uber'.\n"
 	    "\t\t'errno' can either be 'nxio' (the default) or 'io'.\n"
 	    "\n"
+	    "\tzinject -d device -A <degrade|fault> pool\n"
+	    "\t\tPerform a specific action on a particular device\n"
+	    "\n"
 	    "\tzinject -b objset:object:level:blkid pool\n"
 	    "\n"
 	    "\t\tInject an error into pool 'pool' with the numeric bookmark\n"
@@ -497,6 +501,22 @@
 }
 
 int
+perform_action(const char *pool, zinject_record_t *record, int cmd)
+{
+	zfs_cmd_t zc;
+
+	ASSERT(cmd == VDEV_STATE_DEGRADED || cmd == VDEV_STATE_FAULTED);
+	(void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name));
+	zc.zc_guid = record->zi_guid;
+	zc.zc_cookie = cmd;
+
+	if (ioctl(zfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
+		return (0);
+
+	return (1);
+}
+
+int
 main(int argc, char **argv)
 {
 	int c;
@@ -509,6 +529,8 @@
 	int quiet = 0;
 	int error = 0;
 	int domount = 0;
+	int io_type = ZIO_TYPES;
+	int action = VDEV_STATE_UNKNOWN;
 	err_type_t type = TYPE_INVAL;
 	err_type_t label = TYPE_INVAL;
 	zinject_record_t record = { 0 };
@@ -546,11 +568,24 @@
 		return (0);
 	}
 
-	while ((c = getopt(argc, argv, ":ab:d:f:Fqhc:t:l:mr:e:uL:p:")) != -1) {
+	while ((c = getopt(argc, argv,
+	    ":aA:b:d:f:Fqhc:t:T:l:mr:e:uL:p:")) != -1) {
 		switch (c) {
 		case 'a':
 			flags |= ZINJECT_FLUSH_ARC;
 			break;
+		case 'A':
+			if (strcasecmp(optarg, "degrade") == 0) {
+				action = VDEV_STATE_DEGRADED;
+			} else if (strcasecmp(optarg, "fault") == 0) {
+				action = VDEV_STATE_FAULTED;
+			} else {
+				(void) fprintf(stderr, "invalid action '%s': "
+				    "must be 'degrade' or 'fault'\n", optarg);
+				usage();
+				return (1);
+			}
+			break;
 		case 'b':
 			raw = optarg;
 			break;
@@ -611,6 +646,25 @@
 		case 'r':
 			range = optarg;
 			break;
+		case 'T':
+			if (strcasecmp(optarg, "read") == 0) {
+				io_type = ZIO_TYPE_READ;
+			} else if (strcasecmp(optarg, "write") == 0) {
+				io_type = ZIO_TYPE_WRITE;
+			} else if (strcasecmp(optarg, "free") == 0) {
+				io_type = ZIO_TYPE_FREE;
+			} else if (strcasecmp(optarg, "claim") == 0) {
+				io_type = ZIO_TYPE_CLAIM;
+			} else if (strcasecmp(optarg, "all") == 0) {
+				io_type = ZIO_TYPES;
+			} else {
+				(void) fprintf(stderr, "invalid I/O type "
+				    "'%s': must be 'read', 'write', 'free', "
+				    "'claim' or 'all'\n", optarg);
+				usage();
+				return (1);
+			}
+			break;
 		case 't':
 			if ((type = name_to_type(optarg)) == TYPE_INVAL &&
 			    !MOS_TYPE(type)) {
@@ -708,10 +762,15 @@
 			return (1);
 		}
 
+		record.zi_iotype = io_type;
 		if (translate_device(pool, device, label, &record) != 0)
 			return (1);
 		if (!error)
 			error = ENXIO;
+
+		if (action != VDEV_STATE_UNKNOWN)
+			return (perform_action(pool, &record, action));
+
 	} else if (raw != NULL) {
 		if (range != NULL || type != TYPE_INVAL || level != 0 ||
 		    record.zi_func[0] != '\0') {
--- a/usr/src/cmd/zpool/zpool_main.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/zpool/zpool_main.c	Mon Oct 05 09:17:26 2009 -0400
@@ -3624,49 +3624,6 @@
 	int internal;
 } hist_cbdata_t;
 
-char *hist_event_table[LOG_END] = {
-	"invalid event",
-	"pool create",
-	"vdev add",
-	"pool remove",
-	"pool destroy",
-	"pool export",
-	"pool import",
-	"vdev attach",
-	"vdev replace",
-	"vdev detach",
-	"vdev online",
-	"vdev offline",
-	"vdev upgrade",
-	"pool clear",
-	"pool scrub",
-	"pool property set",
-	"create",
-	"clone",
-	"destroy",
-	"destroy_begin_sync",
-	"inherit",
-	"property set",
-	"quota set",
-	"permission update",
-	"permission remove",
-	"permission who remove",
-	"promote",
-	"receive",
-	"rename",
-	"reservation set",
-	"replay_inc_sync",
-	"replay_full_sync",
-	"rollback",
-	"snapshot",
-	"filesystem version upgrade",
-	"refquota set",
-	"refreservation set",
-	"pool scrub done",
-	"user hold",
-	"user release",
-};
-
 /*
  * Print out the command history for a specific pool.
  */
--- a/usr/src/cmd/ztest/ztest.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/cmd/ztest/ztest.c	Mon Oct 05 09:17:26 2009 -0400
@@ -185,6 +185,7 @@
 ztest_func_t ztest_vdev_add_remove;
 ztest_func_t ztest_vdev_aux_add_remove;
 ztest_func_t ztest_scrub;
+ztest_func_t ztest_dmu_snapshot_hold;
 
 typedef struct ztest_info {
 	ztest_func_t	*zi_func;	/* test function */
@@ -203,18 +204,19 @@
 
 ztest_info_t ztest_info[] = {
 	{ ztest_dmu_read_write,			1,	&zopt_always	},
-	{ ztest_dmu_read_write_zcopy,		1,	&zopt_always	},
 	{ ztest_dmu_write_parallel,		30,	&zopt_always	},
 	{ ztest_dmu_object_alloc_free,		1,	&zopt_always	},
 	{ ztest_dmu_commit_callbacks,		10,	&zopt_always	},
 	{ ztest_zap,				30,	&zopt_always	},
 	{ ztest_fzap,				30,	&zopt_always	},
 	{ ztest_zap_parallel,			100,	&zopt_always	},
+	{ ztest_dmu_read_write_zcopy,		1,	&zopt_sometimes	},
 	{ ztest_dsl_prop_get_set,		1,	&zopt_sometimes	},
 	{ ztest_dmu_objset_create_destroy,	1,	&zopt_sometimes },
 	{ ztest_dmu_snapshot_create_destroy,	1,	&zopt_sometimes },
 	{ ztest_spa_create_destroy,		1,	&zopt_sometimes },
 	{ ztest_fault_inject,			1,	&zopt_sometimes	},
+	{ ztest_dmu_snapshot_hold,		1,	&zopt_sometimes	},
 	{ ztest_spa_rename,			1,	&zopt_rarely	},
 	{ ztest_vdev_attach_detach,		1,	&zopt_rarely	},
 	{ ztest_vdev_LUN_growth,		1,	&zopt_rarely	},
@@ -1245,8 +1247,8 @@
 {
 	spa_t *spa = vd->vdev_spa;
 	vdev_t *tvd = vd->vdev_top;
-	vdev_t *pvd = vd->vdev_parent;
 	uint64_t guid = vd->vdev_guid;
+	uint64_t generation = spa->spa_config_generation + 1;
 
 	ASSERT(spa_config_held(spa, SCL_STATE, RW_READER) == SCL_STATE);
 	ASSERT(vd->vdev_ops->vdev_op_leaf);
@@ -1262,10 +1264,14 @@
 	 * vdev may have been detached/replaced while we were
 	 * trying to online it.
 	 */
-	if (vd != vdev_lookup_by_guid(tvd, guid) || vd->vdev_parent != pvd) {
-		if (zopt_verbose >= 6) {
-			(void) printf("vdev %p has disappeared, was "
-			    "guid %llu\n", (void *)vd, (u_longlong_t)guid);
+	if (generation != spa->spa_config_generation) {
+		if (zopt_verbose >= 5) {
+			(void) printf("vdev configuration has changed, "
+			    "guid %llu, state %llu, expected gen %llu, "
+			    "got gen %llu\n", (u_longlong_t)guid,
+			    (u_longlong_t)tvd->vdev_state,
+			    (u_longlong_t)generation,
+			    (u_longlong_t)spa->spa_config_generation);
 		}
 		return (vd);
 	}
@@ -1309,7 +1315,6 @@
 	uint64_t spa_newsize, spa_cursize, ms_count;
 
 	(void) mutex_lock(&ztest_shared->zs_vdev_lock);
-	mutex_enter(&spa_namespace_lock);
 	spa_config_enter(spa, SCL_STATE, spa, RW_READER);
 
 	while (tvd == NULL || tvd->vdev_islog) {
@@ -1330,12 +1335,12 @@
 	psize = vd->vdev_psize;
 
 	/*
-	 * We only try to expand the vdev if it's less than 4x its
-	 * original size and it has a valid psize.
+	 * We only try to expand the vdev if it's healthy, less than 4x its
+	 * original size, and it has a valid psize.
 	 */
-	if (psize == 0 || psize >= 4 * zopt_vdev_size) {
+	if (tvd->vdev_state != VDEV_STATE_HEALTHY ||
+	    psize == 0 || psize >= 4 * zopt_vdev_size) {
 		spa_config_exit(spa, SCL_STATE, spa);
-		mutex_exit(&spa_namespace_lock);
 		(void) mutex_unlock(&ztest_shared->zs_vdev_lock);
 		return;
 	}
@@ -1361,16 +1366,14 @@
 	    tvd->vdev_state != VDEV_STATE_HEALTHY) {
 		if (zopt_verbose >= 5) {
 			(void) printf("Could not expand LUN because "
-			    "some vdevs were not healthy\n");
+			    "the vdev configuration changed.\n");
 		}
 		(void) spa_config_exit(spa, SCL_STATE, spa);
-		mutex_exit(&spa_namespace_lock);
 		(void) mutex_unlock(&ztest_shared->zs_vdev_lock);
 		return;
 	}
 
 	(void) spa_config_exit(spa, SCL_STATE, spa);
-	mutex_exit(&spa_namespace_lock);
 
 	/*
 	 * Expanding the LUN will update the config asynchronously,
@@ -3467,6 +3470,119 @@
 }
 
 /*
+ * Test snapshot hold/release and deferred destroy.
+ */
+void
+ztest_dmu_snapshot_hold(ztest_args_t *za)
+{
+	int error;
+	objset_t *os = za->za_os;
+	objset_t *origin;
+	uint64_t curval = za->za_instance;
+	char snapname[100];
+	char fullname[100];
+	char clonename[100];
+	char tag[100];
+	char osname[MAXNAMELEN];
+
+	(void) rw_rdlock(&ztest_shared->zs_name_lock);
+
+	dmu_objset_name(os, osname);
+
+	(void) snprintf(snapname, 100, "sh1_%llu", curval);
+	(void) snprintf(fullname, 100, "%s@%s", osname, snapname);
+	(void) snprintf(clonename, 100, "%s/ch1_%llu", osname, curval);
+	(void) snprintf(tag, 100, "%tag_%llu", curval);
+
+	/*
+	 * Clean up from any previous run.
+	 */
+	(void) dmu_objset_destroy(clonename, B_FALSE);
+	(void) dsl_dataset_user_release(osname, snapname, tag, B_FALSE);
+	(void) dmu_objset_destroy(fullname, B_FALSE);
+
+	/*
+	 * Create snapshot, clone it, mark snap for deferred destroy,
+	 * destroy clone, verify snap was also destroyed.
+	 */
+	error = dmu_objset_snapshot(osname, snapname, NULL, FALSE);
+	if (error) {
+		if (error == ENOSPC) {
+			ztest_record_enospc("dmu_objset_snapshot");
+			goto out;
+		}
+		fatal(0, "dmu_objset_snapshot(%s) = %d", fullname, error);
+	}
+
+	error = dmu_objset_hold(fullname, FTAG, &origin);
+	if (error)
+		fatal(0, "dmu_objset_hold(%s) = %d", fullname, error);
+
+	error = dmu_objset_clone(clonename, dmu_objset_ds(origin), 0);
+	dmu_objset_rele(origin, FTAG);
+	if (error) {
+		if (error == ENOSPC) {
+			ztest_record_enospc("dmu_objset_clone");
+			goto out;
+		}
+		fatal(0, "dmu_objset_clone(%s) = %d", clonename, error);
+	}
+
+	error = dmu_objset_destroy(fullname, B_TRUE);
+	if (error) {
+		fatal(0, "dmu_objset_destroy(%s, B_TRUE) = %d",
+		    fullname, error);
+	}
+
+	error = dmu_objset_destroy(clonename, B_FALSE);
+	if (error)
+		fatal(0, "dmu_objset_destroy(%s) = %d", clonename, error);
+
+	error = dmu_objset_hold(fullname, FTAG, &origin);
+	if (error != ENOENT)
+		fatal(0, "dmu_objset_hold(%s) = %d", fullname, error);
+
+	/*
+	 * Create snapshot, add temporary hold, verify that we can't
+	 * destroy a held snapshot, mark for deferred destroy,
+	 * release hold, verify snapshot was destroyed.
+	 */
+	error = dmu_objset_snapshot(osname, snapname, NULL, FALSE);
+	if (error) {
+		if (error == ENOSPC) {
+			ztest_record_enospc("dmu_objset_snapshot");
+			goto out;
+		}
+		fatal(0, "dmu_objset_snapshot(%s) = %d", fullname, error);
+	}
+
+	error = dsl_dataset_user_hold(osname, snapname, tag, B_FALSE, B_TRUE);
+	if (error)
+		fatal(0, "dsl_dataset_user_hold(%s)", fullname, tag);
+
+	error = dmu_objset_destroy(fullname, B_FALSE);
+	if (error != EBUSY) {
+		fatal(0, "dmu_objset_destroy(%s, B_FALSE) = %d",
+		    fullname, error);
+	}
+
+	error = dmu_objset_destroy(fullname, B_TRUE);
+	if (error) {
+		fatal(0, "dmu_objset_destroy(%s, B_TRUE) = %d",
+		    fullname, error);
+	}
+
+	error = dsl_dataset_user_release(osname, snapname, tag, B_FALSE);
+	if (error)
+		fatal(0, "dsl_dataset_user_release(%s)", fullname, tag);
+
+	VERIFY(dmu_objset_hold(fullname, FTAG, &origin) == ENOENT);
+
+out:
+	(void) rw_unlock(&ztest_shared->zs_name_lock);
+}
+
+/*
  * Inject random faults into the on-disk data.
  */
 void
@@ -3486,6 +3602,7 @@
 	int maxfaults = zopt_maxfaults;
 	vdev_t *vd0 = NULL;
 	uint64_t guid0 = 0;
+	boolean_t islog = B_FALSE;
 
 	ASSERT(leaves >= 1);
 
@@ -3513,6 +3630,9 @@
 		    zopt_dir, zopt_pool, top * leaves + leaf);
 
 		vd0 = vdev_lookup_by_path(spa->spa_root_vdev, path0);
+		if (vd0 != NULL && vd0->vdev_top->vdev_islog)
+			islog = B_TRUE;
+
 		if (vd0 != NULL && maxfaults != 1) {
 			/*
 			 * Make vd0 explicitly claim to be unreadable,
@@ -3558,22 +3678,38 @@
 
 	spa_config_exit(spa, SCL_STATE, FTAG);
 
-	if (maxfaults == 0)
-		return;
-
 	/*
-	 * If we can tolerate two or more faults, randomly online/offline vd0.
+	 * If we can tolerate two or more faults, or we're dealing
+	 * with a slog, randomly online/offline vd0.
 	 */
-	if (maxfaults >= 2 && guid0 != 0) {
+	if ((maxfaults >= 2 || islog) && guid0 != 0) {
 		if (ztest_random(10) < 6) {
 			int flags = (ztest_random(2) == 0 ?
 			    ZFS_OFFLINE_TEMPORARY : 0);
+
+			/*
+			 * We have to grab the zs_name_lock as writer to
+			 * prevent a race between offlining a slog and
+			 * destroying a dataset. Offlining the slog will
+			 * grab a reference on the dataset which may cause
+			 * dmu_objset_destroy() to fail with EBUSY thus
+			 * leaving the dataset in an inconsistent state.
+			 */
+			if (islog)
+				(void) rw_wrlock(&ztest_shared->zs_name_lock);
+
 			VERIFY(vdev_offline(spa, guid0, flags) != EBUSY);
+
+			if (islog)
+				(void) rw_unlock(&ztest_shared->zs_name_lock);
 		} else {
 			(void) vdev_online(spa, guid0, 0, NULL);
 		}
 	}
 
+	if (maxfaults == 0)
+		return;
+
 	/*
 	 * We have at least single-fault tolerance, so inject data corruption.
 	 */
@@ -3921,7 +4057,7 @@
 ztest_resume(spa_t *spa)
 {
 	if (spa_suspended(spa)) {
-		spa_vdev_state_enter(spa);
+		spa_vdev_state_enter(spa, SCL_NONE);
 		vdev_clear(spa, NULL);
 		(void) spa_vdev_state_exit(spa, NULL, 0);
 		(void) zio_resume(spa);
--- a/usr/src/common/devid/devid_scsi.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/common/devid/devid_scsi.c	Mon Oct 05 09:17:26 2009 -0400
@@ -41,6 +41,9 @@
 #include <sys/dditypes.h>
 #include <sys/ddi_impldefs.h>
 #include <sys/scsi/scsi.h>
+#ifndef _KERNEL
+#include <sys/libdevid.h>
+#endif /* !_KERNEL */
 #include "devid_impl.h"
 
 #define	SCSI_INQUIRY_VID_POS			9
@@ -1247,8 +1250,7 @@
 		return (DDI_FAILURE);
 
 	/* Skip leading 'w' if wwnstr is in unit-address form */
-	if (*wwnstr == 'w')
-		wwnstr++;
+	wwnstr = scsi_wwnstr_skip_ua_prefix(wwnstr);
 
 	if (strlen(wwnstr) != 16)
 		return (DDI_FAILURE);
@@ -1331,6 +1333,23 @@
 }
 
 /*
+ * Function: scsi_wwnstr_skip_ua_prefix
+ *
+ * Description: This routine removes the leading 'w' in wwnstr,
+ * 		if its in unit-address form.
+ *
+ * Arguments: wwnstr - the string wwn to be transformed
+ *
+ */
+const char *
+scsi_wwnstr_skip_ua_prefix(const char *wwnstr)
+{
+	if (*wwnstr == 'w')
+		wwnstr++;
+	return (wwnstr);
+}
+
+/*
  *    Function: scsi_wwnstr_free
  *
  * Description: This routine frees a wwnstr returned by a call
--- a/usr/src/grub/grub-0.97/netboot/fsys_tftp.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/grub/grub-0.97/netboot/fsys_tftp.c	Mon Oct 05 09:17:26 2009 -0400
@@ -395,7 +395,7 @@
 	/* Neither TFTP_OACK nor TFTP_DATA.  */
 	break;
 
-      if ((block || bcounter) && (block != prevblock + (unsigned short) 1))
+      if ((block || bcounter) && (block != (unsigned short) (prevblock + 1)))
 	/* Block order should be continuous */
 	tp.u.ack.block = htons (block = prevblock);
       
--- a/usr/src/lib/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -163,8 +163,6 @@
 	libinstzones	\
 	libpkg
 
-$(CLOSED_BUILD)SUBDIRS += \
-	$(CLOSED)/lib/smartcard
 SUBDIRS += \
 	passwdutil	\
 	pam_modules	\
@@ -363,8 +361,6 @@
 	pam_modules	\
 	pyzfs		\
 	rpcsec_gss
-$(CLOSED_BUILD)MSGSUBDIRS += \
-	$(CLOSED)/lib/smartcard
 MSGSUBDIRS += \
 	$($(MACH)_MSGSUBDIRS)
 
@@ -499,8 +495,7 @@
 
 $(CLOSED_BUILD)HDRSUBDIRS += \
 	$(CLOSED)/lib/libc_i18n	\
-	$(CLOSED)/lib/libike	\
-	$(CLOSED)/lib/smartcard
+	$(CLOSED)/lib/libike
 
 i386_HDRSUBDIRS=	\
 	libparted	\
@@ -553,9 +548,6 @@
 
 _dc:			$(DCSUBDIRS:%=%-nodepend)
 
-# dependencies for pam_modules (see below)
-$(CLOSED_BUILD)SMARTCARD=	$(CLOSED)/lib/smartcard
-
 #
 # Library interdependencies are called out explicitly here
 #
@@ -619,7 +611,7 @@
 libwanboot:	libnvpair libresolv libnsl libsocket libdevinfo libinetutil \
 		libdhcputil
 libwanbootutil:	libnsl
-pam_modules:	libproject passwdutil $(SMARTCARD) smbsrv
+pam_modules:	libproject passwdutil smbsrv
 libscf:		libuutil libmd libgen libsmbios
 libinetsvc:	libscf
 librestart:	libuutil libscf
--- a/usr/src/lib/cfgadm_plugins/scsi/common/cfga_ctl.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/cfgadm_plugins/scsi/common/cfga_ctl.c	Mon Oct 05 09:17:26 2009 -0400
@@ -20,12 +20,10 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include "cfga_scsi.h"
 
 struct larg {
@@ -299,44 +297,54 @@
 			break;
 		}
 
-		/* When unconfiguring a device, first offline it through RCM. */
-		if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
-			if (cmd == SCFGA_DEV_UNCONFIGURE) {
-				dev_list[0] = get_node_path(apidp->path);
-				if (dev_list[0] == NULL) {
-					ret = SCFGA_ERR;
-					break;
-				}
-				if ((ret = scsi_rcm_offline(dev_list,
-				    errstring, flags)) != SCFGA_OK) {
-					break;
+		if (apidp->dyntype == PATH_APID) {
+			/* call a scsi_vhci ioctl to do online/offline path. */
+			ret = path_apid_state_change(apidp, cmd,
+			    flags, errstring, &l_errno, errid);
+		} else {
+			/*
+			 * When unconfiguring a device, first offline it
+			 * through RCM.
+			 */
+			if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
+				if (cmd == SCFGA_DEV_UNCONFIGURE) {
+					dev_list[0] =
+					    get_node_path(apidp->path);
+					if (dev_list[0] == NULL) {
+						ret = SCFGA_ERR;
+						break;
+					}
+					if ((ret = scsi_rcm_offline(dev_list,
+					    errstring, flags)) != SCFGA_OK) {
+						break;
+					}
 				}
 			}
-		}
 
-		ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
-		if (ret != SCFGA_OK) {
-			cfga_err(errstring, l_errno, errid, 0);
+			ret = devctl_cmd(apidp->path, cmd, NULL, &l_errno);
+			if (ret != SCFGA_OK) {
+				cfga_err(errstring, l_errno, errid, 0);
 
 			/*
 			 * If an unconfigure fails, cancel the RCM offline.
 			 * Discard any RCM failures so that the devctl
 			 * failure will still be reported.
 			 */
+				if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
+					if (cmd == SCFGA_DEV_UNCONFIGURE)
+						(void) scsi_rcm_online(dev_list,
+						    errstring, flags);
+				}
+				break;
+			}
 			if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
-				if (cmd == SCFGA_DEV_UNCONFIGURE)
-					(void) scsi_rcm_online(dev_list,
-					    errstring, flags);
-			}
-			break;
-		}
-		if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
 			/*
 			 * Unconfigure succeeded, call the RCM notify_remove.
 			 */
-			if (cmd == SCFGA_DEV_UNCONFIGURE)
-				(void) scsi_rcm_remove(dev_list,
+				if (cmd == SCFGA_DEV_UNCONFIGURE)
+					(void) scsi_rcm_remove(dev_list,
 					    errstring, flags);
+			}
 		}
 		break;
 
--- a/usr/src/lib/cfgadm_plugins/scsi/common/cfga_cvt.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/cfgadm_plugins/scsi/common/cfga_cvt.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 1999, 2001-2002 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include "cfga_scsi.h"
 
 typedef struct {
@@ -78,6 +75,8 @@
     char **dyncompp, int *l_errnop);
 static scfga_ret_t get_hba_devlink(const char *hba_phys,
     char **hba_logpp, int *l_errnop);
+static scfga_ret_t path_apid_dyn_to_path(const char *hba_phys, const char *dyn,
+    char **pathpp, int *l_errnop);
 
 
 /* Globals */
@@ -263,19 +262,152 @@
 	/*
 	 * If the dynamic component has a '/', it was derived from a devlink
 	 * Else it was derived from driver name and instance number.
+	 * If it is pathinfo instance number based ap id, it will have a format
+	 * path#.???.
 	 */
 	if (strchr(dyncomp, '/') != NULL) {
 		ret = devlink_dyn_to_devpath(hba_phys, dyncomp, pathpp,
 		    l_errnop);
+	} else if (strstr(dyncomp, PATH_APID_DYN_SEP) != NULL) {
+		ret = path_apid_dyn_to_path(hba_phys, dyncomp, pathpp,
+		    l_errnop);
 	} else {
 		ret = drv_dyn_to_devpath(hba_phys, dyncomp, pathpp, l_errnop);
 	}
-
 	assert(ret != SCFGA_OK || *pathpp != NULL);
 
+
 	return (ret);
 }
 
+/*
+ * Get the devfs path of pathinfo node that is associated with
+ * the given dynamic component.
+ *
+ * input
+ *   hba_phys: physical path of HBA
+ *   dyn : bus address of pathinfo node
+ * output:
+ *   pathpp: devfs path of the pathinfo node.
+ */
+static scfga_ret_t
+path_apid_dyn_to_path(
+	const char *hba_phys,
+	const char *dyn,
+	char **pathpp,
+	int *l_errnop)
+{
+
+	di_node_t   root, walk_root;
+	di_path_t   pi_node = DI_PATH_NIL;
+	char	    *root_path, *devpath, *cp;
+	int	    len;
+
+	*l_errnop = 0;
+
+	/* *pathpp should be NULL if pathpp is not NULL. */
+	if ((hba_phys == NULL) || (pathpp != NULL) && (*pathpp != NULL)) {
+		return (SCFGA_LIB_ERR);
+	}
+
+	if ((root_path = strdup(hba_phys)) == NULL) {
+		*l_errnop = errno;
+		return (SCFGA_LIB_ERR);
+	}
+
+	/* Fix up path for di_init() */
+	len = strlen(DEVICES_DIR);
+	if (strncmp(root_path, DEVICES_DIR SLASH,
+	    len + strlen(SLASH)) == 0) {
+		cp = root_path + len;
+		(void) memmove(root_path, cp, strlen(cp) + 1);
+	} else if (*root_path != '/') {
+		*l_errnop = 0;
+		S_FREE(root_path);
+		return (SCFGA_ERR);
+	}
+
+	/* Remove dynamic component if any */
+	if ((cp = GET_DYN(root_path)) != NULL) {
+		*cp = '\0';
+	}
+
+	/* Remove minor name if any */
+	if ((cp = strrchr(root_path, ':')) != NULL) {
+		*cp = '\0';
+	}
+
+	/*
+	 * Cached snapshots are always rooted at "/"
+	 */
+
+	/* Get a snapshot */
+	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
+		*l_errnop = errno;
+		S_FREE(root_path);
+		return (SCFGA_ERR);
+	}
+
+	/*
+	 * Lookup the subtree of interest
+	 */
+	walk_root = di_lookup_node(root, root_path);
+
+	if (walk_root == DI_NODE_NIL) {
+		*l_errnop = errno;
+		di_fini(root);
+		S_FREE(root_path);
+		return (SCFGA_LIB_ERR);
+	}
+
+	S_FREE(root_path);
+
+	if ((pi_node = di_path_next_client(walk_root, pi_node)) ==
+	    DI_PATH_NIL) {
+		di_fini(root);
+		return (SCFGA_APID_NOEXIST);
+	}
+
+	/*
+	 * now parse the path info node.
+	 */
+	do {
+		/* check the length first. */
+		if (strlen(di_path_bus_addr(pi_node)) != strlen(dyn)) {
+			continue;
+		}
+
+		if (strcmp(di_path_bus_addr(pi_node), dyn) == 0) {
+			/* get the devfspath of pathinfo node. */
+			devpath = di_path_devfs_path(pi_node);
+			if (devpath == NULL) {
+				*l_errnop = errno;
+				di_fini(root);
+				return (SCFGA_ERR);
+			}
+
+			len = strlen(DEVICES_DIR) + strlen(devpath) + 1;
+			*pathpp = calloc(1, len);
+			if (*pathpp == NULL) {
+				*l_errnop = errno;
+				di_devfs_path_free(devpath);
+				di_fini(root);
+				return (SCFGA_ERR);
+			} else {
+				(void) snprintf(*pathpp, len, "%s%s",
+				    DEVICES_DIR, devpath);
+				di_devfs_path_free(devpath);
+				di_fini(root);
+				return (SCFGA_OK);
+			}
+		}
+		pi_node = di_path_next_client(walk_root, pi_node);
+	} while (pi_node != DI_PATH_NIL);
+
+	di_fini(root);
+	return (SCFGA_APID_NOEXIST);
+}
+
 static scfga_ret_t
 drv_dyn_to_devpath(
 	const char *hba_phys,
@@ -497,7 +629,7 @@
 		path = (char *)physpath;
 	} else {
 		match_minor = 1;
-		snprintf(pathbuf, MAXPATHLEN, "%s:%s", physpath,
+		(void) snprintf(pathbuf, MAXPATHLEN, "%s:%s", physpath,
 		    di_minor_name(minor));
 		path = pathbuf;
 	}
@@ -527,6 +659,36 @@
 	return (ret);
 }
 
+/*
+ * Create a dynamic component of path ap_id for the given path info node.
+ * The caller should free the buffer for the dynamic component.
+ */
+scfga_ret_t
+make_path_dyncomp(
+	di_path_t path,
+	char **dyncompp,
+	int *l_errnop)
+{
+	char *pi_addr;
+
+	if ((path == DI_PATH_NIL) || (*dyncompp != NULL)) {
+		return (SCFGA_LIB_ERR);
+	}
+
+	if ((pi_addr = di_path_bus_addr(path)) != NULL) {
+		*dyncompp = calloc(1, strlen(pi_addr) + 1);
+		if (*dyncompp == NULL) {
+			*l_errnop = errno;
+			return (SCFGA_LIB_ERR);
+		}
+		(void) strncpy(*dyncompp, pi_addr, strlen(pi_addr));
+	} else {
+		return (SCFGA_LIB_ERR);
+	}
+
+	return (SCFGA_OK);
+}
+
 /*ARGSUSED*/
 static scfga_ret_t
 drv_to_dyncomp(di_node_t node, const char *phys, char **dyncompp, int *l_errnop)
@@ -723,7 +885,8 @@
 	cp = strrchr(dyntp->dyncomp, '/');
 
 	/* Remove the mode part */
-	while (isdigit(*(++cp)));
+	while (isdigit(*(++cp))) {
+	};
 	*cp = '\0';
 
 
--- a/usr/src/lib/cfgadm_plugins/scsi/common/cfga_list.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/cfgadm_plugins/scsi/common/cfga_list.c	Mon Oct 05 09:17:26 2009 -0400
@@ -41,6 +41,7 @@
 	uint_t itype;
 	const char *ntype;
 	const char *name;
+	const char *pathname;
 } scfga_devtype_t;
 
 /* The TYPE field is parseable and should not contain spaces */
@@ -57,28 +58,30 @@
     scfga_list_t *lap, int limited_dev_stat);
 static cfga_stat_t bus_devinfo_to_recep_state(uint_t bus_di_state);
 static cfga_stat_t dev_devinfo_to_occupant_state(uint_t dev_di_state);
-static char *get_device_type(di_node_t);
-static void get_hw_info(di_node_t node, cfga_list_data_t *clp);
+static char *get_device_type(di_node_t, dyncomp_t);
+static void get_hw_info(di_node_t node, cfga_list_data_t *clp, dyncomp_t type);
+static scfga_ret_t create_pathinfo_ldata(di_path_t pi_node, scfga_list_t *lap,
+    int *l_errnop);
 
 
 static scfga_devtype_t device_list[] = {
-	{ DTYPE_DIRECT,		DDI_NT_BLOCK_CHAN,	"disk"},
-	{ DTYPE_DIRECT,		DDI_NT_BLOCK,		"disk"},
-	{ DTYPE_DIRECT,		DDI_NT_BLOCK_WWN,	"disk"},
-	{ DTYPE_DIRECT,		DDI_NT_BLOCK_FABRIC,	"disk"},
-	{ DTYPE_DIRECT,		DDI_NT_BLOCK_SAS,	"disk"},
-	{ DTYPE_SEQUENTIAL,	DDI_NT_TAPE,		"tape"},
-	{ DTYPE_PRINTER,	NULL,			"printer"},
-	{ DTYPE_PROCESSOR,	NULL,			"processor"},
-	{ DTYPE_WORM,		NULL,			"WORM"},
-	{ DTYPE_RODIRECT,	DDI_NT_CD_CHAN,		"CD-ROM"},
-	{ DTYPE_RODIRECT,	DDI_NT_CD,		"CD-ROM"},
-	{ DTYPE_SCANNER,	NULL,			"scanner"},
-	{ DTYPE_OPTICAL,	NULL,			"optical"},
-	{ DTYPE_CHANGER,	NULL,			"med-changer"},
-	{ DTYPE_COMM,		NULL,			"comm-device"},
-	{ DTYPE_ARRAY_CTRL,	NULL,			"array-ctrl"},
-	{ DTYPE_ESI,		NULL,			"ESI"}
+	{ DTYPE_DIRECT,	    DDI_NT_BLOCK_CHAN,	"disk",		"disk-path"},
+	{ DTYPE_DIRECT,	    DDI_NT_BLOCK,	"disk",		"disk-path"},
+	{ DTYPE_DIRECT,	    DDI_NT_BLOCK_WWN,	"disk",		"disk-path"},
+	{ DTYPE_DIRECT,	    DDI_NT_BLOCK_FABRIC,    "disk",	"disk-path"},
+	{ DTYPE_DIRECT,	    DDI_NT_BLOCK_SAS,   "disk",		"disk-path"},
+	{ DTYPE_SEQUENTIAL, DDI_NT_TAPE,	"tape",		"tape-path"},
+	{ DTYPE_PRINTER,    NULL,		"printer",	"printer-path"},
+	{ DTYPE_PROCESSOR,  NULL,		"processor",	"PRCS-path"},
+	{ DTYPE_WORM,	    NULL,		"WORM",		"WORM-path"},
+	{ DTYPE_RODIRECT,   DDI_NT_CD_CHAN,	"CD-ROM",	"CD-ROM-path"},
+	{ DTYPE_RODIRECT,   DDI_NT_CD,		"CD-ROM",	"CD-ROM-path"},
+	{ DTYPE_SCANNER,    NULL,		"scanner",	"scanner-path"},
+	{ DTYPE_OPTICAL,    NULL,		"optical",	"optical-path"},
+	{ DTYPE_CHANGER,    NULL,		"med-changer",	"MEDCHGR-path"},
+	{ DTYPE_COMM,	    NULL,		"comm-device",	"COMDEV-path"},
+	{ DTYPE_ARRAY_CTRL, NULL,		"array-ctrl",	"ARRCTRL-path"},
+	{ DTYPE_ESI,	    NULL,		"ESI",		"ESI-path"}
 };
 
 #define	N_DEVICE_TYPES	(sizeof (device_list) / sizeof (device_list[0]))
@@ -182,15 +185,42 @@
 	}
 
 	/* we need to stat at least 1 device for all commands */
-	u.node_args.flags = DI_WALK_CLDFIRST;
-	u.node_args.fcn = stat_dev;
+	if (apidp->dyntype == PATH_APID) {
+		/*
+		 * When cmd is SCFGA_STAT_DEV and the ap id is pathinfo
+		 * related.
+		 */
+		ret = walk_tree(apidp->hba_phys, &larg, init_flag, NULL,
+		    SCFGA_WALK_PATH, &larg.l_errno);
+	} else {
+		/* we need to stat at least 1 device for all commands */
+		u.node_args.flags = DI_WALK_CLDFIRST;
+		u.node_args.fcn = stat_dev;
 
-	/*
-	 * Subtree is ALWAYS rooted at the HBA (not at the device) as
-	 * otherwise deadlock may occur if bus is disconnected.
-	 */
-	ret = walk_tree(apidp->hba_phys, &larg, init_flag, &u,
-	    SCFGA_WALK_NODE, &larg.l_errno);
+		/*
+		 * Subtree is ALWAYS rooted at the HBA (not at the device) as
+		 * otherwise deadlock may occur if bus is disconnected.
+		 */
+		ret = walk_tree(apidp->hba_phys, &larg, init_flag, &u,
+		    SCFGA_WALK_NODE, &larg.l_errno);
+
+		/*
+		 * Check path info on the following conditions.
+		 *
+		 * - chld_config is still set to CFGA_STAT_UNCONFIGURED for
+		 *   SCFGA_STAT_BUS cmd after walking any child node.
+		 * - walking node succeeded for SCFGA_STAT_ALL cmd(Continue on
+		 *   stating path info node).
+		 * - apid is pathinfo associated and larg.ret is still set to
+		 *   SCFGA_APID_NOEXIST for SCFGA_STAT_DEV cmd.
+		 */
+		if (((cmd == SCFGA_STAT_BUS) &&
+		    (larg.chld_config == CFGA_STAT_UNCONFIGURED)) ||
+		    ((cmd == SCFGA_STAT_ALL) && (ret == SCFGA_OK))) {
+			ret = walk_tree(apidp->hba_phys, &larg, init_flag, NULL,
+			    SCFGA_WALK_PATH, &larg.l_errno);
+		}
+	}
 
 	if (ret != SCFGA_OK || (ret = larg.ret) != SCFGA_OK) {
 		if (ret != SCFGA_APID_NOEXIST) {
@@ -380,10 +410,189 @@
 	return (rv);
 }
 
+/*
+ * Create list date entry and add to ldata list.
+ */
+static scfga_ret_t
+create_pathinfo_ldata(di_path_t pi_node, scfga_list_t *lap, int *l_errnop)
+{
+	ldata_list_t	*listp = NULL;
+	cfga_list_data_t	*clp;
+	di_node_t	client_node = DI_NODE_NIL;
+	di_minor_t	minor;
+	scfga_ret_t 	ret;
+	di_path_state_t	pi_state;
+	char		*dyncomp = NULL, *client_path = NULL;
+	char		pathbuf[MAXPATHLEN], *client_devlink = NULL;
+	int		match_minor;
+
+	listp = calloc(1, sizeof (ldata_list_t));
+	if (listp == NULL) {
+		lap->l_errno = errno;
+		return (SCFGA_LIB_ERR);
+	}
+	clp = &listp->ldata;
+	ret = make_path_dyncomp(pi_node, &dyncomp, &lap->l_errno);
+	if (ret != SCFGA_OK) {
+		S_FREE(listp);
+		return (ret);
+	}
+
+	client_node = di_path_client_node(pi_node);
+	if (client_node == DI_NODE_NIL) {
+		*l_errnop = errno;
+		S_FREE(dyncomp);
+		return (SCFGA_LIB_ERR);
+	}
+
+	/* Create logical and physical ap_id */
+	(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
+	    lap->hba_logp, DYN_SEP, dyncomp);
+
+	(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
+	    lap->apidp->hba_phys, DYN_SEP, dyncomp);
+
+	S_FREE(dyncomp);
+
+	/* ap class filled in by libcfgadm */
+	clp->ap_class[0] = '\0';
+	clp->ap_r_state = lap->hba_rstate;
+	/* path info exist so set to configured. */
+	clp->ap_o_state = CFGA_STAT_CONFIGURED;
+
+	/* now fill up ap_info field with client dev link and instance #. */
+	client_path = di_devfs_path(client_node);
+	if (client_path) {
+		/* get first minor node. */
+		minor = di_minor_next(client_node, DI_MINOR_NIL);
+		if (minor == DI_MINOR_NIL) {
+			match_minor = 0;
+			(void) snprintf(pathbuf, MAXPATHLEN, "%s:%s",
+			    DEVICES_DIR, client_path);
+		} else {
+			match_minor = 1;
+			(void) snprintf(pathbuf, MAXPATHLEN, "%s%s:%s",
+			    DEVICES_DIR, client_path, di_minor_name(minor));
+		}
+		(void) physpath_to_devlink(pathbuf, &client_devlink, l_errnop,
+		    match_minor);
+		di_devfs_path_free(client_path);
+	}
+
+	if (client_devlink) {
+		(void) snprintf(clp->ap_info, CFGA_INFO_LEN,
+		    "%s: %s", "Client Device", client_devlink);
+		S_FREE(client_devlink);
+	}
+
+	get_hw_info(client_node, clp, PATH_APID);
+
+	if ((pi_state = di_path_state(pi_node)) == DI_PATH_STATE_OFFLINE) {
+		clp->ap_o_state = CFGA_STAT_UNCONFIGURED;
+	}
+
+	if (pi_state == DI_PATH_STATE_FAULT) {
+		clp->ap_cond = CFGA_COND_FAILED;
+	} else {
+		clp->ap_cond = CFGA_COND_UNKNOWN;
+	}
+
+	/* no way to determine state change */
+	clp->ap_busy = 0;
+	clp->ap_status_time = (time_t)-1;
+
+	/* Link it in */
+	listp->next = lap->listp;
+	lap->listp = listp;
+
+	return (SCFGA_OK);
+}
+
+/*
+ * Routine to stat pathinfo nodes.
+ *
+ * No pathinfo founds returns a success.
+ * When cmd is SCFGA_STAT_DEV, finds a matching pathinfo node and
+ * and create ldata if found.
+ * When cmd is SCFGA_STAT_ALL, create ldata for each pathinfo node.
+ * When cmd is SCFGA_STAT_BUS, checks if any pathinfo exist.
+ *
+ * Return:
+ *  0 for success
+ *  -1 for failure.
+ */
+int
+stat_path_info(
+	di_node_t 	root,
+	void		*arg,
+	int 		*l_errnop)
+{
+	scfga_list_t	*lap = (scfga_list_t *)arg;
+	di_path_t	pi_node;
+
+	if (root == DI_NODE_NIL) {
+		return (-1);
+	}
+
+	/*
+	 * when there is no path_info node return SCFGA_OK.
+	 */
+	if (di_path_next_client(root, DI_PATH_NIL) == DI_PATH_NIL) {
+		return (0);
+	}
+
+	if (lap->cmd == SCFGA_STAT_BUS) {
+		lap->chld_config = CFGA_STAT_CONFIGURED;
+		return (0);
+	} else if (lap->cmd == SCFGA_STAT_DEV) {
+		assert(lap->apidp->dyntype == PATH_APID);
+		for (pi_node = di_path_next_client(root, DI_PATH_NIL); pi_node;
+		    pi_node = di_path_next_client(root, pi_node)) {
+			/*
+			 * NOTE: apidt_create() validated pathinfo apid so
+			 * the apid should have a valid format.
+			 */
+
+			/* check the length first. */
+			if (strlen(di_path_bus_addr(pi_node)) !=
+			    strlen(lap->apidp->dyncomp)) {
+				continue;
+			}
+
+			/* check for full match. */
+			if (strcmp(di_path_bus_addr(pi_node),
+			    lap->apidp->dyncomp)) {
+				continue;
+			}
+
+			/* found match, record information */
+			if (create_pathinfo_ldata(pi_node, lap,
+			    l_errnop) == SCFGA_OK) {
+				lap->ret = SCFGA_OK;
+				return (0);
+			} else {
+				return (-1);
+			}
+		}
+	} else { /* cmd = STAT_ALL */
+		/* set child config to configured */
+		lap->chld_config = CFGA_STAT_CONFIGURED;
+		for (pi_node = di_path_next_client(root, DI_PATH_NIL); pi_node;
+		    pi_node = di_path_next_client(root, pi_node)) {
+			/* continue on even if there is an error on one path. */
+			(void) create_pathinfo_ldata(pi_node, lap, l_errnop);
+		}
+	}
+
+	lap->ret = SCFGA_OK;
+	return (0);
+
+}
 
 struct bus_state {
 	int	b_state;
 	int	b_retired;
+	char	iconnect_type[16];
 };
 
 static scfga_ret_t
@@ -395,6 +604,8 @@
 	struct bus_state bstate = {0};
 	walkarg_t u;
 	scfga_ret_t ret;
+	int i;
+	char itypelower[MAXNAMELEN];
 
 	assert(lap->hba_logp != NULL);
 
@@ -437,8 +648,24 @@
 	clp->ap_status_time = (time_t)-1;
 	clp->ap_info[0] = '\0';
 
-	(void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
-	    SCFGA_BUS_TYPE);
+	if (bstate.iconnect_type) {
+		/*
+		 * For SPI type, keep the existing SCFGA_BUS_TYPE.
+		 * For other types, the ap type will be scsi-'interconnct-type'.
+		 */
+		if (strcmp(bstate.iconnect_type, "SPI") == 0) {
+			(void) snprintf(clp->ap_type, sizeof (clp->ap_type),
+			    "%s", SCFGA_BUS_TYPE);
+		} else {
+			for (i = 0; i < strlen(bstate.iconnect_type); i++) {
+				itypelower[i] =
+				    tolower(bstate.iconnect_type[i]);
+			}
+			itypelower[i] = '\0';
+			(void) snprintf(clp->ap_type, sizeof (clp->ap_type),
+			    "%s-%s", "scsi", itypelower);
+		}
+	}
 
 	/* Link it in */
 	listp->next = lap->listp;
@@ -451,9 +678,17 @@
 get_bus_state(di_node_t node, void *arg)
 {
 	struct bus_state *bsp = (struct bus_state *)arg;
+	char *itype = NULL;
 
 	bsp->b_state = di_state(node);
 	bsp->b_retired = di_retired(node);
+	(void) di_prop_lookup_strings(DDI_DEV_T_ANY,
+	    node, "initiator-interconnect-type", &itype);
+	if (itype != NULL) {
+		(void) strlcpy(bsp->iconnect_type, itype, 16);
+	} else {
+		bsp->iconnect_type[0] = '\0';
+	}
 
 	return (DI_WALK_TERMINATE);
 }
@@ -521,7 +756,7 @@
 	clp->ap_busy = 0; /* no way to determine state change */
 	clp->ap_status_time = (time_t)-1;
 
-	get_hw_info(node, clp);
+	get_hw_info(node, clp, DEV_APID);
 
 	/* Link it in */
 	listp->next = lap->listp;
@@ -532,29 +767,50 @@
 
 /* fill in device type, vid, pid from properties */
 static void
-get_hw_info(di_node_t node, cfga_list_data_t *clp)
+get_hw_info(di_node_t node, cfga_list_data_t *clp, dyncomp_t type)
 {
 	char *cp = NULL;
 	char *inq_vid, *inq_pid;
+	char client_inst[MAXNAMELEN];
 
 	/*
 	 * Fill in type information
 	 */
-	cp = (char *)get_device_type(node);
+	cp = (char *)get_device_type(node, type);
 	if (cp == NULL) {
 		cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE);
 	}
 	(void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s", S_STR(cp));
 
-	/*
-	 * Fill in vendor and product ID.
-	 */
-	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
-	    "inquiry-product-id", &inq_pid) == 1) &&
-	    (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
-	    "inquiry-vendor-id", &inq_vid) == 1)) {
-		(void) snprintf(clp->ap_info, sizeof (clp->ap_info),
-		    "%s %s", inq_vid, inq_pid);
+	if (type == DEV_APID) {
+		/*
+		 * Fill in vendor and product ID.
+		 */
+		if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
+		    "inquiry-product-id", &inq_pid) == 1) &&
+		    (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
+		    "inquiry-vendor-id", &inq_vid) == 1)) {
+			(void) snprintf(clp->ap_info, sizeof (clp->ap_info),
+			    "%s %s", inq_vid, inq_pid);
+		}
+	} else {
+		if ((di_driver_name(node) != NULL) &&
+		    (di_instance(node) != -1)) {
+			if (clp->ap_info == NULL) {
+				(void) snprintf(client_inst, MAXNAMELEN - 1,
+				    "%s%d", di_driver_name(node),
+				    di_instance(node));
+				(void) snprintf(clp->ap_info, MAXNAMELEN - 1,
+				    "Client Device: %s", client_inst);
+			} else {
+				(void) snprintf(client_inst, MAXNAMELEN - 1,
+				    "(%s%d)", di_driver_name(node),
+				    di_instance(node));
+				(void) strlcat(clp->ap_info, client_inst,
+				    CFGA_INFO_LEN);
+			}
+		}
+
 	}
 }
 
@@ -563,7 +819,7 @@
  * derive it from minor node type
  */
 static char *
-get_device_type(di_node_t node)
+get_device_type(di_node_t node, dyncomp_t type)
 {
 	char *name = NULL;
 	int *inq_dtype;
@@ -582,7 +838,9 @@
 			if (device_list[i].itype == DTYPE_UNKNOWN)
 				continue;
 			if (itype == device_list[i].itype) {
-				name = (char *)device_list[i].name;
+				name = (type == DEV_APID) ?
+				    (char *)device_list[i].name :
+				    (char *)device_list[i].pathname;
 				break;
 			}
 		}
@@ -599,7 +857,9 @@
 				if (device_list[i].ntype &&
 				    (strcmp(nodetype, device_list[i].ntype)
 				    == 0)) {
-					name = (char *)device_list[i].name;
+					name = (type == DEV_APID) ?
+					    (char *)device_list[i].name :
+					    (char *)device_list[i].pathname;
 					break;
 				}
 			}
@@ -665,23 +925,10 @@
 static cfga_stat_t
 bus_devinfo_to_recep_state(uint_t bus_di_state)
 {
-	cfga_stat_t rs;
+	if (bus_di_state & (DI_BUS_QUIESCED | DI_BUS_DOWN))
+		return (CFGA_STAT_DISCONNECTED);
 
-	switch (bus_di_state) {
-	case DI_BUS_QUIESCED:
-	case DI_BUS_DOWN:
-		rs = CFGA_STAT_DISCONNECTED;
-		break;
-	/*
-	 * NOTE: An explicit flag for active should probably be added to
-	 * libdevinfo.
-	 */
-	default:
-		rs = CFGA_STAT_CONNECTED;
-		break;
-	}
-
-	return (rs);
+	return (CFGA_STAT_CONNECTED);
 }
 
 /*
@@ -690,15 +937,11 @@
 static cfga_stat_t
 dev_devinfo_to_occupant_state(uint_t dev_di_state)
 {
-	/* Driver attached ? */
-	if ((dev_di_state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
-		return (CFGA_STAT_CONFIGURED);
-	}
+	if (dev_di_state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN))
+		return (CFGA_STAT_UNCONFIGURED);
 
-	if ((dev_di_state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
-	    (dev_di_state & DI_DEVICE_DOWN) == DI_DEVICE_DOWN) {
-		return (CFGA_STAT_UNCONFIGURED);
-	} else {
-		return (CFGA_STAT_NONE);
-	}
+	if (!(dev_di_state & DI_DRIVER_DETACHED))
+		return (CFGA_STAT_CONFIGURED);
+
+	return (CFGA_STAT_NONE);
 }
--- a/usr/src/lib/cfgadm_plugins/scsi/common/cfga_rcm.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/cfgadm_plugins/scsi/common/cfga_rcm.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include "cfga_scsi.h"
 
 #define	MAX_FORMAT	80
@@ -59,7 +56,11 @@
 
 	if ((rret = rcm_request_offline_list(rcm_handle, rsrclist, rflags,
 	    &rinfo)) != RCM_SUCCESS) {
-		cfga_err(errstring, 0, ERRARG_RCM_OFFLINE, 0);
+		if ((flags & FLAG_CLIENT_DEV) == FLAG_CLIENT_DEV) {
+			cfga_err(errstring, 0, ERRARG_RCM_CLIENT_OFFLINE, 0);
+		} else {
+			cfga_err(errstring, 0, ERRARG_RCM_OFFLINE, 0);
+		}
 		if (rinfo) {
 			(void) scsi_rcm_info_table(rinfo, errstring);
 			rcm_free_info(rinfo);
@@ -69,7 +70,7 @@
 			    rflags & ~RCM_FORCE, NULL);
 		ret = SCFGA_BUSY;
 	}
-	rcm_free_handle(rcm_handle);
+	(void) rcm_free_handle(rcm_handle);
 	return (ret);
 }
 
@@ -102,7 +103,7 @@
 		}
 		ret = SCFGA_BUSY;
 	}
-	rcm_free_handle(rcm_handle);
+	(void) rcm_free_handle(rcm_handle);
 	return (ret);
 }
 
@@ -136,7 +137,7 @@
 		ret = SCFGA_BUSY;
 	}
 
-	rcm_free_handle(rcm_handle);
+	(void) rcm_free_handle(rcm_handle);
 	return (ret);
 }
 
@@ -182,7 +183,7 @@
 			    (rflags & (~RCM_FORCE)), NULL);
 		ret = SCFGA_BUSY;
 	}
-	rcm_free_handle(rcm_handle);
+	(void) rcm_free_handle(rcm_handle);
 	return (ret);
 }
 
@@ -220,7 +221,7 @@
 		}
 		ret = SCFGA_BUSY;
 	}
-	rcm_free_handle(rcm_handle);
+	(void) rcm_free_handle(rcm_handle);
 	return (ret);
 }
 
--- a/usr/src/lib/cfgadm_plugins/scsi/common/cfga_scsi.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/cfgadm_plugins/scsi/common/cfga_scsi.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,13 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-
 #include "cfga_scsi.h"
 
 /*
@@ -142,6 +138,10 @@
 		return (err_cvt(ret));
 	}
 
+	if (apidt.dyntype == PATH_APID) {
+		return (CFGA_OPNOTSUPP);
+	}
+
 	if (options != NULL)
 		apidt.flags |= FLAG_DISABLE_RCM;
 
--- a/usr/src/lib/cfgadm_plugins/scsi/common/cfga_scsi.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/cfgadm_plugins/scsi/common/cfga_scsi.h	Mon Oct 05 09:17:26 2009 -0400
@@ -20,15 +20,13 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _CFGA_SCSI_H
 #define	_CFGA_SCSI_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -118,6 +116,7 @@
 	SCFGA_REPLACE_DEV,
 	SCFGA_WALK_NODE,
 	SCFGA_WALK_MINOR,
+	SCFGA_WALK_PATH,
 	SCFGA_BUS_QUIESCE,
 	SCFGA_BUS_UNQUIESCE,
 	SCFGA_BUS_GETSTATE,
@@ -141,6 +140,12 @@
 	SCFGA_CONTINUE
 } scfga_recur_t;
 
+typedef enum {
+	NODYNCOMP = 1,
+	DEV_APID,
+	PATH_APID
+} dyncomp_t;
+
 
 /* Structures for tree walking code */
 
@@ -180,7 +185,8 @@
 typedef struct {
 	char		*hba_phys;
 	char		*dyncomp;
-	char		*path;
+	dyncomp_t	dyntype;    /* is pathinfo or dev apid? */
+	char		*path;	    /* for apid with device dyn comp. */
 	uint_t		flags;
 } apid_t;
 
@@ -192,6 +198,9 @@
 #define	FLAG_DISABLE_RCM	0x01
 #define	FLAG_USE_DIFORCE	0x02
 
+/* internal use for handling pathinfo */
+#define	FLAG_CLIENT_DEV		0x04
+
 /* Message ids */
 typedef enum {
 
@@ -239,6 +248,7 @@
 ERRARG_RCM_SUSPEND,
 ERRARG_RCM_RESUME,
 ERRARG_RCM_OFFLINE,
+ERRARG_RCM_CLIENT_OFFLINE,
 ERRARG_RCM_ONLINE,
 ERRARG_RCM_REMOVE,
 
@@ -323,6 +333,7 @@
 
 #define	DYN_SEP			"::"
 #define	MINOR_SEP		":"
+#define	PATH_APID_DYN_SEP	","
 
 #define	S_FREE(x)	(((x) != NULL) ? (free(x), (x) = NULL) : (void *)0)
 #define	S_STR(x)	(((x) == NULL) ? "" : (x))
@@ -376,6 +387,7 @@
     ldata_list_t **llpp, int *nelem, char **errstring);
 scfga_ret_t list_ext_postprocess(ldata_list_t **llpp, int nelem,
     cfga_list_data_t **ap_id_list, int *nlistp, char **errstring);
+int stat_path_info(di_node_t root, void *arg, int *l_errnop);
 
 
 /* Conversion routines */
@@ -385,6 +397,7 @@
     char **pathpp, int *l_errnop);
 scfga_ret_t make_dyncomp(di_node_t node, const char *physpath,
     char **dyncompp, int *l_errnop);
+scfga_ret_t make_path_dyncomp(di_path_t path, char **dyncomp, int *l_errnop);
 
 
 /* RCM routines */
@@ -411,6 +424,8 @@
 int known_state(di_node_t node);
 scfga_ret_t devctl_cmd(const char *ap_id, scfga_cmd_t cmd,
     uint_t *statep, int *l_errnop);
+scfga_ret_t path_apid_state_change(apid_t *apidp, scfga_cmd_t cmd,
+    cfga_flags_t flags, char **errstring, int *l_errnop, msgid_t errid);
 scfga_ret_t invoke_cmd(const char *func, apid_t *apidt, prompt_t *prp,
     cfga_flags_t flags, char **errstring);
 
--- a/usr/src/lib/cfgadm_plugins/scsi/common/cfga_utils.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/cfgadm_plugins/scsi/common/cfga_utils.c	Mon Oct 05 09:17:26 2009 -0400
@@ -20,12 +20,10 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include "cfga_scsi.h"
 #include <libgen.h>
 #include <limits.h>
@@ -125,6 +123,7 @@
 {ERRARG_RCM_SUSPEND,	0, 1,	"failed to suspend: "},
 {ERRARG_RCM_RESUME,	0, 1,	"failed to resume: "},
 {ERRARG_RCM_OFFLINE,	0, 1,	"failed to offline: "},
+{ERRARG_RCM_CLIENT_OFFLINE,	0, 1,	"failed to offline a client device: "},
 {ERRARG_RCM_ONLINE,	0, 1,	"failed to online: "},
 {ERRARG_RCM_REMOVE,	0, 1,	"failed to remove: "},
 
@@ -327,6 +326,7 @@
 	return (dup);
 }
 
+
 scfga_ret_t
 apidt_create(const char *ap_id, apid_t *apidp, char **errstring)
 {
@@ -355,11 +355,22 @@
 
 		/* Remove the dynamic component from the base */
 		*dyn = '\0';
+	} else {
+		apidp->dyntype = NODYNCOMP;
+	}
+
+	/* get dyn comp type */
+	if (dyncomp != NULL) {
+		if (strstr(dyncomp, PATH_APID_DYN_SEP) != NULL) {
+			apidp->dyntype = PATH_APID;
+		} else {
+			apidp->dyntype = DEV_APID;
+		}
 	}
 
 	/* Create the path */
-	if ((ret = apid_to_path(hba_phys, dyncomp, &path, &l_errno))
-	    != SCFGA_OK) {
+	if ((ret = apid_to_path(hba_phys, dyncomp, &path,
+	    &l_errno)) != SCFGA_OK) {
 		cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
 		goto err;
 	}
@@ -471,6 +482,8 @@
 	if (cmd == SCFGA_WALK_NODE) {
 		rv = di_walk_node(walk_root, up->node_args.flags, arg,
 		    up->node_args.fcn);
+	} else if (cmd == SCFGA_WALK_PATH) {
+		rv = stat_path_info(walk_root, arg, l_errnop);
 	} else {
 		assert(cmd == SCFGA_WALK_MINOR);
 		rv = di_walk_minor(walk_root, up->minor_args.nodetype, 0, arg,
@@ -509,7 +522,8 @@
 	 * Determine if the func has an equal sign; only compare up to
 	 * the equals
 	 */
-	for (len = 0; func[len] != 0 && func[len] != '='; len++);
+	for (len = 0; func[len] != 0 && func[len] != '='; len++) {
+	};
 
 	for (i = 0; i < N_HW_CMDS; i++) {
 		const char *s = GET_MSG_STR(hw_cmds[i].str_id);
@@ -617,10 +631,10 @@
 	if ((apidp == NULL) || (apidp->dyncomp == NULL)) {
 		return;
 	}
-	snprintf(led_msg, sizeof (led_msg), "%-23s\t%s=%s\n",
-			basename(apidp->dyncomp),
-			dgettext(TEXT_DOMAIN, led_strs[led]),
-			dgettext(TEXT_DOMAIN, led_mode_strs[mode]));
+	(void) snprintf(led_msg, sizeof (led_msg), "%-23s\t%s=%s\n",
+	    basename(apidp->dyncomp),
+	    dgettext(TEXT_DOMAIN, led_strs[led]),
+	    dgettext(TEXT_DOMAIN, led_mode_strs[mode]));
 	(void) (*msgp->message_routine)(msgp->appdata_ptr, led_msg);
 }
 
@@ -722,6 +736,211 @@
 	}
 }
 
+/*
+ * Check to see if the given pi_node is the last path to the client device.
+ *
+ * Return:
+ *	0: if there is another path avialable.
+ *	-1: if no other paths available.
+ */
+static int
+check_available_path(
+	di_node_t client_node,
+	di_path_t pi_node)
+{
+	di_path_state_t pi_state;
+	di_path_t   next_pi = DI_PATH_NIL;
+
+	if (((pi_state = di_path_state(pi_node)) != DI_PATH_STATE_ONLINE) &&
+	    (pi_state != DI_PATH_STATE_STANDBY)) {
+		/* it is not last available path */
+		return (0);
+	}
+
+	while (next_pi = di_path_client_next_path(client_node, next_pi)) {
+		/* if anohter pi node is avaialble, return 0 */
+		if ((next_pi != pi_node) &&
+		    (((pi_state = di_path_state(next_pi)) ==
+		    DI_PATH_STATE_ONLINE) ||
+		    pi_state == DI_PATH_STATE_STANDBY)) {
+			return (0);
+		}
+	}
+	return (-1);
+}
+
+scfga_ret_t
+path_apid_state_change(
+	apid_t		*apidp,
+	scfga_cmd_t	cmd,
+	cfga_flags_t	flags,
+	char		**errstring,
+	int		*l_errnop,
+	msgid_t		errid)
+{
+	di_node_t   root, walk_root, client_node;
+	di_path_t   pi_node = DI_PATH_NIL;
+	char	    *root_path, *cp, *client_path, devpath[MAXPATHLEN];
+	int	    len, found = 0;
+	scfga_ret_t ret;
+	char *dev_list[2] = {NULL};
+
+	*l_errnop = 0;
+
+	/* Make sure apid is pathinfo associated apid. */
+	if ((apidp->dyntype != PATH_APID) || (apidp->dyncomp == NULL)) {
+		return (SCFGA_LIB_ERR);
+	}
+
+	if ((cmd != SCFGA_DEV_CONFIGURE) && (cmd != SCFGA_DEV_UNCONFIGURE)) {
+		return (SCFGA_LIB_ERR);
+	}
+
+	if ((root_path = strdup(apidp->hba_phys)) == NULL) {
+		*l_errnop = errno;
+		return (SCFGA_LIB_ERR);
+	}
+
+	/* Fix up path for di_init() */
+	len = strlen(DEVICES_DIR);
+	if (strncmp(root_path, DEVICES_DIR SLASH,
+	    len + strlen(SLASH)) == 0) {
+		cp = root_path + len;
+		(void) memmove(root_path, cp, strlen(cp) + 1);
+	} else if (*root_path != '/') {
+		*l_errnop = 0;
+		S_FREE(root_path);
+		return (SCFGA_ERR);
+	}
+
+	/* Remove dynamic component if any */
+	if ((cp = GET_DYN(root_path)) != NULL) {
+		*cp = '\0';
+	}
+
+	/* Remove minor name if any */
+	if ((cp = strrchr(root_path, ':')) != NULL) {
+		*cp = '\0';
+	}
+
+	/*
+	 * Cached snapshots are always rooted at "/"
+	 */
+
+	/* Get a snapshot */
+	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
+		*l_errnop = errno;
+		S_FREE(root_path);
+		return (SCFGA_ERR);
+	}
+
+	/*
+	 * Lookup the subtree of interest
+	 */
+	walk_root = di_lookup_node(root, root_path);
+
+	if (walk_root == DI_NODE_NIL) {
+		*l_errnop = errno;
+		di_fini(root);
+		S_FREE(root_path);
+		return (SCFGA_LIB_ERR);
+	}
+
+
+	if ((pi_node = di_path_next_client(walk_root, pi_node)) ==
+	    DI_PATH_NIL) {
+		/* the path apid not found */
+		di_fini(root);
+		S_FREE(root_path);
+		return (SCFGA_APID_NOEXIST);
+	}
+
+	do {
+		/* check the length first. */
+		if (strlen(di_path_bus_addr(pi_node)) !=
+		    strlen(apidp->dyncomp)) {
+			continue;
+		}
+
+		/* compare bus addr. */
+		if (strcmp(di_path_bus_addr(pi_node), apidp->dyncomp) == 0) {
+			found = 1;
+			break;
+		}
+		pi_node = di_path_next_client(root, pi_node);
+	} while (pi_node != DI_PATH_NIL);
+
+	if (!found) {
+		di_fini(root);
+		S_FREE(root_path);
+		return (SCFGA_APID_NOEXIST);
+	}
+
+	/* Get client node path. */
+	client_node = di_path_client_node(pi_node);
+	if (client_node == DI_NODE_NIL) {
+		di_fini(root);
+		S_FREE(root_path);
+		return (SCFGA_ERR);
+	} else {
+		client_path = di_devfs_path(client_node);
+		if (client_path == NULL) {
+			di_fini(root);
+			S_FREE(root_path);
+			return (SCFGA_ERR);
+		}
+
+		if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
+			if (cmd == SCFGA_DEV_UNCONFIGURE) {
+				if (check_available_path(client_node,
+				    pi_node) != 0) {
+					/*
+					 * last path. check if unconfiguring
+					 * is okay.
+					 */
+					(void) snprintf(devpath,
+					    strlen(DEVICES_DIR) +
+					    strlen(client_path) + 1, "%s%s",
+					    DEVICES_DIR, client_path);
+					dev_list[0] = devpath;
+					flags |= FLAG_CLIENT_DEV;
+					ret = scsi_rcm_offline(dev_list,
+					    errstring, flags);
+					if (ret != SCFGA_OK) {
+						di_fini(root);
+						di_devfs_path_free(client_path);
+						S_FREE(root_path);
+						return (ret);
+					}
+				}
+			}
+		}
+	}
+
+	ret = devctl_cmd(apidp->path, cmd, NULL, l_errnop);
+	if (ret != SCFGA_OK) {
+		cfga_err(errstring, *l_errnop, errid, 0);
+
+		/*
+		 * If an unconfigure fails, cancel the RCM offline.
+		 * Discard any RCM failures so that the devctl
+		 * failure will still be reported.
+		 */
+		if ((apidp->flags & FLAG_DISABLE_RCM) == 0) {
+			if (cmd == SCFGA_DEV_UNCONFIGURE)
+				(void) scsi_rcm_online(dev_list,
+				    errstring, flags);
+		}
+	}
+
+	di_devfs_path_free(client_path);
+	di_fini(root);
+	S_FREE(root_path);
+
+	return (ret);
+}
+
+
 scfga_ret_t
 devctl_cmd(
 	const char	*physpath,
@@ -840,7 +1059,7 @@
 	 * offline.
 	 */
 	if ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
-		(state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
+	    (state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
 		return (1);
 	}
 
--- a/usr/src/lib/fm/topo/modules/common/disk/disk_common.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/fm/topo/modules/common/disk/disk_common.c	Mon Oct 05 09:17:26 2009 -0400
@@ -613,7 +613,7 @@
 	pnode = NULL;
 	while ((pnode = di_path_client_next_path(node, pnode)) != NULL) {
 		if ((ret = di_path_prop_lookup_strings(pnode,
-		    "target-port", &s)) > 0)
+		    SCSI_ADDR_PROP_TARGET_PORT, &s)) > 0)
 			portcount += ret;
 		pathcount++;
 	}
@@ -629,16 +629,17 @@
 			goto error;
 
 		if ((ret = di_prop_lookup_strings(DDI_DEV_T_ANY, node,
-		    "target-port", &s)) > 0) {
+		    SCSI_ADDR_PROP_TARGET_PORT, &s)) > 0) {
 			if ((dnode->ddn_target_port = topo_mod_zalloc(mod,
 			    ret * sizeof (uintptr_t))) == NULL)
 				goto error;
-
 			dnode->ddn_target_port_count = ret;
 
 			for (i = 0; i < ret; i++) {
 				if ((dnode->ddn_target_port[i] =
-				    topo_mod_strdup(mod, s)) == NULL)
+				    topo_mod_strdup(mod,
+				    scsi_wwnstr_skip_ua_prefix(s))) ==
+				    NULL)
 					goto error;
 
 				s += strlen(s) + 1;
@@ -674,10 +675,12 @@
 				goto error;
 
 			if ((ret = di_path_prop_lookup_strings(pnode,
-			    "target-port", &s)) > 0) {
+			    SCSI_ADDR_PROP_TARGET_PORT, &s)) > 0) {
 				for (i = 0; i < ret; i++) {
 					if ((dnode->ddn_target_port[portcount] =
-					    topo_mod_strdup(mod, s)) == NULL)
+					    topo_mod_strdup(mod,
+					    scsi_wwnstr_skip_ua_prefix(s))) ==
+					    NULL)
 						goto error;
 
 					portcount++;
--- a/usr/src/lib/libadutils/Makefile.com	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libadutils/Makefile.com	Mon Oct 05 09:17:26 2009 -0400
@@ -25,18 +25,21 @@
 
 LIBRARY =	libadutils.a
 VERS =		.1
-OBJECTS =	adutils.o addisc.o
-LINT_OBJECTS =	adutils.o addisc.o
+OBJECTS =	adutils.o addisc.o adutils_threadfuncs.o
+LINT_OBJECTS =	adutils.o addisc.o adutils_threadfuncs.o
 
 include ../../Makefile.lib
 
+C99MODE=	-xc99=%all
+C99LMODE=	-Xc99=%all
+
 LIBS =		$(DYNLIB) $(LINTLIB)
 LDLIBS +=	-lc -lldap -lresolv -lsocket -lnsl
 SRCDIR =	../common
 $(LINTLIB):=	SRCS = $(SRCDIR)/$(LINTSRC)
 
 CFLAGS +=	$(CCVERBOSE)
-CPPFLAGS +=	-D_REENTRANT -I$(SRCDIR)
+CPPFLAGS +=	-D_REENTRANT -I$(SRCDIR) -I$(SRC)/lib/libldap5/include/ldap
 
 lint := OBJECTS = $(LINT_OBJECTS)
 
--- a/usr/src/lib/libadutils/common/addisc.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libadutils/common/addisc.c	Mon Oct 05 09:17:26 2009 -0400
@@ -834,7 +834,6 @@
 		ldversion = LDAP_VERSION3;
 		(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
 		    &ldversion);
-
 		(void) ldap_set_option(ld, LDAP_OPT_REFERRALS,
 		    LDAP_OPT_OFF);
 		(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
@@ -845,6 +844,14 @@
 		(void) ldap_set_option(ld, LDAP_OPT_RESTART,
 		    LDAP_OPT_ON);
 
+		rc = adutils_set_thread_functions(ld);
+		if (rc != LDAP_SUCCESS) {
+			/* Error has already been logged */
+			(void) ldap_unbind(ld);
+			ld = NULL;
+			continue;
+		}
+
 		rc = ldap_sasl_interactive_bind_s(ld, "" /* binddn */,
 		    saslmech, NULL, NULL, saslflags, &saslcallback,
 		    NULL /* defaults */);
--- a/usr/src/lib/libadutils/common/adutils.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libadutils/common/adutils.c	Mon Oct 05 09:17:26 2009 -0400
@@ -615,6 +615,15 @@
 	(void) ldap_set_option(adh->ld, LDAP_OPT_SIZELIMIT, &zero);
 	(void) ldap_set_option(adh->ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms);
 	(void) ldap_set_option(adh->ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
+
+	rc = adutils_set_thread_functions(adh->ld);
+	if (rc != LDAP_SUCCESS) {
+		/* Error has already been logged */
+		(void) ldap_unbind(adh->ld);
+		adh->ld = NULL;
+		goto out;
+	}
+
 	rc = ldap_sasl_interactive_bind_s(adh->ld, "" /* binddn */,
 	    adh->saslmech, NULL, NULL, adh->saslflags, &saslcallback,
 	    NULL);
--- a/usr/src/lib/libadutils/common/adutils_impl.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libadutils/common/adutils_impl.h	Mon Oct 05 09:17:26 2009 -0400
@@ -155,6 +155,8 @@
 
 int saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts);
 
+int adutils_set_thread_functions(LDAP *ld);
+
 /* Global logger function */
 
 extern adutils_logger logger;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libadutils/common/adutils_threadfuncs.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,276 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Functions for managing thread-local storage for LDAP, and in particular
+ * for managing storage of the LDAP error state.
+ */
+
+#include <ldap.h>
+#include <pthread.h>
+#include <errno.h>
+#include <note.h>
+#include <syslog.h>
+#include <string.h>
+#include "solaris-int.h"	/* This is a libladp5 private include file */
+				/* which has the defintion for */
+				/* struct ldap_extra_thread_fns */
+#include "adutils_impl.h"
+
+struct adutils_lderrno {
+	int le_errno;
+	char *le_matched;
+	char *le_errmsg;
+};
+
+static void *adutils_mutex_alloc(void);
+static void adutils_mutex_free(void *mutexp);
+static int adutils_get_errno(void);
+static void adutils_set_errno(int err);
+static void adutils_set_lderrno(int err, char *matched, char *errmsg,
+    void *dummy);
+static int adutils_get_lderrno(char **matched, char **errmsg, void *dummy);
+static void adutils_lderrno_destructor(void *tsd);
+
+static pthread_key_t adutils_lderrno_key = PTHREAD_ONCE_KEY_NP;
+
+static struct ldap_thread_fns thread_fns = {
+	.ltf_mutex_alloc = adutils_mutex_alloc,
+	.ltf_mutex_free = adutils_mutex_free,
+	.ltf_mutex_lock = (int (*)(void *)) pthread_mutex_lock,
+	.ltf_mutex_unlock = (int (*)(void *)) pthread_mutex_unlock,
+	.ltf_get_errno = adutils_get_errno,
+	.ltf_set_errno = adutils_set_errno,
+	.ltf_get_lderrno = adutils_get_lderrno,
+	.ltf_set_lderrno = adutils_set_lderrno,
+	.ltf_lderrno_arg = NULL
+};
+
+struct ldap_extra_thread_fns extra_thread_fns = {
+	.ltf_threadid_fn = (void * (*)(void))pthread_self
+};
+
+
+/*
+ * Set up thread management functions for the specified LDAP session.
+ * Returns either LDAP_SUCCESS or -1.
+ */
+int
+adutils_set_thread_functions(LDAP *ld)
+{
+	int rc;
+
+	if (adutils_lderrno_key == PTHREAD_ONCE_KEY_NP) {
+		if ((rc = pthread_key_create_once_np(&adutils_lderrno_key,
+		    adutils_lderrno_destructor)) != 0) {
+			logger(LOG_ERR, "adutils_set_thread_functions() "
+			    "pthread_key_create_once_np failed (%s)",
+			    strerror(rc));
+			rc = -1;
+			return (rc);
+		}
+	}
+
+	rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS,
+	    &thread_fns);
+	if (rc != LDAP_SUCCESS) {
+		logger(LOG_ERR,
+		    "ldap_set_option LDAP_OPT_THREAD_FN_PTRS failed");
+		return (rc);
+	}
+
+	rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
+	    &extra_thread_fns);
+	if (rc != LDAP_SUCCESS) {
+		logger(LOG_ERR,
+		    "ldap_set_option LDAP_OPT_EXTRA_THREAD_FN_PTRS failed");
+		return (rc);
+	}
+	return (rc);
+}
+
+/*
+ * Allocate a mutex.
+ */
+static
+void *
+adutils_mutex_alloc(void)
+{
+	pthread_mutex_t *mutexp;
+	int rc;
+
+	mutexp = malloc(sizeof (pthread_mutex_t));
+	if (mutexp == NULL) {
+		logger(LOG_ERR,
+		    "adutils_mutex_alloc: malloc failed (%s)",
+		    strerror(errno));
+		return (NULL);
+	}
+
+	rc = pthread_mutex_init(mutexp, NULL);
+	if (rc != 0) {
+		logger(LOG_ERR,
+		    "adutils_mutex_alloc: "
+		    "pthread_mutex_init failed (%s)",
+		    strerror(rc));
+		free(mutexp);
+		return (NULL);
+	}
+	return (mutexp);
+}
+
+/*
+ * Free a mutex.
+ */
+static
+void
+adutils_mutex_free(void *mutexp)
+{
+	(void) pthread_mutex_destroy((pthread_mutex_t *)mutexp);
+	free(mutexp);
+}
+
+/*
+ * Get the thread's local errno.
+ */
+static
+int
+adutils_get_errno(void)
+{
+	return (errno);
+}
+
+/*
+ * Set the thread's local errno.
+ */
+static
+void
+adutils_set_errno(int err)
+{
+	errno = err;
+}
+
+/*
+ * Get a pointer to the thread's local LDAP error state structure.
+ * Lazily allocate the thread-local storage, so that we don't need
+ * initialization when each thread starts.
+ */
+static
+struct adutils_lderrno *
+adutils_get_lderrno_struct(void)
+{
+	struct adutils_lderrno *le;
+	int rc;
+
+	le = pthread_getspecific(adutils_lderrno_key);
+	if (le == NULL) {
+		le = calloc(1, sizeof (*le));
+		if (le == NULL) {
+			logger(LOG_ERR,
+			    "adutils_get_lderrno_struct:  calloc failed (%s)",
+			    strerror(errno));
+			return (NULL);
+		}
+		rc = pthread_setspecific(adutils_lderrno_key, le);
+		if (rc != 0) {
+			logger(LOG_ERR,
+			    "adutils_get_lderrno_struct:  "
+			    "pthread_setspecific failed (%s)",
+			    strerror(rc));
+			free(le);
+			return (NULL);
+		}
+	}
+
+	return (le);
+}
+
+/*
+ * Store an error report in the thread's local LDAP error state structure.
+ */
+static
+void
+adutils_set_lderrno(int err, char *matched, char *errmsg, void *dummy)
+{
+	NOTE(ARGUNUSED(dummy))
+	struct adutils_lderrno *le;
+
+	le = adutils_get_lderrno_struct();
+	if (le != NULL) {
+		le->le_errno = err;
+		if (le->le_matched != NULL)
+			ldap_memfree(le->le_matched);
+		le->le_matched = matched;
+		if (le->le_errmsg != NULL)
+			ldap_memfree(le->le_errmsg);
+		le->le_errmsg = errmsg;
+	}
+}
+
+/*
+ * Retrieve an error report from the thread's local LDAP error state structure.
+ */
+static
+int
+adutils_get_lderrno(char **matched, char **errmsg, void *dummy)
+{
+	NOTE(ARGUNUSED(dummy))
+	struct adutils_lderrno *le;
+	static struct adutils_lderrno empty = { LDAP_SUCCESS, NULL, NULL };
+
+	le = adutils_get_lderrno_struct();
+	if (le == NULL)
+		le = &empty;
+
+	if (matched != NULL)
+		*matched = le->le_matched;
+	if (errmsg != NULL)
+		*errmsg = le->le_errmsg;
+	return (le->le_errno);
+}
+
+/*
+ * Free the thread's local LDAP error state structure.
+ */
+static
+void
+adutils_lderrno_destructor(void *tsd)
+{
+	struct adutils_lderrno *le = tsd;
+
+	if (le == NULL)
+		return;
+
+	if (le->le_matched != NULL) {
+		ldap_memfree(le->le_matched);
+		le->le_matched = NULL;
+	}
+	if (le->le_errmsg != NULL) {
+		ldap_memfree(le->le_errmsg);
+		le->le_errmsg = NULL;
+	}
+	free(le);
+}
--- a/usr/src/lib/libdevice/llib-ldevice	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libdevice/llib-ldevice	Mon Oct 05 09:17:26 2009 -0400
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -23,60 +22,10 @@
 /* PROTOLIB1 */
 
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-#include <sys/types.h>
-#include "libdevice.h"
-
-/*
- * usr/src/lib/libdevice
- */
 
-/* devctl.c */
-devctl_hdl_t devctl_device_acquire(char *devfs_path, uint_t flags);
-devctl_hdl_t devctl_bus_acquire(char *devfs_path, uint_t flags);
-void devctl_release(devctl_hdl_t hdl);
-int devctl_pm_raisepower(devctl_hdl_t hdl);
-int devctl_pm_changepowerlow(devctl_hdl_t hdl);
-int devctl_pm_changepowerhigh(devctl_hdl_t hdl);
-int devctl_pm_idlecomponent(devctl_hdl_t hdl);
-int devctl_pm_busycomponent(devctl_hdl_t hdl);
-int devctl_pm_testbusy(devctl_hdl_t hdl, uint_t *busyp);
-int devctl_pm_failsuspend(devctl_hdl_t hdl);
-int devctl_pm_bus_teststrict(devctl_hdl_t hdl, uint_t *strict);
-int devctl_pm_device_changeonresume(devctl_hdl_t hdl);
-int devctl_pm_device_no_lower_power(devctl_hdl_t hdl);
-int devctl_pm_bus_no_invol(devctl_hdl_t hdl);
-int devctl_pm_device_promprintf(devctl_hdl_t hdl);
-int devctl_device_offline(devctl_hdl_t hdl);
-int devctl_device_online(devctl_hdl_t hdl);
-int devctl_device_reset(devctl_hdl_t hdl);
-int devctl_device_getstate(devctl_hdl_t hdl, uint_t *statep);
-int devctl_bus_quiesce(devctl_hdl_t hdl);
-int devctl_bus_unquiesce(devctl_hdl_t hdl);
-int devctl_bus_reset(devctl_hdl_t hdl);
-int devctl_bus_resetall(devctl_hdl_t hdl);
-int devctl_bus_getstate(devctl_hdl_t hdl, uint_t *statep);
-devctl_hdl_t devctl_ap_acquire(char *devfs_path, uint_t flags);
-devctl_hdl_t devctl_pm_dev_acquire(char *devfs_path, uint_t flags);
-devctl_hdl_t devctl_pm_bus_acquire(char *devfs_path, uint_t flags);
-int devctl_ap_insert(devctl_hdl_t hdl, nvlist_t *ap_data);
-int devctl_ap_remove(devctl_hdl_t hdl, nvlist_t *ap_data);
-int devctl_ap_connect(devctl_hdl_t hdl, nvlist_t *ap_data);
-int devctl_ap_disconnect(devctl_hdl_t hdl, nvlist_t *ap_data);
-int devctl_ap_configure(devctl_hdl_t hdl, nvlist_t *ap_data);
-int devctl_ap_unconfigure(devctl_hdl_t hdl, nvlist_t *ap_data);
-int devctl_ap_getstate(devctl_hdl_t hdl, nvlist_t *ap_data,
-    devctl_ap_state_t *statep);
-devctl_ddef_t devctl_ddef_alloc(char *, int);
-void devctl_ddef_free(devctl_ddef_t);
-int devctl_ddef_int(devctl_ddef_t, char *, int32_t);
-int devctl_ddef_int_array(devctl_ddef_t, char *, int, int32_t *value);
-int devctl_ddef_string(devctl_ddef_t ddef_hdl, char *name, char *value);
-int devctl_ddef_string_array(devctl_ddef_t, char *, int, char **);
-int devctl_ddef_byte_array(devctl_ddef_t, char *, int, uchar_t *value);
-int devctl_bus_dev_create(devctl_hdl_t, devctl_ddef_t, uint_t, devctl_hdl_t *);
-char *devctl_get_pathname(devctl_hdl_t, char *, size_t);
+#include <sys/types.h>
+#include <sys/devctl.h>
+#include "libdevice.h"
--- a/usr/src/lib/libdevid/libdevid.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libdevid/libdevid.h	Mon Oct 05 09:17:26 2009 -0400
@@ -53,6 +53,7 @@
 extern char	*scsi_wwn_to_wwnstr(uint64_t wwn,
 		    int unit_address_form, char *wwnstr);
 extern void	scsi_wwnstr_hexcase(char *wwnstr, int lower_case);
+extern const char	*scsi_wwnstr_skip_ua_prefix(const char *wwnstr);
 extern void	scsi_free_wwnstr(char *wwnstr);
 
 #ifdef	SCSI_ADDR_PROP_LUN64
--- a/usr/src/lib/libdevid/mapfile-vers	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libdevid/mapfile-vers	Mon Oct 05 09:17:26 2009 -0400
@@ -67,6 +67,7 @@
 	scsi_lun_to_lun64;
 	scsi_wwn_to_wwnstr;
 	scsi_wwnstr_hexcase;
+	scsi_wwnstr_skip_ua_prefix;
 	scsi_wwnstr_to_wwn;
     local:
 	*;
--- a/usr/src/lib/libdevinfo/devinfo.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libdevinfo/devinfo.c	Mon Oct 05 09:17:26 2009 -0400
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Interfaces for getting device configuration data from kernel
  * through the devinfo driver.
@@ -977,9 +975,11 @@
 	if (DI_NODE(node)->state & DEVI_DEVICE_OFFLINE)
 		result |= DI_DEVICE_OFFLINE;
 	if (DI_NODE(node)->state & DEVI_DEVICE_DOWN)
-		result |= DI_DEVICE_OFFLINE;
+		result |= DI_DEVICE_DOWN;
 	if (DI_NODE(node)->state & DEVI_DEVICE_DEGRADED)
 		result |= DI_DEVICE_DEGRADED;
+	if (DI_NODE(node)->state & DEVI_DEVICE_REMOVED)
+		result |= DI_DEVICE_REMOVED;
 	if (DI_NODE(node)->state & DEVI_BUS_QUIESCED)
 		result |= DI_BUS_QUIESCED;
 	if (DI_NODE(node)->state & DEVI_BUS_DOWN)
@@ -2028,6 +2028,12 @@
 	return ((di_path_state_t)DI_PATH(path)->path_state);
 }
 
+uint_t
+di_path_flags(di_path_t path)
+{
+	return (DI_PATH(path)->path_flags);
+}
+
 char *
 di_path_node_name(di_path_t path)
 {
--- a/usr/src/lib/libdevinfo/libdevinfo.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libdevinfo/libdevinfo.h	Mon Oct 05 09:17:26 2009 -0400
@@ -81,6 +81,7 @@
 #define	DI_DEVICE_OFFLINE	0x1
 #define	DI_DEVICE_DOWN		0x2
 #define	DI_DEVICE_DEGRADED	0x4
+#define	DI_DEVICE_REMOVED	0x8
 #define	DI_BUS_QUIESCED		0x100
 #define	DI_BUS_DOWN		0x200
 
@@ -192,6 +193,7 @@
 extern char		*di_path_bus_addr(di_path_t path);
 extern int		di_path_instance(di_path_t path);
 extern di_path_state_t	di_path_state(di_path_t path);
+extern uint_t		di_path_flags(di_path_t path);
 
 extern char		*di_path_devfs_path(di_path_t path);
 extern char		*di_path_client_devfs_path(di_path_t path);
--- a/usr/src/lib/libdevinfo/mapfile-vers	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libdevinfo/mapfile-vers	Mon Oct 05 09:17:26 2009 -0400
@@ -206,6 +206,7 @@
 	di_minor_devinfo;
 	di_node_state;
 	di_parent_private_data;
+	di_path_flags;
 # XXX remove: di_path_(addr,next,next_client,next_phci)
 	di_path_addr;
 	di_path_next;
--- a/usr/src/lib/libelfsign/common/elfcertlib.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libelfsign/common/elfcertlib.c	Mon Oct 05 09:17:26 2009 -0400
@@ -82,6 +82,9 @@
 boolean_t
 elfcertlib_verifycert(ELFsign_t ess, ELFCert_t cert)
 {
+	KMF_ATTRIBUTE	attrlist[8];
+	int		numattr;
+
 	KMF_RETURN rv;
 	if ((cert->c_verified == E_OK) || (cert->c_verified == E_IS_TA)) {
 		return (B_TRUE);
@@ -99,9 +102,15 @@
 	(void) pthread_mutex_unlock(&ca_mutex);
 
 	if (CACERT != NULL) {
-		rv = KMF_VerifyCertWithCert(ess->es_kmfhandle,
-		    (const KMF_DATA *)&cert->c_cert,
-		    (const KMF_DATA *)&CACERT->c_cert.certificate);
+		numattr = 0;
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_CERT_DATA_ATTR, &cert->c_cert.certificate,
+		    sizeof (KMF_DATA));
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_SIGNER_CERT_DATA_ATTR, &CACERT->c_cert.certificate,
+		    sizeof (KMF_DATA));
+
+		rv = kmf_verify_cert(ess->es_kmfhandle, numattr, attrlist);
 		if (rv == KMF_OK) {
 			if (ess->es_certCAcallback != NULL)
 				(ess->es_certvercallback)(ess->es_callbackctx,
@@ -112,9 +121,15 @@
 	}
 
 	if (OBJCACERT != NULL) {
-		rv = KMF_VerifyCertWithCert(ess->es_kmfhandle,
-		    (const KMF_DATA *)&cert->c_cert,
-		    (const KMF_DATA *)&OBJCACERT->c_cert.certificate);
+		numattr = 0;
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_CERT_DATA_ATTR, &cert->c_cert.certificate,
+		    sizeof (KMF_DATA));
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_SIGNER_CERT_DATA_ATTR, &OBJCACERT->c_cert.certificate,
+		    sizeof (KMF_DATA));
+
+		rv = kmf_verify_cert(ess->es_kmfhandle, numattr, attrlist);
 		if (rv == KMF_OK) {
 			if (ess->es_certCAcallback != NULL)
 				(ess->es_certvercallback)(ess->es_callbackctx,
@@ -145,7 +160,6 @@
 {
 	KMF_RETURN rv;
 	ELFCert_t	cert = NULL;
-	KMF_FINDCERT_PARAMS fcparams;
 	KMF_X509_DER_CERT certbuf[2];
 	uint32_t ncerts;
 	boolean_t ret = B_FALSE;
@@ -181,31 +195,58 @@
 	}
 
 	for (plp = pathlist; *plp; plp++) {
-		(void) memset(&fcparams, 0, sizeof (fcparams));
-		fcparams.kstype = KMF_KEYSTORE_OPENSSL;
-		fcparams.sslparms.certfile = *plp;
-		fcparams.subject = signer_DN;
+		KMF_ATTRIBUTE	attrlist[8];
+		KMF_KEYSTORE_TYPE	kstype;
+		KMF_CERT_VALIDITY	certvalidity;
+		int		numattr;
+
+		kstype = KMF_KEYSTORE_OPENSSL;
+		certvalidity = KMF_ALL_CERTS;
 		ncerts = 2;
 
-		rv = KMF_FindCert(ess->es_kmfhandle, &fcparams, certbuf,
-		    &ncerts);
+		numattr = 0;
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype));
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_X509_DER_CERT_ATTR, certbuf,
+		    sizeof (KMF_X509_DER_CERT));
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_COUNT_ATTR, &ncerts, sizeof (uint32_t));
+		if (signer_DN != NULL) {
+			kmf_set_attr_at_index(attrlist, numattr++,
+			    KMF_SUBJECT_NAME_ATTR, signer_DN,
+			    strlen(signer_DN));
+		}
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_CERT_VALIDITY_ATTR, &certvalidity,
+		    sizeof (KMF_CERT_VALIDITY));
+		kmf_set_attr_at_index(attrlist, numattr++,
+		    KMF_CERT_FILENAME_ATTR, *plp, strlen (*plp));
+
+		rv = kmf_find_cert(ess->es_kmfhandle, numattr, attrlist);
+
 		if (rv != KMF_OK)
 			continue;
-		if (ncerts > 1 && signer_DN == NULL) {
-			/* There can be only one */
-			cryptodebug("elfcertlib_getcert: "
-			    "too many certificates found in %s",
-			    cert_pathname);
-			goto cleanup;
+		/* found one */
+		cert->c_cert = certbuf[0];
+		if (ncerts > 1) {
+			/* release any extras */
+			kmf_free_kmf_cert(ess->es_kmfhandle, &certbuf[1]);
+			if (signer_DN == NULL) {
+				/* There can be only one */
+				cryptodebug("elfcertlib_getcert: "
+				    "too many certificates found in %s",
+				    cert_pathname);
+				goto cleanup;
+			}
 		}
-		/* found it, cache subject and issuer */
-		cert->c_cert = certbuf[0];
-		rv = KMF_GetCertSubjectNameString(ess->es_kmfhandle,
+		/* cache subject and issuer */
+		rv = kmf_get_cert_subject_str(ess->es_kmfhandle,
 		    &cert->c_cert.certificate, &cert->c_subject);
 		if (rv != KMF_OK)
 			goto cleanup;
 
-		rv = KMF_GetCertIssuerNameString(ess->es_kmfhandle,
+		rv = kmf_get_cert_issuer_str(ess->es_kmfhandle,
 		    &cert->c_cert.certificate, &cert->c_issuer);
 		if (rv != KMF_OK)
 			goto cleanup;
@@ -262,17 +303,39 @@
 boolean_t
 elfcertlib_loadprivatekey(ELFsign_t ess, ELFCert_t cert, const char *pathname)
 {
-	KMF_RETURN rv = KMF_OK;
-	uint32_t nkeys = 2;
-	KMF_FINDKEY_PARAMS fkparams;
+	KMF_RETURN	rv = KMF_OK;
 	KMF_KEY_HANDLE	keybuf[2];
+	KMF_ATTRIBUTE	attrlist[16];
+	uint32_t	nkeys;
+	KMF_KEYSTORE_TYPE	kstype;
+	KMF_KEY_ALG	keytype;
+	KMF_KEY_CLASS	keyclass;
+	KMF_ENCODE_FORMAT	format;
+	int		numattr;
+
+	kstype = KMF_KEYSTORE_OPENSSL;
+	nkeys = 2;
+	keytype = KMF_KEYALG_NONE;
+	keyclass = KMF_ASYM_PRI;
+	format = KMF_FORMAT_UNDEF;
 
-	(void) memset(&fkparams, 0, sizeof (fkparams));
-	fkparams.keyclass = KMF_ASYM_PRI;
-	fkparams.kstype = KMF_KEYSTORE_OPENSSL;
-	fkparams.sslparms.keyfile = (char *)pathname;
+	numattr = 0;
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYSTORE_TYPE_ATTR,
+	    &kstype, sizeof (kstype));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_KEY_HANDLE_ATTR,
+	    keybuf, sizeof (KMF_KEY_HANDLE));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_COUNT_ATTR,
+	    &nkeys, sizeof (uint32_t));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYALG_ATTR,
+	    &keytype, sizeof (keytype));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYCLASS_ATTR,
+	    &keyclass, sizeof (keyclass));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_ENCODE_FORMAT_ATTR,
+	    &format, sizeof (format));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_KEY_FILENAME_ATTR,
+	    (char *)pathname, strlen(pathname));
 
-	rv = KMF_FindKey(ess->es_kmfhandle, &fkparams, keybuf, &nkeys);
+	rv = kmf_find_key(ess->es_kmfhandle, numattr, attrlist);
 	if (rv != KMF_OK)
 		return (B_FALSE);
 	if (nkeys != 1) {
@@ -299,35 +362,18 @@
 elfcertlib_loadtokenkey(ELFsign_t ess, ELFCert_t cert,
     const char *token_label, const char *pin)
 {
-	KMF_RETURN rv = KMF_OK;
-	KMF_FINDKEY_PARAMS fkparams;
-	KMF_CONFIG_PARAMS cfgparams;
-	uint32_t nkeys = 1;
-	char *idstr = NULL;
-	char *err = NULL;
-
-	(void) memset(&fkparams, 0, sizeof (fkparams));
-	(void) memset(&cfgparams, 0, sizeof (cfgparams));
-
-	cfgparams.kstype = KMF_KEYSTORE_PK11TOKEN;
-	cfgparams.pkcs11config.label = (char *)token_label;
-	cfgparams.pkcs11config.readonly = B_TRUE;
-	rv = KMF_ConfigureKeystore(ess->es_kmfhandle, &cfgparams);
-	if (rv != KMF_OK) {
-		if (KMF_GetKMFErrorString(rv, &err) == KMF_OK) {
-			cryptodebug("Error configuring token access:"
-			    " %s\n", err);
-			free(err);
-		}
-		return (B_FALSE);
-	}
-
-	fkparams.idstr = idstr;
-	fkparams.kstype = KMF_KEYSTORE_PK11TOKEN;
-	fkparams.keyclass = KMF_ASYM_PRI;
-	fkparams.cred.cred = (char *)pin;
-	fkparams.cred.credlen = (pin != NULL ? strlen(pin) : 0);
-	fkparams.pkcs11parms.private = B_TRUE;
+	KMF_RETURN	rv;
+	char		*idstr = NULL;
+	char		*kmferr;
+	KMF_ATTRIBUTE	attrlist[16];
+	uint32_t	nkeys;
+	KMF_KEYSTORE_TYPE	kstype;
+	KMF_KEY_ALG	keytype;
+	KMF_KEY_CLASS	keyclass;
+	KMF_ENCODE_FORMAT	format;
+	KMF_CREDENTIAL	pincred;
+	boolean_t	tokenbool, privatebool;
+	int		numattr;
 
 	/*
 	 * We will search for the key based on the ID attribute
@@ -335,31 +381,64 @@
 	 * a SHA-1 hash of the public modulus shared by the
 	 * key and the certificate.
 	 */
-	rv = KMF_GetCertIDString(&cert->c_cert.certificate, &idstr);
+	rv = kmf_get_cert_id_str(&cert->c_cert.certificate, &idstr);
 	if (rv != KMF_OK) {
-		if (KMF_GetKMFErrorString(rv, &err) == KMF_OK) {
-			cryptodebug("Error getting ID from cert: %s\n", err);
-			free(err);
-		}
+		(void) kmf_get_kmf_error_str(rv, &kmferr);
+		cryptodebug("Error getting ID from cert: %s\n",
+		    (kmferr ? kmferr : "Unrecognized KMF error"));
+		free(kmferr);
 		return (B_FALSE);
 	}
-	fkparams.idstr = idstr;
+
+	kstype = KMF_KEYSTORE_PK11TOKEN;
+	nkeys = 1;
+	keytype = KMF_KEYALG_NONE;
+	keyclass = KMF_ASYM_PRI;
+	format = KMF_FORMAT_UNDEF;
+	pincred.cred = (char *)pin;
+	pincred.credlen = strlen(pin);
+	tokenbool = B_FALSE;
+	privatebool = B_TRUE;
 
-	rv = KMF_FindKey(ess->es_kmfhandle, &fkparams,
-	    &cert->c_privatekey, &nkeys);
-	if (rv != KMF_OK || nkeys != 1) {
-		if (KMF_GetKMFErrorString(rv, &err) == KMF_OK) {
-			cryptodebug("Error finding private key: %s\n", err);
-			free(err);
-		}
-		free(idstr);
+	numattr = 0;
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYSTORE_TYPE_ATTR,
+	    &kstype, sizeof (kstype));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_KEY_HANDLE_ATTR,
+	    &cert->c_privatekey, sizeof (KMF_KEY_HANDLE));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_COUNT_ATTR,
+	    &nkeys, sizeof (uint32_t));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYALG_ATTR,
+	    &keytype, sizeof (keytype));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYCLASS_ATTR,
+	    &keyclass, sizeof (keyclass));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_ENCODE_FORMAT_ATTR,
+	    &format, sizeof (format));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_IDSTR_ATTR,
+	    idstr, strlen(idstr));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_CREDENTIAL_ATTR,
+	    &pincred, sizeof (KMF_CREDENTIAL));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_TOKEN_BOOL_ATTR,
+	    &tokenbool, sizeof (tokenbool));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_PRIVATE_BOOL_ATTR,
+	    &privatebool, sizeof (privatebool));
+
+	rv = kmf_find_key(ess->es_kmfhandle, numattr, attrlist);
+	free(idstr);
+	if (rv != KMF_OK) {
+		(void) kmf_get_kmf_error_str(rv, &kmferr);
+		cryptodebug("Error finding private key: %s\n",
+		    (kmferr ? kmferr : "Unrecognized KMF error"));
+		free(kmferr);
+		return (B_FALSE);
+	}
+	if (nkeys != 1) {
+		cryptodebug("Error finding private key: No key found\n");
 		return (B_FALSE);
 	}
 	cryptodebug("key found in %s", token_label);
 	cryptodebug("elfcertlib_loadprivatekey = 0x%.8X",
 	    &cert->c_privatekey);
 
-	free(idstr);
 	return (B_TRUE);
 }
 
@@ -384,10 +463,12 @@
 	const uchar_t *data, size_t data_len,
 	uchar_t *sig, size_t *sig_len)
 {
-	KMF_RETURN ret = KMF_OK;
-	KMF_DATA tobesigned;
-	KMF_DATA signature;
-	uchar_t	 der_data[sizeof (MD5_DER_PREFIX) + MD5_DIGEST_LENGTH];
+	KMF_RETURN	ret;
+	KMF_DATA	tobesigned;
+	KMF_DATA	signature;
+	uchar_t		der_data[sizeof (MD5_DER_PREFIX) + MD5_DIGEST_LENGTH];
+	KMF_ATTRIBUTE	attrlist[8];
+	int		numattr;
 
 	if (ess->es_version <= FILESIG_VERSION2) {
 		/* compatibility: take MD5 hash of SHA1 hash */
@@ -420,17 +501,28 @@
 	signature.Data = (uchar_t *)sig;
 	signature.Length = *sig_len;
 
-	ret = KMF_SignDataWithKey(ess->es_kmfhandle,
-	    &cert->c_privatekey, (KMF_OID *)&KMFOID_RSA,
-	    &tobesigned, &signature);
+	numattr = 0;
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_KEYSTORE_TYPE_ATTR, &(cert->c_privatekey.kstype),
+	    sizeof (KMF_KEYSTORE_TYPE));
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_KEY_HANDLE_ATTR, &cert->c_privatekey, sizeof (KMF_KEY_HANDLE));
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_OID_ATTR, (KMF_OID *)&KMFOID_RSA, sizeof (KMF_OID));
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_DATA_ATTR, &tobesigned, sizeof (KMF_DATA));
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_OUT_DATA_ATTR, &signature, sizeof (KMF_DATA));
+
+	ret = kmf_sign_data(ess->es_kmfhandle, numattr, attrlist);
 
 	if (ret != KMF_OK) {
-		char *err;
-		if (KMF_GetKMFErrorString(ret, &err) == KMF_OK &&
-		    err != NULL) {
-			cryptodebug("Error signing data: %s\n", err);
-			free(err);
-		}
+		char	*kmferr;
+
+		(void) kmf_get_kmf_error_str(ret, &kmferr);
+		cryptodebug("Error signing data: %s\n",
+		    (kmferr ? kmferr : "Unrecognized KMF error"));
+		free(kmferr);
 		*sig_len = 0;
 		return (B_FALSE);
 	}
@@ -459,6 +551,9 @@
 	KMF_DATA	indata;
 	KMF_DATA	insig;
 	KMF_ALGORITHM_INDEX algid;
+	KMF_ATTRIBUTE	attrlist[8];
+	KMF_KEYSTORE_TYPE	kstype;
+	int		numattr;
 
 	indata.Data = (uchar_t *)data;
 	indata.Length = data_len;
@@ -476,9 +571,21 @@
 	 * all validation within the FIPS-140 boundary for
 	 * the Cryptographic Framework.
 	 */
-	rv = KMF_VerifyDataWithCert(ess->es_kmfhandle,
-	    KMF_KEYSTORE_PK11TOKEN, algid,
-	    &indata, &insig, &cert->c_cert.certificate);
+	kstype = KMF_KEYSTORE_PK11TOKEN;
+
+	numattr = 0;
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_KEYSTORE_TYPE_ATTR,
+	    &kstype,  sizeof (kstype));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_DATA_ATTR,
+	    &indata, sizeof (KMF_DATA));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_IN_SIGN_ATTR,
+	    &insig, sizeof (KMF_DATA));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_SIGNER_CERT_DATA_ATTR,
+	    (KMF_DATA *)(&cert->c_cert.certificate), sizeof (KMF_DATA));
+	kmf_set_attr_at_index(attrlist, numattr++, KMF_ALGORITHM_INDEX_ATTR,
+	    &algid, sizeof (algid));
+
+	rv = kmf_verify_data(ess->es_kmfhandle, numattr, attrlist);
 
 	return ((rv == KMF_OK));
 }
@@ -519,7 +626,7 @@
 	boolean_t rc = B_TRUE;
 	KMF_RETURN rv;
 	if (ess->es_kmfhandle == NULL) {
-		rv = KMF_Initialize(&ess->es_kmfhandle, NULL, NULL);
+		rv = kmf_initialize(&ess->es_kmfhandle, NULL, NULL);
 		if (rv != KMF_OK) {
 			cryptoerror(LOG_ERR,
 			    "unable to initialize KMF library");
@@ -532,7 +639,7 @@
 void
 elfcertlib_fini(ELFsign_t ess)
 {
-	(void) KMF_Finalize(ess->es_kmfhandle);
+	(void) kmf_finalize(ess->es_kmfhandle);
 }
 
 /*
@@ -541,15 +648,25 @@
 boolean_t
 elfcertlib_settoken(ELFsign_t ess, char *token)
 {
-	boolean_t rc = B_TRUE;
-	KMF_RETURN rv;
-	KMF_CONFIG_PARAMS cfgparams;
+	boolean_t	rc = B_TRUE;
+	KMF_RETURN	rv;
+	KMF_ATTRIBUTE	attrlist[8];
+	KMF_KEYSTORE_TYPE	kstype;
+	boolean_t	readonly;
+	int	numattr;
+
+	kstype = KMF_KEYSTORE_PK11TOKEN;
+	readonly = B_TRUE;
 
-	(void) memset(&cfgparams, 0, sizeof (cfgparams));
-	cfgparams.kstype = KMF_KEYSTORE_PK11TOKEN;
-	cfgparams.pkcs11config.label = token;
-	cfgparams.pkcs11config.readonly = B_TRUE;
-	rv = KMF_ConfigureKeystore(ess->es_kmfhandle, &cfgparams);
+	numattr = 0;
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_KEYSTORE_TYPE_ATTR, &kstype, sizeof (kstype));
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_TOKEN_LABEL_ATTR, token, strlen(token));
+	kmf_set_attr_at_index(attrlist, numattr++,
+	    KMF_READONLY_ATTR, &readonly, sizeof (readonly));
+
+	rv = kmf_configure_keystore(ess->es_kmfhandle, numattr, attrlist);
 	if (rv != KMF_OK) {
 		cryptoerror(LOG_ERR, "unable to select token\n");
 		rc = B_FALSE;
@@ -636,8 +753,8 @@
 	free(cert->c_subject);
 	free(cert->c_issuer);
 
-	KMF_FreeKMFCert(ess->es_kmfhandle, &cert->c_cert);
-	KMF_FreeKMFKey(ess->es_kmfhandle, &cert->c_privatekey);
+	kmf_free_kmf_cert(ess->es_kmfhandle, &cert->c_cert);
+	kmf_free_kmf_key(ess->es_kmfhandle, &cert->c_privatekey);
 
 	free(cert);
 }
--- a/usr/src/lib/libfdisk/i386/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libfdisk/i386/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -68,6 +68,8 @@
 i386_CFLAGS += -D_LARGEFILE64_SOURCE
 i386_CFLAGS += -D_FILE_OFFSET_BITS=64
 
+LINTFLAGS +=	-erroff=E_BAD_PTR_CAST_ALIGN
+
 .KEEP_STATE:
 
 LIBS=	$(DYNLIB) $(LINTLIB)
--- a/usr/src/lib/libfdisk/i386/libfdisk.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libfdisk/i386/libfdisk.c	Mon Oct 05 09:17:26 2009 -0400
@@ -235,7 +235,7 @@
 }
 
 int
-fdisk_is_linux_swap(ext_part_t *epp, uint32_t part_start, off_t *lsm_offset)
+fdisk_is_linux_swap(ext_part_t *epp, uint32_t part_start, uint64_t *lsm_offset)
 {
 	int		i;
 	int		rval = -1;
@@ -243,6 +243,8 @@
 	uint32_t	linux_pg_size;
 	char		*buf, *linux_swap_magic;
 	int		sec_sz = fdisk_get_disk_geom(epp, PHYSGEOM, SSIZE);
+	off_t		label_offset;
+
 	/*
 	 * Known linux kernel page sizes
 	 * The linux swap magic is found as the last 10 bytes of a disk chunk
@@ -255,6 +257,28 @@
 		return (ENOMEM);
 	}
 
+	/*
+	 * Check if there is a sane Solaris VTOC
+	 * If there is a valid vtoc, no need to lookup
+	 * for the linux swap signature.
+	 */
+	label_offset = (part_start + DK_LABEL_LOC) * sec_sz;
+	if ((rval = lseek(epp->dev_fd, label_offset, SEEK_SET)) < 0)
+		goto done;
+
+	if ((rval = read(epp->dev_fd, buf, sec_sz)) < sec_sz) {
+		rval = EIO;
+		goto done;
+	}
+
+
+	if ((((struct dk_label *)buf)->dkl_magic == DKL_MAGIC) &&
+	    (((struct dk_label *)buf)->dkl_vtoc.v_sanity == VTOC_SANE)) {
+		rval = -1;
+		goto done;
+	}
+
+	/* No valid vtoc, so check for linux swap signature */
 	linux_swap_magic = buf + sec_sz - LINUX_SWAP_MAGIC_LENGTH;
 
 	for (i = 0; i < sizeof (linux_pg_size_arr)/sizeof (uint32_t); i++) {
@@ -278,11 +302,13 @@
 		    LINUX_SWAP_MAGIC_LENGTH) == 0)) {
 			/* Found a linux swap */
 			rval = 0;
-			*lsm_offset = seek_offset;
+			if (lsm_offset != NULL)
+				*lsm_offset = (uint64_t)seek_offset;
 			break;
 		}
 	}
 
+done:
 	free(buf);
 	return (rval);
 }
@@ -295,12 +321,13 @@
 	uint32_t part_start;
 	int pno;
 	int rval = -1;
-	off_t lsmo = 0;
 
 	for (pno = 5; temp != NULL; temp = temp->next, pno++) {
 		if (fdisk_is_solaris_part(LE_8(temp->parts[0].systid))) {
 			part_start = temp->abs_secnum + temp->logdrive_offset;
-			if (fdisk_is_linux_swap(epp, part_start, &lsmo) == 0) {
+			if ((temp->parts[0].systid == SUNIXOS) &&
+			    (fdisk_is_linux_swap(epp, part_start,
+			    NULL) == 0)) {
 				continue;
 			}
 			*pnum = pno;
@@ -1112,15 +1139,10 @@
 	int ld_count;
 	uint32_t abs_secnum;
 	int check_mounts = 0;
-	off_t lsmo;
-	char *lsm_buf;
 
 	if ((ebr_buf = (unsigned char *)malloc(sectsize)) == NULL) {
 		return (ENOMEM);
 	}
-	if ((lsm_buf = calloc(1, sectsize)) == NULL) {
-		return (ENOMEM);
-	}
 
 	if (epp->first_ebr_is_null) {
 		/*
@@ -1162,9 +1184,6 @@
 		if (ebr_buf) {
 			free(ebr_buf);
 		}
-		if (lsm_buf) {
-			free(lsm_buf);
-		}
 		return (rval);
 	}
 
@@ -1189,9 +1208,6 @@
 			if (ebr_buf) {
 				free(ebr_buf);
 			}
-			if (lsm_buf) {
-				free(lsm_buf);
-			}
 			return (FDISK_SUCCESS);
 		}
 
@@ -1202,56 +1218,6 @@
 		 */
 		for (temp = epp->ld_head, ld_count = 0; temp != NULL;
 		    temp = temp->next, ld_count++) {
-			/*
-			 * Check if the current partition is a solaris old
-			 * partition. In that case, check if it was previously
-			 * a linux swap. If so, overwrite the linux swap magic.
-			 */
-			if (temp->parts[0].systid == SUNIXOS) {
-				uint32_t secnum = temp->abs_secnum +
-				    temp->logdrive_offset;
-				if (fdisk_is_linux_swap(epp, secnum,
-				    &lsmo) == 0) {
-					if ((rval = lseek(epp->dev_fd, lsmo,
-					    SEEK_SET)) < 0) {
-						if (ld_count) {
-							break;
-						}
-						goto error;
-					}
-
-					if (read(epp->dev_fd, lsm_buf,
-					    sectsize) < sectsize) {
-						rval = EIO;
-						if (ld_count) {
-							break;
-						}
-						goto error;
-					}
-
-					bzero(lsm_buf + sectsize -
-					    LINUX_SWAP_MAGIC_LENGTH,
-					    LINUX_SWAP_MAGIC_LENGTH);
-
-					if ((rval = lseek(epp->dev_fd, lsmo,
-					    SEEK_SET)) < 0) {
-						if (ld_count) {
-							break;
-						}
-						goto error;
-					}
-
-					if ((rval = write(epp->dev_fd, lsm_buf,
-					    sectsize)) < sectsize) {
-						rval = EIO;
-						if (ld_count) {
-							break;
-						}
-						goto error;
-					}
-				}
-			}
-
 			if (ld_count == 0) {
 				abs_secnum = epp->ext_beg_sec;
 			} else {
@@ -1293,9 +1259,6 @@
 	if (ebr_buf) {
 		free(ebr_buf);
 	}
-	if (lsm_buf) {
-		free(lsm_buf);
-	}
 	return (rval);
 }
 
--- a/usr/src/lib/libfdisk/i386/libfdisk.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libfdisk/i386/libfdisk.h	Mon Oct 05 09:17:26 2009 -0400
@@ -268,7 +268,7 @@
 #define	fdisk_is_dos_extended(id) (((id) == EXTDOS) || ((id) == FDISK_EXTLBA))
 
 extern int fdisk_is_linux_swap(ext_part_t *epp, uint32_t part_start,
-    off_t *lsm_offset);
+    uint64_t *lsm_offset);
 extern int libfdisk_init(ext_part_t **epp, char *devstr, struct ipart *parttab,
     int opflag);
 extern int libfdisk_reset(ext_part_t *epp);
--- a/usr/src/lib/libgss/mapfile-vers	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libgss/mapfile-vers	Mon Oct 05 09:17:26 2009 -0400
@@ -36,6 +36,14 @@
 #
 # MAPFILE HEADER END
 #
+SUNW_1.3 {
+    global:
+	gss_add_buffer_set_member;
+	gss_create_empty_buffer_set;
+	GSS_C_INQ_SSPI_SESSION_KEY;
+	gss_inquire_sec_context_by_oid;
+	gss_release_buffer_set;
+} SUNW_1.2;
 
 SUNW_1.2 {
     global:
@@ -52,7 +60,6 @@
 	GSS_C_NT_MACHINE_UID_NAME;
 	GSS_C_NT_STRING_UID_NAME;
 	GSS_C_NT_USER_NAME;
-	GSS_C_INQ_SSPI_SESSION_KEY;
 	gss_compare_name;
 	gss_context_time;
 	gss_create_empty_oid_set;
@@ -88,10 +95,6 @@
 	gss_verify_mic;
 	gss_wrap;
 	gss_wrap_size_limit;
-	gss_add_buffer_set_member;
-	gss_create_empty_buffer_set;
-	gss_release_buffer_set;
-	gss_inquire_sec_context_by_oid;
 } SUNW_1.1;
 
 # Due to mistakes made early in the history of this library, there are
--- a/usr/src/lib/libinstzones/common/mapfile-vers	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libinstzones/common/mapfile-vers	Mon Oct 05 09:17:26 2009 -0400
@@ -88,7 +88,7 @@
 	z_zlist_is_zone_runnable;
 	z_zlist_restore_zone_state;
 	z_zone_exec;
+	z_zones_are_implemented;
   local:
 	*;
 };
-
--- a/usr/src/lib/libpam/pam_appl.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libpam/pam_appl.h	Mon Oct 05 09:17:26 2009 -0400
@@ -19,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _PAM_APPL_H
 #define	_PAM_APPL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 
 #ifdef	__cplusplus
@@ -108,15 +106,6 @@
 #define	PAM_TEXT_INFO		4	/* Textual information */
 
 /*
- * Sun's proprietary message types
- * Can these new new message types supported in version 2
- * have the numbers like -XXX (ie., negative numbers).
- * Hence will not clash with new proposals from X/OPEN
- */
-#define	PAM_MSG_NOCONF		2001	/* No confirmation from user */
-#define	PAM_CONV_INTERRUPT	2002	/* Return from conv() */
-
-/*
  * max # of messages passed to the application through the
  * conversation function call
  */
@@ -226,13 +215,6 @@
 typedef struct pam_repository pam_repository_t;
 
 /*
- * PAM message version.
- * Sun proprietary pam_[sg]et_item() extension
- */
-#define	PAM_MSG_VERSION	3001		/* PAM message version supported */
-#define	PAM_MSG_VERSION_V2 "2.0"	/* PAM 2.0 message version */
-
-/*
  * pam_get_user is called to retrieve the user name (PAM_USER). If PAM_USER
  * is not set then this call will prompt for the user name using the
  * conversation function. This function should only be used by modules, not
--- a/usr/src/lib/libpam/pam_framework.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libpam/pam_framework.c	Mon Oct 05 09:17:26 2009 -0400
@@ -110,14 +110,6 @@
 {
 	char *name;
 
-	/*
-	 * XXX -- Contracted Consolidation Private
-	 *	  to be eliminated when dtlogin contract is terminated
-	 * Version number requested by PAM's client
-	 */
-	if (item_type == PAM_MSG_VERSION)
-		return ("msg_version");
-
 	if (item_type <= 0 ||
 	    item_type >= PAM_MAX_ITEMS ||
 	    (name = pam_inames[item_type]) == NULL) {
@@ -415,29 +407,6 @@
 		return (PAM_PERM_DENIED);
 
 	/*
-	 * XXX -- Contracted Consolidation Private
-	 *	  to be eliminated when dtlogin contract is terminated
-	 * Check if tag is Sun proprietary
-	 */
-	if (item_type == PAM_MSG_VERSION) {
-		if (pamh->pam_client_message_version_number)
-			free(pamh->pam_client_message_version_number);
-
-		if (item == NULL)
-			pamh->pam_client_message_version_number = NULL;
-		else
-			if ((pamh->pam_client_message_version_number =
-			    strdup((char *)item)) == NULL)
-				return (PAM_BUF_ERR);
-
-		pam_trace(PAM_DEBUG_ITEM,
-		    "pam_set_item(%p:%s)=%s", (void *)pamh,
-		    pam_trace_iname(item_type, iname_buf),
-		    item ? (char *)item : "NULL");
-		return (PAM_SUCCESS);
-	}
-
-	/*
 	 * Check that item_type is within valid range
 	 */
 
@@ -584,19 +553,6 @@
 	if (pamh == NULL)
 		return (PAM_SYSTEM_ERR);
 
-	/*
-	 * XXX -- Contracted Consolidation Private
-	 *	  to be eliminated when dtlogin contract is terminated
-	 * Check if tag is Sun proprietary
-	 */
-	if (item_type == PAM_MSG_VERSION) {
-		*item = pamh->pam_client_message_version_number;
-		pam_trace(PAM_DEBUG_ITEM, "pam_get_item(%p:%s)=%s",
-		    (void *)pamh, pam_trace_iname(item_type, iname_buf),
-		    *item ? (char *)*item : "NULL");
-		return (PAM_SUCCESS);
-	}
-
 	if (item_type <= 0 || item_type >= PAM_MAX_ITEMS)
 		return (PAM_SYMBOL_ERR);
 
@@ -2546,10 +2502,6 @@
 	pam_repository_t *auth_rep;
 
 	if (pamh) {
-		/* Cleanup Sun proprietary tag information */
-		if (pamh->pam_client_message_version_number)
-			free(pamh->pam_client_message_version_number);
-
 		while (pamh->include_depth >= 0) {
 			free_pam_conf_info(pamh);
 			pamh->include_depth--;
--- a/usr/src/lib/libpam/pam_impl.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libpam/pam_impl.h	Mon Oct 05 09:17:26 2009 -0400
@@ -19,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_PAM_IMPL_H
 #define	_PAM_IMPL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -230,13 +228,6 @@
 	struct	pam_module_data *ssd;		/* module specific data */
 	fd_list *fd;				/* module fd's */
 	env_list *pam_env;			/* environment variables */
-
-	/*
-	 * XXX -- Contracted Consolidation Private
-	 *	  to be eliminated when dtlogin contract is terminated
-	 * Version number requested by PAM's client
-	 */
-	char	*pam_client_message_version_number;
 };
 
 /*
--- a/usr/src/lib/libstmf/common/libstmf.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libstmf/common/libstmf.h	Mon Oct 05 09:17:26 2009 -0400
@@ -355,6 +355,7 @@
 int stmfSetProviderData(char *providerName, nvlist_t *nvl, int providerType);
 int stmfSetProviderDataProt(char *providerName, nvlist_t *nvl,
     int providerType, uint64_t *setToken);
+int stmfValidateView(stmfViewEntry *viewEntry);
 
 #ifdef	__cplusplus
 }
--- a/usr/src/lib/libstmf/common/mapfile-vers	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libstmf/common/mapfile-vers	Mon Oct 05 09:17:26 2009 -0400
@@ -83,6 +83,7 @@
 	    stmfSetProviderData;
 	    stmfSetProviderDataProt;
 	    stmfSetLuProp;
+	    stmfValidateView;
 	local:
 		*;
 };
--- a/usr/src/lib/libstmf/common/stmf.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libstmf/common/stmf.c	Mon Oct 05 09:17:26 2009 -0400
@@ -5822,3 +5822,190 @@
 
 	return (ret);
 }
+
+/*
+ * validateLunNumIoctl
+ *
+ * Purpose: Issues ioctl to check and get available lun# in view entry
+ *
+ * viewEntry - view entry to use
+ */
+static int
+validateLunNumIoctl(int fd, stmfViewEntry *viewEntry)
+{
+	int ret = STMF_STATUS_SUCCESS;
+	int ioctlRet;
+	stmf_iocdata_t stmfIoctl;
+	stmf_view_op_entry_t ioctlViewEntry;
+
+	bzero(&ioctlViewEntry, sizeof (ioctlViewEntry));
+	/*
+	 * don't set ve_ndx or ve_ndx_valid as ve_ndx_valid should be
+	 * false on input
+	 */
+	ioctlViewEntry.ve_lu_number_valid = viewEntry->luNbrValid;
+	ioctlViewEntry.ve_all_hosts = viewEntry->allHosts;
+	ioctlViewEntry.ve_all_targets = viewEntry->allTargets;
+
+	if (viewEntry->allHosts == B_FALSE) {
+		bcopy(viewEntry->hostGroup, &ioctlViewEntry.ve_host_group.name,
+		    sizeof (stmfGroupName));
+		ioctlViewEntry.ve_host_group.name_size =
+		    strlen((char *)viewEntry->hostGroup);
+	}
+	if (viewEntry->allTargets == B_FALSE) {
+		bcopy(viewEntry->targetGroup,
+		    &ioctlViewEntry.ve_target_group.name,
+		    sizeof (stmfGroupName));
+		ioctlViewEntry.ve_target_group.name_size =
+		    strlen((char *)viewEntry->targetGroup);
+	}
+	/* Validating the lun number */
+	if (viewEntry->luNbrValid) {
+		bcopy(viewEntry->luNbr, &ioctlViewEntry.ve_lu_nbr,
+		    sizeof (ioctlViewEntry.ve_lu_nbr));
+	}
+
+	bzero(&stmfIoctl, sizeof (stmfIoctl));
+	/*
+	 * Issue ioctl to validate lun# in the view entry
+	 */
+	stmfIoctl.stmf_version = STMF_VERSION_1;
+	stmfIoctl.stmf_ibuf_size = sizeof (ioctlViewEntry);
+	stmfIoctl.stmf_ibuf = (uint64_t)(unsigned long)&ioctlViewEntry;
+	stmfIoctl.stmf_obuf_size = sizeof (ioctlViewEntry);
+	stmfIoctl.stmf_obuf = (uint64_t)(unsigned long)&ioctlViewEntry;
+	ioctlRet = ioctl(fd, STMF_IOCTL_VALIDATE_VIEW, &stmfIoctl);
+
+	/* save available lun number */
+	if (!viewEntry->luNbrValid) {
+		bcopy(ioctlViewEntry.ve_lu_nbr, viewEntry->luNbr,
+		    sizeof (ioctlViewEntry.ve_lu_nbr));
+	}
+	if (ioctlRet != 0) {
+		switch (errno) {
+			case EBUSY:
+				ret = STMF_ERROR_BUSY;
+				break;
+			case EPERM:
+				ret = STMF_ERROR_PERM;
+				break;
+			case EACCES:
+				switch (stmfIoctl.stmf_error) {
+					case STMF_IOCERR_UPDATE_NEED_CFG_INIT:
+						ret = STMF_ERROR_CONFIG_NONE;
+						break;
+					default:
+						ret = STMF_ERROR_PERM;
+						break;
+				}
+				break;
+			default:
+				switch (stmfIoctl.stmf_error) {
+					case STMF_IOCERR_LU_NUMBER_IN_USE:
+						ret = STMF_ERROR_LUN_IN_USE;
+						break;
+					case STMF_IOCERR_VIEW_ENTRY_CONFLICT:
+						ret = STMF_ERROR_VE_CONFLICT;
+						break;
+					case STMF_IOCERR_UPDATE_NEED_CFG_INIT:
+						ret = STMF_ERROR_CONFIG_NONE;
+						break;
+					case STMF_IOCERR_INVALID_HG:
+						ret = STMF_ERROR_INVALID_HG;
+						break;
+					case STMF_IOCERR_INVALID_TG:
+						ret = STMF_ERROR_INVALID_TG;
+						break;
+					default:
+						syslog(LOG_DEBUG,
+						    "addViewEntryIoctl"
+						    ":error(%d)",
+						    stmfIoctl.stmf_error);
+						ret = STMF_STATUS_ERROR;
+						break;
+				}
+				break;
+		}
+	}
+	return (ret);
+}
+
+/*
+ * stmfValidateView
+ *
+ * Purpose: Validate or get lun # base on TG, HG of view entry
+ *
+ * viewEntry - view entry structure to use
+ */
+int
+stmfValidateView(stmfViewEntry *viewEntry)
+{
+	int ret;
+	int fd;
+	stmfViewEntry iViewEntry;
+
+	if (viewEntry == NULL) {
+		return (STMF_ERROR_INVALID_ARG);
+	}
+
+	/* initialize and set internal view entry */
+	bzero(&iViewEntry, sizeof (iViewEntry));
+
+	if (!viewEntry->allHosts) {
+		bcopy(viewEntry->hostGroup, iViewEntry.hostGroup,
+		    sizeof (iViewEntry.hostGroup));
+	} else {
+		iViewEntry.allHosts = B_TRUE;
+	}
+
+	if (!viewEntry->allTargets) {
+		bcopy(viewEntry->targetGroup, iViewEntry.targetGroup,
+		    sizeof (iViewEntry.targetGroup));
+	} else {
+		iViewEntry.allTargets = B_TRUE;
+	}
+
+	if (viewEntry->luNbrValid) {
+		iViewEntry.luNbrValid = B_TRUE;
+		bcopy(viewEntry->luNbr, iViewEntry.luNbr,
+		    sizeof (iViewEntry.luNbr));
+	}
+
+	/*
+	 * set users return view entry index valid flag to false
+	 * in case of failure
+	 */
+	viewEntry->veIndexValid = B_FALSE;
+
+	/* Check to ensure service exists */
+	if (psCheckService() != STMF_STATUS_SUCCESS) {
+		return (STMF_ERROR_SERVICE_NOT_FOUND);
+	}
+
+	/* call init */
+	ret = initializeConfig();
+	if (ret != STMF_STATUS_SUCCESS) {
+		return (ret);
+	}
+
+	/*
+	 * Open control node for stmf
+	 */
+	if ((ret = openStmf(OPEN_STMF, &fd)) != STMF_STATUS_SUCCESS)
+		return (ret);
+
+	/*
+	 * Validate lun# in the view entry from the driver
+	 */
+	ret = validateLunNumIoctl(fd, &iViewEntry);
+	(void) close(fd);
+
+	/* save available lun number */
+	if (!viewEntry->luNbrValid) {
+		bcopy(iViewEntry.luNbr, viewEntry->luNbr,
+		    sizeof (iViewEntry.luNbr));
+	}
+
+	return (ret);
+}
--- a/usr/src/lib/libzfs/common/libzfs.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libzfs/common/libzfs.h	Mon Oct 05 09:17:26 2009 -0400
@@ -332,10 +332,14 @@
  */
 struct zfs_cmd;
 
+extern const char *hist_event_table[LOG_END];
+
 extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *,
     boolean_t verbose);
 extern int zpool_upgrade(zpool_handle_t *, uint64_t);
 extern int zpool_get_history(zpool_handle_t *, nvlist_t **);
+extern int zpool_history_unpack(char *, uint64_t, uint64_t *,
+    nvlist_t ***, uint_t *);
 extern void zpool_set_history_str(const char *subcommand, int argc,
     char **argv, char *history_str);
 extern int zpool_stage_history(libzfs_handle_t *, const char *);
--- a/usr/src/lib/libzfs/common/libzfs_pool.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c	Mon Oct 05 09:17:26 2009 -0400
@@ -42,6 +42,49 @@
 #include "zfs_prop.h"
 #include "libzfs_impl.h"
 
+const char *hist_event_table[LOG_END] = {
+	"invalid event",
+	"pool create",
+	"vdev add",
+	"pool remove",
+	"pool destroy",
+	"pool export",
+	"pool import",
+	"vdev attach",
+	"vdev replace",
+	"vdev detach",
+	"vdev online",
+	"vdev offline",
+	"vdev upgrade",
+	"pool clear",
+	"pool scrub",
+	"pool property set",
+	"create",
+	"clone",
+	"destroy",
+	"destroy_begin_sync",
+	"inherit",
+	"property set",
+	"quota set",
+	"permission update",
+	"permission remove",
+	"permission who remove",
+	"promote",
+	"receive",
+	"rename",
+	"reservation set",
+	"replay_inc_sync",
+	"replay_full_sync",
+	"rollback",
+	"snapshot",
+	"filesystem version upgrade",
+	"refquota set",
+	"refreservation set",
+	"pool scrub done",
+	"user hold",
+	"user release",
+};
+
 static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
 
 #if defined(__i386) || defined(__amd64)
@@ -2804,7 +2847,7 @@
  * into 'records'.  'leftover' is set to the number of bytes that weren't
  * processed as there wasn't a complete record.
  */
-static int
+int
 zpool_history_unpack(char *buf, uint64_t bytes_read, uint64_t *leftover,
     nvlist_t ***records, uint_t *numrecords)
 {
--- a/usr/src/lib/libzfs/common/mapfile-vers	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/libzfs/common/mapfile-vers	Mon Oct 05 09:17:26 2009 -0400
@@ -45,6 +45,7 @@
 	fletcher_4_byteswap;
 	fletcher_4_incremental_native;
 	fletcher_4_incremental_byteswap;
+	hist_event_table;
 	libzfs_errno;
 	libzfs_error_action;
 	libzfs_error_description;
@@ -170,6 +171,7 @@
 	zpool_get_prop_int;
 	zpool_get_state;
 	zpool_get_status;
+	zpool_history_unpack;
 	zpool_import;
 	zpool_import_props;
 	zpool_import_status;
--- a/usr/src/lib/mpapi/libmpscsi_vhci/common/MP_GetMultipathLusPlugin.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/mpapi/libmpscsi_vhci/common/MP_GetMultipathLusPlugin.c	Mon Oct 05 09:17:26 2009 -0400
@@ -32,9 +32,40 @@
 
 #include <libdevinfo.h>
 
+/*
+ * Checks whether there is online path or not.
+ *  - no path found returns -1.
+ *  - online/standby path found returns 1.
+ *  - path exists but no online/standby path found returns 0.
+ */
+static int checkAvailablePath(di_node_t node)
+{
+	di_path_t path;
+	di_path_state_t state;
+
+	if ((path = di_path_client_next_path(node, DI_PATH_NIL))
+	    == DI_PATH_NIL) {
+		log(LOG_INFO, "checkAvailalblePath()",
+		    " - No path found");
+		return (-1);
+	}
+
+	do {
+		/* ignore the path that is neither online nor standby. */
+		if (((state = di_path_state(path)) == DI_PATH_STATE_ONLINE) ||
+		    (state == DI_PATH_STATE_STANDBY)) {
+			return (1);
+		}
+	} while ((path = di_path_client_next_path(node, path)) != DI_PATH_NIL);
+
+	/* return 0 for the case that there is no online path to the node. */
+	log(LOG_INFO, "checkAvailalblePath()", " - No online path found");
+	return (0);
+}
+
 static int getOidList(di_node_t root_node, MP_OID_LIST *pOidList)
 {
-	int numNodes = 0;
+	int numNodes = 0, state;
 
 	MP_UINT64 instNum = 0;
 
@@ -59,8 +90,21 @@
 
 	while (DI_NODE_NIL != sv_child_node) {
 
-		/* Skip the node which is not online. */
-		if (di_state(sv_child_node) != 0) {
+		/* skip the node which is offline, down or detached. */
+		state = di_state(sv_child_node);
+		if ((state & DI_DEVICE_DOWN) ||
+		    (state & DI_DEVICE_OFFLINE)) {
+			sv_child_node = di_sibling_node(sv_child_node);
+			continue;
+		}
+
+		/*
+		 * skip if the node doesn't have any path avaialble.
+		 * If any path is found from the DINFOCACHE snaphost
+		 * that means the driver keeps track of the path regadless
+		 * of state.
+		 */
+		if (checkAvailablePath(sv_child_node) == -1) {
 			sv_child_node = di_sibling_node(sv_child_node);
 			continue;
 		}
--- a/usr/src/lib/pam_modules/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/pam_modules/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -19,11 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
 # lib/pam_modules/Makefile
 #
 
@@ -53,9 +51,6 @@
 	unix_cred	\
 	unix_session
 
-$(CLOSED_BUILD)SUBDIRS += \
-	$(CLOSED)/lib/pam_modules/smartcard
-
 all :=		TARGET= all
 catalog :=	TARGET= _msg
 clean :=	TARGET= clean
--- a/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelKeys.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelKeys.c	Mon Oct 05 09:17:26 2009 -0400
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <strings.h>
 #include <errno.h>
 #include <ecc_impl.h>
@@ -1983,18 +1981,19 @@
 get_base_key_attributes(kernel_object_t *base_key, crypto_key_t *key_by_value)
 {
 	CK_ATTRIBUTE tmp;
-	crypto_object_attribute_t *attrs;
+	crypto_object_attribute_t *attrs = NULL;
 	biginteger_t *big;
-	int rv;
+	int i, count = 0, rv;
 
 	switch (base_key->key_type) {
 	case CKK_EC:
-		attrs = malloc(2 * sizeof (crypto_object_attribute_t));
+		count = 2;
+		attrs = malloc(count * sizeof (crypto_object_attribute_t));
 		if (attrs == NULL) {
 			rv = CKR_HOST_MEMORY;
 			goto out;
 		}
-		bzero(attrs, 2 * sizeof (crypto_object_attribute_t));
+		bzero(attrs, count * sizeof (crypto_object_attribute_t));
 
 		(void) pthread_mutex_lock(&base_key->object_mutex);
 
@@ -2029,6 +2028,7 @@
 		}
 		rv = kernel_get_attribute(base_key, &tmp);
 		if (rv != CKR_OK) {
+			free(tmp.pValue);
 			goto out;
 		}
 		attrs[0].oa_type = tmp.type;
@@ -2058,6 +2058,7 @@
 		}
 		rv = kernel_get_attribute(base_key, &tmp);
 		if (rv != CKR_OK) {
+			free(tmp.pValue);
 			goto out;
 		}
 		attrs[1].oa_type = tmp.type;
@@ -2068,12 +2069,13 @@
 		break;
 
 	case CKK_DH:
-		attrs = malloc(3 * sizeof (crypto_object_attribute_t));
+		count = 3;
+		attrs = malloc(count * sizeof (crypto_object_attribute_t));
 		if (attrs == NULL) {
 			rv = CKR_HOST_MEMORY;
 			goto out;
 		}
-		bzero(attrs, 3 * sizeof (crypto_object_attribute_t));
+		bzero(attrs, count * sizeof (crypto_object_attribute_t));
 
 		(void) pthread_mutex_lock(&base_key->object_mutex);
 
@@ -2102,6 +2104,7 @@
 		}
 		rv = kernel_get_attribute(base_key, &tmp);
 		if (rv != CKR_OK) {
+			free(tmp.pValue);
 			goto out;
 		}
 		attrs[0].oa_type = tmp.type;
@@ -2124,13 +2127,14 @@
 		}
 		rv = kernel_get_attribute(base_key, &tmp);
 		if (rv != CKR_OK) {
+			free(tmp.pValue);
 			goto out;
 		}
 		attrs[1].oa_type = tmp.type;
 		attrs[1].oa_value = tmp.pValue;
 		attrs[1].oa_value_len = tmp.ulValueLen;
 
-		big = OBJ_PRI_EC_VALUE(base_key);
+		big = OBJ_PRI_DH_VALUE(base_key);
 		tmp.type = CKA_VALUE;
 
 		tmp.ulValueLen = big->big_value_len;
@@ -2141,6 +2145,7 @@
 		}
 		rv = kernel_get_attribute(base_key, &tmp);
 		if (rv != CKR_OK) {
+			free(tmp.pValue);
 			goto out;
 		}
 		attrs[2].oa_type = tmp.type;
@@ -2159,6 +2164,13 @@
 
 out:
 	(void) pthread_mutex_unlock(&base_key->object_mutex);
+	if (attrs != NULL) {
+		for (i = 0; i < count; i++) {
+			if (attrs[i].oa_value != NULL)
+				free(attrs[i].oa_value);
+		}
+		free(attrs);
+	}
 	return (rv);
 }
 
@@ -2226,12 +2238,14 @@
 	if (rv != CKR_OK) {
 		goto failed_exit;
 	}
+	obj_ndk.ndk_in_count = attr_count - 1;
 
 	rv = process_object_attributes(&newTemplate[attr_count - 1],
 	    1, &obj_ndk.ndk_out_attributes, &is_token_obj);
 	if (rv != CKR_OK) {
 		goto failed_exit;
 	}
+	obj_ndk.ndk_out_count = 1;
 
 	/* Cannot create a token object with a READ-ONLY session. */
 	if (is_token_obj && session_p->ses_RO) {
@@ -2253,8 +2267,6 @@
 	}
 
 	obj_ndk.ndk_base_key.ck_format = CRYPTO_KEY_ATTR_LIST;
-	obj_ndk.ndk_in_count = attr_count - 1;
-	obj_ndk.ndk_out_count = 1;
 
 	while ((r = ioctl(kernel_fd, CRYPTO_NOSTORE_DERIVE_KEY,
 	    &obj_ndk)) < 0) {
--- a/usr/src/pkgdefs/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -62,8 +62,6 @@
 	SUNWidn.u \
 	SUNWifp  \
 	SUNWifph \
-	SUNWjcom \
-	SUNWjib \
 	SUNWkvm.c  \
 	SUNWkvm.d  \
 	SUNWkvm.m  \
@@ -84,17 +82,17 @@
 	SUNWiopc.u \
 	SUNWiopc.v \
 	SUNWpdu	  \
+	SUNWpmcsr  \
+	SUNWpmcsu  \
 	SUNWpstl.u \
 	SUNWqfed \
 	SUNWqus \
 	SUNWsckmr \
 	SUNWsckmu.u \
 	SUNWsckm.u \
-	SUNWscmhdlr \
 	SUNWsfdr.u \
 	SUNWsfdrr.u \
 	SUNWssad  \
-	SUNWstc.u \
 	SUNWus.u \
 	SUNWust1.v \
 	SUNWust2.v \
@@ -149,6 +147,8 @@
 	SUNWonmtst.i \
 	SUNWos86r  \
 	SUNWparted  \
+	SUNWpmcsr \
+	SUNWpmcsu \
 	SUNWpsdcr  \
 	SUNWpsdir  \
 	SUNWpsh \
@@ -323,6 +323,7 @@
 	SUNWixgbe \
 	SUNWkrbr \
 	SUNWkrbu \
+	SUNWlatencytop \
 	SUNWldskint \
 	SUNWlibsasl \
 	SUNWllc \
@@ -367,10 +368,6 @@
 	SUNWnisu  \
 	SUNWnwamintr \
 	SUNWnwamintu \
-	SUNWocf \
-	SUNWocfd \
-	SUNWocfh \
-	SUNWocfr \
 	SUNWonfmes \
 	SUNWonzfsr \
 	SUNWonzfs \
@@ -379,7 +376,6 @@
 	SUNWypu \
 	SUNWpacketh \
 	SUNWpacketu \
-	SUNWpamsc \
 	SUNWpapi \
 	SUNWpcan \
 	SUNWpcelx \
--- a/usr/src/pkgdefs/SUNW0on/prototype_com	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/SUNW0on/prototype_com	Mon Oct 05 09:17:26 2009 -0400
@@ -410,11 +410,3 @@
 f none usr/lib/help/profiles/locale/RtMMSAdmin.html 444 root bin
 f none usr/lib/help/profiles/locale/RtMMSOper.html 444 root bin
 f none usr/lib/help/profiles/locale/RtMMSUser.html 444 root bin
-#
-#
-# OCF Messages
-#
-f none usr/lib/locale/C/LC_MESSAGES/libsmartcard.msg 644 root sys
-d none usr/share/lib/locale/com/sun/opencard 755 root bin
-d none usr/share/lib/locale/com/sun/opencard/cmd 755 root bin
-f none usr/share/lib/locale/com/sun/opencard/cmd/OCFMessages.properties 644 root sys
--- a/usr/src/pkgdefs/SUNWcsr/prototype_com	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/SUNWcsr/prototype_com	Mon Oct 05 09:17:26 2009 -0400
@@ -394,6 +394,7 @@
 f none lib/svc/share/net_include.sh 0444 root bin
 f none lib/svc/share/routing_include.sh 0444 root bin
 f none lib/svc/share/smf_include.sh 0444 root bin
+f none lib/svc/share/mfsthistory 0444 root bin
 f none lib/svc/share/manifest_cleanup.ksh 0444 root bin
 d none mnt 755 root sys
 d none opt 755 root sys
--- a/usr/src/pkgdefs/SUNWhea/prototype_com	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/SUNWhea/prototype_com	Mon Oct 05 09:17:26 2009 -0400
@@ -744,6 +744,8 @@
 f none usr/include/sys/cyclic_impl.h 644 root bin
 f none usr/include/sys/dacf.h 644 root bin
 f none usr/include/sys/dacf_impl.h 644 root bin
+f none usr/include/sys/damap.h 644 root bin
+f none usr/include/sys/damap_impl.h 644 root bin
 f none usr/include/sys/dc_ki.h 644 root bin
 f none usr/include/sys/ddi.h 644 root bin
 f none usr/include/sys/ddifm.h 644 root bin
--- a/usr/src/pkgdefs/SUNWhea/prototype_sparc	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/SUNWhea/prototype_sparc	Mon Oct 05 09:17:26 2009 -0400
@@ -182,7 +182,6 @@
 f none usr/platform/sun4u/include/sys/i2c/misc/i2c_svc.h 644 root bin
 f none usr/platform/sun4u/include/sys/i2c/clients/max1617.h 644 root bin
 f none usr/platform/sun4u/include/sys/i2c/clients/i2c_client.h 644 root bin
-f none usr/platform/sun4u/include/sys/i2c/clients/scmi2c.h 644 root bin
 f none usr/platform/sun4u/include/sys/i2c/clients/hpc3130.h 644 root bin
 f none usr/platform/sun4u/include/sys/i2c/clients/lm75.h 644 root bin
 f none usr/platform/sun4u/include/sys/i2c/clients/pcf8591.h 644 root bin
--- a/usr/src/pkgdefs/SUNWintgige/postinstall	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/SUNWintgige/postinstall	Mon Oct 05 09:17:26 2009 -0400
@@ -208,8 +208,15 @@
 	"pci8086,10de"
 	"pci8086,10df"
 	"pci8086,10e5"
+	"pci8086,10ea"
+	"pci8086,10eb"
+	"pci8086,10ef"
+	"pci8086,10f0"
 	"pci8086,10f5"
+	"pci8086,10f6"
+	"pci8086,150c"
 	"pci8086,294c"
+	"pci8086,f0fe"
 	"pciex8086,1049"
 	"pciex8086,104a"
 	"pciex8086,104b"
@@ -250,6 +257,13 @@
 	"pciex8086,10de"
 	"pciex8086,10df"
 	"pciex8086,10e5"
+	"pciex8086,10ea"
+	"pciex8086,10eb"
+	"pciex8086,10ef"
+	"pciex8086,10f0"
 	"pciex8086,10f5"
-	"pciex8086,294c"' \
+	"pciex8086,10f6"
+	"pciex8086,150c"
+	"pciex8086,294c"
+	"pciex8086,f0fe"' \
 	-m '* 0666 root sys' e1000g
--- a/usr/src/pkgdefs/SUNWjcom/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-include	../Makefile.com
-
-.KEEP_STATE:
-
-all: $(FILES) depend
-install: all pkg
-
-include ../Makefile.targ
--- a/usr/src/pkgdefs/SUNWjcom/depend	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-#
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# This package information file defines software dependencies associated
-# with the pkg.  You can define three types of pkg dependencies with this file:
-#	 P indicates a prerequisite for installation
-#	 I indicates an incompatible package
-#	 R indicates a reverse dependency
-# <pkg.abbr> see pkginfo(4), PKG parameter
-# <name> see pkginfo(4), NAME parameter
-# <version> see pkginfo(4), VERSION parameter
-# <arch> see pkginfo(4), ARCH parameter
-# <type> <pkg.abbr> <name>
-# 	(<arch>)<version>
-# 	(<arch>)<version>
-# 	...
-# <type> <pkg.abbr> <name>
-# ...
-
-P SUNWcar	Core Architecture, (Root)
-P SUNWcakr	Core Solaris Kernel Architecture (Root)
-P SUNWkvm	Core Architecture, (Kvm)
-P SUNWcsr	Core Solaris, (Root)
-P SUNWckr	Core Solaris Kernel (Root)
-P SUNWcnetr	Core Solaris Network Infrastructure (Root)
-P SUNWcsu	Core Solaris, (Usr)
-P SUNWcsd	Core Solaris Devices
-P SUNWcsl	Core Solaris Libraries
-P SUNWcslr	Core Solaris Libraries (Root)
-P SUNWocf	Opencard Card Framework
--- a/usr/src/pkgdefs/SUNWjcom/pkginfo.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-
-#
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-#
-# This required package information file describes characteristics of the
-# package, such as package abbreviation, full package name, package version,
-# and package architecture.
-#
-PKG="SUNWjcom"
-NAME="Java Communications API"
-ARCH="ISA"
-SUNW_PRODNAME="SunOS"
-SUNW_PKGTYPE="usr"
-MAXINST="1000"
-CATEGORY="system"
-DESC="Java Communications API for Smart Card support - Java and Native Code"
-VENDOR="Sun Microsystems, Inc."
-HOTLINE="Please contact your local service provider"
-EMAIL=""
-CLASSES="none"
-BASEDIR=/
-SUNW_PKGVERS="1.0"
-SUNW_PKG_ALLZONES="true"
-SUNW_PKG_HOLLOW="false"
-SUNW_PKG_THISZONE="false"
-#VSTOCK="<reserved by Release Engineering for package part #>"
-#ISTATES="<developer defined>"
-#RSTATES='<developer defined>'
-#ULIMIT="<developer defined>"
-#ORDER="<developer defined>"
-#INTONLY="<developer defined>"
-VERSION="ONVERS,REV=0.0.0"
-#PSTAMP=""
-SUNW_PRODVERS="RELEASE/VERSION"
--- a/usr/src/pkgdefs/SUNWjcom/prototype_com	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-# packaging files
-i pkginfo
-i copyright
-i depend
-#
-# source locations relative to the prototype file
-#
-# SUNWjcom
-#
-# javax.comm package
-#
-# javax.comm Native Library Files
-#
-d none usr 0755 root sys
-d none usr/lib 0755 root bin
-#
-# javax.comm jar Files
-#
-d none usr/share 755 root sys
-d none usr/share/lib 755 root sys
--- a/usr/src/pkgdefs/SUNWjcom/prototype_i386	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-#
-# List files which are I386 specific here
-#
-# source locations relative to the prototype file
-#
-#
-# SUNWjcom
-#
--- a/usr/src/pkgdefs/SUNWjcom/prototype_sparc	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-#
-# List files which are SPARC specific here
-#
-# source locations relative to the prototype file
-#
-#
-# SUNWjcom
-#
-#
-# javax.comm package
-#
-# javax.comm Native Library Files
-#
-d none usr/lib/smartcard 0755 root bin
-f none usr/lib/smartcard/libSolarisSerialParallel.so.1 0755 root bin
-s none usr/lib/smartcard/libSolarisSerialParallel.so=libSolarisSerialParallel.so.1
-#
-# javax.comm jar Files
-#
-d none usr/share/lib/smartcard 0755 root sys
-f none usr/share/lib/smartcard/comm.jar 0644 root sys
--- a/usr/src/pkgdefs/SUNWjib/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-include	../Makefile.com
-
-.KEEP_STATE:
-
-all: $(FILES) depend
-install: all pkg
-
-include ../Makefile.targ
--- a/usr/src/pkgdefs/SUNWjib/depend	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-#
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# This package information file defines software dependencies associated
-# with the pkg.  You can define three types of pkg dependencies with this file:
-#        P indicates a prerequisite for installation
-#        I indicates an incompatible package
-#        R indicates a reverse dependency
-# <pkg.abbr> see pkginfo(4), PKG parameter
-# <name> see pkginfo(4), NAME parameter
-# <version> see pkginfo(4), VERSION parameter
-# <arch> see pkginfo(4), ARCH parameter
-# <type> <pkg.abbr> <name>
-#       (<arch>)<version>
-#       (<arch>)<version>
-#       ...
-# <type> <pkg.abbr> <name>
-# ...
-
-P SUNWcar	Core Architecture, (Root)
-P SUNWcakr	Core Solaris Kernel Architecture (Root)
-P SUNWkvm	Core Architecture, (Kvm)
-P SUNWcsr	Core Solaris, (Root)
-P SUNWckr	Core Solaris Kernel (Root)
-P SUNWcnetr	Core Solaris Network Infrastructure (Root)
-P SUNWcsu	Core Solaris, (Usr)
-P SUNWcsd	Core Solaris Devices
-P SUNWcsl	Core Solaris Libraries
-P SUNWcslr	Core Solaris Libraries (Root)
-P SUNWjcom	javax.comm package
--- a/usr/src/pkgdefs/SUNWjib/pkginfo.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-
-#
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-#
-# This required package information file describes characteristics of the
-# package, such as package abbreviation, full package name, package version,
-# and package architecture.
-#
-PKG="SUNWjib"
-NAME="iButton OCF CT Driver"
-ARCH="ISA"
-SUNW_PRODNAME="SunOS"
-SUNW_PKGTYPE="usr"
-MAXINST="1000"
-CATEGORY="system"
-DESC="Dallas Semiconductor serial iButton OCF Card Terminal Driver"
-VENDOR="Sun Microsystems, Inc."
-HOTLINE="Please contact your local service provider"
-EMAIL=""
-CLASSES="none"
-BASEDIR=/
-SUNW_PKGVERS="1.0"
-SUNW_PKG_ALLZONES="true"
-SUNW_PKG_HOLLOW="false"
-SUNW_PKG_THISZONE="false"
-#VSTOCK="<reserved by Release Engineering for package part #>"
-#ISTATES="<developer defined>"
-#RSTATES='<developer defined>'
-#ULIMIT="<developer defined>"
-#ORDER="<developer defined>"
-#INTONLY="<developer defined>"
-VERSION="ONVERS,REV=0.0.0"
-#PSTAMP=""
-SUNW_PRODVERS="RELEASE/VERSION"
--- a/usr/src/pkgdefs/SUNWjib/prototype_com	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-# packaging files
-i pkginfo
-i copyright
-i depend
-#
-# source locations relative to the prototype file
-#
-# SUNWjib
-#
-#
-# Dallas Semiconductor iButton Card Terminal Driver Files
-#
-d none usr 0755 root sys
-d none usr/share 755 root sys
-d none usr/share/lib 755 root sys
--- a/usr/src/pkgdefs/SUNWjib/prototype_i386	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-#
-# List files which are I386 specific here
-#
-# source locations relative to the prototype file
-#
-#
-# SUNWjib
-#
--- a/usr/src/pkgdefs/SUNWjib/prototype_sparc	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-#
-# List files which are SPARC specific here
-#
-# source locations relative to the prototype file
-#
-#
-# SUNWjib
-#
-#
-#
-# Dallas Semiconductor iButton Card Terminal Driver Files
-#
-d none usr/share/lib/smartcard 0755 root sys
-f none usr/share/lib/smartcard/ibutton.jar 0644 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWlatencytop/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all: $(FILES) depend
+install: all pkg
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWlatencytop/depend	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+P SUNWcsu	Core Solaris, (Usr)
+P SUNWcslr	Core Solaris Libraries (Root)
+P SUNWdtrc	DTrace Clients 
+P SUNWdtrp	DTrace Providers
+P SUNWGlib	GLIB - Library of useful routines for C programming
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWlatencytop/pkginfo.tmpl	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWlatencytop"
+NAME="LatencyTOP tool"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+CATEGORY="system"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+DESC="LatencyTOP tool"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="usr"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+MAXINST="1000"
+CLASSES="none"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="false"
+SUNW_PKG_THISZONE="false"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWlatencytop/prototype_com	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+#
+# source locations relative to the prototype file
+#
+# SUNWlatencytop
+#
+d none usr 0755 root sys
+d none usr/bin 0755 root bin
+l none usr/bin/latencytop=../../usr/lib/isaexec
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWlatencytop/prototype_i386	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,54 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are I386 specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWlatencytop
+#
+d none usr/bin/i86 755 root bin
+f none usr/bin/i86/latencytop 555 root bin
+d none usr/bin/amd64 755 root bin
+f none usr/bin/amd64/latencytop 555 root bin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWlatencytop/prototype_sparc	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008-2009, Intel Corporation.
+# All Rights Reserved.
+#
+
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWlatencytop
+#
+d none usr/bin/sparcv9 755 root bin
+f none usr/bin/sparcv9/latencytop 555 root bin
--- a/usr/src/pkgdefs/SUNWocf/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-include	../Makefile.com
-
-TMPLFILES += preremove
-
-$(CLOSED_BUILD)LICENSEFILES += \
-	../../../closed/lib/smartcard/ocfserv/opencard/core/event/THIRDPARTYLICENSE
-
-.KEEP_STATE:
-
-all: $(FILES) depend
-install: all pkg
-
-include ../Makefile.targ
-include	../Makefile.prtarg
--- a/usr/src/pkgdefs/SUNWocf/depend	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-#
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# This package information file defines software dependencies associated
-# with the pkg.  You can define three types of pkg dependencies with this file:
-#	 P indicates a prerequisite for installation
-#	 I indicates an incompatible package
-#	 R indicates a reverse dependency
-# <pkg.abbr> see pkginfo(4), PKG parameter
-# <name> see pkginfo(4), NAME parameter
-# <version> see pkginfo(4), VERSION parameter
-# <arch> see pkginfo(4), ARCH parameter
-# <type> <pkg.abbr> <name>
-# 	(<arch>)<version>
-# 	(<arch>)<version>
-# 	...
-# <type> <pkg.abbr> <name>
-# ...
-#
-
-P SUNWcar       Core Architecture, (Root)
-P SUNWcakr	Core Solaris Kernel Architecture (Root)
-P SUNWkvm       Core Architecture, (Kvm)
-P SUNWcsr       Core Solaris, (Root)
-P SUNWckr	Core Solaris Kernel (Root)
-P SUNWcnetr	Core Solaris Network Infrastructure (Root)
-P SUNWcsu       Core Solaris, (Usr)
-P SUNWcsd       Core Solaris Devices
-P SUNWcsl	Core Solaris Libraries
-P SUNWcslr	Core Solaris Libraries (Root)
-P SUNWj5rt	JDK 1.5 runtime environment
-P SUNWocfr	Configuration files for Open Card Framework
--- a/usr/src/pkgdefs/SUNWocf/pkginfo.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-
-#
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-#
-# This required package information file describes characteristics of the
-# package, such as package abbreviation, full package name, package version,
-# and package architecture.
-#
-PKG="SUNWocf"
-NAME="Open Card Framework"
-ARCH="ISA"
-SUNW_PRODNAME="SunOS"
-SUNW_PKGTYPE="usr"
-MAXINST="1000"
-CATEGORY="system"
-DESC="Open Card Framework - core libraries and utilities"
-VENDOR="Sun Microsystems, Inc."
-HOTLINE="Please contact your local service provider"
-EMAIL=""
-CLASSES="none"
-BASEDIR=/
-SUNW_PKGVERS="1.0"
-SUNW_PKG_ALLZONES="true"
-SUNW_PKG_HOLLOW="false"
-SUNW_PKG_THISZONE="false"
-#VSTOCK="<reserved by Release Engineering for package part #>"
-#ISTATES="<developer defined>"
-#RSTATES='<developer defined>'
-#ULIMIT="<developer defined>"
-#ORDER="<developer defined>"
-#INTONLY="<developer defined>"
-VERSION="ONVERS,REV=0.0.0"
-#PSTAMP=""
-SUNW_PRODVERS="RELEASE/VERSION"
--- a/usr/src/pkgdefs/SUNWocf/preremove.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-#!/bin/sh
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-include inetd_remove
-
-# check if ocfserv daemon is still running
-
-inetd_check network/rpc/ocfserv:default
-if [ $? -ne 0 ]; then
-	exit 1
-fi
-exit 0
--- a/usr/src/pkgdefs/SUNWocf/prototype_com	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-# packaging files
-i pkginfo
-i copyright
-i depend
-i preremove
-#
-# source locations relative to the prototype file
-#
-# SUNWocf
-#
-# OCF Server
-#
-d none usr 0755 root sys
-d none usr/sbin 0755 root bin
-f none usr/sbin/ocfserv 0555 root bin
-#
-# OCF Commands
-#
-d none usr/bin 0755 root bin
-f none usr/bin/smartcard 0555 root bin
-#
-# OCF Library Files
-#
-d none usr/lib 0755 root bin
-f none usr/lib/libsmartcard.so.1 0755 root bin
-s none usr/lib/libsmartcard.so=libsmartcard.so.1
-f none usr/lib/llib-lsmartcard 644 root bin
-f none usr/lib/llib-lsmartcard.ln 644 root bin
-d none usr/lib/smartcard 0755 root bin
-f none usr/lib/smartcard/libIFDNative.so.1 0755 root bin
-s none usr/lib/smartcard/libIFDNative.so=libIFDNative.so.1
-#
-# OCF Java Library Files
-#
-d none usr/share 755 root sys
-d none usr/share/lib 755 root sys
-d none usr/share/lib/smartcard 0755 root sys
-f none usr/share/lib/smartcard/ocf.jar 0644 root sys
-f none usr/share/lib/smartcard/smartcard.jar 0644 root sys
-f none usr/share/lib/smartcard/commands.jar 0644 root sys
-f none usr/share/lib/smartcard/IFDCardTerminal.jar 0644 root sys
-f none usr/share/lib/smartcard/SolarisAuthApplet.capx 0644 root sys
-f none usr/share/lib/smartcard/SolarisAuthApplet.jib 0644 root sys
-f none usr/share/lib/smartcard/SolarisAuthApplet.bin 0644 root sys
-f none usr/share/lib/smartcard/SolarisAuthApplet.bin.sig 0644 root sys
-f none usr/share/lib/smartcard/CDESmartCardAdmin 0744 root sys
--- a/usr/src/pkgdefs/SUNWocf/prototype_i386	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-#
-# List files which are I386 specific here
-#
-# source locations relative to the prototype file
-#
-#
-# SUNWocf
-#
-d none usr/lib/amd64 0755 root bin
-f none usr/lib/amd64/libsmartcard.so.1 0755 root bin
-s none usr/lib/amd64/libsmartcard.so=libsmartcard.so.1
-f none usr/lib/amd64/llib-lsmartcard.ln 644 root bin
--- a/usr/src/pkgdefs/SUNWocf/prototype_sparc	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-# List files which are SPARC specific here
-#
-# source locations relative to the prototype file
-#
-# SUNWocf
-d none usr/lib/sparcv9 0755 root bin
-f none usr/lib/sparcv9/libsmartcard.so.1 0755 root bin
-s none usr/lib/sparcv9/libsmartcard.so=libsmartcard.so.1
-f none usr/lib/sparcv9/llib-lsmartcard.ln 644 root bin
--- a/usr/src/pkgdefs/SUNWocfd/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-include	../Makefile.com
-
-.KEEP_STATE:
-
-all: $(FILES)
-install: all pkg
-
-include ../Makefile.targ
-include	../Makefile.prtarg
--- a/usr/src/pkgdefs/SUNWocfd/pkginfo.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-
-#
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-#
-# This required package information file describes characteristics of the
-# package, such as package abbreviation, full package name, package version,
-# and package architecture.
-#
-PKG="SUNWocfd"
-NAME="Open Card Framework"
-ARCH="ISA"
-SUNW_PRODNAME="SunOS"
-SUNW_PKGTYPE="usr"
-MAXINST="1000"
-CATEGORY="system"
-DESC="Open Card Framework - Javadoc for SCF Java APIs"
-VENDOR="Sun Microsystems, Inc."
-HOTLINE="Please contact your local service provider"
-EMAIL=""
-CLASSES="none"
-BASEDIR=/
-SUNW_PKGVERS="1.0"
-SUNW_PKG_ALLZONES="true"
-SUNW_PKG_HOLLOW="false"
-SUNW_PKG_THISZONE="false"
-#VSTOCK="<reserved by Release Engineering for package part #>"
-#ISTATES="<developer defined>"
-#RSTATES='<developer defined>'
-#ULIMIT="<developer defined>"
-#ORDER="<developer defined>"
-#INTONLY="<developer defined>"
-VERSION="ONVERS,REV=0.0.0"
-#PSTAMP=""
-SUNW_PRODVERS="RELEASE/VERSION"
--- a/usr/src/pkgdefs/SUNWocfd/prototype_com	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-# packaging files
-i pkginfo
-i copyright
-#
-# source locations relative to the prototype file
-#
-# SUNWocfd
-#
-# Javadoc for SCF Java API
-#
-d none usr 0755 root sys
-d none usr/share 0755 root sys
-d none usr/share/javadoc 0755 root bin
-d none usr/share/javadoc/smartcard 0755 root bin
-f none usr/share/javadoc/smartcard/allclasses-frame.html 0644 root bin
-f none usr/share/javadoc/smartcard/allclasses-noframe.html 0644 root bin
-d none usr/share/javadoc/smartcard/com 0755 root bin
-d none usr/share/javadoc/smartcard/com/sun 0755 root bin
-d none usr/share/javadoc/smartcard/com/sun/smartcard 0755 root bin
-d none usr/share/javadoc/smartcard/com/sun/smartcard/scf 0755 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/package-frame.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/package-summary.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/package-tree.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/TerminalEventListener.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/Card.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/Session.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/Terminal.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/CardLockedException.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/CardRemovedException.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/CommException.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/DoubleLockException.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/InternalException.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/InvalidStateException.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/NoCardException.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/NoSuchTerminalException.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/SCFException.html 0644 root bin
-f none usr/share/javadoc/smartcard/com/sun/smartcard/scf/TimeoutException.html 0644 root bin
-f none usr/share/javadoc/smartcard/constant-values.html 0644 root bin
-f none usr/share/javadoc/smartcard/deprecated-list.html 0644 root bin
-f none usr/share/javadoc/smartcard/help-doc.html 0644 root bin
-f none usr/share/javadoc/smartcard/index-all.html 0644 root bin
-f none usr/share/javadoc/smartcard/index.html 0644 root bin
-f none usr/share/javadoc/smartcard/overview-tree.html 0644 root bin
-f none usr/share/javadoc/smartcard/package-list 0644 root bin
-d none usr/share/javadoc/smartcard/resources 0755 root bin
-f none usr/share/javadoc/smartcard/resources/inherit.gif 0644 root bin
-f none usr/share/javadoc/smartcard/serialized-form.html 0644 root bin
-f none usr/share/javadoc/smartcard/stylesheet.css 0644 root bin
--- a/usr/src/pkgdefs/SUNWocfd/prototype_i386	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-#
-# List files which are I386 specific here
-#
-# source locations relative to the prototype file
--- a/usr/src/pkgdefs/SUNWocfd/prototype_sparc	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-# List files which are SPARC specific here
-#
-# source locations relative to the prototype file
--- a/usr/src/pkgdefs/SUNWocfh/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-include	../Makefile.com
-
-.KEEP_STATE:
-
-LICENSEFILES += copyright.in
-CDDL=
-
-all: $(FILES) depend
-install: all pkg
-
-include ../Makefile.targ
-
--- a/usr/src/pkgdefs/SUNWocfh/copyright.in	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-Content of the following file and man pages are also governed by
-the LICENSE file from "MUSCLE PC/SC IFD Driver Developer Kit". 
-
-ifdhandler.h
-IFDHCloseChannel(3SMARTCARD)
-IFDHControl(3SMARTCARD)
-IFDHCreateChannel(3SMARTCARD)
-IFDHCreateChannelByName(3SMARTCARD)
-IFDHGetCapabilities(3SMARTCARD)
-IFDHICCPresence(3SMARTCARD)
-IFDHPowerICC(3SMARTCARD)
-IFDHSetCapabilities(3SMARTCARD)
-IFDHSetProtocolParameters(3SMARTCARD)
-IFDHTransmitToICC(3SMARTCARD)
-
-
-LICENSE file from "MUSCLE PC/SC IFD Driver Developer Kit"
------------------------------------------------------------
-
-Copyright (c) 2000 David Corcoran <corcoran@linuxnet.com>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. All advertising materials mentioning features or use of this software
-   must display the following acknowledgement:
-     This product includes software developed by: 
-      David Corcoran <corcoran@linuxnet.com>
-      http://www.linuxnet.com (MUSCLE)
-4. The name of the author may not be used to endorse or promote products
-   derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- a/usr/src/pkgdefs/SUNWocfh/depend	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-#
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-# This package information file defines software dependencies associated
-# with the pkg.  You can define three types of pkg dependencies with this file:
-#	 P indicates a prerequisite for installation
-#	 I indicates an incompatible package
-#	 R indicates a reverse dependency
-# <pkg.abbr> see pkginfo(4), PKG parameter
-# <name> see pkginfo(4), NAME parameter
-# <version> see pkginfo(4), VERSION parameter
-# <arch> see pkginfo(4), ARCH parameter
-# <type> <pkg.abbr> <name>
-# 	(<arch>)<version>
-# 	(<arch>)<version>
-# 	...
-# <type> <pkg.abbr> <name>
-# ...
-
-P SUNWocf	Open Card Framework
--- a/usr/src/pkgdefs/SUNWocfh/pkginfo.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-
-#
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-#
-# This required package information file describes characteristics of the
-# package, such as package abbreviation, full package name, package version,
-# and package architecture.
-#
-PKG="SUNWocfh"
-NAME="Open Card Framework header files"
-ARCH="ISA"
-SUNW_PRODNAME="SunOS"
-SUNW_PKGTYPE="usr"
-MAXINST="1000"
-CATEGORY="system"
-DESC="Open Card Framework - header files"
-VENDOR="Sun Microsystems, Inc."
-HOTLINE="Please contact your local service provider"
-EMAIL=""
-CLASSES="none"
-BASEDIR=/
-SUNW_PKGVERS="1.0"
-SUNW_PKG_ALLZONES="true"
-SUNW_PKG_HOLLOW="false"
-SUNW_PKG_THISZONE="false"
-#VSTOCK="<reserved by Release Engineering for package part #>"
-#ISTATES="<developer defined>"
-#RSTATES='<developer defined>'
-#ULIMIT="<developer defined>"
-#ORDER="<developer defined>"
-#INTONLY="<developer defined>"
-VERSION="ONVERS,REV=0.0.0"
-#PSTAMP=""
-SUNW_PRODVERS="RELEASE/VERSION"
--- a/usr/src/pkgdefs/SUNWocfh/prototype_com	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-# packaging files
-i pkginfo
-i copyright
-i depend
-#
-# source locations relative to the prototype file
-#
-# SUNWocfh
-#
-# OCF Header Files
-#
-d none usr 0755 root sys
-d none usr/include 0755 root bin
-d none usr/include/smartcard 0755 root bin
-f none usr/include/smartcard/scf.h 0644 root bin
-f none usr/include/smartcard/ifdhandler.h 0644 root bin
--- a/usr/src/pkgdefs/SUNWocfh/prototype_i386	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-#
-# List files which are I386 specific here
-#
-# source locations relative to the prototype file
-#
-#
-# SUNWocf
-#
--- a/usr/src/pkgdefs/SUNWocfh/prototype_sparc	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999, 2001 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-# List files which are SPARC specific here
-#
-# source locations relative to the prototype file
-#
-# SUNWocf
--- a/usr/src/pkgdefs/SUNWocfr/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-include	../Makefile.com
-
-TMPLFILES += postinstall
-DATAFILES += depend i.preserve i.manifest r.manifest
-
-.KEEP_STATE:
-
-all: $(FILES)
-
-install: all pkg
-
-include ../Makefile.targ
-include	../Makefile.prtarg
--- a/usr/src/pkgdefs/SUNWocfr/pkginfo.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-
-#
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-#
-# This required package information file describes characteristics of the
-# package, such as package abbreviation, full package name, package version,
-# and package architecture.
-#
-PKG="SUNWocfr"
-NAME="Configuration files for Open Card Framework"
-ARCH="ISA"
-SUNW_PRODNAME="SunOS"
-SUNW_PKGTYPE="root"
-MAXINST="1000"
-CATEGORY="system"
-DESC="Open Card Framework - configuration files"
-VENDOR="Sun Microsystems, Inc."
-HOTLINE="Please contact your local service provider"
-EMAIL=""
-CLASSES="none preserve manifest"
-BASEDIR=/
-SUNW_PKGVERS="1.0"
-SUNW_PKG_ALLZONES="true"
-SUNW_PKG_HOLLOW="false"
-SUNW_PKG_THISZONE="false"
-#VSTOCK="<reserved by Release Engineering for package part #>"
-#ISTATES="<developer defined>"
-#RSTATES='<developer defined>'
-#ULIMIT="<developer defined>"
-#ORDER="<developer defined>"
-#INTONLY="<developer defined>"
-VERSION="ONVERS,REV=0.0.0"
-#PSTAMP=""
-SUNW_PRODVERS="RELEASE/VERSION"
--- a/usr/src/pkgdefs/SUNWocfr/postinstall.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,295 +0,0 @@
-#!/bin/sh
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-#
-# Function: getKeyValue
-#
-# Try to get key value pair from the given line.
-# The line to be parsed should conform to the format loadable
-# by the Java Properties object.  
-# 
-# The variables "key" and "value" will be set if successful.
-# 0 will be returned if a key/value pair is found. 
-# 1 will be returned otherwise.
-#
-getKeyValue() {
-
-	key=""
-	value=""
-
-	if [ $# != 1 ] ; then
-		#no argument to be parsed
-		return 1
-	fi
-
-	if [ "XX$1" = "XX" ] ; then
-		#empty line, nothing to be parsed
-		return 1
-	fi
-
-	#first, remove any leading and trailing blank spaces in the line
-	line=`echo "$1"|sed 's/^[ 	]*//'`
-
-	#Check to see if it is a comment line.  Lines starting
-	#with '#' or '!' are comment lines
-	echo "$line"|egrep "^[#!]" > /dev/null 2>& 1
-
-	if [ "XX$?" = "XX0" ] ; then
-		#It's a comment line
-		return 1
-	fi
-
-	#Check to make sure the line have some kind of delimiter
-	echo "$line"|egrep "[ 	=:]" > /dev/null 2>& 1
-	if [ "XX$?" != "XX0" ] ; then
-		#No delimiter in line, return error
-		return 1
-	fi
-
-	key=`echo "$line" | sed \
-		's/^\([^ 	=:]*\)[ 	=:].*$/\1/'`
-
-	if [ "XX$key" != "XX$line" ] ; then
-		value=`echo "$line" | sed \
-			's/^[^ 	=:]*[ 	]*[=:]\{0,1\}[ 	]*\(.*\)$/\1/'`
-		return 0
-	fi
-
-	#Should never get here, but just in case.
-	return 1
-}
-
-#
-# If desktop properties are found in opencard.properties
-# because this is an upgrade, move the properties from
-# /etc/smartcard/opencard.properties to /etc/smartcard/desktop.properties.
-#
-ETCSCDIR=$PKG_INSTALL_ROOT/etc/smartcard
-OP=$ETCSCDIR/opencard.properties
-DP=$ETCSCDIR/desktop.properties
-
-if [ ! -f $OP ] ; then
-	exit 1
-fi
-
-if [ ! -f $DP ] ; then
-	exit 1
-fi
-
-if [ ! -d /tmp ] ; then
-	exit 1
-fi
-
-TMP_OP=/tmp/op.$$
-TMP_OP2=/tmp/op2.$$
-RESULT_OP=/tmp/op3.$$
-RESULT_DP=/tmp/dp.$$
-TMP_DP=/tmp/dp2.$$
-
-#
-# First, join all the lines that have the '\' continuation character
-#
-nawk '{ \
-	while (match($0, /^([^\\]|\\.)*\\$/ )) { \
-		getline line \
-		sub(/.$/, line, $0) \
-	} \
-	print $0 \
-}' ${OP} > ${TMP_OP2}
-
-#
-# Get list of desktop properties to be processed
-#
-egrep "^[ 	]*ocf\.client\.dtsession\.root" ${TMP_OP2} > ${TMP_OP}
-
-cp ${DP} ${RESULT_DP}
-if [ "XX$?" != "XX0" ] ; then
-	#can't continue if failed to copy original desktop.properties file.
-	rm -f ${TMP_OP} ${TMP_OP2}
-	exit 1
-fi
-
-cp ${TMP_OP2} ${RESULT_OP}
-if [ "XX$?" != "XX0" ] ; then
-	#can't continue if failed to copy original opencard.properties file.
-	rm -f ${TMP_OP} ${TMP_OP2} ${RESULT_DP}
-	exit 1
-fi
-
-cat ${TMP_OP} | \
-while read opval ; do
-
-	getKeyValue "$opval"
-
-	if [ "XX$?" = "XX1" ] ; then
-		#Something is wrong with line.  Don't put it in new file
-		continue
-	fi
-
-	oldKey="$key"
-
-	#Successful in finding key-value pair.
-	#construct the new key.  Replace "ocf.client.dtsession.root" with the
-	#word "desktop"
-	key=`echo $key|sed 's/^ocf\.client\.dtsession\.root/desktop/'`
-
-	#check if key is in desktop.properties, if so, remove it before adding
-	egrep "^[ 	]*$key" ${RESULT_DP} > /dev/null 2>& 1
-	if [ "XX$?" = "XX0" ] ; then
-		#line exists in file, need to remove
-		egrep -v "^[ 	]*$key" ${RESULT_DP} > ${TMP_DP}
-		if [ "XX$?" = "XX0" ] ; then
-			cp ${TMP_DP} ${RESULT_DP}
-		fi
-	fi
-	echo "$key=$value" >> ${RESULT_DP}
-
-	# Remove opencard.properties entry that are 
-	# moved to desktop.properties file
-	egrep -v "^[ 	]*$oldKey" ${RESULT_OP} > ${TMP_OP2}
-	if [ "XX$?" = "XX0" ] ; then
-		cp ${TMP_OP2} ${RESULT_OP}
-	fi
-done
-
-#Only copy the files if they have been modified.  Having the temp
-#desktop.properties file means something has been modified.
-if [ -f ${TMP_DP} ] ; then
-	#Remove any dtlogin entries from opencard.properties
-	egrep -v "^[ 	]*ocf\.client\.dtlogin\.root" \
-		${RESULT_OP} > ${TMP_OP2}
-	if [ "XX$?" = "XX0" ] ; then
-		cp ${TMP_OP2} ${RESULT_OP}
-	fi
-
-	cp ${RESULT_DP} ${DP}
-	cp ${RESULT_OP} ${OP}
-
-	if [ "${PKG_INSTALL_ROOT:-/}" = "/" ] ; then
-	    # Note "svcadm restart" cannot be used due to 5079309.
-	    /usr/sbin/svcadm disable network/rpc/ocfserv
-	    /usr/sbin/svcadm enable network/rpc/ocfserv
-	fi
-fi
-
-# cleanup
-rm -f ${TMP_OP} ${TMP_OP2} ${RESULT_DP} ${RESULT_OP} ${TMP_DP}
-
-
-##
-## Update opencard.properties with IFD handler for internal reader
-##
-
-/bin/cp -p $OP $OP.noifd
-
-if [ $? -ne 0 ]; then
-    ## Failed to create a temporary file.
-    /bin/rm -f $OP.noifd
-    exit 1
-fi
-
-
-# Join the lines that have the '\' continuation character
-
-nawk '{ \
-	while (match($0, /^([^\\]|\\.)*\\$/ )) { \
-		getline line \
-		sub(/.$/, line, $0) \
-	} \
-	print $0 \
-}' $OP.noifd > $OP.noifd2
-
-if [ $? -ne 0 ]; then
-    /bin/rm -f $OP.noifd $OP.noifd2
-    exit 1
-fi
-
-
-
-# Check if there is an entry to be updated.
-
-OLD_TERMINAL=com.sun.opencard.terminal.scm.SCMI2c.SCMI2cCardTerminalFactory
-
-/bin/egrep -s "$OLD_TERMINAL" $OP.noifd2
-
-if [ $? -ne 0 ]; then
-    # Entry does not exist.  Do nothing.
-    /bin/rm -f $OP.noifd $OP.noifd2
-
-    exit 0
-fi
-
-
-
-# Copy to create a new file with the same file attributes as original.
-
-/bin/cp -p $OP $OP.new
-
-if [ $? -ne 0 ]; then
-    /bin/rm -f $OP.noifd $OP.noifd2 $OP.new
-    exit 1
-fi
-
-
-# Replace the card terminal factory and 
-# append the IFD handler name to the entry
-
-NEW_TERMINAL=com.sun.opencard.terminal.IFDCardTerminal.IFDCardTerminalFactory
-IFD_HANDLER=/usr/lib/smartcard/ifdh_scmi2c.so
-
-/bin/sed -e "s+$OLD_TERMINAL\([^ ]*\)+$NEW_TERMINAL\1|$IFD_HANDLER+" \
-	$OP.noifd2 > $OP.new
-
-if [ $? -ne 0 ]; then
-    /bin/rm -f $OP.noifd $OP.noifd2 $OP.new
-    exit 1
-fi
-
-
-# We are done with this temporary file.
-
-/bin/rm -f $OP.noifd2
-
-
-# Replace the configuration file with the new one.
-
-/bin/mv -f $OP.new $OP
-
-if [ $? -ne 0 ]; then
-    /bin/rm -f $OP.noifd $OP.new
-    exit 1
-fi
-
-if [ "${PKG_INSTALL_ROOT:-/}" = "/" ]; then
-    # Note "svcadm restart" cannot be used due to 5079309.
-    /usr/sbin/svcadm disable network/rpc/ocfserv
-    /usr/sbin/svcadm enable network/rpc/ocfserv
-fi
-
-exit 0
--- a/usr/src/pkgdefs/SUNWocfr/prototype_com	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-# packaging files
-i pkginfo
-i copyright
-i depend
-i postinstall
-i i.preserve
-i i.manifest
-i r.manifest
-#
-# source locations relative to the prototype file
-#
-# SUNWocfr
-#
-# Configuration files
-#
-d none etc 0755 root sys
-d none etc/smartcard 0755 root sys
-e preserve etc/smartcard/opencard.properties 0644 root sys
-e preserve etc/smartcard/ocf.classpath 0644 root sys
-e preserve etc/smartcard/.keys 0600 root sys
-e preserve etc/smartcard/desktop.properties 0644 root sys
-d none var 0755 root sys
-d none var/svc 0755 root sys
-d none var/svc/manifest 0755 root sys
-d none var/svc/manifest/network 0755 root sys
-d none var/svc/manifest/network/rpc 0755 root sys
-f manifest var/svc/manifest/network/rpc/ocfserv.xml 0444 root sys
--- a/usr/src/pkgdefs/SUNWocfr/prototype_i386	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-#
-# List files which are I386 specific here
-#
-# source locations relative to the prototype file
-#
-#
-# SUNWocfr
-#
--- a/usr/src/pkgdefs/SUNWocfr/prototype_sparc	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999, 2001 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-#
-# List files which are SPARC specific here
-#
-# source locations relative to the prototype file
--- a/usr/src/pkgdefs/SUNWpacketh/pkginfo.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/SUNWpacketh/pkginfo.tmpl	Mon Oct 05 09:17:26 2009 -0400
@@ -45,7 +45,7 @@
 BASEDIR=/
 SUNW_PKGVERS="1.0"
 SUNW_PKG_ALLZONES="true"
-SUNW_PKG_HOLLOW="true"
+SUNW_PKG_HOLLOW="false"
 SUNW_PKG_THISZONE="false"
 #VSTOCK="<reserved by Release Engineering for package part #>"
 #ISTATES="<developer defined>"
--- a/usr/src/pkgdefs/SUNWpacketu/pkginfo.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/SUNWpacketu/pkginfo.tmpl	Mon Oct 05 09:17:26 2009 -0400
@@ -34,18 +34,18 @@
 VERSION="ONVERS,REV=0.0.0"
 SUNW_PRODNAME="SunOS"
 SUNW_PRODVERS="RELEASE/VERSION"
-SUNW_PKGTYPE="root"
+SUNW_PKGTYPE="usr"
 MAXINST="1000"
 CATEGORY="system"
 DESC="BPF/PF_PACKET kernel packet modules"
 VENDOR="Sun Microsystems, Inc."
 HOTLINE="Please contact your local service provider"
 EMAIL=""
-CLASSES="none preserve manifest"
+CLASSES="none preserve"
 BASEDIR=/
 SUNW_PKGVERS="1.0"
 SUNW_PKG_ALLZONES="true"
-SUNW_PKG_HOLLOW="false"
+SUNW_PKG_HOLLOW="true"
 SUNW_PKG_THISZONE="false"
 #VSTOCK="<reserved by Release Engineering for package part #>"
 #ISTATES="<developer defined>"
--- a/usr/src/pkgdefs/SUNWpamsc/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-#
-#	Makefile for SUNWpamsc package
-#
-
-include	../Makefile.com
-
-.KEEP_STATE:
-
-all: $(FILES) depend
-install: all pkg
-
-include ../Makefile.targ
--- a/usr/src/pkgdefs/SUNWpamsc/depend	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-#
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-# This package information file defines software dependencies associated
-# with the pkg.  You can define three types of pkg dependencies with this file:
-#	 P indicates a prerequisite for installation
-#	 I indicates an incompatible package
-#	 R indicates a reverse dependency
-# <pkg.abbr> see pkginfo(4), PKG parameter
-# <name> see pkginfo(4), NAME parameter
-# <version> see pkginfo(4), VERSION parameter
-# <arch> see pkginfo(4), ARCH parameter
-# <type> <pkg.abbr> <name>
-# 	(<arch>)<version>
-# 	(<arch>)<version>
-# 	...
-# <type> <pkg.abbr> <name>
-# ...
-
-P SUNWocf	OpenCard Framework
--- a/usr/src/pkgdefs/SUNWpamsc/pkginfo.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-
-#
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-#
-# This required package information file describes characteristics of the
-# package, such as package abbreviation, full package name, package version,
-# and package architecture.
-#
-PKG="SUNWpamsc"
-NAME="PAM Smart Card module"
-ARCH="ISA"
-SUNW_PRODNAME="SunOS"
-SUNW_PKGTYPE="usr"
-MAXINST="1000"
-CATEGORY="system"
-DESC="Pluggable Authentication Module for Smart Card Authentication"
-VENDOR="Sun Microsystems, Inc."
-HOTLINE="Please contact your local service provider"
-EMAIL=""
-CLASSES="none"
-BASEDIR=/
-SUNW_PKGVERS="1.0"
-SUNW_PKG_ALLZONES="true"
-SUNW_PKG_HOLLOW="false"
-SUNW_PKG_THISZONE="false"
-#VSTOCK="<reserved by Release Engineering for package part #>"
-#ISTATES="<developer defined>"
-#RSTATES='<developer defined>'
-#ULIMIT="<developer defined>"
-#ORDER="<developer defined>"
-#INTONLY="<developer defined>"
-VERSION="ONVERS,REV=0.0.0"
-#PSTAMP=""
-SUNW_PRODVERS="RELEASE/VERSION"
--- a/usr/src/pkgdefs/SUNWpamsc/prototype_com	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 1999, 2001-2002 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-# packaging files
-i pkginfo
-i copyright
-i depend
-#
-# source locations relative to the prototype file
-#
-# SUNWpamsc
-#
-d none usr 0755 root sys
-d none usr/lib 0755 root bin
-d none usr/lib/security 0755 root bin
-f none usr/lib/security/pam_smartcard.so.1 0755 root bin
-s none usr/lib/security/pam_smartcard.so=pam_smartcard.so.1
--- a/usr/src/pkgdefs/SUNWpamsc/prototype_i386	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-#
-# List files which are I386 specific here
-#
-# source locations relative to the prototype file
-#
-#
-# SUNWpamsc
-#
-d none usr/lib/security/amd64 0755 root bin
-f none usr/lib/security/amd64/pam_smartcard.so.1 0755 root bin
-s none usr/lib/security/amd64/pam_smartcard.so=pam_smartcard.so.1
--- a/usr/src/pkgdefs/SUNWpamsc/prototype_sparc	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 1999,2001,2003 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-#
-# List files which are SPARC specific here
-#
-# source locations relative to the prototype file
-#
-#
-# SUNWpamsc
-d none usr/lib/security/sparcv9 0755 root bin
-f none usr/lib/security/sparcv9/pam_smartcard.so.1 0755 root bin
-s none usr/lib/security/sparcv9/pam_smartcard.so=pam_smartcard.so.1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpmcsr/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,38 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+ 
+include ../Makefile.com
+
+TMPLFILES += postinstall preremove
+DATAFILES += depend i.pmcsconf
+
+.KEEP_STATE:
+
+all: $(FILES)
+install: all pkg
+
+include ../Makefile.targ
+include ../Makefile.prtarg
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpmcsr/pkginfo.tmpl	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+PKG="SUNWpmcsr"
+NAME="PMC-Sierra SAS-2 HBA driver (root)"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="root"
+MAXINST="1000"
+CATEGORY=system
+VENDOR="Sun Microsystems, Inc."
+DESC="PMC-Sierra SAS-2 HBA Driver (root)"
+CLASSES="none pmcsconf"
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+BASEDIR=/
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+SUNW_PKG_THISZONE="false"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpmcsr/postinstall.tmpl	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include drv_utils
+
+ALIASES1="pciex11f8,8001"
+
+pkg_drvadd -i $ALIASES1			\
+	-m '* 0600 root sys'		\
+	-c scsi-self-identifying	\
+	pmcs || exit 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpmcsr/preremove.tmpl	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,28 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include drv_utils
+
+pkg_drvrem pmcs || exit 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpmcsr/prototype_com	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,51 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+#
+i pkginfo
+i copyright
+i depend
+i postinstall
+i preremove
+i i.pmcsconf
+
+d none kernel 0755 root sys
+d none kernel/drv 0755 root sys
+e pmcsconf kernel/drv/pmcs.conf 0644 root sys
+d none kernel/kmdb 0755 root sys
+d none kernel/misc 0755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpmcsr/prototype_i386	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,51 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+
+# PMC-Sierra (pmcs) SAS and SATA HBA Driver
+f none kernel/drv/pmcs 0755 root sys
+d none kernel/drv/amd64	0755 root sys
+f none kernel/drv/amd64/pmcs 0755 root sys
+f none kernel/kmdb/pmcs 0555 root sys
+d none kernel/kmdb/amd64 0755 root sys
+f none kernel/kmdb/amd64/pmcs 0555 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpmcsr/prototype_sparc	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,49 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+
+# PMC-Sierra (pmcs) SAS and SATA HBA Driver
+d none kernel/drv/sparcv9 0755 root sys
+f none kernel/drv/sparcv9/pmcs 0755 root sys
+d none kernel/kmdb/sparcv9 0755 root sys
+f none kernel/kmdb/sparcv9/pmcs 0555 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpmcsu/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+ 
+include ../Makefile.com
+
+DATAFILES += depend
+
+.KEEP_STATE:
+
+all: $(FILES)
+install: all pkg
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpmcsu/pkginfo.tmpl	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+PKG=SUNWpmcsu
+NAME=PMC-Sierra SAS-2 HBA driver (usr)
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="root"
+MAXINST="1000"
+CATEGORY=system
+VENDOR="Sun Microsystems, Inc."
+DESC="PMC-Sierra SAS-2 HBA driver (usr)"
+CLASSES="none"
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+BASEDIR=/
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+SUNW_PKG_THISZONE="false"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpmcsu/prototype_com	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+#
+i pkginfo
+i copyright
+i depend
+
+d none usr 0755 root sys
+d none usr/lib 0755 root bin
+d none usr/lib/mdb 0755 root sys
+d none usr/lib/mdb/kvm 0755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpmcsu/prototype_i386	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+
+d none usr/lib/mdb/kvm/amd64 0755 root sys
+f none usr/lib/mdb/kvm/pmcs.so 0555 root sys
+f none usr/lib/mdb/kvm/amd64/pmcs.so 0555 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpmcsu/prototype_sparc	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+
+d none usr/lib/mdb/kvm/sparcv9 0755 root sys
+f none usr/lib/mdb/kvm/sparcv9/pmcs.so 0555 root sys
--- a/usr/src/pkgdefs/SUNWscmhdlr/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-include ../Makefile.com
-
-.KEEP_STATE:
-
-all: $(FILES) depend
-install: all pkg
-
-include ../Makefile.targ
--- a/usr/src/pkgdefs/SUNWscmhdlr/depend	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-#
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# This package information file defines software dependencies associated
-# with the pkg.  You can define three types of pkg dependencies with this file:
-#	 P indicates a prerequisite for installation
-#	 I indicates an incompatible package
-#	 R indicates a reverse dependency
-# <pkg.abbr> see pkginfo(4), PKG parameter
-# <name> see pkginfo(4), NAME parameter
-# <version> see pkginfo(4), VERSION parameter
-# <arch> see pkginfo(4), ARCH parameter
-# <type> <pkg.abbr> <name>
-# 	(<arch>)<version>
-# 	(<arch>)<version>
-# 	...
-# <type> <pkg.abbr> <name>
-# ...
-
-P SUNWcar	Core Architecture, (Root)
-P SUNWcakr	Core Solaris Kernel Architecture (Root)
-P SUNWkvm	Core Architecture, (Kvm)
-P SUNWcsr	Core Solaris, (Root)
-P SUNWckr	Core Solaris Kernel (Root)
-P SUNWcnetr	Core Solaris Network Infrastructure (Root)
-P SUNWcsu	Core Solaris, (Usr)
-P SUNWcsd	Core Solaris Devices
-P SUNWcsl	Core Solaris Libraries
-P SUNWcslr	Core Solaris Libraries (Root)
-P SUNWstc	Smart Card Reader Device Driver
--- a/usr/src/pkgdefs/SUNWscmhdlr/pkginfo.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-
-#
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-#
-# This required package information file describes characteristics of the
-# package, such as package abbreviation, full package name, package version,
-# and package architecture.
-#
-PKG="SUNWscmhdlr"
-NAME="SCM Smart Card Reader IFD Handler"
-ARCH="ISA"
-VERSION="ONVERS,REV=0.0.0"
-CATEGORY="system"
-DESC="SunOS SCM internal reader IFD Handler"
-SUNW_PRODNAME="SunOS"
-SUNW_PRODVERS="RELEASE/VERSION"
-SUNW_PKGTYPE="usr"
-MAXINST="1000"
-VENDOR="Sun Microsystems, Inc."
-HOTLINE="Please contact your local service provider"
-EMAIL=""
-CLASSES="none"
-BASEDIR=/
-SUNW_PKGVERS="1.0"
-SUNW_PKG_ALLZONES="true"
-SUNW_PKG_HOLLOW="false"
-SUNW_PKG_THISZONE="false"
-#VSTOCK="<reserved by Release Engineering for package part #>"
-#ISTATES="<developer defined>"
-#RSTATES='<developer defined>'
-#ULIMIT="<developer defined>"
-#ORDER="<developer defined>"
-#PSTAMP="<developer defined>"
-#INTONLY="<developer defined>"
--- a/usr/src/pkgdefs/SUNWscmhdlr/prototype_com	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-# packaging files
-i pkginfo
-i copyright
-i depend
-#
-# source locations relative to the prototype file
-#
-# SUNWscmhdlr
-#
-d none usr 755 root sys
-d none usr/lib 755 root bin
--- a/usr/src/pkgdefs/SUNWscmhdlr/prototype_sparc	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-# All rights reserved.
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-#
-!include prototype_com
-#
-#
-#
-# List files which are SPARC specific here
-#
-# source locations relative to the prototype file
-#
-#
-# SUNWscmhdlr
-#
-#
-# SCM Card Terminal Serial Driver Files
-#
-d none usr/lib/smartcard 0755 root bin
-f none usr/lib/smartcard/ifdh_scmi2c.so.1 0755 root bin
-s none usr/lib/smartcard/ifdh_scmi2c.so=ifdh_scmi2c.so.1
--- a/usr/src/pkgdefs/SUNWsmhba/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/SUNWsmhba/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -26,9 +26,12 @@
 
 DATAFILES += depend
 
+LICENSEFILES += ../../lib/smhba/THIRDPARTYLICENSE
+
 .KEEP_STATE:
 
 all: $(FILES) 
+
 install: all pkg
 
 include ../Makefile.targ
--- a/usr/src/pkgdefs/SUNWstc.u/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999-2000 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-include	../Makefile.com
-
-DATAFILES += depend
-
-.KEEP_STATE:
-
-all: $(FILES)
-install: all pkg
-
-include ../Makefile.targ
--- a/usr/src/pkgdefs/SUNWstc.u/pkginfo.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-
-#
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
-
-#
-# This required package information file describes characteristics of the
-# package, such as package abbreviation, full package name, package version,
-# and package architecture.
-#
-PKG="SUNWstc"
-NAME="SUN ISCRI Kernel Driver"
-ARCH="sparc.sun4u"
-SUNW_PRODNAME="SunOS"
-SUNW_PKGTYPE="root"
-MAXINST="1000"
-CATEGORY="system"
-DESC="I2C Smartcard kernel device driver for SCM STC chip"
-VENDOR="Sun Microsystems, Inc."
-HOTLINE="Please contact your local service provider"
-EMAIL=""
-CLASSES="none"
-BASEDIR=/
-SUNW_PKGVERS="1.0"
-SUNW_PKG_ALLZONES="true"
-SUNW_PKG_HOLLOW="true"
-SUNW_PKG_THISZONE="false"
-#VSTOCK="<reserved by Release Engineering for package part #>"
-#ISTATES="<developer defined>"
-#RSTATES='<developer defined>'
-#ULIMIT="<developer defined>"
-#ORDER="<developer defined>"
-#INTONLY="<developer defined>"
-VERSION="ONVERS,REV=0.0.0"
-#PSTAMP=""
-SUNW_PRODVERS="RELEASE/VERSION"
--- a/usr/src/pkgdefs/SUNWstc.u/postinstall	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-#! /bin/sh
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-#
-# Copyright (c) 1999-2000 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-
-PATH=/usr/bin:/usr/sbin:${PATH}
-export PATH
-
-
-TMP=/tmp/$$
-
-
-#
-# Make sure add_drv has *not* been previously executed
-# before attempting to add the driver.
-#
-grep '^scmi2c\>' ${BASEDIR}/etc/name_to_major > /dev/null 2>&1
-if [ $? -ne 0 ]; then
-	add_drv -b ${BASEDIR} -i 'i2c-scm001 card-reader' scmi2c
-	if [ $? -ne 0 ]; then
-                echo "\nFailed add_drv!\n" >&2
-                exit 1
-        fi
-fi
-exit 0
--- a/usr/src/pkgdefs/SUNWstc.u/preremove	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-#! /bin/sh
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-#
-# Copyright (c) 1999-2000 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-
-PATH="/usr/bin:/usr/sbin:${PATH}"
-export PATH
-
-TMP=/tmp/$$
-
-rem_drv -b ${BASEDIR} scmi2c
-rm -f ${BASEDIR}/dev/scmi2c0
-
-
-exit 0
--- a/usr/src/pkgdefs/SUNWstc.u/prototype_com	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright (c) 1999-2000 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-# packaging files
-i copyright
-i pkginfo
-i depend
-i postinstall
-i preremove 
--- a/usr/src/pkgdefs/SUNWstc.u/prototype_sparc	Mon Sep 28 10:38:31 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-# Copyright 1999-2000,2003 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-#pragma	ident	"%Z%%M%	%I%	%E% SMI"
-#
-# This required package information file contains a list of package contents.
-# The 'pkgmk' command uses this file to identify the contents of a package
-# and their location on the development machine when building the package.
-# Can be created via a text editor or through use of the 'pkgproto' command.
-
-#!search <pathname pathname ...>	# where to find pkg objects
-#!include <filename>			# include another 'prototype' file
-#!default <mode> <owner> <group>	# default used if not specified on entry
-#!<param>=<value>			# puts parameter in pkg environment
-
-#
-# Include ISA independent files (prototype_com)
-
-!include prototype_com
-#
-#
-# List files which are SPARC specific here
-#
-# source locations relative to the prototype file
-#
-# SUNWstc
-#
-# I2C Smart Card Device Driver header files
-# scmi2c.h file is installed from SUNWhea package
-#
-# I2C Smartcard Device Driver Files
-#
-d none platform 0755 root sys
-d none platform/sun4u 0755 root sys
-d none platform/sun4u/kernel 0755 root sys
-d none platform/sun4u/kernel/drv 0755 root sys
-d none platform/sun4u/kernel/drv/sparcv9 0755 root sys
-f none platform/sun4u/kernel/drv/sparcv9/scmi2c 0755 root sys
--- a/usr/src/pkgdefs/SUNWtpm/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/SUNWtpm/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -27,9 +27,21 @@
 TMPLFILES += postinstall preremove
 DATAFILES += depend
 
+ACTION=grep SUNWrmodr depend > /dev/null || \
+	(chmod 666 depend; \
+	echo "P SUNWrmodr	Realmode Modules, (Root)" >> depend; \
+	chmod 444 depend;)
+
 .KEEP_STATE:
 
-all: $(FILES)
+all: $(FILES) action_$(MACH)
+
+action_i386: depend
+	$(ACTION)
+	touch $@
+
+action_sparc:
+
 install: all pkg
 
 include ../Makefile.targ
--- a/usr/src/pkgdefs/SUNWtpm/postinstall.tmpl	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/SUNWtpm/postinstall.tmpl	Mon Oct 05 09:17:26 2009 -0400
@@ -80,7 +80,12 @@
 # Add entry to /etc/devlink.tab if not there already
 # Note: the tab after ${DRV} here is important.
 if [ "${ISA_TYPE}" = "${ISA_TYPE_I386}" ]; then
-	add_acpi
+	/usr/bin/pkginfo SUNWrmodr > /dev/null 2>&1
+	if [ $? -ne 0 ]; then
+		echo "The SUNWrmodr package must be installed in order for TPM device to be recognized"
+	else
+		add_acpi
+	fi
 	add_driver
 else 
 	if [ "${ISA_TYPE}" = "${ISA_TYPE_SPARC}" -a "${MACH}" = "sun4v" ]; then
--- a/usr/src/pkgdefs/common_files/i.etcrpc	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/common_files/i.etcrpc	Mon Oct 05 09:17:26 2009 -0400
@@ -20,11 +20,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 
 PATH="/usr/bin:/usr/sbin:${PATH}"
 export PATH
@@ -91,11 +89,14 @@
 			rm -f /tmp/etcrpc.$$
 		fi	
 
-		# Add ocfserv entry
-		grep 'ocfserv' $dest > /dev/null 2>&1
-		if [ $? -ne 0 ] ; then
-			echo 'ocfserv		100150' >> $dest
-		fi
+		# Delete ocfserv entry
+		grep 'ocfserv[	]*100150' $dest >/dev/null 2>&1
+		if [ $? -eq 0 ] ; then
+			grep -v 'ocfserv[	]*100150' $dest \
+					>/tmp/etcrpc.$$ 2>/dev/null
+			cp /tmp/etcrpc.$$ $dest
+			rm -f /tmp/etcrpc.$$
+		fi	
 
 		# Add metad entry
 		grep 'metad' $dest > /dev/null 2>&1
--- a/usr/src/pkgdefs/common_files/i.ftpusers	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/common_files/i.ftpusers	Mon Oct 05 09:17:26 2009 -0400
@@ -19,7 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # This is similar to i.preserve, except we also check if there is a file with
@@ -59,7 +59,7 @@
 			fi
 		fi
 	fi
-	for user in dladm smmsp gdm webservd mysql openldap
+	for user in dladm smmsp gdm webservd mysql openldap xvm
 	do
 		egrep "^$user$|^#[ 	]*$user$" $dest >/dev/null 2>&1 || \
 			echo $user >> $dest
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/common_files/i.pmcsconf	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,137 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PATH=/usr/bin:/usr/sbin:$PATH; export PATH
+PREFIX=/tmp/pmcs.conf.$$
+
+add_comment_for_vhci_class()
+{
+	if grep "^# As a pHCI driver, pmcs must spec" $1 > /dev/null 2>&1; then
+		return
+	fi
+	cat >> $1 << EOF
+#
+# As a pHCI driver, pmcs must specify the vHCI class it belongs to (scsi_vhci).
+#
+EOF
+}
+
+add_comment_for_mpxio_disable()
+{
+	if grep "^# By default, MPxIO will be enabled" $1 > /dev/null 2>&1; then
+		return
+	fi
+	cat >> $1 << EOF
+#
+# By default, MPxIO will be enabled on all pmcs controllers. To disable MPxIO
+# for all pmcs controllers, set:
+#
+#mpxio-disable="yes";
+
+#
+# You can disable MPxIO on a per-HBA basis.  Per port settings override the
+# global setting for the specified ports. To disable MPxIO for the controller
+# whose unit-address is 0 and whose parent is /pci@0/pci10de,5d@e, set:
+#
+#name="pmcs" parent="/pci@0/pci10de,5d@e" unit-address="0" mpxio-disable="yes";
+
+EOF
+}
+
+add_comment_for_debug()
+{
+	if grep "^# Global debug settings may be set " $1 > /dev/null 2>&1; then
+		return
+	fi
+	cat >> $1 << EOF
+#
+# Global debug settings may be set using the 'pmcs-debug-mask' property.
+# Any combination of values may be set according to the following:
+#
+# 0x0001 - Basic info; shouldn't print anything during normal operation
+# 0x0002 - Small amount of I/O information during normal operation
+# 0x0004 - Much more info during I/O; will impact performance
+# 0x0008 - Very verbose at all times; definitely a performance impact
+# 0x0010 - Debug information with regard to discovery/configuration
+# 0x0020 - Debug information with regard to iport
+# 0x0040 - Debug information with regard to map
+# 0x0080 - Report on SCSI underflow status
+# 0x0100 - Report SCSI status for every command
+# 0x0200 - PHY lock/unlock debug (very noisy)
+# 0x0400 - Debug information with regard to device state
+#
+pmcs-debug-mask=0x71;
+
+EOF
+}
+
+update_pmcsconf()
+{
+	NEWHDR1=$PREFIX.hdr1
+	NEWHDR2=$PREFIX.hdr2
+	TMPFILE=$PREFIX.tmp
+	
+	# replace old copyright with new one	
+	HEADER="^#.* Copyright.*Sun Microsystems.*$"
+	if grep "$HEADER" $1 > $NEWHDR1 2>/dev/null; then
+		# replace / by \/
+		sed "s/\\//\\\\\\//g" $NEWHDR1 > $NEWHDR2 2>/dev/null
+		if sed "s/$HEADER/`cat $NEWHDR2`/" $2 > $TMPFILE 2>/dev/null
+		then
+			cp $TMPFILE $2
+		fi
+	fi
+
+	# ddi-vhci-class: Add comment
+	add_comment_for_vhci_class $2
+
+	# ddi-vhci-class: Add property
+	grep '^ddi-vhci-class' $2 > /dev/null 2>&1
+	if [ $? != 0 ] ; then
+		# add the property
+		echo 'ddi-vhci-class="scsi_vhci";' >> $2
+		echo '' >> $2
+	fi
+
+	# mpxio-disable: Add comment
+	add_comment_for_mpxio_disable $2
+
+	# pmcs-debug-mask: Add comment
+	add_comment_for_debug $2
+
+	rm -f $NEWHDR1 $NEWHDR2 $TMPFILE
+}
+
+if read src dest; then
+	if [ ! -f $dest ]; then
+		cp $src $dest
+	else
+		# upgrading solaris
+		update_pmcsconf $src $dest
+	fi
+
+fi
+
+exit 0
--- a/usr/src/pkgdefs/etc/exception_list_sparc	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/pkgdefs/etc/exception_list_sparc	Mon Oct 05 09:17:26 2009 -0400
@@ -805,12 +805,6 @@
 #
 usr/include/aclutils.h			sparc
 #
-# Entries for the SCM IFD Handler which are used only to build
-# the SCM IFD Handler and are not delivered to customers.
-#
-usr/include/scm_ifdh.h			sparc
-usr/include/scm_protocol.h		sparc
-#
 # USB skeleton driver stays in sync with the rest of USB but doesn't ship.
 #
 kernel/drv/sparcv9/usbskel		sparc
--- a/usr/src/tools/opensolaris/license-list	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/tools/opensolaris/license-list	Mon Oct 05 09:17:26 2009 -0400
@@ -1,5 +1,4 @@
 usr/closed/cmd/man/src/util/solbookv1/THIRDPARTYLICENSE
-usr/closed/lib/smartcard/ocfserv/opencard/core/event/THIRDPARTYLICENSE
 usr/closed/uts/intel/io/iprb/THIRDPARTYLICENSE
 usr/src/cmd/agents/snmp/THIRDPARTYLICENSE
 usr/src/cmd/ast/THIRDPARTYLICENSE
@@ -115,6 +114,7 @@
 usr/src/lib/passwdutil/THIRDPARTYLICENSE
 usr/src/lib/pkcs11/include/THIRDPARTYLICENSE
 usr/src/lib/print/libhttp-core/common/LICENSE.txt
+usr/src/lib/smhba/THIRDPARTYLICENSE
 usr/src/stand/lib/tcp/THIRDPARTYLICENSE
 usr/src/tools/ctf/dwarf/THIRDPARTYLICENSE
 usr/src/tools/onbld/THIRDPARTYLICENSE
--- a/usr/src/tools/scripts/bfu.sh	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/tools/scripts/bfu.sh	Mon Oct 05 09:17:26 2009 -0400
@@ -148,10 +148,6 @@
 	etc/shadow
 	etc/skel/.profile
 	etc/skel/local.*
-	etc/smartcard/.keys
-	etc/smartcard/desktop.properties
-	etc/smartcard/ocf.classpath
-	etc/smartcard/opencard.properties
 	etc/ssh/ssh_config
 	etc/ssh/sshd_config
 	etc/syslog.conf
@@ -222,6 +218,7 @@
 	kernel/drv/mpt.conf
 	kernel/drv/mpt_sas.conf
 	kernel/drv/options.conf
+	kernel/drv/pmcs.conf
 	kernel/drv/qlc.conf
 	kernel/drv/ra.conf
 	kernel/drv/scsa2usb.conf
@@ -498,7 +495,6 @@
 	metamedd
 	metamhd
 	name
-	ocfserv
 	printer
 	rexd
 	rquotad
@@ -1537,6 +1533,7 @@
 	var/svc/manifest/network/datalink-init.xml
 	var/svc/manifest/network/iscsi_initiator.xml
 	var/svc/manifest/network/fcoe_config.xml
+	var/svc/manifest/network/rpc/ocfserv.xml
 "
 
 # smf services whose manifests have been renamed
@@ -3933,6 +3930,44 @@
 	printf '\n'
 }
 
+#
+# Remove EOF Smartcard framework
+#
+remove_eof_smartcard()
+{
+	# Packages to remove
+	typeset -r smartcard_pkg='SUNWjcom SUNWkib SUNWocf SUNWocfd SUNWocfh
+SUNWocfr SUNWpamsc SUNWscmhdlr'
+	typeset pkg
+
+	printf 'Removing EOF Smartcard... '
+
+	for pkg in $smartcard_pkgs
+	do
+		if [ -d $rootprefix/var/sadm/pkg/$pkg ]; then
+			rm -rf $rootprefix/var/sadm/pkg/$pkg
+			grep -vw $pkg $rootprefix/var/sadm/install/contents > \
+			    /tmp/contents.$$
+			cp /tmp/contents.$$ /var/sadm/install/contents.$$
+			rm /tmp/contents.$$
+		fi
+	done
+
+	#
+	# Remove smartcard headers, libraries,  Old readers and the 
+	# parts delivered from other consolidations
+	# that no longer work with the ON parts removed.
+	#
+	rm -rf  $usr/lib/smartcard \
+		$usr/share/lib/smartcard \
+		$usr/include/smartcard.h \
+		$usr/include/smartcard \
+		$root/etc/smartcard \
+		$root/platform/sun4u/kernel/drv/sparcv9/scmi2c
+
+	printf 'done.\n'
+}
+
 remove_properties() {
 
 	#
@@ -6533,6 +6568,13 @@
 	fi
 
 	#
+	# Remove EOF Smartcard support
+	#
+	if [ -d $usr/lib/smartcard ]; then
+		remove_eof_smartcard
+	fi
+
+	#
 	# Remove DMI
 	#
 	if [ -d $usr/lib/dmi -o \
@@ -7300,39 +7342,6 @@
 	rm -f $root/kernel/drv/sparcv9/rpcib
 
 	#
-	# Remove old smartcard header files
-	#
-
-	rm -f \
-		$usr/include/smartcard.h \
-		$usr/include/smartcard/ocf_authenticate.h \
-		$usr/include/smartcard/ocf_core.h \
-		$usr/include/smartcard/ocf_core_cardservices.h
-
-	#
-	# Remove smartcard libraries that should not have been shipped.
-	#
-	rm -rf  $usr/lib/smartcard/sparcv9/ \
-		$usr/share/lib/smartcard/scmtester.jar
-
-	#
-	# Remove external smartcard reader driver
-	#
-	rm -f $usr/share/lib/smartcard/scmrsr3.jar
-
-	#
-	# Remove old internal smartcard reader driver
-	#
-	rm -f $usr/share/lib/smartcard/scmiscr.jar
-	rm -f $usr/lib/smartcard/libSCMI2CNative.so
-	rm -f $usr/lib/smartcard/libSCMI2CNative.so.1
-
-	#
-	# Remove Smart OS
-	#
-	rm -f $usr/share/lib/smartcard/smartos.jar
-
-	#
 	# Remove drivers & header files for EOL of soc & pln drivers
 	# as per PSARC/2003/233
 	#
--- a/usr/src/tools/scripts/bindrop.sh	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/tools/scripts/bindrop.sh	Mon Oct 05 09:17:26 2009 -0400
@@ -21,11 +21,9 @@
 #
 
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
 # Create an encumbered binaries tarball from a full build proto area,
 # less the contents of an OpenSolaris proto area.  Special handling
 # for crypto binaries that need to be signed by Sun Release
@@ -184,7 +182,6 @@
 "
 # encumbered binaries and associated files
 delete="$delete
-	etc/smartcard/
 	kernel/drv/amd64/audioens
 	kernel/drv/amd64/bmc
 	kernel/drv/amd64/pcn
@@ -211,9 +208,7 @@
 	usr/bin/ksh
 	usr/bin/pfksh
 	usr/bin/rksh
-	usr/bin/smartcard
 	usr/ccs/bin/dis
-	usr/include/smartcard/
 	usr/include/sys/audio/audioens.h
 	usr/include/sys/phx.h
 	usr/include/sys/scsi/adapters/ifpcmd.h
@@ -225,27 +220,9 @@
 	usr/include/sys/scsi/adapters/ispmail.h
 	usr/include/sys/scsi/adapters/ispreg.h
 	usr/include/sys/scsi/adapters/ispvar.h
-	usr/lib/amd64/libsmartcard.so.1
-	usr/lib/amd64/libsmartcard.so
-	usr/lib/amd64/llib-lsmartcard.ln
-	usr/lib/libsmartcard.so.1
-	usr/lib/libsmartcard.so
-	usr/lib/llib-lsmartcard.ln
-	usr/lib/llib-lsmartcard
-	usr/lib/locale/C/LC_MESSAGES/libsmartcard.msg
 	usr/lib/mdb/disasm/sparc.so
 	usr/lib/mdb/disasm/sparcv9/sparc.so
 	usr/lib/mdb/kvm/sparcv9/isp.so
-	usr/lib/security/amd64/pam_smartcard.so.1
-	usr/lib/security/amd64/pam_smartcard.so
-	usr/lib/security/pam_smartcard.so.1
-	usr/lib/security/pam_smartcard.so
-	usr/lib/security/sparcv9/pam_smartcard.so.1
-	usr/lib/security/sparcv9/pam_smartcard.so
-	usr/lib/smartcard/
-	usr/lib/sparcv9/libsmartcard.so.1
-	usr/lib/sparcv9/libsmartcard.so
-	usr/lib/sparcv9/llib-lsmartcard.ln
 	usr/platform/SUNW,Netra-T12/
 	usr/platform/sun4u/include/sys/i2c/misc/i2c_svc.h
 	usr/platform/sun4u/include/sys/memtestio.h
@@ -260,14 +237,10 @@
 	usr/platform/sun4v/include/sys/memtestio.h
 	usr/platform/sun4v/include/sys/memtestio_ni.h
 	usr/platform/sun4v/include/sys/memtestio_v.h
-	usr/sbin/ocfserv
-	usr/share/javadoc/smartcard/
-	usr/share/lib/smartcard/
 	usr/share/lib/sgml/locale/C/dtds/docbook/docbook.dtd
 	usr/share/lib/sgml/locale/C/dtds/docbook/
 	usr/share/lib/sgml/locale/C/dtds/solbookv1/solbook.dtd
 	usr/share/lib/sgml/locale/C/dtds/solbookv1/
-	var/svc/manifest/network/rpc/ocfserv.xml
 "
 # memory fault injector test framework
 delete="$delete
--- a/usr/src/uts/Makefile.targ	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/Makefile.targ	Mon Oct 05 09:17:26 2009 -0400
@@ -194,6 +194,9 @@
 $(ROOT_SCSI_VHCI_DIR)/%: $(OBJS_DIR)/% $(ROOT_SCSI_VHCI_DIR) FRC
 	$(INS.file)
 
+$(ROOT_PMCS_FW_DIR)/%:	$(OBJS_DIR)/% $(ROOT_PMCS_FW_DIR) FRC
+	$(INS.file)
+
 $(ROOT_QLC_FW_DIR)/%:	$(OBJS_DIR)/% $(ROOT_QLC_FW_DIR) FRC
 	$(INS.file)
 
--- a/usr/src/uts/Makefile.uts	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/Makefile.uts	Mon Oct 05 09:17:26 2009 -0400
@@ -461,7 +461,6 @@
 ROOT_KGSS_DIR_32	= $(ROOT_MOD_DIR)/misc/kgss
 ROOT_SCSI_VHCI_DIR_32	= $(ROOT_MOD_DIR)/misc/scsi_vhci
 ROOT_QLC_FW_DIR_32	= $(ROOT_MOD_DIR)/misc/qlc
-
 ROOT_EMLXS_FW_DIR_32	= $(ROOT_MOD_DIR)/misc/emlxs
 ROOT_NLMISC_DIR_32	= $(ROOT_MOD_DIR)/misc
 ROOT_MACH_DIR_32	= $(ROOT_MOD_DIR)/mach
--- a/usr/src/uts/common/Makefile.files	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/Makefile.files	Mon Oct 05 09:17:26 2009 -0400
@@ -125,6 +125,7 @@
 		cs_stubs.o	\
 		dacf.o		\
 		dacf_clnt.o	\
+		damap.o	\
 		cyclic.o	\
 		ddi.o		\
 		ddifm.o		\
@@ -889,6 +890,8 @@
 
 SV_OBJS +=	sv.o
 
+PMCS_OBJS += pmcs_attach.o pmcs_intr.o pmcs_nvram.o pmcs_sata.o \
+		pmcs_scsa.o pmcs_smhba.o pmcs_subr.o pmcs_fwlog.o
 
 #
 #	Build up defines and paths.
--- a/usr/src/uts/common/Makefile.rules	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/Makefile.rules	Mon Oct 05 09:17:26 2009 -0400
@@ -332,6 +332,14 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:	$(UTSBASE)/common/io/scsi/adapters/pmcs/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o:	$(UTSBASE)/common/io/scsi/adapters/pmcs/%.bin
+	$(COMPILE.b) -o $@ $<
+	$(CTFCONVERT_O)
+
 KMECHKRB5_BASE=$(UTSBASE)/common/gssapi/mechs/krb5
 
 KGSSDFLAGS=-I $(UTSBASE)/common/gssapi/include 
@@ -2153,6 +2161,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/scsi/adapters/blk2scsa/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/scsi/adapters/pmcs/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/scsi/adapters/scsi_vhci/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- a/usr/src/uts/common/crypto/core/kcf_callprov.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/crypto/core/kcf_callprov.c	Mon Oct 05 09:17:26 2009 -0400
@@ -114,7 +114,7 @@
 	crypto_key_t *key)
 {
 	crypto_mech_info_t *mech_info = NULL;
-	uint_t keylen = 0;
+	size_t keylen = 0;
 	ssize_t attr_len;
 	uchar_t *attr;
 
@@ -169,7 +169,7 @@
 
 	DTRACE_PROBE4(keylen__check,
 	    crypto_mech_type_t, mech_type,
-	    uint_t, keylen,
+	    size_t, keylen,
 	    ssize_t, mech_info->cm_min_key_length,
 	    ssize_t, mech_info->cm_max_key_length);
 	/* Do the actual check. */
--- a/usr/src/uts/common/fs/devfs/devfs_subr.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/devfs/devfs_subr.c	Mon Oct 05 09:17:26 2009 -0400
@@ -1184,6 +1184,17 @@
 
 found:
 	/*
+	 * Fail lookup of device that has now become hidden (typically via
+	 * hot removal of open device).
+	 */
+	if (dv->dv_devi && ndi_dev_is_hidden_node(dv->dv_devi)) {
+		dcmn_err2(("dv_find: nm %s failed: hidden/removed\n", nm));
+		VN_RELE(vp);
+		rv = ENOENT;
+		goto notfound;
+	}
+
+	/*
 	 * Skip non-kernel lookups of internal nodes.
 	 * This use of kcred to distinguish between user and
 	 * internal kernel lookups is unfortunate.  The information
@@ -1192,6 +1203,7 @@
 	 * this distinction.
 	 */
 	if ((dv->dv_flags & DV_INTERNAL) && (cred != kcred)) {
+		dcmn_err2(("dv_find: nm %s failed: internal\n", nm));
 		VN_RELE(vp);
 		rv = ENOENT;
 		goto notfound;
@@ -1251,7 +1263,7 @@
 	ndi_devi_enter(pdevi, &circ);
 	for (devi = ddi_get_child(pdevi); devi;
 	    devi = ddi_get_next_sibling(devi)) {
-		if (i_ddi_node_state(devi) < DS_PROBED)
+		if (i_ddi_node_state(devi) < DS_INITIALIZED)
 			continue;
 
 		/* skip hidden nodes */
--- a/usr/src/uts/common/fs/devfs/devfs_vnops.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/devfs/devfs_vnops.c	Mon Oct 05 09:17:26 2009 -0400
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * vnode ops for the devfs
  *
@@ -59,7 +57,7 @@
 #include <sys/debug.h>
 #include <sys/policy.h>
 #include <sys/modctl.h>
-
+#include <sys/sunndi.h>
 #include <fs/fs_subr.h>
 #include <sys/fs/dv_node.h>
 
@@ -960,13 +958,23 @@
 	diroff++;
 	for (dv = DV_FIRST_ENTRY(ddv); dv;
 	    dv = DV_NEXT_ENTRY(ddv, dv), diroff++) {
+		/* skip entries until at correct directory offset */
+		if (diroff < soff)
+			continue;
+
 		/*
-		 * although DDM_INTERNAL_PATH minor nodes are skipped for
-		 * readdirs outside the kernel, they still occupy directory
-		 * offsets
+		 * hidden nodes are skipped (but they still occupy a
+		 * directory offset).
 		 */
-		if (diroff < soff ||
-		    ((dv->dv_flags & DV_INTERNAL) && (cred != kcred)))
+		if (dv->dv_devi && ndi_dev_is_hidden_node(dv->dv_devi))
+			continue;
+
+		/*
+		 * DDM_INTERNAL_PATH minor nodes are skipped for readdirs
+		 * outside the kernel (but they still occupy a directory
+		 * offset).
+		 */
+		if ((dv->dv_flags & DV_INTERNAL) && (cred != kcred))
 			continue;
 
 		reclen = DIRENT64_RECLEN(strlen(dv->dv_name));
--- a/usr/src/uts/common/fs/zfs/dsl_scrub.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/dsl_scrub.c	Mon Oct 05 09:17:26 2009 -0400
@@ -1033,7 +1033,7 @@
 	 * spa_scrub_reopen flag indicates that vdev_open() should not
 	 * attempt to start another scrub.
 	 */
-	spa_vdev_state_enter(spa);
+	spa_vdev_state_enter(spa, SCL_NONE);
 	spa->spa_scrub_reopen = B_TRUE;
 	vdev_reopen(spa->spa_root_vdev);
 	spa->spa_scrub_reopen = B_FALSE;
--- a/usr/src/uts/common/fs/zfs/spa.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/spa.c	Mon Oct 05 09:17:26 2009 -0400
@@ -1141,6 +1141,15 @@
 	return (0);
 }
 
+static void
+spa_aux_check_removed(spa_aux_vdev_t *sav)
+{
+	int i;
+
+	for (i = 0; i < sav->sav_count; i++)
+		spa_check_removed(sav->sav_vdevs[i]);
+}
+
 /*
  * Load an existing storage pool, using the pool's builtin spa_config as a
  * source of configuration information.
@@ -1503,6 +1512,7 @@
 		    spa->spa_pool_props_object,
 		    zpool_prop_to_name(ZPOOL_PROP_AUTOREPLACE),
 		    sizeof (uint64_t), 1, &autoreplace);
+		spa->spa_autoreplace = (autoreplace != 0);
 		(void) zap_lookup(spa->spa_meta_objset,
 		    spa->spa_pool_props_object,
 		    zpool_prop_to_name(ZPOOL_PROP_DELEGATION),
@@ -1524,8 +1534,18 @@
 	 * unopenable vdevs so that the normal autoreplace handler can take
 	 * over.
 	 */
-	if (autoreplace && state != SPA_LOAD_TRYIMPORT)
+	if (spa->spa_autoreplace && state != SPA_LOAD_TRYIMPORT) {
 		spa_check_removed(spa->spa_root_vdev);
+		/*
+		 * For the import case, this is done in spa_import(), because
+		 * at this point we're using the spare definitions from
+		 * the MOS config, not necessarily from the userland config.
+		 */
+		if (state != SPA_LOAD_IMPORT) {
+			spa_aux_check_removed(&spa->spa_spares);
+			spa_aux_check_removed(&spa->spa_l2cache);
+		}
+	}
 
 	/*
 	 * Load the vdev state for all toplevel vdevs.
@@ -2642,6 +2662,14 @@
 		spa->spa_l2cache.sav_sync = B_TRUE;
 	}
 
+	/*
+	 * Check for any removed devices.
+	 */
+	if (spa->spa_autoreplace) {
+		spa_aux_check_removed(&spa->spa_spares);
+		spa_aux_check_removed(&spa->spa_l2cache);
+	}
+
 	if (spa_writeable(spa)) {
 		/*
 		 * Update the config cache to include the newly-imported pool.
@@ -3930,7 +3958,7 @@
 	 * See if any devices need to be marked REMOVED.
 	 */
 	if (tasks & SPA_ASYNC_REMOVE) {
-		spa_vdev_state_enter(spa);
+		spa_vdev_state_enter(spa, SCL_NONE);
 		spa_async_remove(spa, spa->spa_root_vdev);
 		for (int i = 0; i < spa->spa_l2cache.sav_count; i++)
 			spa_async_remove(spa, spa->spa_l2cache.sav_vdevs[i]);
@@ -3949,7 +3977,7 @@
 	 * See if any devices need to be probed.
 	 */
 	if (tasks & SPA_ASYNC_PROBE) {
-		spa_vdev_state_enter(spa);
+		spa_vdev_state_enter(spa, SCL_NONE);
 		spa_async_probe(spa, spa->spa_root_vdev);
 		(void) spa_vdev_state_exit(spa, NULL, 0);
 	}
--- a/usr/src/uts/common/fs/zfs/spa_misc.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/spa_misc.c	Mon Oct 05 09:17:26 2009 -0400
@@ -880,6 +880,7 @@
 	if (error == 0 && !list_is_empty(&spa->spa_config_dirty_list)) {
 		dsl_pool_scrub_restart(spa->spa_dsl_pool);
 		config_changed = B_TRUE;
+		spa->spa_config_generation++;
 	}
 
 	/*
@@ -939,18 +940,24 @@
  * Lock the given spa_t for the purpose of changing vdev state.
  */
 void
-spa_vdev_state_enter(spa_t *spa)
+spa_vdev_state_enter(spa_t *spa, int oplocks)
 {
-	spa_config_enter(spa, SCL_STATE_ALL, spa, RW_WRITER);
+	int locks = SCL_STATE_ALL | oplocks;
+
+	spa_config_enter(spa, locks, spa, RW_WRITER);
+	spa->spa_vdev_locks = locks;
 }
 
 int
 spa_vdev_state_exit(spa_t *spa, vdev_t *vd, int error)
 {
-	if (vd != NULL)
+	if (vd != NULL) {
 		vdev_state_dirty(vd->vdev_top);
+		spa->spa_config_generation++;
+	}
 
-	spa_config_exit(spa, SCL_STATE_ALL, spa);
+	ASSERT3U(spa->spa_vdev_locks, >=, SCL_STATE_ALL);
+	spa_config_exit(spa, spa->spa_vdev_locks, spa);
 
 	/*
 	 * If anything changed, wait for it to sync.  This ensures that,
--- a/usr/src/uts/common/fs/zfs/sys/spa.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/sys/spa.h	Mon Oct 05 09:17:26 2009 -0400
@@ -411,6 +411,7 @@
 extern void spa_close(spa_t *spa, void *tag);
 extern boolean_t spa_refcount_zero(spa_t *spa);
 
+#define	SCL_NONE	0x00
 #define	SCL_CONFIG	0x01
 #define	SCL_STATE	0x02
 #define	SCL_L2ARC	0x04		/* hack until L2ARC 2.0 */
@@ -436,7 +437,7 @@
 extern int spa_vdev_exit(spa_t *spa, vdev_t *vd, uint64_t txg, int error);
 
 /* Pool vdev state change lock */
-extern void spa_vdev_state_enter(spa_t *spa);
+extern void spa_vdev_state_enter(spa_t *spa, int oplock);
 extern int spa_vdev_state_exit(spa_t *spa, vdev_t *vd, int error);
 
 /* Accessor functions */
--- a/usr/src/uts/common/fs/zfs/sys/spa_impl.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/sys/spa_impl.h	Mon Oct 05 09:17:26 2009 -0400
@@ -122,6 +122,7 @@
 	spa_aux_vdev_t	spa_spares;		/* hot spares */
 	spa_aux_vdev_t	spa_l2cache;		/* L2ARC cache devices */
 	uint64_t	spa_config_object;	/* MOS object for pool config */
+	uint64_t	spa_config_generation;	/* config generation number */
 	uint64_t	spa_syncing_txg;	/* txg currently syncing */
 	uint64_t	spa_sync_bplist_obj;	/* object for deferred frees */
 	bplist_t	spa_sync_bplist;	/* deferred-free bplist */
@@ -171,6 +172,8 @@
 	int		spa_mode;		/* FREAD | FWRITE */
 	spa_log_state_t spa_log_state;		/* log state */
 	uint64_t	spa_autoexpand;		/* lun expansion on/off */
+	boolean_t	spa_autoreplace;	/* autoreplace set in open */
+	int		spa_vdev_locks;		/* locks grabbed */
 	/*
 	 * spa_refcnt & spa_config_lock must be the last elements
 	 * because refcount_t changes size based on compilation options.
--- a/usr/src/uts/common/fs/zfs/sys/vdev.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/sys/vdev.h	Mon Oct 05 09:17:26 2009 -0400
@@ -80,7 +80,6 @@
 extern void vdev_stat_update(zio_t *zio, uint64_t psize);
 extern void vdev_scrub_stat_update(vdev_t *vd, pool_scrub_type_t type,
     boolean_t complete);
-extern int vdev_getspec(spa_t *spa, uint64_t vdev, char **vdev_spec);
 extern void vdev_propagate_state(vdev_t *vd);
 extern void vdev_set_state(vdev_t *vd, boolean_t isopen, vdev_state_t state,
     vdev_aux_t aux);
--- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h	Mon Oct 05 09:17:26 2009 -0400
@@ -118,6 +118,8 @@
 	uint32_t	zi_freq;
 	uint32_t	zi_failfast;
 	char		zi_func[MAXNAMELEN];
+	uint32_t	zi_iotype;
+	uint32_t	zi_pad;		/* 64-bit alignment */
 } zinject_record_t;
 
 #define	ZINJECT_NULL		0x1
--- a/usr/src/uts/common/fs/zfs/vdev.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/vdev.c	Mon Oct 05 09:17:26 2009 -0400
@@ -1935,7 +1935,7 @@
 {
 	vdev_t *vd;
 
-	spa_vdev_state_enter(spa);
+	spa_vdev_state_enter(spa, SCL_NONE);
 
 	if ((vd = spa_lookup_by_guid(spa, guid, B_TRUE)) == NULL)
 		return (spa_vdev_state_exit(spa, NULL, ENODEV));
@@ -1955,7 +1955,8 @@
 	 * unavailable, then back off and simply mark the vdev as degraded
 	 * instead.
 	 */
-	if (vdev_is_dead(vd->vdev_top) && vd->vdev_aux == NULL) {
+	if (vdev_is_dead(vd->vdev_top) && !vd->vdev_islog &&
+	    vd->vdev_aux == NULL) {
 		vd->vdev_degraded = 1ULL;
 		vd->vdev_faulted = 0ULL;
 
@@ -1984,7 +1985,7 @@
 {
 	vdev_t *vd;
 
-	spa_vdev_state_enter(spa);
+	spa_vdev_state_enter(spa, SCL_NONE);
 
 	if ((vd = spa_lookup_by_guid(spa, guid, B_TRUE)) == NULL)
 		return (spa_vdev_state_exit(spa, NULL, ENODEV));
@@ -2017,7 +2018,7 @@
 {
 	vdev_t *vd, *tvd, *pvd, *rvd = spa->spa_root_vdev;
 
-	spa_vdev_state_enter(spa);
+	spa_vdev_state_enter(spa, SCL_NONE);
 
 	if ((vd = spa_lookup_by_guid(spa, guid, B_TRUE)) == NULL)
 		return (spa_vdev_state_exit(spa, NULL, ENODEV));
@@ -2064,12 +2065,33 @@
 }
 
 int
+vdev_offline_log(spa_t *spa)
+{
+	int error = 0;
+
+	if ((error = dmu_objset_find(spa_name(spa), zil_vdev_offline,
+	    NULL, DS_FIND_CHILDREN)) == 0) {
+
+		/*
+		 * We successfully offlined the log device, sync out the
+		 * current txg so that the "stubby" block can be removed
+		 * by zil_sync().
+		 */
+		txg_wait_synced(spa->spa_dsl_pool, 0);
+	}
+	return (error);
+}
+
+int
 vdev_offline(spa_t *spa, uint64_t guid, uint64_t flags)
 {
 	vdev_t *vd, *tvd;
-	int error;
-
-	spa_vdev_state_enter(spa);
+	int error = 0;
+	uint64_t generation;
+	metaslab_group_t *mg;
+
+top:
+	spa_vdev_state_enter(spa, SCL_ALLOC);
 
 	if ((vd = spa_lookup_by_guid(spa, guid, B_TRUE)) == NULL)
 		return (spa_vdev_state_exit(spa, NULL, ENODEV));
@@ -2078,6 +2100,8 @@
 		return (spa_vdev_state_exit(spa, NULL, ENOTSUP));
 
 	tvd = vd->vdev_top;
+	mg = tvd->vdev_mg;
+	generation = spa->spa_config_generation + 1;
 
 	/*
 	 * If the device isn't already offline, try to offline it.
@@ -2093,6 +2117,38 @@
 			return (spa_vdev_state_exit(spa, NULL, EBUSY));
 
 		/*
+		 * If the top-level is a slog and it's had allocations
+		 * then proceed. We check that the vdev's metaslab
+		 * grop is not NULL since it's possible that we may
+		 * have just added this vdev and have not yet initialized
+		 * it's metaslabs.
+		 */
+		if (tvd->vdev_islog && mg != NULL) {
+			/*
+			 * Prevent any future allocations.
+			 */
+			metaslab_class_remove(spa->spa_log_class, mg);
+			(void) spa_vdev_state_exit(spa, vd, 0);
+
+			error = vdev_offline_log(spa);
+
+			spa_vdev_state_enter(spa, SCL_ALLOC);
+
+			/*
+			 * Check to see if the config has changed.
+			 */
+			if (error || generation != spa->spa_config_generation) {
+				metaslab_class_add(spa->spa_log_class, mg);
+				if (error)
+					return (spa_vdev_state_exit(spa,
+					    vd, error));
+				(void) spa_vdev_state_exit(spa, vd, 0);
+				goto top;
+			}
+			ASSERT3U(tvd->vdev_stat.vs_alloc, ==, 0);
+		}
+
+		/*
 		 * Offline this device and reopen its top-level vdev.
 		 * If the top-level vdev is a log device then just offline
 		 * it. Otherwise, if this action results in the top-level
@@ -2107,28 +2163,18 @@
 			vdev_reopen(tvd);
 			return (spa_vdev_state_exit(spa, NULL, EBUSY));
 		}
+
+		/*
+		 * Add the device back into the metaslab rotor so that
+		 * once we online the device it's open for business.
+		 */
+		if (tvd->vdev_islog && mg != NULL)
+			metaslab_class_add(spa->spa_log_class, mg);
 	}
 
 	vd->vdev_tmpoffline = !!(flags & ZFS_OFFLINE_TEMPORARY);
 
-	if (!tvd->vdev_islog || !vdev_is_dead(tvd))
-		return (spa_vdev_state_exit(spa, vd, 0));
-
-	(void) spa_vdev_state_exit(spa, vd, 0);
-
-	error = dmu_objset_find(spa_name(spa), zil_vdev_offline,
-	    NULL, DS_FIND_CHILDREN);
-	if (error) {
-		(void) vdev_online(spa, guid, 0, NULL);
-		return (error);
-	}
-	/*
-	 * If we successfully offlined the log device then we need to
-	 * sync out the current txg so that the "stubby" block can be
-	 * removed by zil_sync().
-	 */
-	txg_wait_synced(spa->spa_dsl_pool, 0);
-	return (0);
+	return (spa_vdev_state_exit(spa, vd, 0));
 }
 
 /*
@@ -2356,6 +2402,14 @@
 	    !(zio->io_flags & ZIO_FLAG_IO_RETRY))
 		return;
 
+	/*
+	 * Intent logs writes won't propagate their error to the root
+	 * I/O so don't mark these types of failures as pool-level
+	 * errors.
+	 */
+	if (zio->io_vd == NULL && (zio->io_flags & ZIO_FLAG_DONT_PROPAGATE))
+		return;
+
 	mutex_enter(&vd->vdev_stat_lock);
 	if (type == ZIO_TYPE_READ && !vdev_is_dead(vd)) {
 		if (zio->io_error == ECKSUM)
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c	Mon Oct 05 09:17:26 2009 -0400
@@ -1952,8 +1952,10 @@
 			spa_config_sync(spa, B_FALSE, B_TRUE);
 		}
 		mutex_exit(&spa_namespace_lock);
-		if (spa != NULL)
+		if (spa != NULL) {
+			nvlist_free(props);
 			return (0);
+		}
 	}
 
 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
@@ -2565,7 +2567,7 @@
 
 	err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy);
 	if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
-		zvol_remove_minor(zc->zc_name);
+		(void) zvol_remove_minor(zc->zc_name);
 	return (err);
 }
 
@@ -2981,7 +2983,7 @@
 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
 		return (error);
 
-	spa_vdev_state_enter(spa);
+	spa_vdev_state_enter(spa, SCL_NONE);
 
 	if (zc->zc_guid == 0) {
 		vd = NULL;
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Mon Oct 05 09:17:26 2009 -0400
@@ -951,7 +951,7 @@
 
 	zfsvfs->z_log = zil_open(zfsvfs->z_os, zfs_get_data);
 	if (zil_disable) {
-		zil_destroy(zfsvfs->z_log, 0);
+		zil_destroy(zfsvfs->z_log, B_FALSE);
 		zfsvfs->z_log = NULL;
 	}
 
--- a/usr/src/uts/common/fs/zfs/zil.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/zil.c	Mon Oct 05 09:17:26 2009 -0400
@@ -77,6 +77,8 @@
 
 static kmem_cache_t *zil_lwb_cache;
 
+static boolean_t zil_empty(zilog_t *zilog);
+
 static int
 zil_dva_compare(const void *x1, const void *x2)
 {
@@ -436,23 +438,12 @@
 
 	mutex_enter(&zilog->zl_lock);
 
-	/*
-	 * It is possible for the ZIL to get the previously mounted zilog
-	 * structure of the same dataset if quickly remounted and the dbuf
-	 * eviction has not completed. In this case we can see a non
-	 * empty lwb list and keep_first will be set. We fix this by
-	 * clearing the keep_first. This will be slower but it's very rare.
-	 */
-	if (!list_is_empty(&zilog->zl_lwb_list) && keep_first)
-		keep_first = B_FALSE;
-
 	ASSERT3U(zilog->zl_destroy_txg, <, txg);
 	zilog->zl_destroy_txg = txg;
-	zilog->zl_keep_first = keep_first;
 
 	if (!list_is_empty(&zilog->zl_lwb_list)) {
 		ASSERT(zh->zh_claim_txg == 0);
-		ASSERT(!keep_first);
+		zilog->zl_keep_first = B_FALSE;
 		while ((lwb = list_head(&zilog->zl_lwb_list)) != NULL) {
 			list_remove(&zilog->zl_lwb_list, lwb);
 			if (lwb->lwb_buf != NULL)
@@ -461,9 +452,23 @@
 			kmem_cache_free(zil_lwb_cache, lwb);
 		}
 	} else {
-		if (!keep_first) {
+		zilog->zl_keep_first = keep_first;
+		if (zh->zh_flags & ZIL_REPLAY_NEEDED) {
+			ASSERT(!keep_first);
 			(void) zil_parse(zilog, zil_free_log_block,
 			    zil_free_log_record, tx, zh->zh_claim_txg);
+		} else {
+			/*
+			 * Would like to assert zil_empty() but that
+			 * would force us to read the log chain which
+			 * requires us to do I/O to the log. This is
+			 * overkill since we really just want to destroy
+			 * the chain anyway.
+			 */
+			if (!keep_first) {
+				blkptr_t bp = zh->zh_log;
+				zio_free_blk(zilog->zl_spa, &bp, txg);
+			}
 		}
 	}
 	mutex_exit(&zilog->zl_lock);
@@ -746,7 +751,7 @@
 		lwb->lwb_zio = zio_rewrite(zilog->zl_root_zio, zilog->zl_spa,
 		    0, &lwb->lwb_blk, lwb->lwb_buf, lwb->lwb_sz,
 		    zil_lwb_write_done, lwb, ZIO_PRIORITY_LOG_WRITE,
-		    ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE, &zb);
+		    ZIO_FLAG_CANFAIL | ZIO_FLAG_DONT_PROPAGATE, &zb);
 	}
 }
 
--- a/usr/src/uts/common/fs/zfs/zio.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/zio.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2196,8 +2196,9 @@
 		if (zio->io_error != ECKSUM && vd != NULL && !vdev_is_dead(vd))
 			zfs_ereport_post(FM_EREPORT_ZFS_IO, spa, vd, zio, 0, 0);
 
-		if ((zio->io_error == EIO ||
-		    !(zio->io_flags & ZIO_FLAG_SPECULATIVE)) && zio == lio) {
+		if ((zio->io_error == EIO || !(zio->io_flags &
+		    (ZIO_FLAG_SPECULATIVE | ZIO_FLAG_DONT_PROPAGATE))) &&
+		    zio == lio) {
 			/*
 			 * For logical I/O requests, tell the SPA to log the
 			 * error and generate a logical data ereport.
--- a/usr/src/uts/common/fs/zfs/zio_inject.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/zio_inject.c	Mon Oct 05 09:17:26 2009 -0400
@@ -184,7 +184,7 @@
 	int label;
 	int ret = 0;
 
-	if (offset + zio->io_size > VDEV_LABEL_START_SIZE &&
+	if (offset >= VDEV_LABEL_START_SIZE &&
 	    offset < vd->vdev_psize - VDEV_LABEL_END_SIZE)
 		return (0);
 
@@ -226,6 +226,18 @@
 	inject_handler_t *handler;
 	int ret = 0;
 
+	/*
+	 * We skip over faults in the labels unless it's during
+	 * device open (i.e. zio == NULL).
+	 */
+	if (zio != NULL) {
+		uint64_t offset = zio->io_offset;
+
+		if (offset < VDEV_LABEL_START_SIZE ||
+		    offset >= vd->vdev_psize - VDEV_LABEL_END_SIZE)
+		return (0);
+	}
+
 	rw_enter(&inject_lock, RW_READER);
 
 	for (handler = list_head(&inject_handlers); handler != NULL;
@@ -243,6 +255,12 @@
 				continue;
 			}
 
+			/* Handle type specific I/O failures */
+			if (zio != NULL &&
+			    handler->zi_record.zi_iotype != ZIO_TYPES &&
+			    handler->zi_record.zi_iotype != zio->io_type)
+				continue;
+
 			if (handler->zi_record.zi_error == error) {
 				/*
 				 * For a failed open, pretend like the device
--- a/usr/src/uts/common/fs/zfs/zvol.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/zvol.c	Mon Oct 05 09:17:26 2009 -0400
@@ -1771,7 +1771,7 @@
 		dmu_tx_abort(tx);
 		return (error);
 	}
-	dmu_object_set_blocksize(os, ZVOL_OBJ, vbs, 0, tx);
+	(void) dmu_object_set_blocksize(os, ZVOL_OBJ, vbs, 0, tx);
 	dmu_tx_commit(tx);
 
 	return (0);
--- a/usr/src/uts/common/inet/ip/ip.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/inet/ip/ip.c	Mon Oct 05 09:17:26 2009 -0400
@@ -4363,7 +4363,6 @@
 	sin_t		*sin;
 	ipa_conn_t	*ac;
 	uchar_t		*ucp;
-	mblk_t		*mp1;
 	int		error = 0;
 	int		protocol;
 	ipa_conn_x_t	*acx;
@@ -4451,10 +4450,6 @@
 		ip1dbg(("ip_bind: unaligned address\n"));
 		goto bad_addr;
 	}
-	/*
-	 * Check for trailing mps.
-	 */
-	mp1 = mp->b_cont;
 
 	switch (tbr->ADDR_length) {
 	default:
@@ -4464,13 +4459,13 @@
 
 	case IP_ADDR_LEN:
 		/* Verification of local address only */
-		error = ip_bind_laddr_v4(connp, &mp1, protocol,
+		error = ip_bind_laddr_v4(connp, &mp->b_cont, protocol,
 		    *(ipaddr_t *)ucp, 0, B_FALSE);
 		break;
 
 	case sizeof (sin_t):
 		sin = (sin_t *)ucp;
-		error = ip_bind_laddr_v4(connp, &mp1, protocol,
+		error = ip_bind_laddr_v4(connp, &mp->b_cont, protocol,
 		    sin->sin_addr.s_addr, sin->sin_port, B_TRUE);
 		break;
 
@@ -4480,7 +4475,7 @@
 		if (ac->ac_lport == 0)
 			ac->ac_lport = connp->conn_lport;
 		/* Always verify destination reachability. */
-		error = ip_bind_connected_v4(connp, &mp1, protocol,
+		error = ip_bind_connected_v4(connp, &mp->b_cont, protocol,
 		    &ac->ac_laddr, ac->ac_lport, ac->ac_faddr, ac->ac_fport,
 		    B_TRUE, B_TRUE, cr);
 		break;
@@ -4491,7 +4486,7 @@
 		 * Whether or not to verify destination reachability depends
 		 * on the setting of the ACX_VERIFY_DST flag in acx->acx_flags.
 		 */
-		error = ip_bind_connected_v4(connp, &mp1, protocol,
+		error = ip_bind_connected_v4(connp, &mp->b_cont, protocol,
 		    &acx->acx_conn.ac_laddr, acx->acx_conn.ac_lport,
 		    acx->acx_conn.ac_faddr, acx->acx_conn.ac_fport,
 		    B_TRUE, (acx->acx_flags & ACX_VERIFY_DST) != 0, cr);
--- a/usr/src/uts/common/inet/ip/ip_if.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/inet/ip/ip_if.c	Mon Oct 05 09:17:26 2009 -0400
@@ -17637,7 +17637,14 @@
 		return (EEXIST);
 	}
 
-	/* The new index is unused. Set it in the phyint. */
+	/*
+	 * The new index is unused. Set it in the phyint. However we must not
+	 * forget to trigger NE_IFINDEX_CHANGE event before the ifindex
+	 * changes. The event must be bound to old ifindex value.
+	 */
+	ill_nic_event_dispatch(ill, 0, NE_IFINDEX_CHANGE,
+	    &index, sizeof (index));
+
 	old_index = phyi->phyint_ifindex;
 	phyi->phyint_ifindex = index;
 
@@ -20265,6 +20272,8 @@
 		return ("LIF_UP");
 	case NE_LIF_DOWN:
 		return ("LIF_DOWN");
+	case NE_IFINDEX_CHANGE:
+		return ("IFINDEX_CHANGE");
 	default:
 		return ("UNKNOWN");
 	}
--- a/usr/src/uts/common/inet/ipnet/ipnet.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/inet/ipnet/ipnet.c	Mon Oct 05 09:17:26 2009 -0400
@@ -1428,6 +1428,14 @@
 		break;
 	}
 
+	/*
+	 * The zoneid stored in ipnetif_t needs to correspond to the actual
+	 * zone the address is being used in. This facilitates finding the
+	 * correct netstack_t pointer, amongst other things, later.
+	 */
+	if (zoneid == ALL_ZONES)
+		zoneid = GLOBAL_ZONEID;
+
 	mutex_enter(&ipnetif->if_addr_lock);
 	if (zoneid != ipnetif->if_zoneid) {
 		ipnetif_t *ifp2;
--- a/usr/src/uts/common/inet/iptun/iptun.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/inet/iptun/iptun.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2599,7 +2599,6 @@
 	ipha_t	*outer4, *inner4;
 	ip6_t	*outer6, *inner6;
 	mblk_t	*data_mp = mp;
-	boolean_t ipsec = B_FALSE;
 
 	ASSERT(IPCL_IS_IPTUN(connp));
 	ASSERT(DB_TYPE(mp) == M_DATA || DB_TYPE(mp) == M_CTL);
@@ -2616,7 +2615,6 @@
 			iptun_input_icmp(iptun, mp, data_mp);
 			return;
 		}
-		ipsec = B_TRUE;
 	}
 
 	/*
@@ -2649,14 +2647,13 @@
 			goto drop;
 	}
 
-	if (ipsec) {
-		if (!ipsec_tun_inbound(mp, &data_mp, iptun->iptun_itp, inner4,
-		    inner6, outer4, outer6, outer_hlen, iptun->iptun_ns)) {
-			/* Callee did all of the freeing. */
-			return;
-		}
-		mp = data_mp;
+	if (!ipsec_tun_inbound((mp == data_mp ? NULL : mp), &data_mp,
+	    iptun->iptun_itp, inner4, inner6, outer4, outer6, outer_hlen,
+	    iptun->iptun_ns)) {
+		/* Callee did all of the freeing. */
+		return;
 	}
+	mp = data_mp;
 
 	if (iptun->iptun_typeinfo->iti_type == IPTUN_TYPE_6TO4 &&
 	    !iptun_in_6to4_ok(iptun, outer4, inner6))
--- a/usr/src/uts/common/inet/sockmods/socksctp.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/inet/sockmods/socksctp.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2091,15 +2091,10 @@
 	mutex_enter(&so->so_lock);
 
 	/*
-	 * Override b_flag for SCTP sockfs internal use
-	 */
-	mp->b_flag = (short)flags;
-
-	/*
 	 * For notify messages, need to fill in association id.
 	 * For data messages, sndrcvinfo could be in ancillary data.
 	 */
-	if (flags & SCTP_NOTIFICATION) {
+	if (mp->b_flag & SCTP_NOTIFICATION) {
 		mp2 = mp->b_cont;
 		sn = (union sctp_notification *)mp2->b_rptr;
 		switch (sn->sn_header.sn_type) {
--- a/usr/src/uts/common/io/1394/targets/scsa1394/hba.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/1394/targets/scsa1394/hba.c	Mon Oct 05 09:17:26 2009 -0400
@@ -813,13 +813,15 @@
 		ndi_devi_alloc_sleep(sp->s_dip, node_name,
 		    (pnode_t)DEVI_SID_NODEID, &cdip);
 
-		ret = ndi_prop_update_int(DDI_DEV_T_NONE, cdip, "target", 0);
+		ret = ndi_prop_update_int(DDI_DEV_T_NONE, cdip,
+		    SCSI_ADDR_PROP_TARGET, 0);
 		if (ret != DDI_PROP_SUCCESS) {
 			(void) ndi_devi_free(cdip);
 			continue;
 		}
 
-		ret = ndi_prop_update_int(DDI_DEV_T_NONE, cdip, "lun", i);
+		ret = ndi_prop_update_int(DDI_DEV_T_NONE, cdip,
+		    SCSI_ADDR_PROP_LUN, i);
 		if (ret != DDI_PROP_SUCCESS) {
 			ddi_prop_remove_all(cdip);
 			(void) ndi_devi_free(cdip);
@@ -973,8 +975,8 @@
 	int		ret = DDI_FAILURE;
 
 	if (ddi_prop_op(DDI_DEV_T_ANY, cdip, PROP_LEN_AND_VAL_BUF,
-	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "lun", (caddr_t)&lun,
-	    &plen) != DDI_PROP_SUCCESS) {
+	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, SCSI_ADDR_PROP_LUN,
+	    (caddr_t)&lun, &plen) != DDI_PROP_SUCCESS) {
 		return (DDI_FAILURE);
 	}
 
@@ -1019,8 +1021,8 @@
 	}
 
 	if (ddi_prop_op(DDI_DEV_T_ANY, cdip, PROP_LEN_AND_VAL_BUF,
-	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "lun", (caddr_t)&lun,
-	    &plen) != DDI_PROP_SUCCESS) {
+	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, SCSI_ADDR_PROP_LUN,
+	    (caddr_t)&lun, &plen) != DDI_PROP_SUCCESS) {
 		return;
 	}
 
--- a/usr/src/uts/common/io/cmlb.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/cmlb.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2251,6 +2251,30 @@
 		return (ENOMEM);
 	}
 
+	/*
+	 * Check if there is a sane Solaris VTOC
+	 * If there is a valid vtoc, no need to lookup
+	 * for the linux swap signature.
+	 */
+	mutex_exit(CMLB_MUTEX(cl));
+	rval = DK_TG_READ(cl, buf, part_start + DK_LABEL_LOC,
+	    sec_sz, tg_cookie);
+	mutex_enter(CMLB_MUTEX(cl));
+	if (rval != 0) {
+		cmlb_dbg(CMLB_ERROR,  cl,
+		    "cmlb_is_linux_swap: disk vtoc read err\n");
+		rval = EIO;
+		goto done;
+	}
+
+	if ((((struct dk_label *)buf)->dkl_magic == DKL_MAGIC) &&
+	    (((struct dk_label *)buf)->dkl_vtoc.v_sanity == VTOC_SANE)) {
+		rval = -1;
+		goto done;
+	}
+
+
+	/* No valid vtoc, so check for linux swap signature */
 	linux_swap_magic = buf + sec_sz - 10;
 
 	for (i = 0; i < sizeof (linux_pg_size_arr)/sizeof (uint32_t); i++) {
@@ -2279,6 +2303,7 @@
 		}
 	}
 
+done:
 	kmem_free(buf, sec_sz);
 	return (rval);
 }
@@ -2331,7 +2356,6 @@
 	struct ipart	*efdp2 = &eparts[1];
 	int		ext_part_exists = 0;
 	int		ld_count = 0;
-	int		is_linux_swap;
 #endif
 
 	ASSERT(cl != NULL);
@@ -2536,17 +2560,11 @@
 				cl->cl_fmap[j].fmap_systid = systid;
 				ld_count++;
 
-				is_linux_swap = 0;
-				if (efdp1->systid == SUNIXOS) {
-					if (cmlb_is_linux_swap(cl, abs_secnum,
-					    tg_cookie) == 0) {
-						is_linux_swap = 1;
-					}
-				}
-
-				if ((efdp1->systid == SUNIXOS) ||
-				    (efdp1->systid == SUNIXOS2)) {
-					if ((uidx == -1) && (!is_linux_swap)) {
+				if ((efdp1->systid == SUNIXOS &&
+				    (cmlb_is_linux_swap(cl, abs_secnum,
+				    tg_cookie) != 0)) ||
+				    efdp1->systid == SUNIXOS2) {
+					if (uidx == -1) {
 						uidx = 0;
 						solaris_offset = abs_secnum;
 						solaris_size = ext_numsect;
@@ -2575,14 +2593,10 @@
 		 */
 		if ((uidx == -1) || (fdp->bootid == ACTIVE)) {
 #if defined(__i386) || defined(__amd64)
-			is_linux_swap = 0;
-			if (fdp->systid == SUNIXOS) {
-				if (cmlb_is_linux_swap(cl, relsect,
-				    tg_cookie) == 0) {
-					is_linux_swap = 1;
-				}
-			}
-			if (!is_linux_swap) {
+			if (fdp->systid != SUNIXOS ||
+			    (fdp->systid == SUNIXOS &&
+			    (cmlb_is_linux_swap(cl, relsect,
+			    tg_cookie) != 0))) {
 #endif
 				uidx = i;
 				solaris_offset = relsect;
--- a/usr/src/uts/common/io/comstar/stmf/lun_map.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/comstar/stmf/lun_map.c	Mon Oct 05 09:17:26 2009 -0400
@@ -1668,3 +1668,90 @@
 	}
 	return (NULL);
 }
+
+stmf_status_t
+stmf_validate_lun_view_entry(stmf_id_data_t *hg, stmf_id_data_t *tg,
+    uint8_t *lun, uint32_t *err_detail)
+{
+	char			*phg, *ptg;
+	stmf_lun_map_t		*ve_map = NULL;
+	stmf_ver_hg_t		*verhg = NULL;
+	stmf_ver_tg_t		*vertg = NULL;
+	uint16_t		lun_num;
+	stmf_status_t		ret = STMF_SUCCESS;
+
+	ASSERT(mutex_owned(&stmf_state.stmf_lock));
+
+	ve_map = stmf_duplicate_ve_map(0);
+	for (vertg = stmf_state.stmf_ver_tg_head; vertg != NULL;
+	    vertg = vertg->vert_next) {
+		ptg = (char *)vertg->vert_tg_ref->id_data;
+		if ((ptg[0] != '*') && (tg->id_data[0] != '*') &&
+		    (vertg->vert_tg_ref != tg)) {
+			continue;
+		}
+		for (verhg = vertg->vert_verh_list; verhg != NULL;
+		    verhg = verhg->verh_next) {
+			phg = (char *)verhg->verh_hg_ref->id_data;
+			if ((phg[0] != '*') && (hg->id_data[0] != '*') &&
+			    (verhg->verh_hg_ref != hg)) {
+				continue;
+			}
+			(void) stmf_merge_ve_map(&verhg->verh_ve_map, ve_map,
+			    &ve_map, 0);
+		}
+	}
+
+	ret = STMF_SUCCESS;
+	/* Return an available lun number */
+	if (lun[2] == 0xFF) {
+		/* Pick a LUN number */
+		lun_num = stmf_get_next_free_lun(ve_map, lun);
+		if (lun_num > 0x3FFF)
+			ret = STMF_NOT_SUPPORTED;
+	} else {
+		lun_num = (uint16_t)lun[1] | (((uint16_t)(lun[0] & 0x3F)) << 8);
+		if (stmf_get_ent_from_map(ve_map, lun_num) != NULL) {
+			*err_detail = STMF_IOCERR_LU_NUMBER_IN_USE;
+			ret = STMF_LUN_TAKEN;
+		}
+	}
+	stmf_destroy_ve_map(ve_map);
+
+	return (ret);
+}
+
+int
+stmf_validate_lun_ve(uint8_t *hgname, uint16_t hgname_size,
+		uint8_t *tgname, uint16_t tgname_size,
+		uint8_t *luNbr, uint32_t *err_detail)
+{
+	stmf_id_data_t		*hg;
+	stmf_id_data_t		*tg;
+	stmf_status_t		ret;
+
+	ASSERT(mutex_owned(&stmf_state.stmf_lock));
+
+	hg = stmf_lookup_id(&stmf_state.stmf_hg_list, hgname_size,
+	    (uint8_t *)hgname);
+	if (!hg) {
+		*err_detail = STMF_IOCERR_INVALID_HG;
+		return (ENOENT); /* could not find group */
+	}
+	tg = stmf_lookup_id(&stmf_state.stmf_tg_list, tgname_size,
+	    (uint8_t *)tgname);
+	if (!tg) {
+		*err_detail = STMF_IOCERR_INVALID_TG;
+		return (ENOENT); /* could not find group */
+	}
+	ret = stmf_validate_lun_view_entry(hg, tg, luNbr, err_detail);
+
+	if (ret == STMF_LUN_TAKEN) {
+		return (EEXIST);
+	} else if (ret == STMF_NOT_SUPPORTED) {
+		return (E2BIG);
+	} else if (ret != STMF_SUCCESS) {
+		return (EINVAL);
+	}
+	return (0);
+}
--- a/usr/src/uts/common/io/comstar/stmf/lun_map.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/comstar/stmf/lun_map.h	Mon Oct 05 09:17:26 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 #ifndef	_LUN_MAP_H
@@ -153,6 +153,8 @@
 int stmf_add_ve(uint8_t *hgname, uint16_t hgname_size, uint8_t *tgname,
 		uint16_t tgname_size, uint8_t *lu_guid, uint32_t *ve_id,
 		uint8_t *luNbr, uint32_t *err_detail);
+int stmf_validate_lun_ve(uint8_t *hgname, uint16_t hgname_size, uint8_t *tgname,
+		uint16_t tgname_size, uint8_t *luNbr, uint32_t *err_detail);
 int stmf_remove_ve_by_id(uint8_t *guid, uint32_t veid, uint32_t *err_detail);
 stmf_id_data_t *stmf_lookup_id(stmf_id_list_t *idlist, uint16_t id_size,
 		uint8_t *data);
--- a/usr/src/uts/common/io/comstar/stmf/stmf.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/comstar/stmf/stmf.c	Mon Oct 05 09:17:26 2009 -0400
@@ -887,6 +887,7 @@
 		    grpname->name_size, idtype, &iocd->stmf_error);
 		mutex_exit(&stmf_state.stmf_lock);
 		break;
+	case STMF_IOCTL_VALIDATE_VIEW:
 	case STMF_IOCTL_ADD_VIEW_ENTRY:
 		if (stmf_state.stmf_config_state == STMF_CONFIG_NONE) {
 			ret = EACCES;
@@ -914,14 +915,23 @@
 		else
 			veid = 0xffffffff;
 		mutex_enter(&stmf_state.stmf_lock);
-		ret = stmf_add_ve(ve->ve_host_group.name,
-		    ve->ve_host_group.name_size,
-		    ve->ve_target_group.name,
-		    ve->ve_target_group.name_size,
-		    ve->ve_guid,
-		    &veid,
-		    ve->ve_lu_nbr,
-		    &iocd->stmf_error);
+		if (cmd == STMF_IOCTL_ADD_VIEW_ENTRY) {
+			ret = stmf_add_ve(ve->ve_host_group.name,
+			    ve->ve_host_group.name_size,
+			    ve->ve_target_group.name,
+			    ve->ve_target_group.name_size,
+			    ve->ve_guid,
+			    &veid,
+			    ve->ve_lu_nbr,
+			    &iocd->stmf_error);
+		} else {  /* STMF_IOCTL_VALIDATE_VIEW */
+			ret = stmf_validate_lun_ve(ve->ve_host_group.name,
+			    ve->ve_host_group.name_size,
+			    ve->ve_target_group.name,
+			    ve->ve_target_group.name_size,
+			    ve->ve_lu_nbr,
+			    &iocd->stmf_error);
+		}
 		mutex_exit(&stmf_state.stmf_lock);
 		if (ret == 0 &&
 		    (!ve->ve_ndx_valid || !ve->ve_lu_number_valid) &&
--- a/usr/src/uts/common/io/devinfo.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/devinfo.c	Mon Oct 05 09:17:26 2009 -0400
@@ -647,7 +647,7 @@
 		rv = i_ddi_devs_attached(i);
 		modunload_enable();
 
-		i_ddi_di_cache_invalidate(KM_SLEEP);
+		i_ddi_di_cache_invalidate();
 
 		return ((rv == DDI_SUCCESS)? 0 : ENXIO);
 
@@ -2638,6 +2638,19 @@
 	}
 }
 
+static uint_t
+path_flags_convert(uint_t pi_path_flags)
+{
+	uint_t	di_path_flags = 0;
+
+	/* MDI_PATHINFO_FLAGS_HIDDEN nodes not in snapshot */
+
+	if (pi_path_flags & MDI_PATHINFO_FLAGS_DEVICE_REMOVED)
+		di_path_flags |= DI_PATH_FLAGS_DEVICE_REMOVED;
+
+	return (di_path_flags);
+}
+
 
 static di_off_t
 di_path_getprop(mdi_pathinfo_t *pip, di_off_t *off_p,
@@ -2793,13 +2806,19 @@
 
 	pip = NULL;
 	while (pip = (*next_pip)(dip, pip)) {
-		mdi_pathinfo_state_t state;
 		di_off_t stored_offset;
 
 		dcmn_err((CE_WARN, "marshalling pip = %p", (void *)pip));
 
 		mdi_pi_lock(pip);
 
+		/* We don't represent hidden paths in the snapshot */
+		if (mdi_pi_ishidden(pip)) {
+			dcmn_err((CE_WARN, "hidden, skip"));
+			mdi_pi_unlock(pip);
+			continue;
+		}
+
 		if (di_pip_find(st, pip, &stored_offset) != -1) {
 			/*
 			 * We've already seen this pathinfo node so we need to
@@ -2856,8 +2875,8 @@
 		 */
 		di_register_pip(st, pip, me->self);
 
-		state = mdi_pi_get_state(pip);
-		me->path_state = path_state_convert(state);
+		me->path_state = path_state_convert(mdi_pi_get_state(pip));
+		me->path_flags = path_flags_convert(mdi_pi_get_flags(pip));
 
 		me->path_instance = mdi_pi_get_path_instance(pip);
 
--- a/usr/src/uts/common/io/dls/dls_link.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/dls/dls_link.c	Mon Oct 05 09:17:26 2009 -0400
@@ -167,10 +167,11 @@
 		prevp->b_next = mp;
 
 		/*
-		 * The source, destination, sap, vlan id and the MSGNOLOOP
-		 * flag must all match in a given subchain.
+		 * The source, destination, sap, vlan tag must all match in
+		 * a given subchain.
 		 */
-		if (memcmp(mhip->mhi_daddr, cmhi.mhi_daddr, addr_size) != 0 ||
+		if (mhip->mhi_saddr == NULL || cmhi.mhi_saddr == NULL ||
+		    memcmp(mhip->mhi_daddr, cmhi.mhi_daddr, addr_size) != 0 ||
 		    memcmp(mhip->mhi_saddr, cmhi.mhi_saddr, addr_size) != 0 ||
 		    mhip->mhi_bindsap != cmhi.mhi_bindsap) {
 			/*
--- a/usr/src/uts/common/io/e1000g/README	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/README	Mon Oct 05 09:17:26 2009 -0400
@@ -685,9 +685,9 @@
 
 5.3.12
 ====
-   This version has the following fix:
-    6846262 T2000 fma shows fault.io.pciex.device-interr in snv_115
-    6870404 e1000g_reset can call e1000g_start after releasing dma resources
+  This version has the following fix:
+   6846262 T2000 fma shows fault.io.pciex.device-interr in snv_115
+   6870404 e1000g_reset can call e1000g_start after releasing dma resources
 
 5.3.13
 ====
@@ -700,3 +700,10 @@
   This version has the following fix:
    6820747 kstat output incorrect for MMF version of e1000g making it impossible to troubleshoot
    6847888 HW initialization updates for 82541 and 82547 chips
+
+5.3.15
+======
+  This version has the following fix:
+   6797885 need to add support for network device (8086,10ea) in a new Intel system
+   6803799 need to add network device support (8086,10ef) for a new Intel system
+   6808388 e1000g inteface experience packet lost when switch between joining and leaving a multicast stream
--- a/usr/src/uts/common/io/e1000g/e1000_80003es2lan.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_80003es2lan.c	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.76 sol_anvik_patch
+ * IntelVersion: 1.86 v3-1-3_2009-8-20
  */
 /*
  * 80003ES2LAN Gigabit Ethernet Controller (Copper)
@@ -37,9 +37,7 @@
 static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw);
 static s32 e1000_init_mac_params_80003es2lan(struct e1000_hw *hw);
 static s32 e1000_acquire_phy_80003es2lan(struct e1000_hw *hw);
-static s32  e1000_acquire_mac_csr_80003es2lan(struct e1000_hw *hw);
 static void e1000_release_phy_80003es2lan(struct e1000_hw *hw);
-static void e1000_release_mac_csr_80003es2lan(struct e1000_hw *hw);
 static s32 e1000_acquire_nvm_80003es2lan(struct e1000_hw *hw);
 static void e1000_release_nvm_80003es2lan(struct e1000_hw *hw);
 static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw,
@@ -177,6 +175,7 @@
 	 * for setting word_size.
 	 */
 	size += NVM_WORD_SIZE_BASE_SHIFT;
+
 	/* EEPROM access above 16k is unsupported */
 	if (size > 14)
 		size = 14;
@@ -269,6 +268,8 @@
 	mac->ops.mta_set = e1000_mta_set_generic;
 	/* read mac address */
 	mac->ops.read_mac_addr = e1000_read_mac_addr_80003es2lan;
+	/* ID LED init */
+	mac->ops.id_led_init = e1000_id_led_init_generic;
 	/* blink LED */
 	mac->ops.blink_led = e1000_blink_led_generic;
 	/* setup LED */
@@ -283,6 +284,8 @@
 	/* link info */
 	mac->ops.get_link_up_info = e1000_get_link_up_info_80003es2lan;
 
+	/* set lan id for port to determine which phy lock to use */
+	hw->mac.ops.set_lan_id(hw);
 out:
 	return (ret_val);
 }
@@ -301,7 +304,6 @@
 	hw->mac.ops.init_params = e1000_init_mac_params_80003es2lan;
 	hw->nvm.ops.init_params = e1000_init_nvm_params_80003es2lan;
 	hw->phy.ops.init_params = e1000_init_phy_params_80003es2lan;
-	(void) e1000_get_bus_info_pcie_generic(hw);
 }
 
 /*
@@ -536,29 +538,35 @@
 		goto out;
 	}
 
-	/*
-	 * The "ready" bit in the MDIC register may be incorrectly set
-	 * before the device has completed the "Page Select" MDI
-	 * transaction.  So we wait 200us after each MDI command...
-	 */
-	usec_delay(200);
+	if (hw->dev_spec._80003es2lan.mdic_wa_enable == true) {
+		/*
+		 * The "ready" bit in the MDIC register may be incorrectly set
+		 * before the device has completed the "Page Select" MDI
+		 * transaction.  So we wait 200us after each MDI command...
+		 */
+		usec_delay(200);
 
-	/* ...and verify the command was successful. */
-	ret_val = e1000_read_phy_reg_mdic(hw, page_select, &temp);
+		/* ...and verify the command was successful. */
+		ret_val = e1000_read_phy_reg_mdic(hw, page_select, &temp);
 
-	if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) {
-		ret_val = -E1000_ERR_PHY;
-		e1000_release_phy_80003es2lan(hw);
-		goto out;
-	}
+		if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) {
+			ret_val = -E1000_ERR_PHY;
+			e1000_release_phy_80003es2lan(hw);
+			goto out;
+		}
+
+		usec_delay(200);
 
-	usec_delay(200);
+		ret_val = e1000_read_phy_reg_mdic(hw,
+		    MAX_PHY_REG_ADDRESS & offset,
+		    data);
 
-	ret_val = e1000_read_phy_reg_mdic(hw,
-	    MAX_PHY_REG_ADDRESS & offset,
-	    data);
+		usec_delay(200);
+	} else
+		ret_val = e1000_read_phy_reg_mdic(hw,
+		    MAX_PHY_REG_ADDRESS & offset,
+		    data);
 
-	usec_delay(200);
 	e1000_release_phy_80003es2lan(hw);
 
 out:
@@ -605,29 +613,35 @@
 		goto out;
 	}
 
-	/*
-	 * The "ready" bit in the MDIC register may be incorrectly set
-	 * before the device has completed the "Page Select" MDI
-	 * transaction.  So we wait 200us after each MDI command...
-	 */
-	usec_delay(200);
+	if (hw->dev_spec._80003es2lan.mdic_wa_enable == true) {
+		/*
+		 * The "ready" bit in the MDIC register may be incorrectly set
+		 * before the device has completed the "Page Select" MDI
+		 * transaction.  So we wait 200us after each MDI command...
+		 */
+		usec_delay(200);
 
-	/* ...and verify the command was successful. */
-	ret_val = e1000_read_phy_reg_mdic(hw, page_select, &temp);
+		/* ...and verify the command was successful. */
+		ret_val = e1000_read_phy_reg_mdic(hw, page_select, &temp);
 
-	if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) {
-		ret_val = -E1000_ERR_PHY;
-		e1000_release_phy_80003es2lan(hw);
-		goto out;
-	}
+		if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) {
+			ret_val = -E1000_ERR_PHY;
+			e1000_release_phy_80003es2lan(hw);
+			goto out;
+		}
+
+		usec_delay(200);
 
-	usec_delay(200);
+		ret_val = e1000_write_phy_reg_mdic(hw,
+		    MAX_PHY_REG_ADDRESS & offset,
+		    data);
 
-	ret_val = e1000_write_phy_reg_mdic(hw,
-	    MAX_PHY_REG_ADDRESS & offset,
-	    data);
+		usec_delay(200);
+	} else
+		ret_val = e1000_write_phy_reg_mdic(hw,
+		    MAX_PHY_REG_ADDRESS & offset,
+		    data);
 
-	usec_delay(200);
 	e1000_release_phy_80003es2lan(hw);
 
 out:
@@ -697,7 +711,7 @@
 static s32
 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw)
 {
-	s32 ret_val;
+	s32 ret_val = E1000_SUCCESS;
 	u16 phy_data;
 	bool link;
 
@@ -814,16 +828,15 @@
 
 	index = phy_data & GG82563_DSPD_CABLE_LENGTH;
 
-	if (index < GG82563_CABLE_LENGTH_TABLE_SIZE + 5) {
-		phy->min_cable_length = e1000_gg82563_cable_length_table[index];
-		phy->max_cable_length =
-		    e1000_gg82563_cable_length_table[index+5];
+	if (index >= GG82563_CABLE_LENGTH_TABLE_SIZE - 5) {
+		ret_val = E1000_ERR_PHY;
+		goto out;
+	}
 
-		phy->cable_length = (phy->min_cable_length +
-		    phy->max_cable_length) / 2;
-	} else {
-		ret_val = E1000_ERR_PHY;
-	}
+	phy->min_cable_length = e1000_gg82563_cable_length_table[index];
+	phy->max_cable_length = e1000_gg82563_cable_length_table[index + 5];
+
+	phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
 
 out:
 	return (ret_val);
@@ -905,7 +918,7 @@
 	E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
 	(void) E1000_READ_REG(hw, E1000_ICR);
 
-	(void) e1000_check_alt_mac_addr_generic(hw);
+	ret_val = e1000_check_alt_mac_addr_generic(hw);
 
 out:
 	return (ret_val);
@@ -930,7 +943,7 @@
 	e1000_initialize_hw_bits_80003es2lan(hw);
 
 	/* Initialize identification LED */
-	ret_val = e1000_id_led_init_generic(hw);
+	ret_val = mac->ops.id_led_init(hw);
 	if (ret_val) {
 		DEBUGOUT("Error initializing identification LED\n");
 		/* This is not fatal and we should not stop init due to this */
@@ -984,6 +997,19 @@
 	reg_data &= ~0x00100000;
 	E1000_WRITE_REG_ARRAY(hw, E1000_FFLT, 0x0001, reg_data);
 
+	/* default to true to enable the MDIC W/A */
+	hw->dev_spec._80003es2lan.mdic_wa_enable = true;
+
+	ret_val = e1000_read_kmrn_reg_80003es2lan(hw,
+	    E1000_KMRNCTRLSTA_OFFSET >>
+	    E1000_KMRNCTRLSTA_OFFSET_SHIFT,
+	    &i);
+	if (!ret_val) {
+		if ((i & E1000_KMRNCTRLSTA_OPMODE_MASK) ==
+		    E1000_KMRNCTRLSTA_OPMODE_INBAND_MDIO)
+			hw->dev_spec._80003es2lan.mdic_wa_enable = false;
+	}
+
 	/*
 	 * Clear all of the statistics registers (clear on read).  It is
 	 * important that we do this after we have tried to establish link
@@ -1164,21 +1190,18 @@
 	if (!(hw->mac.ops.check_mng_mode(hw))) {
 		/* Enable Electrical Idle on the PHY */
 		data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE;
-		ret_val = hw->phy.ops.write_reg(hw,
-		    GG82563_PHY_PWR_MGMT_CTRL,
+		ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_PWR_MGMT_CTRL,
 		    data);
 		if (ret_val)
 			goto out;
-		ret_val = hw->phy.ops.read_reg(hw,
-		    GG82563_PHY_KMRN_MODE_CTRL,
+		ret_val = hw->phy.ops.read_reg(hw, GG82563_PHY_KMRN_MODE_CTRL,
 		    &data);
 
 		if (ret_val)
 			goto out;
 
 		data &= ~GG82563_KMCR_PASS_FALSE_CARRIER;
-		ret_val = hw->phy.ops.write_reg(hw,
-		    GG82563_PHY_KMRN_MODE_CTRL,
+		ret_val = hw->phy.ops.write_reg(hw, GG82563_PHY_KMRN_MODE_CTRL,
 		    data);
 
 		if (ret_val)
@@ -1417,7 +1440,7 @@
  * using the kumeran interface.  The information retrieved is stored in data.
  * Release the semaphore before exiting.
  */
-s32
+static s32
 e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset, u16 *data)
 {
 	u32 kmrnctrlsta;
@@ -1454,7 +1477,7 @@
  * at the offset using the kumeran interface.  Release semaphore
  * before exiting.
  */
-s32
+static s32
 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset, u16 data)
 {
 	u32 kmrnctrlsta;
@@ -1488,9 +1511,18 @@
 	s32 ret_val = E1000_SUCCESS;
 
 	DEBUGFUNC("e1000_read_mac_addr_80003es2lan");
-	if (e1000_check_alt_mac_addr_generic(hw))
-		ret_val = e1000_read_mac_addr_generic(hw);
+	/*
+	 * If there's an alternate MAC address place it in RAR0
+	 * so that it will override the Si installed default perm
+	 * address.
+	 */
+	ret_val = e1000_check_alt_mac_addr_generic(hw);
+	if (ret_val)
+		goto out;
 
+	ret_val = e1000_read_mac_addr_generic(hw);
+
+out:
 	return (ret_val);
 }
 
--- a/usr/src/uts/common/io/e1000g/e1000_80003es2lan.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_80003es2lan.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.9 sol_anvik_patch
+ * IntelVersion: 1.10 v3-1-3_2009-8-20
  */
 #ifndef _E1000_80003ES2LAN_H_
 #define	_E1000_80003ES2LAN_H_
@@ -46,8 +46,11 @@
 #define	E1000_KMRNCTRLSTA_HD_CTRL_1000_DEFAULT	0x0000
 #define	E1000_KMRNCTRLSTA_OPMODE_E_IDLE		0x2000
 
+#define	E1000_KMRNCTRLSTA_OPMODE_MASK		0x000C
+#define	E1000_KMRNCTRLSTA_OPMODE_INBAND_MDIO	0x0004
+
 /* Gigabit Carry Extend Padding */
-#define	E1000_TCTL_EXT_GCEX_MASK		0x000FFC00
+#define	E1000_TCTL_EXT_GCEX_MASK	0x000FFC00
 
 #define	DEFAULT_TCTL_EXT_GCEX_80003ES2LAN	0x00010000
 
--- a/usr/src/uts/common/io/e1000g/e1000_82540.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_82540.c	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.54 sol_anvik_patch
+ * IntelVersion: 1.57 v3-1-3_2009-8-20
  */
 
 /*
@@ -52,6 +52,7 @@
 static s32 e1000_setup_copper_link_82540(struct e1000_hw *hw);
 static s32 e1000_setup_fiber_serdes_link_82540(struct e1000_hw *hw);
 static void e1000_power_down_phy_copper_82540(struct e1000_hw *hw);
+static s32 e1000_read_mac_addr_82540(struct e1000_hw *hw);
 
 /*
  * e1000_init_phy_params_82540 - Init PHY func ptrs.
@@ -184,6 +185,8 @@
 
 	/* bus type/speed/width */
 	mac->ops.get_bus_info = e1000_get_bus_info_pci_generic;
+	/* function id */
+	mac->ops.set_lan_id = e1000_set_lan_id_multi_port_pci;
 	/* reset */
 	mac->ops.reset_hw = e1000_reset_hw_82540;
 	/* hw initialization */
@@ -223,8 +226,12 @@
 	mac->ops.clear_vfta = e1000_clear_vfta_generic;
 	/* setting MTA */
 	mac->ops.mta_set = e1000_mta_set_generic;
+	/* ID LED init */
+	mac->ops.id_led_init = e1000_id_led_init_generic;
 	/* setup LED */
 	mac->ops.setup_led = e1000_setup_led_generic;
+	/* read mac address */
+	mac->ops.read_mac_addr = e1000_read_mac_addr_82540;
 	/* cleanup LED */
 	mac->ops.cleanup_led = e1000_cleanup_led_generic;
 	/* turn on/off LED */
@@ -254,10 +261,10 @@
 }
 
 /*
- *  e1000_reset_hw_82540 - Reset hardware
- *  @hw: pointer to the HW structure
+ * e1000_reset_hw_82540 - Reset hardware
+ * @hw: pointer to the HW structure
  *
- *  This resets the hardware into a known state.
+ * This resets the hardware into a known state.
  */
 static s32
 e1000_reset_hw_82540(struct e1000_hw *hw)
@@ -313,10 +320,10 @@
 }
 
 /*
- *  e1000_init_hw_82540 - Initialize hardware
- *  @hw: pointer to the HW structure
+ * e1000_init_hw_82540 - Initialize hardware
+ * @hw: pointer to the HW structure
  *
- *  This inits the hardware readying it for operation.
+ * This inits the hardware readying it for operation.
  */
 static s32
 e1000_init_hw_82540(struct e1000_hw *hw)
@@ -329,7 +336,7 @@
 	DEBUGFUNC("e1000_init_hw_82540");
 
 	/* Initialize identification LED */
-	ret_val = e1000_id_led_init_generic(hw);
+	ret_val = mac->ops.id_led_init(hw);
 	if (ret_val) {
 		DEBUGOUT("Error initializing identification LED\n");
 		/* This is not fatal and we should not stop init due to this */
@@ -393,13 +400,13 @@
 }
 
 /*
- *  e1000_setup_copper_link_82540 - Configure copper link settings
- *  @hw: pointer to the HW structure
+ * e1000_setup_copper_link_82540 - Configure copper link settings
+ * @hw: pointer to the HW structure
  *
- *  Calls the appropriate function to configure the link for auto-neg or forced
- *  speed and duplex.  Then we check for link, once link is established calls
- *  to configure collision distance and flow control are called.  If link is
- *  not established, we return -E1000_ERR_PHY (-2).
+ * Calls the appropriate function to configure the link for auto-neg or forced
+ * speed and duplex.  Then we check for link, once link is established calls
+ * to configure collision distance and flow control are called.  If link is
+ * not established, we return -E1000_ERR_PHY (-2).
  */
 static s32
 e1000_setup_copper_link_82540(struct e1000_hw *hw)
@@ -442,13 +449,13 @@
 }
 
 /*
- *  e1000_setup_fiber_serdes_link_82540 - Setup link for fiber/serdes
- *  @hw: pointer to the HW structure
+ * e1000_setup_fiber_serdes_link_82540 - Setup link for fiber/serdes
+ * @hw: pointer to the HW structure
  *
- *  Set the output amplitude to the value in the EEPROM and adjust the VCO
- *  speed to improve Bit Error Rate (BER) performance.  Configures collision
- *  distance and flow control for fiber and serdes links.  Upon successful
- *  setup, poll for link.
+ * Set the output amplitude to the value in the EEPROM and adjust the VCO
+ * speed to improve Bit Error Rate (BER) performance.  Configures collision
+ * distance and flow control for fiber and serdes links.  Upon successful
+ * setup, poll for link.
  */
 static s32
 e1000_setup_fiber_serdes_link_82540(struct e1000_hw *hw)
@@ -485,10 +492,10 @@
 }
 
 /*
- *  e1000_adjust_serdes_amplitude_82540 - Adjust amplitude based on EEPROM
- *  @hw: pointer to the HW structure
+ * e1000_adjust_serdes_amplitude_82540 - Adjust amplitude based on EEPROM
+ * @hw: pointer to the HW structure
  *
- *  Adjust the SERDES output amplitude based on the EEPROM settings.
+ * Adjust the SERDES output amplitude based on the EEPROM settings.
  */
 static s32
 e1000_adjust_serdes_amplitude_82540(struct e1000_hw *hw)
@@ -516,10 +523,10 @@
 }
 
 /*
- *  e1000_set_vco_speed_82540 - Set VCO speed for better performance
- *  @hw: pointer to the HW structure
+ * e1000_set_vco_speed_82540 - Set VCO speed for better performance
+ * @hw: pointer to the HW structure
  *
- *  Set the VCO speed to improve Bit Error Rate (BER) performance.
+ * Set the VCO speed to improve Bit Error Rate (BER) performance.
  */
 static s32
 e1000_set_vco_speed_82540(struct e1000_hw *hw)
@@ -574,13 +581,13 @@
 }
 
 /*
- *  e1000_set_phy_mode_82540 - Set PHY to class A mode
- *  @hw: pointer to the HW structure
+ * e1000_set_phy_mode_82540 - Set PHY to class A mode
+ * @hw: pointer to the HW structure
  *
- *  Sets the PHY to class A mode and assumes the following operations will
- *  follow to enable the new class mode:
- *    1.  Do a PHY soft reset.
- *    2.  Restart auto-negotiation or force link.
+ * Sets the PHY to class A mode and assumes the following operations will
+ * follow to enable the new class mode:
+ * 1.  Do a PHY soft reset.
+ * 2.  Restart auto-negotiation or force link.
  */
 static s32
 e1000_set_phy_mode_82540(struct e1000_hw *hw)
@@ -638,10 +645,10 @@
 }
 
 /*
- *  e1000_clear_hw_cntrs_82540 - Clear device specific hardware counters
- *  @hw: pointer to the HW structure
+ * e1000_clear_hw_cntrs_82540 - Clear device specific hardware counters
+ * @hw: pointer to the HW structure
  *
- *  Clears the hardware counters by reading the counter registers.
+ * Clears the hardware counters by reading the counter registers.
  */
 static void
 e1000_clear_hw_cntrs_82540(struct e1000_hw *hw)
@@ -674,3 +681,47 @@
 	(void) E1000_READ_REG(hw, E1000_MGTPDC);
 	(void) E1000_READ_REG(hw, E1000_MGTPTC);
 }
+
+/*
+ * e1000_read_mac_addr_82540 - Read device MAC address
+ * @hw: pointer to the HW structure
+ *
+ * Reads the device MAC address from the EEPROM and stores the value.
+ * Since devices with two ports use the same EEPROM, we increment the
+ * last bit in the MAC address for the second port.
+ *
+ * This version is being used over generic because of customer issues
+ * with VmWare and Virtual Box when using generic. It seems in
+ * the emulated 82545, RAR[0] does NOT have a valid address after a
+ * reset, this older method works and using this breaks nothing for
+ * these legacy adapters.
+ */
+s32
+e1000_read_mac_addr_82540(struct e1000_hw *hw)
+{
+	s32  ret_val = E1000_SUCCESS;
+	u16 offset, nvm_data, i;
+
+	DEBUGFUNC("e1000_read_mac_addr");
+
+	for (i = 0; i < ETH_ADDR_LEN; i += 2) {
+		offset = i >> 1;
+		ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data);
+		if (ret_val) {
+			DEBUGOUT("NVM Read Error\n");
+			goto out;
+		}
+		hw->mac.perm_addr[i] = (u8)(nvm_data & 0xFF);
+		hw->mac.perm_addr[i+1] = (u8)(nvm_data >> 8);
+	}
+
+	/* Flip last bit of mac address if we're on second port */
+	if (hw->bus.func == E1000_FUNC_1)
+		hw->mac.perm_addr[5] ^= 1;
+
+	for (i = 0; i < ETH_ADDR_LEN; i++)
+		hw->mac.addr[i] = hw->mac.perm_addr[i];
+
+out:
+	return (ret_val);
+}
--- a/usr/src/uts/common/io/e1000g/e1000_82541.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_82541.c	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.64 sol_anvik_patch
+ * IntelVersion: 1.68 v3-1-3_2009-8-20
  */
 
 /*
@@ -112,6 +112,7 @@
 		ret_val = -E1000_ERR_PHY;
 		goto out;
 	}
+
 out:
 	return (ret_val);
 }
@@ -237,6 +238,8 @@
 
 	/* bus type/speed/width */
 	mac->ops.get_bus_info = e1000_get_bus_info_pci_generic;
+	/* function id */
+	mac->ops.set_lan_id = e1000_set_lan_id_single_port;
 	/* reset */
 	mac->ops.reset_hw = e1000_reset_hw_82541;
 	/* hw initialization */
@@ -257,6 +260,8 @@
 	mac->ops.clear_vfta = e1000_clear_vfta_generic;
 	/* setting MTA */
 	mac->ops.mta_set = e1000_mta_set_generic;
+	/* ID LED init */
+	mac->ops.id_led_init = e1000_id_led_init_generic;
 	/* setup LED */
 	mac->ops.setup_led = e1000_setup_led_82541;
 	/* cleanup LED */
@@ -385,20 +390,17 @@
 	DEBUGFUNC("e1000_init_hw_82541");
 
 	/* Initialize identification LED */
-	ret_val = e1000_id_led_init_generic(hw);
+	ret_val = mac->ops.id_led_init(hw);
 	if (ret_val) {
 		DEBUGOUT("Error initializing identification LED\n");
 		/* This is not fatal and we should not stop init due to this */
 	}
 
-	/*
-	 * workaround - Store Default value in dev_spec->spd_default for
-	 * later use
-	 */
-	ret_val = hw->phy.ops.read_reg(hw,
-	    IGP01E1000_GMII_FIFO,
-	    &dev_spec->spd_default);
-	if (ret_val)
+	/* Storing the Speed Power Down  value for later use */
+	ret_val = hw->phy.ops.read_reg(hw,
+	    IGP01E1000_GMII_FIFO,
+	    &dev_spec->spd_default);
+	if (ret_val)
 		goto out;
 
 	pba = E1000_READ_REG(hw, E1000_PBA);
--- a/usr/src/uts/common/io/e1000g/e1000_82541.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_82541.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.9 sol_anvik_patch
+ * IntelVersion: 1.9 v3-1-3_2009-8-20
  */
 #ifndef _E1000_82541_H_
 #define	_E1000_82541_H_
--- a/usr/src/uts/common/io/e1000g/e1000_82542.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_82542.c	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.50 sol_anvik_patch
+ * IntelVersion: 1.53 v3-1-3_2009-8-20
  */
 
 /*
@@ -44,6 +44,7 @@
 static s32 e1000_led_off_82542(struct e1000_hw *hw);
 static void e1000_rar_set_82542(struct e1000_hw *hw, u8 *addr, u32 index);
 static void e1000_clear_hw_cntrs_82542(struct e1000_hw *hw);
+static s32  e1000_read_mac_addr_82542(struct e1000_hw *hw);
 
 /*
  * e1000_init_phy_params_82542 - Init PHY func ptrs.
@@ -112,6 +113,8 @@
 
 	/* bus type/speed/width */
 	mac->ops.get_bus_info = e1000_get_bus_info_82542;
+	/* function id */
+	mac->ops.set_lan_id = e1000_set_lan_id_multi_port_pci;
 	/* reset */
 	mac->ops.reset_hw = e1000_reset_hw_82542;
 	/* hw initialization */
@@ -131,6 +134,8 @@
 	mac->ops.clear_vfta = e1000_clear_vfta_generic;
 	/* setting MTA */
 	mac->ops.mta_set = e1000_mta_set_generic;
+	/* read mac address */
+	mac->ops.read_mac_addr = e1000_read_mac_addr_82542;
 	/* set RAR */
 	mac->ops.rar_set = e1000_rar_set_82542;
 	/* turn on/off LED */
@@ -561,3 +566,35 @@
 	(void) E1000_READ_REG(hw, E1000_PTC1023);
 	(void) E1000_READ_REG(hw, E1000_PTC1522);
 }
+
+/*
+ * e1000_read_mac_addr_82542 - Read device MAC address
+ * @hw: pointer to the HW structure
+ *
+ * Reads the device MAC address from the EEPROM and stores the value.
+ */
+s32
+e1000_read_mac_addr_82542(struct e1000_hw *hw)
+{
+	s32  ret_val = E1000_SUCCESS;
+	u16 offset, nvm_data, i;
+
+	DEBUGFUNC("e1000_read_mac_addr");
+
+	for (i = 0; i < ETH_ADDR_LEN; i += 2) {
+		offset = i >> 1;
+		ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data);
+		if (ret_val) {
+			DEBUGOUT("NVM Read Error\n");
+			goto out;
+		}
+		hw->mac.perm_addr[i] = (u8)(nvm_data & 0xFF);
+		hw->mac.perm_addr[i+1] = (u8)(nvm_data >> 8);
+	}
+
+	for (i = 0; i < ETH_ADDR_LEN; i++)
+		hw->mac.addr[i] = hw->mac.perm_addr[i];
+
+	out:
+		return (ret_val);
+}
--- a/usr/src/uts/common/io/e1000g/e1000_82543.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_82543.c	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.67 sol_anvik_patch
+ * IntelVersion: 1.68 v3-1-3_2009-8-20
  */
 
 /*
@@ -212,6 +212,8 @@
 
 	/* bus type/speed/width */
 	mac->ops.get_bus_info = e1000_get_bus_info_pci_generic;
+	/* function id */
+	mac->ops.set_lan_id = e1000_set_lan_id_multi_port_pci;
 	/* reset */
 	mac->ops.reset_hw = e1000_reset_hw_82543;
 	/* hw initialization */
--- a/usr/src/uts/common/io/e1000g/e1000_82543.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_82543.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.8 sol_anvik_patch
+ * IntelVersion: 1.8 v3-1-3_2009-8-20
  */
 #ifndef _E1000_82543_H_
 #define	_E1000_82543_H_
--- a/usr/src/uts/common/io/e1000g/e1000_82571.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_82571.c	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.94 sol_anvik_patch
+ * IntelVersion: 1.113 v3-1-3_2009-8-20
  */
 
 /*
@@ -41,6 +41,7 @@
  * 82573E Gigabit Ethernet Controller (Copper)
  * 82573L Gigabit Ethernet Controller
  * 82574L Gigabit Network Connection
+ * 82583V Gigabit Network Connection
  */
 
 #include "e1000_api.h"
@@ -62,15 +63,13 @@
 static void e1000_clear_vfta_82571(struct e1000_hw *hw);
 static bool e1000_check_mng_mode_82574(struct e1000_hw *hw);
 static s32 e1000_led_on_82574(struct e1000_hw *hw);
-static void e1000_update_mc_addr_list_82571(struct e1000_hw *hw,
-    u8 *mc_addr_list, u32 mc_addr_count,
-    u32 rar_used_count, u32 rar_count);
 static s32 e1000_setup_link_82571(struct e1000_hw *hw);
 static s32 e1000_setup_copper_link_82571(struct e1000_hw *hw);
+static s32  e1000_check_for_serdes_link_82571(struct e1000_hw *hw);
 static s32 e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw);
 static s32 e1000_valid_led_default_82571(struct e1000_hw *hw, u16 *data);
 static void e1000_clear_hw_cntrs_82571(struct e1000_hw *hw);
-static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw);
+static s32  e1000_get_hw_semaphore_82571(struct e1000_hw *hw);
 static s32 e1000_fix_nvm_checksum_82571(struct e1000_hw *hw);
 static s32 e1000_get_phy_id_82571(struct e1000_hw *hw);
 static void e1000_put_hw_semaphore_82571(struct e1000_hw *hw);
@@ -152,6 +151,7 @@
 		}
 		break;
 	case e1000_82574:
+	case e1000_82583:
 		phy->type = e1000_phy_bm;
 		phy->ops.get_cfg_done = e1000_get_cfg_done_generic;
 		phy->ops.get_info = e1000_get_phy_info_m88;
@@ -212,6 +212,7 @@
 	switch (hw->mac.type) {
 	case e1000_82573:
 	case e1000_82574:
+	case e1000_82583:
 		if (((eecd >> 15) & 0x3) == 0x3) {
 			nvm->type = e1000_nvm_flash_hw;
 			nvm->word_size = 2048;
@@ -262,6 +263,9 @@
 {
 	struct e1000_mac_info *mac = &hw->mac;
 	s32 ret_val = E1000_SUCCESS;
+	u32 swsm  = 0;
+	u32 swsm2  = 0;
+	bool force_clear_smbi = false;
 
 	DEBUGFUNC("e1000_init_mac_params_82571");
 
@@ -298,6 +302,16 @@
 
 	/* bus type/speed/width */
 	mac->ops.get_bus_info = e1000_get_bus_info_pcie_generic;
+	/* function id */
+	switch (hw->mac.type) {
+	case e1000_82573:
+	case e1000_82574:
+	case e1000_82583:
+		mac->ops.set_lan_id = e1000_set_lan_id_single_port;
+		break;
+	default:
+		break;
+	}
 	/* reset */
 	mac->ops.reset_hw = e1000_reset_hw_82571;
 	/* hw initialization */
@@ -318,7 +332,7 @@
 		mac->ops.check_for_link = e1000_check_for_fiber_link_generic;
 		break;
 	case e1000_media_type_internal_serdes:
-		mac->ops.check_for_link = e1000_check_for_serdes_link_generic;
+		mac->ops.check_for_link = e1000_check_for_serdes_link_82571;
 		break;
 	default:
 		ret_val = -E1000_ERR_CONFIG;
@@ -327,6 +341,7 @@
 	/* check management mode */
 	switch (hw->mac.type) {
 	case e1000_82574:
+	case e1000_82583:
 		mac->ops.check_mng_mode = e1000_check_mng_mode_82574;
 		break;
 	default:
@@ -334,7 +349,7 @@
 		break;
 	}
 	/* multicast address update */
-	mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_82571;
+	mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
 	/* writing VFTA */
 	mac->ops.write_vfta = e1000_write_vfta_generic;
 	/* clearing VFTA */
@@ -343,6 +358,8 @@
 	mac->ops.mta_set = e1000_mta_set_generic;
 	/* read mac address */
 	mac->ops.read_mac_addr = e1000_read_mac_addr_82571;
+	/* ID LED init */
+	mac->ops.id_led_init = e1000_id_led_init_generic;
 	/* blink LED */
 	mac->ops.blink_led = e1000_blink_led_generic;
 	/* setup LED */
@@ -352,6 +369,7 @@
 	/* turn on/off LED */
 	switch (hw->mac.type) {
 	case e1000_82574:
+	case e1000_82583:
 		mac->ops.led_on = e1000_led_on_82574;
 		break;
 	default:
@@ -367,6 +385,51 @@
 	    ? e1000_get_speed_and_duplex_copper_generic
 	    : e1000_get_speed_and_duplex_fiber_serdes_generic;
 
+	/*
+	 * Ensure that the inter-port SWSM.SMBI lock bit is clear before
+	 * first NVM or PHY acess. This should be done for single-port
+	 * devices, and for one port only on dual-port devices so that
+	 * for those devices we can still use the SMBI lock to synchronize
+	 * inter-port accesses to the PHY & NVM.
+	 */
+	switch (hw->mac.type) {
+	case e1000_82571:
+	case e1000_82572:
+		swsm2 = E1000_READ_REG(hw, E1000_SWSM2);
+
+		if (!(swsm2 & E1000_SWSM2_LOCK)) {
+			/* Only do this for the first interface on this card */
+			E1000_WRITE_REG(hw, E1000_SWSM2,
+			    swsm2 | E1000_SWSM2_LOCK);
+				force_clear_smbi = true;
+			} else
+				force_clear_smbi = false;
+		break;
+	default:
+		force_clear_smbi = true;
+		break;
+	}
+
+	if (force_clear_smbi) {
+		/* Make sure SWSM.SMBI is clear */
+		swsm = E1000_READ_REG(hw, E1000_SWSM);
+		if (swsm & E1000_SWSM_SMBI) {
+			/*
+			 * This bit should not be set on a first interface, and
+			 * indicates that the bootagent or EFI code has
+			 * improperly left this bit enabled
+			 */
+			DEBUGOUT("Please update your 82571 Bootagent\n");
+		}
+		E1000_WRITE_REG(hw, E1000_SWSM, swsm & ~E1000_SWSM_SMBI);
+	}
+
+	/*
+	 * Initialze device specific counter of SMBI acquisition
+	 * timeouts.
+	 */
+	hw->dev_spec._82571.smb_counter = 0;
+
 out:
 	return (ret_val);
 }
@@ -417,6 +480,7 @@
 		ret_val = e1000_get_phy_id(hw);
 		break;
 	case e1000_82574:
+	case e1000_82583:
 		ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id);
 		if (ret_val)
 			goto out;
@@ -445,18 +509,44 @@
  *
  * Acquire the HW semaphore to access the PHY or NVM
  */
-static s32
+s32
 e1000_get_hw_semaphore_82571(struct e1000_hw *hw)
 {
 	u32 swsm;
 	s32 ret_val = E1000_SUCCESS;
-	s32 timeout = hw->nvm.word_size + 1;
+	s32 sw_timeout = hw->nvm.word_size + 1;
+	s32 fw_timeout = hw->nvm.word_size + 1;
 	s32 i = 0;
 
 	DEBUGFUNC("e1000_get_hw_semaphore_82571");
 
+	/*
+	 * If we have timedout 3 times on trying to acquire
+	 * the inter-port SMBI semaphore, there is old code
+	 * operating on the other port, and it is not
+	 * releasing SMBI. Modify the number of times that
+	 * we try for the semaphore to interwork with this
+	 * older code.
+	 */
+	if (hw->dev_spec._82571.smb_counter > 2)
+		sw_timeout = 1;
+
+	/* Get the SW semaphore */
+	while (i < sw_timeout) {
+		swsm = E1000_READ_REG(hw, E1000_SWSM);
+		if (!(swsm & E1000_SWSM_SMBI))
+			break;
+
+		usec_delay(50);
+		i++;
+	}
+
+	if (i == sw_timeout) {
+		DEBUGOUT("Driver can't access device - SMBI bit is set.\n");
+		hw->dev_spec._82571.smb_counter++;
+	}
 	/* Get the FW semaphore. */
-	for (i = 0; i < timeout; i++) {
+	for (i = 0; i < fw_timeout; i++) {
 		swsm = E1000_READ_REG(hw, E1000_SWSM);
 		E1000_WRITE_REG(hw, E1000_SWSM, swsm | E1000_SWSM_SWESMBI);
 
@@ -467,9 +557,9 @@
 		usec_delay(50);
 	}
 
-	if (i == timeout) {
+	if (i == fw_timeout) {
 		/* Release semaphores */
-		e1000_put_hw_semaphore_generic(hw);
+		e1000_put_hw_semaphore_82571(hw);
 		DEBUGOUT("Driver can't access the NVM\n");
 		ret_val = -E1000_ERR_NVM;
 		goto out;
@@ -485,16 +575,16 @@
  *
  * Release hardware semaphore used to access the PHY or NVM
  */
-static void
+void
 e1000_put_hw_semaphore_82571(struct e1000_hw *hw)
 {
 	u32 swsm;
 
-	DEBUGFUNC("e1000_put_hw_semaphore_82571");
+	DEBUGFUNC("e1000_put_hw_semaphore_generic");
 
 	swsm = E1000_READ_REG(hw, E1000_SWSM);
 
-	swsm &= ~E1000_SWSM_SWESMBI;
+	swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI);
 
 	E1000_WRITE_REG(hw, E1000_SWSM, swsm);
 }
@@ -519,8 +609,15 @@
 	if (ret_val)
 		goto out;
 
-	if (hw->mac.type != e1000_82573 && hw->mac.type != e1000_82574)
+	switch (hw->mac.type) {
+	case e1000_82573:
+	case e1000_82574:
+	case e1000_82583:
+		break;
+	default:
 		ret_val = e1000_acquire_nvm_generic(hw);
+		break;
+	}
 
 	if (ret_val)
 		e1000_put_hw_semaphore_82571(hw);
@@ -567,6 +664,7 @@
 	switch (hw->mac.type) {
 	case e1000_82573:
 	case e1000_82574:
+	case e1000_82583:
 		ret_val = e1000_write_nvm_eewr_82571(hw, offset, words, data);
 		break;
 	case e1000_82571:
@@ -747,6 +845,7 @@
 	}
 	if (!timeout) {
 		DEBUGOUT("MNG configuration cycle has not completed.\n");
+		ret_val = -E1000_ERR_RESET;
 		goto out;
 	}
 
@@ -875,7 +974,10 @@
 	 * Must acquire the MDIO ownership before MAC reset. Ownership
 	 * defaults to firmware after a reset.
 	 */
-	if (hw->mac.type == e1000_82573 || hw->mac.type == e1000_82574) {
+	switch (hw->mac.type) {
+	case e1000_82573:
+	case e1000_82574:
+	case e1000_82583:
 		extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
 		extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP;
 
@@ -891,6 +993,9 @@
 			msec_delay(2);
 			i++;
 		} while (i < MDIO_OWNERSHIP_TIMEOUT);
+		break;
+	default:
+		break;
 	}
 
 	ctrl = E1000_READ_REG(hw, E1000_CTRL);
@@ -916,15 +1021,30 @@
 	 * Need to wait for Phy configuration completion before accessing
 	 * NVM and Phy.
 	 */
-	if (hw->mac.type == e1000_82573 || hw->mac.type == e1000_82574)
+	switch (hw->mac.type) {
+	case e1000_82573:
+	case e1000_82574:
+	case e1000_82583:
 		msec_delay(25);
+		break;
+	default:
+		break;
+	}
 
 	/* Clear any pending interrupt events. */
 	E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
 	(void) E1000_READ_REG(hw, E1000_ICR);
 
-	if (!(e1000_check_alt_mac_addr_generic(hw)))
-		e1000_set_laa_state_82571(hw, true);
+	/* Install any alternate MAC address into RAR0 */
+	ret_val = e1000_check_alt_mac_addr_generic(hw);
+	if (ret_val)
+		goto out;
+
+	e1000_set_laa_state_82571(hw, true);
+
+	/* Reinitialize the 82571 serdes link state machine */
+	if (hw->phy.media_type == e1000_media_type_internal_serdes)
+		hw->mac.serdes_link_state = e1000_serdes_link_down;
 
 out:
 	return (ret_val);
@@ -949,7 +1069,7 @@
 	e1000_initialize_hw_bits_82571(hw);
 
 	/* Initialize identification LED */
-	ret_val = e1000_id_led_init_generic(hw);
+	ret_val = mac->ops.id_led_init(hw);
 	if (ret_val) {
 		DEBUGOUT("Error initializing identification LED\n");
 		/* This is not fatal and we should not stop init due to this */
@@ -985,17 +1105,22 @@
 	E1000_WRITE_REG(hw, E1000_TXDCTL(0), reg_data);
 
 	/* ...for both queues. */
-	if (mac->type != e1000_82573 && mac->type != e1000_82574) {
+	switch (mac->type) {
+	case e1000_82573:
+	case e1000_82574:
+	case e1000_82583:
+		e1000_enable_tx_pkt_filtering_generic(hw);
+		reg_data = E1000_READ_REG(hw, E1000_GCR);
+		reg_data |= E1000_GCR_L1_ACT_WITHOUT_L0S_RX;
+		E1000_WRITE_REG(hw, E1000_GCR, reg_data);
+		break;
+	default:
 		reg_data = E1000_READ_REG(hw, E1000_TXDCTL(1));
 		reg_data = (reg_data & ~E1000_TXDCTL_WTHRESH) |
 		    E1000_TXDCTL_FULL_TX_DESC_WB |
 		    E1000_TXDCTL_COUNT_DESC;
 		E1000_WRITE_REG(hw, E1000_TXDCTL(1), reg_data);
-	} else {
-		(void) e1000_enable_tx_pkt_filtering_generic(hw);
-		reg_data = E1000_READ_REG(hw, E1000_GCR);
-		reg_data |= E1000_GCR_L1_ACT_WITHOUT_L0S_RX;
-		E1000_WRITE_REG(hw, E1000_GCR, reg_data);
+		break;
 	}
 
 	/*
@@ -1063,25 +1188,69 @@
 	}
 
 	/* Device Control */
-	if (hw->mac.type == e1000_82573 || hw->mac.type == e1000_82574) {
+	switch (hw->mac.type) {
+	case e1000_82573:
+	case e1000_82574:
+	case e1000_82583:
 		reg = E1000_READ_REG(hw, E1000_CTRL);
 		reg &= ~(1 << 29);
 		E1000_WRITE_REG(hw, E1000_CTRL, reg);
+		break;
+	default:
+		break;
 	}
 
 	/* Extended Device Control */
-	if (hw->mac.type == e1000_82573 || hw->mac.type == e1000_82574) {
+	switch (hw->mac.type) {
+	case e1000_82573:
+	case e1000_82574:
+	case e1000_82583:
 		reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
 		reg &= ~(1 << 23);
 		reg |= (1 << 22);
 		E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg);
+		break;
+	default:
+		break;
+	}
+
+	if (hw->mac.type == e1000_82571) {
+		reg = E1000_READ_REG(hw, E1000_PBA_ECC);
+		reg |= E1000_PBA_ECC_CORR_EN;
+		E1000_WRITE_REG(hw, E1000_PBA_ECC, reg);
 	}
 
-	/* PCI-Ex Control Register */
-	if (hw->mac.type == e1000_82574) {
+	/*
+	 * Workaround for hardware errata.
+	 * Ensure that DMA Dynamic Clock gating is disabled on 82571 and 82572
+	 */
+	if ((hw->mac.type == e1000_82571) ||
+	    (hw->mac.type == e1000_82572)) {
+		reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
+		reg &= ~E1000_CTRL_EXT_DMA_DYN_CLK_EN;
+		E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg);
+	}
+
+	/* PCI-Ex Control Registers */
+	switch (hw->mac.type) {
+	case e1000_82574:
+	case e1000_82583:
 		reg = E1000_READ_REG(hw, E1000_GCR);
 		reg |= (1 << 22);
 		E1000_WRITE_REG(hw, E1000_GCR, reg);
+		/*
+		 * Workaround for hardware errata.
+		 * apply workaround for hardware errata documented in errata
+		 * docs Fixes issue where some error prone or unreliable PCIe
+		 * completions are occurring, particularly with ASPM enabled.
+		 * Without fix, issue can cause tx timeouts.
+		 */
+		reg = E1000_READ_REG(hw, E1000_GCR2);
+		reg |= 1;
+		E1000_WRITE_REG(hw, E1000_GCR2, reg);
+		break;
+	default:
+		break;
 	}
 }
 
@@ -1102,31 +1271,41 @@
 
 	DEBUGFUNC("e1000_clear_vfta_82571");
 
-	if (hw->mac.type == e1000_82573 || hw->mac.type == e1000_82574) {
+	switch (hw->mac.type) {
+	case e1000_82573:
+	case e1000_82574:
+	case e1000_82583:
 		if (hw->mng_cookie.vlan_id != 0) {
 			/*
-			 * The VFTA is a 4096b bit-field, each identifying a
-			 * single VLAN ID.  The following operations determine
-			 * which 32b entry (i.e. offset) into the array we
-			 * want to set the VLAN ID (i.e. bit) of the
-			 * manageability unit.
+			 * The VFTA is a 4096b bit-field, each identifying
+			 * a single VLAN ID.  The following operations
+			 * determine which 32b entry (i.e. offset) into the
+			 * array we want to set the VLAN ID (i.e. bit) of
+			 * the manageability unit.
 			 */
 			vfta_offset = (hw->mng_cookie.vlan_id >>
-			    E1000_VFTA_ENTRY_SHIFT) &
-			    E1000_VFTA_ENTRY_MASK;
+			    E1000_VFTA_ENTRY_SHIFT) & E1000_VFTA_ENTRY_MASK;
+
 			vfta_bit_in_reg = 1 << (hw->mng_cookie.vlan_id &
 			    E1000_VFTA_ENTRY_BIT_SHIFT_MASK);
 		}
-	}
-	for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) {
-		/*
-		 * If the offset we want to clear is the same offset of the
-		 * manageability VLAN ID, then clear all bits except that of
-		 * the manageability unit.
-		 */
-		vfta_value = (offset == vfta_offset) ? vfta_bit_in_reg : 0;
-		E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, vfta_value);
-		E1000_WRITE_FLUSH(hw);
+
+		for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE;
+		    offset ++) {
+			/*
+			 * If the offset we want to clear is the same offset of
+			 * the manageability VLAN ID, then clear all bits except
+			 * that of the manageability unit
+			 */
+			vfta_value = (offset == vfta_offset) ?
+			    vfta_bit_in_reg : 0;
+			E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset,
+			    vfta_value);
+			E1000_WRITE_FLUSH(hw);
+		}
+		break;
+	default:
+		break;
 	}
 }
 
@@ -1179,33 +1358,6 @@
 }
 
 /*
- * e1000_update_mc_addr_list_82571 - Update Multicast addresses
- * @hw: pointer to the HW structure
- * @mc_addr_list: array of multicast addresses to program
- * @mc_addr_count: number of multicast addresses to program
- * @rar_used_count: the first RAR register free to program
- * @rar_count: total number of supported Receive Address Registers
- *
- * Updates the Receive Address Registers and Multicast Table Array.
- * The caller must have a packed mc_addr_list of multicast addresses.
- * The parameter rar_count will usually be hw->mac.rar_entry_count
- * unless there are workarounds that change this.
- */
-static void
-e1000_update_mc_addr_list_82571(struct e1000_hw *hw,
-    u8 *mc_addr_list, u32 mc_addr_count,
-    u32 rar_used_count, u32 rar_count)
-{
-	DEBUGFUNC("e1000_update_mc_addr_list_82571");
-
-	if (e1000_get_laa_state_82571(hw))
-		rar_count--;
-
-	e1000_update_mc_addr_list_generic(hw, mc_addr_list, mc_addr_count,
-	    rar_used_count, rar_count);
-}
-
-/*
  * e1000_setup_link_82571 - Setup flow control and link settings
  * @hw: pointer to the HW structure
  *
@@ -1224,10 +1376,16 @@
 	 * 82573 does not have a word in the NVM to determine the default flow
 	 * control setting, so we explicitly set it to full.
 	 */
-	if ((hw->mac.type == e1000_82573 || hw->mac.type == e1000_82574) &&
-	    hw->fc.requested_mode == e1000_fc_default)
-		hw->fc.requested_mode = e1000_fc_full;
-
+	switch (hw->mac.type) {
+	case e1000_82573:
+	case e1000_82574:
+	case e1000_82583:
+		if (hw->fc.requested_mode == e1000_fc_default)
+			hw->fc.requested_mode = e1000_fc_full;
+		break;
+	default:
+		break;
+	}
 	return (e1000_setup_link_generic(hw));
 }
 
@@ -1242,7 +1400,7 @@
 static s32
 e1000_setup_copper_link_82571(struct e1000_hw *hw)
 {
-	u32 ctrl, led_ctrl;
+	u32 ctrl;
 	s32 ret_val;
 
 	DEBUGFUNC("e1000_setup_copper_link_82571");
@@ -1259,11 +1417,6 @@
 		break;
 	case e1000_phy_igp_2:
 		ret_val = e1000_copper_link_setup_igp(hw);
-		/* Setup activity LED */
-		led_ctrl = E1000_READ_REG(hw, E1000_LEDCTL);
-		led_ctrl &= IGP_ACTIVITY_LED_MASK;
-		led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE);
-		E1000_WRITE_REG(hw, E1000_LEDCTL, led_ctrl);
 		break;
 	default:
 		ret_val = -E1000_ERR_PHY;
@@ -1312,6 +1465,157 @@
 }
 
 /*
+ * e1000_check_for_serdes_link_82571 - Check for link (Serdes)
+ * @hw: pointer to the HW structure
+ *
+ * Reports the link state as up or down.
+ *
+ * If autonegotiation is supported by the link partner, the link state is
+ * determined by the result of autongotiation. This is the most likely case.
+ * If autonegotiation is not supported by the link partner, and the link
+ * has a valid signal, force the link up.
+ *
+ * The link state is represented internally here by 4 states:
+ *
+ * 1) down
+ * 2) autoneg_progress
+ * 3) autoneg_complete (the link sucessfully autonegotiated)
+ * 4) forced_up (the link has been forced up, it did not autonegotiate)
+ */
+s32
+e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
+{
+	struct e1000_mac_info *mac = &hw->mac;
+	u32 rxcw;
+	u32 ctrl;
+	u32 status;
+	s32 ret_val = E1000_SUCCESS;
+
+	DEBUGFUNC("e1000_check_for_serdes_link_82571");
+
+	ctrl = E1000_READ_REG(hw, E1000_CTRL);
+	status = E1000_READ_REG(hw, E1000_STATUS);
+	rxcw = E1000_READ_REG(hw, E1000_RXCW);
+
+	if ((rxcw & E1000_RXCW_SYNCH) && !(rxcw & E1000_RXCW_IV)) {
+
+		/* Receiver is synchronized with no invalid bits.  */
+		switch (mac->serdes_link_state) {
+		case e1000_serdes_link_autoneg_complete:
+			if (!(status & E1000_STATUS_LU)) {
+				/*
+				 * We have lost link, retry autoneg before
+				 * reporting link failure
+				 */
+				mac->serdes_link_state =
+				    e1000_serdes_link_autoneg_progress;
+				mac->serdes_has_link = false;
+				DEBUGOUT("AN_UP     -> AN_PROG\n");
+			}
+			break;
+
+		case e1000_serdes_link_forced_up:
+			/*
+			 * If we are receiving /C/ ordered sets, re-enable
+			 * auto-negotiation in the TXCW register and disable
+			 * forced link in the Device Control register in an
+			 * attempt to auto-negotiate with our link partner.
+			 */
+			if (rxcw & E1000_RXCW_C) {
+				/* Enable autoneg, and unforce link up */
+				E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw);
+				E1000_WRITE_REG(hw, E1000_CTRL,
+				    (ctrl & ~E1000_CTRL_SLU));
+				mac->serdes_link_state =
+				    e1000_serdes_link_autoneg_progress;
+				mac->serdes_has_link = false;
+				DEBUGOUT("FORCED_UP -> AN_PROG\n");
+			}
+			break;
+
+		case e1000_serdes_link_autoneg_progress:
+			if (rxcw & E1000_RXCW_C) {
+				/*
+				 * We received /C/ ordered sets, meaning the
+				 * link partner has autonegotiated, and we can
+				 * trust the Link Up (LU) status bit
+				 */
+				if (status & E1000_STATUS_LU) {
+					mac->serdes_link_state =
+					    e1000_serdes_link_autoneg_complete;
+					DEBUGOUT("AN_PROG   -> AN_UP\n");
+					mac->serdes_has_link = true;
+				} else {
+					/* Autoneg completed, but failed */
+					mac->serdes_link_state =
+					    e1000_serdes_link_down;
+					DEBUGOUT("AN_PROG   -> DOWN\n");
+				}
+			} else {
+				/*
+				 * The link partner did not autoneg.
+				 * Force link up and full duplex, and change
+				 * state to forced.
+				 */
+				E1000_WRITE_REG(hw, E1000_TXCW,
+				    (mac->txcw & ~E1000_TXCW_ANE));
+				ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD);
+				E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+
+				/* Configure Flow Control after link up. */
+				ret_val =
+				    e1000_config_fc_after_link_up_generic(hw);
+				if (ret_val) {
+					DEBUGOUT("Error config flow control\n");
+					break;
+				}
+				mac->serdes_link_state =
+				    e1000_serdes_link_forced_up;
+				mac->serdes_has_link = true;
+				DEBUGOUT("AN_PROG   -> FORCED_UP\n");
+			}
+			break;
+
+		case e1000_serdes_link_down:
+		default:
+			/*
+			 * The link was down but the receiver has now gained
+			 * valid sync, so lets see if we can bring the link
+			 * up.
+			 */
+			E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw);
+			E1000_WRITE_REG(hw, E1000_CTRL,
+			    (ctrl & ~E1000_CTRL_SLU));
+			mac->serdes_link_state =
+			    e1000_serdes_link_autoneg_progress;
+			DEBUGOUT("DOWN      -> AN_PROG\n");
+			break;
+		}
+	} else {
+		if (!(rxcw & E1000_RXCW_SYNCH)) {
+			mac->serdes_has_link = false;
+			mac->serdes_link_state = e1000_serdes_link_down;
+			DEBUGOUT("ANYSTATE  -> DOWN\n");
+		} else {
+			/*
+			 * We have sync, and can tolerate one
+			 * invalid (IV) codeword before declaring
+			 * link down, so reread to look again
+			 */
+			usec_delay(10);
+			rxcw = E1000_READ_REG(hw, E1000_RXCW);
+			if (rxcw & E1000_RXCW_IV) {
+				mac->serdes_link_state = e1000_serdes_link_down;
+				mac->serdes_has_link = false;
+				DEBUGOUT("ANYSTATE  -> DOWN\n");
+			}
+		}
+	}
+
+	return (ret_val);
+}
+
+/*
  * e1000_valid_led_default_82571 - Verify a valid default LED config
  * @hw: pointer to the HW structure
  * @data: pointer to the NVM (EEPROM)
@@ -1332,11 +1636,19 @@
 		goto out;
 	}
 
-	if ((hw->mac.type == e1000_82573 || hw->mac.type == e1000_82574) &&
-	    *data == ID_LED_RESERVED_F746)
-		*data = ID_LED_DEFAULT_82573;
-	else if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF)
-		*data = ID_LED_DEFAULT;
+	switch (hw->mac.type) {
+	case e1000_82573:
+	case e1000_82574:
+	case e1000_82583:
+		if (*data == ID_LED_RESERVED_F746)
+			*data = ID_LED_DEFAULT_82573;
+		break;
+	default:
+		if (*data == ID_LED_RESERVED_0000 ||
+		    *data == ID_LED_RESERVED_FFFF)
+			*data = ID_LED_DEFAULT;
+		break;
+	}
 
 out:
 	return (ret_val);
@@ -1455,9 +1767,19 @@
 	s32 ret_val = E1000_SUCCESS;
 
 	DEBUGFUNC("e1000_read_mac_addr_82571");
-	if (e1000_check_alt_mac_addr_generic(hw))
-		ret_val = e1000_read_mac_addr_generic(hw);
 
+	/*
+	 * If there's an alternate MAC address place it in RAR0
+	 * so that it will override the Si installed default perm
+	 * address.
+	 */
+	ret_val = e1000_check_alt_mac_addr_generic(hw);
+	if (ret_val)
+		goto out;
+
+	ret_val = e1000_read_mac_addr_generic(hw);
+
+out:
 	return (ret_val);
 }
 
@@ -1494,6 +1816,7 @@
 	DEBUGFUNC("e1000_clear_hw_cntrs_82571");
 
 	e1000_clear_hw_cntrs_base_generic(hw);
+
 	(void) E1000_READ_REG(hw, E1000_PRC64);
 	(void) E1000_READ_REG(hw, E1000_PRC127);
 	(void) E1000_READ_REG(hw, E1000_PRC255);
--- a/usr/src/uts/common/io/e1000g/e1000_82571.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_82571.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.16 sol_anvik_patch
+ * IntelVersion: 1.16 v3-1-3_2009-8-20
  */
 #ifndef _E1000_82571_H_
 #define	_E1000_82571_H_
--- a/usr/src/uts/common/io/e1000g/e1000_api.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_api.c	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.107 sol_anvik_patch
+ * IntelVersion: 1.124 v3-1-3_2009-8-20
  */
 
 #include "e1000_api.h"
@@ -211,8 +211,12 @@
 		mac->type = e1000_82573;
 		break;
 	case E1000_DEV_ID_82574L:
+	case E1000_DEV_ID_82574LA:
 		mac->type = e1000_82574;
 		break;
+	case E1000_DEV_ID_82583V:
+		mac->type = e1000_82583;
+		break;
 	case E1000_DEV_ID_80003ES2LAN_COPPER_DPT:
 	case E1000_DEV_ID_80003ES2LAN_SERDES_DPT:
 	case E1000_DEV_ID_80003ES2LAN_COPPER_SPT:
@@ -244,8 +248,15 @@
 		break;
 	case E1000_DEV_ID_ICH10_D_BM_LM:
 	case E1000_DEV_ID_ICH10_D_BM_LF:
+	case E1000_DEV_ID_ICH10_HANKSVILLE:
 		mac->type = e1000_ich10lan;
 		break;
+	case E1000_DEV_ID_PCH_D_HV_DM:
+	case E1000_DEV_ID_PCH_D_HV_DC:
+	case E1000_DEV_ID_PCH_M_HV_LM:
+	case E1000_DEV_ID_PCH_M_HV_LC:
+		mac->type = e1000_pchlan;
+		break;
 	default:
 		/* Should never have loaded on this device */
 		ret_val = -E1000_ERR_MAC_INIT;
@@ -278,6 +289,7 @@
 		DEBUGOUT("ERROR: MAC type could not be set properly.\n");
 		goto out;
 	}
+
 	if (!hw->hw_addr) {
 		DEBUGOUT("ERROR: Registers not mapped\n");
 		ret_val = -E1000_ERR_CONFIG;
@@ -322,6 +334,7 @@
 	case e1000_82572:
 	case e1000_82573:
 	case e1000_82574:
+	case e1000_82583:
 		e1000_init_function_pointers_82571(hw);
 		break;
 	case e1000_80003es2lan:
@@ -330,6 +343,7 @@
 	case e1000_ich8lan:
 	case e1000_ich9lan:
 	case e1000_ich10lan:
+	case e1000_pchlan:
 		e1000_init_function_pointers_ich8lan(hw);
 		break;
 	default:
@@ -354,7 +368,6 @@
 		ret_val = e1000_init_phy_params(hw);
 		if (ret_val)
 			goto out;
-
 	}
 
 out:
@@ -413,26 +426,17 @@
  * @hw: pointer to the HW structure
  * @mc_addr_list: array of multicast addresses to program
  * @mc_addr_count: number of multicast addresses to program
- * @rar_used_count: the first RAR register free to program
- * @rar_count: total number of supported Receive Address Registers
  *
- * Updates the Receive Address Registers and Multicast Table Array.
+ * Updates the Multicast Table Array.
  * The caller must have a packed mc_addr_list of multicast addresses.
- * The parameter rar_count will usually be hw->mac.rar_entry_count
- * unless there are workarounds that change this.  Currently no func pointer
- * exists and all implementations are handled in the generic version of this
- * function.
  */
 void
 e1000_update_mc_addr_list(struct e1000_hw *hw, u8 *mc_addr_list,
-    u32 mc_addr_count, u32 rar_used_count, u32 rar_count)
+    u32 mc_addr_count)
 {
 	if (hw->mac.ops.update_mc_addr_list)
-		hw->mac.ops.update_mc_addr_list(hw,
-		    mc_addr_list,
-		    mc_addr_count,
-		    rar_used_count,
-		    rar_count);
+		hw->mac.ops.update_mc_addr_list(hw, mc_addr_list,
+		    mc_addr_count);
 }
 
 /*
@@ -615,6 +619,22 @@
 }
 
 /*
+ *  e1000_id_led_init - store LED configurations in SW
+ *  @hw: pointer to the HW structure
+ *
+ *  Initializes the LED config in SW. This is a function pointer entry point
+ *  called by drivers.
+ */
+s32
+e1000_id_led_init(struct e1000_hw *hw)
+{
+	if (hw->mac.ops.id_led_init)
+		return (hw->mac.ops.id_led_init(hw));
+
+	return (E1000_SUCCESS);
+}
+
+/*
  * e1000_led_on - Turn on SW controllable LED
  * @hw: pointer to the HW structure
  *
--- a/usr/src/uts/common/io/e1000g/e1000_api.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_api.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.50 sol_anvik_patch
+ * IntelVersion: 1.53 v3-1-3_2009-8-20
  */
 #ifndef _E1000_API_H_
 #define	_E1000_API_H_
@@ -63,14 +63,14 @@
 void e1000_mta_set(struct e1000_hw *hw, u32 hash_value);
 u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr);
 void e1000_update_mc_addr_list(struct e1000_hw *hw,
-    u8 *mc_addr_list, u32 mc_addr_count,
-    u32 rar_used_count, u32 rar_count);
+    u8 *mc_addr_list, u32 mc_addr_count);
 s32 e1000_setup_led(struct e1000_hw *hw);
 s32 e1000_cleanup_led(struct e1000_hw *hw);
 s32 e1000_check_reset_block(struct e1000_hw *hw);
 s32 e1000_blink_led(struct e1000_hw *hw);
 s32 e1000_led_on(struct e1000_hw *hw);
 s32 e1000_led_off(struct e1000_hw *hw);
+s32 e1000_id_led_init(struct e1000_hw *hw);
 void e1000_reset_adaptive(struct e1000_hw *hw);
 void e1000_update_adaptive(struct e1000_hw *hw);
 s32 e1000_get_cable_length(struct e1000_hw *hw);
@@ -86,7 +86,7 @@
 void e1000_power_up_phy(struct e1000_hw *hw);
 void e1000_power_down_phy(struct e1000_hw *hw);
 s32 e1000_read_mac_addr(struct e1000_hw *hw);
-s32 e1000_read_pba_num(struct e1000_hw *hw, u32 *pba_num);
+s32 e1000_read_pba_num(struct e1000_hw *hw, u32 *part_num);
 void e1000_reload_nvm(struct e1000_hw *hw);
 s32 e1000_update_nvm_checksum(struct e1000_hw *hw);
 s32 e1000_validate_nvm_checksum(struct e1000_hw *hw);
--- a/usr/src/uts/common/io/e1000g/e1000_defines.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_defines.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,15 +24,11 @@
  */
 
 /*
- * IntelVersion: 1.79 sol_anvik_patch
+ * IntelVersion: 1.116 v3-1-3_2009-8-20
  */
 #ifndef _E1000_DEFINES_H_
 #define	_E1000_DEFINES_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /* Number of Transmit and Receive Descriptors must be a multiple of 8 */
 #define	REQ_TX_DESCRIPTOR_MULTIPLE	8
 #define	REQ_RX_DESCRIPTOR_MULTIPLE	8
@@ -62,19 +58,33 @@
 #define	E1000_WUFC_FLX1_PHY	0x00002000 /* Flexible Filter 1 Enable */
 #define	E1000_WUFC_FLX2_PHY	0x00004000 /* Flexible Filter 2 Enable */
 #define	E1000_WUFC_FLX3_PHY	0x00008000 /* Flexible Filter 3 Enable */
+#define	E1000_WUFC_FLX4_PHY	0x00000200 /* Flexible Filter 4 Enable */
+#define	E1000_WUFC_FLX5_PHY	0x00000400 /* Flexible Filter 5 Enable */
 #define	E1000_WUFC_IGNORE_TCO	0x00008000 /* Ignore WakeOn TCO packets */
 #define	E1000_WUFC_FLX0	0x00010000	/* Flexible Filter 0 Enable */
 #define	E1000_WUFC_FLX1	0x00020000	/* Flexible Filter 1 Enable */
 #define	E1000_WUFC_FLX2	0x00040000	/* Flexible Filter 2 Enable */
 #define	E1000_WUFC_FLX3	0x00080000	/* Flexible Filter 3 Enable */
+#define	E1000_WUFC_FLX4 0x00100000	/* Flexible Filter 4 Enable */
+#define	E1000_WUFC_FLX5 0x00200000	/* Flexible Filter 5 Enable */
 /* Mask for all wakeup filters */
 #define	E1000_WUFC_ALL_FILTERS_PHY_4 0x0000F0FF
 #define	E1000_WUFC_FLX_OFFSET_PHY 12 /* Offset to the Flexible Filters bits */
 /* Mask for 4 flexible filters */
-#define	E1000_WUFC_FLX_FILTERS_PHY_4 0x0000F000
+#define	E1000_WUFC_FLX_FILTERS_PHY_4	0x0000F000
+/* Mask for 6 wakeup filters */
+#define	E1000_WUFC_ALL_FILTERS_PHY_6	0x0000F6FF
+/* Mask for 6 flexible filters */
+#define	E1000_WUFC_FLX_FILTERS_PHY_6	0x0000F600
 #define	E1000_WUFC_ALL_FILTERS	0x000F00FF /* Mask for all wakeup filters */
-#define	E1000_WUFC_FLX_OFFSET	16 /* Offset to the Flexible Filters bits */
-#define	E1000_WUFC_FLX_FILTERS	0x000F0000 /* Mask for the 4 flexible filters */
+/* Mask for all 6 wakeup filters */
+#define	E1000_WUFC_ALL_FILTERS_6	0x003F00FF
+/* Offset to the Flexible Filters bits */
+#define	E1000_WUFC_FLX_OFFSET	16
+/* Mask for the 4 flexible filters */
+#define	E1000_WUFC_FLX_FILTERS	0x000F0000
+/* Mask for 6 flexible filters */
+#define	E1000_WUFC_FLX_FILTERS_6  0x003F0000
 
 /* Wake Up Status */
 #define	E1000_WUS_LNKC			E1000_WUFC_LNKC
@@ -89,23 +99,34 @@
 #define	E1000_WUS_FLX1_PHY		E1000_WUFC_FLX1_PHY
 #define	E1000_WUS_FLX2_PHY		E1000_WUFC_FLX2_PHY
 #define	E1000_WUS_FLX3_PHY		E1000_WUFC_FLX3_PHY
+#define	E1000_WUS_FLX4_PHY		E1000_WUFC_FLX4_PHY
+#define	E1000_WUS_FLX5_PHY		E1000_WUFC_FLX5_PHY
+
 #define	E1000_WUS_FLX_FILTERS_PHY_4	E1000_WUFC_FLX_FILTERS_PHY_4
 #define	E1000_WUS_FLX0			E1000_WUFC_FLX0
 #define	E1000_WUS_FLX1			E1000_WUFC_FLX1
 #define	E1000_WUS_FLX2			E1000_WUFC_FLX2
 #define	E1000_WUS_FLX3			E1000_WUFC_FLX3
+#define	E1000_WUS_FLX4			E1000_WUFC_FLX4
+#define	E1000_WUS_FLX5			E1000_WUFC_FLX5
+
 #define	E1000_WUS_FLX_FILTERS		E1000_WUFC_FLX_FILTERS
+#define	E1000_WUS_FLX_FILTERS_6		E1000_WUFC_FLX_FILTERS_6
+#define	E1000_WUS_FLX_FILTERS_PHY_6	E1000_WUFC_FLX_FILTERS_PHY_6
 
 /* Wake Up Packet Length */
 #define	E1000_WUPL_LENGTH_MASK	0x0FFF	/* Only the lower 12 bits are valid */
 
 /* Four Flexible Filters are supported */
 #define	E1000_FLEXIBLE_FILTER_COUNT_MAX	4
+/* Six Flexible Filters are supported */
+#define	E1000_FLEXIBLE_FILTER_COUNT_MAX_6   6
 
 /* Each Flexible Filter is at most 128 (0x80) bytes in length */
 #define	E1000_FLEXIBLE_FILTER_SIZE_MAX	128
 
 #define	E1000_FFLT_SIZE	E1000_FLEXIBLE_FILTER_COUNT_MAX
+#define	E1000_FFLT_SIZE_6 E1000_FLEXIBLE_FILTER_COUNT_MAX_6
 #define	E1000_FFMT_SIZE	E1000_FLEXIBLE_FILTER_SIZE_MAX
 #define	E1000_FFVT_SIZE	E1000_FLEXIBLE_FILTER_SIZE_MAX
 
@@ -120,17 +141,19 @@
 #define	E1000_CTRL_EXT_SDP5_DATA 0x00000020 /* Value of SW Definable Pin 5 */
 #define	E1000_CTRL_EXT_PHY_INT	E1000_CTRL_EXT_SDP5_DATA
 #define	E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Definable Pin 6 */
-#define	E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Definable Pin 7 */
+#define	E1000_CTRL_EXT_SDP3_DATA 0x00000080 /* Value of SW Definable Pin 3 */
 /* SDP 4/5 (bits 8,9) are reserved in >= 82575 */
 #define	E1000_CTRL_EXT_SDP4_DIR  0x00000100 /* Direction of SDP4 0=in 1=out */
 #define	E1000_CTRL_EXT_SDP5_DIR  0x00000200 /* Direction of SDP5 0=in 1=out */
 #define	E1000_CTRL_EXT_SDP6_DIR  0x00000400 /* Direction of SDP6 0=in 1=out */
-#define	E1000_CTRL_EXT_SDP7_DIR  0x00000800 /* Direction of SDP7 0=in 1=out */
+#define	E1000_CTRL_EXT_SDP3_DIR  0x00000800 /* Direction of SDP3 0=in 1=out */
 #define	E1000_CTRL_EXT_ASDCHK	0x00001000  /* Initiate an ASD sequence */
 #define	E1000_CTRL_EXT_EE_RST	0x00002000  /* Reinitialize from EEPROM */
 #define	E1000_CTRL_EXT_IPS	0x00004000  /* Invert Power State */
 #define	E1000_CTRL_EXT_SPD_BYPS	0x00008000  /* Speed Select Bypass */
 #define	E1000_CTRL_EXT_RO_DIS	0x00020000  /* Relaxed Ordering disable */
+/* DMA Dynamic Clock Gating */
+#define	E1000_CTRL_EXT_DMA_DYN_CLK_EN	0x00080000
 #define	E1000_CTRL_EXT_LINK_MODE_MASK	0x00C00000
 #define	E1000_CTRL_EXT_LINK_MODE_GMII	0x00000000
 #define	E1000_CTRL_EXT_LINK_MODE_TBI	0x00C00000
@@ -149,8 +172,6 @@
 #define	E1000_CTRL_EXT_DRV_LOAD	0x10000000 /* Driver loaded bit for FW */
 /* IAME enable bit (27) was removed in >= 82575 */
 #define	E1000_CTRL_EXT_IAME	0x08000000 /* Int acknowledge Auto-mask */
-/* Clear Interrupt timers after IMS clear */
-#define	E1000_CTRL_EXT_INT_TIMER_CLR	0x20000000
 /* packet buffer parity error detection enabled */
 #define	E1000_CRTL_EXT_PB_PAREN		0x01000000
 /* descriptor FIFO parity error detection enable */
@@ -158,6 +179,7 @@
 #define	E1000_CTRL_EXT_GHOST_PAREN	0x40000000
 #define	E1000_CTRL_EXT_PBA_CLR		0x80000000	/* PBA Clear */
 #define	E1000_CTRL_EXT_LSECCK		0x00001000
+#define	E1000_CTRL_EXT_PHYPDEN		0x00100000
 #define	E1000_I2CCMD_REG_ADDR_SHIFT	16
 #define	E1000_I2CCMD_REG_ADDR		0x00FF0000
 #define	E1000_I2CCMD_PHY_ADDR_SHIFT	24
@@ -283,8 +305,8 @@
 #define	E1000_RCTL_RST		0x00000001	/* Software reset */
 #define	E1000_RCTL_EN		0x00000002	/* enable */
 #define	E1000_RCTL_SBP		0x00000004	/* store bad packet */
-#define	E1000_RCTL_UPE		0x00000008	/* unicast promiscuous enable */
-#define	E1000_RCTL_MPE		0x00000010	/* multicast promiscuous enab */
+#define	E1000_RCTL_UPE		0x00000008	/* unicast promisc enable */
+#define	E1000_RCTL_MPE		0x00000010	/* multicast promisc enable */
 #define	E1000_RCTL_LPE		0x00000020	/* long packet enable */
 #define	E1000_RCTL_LBM_NO	0x00000000	/* no loopback mode */
 #define	E1000_RCTL_LBM_MAC	0x00000040	/* MAC loopback mode */
@@ -292,9 +314,9 @@
 #define	E1000_RCTL_LBM_TCVR	0x000000C0	/* tcvr loopback mode */
 #define	E1000_RCTL_DTYP_MASK	0x00000C00	/* Descriptor type mask */
 #define	E1000_RCTL_DTYP_PS	0x00000400	/* Packet Split descriptor */
-#define	E1000_RCTL_RDMTS_HALF	0x00000000	/* rx desc min threshold size */
-#define	E1000_RCTL_RDMTS_QUAT	0x00000100	/* rx desc min threshold size */
-#define	E1000_RCTL_RDMTS_EIGTH	0x00000200	/* rx desc min threshold size */
+#define	E1000_RCTL_RDMTS_HALF	0x00000000	/* rx desc min thresh size */
+#define	E1000_RCTL_RDMTS_QUAT	0x00000100	/* rx desc min thresh size */
+#define	E1000_RCTL_RDMTS_EIGTH	0x00000200	/* rx desc min thresh size */
 #define	E1000_RCTL_MO_SHIFT	12		/* multicast offset shift */
 #define	E1000_RCTL_MO_0		0x00000000	/* multicast offset 11:0 */
 #define	E1000_RCTL_MO_1		0x00001000	/* multicast offset 12:1 */
@@ -349,10 +371,10 @@
 #define	E1000_PSRCTL_BSIZE3_SHIFT	14	/* Shift _left_ 14 */
 
 /* SWFW_SYNC Definitions */
-#define	E1000_SWFW_EEP_SM	0x1
-#define	E1000_SWFW_PHY0_SM	0x2
-#define	E1000_SWFW_PHY1_SM	0x4
-#define	E1000_SWFW_CSR_SM	0x8
+#define	E1000_SWFW_EEP_SM	0x01
+#define	E1000_SWFW_PHY0_SM	0x02
+#define	E1000_SWFW_PHY1_SM	0x04
+#define	E1000_SWFW_CSR_SM	0x08
 
 /* FACTPS Definitions */
 #define	E1000_FACTPS_LFS	0x40000000	/* LAN Function Select */
@@ -360,7 +382,7 @@
 #define	E1000_CTRL_FD	0x00000001	/* Full duplex.0=half; 1=full */
 #define	E1000_CTRL_BEM	0x00000002	/* Endian Mode.0=little,1=big */
 #define	E1000_CTRL_PRIOR 0x00000004	/* Priority on PCI. 0=rx,1=fair */
-#define	E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /* Block new Master requests */
+#define	E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /* Block new Master reqs */
 #define	E1000_CTRL_LRST	0x00000008	/* Link reset. 0=normal,1=reset */
 #define	E1000_CTRL_TME	0x00000010	/* Test mode. 0=normal,1=test */
 #define	E1000_CTRL_SLE	0x00000020	/* Serial Link on 0=dis,1=en */
@@ -460,6 +482,7 @@
 #define	E1000_STATUS_SPEED_1000	0x00000080	/* Speed 1000Mb/s */
 #define	E1000_STATUS_LAN_INIT_DONE 0x00000200	/* Lan Init Completion by NVM */
 #define	E1000_STATUS_ASDV	0x00000300	/* Auto	speed detect value */
+#define	E1000_STATUS_PHYRA	0x00000400	/* PHY Reset Asserted */
 /* Change in Dock/Undock state. Clear on write '0'. */
 #define	E1000_STATUS_DOCK_CI	0x00000800
 /* Master request status */
@@ -519,6 +542,11 @@
 #define	AUTONEG_ADVERTISE_SPEED_DEFAULT	E1000_ALL_SPEED_DUPLEX
 
 /* LED Control */
+#define	E1000_PHY_LED0_MODE_MASK	0x00000007
+#define	E1000_PHY_LED0_IVRT		0x00000008
+#define	E1000_PHY_LED0_BLINK		0x00000010
+#define	E1000_PHY_LED0_MASK		0x0000001F
+
 #define	E1000_LEDCTL_LED0_MODE_MASK	0x0000000F
 #define	E1000_LEDCTL_LED0_MODE_SHIFT	0
 #define	E1000_LEDCTL_LED0_BLINK_RATE	0x00000020
@@ -680,14 +708,19 @@
 /* PBA constants */
 #define	E1000_PBA_6K	0x0006	/* 6KB */
 #define	E1000_PBA_8K	0x0008	/* 8KB */
+#define	E1000_PBA_10K	0x000A	/* 10KB */
 #define	E1000_PBA_12K	0x000C	/* 12KB */
+#define	E1000_PBA_14K	0x000E	/* 14KB */
 #define	E1000_PBA_16K	0x0010	/* 16KB */
+#define	E1000_PBA_18K	0x0012
 #define	E1000_PBA_20K	0x0014
 #define	E1000_PBA_22K	0x0016
 #define	E1000_PBA_24K	0x0018
+#define	E1000_PBA_26K	0x001A
 #define	E1000_PBA_30K	0x001E
 #define	E1000_PBA_32K	0x0020
 #define	E1000_PBA_34K	0x0022
+#define	E1000_PBA_35K	0x0023
 #define	E1000_PBA_38K	0x0026
 #define	E1000_PBA_40K	0x0028
 #define	E1000_PBA_48K	0x0030	/* 48KB */
@@ -708,6 +741,9 @@
 #define	E1000_SWSM_WMNG		0x00000004	/* Wake MNG Clock */
 #define	E1000_SWSM_DRV_LOAD	0x00000008	/* Driver Loaded Bit */
 
+/* Secondary driver semaphore bit */
+#define	E1000_SWSM2_LOCK	0x00000002
+
 /* Interrupt Cause Read */
 #define	E1000_ICR_TXDW		0x00000001	/* Transmit desc written back */
 #define	E1000_ICR_TXQE		0x00000002	/* Transmit Queue empty */
@@ -758,6 +794,15 @@
 #define	E1000_ICR_TXQ1		0x00800000	/* Tx Queue 1 Interrupt */
 #define	E1000_ICR_OTHER		0x01000000	/* Other Interrupts */
 
+/* PBA ECC Register */
+#define	E1000_PBA_ECC_COUNTER_MASK	0xFFF00000 /* ECC counter mask */
+#define	E1000_PBA_ECC_COUNTER_SHIFT	20	/* ECC counter shift value */
+/* Enable ECC error correction */
+#define	E1000_PBA_ECC_CORR_EN		0x00000001
+#define	E1000_PBA_ECC_STAT_CLR		0x00000002 /* Clear ECC error counter */
+/* Enable ICR bit 5 on ECC error */
+#define	E1000_PBA_ECC_INT_EN		0x00000004
+
 /*
  * This defines the bits that are set in the Interrupt Mask
  * Set/Read Register.  Each bit is documented below:
@@ -785,7 +830,7 @@
     E1000_IMS_LSC)
 
 /* Interrupt Mask Set */
-#define	E1000_IMS_TXDW		E1000_ICR_TXDW	/* Transmit desc written back */
+#define	E1000_IMS_TXDW		E1000_ICR_TXDW	/* Tx desc written back */
 #define	E1000_IMS_TXQE		E1000_ICR_TXQE	/* Transmit Queue empty */
 #define	E1000_IMS_LSC		E1000_ICR_LSC	/* Link Status Change */
 #define	E1000_IMS_VMMB		E1000_ICR_VMMB	/* Mail box activity */
@@ -827,7 +872,7 @@
 #define	E1000_IMS_OTHER		E1000_ICR_OTHER	/* Other Interrupts */
 
 /* Interrupt Cause Set */
-#define	E1000_ICS_TXDW		E1000_ICR_TXDW	/* Transmit desc written back */
+#define	E1000_ICS_TXDW		E1000_ICR_TXDW	/* Tx desc written back */
 #define	E1000_ICS_TXQE		E1000_ICR_TXQE	/* Transmit Queue empty */
 #define	E1000_ICS_LSC		E1000_ICR_LSC	/* Link Status Change */
 #define	E1000_ICS_RXSEQ		E1000_ICR_RXSEQ	/* rx sequence error */
@@ -892,6 +937,10 @@
  */
 #define	E1000_RAR_ENTRIES	15
 #define	E1000_RAH_AV		0x80000000	/* Receive descriptor valid */
+#define	E1000_RAL_MAC_ADDR_LEN	4
+#define	E1000_RAH_MAC_ADDR_LEN	2
+#define	E1000_RAH_POOL_MASK	0x03FC0000
+#define	E1000_RAH_POOL_1	0x00040000
 
 /* Error Codes */
 #define	E1000_SUCCESS		0
@@ -907,6 +956,7 @@
 #define	E1000_BLK_PHY_RESET	12
 #define	E1000_ERR_SWFW_SYNC	13
 #define	E1000_NOT_IMPLEMENTED	14
+#define	E1000_ERR_MBX		15
 
 /* Loop limit on how long we wait for auto-negotiation to complete */
 #define	FIBER_LINK_UP_LIMIT	50
@@ -956,6 +1006,10 @@
 #define	E1000_GCR_TXD_NO_SNOOP		0x00000008
 #define	E1000_GCR_TXDSCW_NO_SNOOP	0x00000010
 #define	E1000_GCR_TXDSCR_NO_SNOOP	0x00000020
+#define	E1000_GCR_CMPL_TMOUT_MASK	0x0000F000
+#define	E1000_GCR_CMPL_TMOUT_10ms	0x00001000
+#define	E1000_GCR_CMPL_TMOUT_RESEND	0x00010000
+#define	E1000_GCR_CAP_VER2		0x00040000
 
 #define	PCIE_NO_SNOOP_ALL	(E1000_GCR_RXD_NO_SNOOP	| \
 				E1000_GCR_RXDSCW_NO_SNOOP | \
@@ -1071,6 +1125,8 @@
 #define	PHY_1000T_STATUS	0x0A	/* 1000Base-T Status Reg */
 #define	PHY_EXT_STATUS		0x0F	/* Extended Status Reg */
 
+#define	PHY_CONTROL_LB		0x4000	/* PHY Loopback bit */
+
 /* NVM Control */
 #define	E1000_EECD_SK		0x00000001 /* NVM Clock */
 #define	E1000_EECD_CS		0x00000002 /* NVM Chip Select */
@@ -1101,6 +1157,7 @@
 #define	E1000_EECD_SHADV	0x00200000 /* Shadow RAM Data Valid */
 #define	E1000_EECD_SEC1VAL	0x00400000 /* Sector One Valid */
 #define	E1000_EECD_SECVAL_SHIFT		22
+#define	E1000_EECD_SEC1VAL_VALID_MASK	(E1000_EECD_AUTO_RD | E1000_EECD_PRES)
 
 #define	E1000_NVM_SWDPIN0	0x0001	/* SWDPIN 0 NVM Value */
 #define	E1000_NVM_LED_LOGIC	0x0020	/* Led Logic Word */
@@ -1132,8 +1189,10 @@
 #define	NVM_ALT_MAC_ADDR_PTR		0x0037
 #define	NVM_CHECKSUM_REG		0x003F
 
-#define	E1000_NVM_CFG_DONE_PORT_0	0x40000	/* MNG config cycle done */
-#define	E1000_NVM_CFG_DONE_PORT_1	0x80000	/* ...for second port */
+/* MNG config cycle done */
+#define	E1000_NVM_CFG_DONE_PORT_0	0x040000
+/* ...for second port */
+#define	E1000_NVM_CFG_DONE_PORT_1	0x080000
 
 /* Mask bits for fields in Word 0x0f of the NVM */
 #define	NVM_WORD0F_PAUSE_MASK		0x3000
@@ -1211,6 +1270,7 @@
 #define	PCIX_STATUS_REGISTER_HI		0xEA
 #define	PCI_HEADER_TYPE_REGISTER	0x0E
 #define	PCIE_LINK_STATUS		0x12
+#define	PCIE_DEVICE_CONTROL2		0x28
 
 #define	PCIX_COMMAND_MMRBC_MASK		0x000C
 #define	PCIX_COMMAND_MMRBC_SHIFT	0x2
@@ -1222,6 +1282,7 @@
 #define	PCI_HEADER_TYPE_MULTIFUNC	0x80
 #define	PCIE_LINK_WIDTH_MASK		0x3F0
 #define	PCIE_LINK_WIDTH_SHIFT		4
+#define	PCIE_DEVICE_CONTROL2_16ms	0x0005
 
 #ifndef ETH_ADDR_LEN
 #define	ETH_ADDR_LEN		6
@@ -1249,6 +1310,8 @@
 #define	IFE_C_E_PHY_ID		0x02A80310
 #define	BME1000_E_PHY_ID	0x01410CB0
 #define	BME1000_E_PHY_ID_R2	0x01410CB1
+#define	I82577_E_PHY_ID		0x01540050
+#define	I82578_E_PHY_ID		0x004DD040
 #define	M88_VENDOR		0x0141
 
 /* M88E1000 Specific Registers */
@@ -1267,7 +1330,7 @@
 
 /* M88E1000 PHY Specific Control Register */
 #define	M88E1000_PSCR_JABBER_DISABLE	0x0001 /* 1=Jabber Function disabled */
-#define	M88E1000_PSCR_POLARITY_REVERSAL	0x0002 /* 1=Polarity Reversal enabled */
+#define	M88E1000_PSCR_POLARITY_REVERSAL	0x0002 /* 1=Polarity Reverse enabled */
 #define	M88E1000_PSCR_SQE_TEST		0x0004 /* 1=SQE Test enabled */
 /* 1=CLK125 low, 0=CLK125 toggling */
 #define	M88E1000_PSCR_CLK125_DISABLE	0x0010
@@ -1287,7 +1350,7 @@
 #define	M88E1000_PSCR_MII_5BIT_ENABLE	0x0100
 #define	M88E1000_PSCR_SCRAMBLER_DISABLE	0x0200	/* 1=Scrambler disable */
 #define	M88E1000_PSCR_FORCE_LINK_GOOD	0x0400	/* 1=Force link good */
-#define	M88E1000_PSCR_ASSERT_CRS_ON_TX	0x0800	/* 1=Assert CRS on Transmit */
+#define	M88E1000_PSCR_ASSERT_CRS_ON_TX	0x0800	/* 1=Assert CRS on Tx */
 
 /* M88E1000 PHY Specific Status Register */
 #define	M88E1000_PSSR_JABBER		0x0001	/* 1=Jabber */
@@ -1355,6 +1418,9 @@
 #define	M88EC018_EPSCR_DOWNSHIFT_COUNTER_7X	0x0C00
 #define	M88EC018_EPSCR_DOWNSHIFT_COUNTER_8X	0x0E00
 
+#define	I82578_EPSCR_DOWNSHIFT_ENABLE		0x0020
+#define	I82578_EPSCR_DOWNSHIFT_COUNTER_MASK	0x001C
+
 /* BME1000 PHY Specific Control Register */
 #define	BME1000_PSCR_ENABLE_DOWNSHIFT	0x0800	/* 1 = enable downshift */
 
@@ -1449,8 +1515,4 @@
 #define	E1000_GEN_CTL_ADDRESS_SHIFT	8
 #define	E1000_GEN_POLL_TIMEOUT		640
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif	/* _E1000_DEFINES_H_ */
--- a/usr/src/uts/common/io/e1000g/e1000_hw.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_hw.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.398 sol_anvik_patch
+ * IntelVersion: 1.432 v3-1-3_2009-8-20
  */
 #ifndef _E1000_HW_H_
 #define	_E1000_HW_H_
@@ -92,6 +92,8 @@
 #define	E1000_DEV_ID_82573E_IAMT		0x108C
 #define	E1000_DEV_ID_82573L			0x109A
 #define	E1000_DEV_ID_82574L			0x10D3
+#define	E1000_DEV_ID_82574LA			0x10F6
+#define	E1000_DEV_ID_82583V			0x150C
 #define	E1000_DEV_ID_80003ES2LAN_COPPER_DPT	0x1096
 #define	E1000_DEV_ID_80003ES2LAN_SERDES_DPT	0x1098
 #define	E1000_DEV_ID_80003ES2LAN_COPPER_SPT	0x10BA
@@ -115,8 +117,13 @@
 #define	E1000_DEV_ID_ICH10_R_BM_LM		0x10CC
 #define	E1000_DEV_ID_ICH10_R_BM_LF		0x10CD
 #define	E1000_DEV_ID_ICH10_R_BM_V		0x10CE
+#define	E1000_DEV_ID_ICH10_HANKSVILLE		0xF0FE
 #define	E1000_DEV_ID_ICH10_D_BM_LM		0x10DE
 #define	E1000_DEV_ID_ICH10_D_BM_LF		0x10DF
+#define	E1000_DEV_ID_PCH_M_HV_LM		0x10EA
+#define	E1000_DEV_ID_PCH_M_HV_LC		0x10EB
+#define	E1000_DEV_ID_PCH_D_HV_DM		0x10EF
+#define	E1000_DEV_ID_PCH_D_HV_DC		0x10F0
 
 #define	E1000_REVISION_0	0
 #define	E1000_REVISION_1	1
@@ -127,6 +134,12 @@
 #define	E1000_FUNC_0	0
 #define	E1000_FUNC_1	1
 
+#define	E1000_ALT_MAC_ADDRESS_OFFSET_LAN0	0
+#define	E1000_ALT_MAC_ADDRESS_OFFSET_LAN1	3
+
+/* Maximum size of the MTA register table in all supported adapters */
+#define	MAX_MTA_REG 128
+
 enum e1000_mac_type {
 	e1000_undefined = 0,
 	e1000_82542,
@@ -145,10 +158,12 @@
 	e1000_82572,
 	e1000_82573,
 	e1000_82574,
+	e1000_82583,
 	e1000_80003es2lan,
 	e1000_ich8lan,
 	e1000_ich9lan,
 	e1000_ich10lan,
+	e1000_pchlan,
 	e1000_num_macs	/* List is 1-based, so subtract 1 for true count. */
 };
 
@@ -187,7 +202,8 @@
 	e1000_phy_igp_3,
 	e1000_phy_ife,
 	e1000_phy_bm,
-	e1000_phy_vf
+	e1000_phy_82578,
+	e1000_phy_82577,
 };
 
 enum e1000_bus_type {
@@ -267,6 +283,13 @@
 	e1000_smart_speed_off
 };
 
+enum e1000_serdes_link_state {
+	e1000_serdes_link_down = 0,
+	e1000_serdes_link_autoneg_progress,
+	e1000_serdes_link_autoneg_complete,
+	e1000_serdes_link_forced_up
+};
+
 /* Receive Descriptor */
 struct e1000_rx_desc {
 	__le64 buffer_addr;	/* Address of the descriptor's data buffer */
@@ -537,6 +560,7 @@
 struct e1000_mac_operations {
 	/* Function pointers for the MAC. */
 	s32 (*init_params)(struct e1000_hw *);
+	s32 (*id_led_init)(struct e1000_hw *);
 	s32 (*blink_led)(struct e1000_hw *);
 	s32 (*check_for_link)(struct e1000_hw *);
 	bool (*check_mng_mode)(struct e1000_hw *hw);
@@ -544,10 +568,11 @@
 	void (*clear_hw_cntrs)(struct e1000_hw *);
 	void (*clear_vfta)(struct e1000_hw *);
 	s32 (*get_bus_info)(struct e1000_hw *);
+	void (*set_lan_id)(struct e1000_hw *);
 	s32 (*get_link_up_info)(struct e1000_hw *, u16 *, u16 *);
 	s32 (*led_on)(struct e1000_hw *);
 	s32 (*led_off)(struct e1000_hw *);
-	void (*update_mc_addr_list)(struct e1000_hw *, u8 *, u32, u32, u32);
+	void (*update_mc_addr_list)(struct e1000_hw *, u8 *, u32);
 	s32 (*reset_hw)(struct e1000_hw *);
 	s32 (*init_hw)(struct e1000_hw *);
 	s32 (*setup_link)(struct e1000_hw *);
@@ -620,6 +645,7 @@
 	u16 ifs_ratio;
 	u16 ifs_step_size;
 	u16 mta_reg_count;
+	u32 mta_shadow[MAX_MTA_REG];
 	u16 rar_entry_count;
 
 	u8 forced_speed_duplex;
@@ -632,6 +658,7 @@
 	bool get_link_status;
 	bool in_ifs_mode;
 	bool report_tx_early;
+	enum e1000_serdes_link_state serdes_link_state;
 	bool serdes_has_link;
 	bool tx_pkt_filtering;
 };
@@ -728,6 +755,11 @@
 
 struct e1000_dev_spec_82571 {
 	bool laa_is_present;
+	u32 smb_counter;
+};
+
+struct e1000_dev_spec_80003es2lan {
+	bool  mdic_wa_enable;
 };
 
 struct e1000_shadow_ram {
@@ -740,6 +772,8 @@
 struct e1000_dev_spec_ich8lan {
 	bool kmrn_lock_loss_workaround_enabled;
 	struct e1000_shadow_ram shadow_ram[E1000_SHADOW_RAM_WORDS];
+	E1000_MUTEX nvm_mutex;
+	E1000_MUTEX swflag_mutex;
 };
 
 struct e1000_hw {
@@ -761,6 +795,7 @@
 		struct e1000_dev_spec_82542	_82542;
 		struct e1000_dev_spec_82543	_82543;
 		struct e1000_dev_spec_82571	_82571;
+		struct e1000_dev_spec_80003es2lan _80003es2lan;
 		struct e1000_dev_spec_ich8lan	ich8lan;
 	} dev_spec;
 
@@ -782,6 +817,7 @@
 void e1000_pci_clear_mwi(struct e1000_hw *hw);
 void e1000_pci_set_mwi(struct e1000_hw *hw);
 s32 e1000_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value);
+s32 e1000_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value);
 void e1000_read_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value);
 void e1000_write_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value);
 
--- a/usr/src/uts/common/io/e1000g/e1000_ich8lan.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_ich8lan.c	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.136 sol_anvik_patch
+ * IntelVersion: 1.180 v3-1-3_2009-8-20
  */
 
 /*
@@ -49,19 +49,24 @@
  * 82567LF-3 Gigabit Network Connection
  * 82567LM-3 Gigabit Network Connection
  * 82567LM-4 Gigabit Network Connection
+ * 82577LM Gigabit Network Connection
+ * 82577LC Gigabit Network Connection
+ * 82578DM Gigabit Network Connection
+ * 82578DC Gigabit Network Connection
  */
 
 #include "e1000_api.h"
 
 static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw);
+static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw);
 static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw);
 static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw);
 static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw);
 static void e1000_release_swflag_ich8lan(struct e1000_hw *hw);
+static s32  e1000_acquire_nvm_ich8lan(struct e1000_hw *hw);
+static void e1000_release_nvm_ich8lan(struct e1000_hw *hw);
 static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw);
-static s32 e1000_check_polarity_ife_ich8lan(struct e1000_hw *hw);
 static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw);
-static s32 e1000_phy_force_speed_duplex_ich8lan(struct e1000_hw *hw);
 static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw);
 static s32 e1000_get_phy_info_ich8lan(struct e1000_hw *hw);
 static s32 e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw,
@@ -76,6 +81,7 @@
 static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw);
 static s32 e1000_valid_led_default_ich8lan(struct e1000_hw *hw,
     u16 *data);
+static s32 e1000_id_led_init_pchlan(struct e1000_hw *hw);
 static s32 e1000_get_bus_info_ich8lan(struct e1000_hw *hw);
 static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw);
 static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw);
@@ -86,6 +92,10 @@
 static s32 e1000_cleanup_led_ich8lan(struct e1000_hw *hw);
 static s32 e1000_led_on_ich8lan(struct e1000_hw *hw);
 static s32 e1000_led_off_ich8lan(struct e1000_hw *hw);
+static s32  e1000_setup_led_pchlan(struct e1000_hw *hw);
+static s32  e1000_cleanup_led_pchlan(struct e1000_hw *hw);
+static s32  e1000_led_on_pchlan(struct e1000_hw *hw);
+static s32  e1000_led_off_pchlan(struct e1000_hw *hw);
 static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw);
 static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank);
 static s32 e1000_flash_cycle_ich8lan(struct e1000_hw *hw, u32 timeout);
@@ -107,6 +117,7 @@
     u8 size, u16 data);
 static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw);
 static void e1000_power_down_phy_copper_ich8lan(struct e1000_hw *hw);
+static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw);
 
 /* ICH GbE Flash Hardware Sequencing Flash Status Register bit breakdown */
 /* Offset 04h HSFSTS */
@@ -150,6 +161,56 @@
 };
 
 /*
+ * e1000_init_phy_params_pchlan - Initialize PHY function pointers
+ * @hw: pointer to the HW structure
+ *
+ * Initialize family-specific PHY parameters and function pointers.
+ */
+static s32
+e1000_init_phy_params_pchlan(struct e1000_hw *hw)
+{
+	struct e1000_phy_info *phy = &hw->phy;
+	s32 ret_val = E1000_SUCCESS;
+
+	DEBUGFUNC("e1000_init_phy_params_pchlan");
+
+	phy->addr = 1;
+	phy->reset_delay_us = 100;
+
+	phy->ops.acquire = e1000_acquire_swflag_ich8lan;
+	phy->ops.check_polarity = e1000_check_polarity_ife;
+	phy->ops.check_reset_block = e1000_check_reset_block_ich8lan;
+	phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_ife;
+	phy->ops.get_cable_length = e1000_get_cable_length_igp_2;
+	phy->ops.get_cfg_done = e1000_get_cfg_done_ich8lan;
+	phy->ops.get_info = e1000_get_phy_info_ich8lan;
+	phy->ops.read_reg = e1000_read_phy_reg_hv;
+	phy->ops.release = e1000_release_swflag_ich8lan;
+	phy->ops.reset = e1000_phy_hw_reset_ich8lan;
+	phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_ich8lan;
+	phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_ich8lan;
+	phy->ops.write_reg = e1000_write_phy_reg_hv;
+	phy->ops.power_up = e1000_power_up_phy_copper;
+	phy->ops.power_down = e1000_power_down_phy_copper_ich8lan;
+	phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+
+	phy->id = e1000_phy_unknown;
+	e1000_get_phy_id(hw);
+	phy->type = e1000_get_phy_type_from_id(phy->id);
+
+	if (phy->type == e1000_phy_82577) {
+		phy->ops.check_polarity = e1000_check_polarity_82577;
+		phy->ops.force_speed_duplex =
+		    e1000_phy_force_speed_duplex_82577;
+		phy->ops.get_cable_length   = e1000_get_cable_length_82577;
+		phy->ops.get_info = e1000_get_phy_info_82577;
+		phy->ops.commit = e1000_phy_sw_reset_generic;
+	}
+
+	return (ret_val);
+}
+
+/*
  * e1000_init_phy_params_ich8lan - Initialize PHY function pointers
  * @hw: pointer to the HW structure
  *
@@ -168,9 +229,9 @@
 	phy->reset_delay_us = 100;
 
 	phy->ops.acquire = e1000_acquire_swflag_ich8lan;
-	phy->ops.check_polarity = e1000_check_polarity_ife_ich8lan;
+	phy->ops.check_polarity = e1000_check_polarity_ife;
 	phy->ops.check_reset_block = e1000_check_reset_block_ich8lan;
-	phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_ich8lan;
+	phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_ife;
 	phy->ops.get_cable_length = e1000_get_cable_length_igp_2;
 	phy->ops.get_cfg_done = e1000_get_cfg_done_ich8lan;
 	phy->ops.get_info = e1000_get_phy_info_ich8lan;
@@ -292,10 +353,13 @@
 		dev_spec->shadow_ram[i].value = 0xFFFF;
 	}
 
+	E1000_MUTEX_INIT(&dev_spec->nvm_mutex);
+	E1000_MUTEX_INIT(&dev_spec->swflag_mutex);
+
 	/* Function Pointers */
-	nvm->ops.acquire = e1000_acquire_swflag_ich8lan;
+	nvm->ops.acquire = e1000_acquire_nvm_ich8lan;
+	nvm->ops.release = e1000_release_nvm_ich8lan;
 	nvm->ops.read = e1000_read_nvm_ich8lan;
-	nvm->ops.release = e1000_release_swflag_ich8lan;
 	nvm->ops.update = e1000_update_nvm_checksum_ich8lan;
 	nvm->ops.valid_led_default = e1000_valid_led_default_ich8lan;
 	nvm->ops.validate = e1000_validate_nvm_checksum_ich8lan;
@@ -337,6 +401,8 @@
 
 	/* bus type/speed/width */
 	mac->ops.get_bus_info = e1000_get_bus_info_ich8lan;
+	/* function id */
+	mac->ops.set_lan_id = e1000_set_lan_id_single_port;
 	/* reset */
 	mac->ops.reset_hw = e1000_reset_hw_ich8lan;
 	/* hw initialization */
@@ -346,7 +412,7 @@
 	/* physical interface setup */
 	mac->ops.setup_physical_interface = e1000_setup_copper_link_ich8lan;
 	/* check for link */
-	mac->ops.check_for_link = e1000_check_for_copper_link_generic;
+	mac->ops.check_for_link = e1000_check_for_copper_link_ich8lan;
 	/* check management mode */
 	mac->ops.check_mng_mode = e1000_check_mng_mode_ich8lan;
 	/* link info */
@@ -355,18 +421,41 @@
 	mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
 	/* setting MTA */
 	mac->ops.mta_set = e1000_mta_set_generic;
-	/* blink LED */
-	mac->ops.blink_led = e1000_blink_led_generic;
-	/* setup LED */
-	mac->ops.setup_led = e1000_setup_led_generic;
-	/* cleanup LED */
-	mac->ops.cleanup_led = e1000_cleanup_led_ich8lan;
-	/* turn on/off LED */
-	mac->ops.led_on = e1000_led_on_ich8lan;
-	mac->ops.led_off = e1000_led_off_ich8lan;
 	/* clear hardware counters */
 	mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_ich8lan;
 
+	/* LED operations */
+	switch (mac->type) {
+	case e1000_ich8lan:
+	case e1000_ich9lan:
+	case e1000_ich10lan:
+		/* ID LED init */
+		mac->ops.id_led_init = e1000_id_led_init_generic;
+		/* blink LED */
+		mac->ops.blink_led = e1000_blink_led_generic;
+		/* setup LED */
+		mac->ops.setup_led = e1000_setup_led_generic;
+		/* cleanup LED */
+		mac->ops.cleanup_led = e1000_cleanup_led_ich8lan;
+		/* turn on/off LED */
+		mac->ops.led_on = e1000_led_on_ich8lan;
+		mac->ops.led_off = e1000_led_off_ich8lan;
+		break;
+	case e1000_pchlan:
+		/* ID LED init */
+		mac->ops.id_led_init = e1000_id_led_init_pchlan;
+		/* setup LED */
+		mac->ops.setup_led = e1000_setup_led_pchlan;
+		/* cleanup LED */
+		mac->ops.cleanup_led = e1000_cleanup_led_pchlan;
+		/* turn on/off LED */
+		mac->ops.led_on = e1000_led_on_pchlan;
+		mac->ops.led_off = e1000_led_off_pchlan;
+		break;
+	default:
+		break;
+	}
+
 	/* Enable PCS Lock-loss workaround for ICH8 */
 	if (mac->type == e1000_ich8lan)
 		e1000_set_kmrn_lock_loss_workaround_ich8lan(hw, true);
@@ -375,6 +464,98 @@
 }
 
 /*
+ * e1000_check_for_copper_link_ich8lan - Check for link (Copper)
+ * @hw: pointer to the HW structure
+ *
+ * Checks to see of the link status of the hardware has changed.  If a
+ * change in link status has been detected, then we read the PHY registers
+ * to get the current speed/duplex if link exists.
+ */
+static s32
+e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
+{
+	struct e1000_mac_info *mac = &hw->mac;
+	s32 ret_val;
+	bool link;
+
+	DEBUGFUNC("e1000_check_for_copper_link_ich8lan");
+
+	/*
+	 * We only want to go out to the PHY registers to see if Auto-Neg
+	 * has completed and/or if our link status has changed.  The
+	 * get_link_status flag is set upon receiving a Link Status
+	 * Change or Rx Sequence Error interrupt.
+	 */
+	if (!mac->get_link_status) {
+		ret_val = E1000_SUCCESS;
+		goto out;
+	}
+
+	if (hw->mac.type == e1000_pchlan) {
+		ret_val = e1000_write_kmrn_reg_generic(hw,
+		    E1000_KMRNCTRLSTA_K1_CONFIG,
+		    E1000_KMRNCTRLSTA_K1_ENABLE);
+		if (ret_val)
+			goto out;
+	}
+
+	/*
+	 * First we want to see if the MII Status Register reports
+	 * link.  If so, then we want to get the current speed/duplex
+	 * of the PHY.
+	 */
+	ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
+	if (ret_val)
+		goto out;
+
+	if (!link)
+		goto out; /* No link detected */
+
+	mac->get_link_status = false;
+
+	if (hw->phy.type == e1000_phy_82578) {
+		ret_val = e1000_link_stall_workaround_hv(hw);
+		if (ret_val)
+			goto out;
+	}
+
+	/*
+	 * Check if there was DownShift, must be checked
+	 * immediately after link-up
+	 */
+	e1000_check_downshift_generic(hw);
+
+	/*
+	 * If we are forcing speed/duplex, then we simply return since
+	 * we have already determined whether we have link or not.
+	 */
+	if (!mac->autoneg) {
+		ret_val = -E1000_ERR_CONFIG;
+		goto out;
+	}
+
+	/*
+	 * Auto-Neg is enabled.  Auto Speed Detection takes care
+	 * of MAC speed/duplex configuration.  So we only need to
+	 * configure Collision Distance in the MAC.
+	 */
+	e1000_config_collision_dist_generic(hw);
+
+	/*
+	 * Configure Flow Control now that Auto-Neg has completed.
+	 * First, we need to restore the desired flow control
+	 * settings because we may have had to re-autoneg with a
+	 * different link partner.
+	 */
+	ret_val = e1000_config_fc_after_link_up_generic(hw);
+	if (ret_val)
+		DEBUGOUT("Error configuring flow control\n");
+
+out:
+	return (ret_val);
+}
+
+/*
  * e1000_init_function_pointers_ich8lan - Initialize ICH8 function pointers
  * @hw: pointer to the HW structure
  *
@@ -387,7 +568,48 @@
 
 	hw->mac.ops.init_params = e1000_init_mac_params_ich8lan;
 	hw->nvm.ops.init_params = e1000_init_nvm_params_ich8lan;
-	hw->phy.ops.init_params = e1000_init_phy_params_ich8lan;
+	switch (hw->mac.type) {
+	case e1000_ich8lan:
+	case e1000_ich9lan:
+	case e1000_ich10lan:
+		hw->phy.ops.init_params = e1000_init_phy_params_ich8lan;
+		break;
+	case e1000_pchlan:
+		hw->phy.ops.init_params = e1000_init_phy_params_pchlan;
+		break;
+	default:
+		break;
+	}
+}
+
+/*
+ * e1000_acquire_nvm_ich8lan - Acquire NVM mutex
+ * @hw: pointer to the HW structure
+ *
+ * Acquires the mutex for performing NVM operations.
+ */
+static s32
+e1000_acquire_nvm_ich8lan(struct e1000_hw *hw)
+{
+	DEBUGFUNC("e1000_acquire_nvm_ich8lan");
+
+	E1000_MUTEX_LOCK(&hw->dev_spec.ich8lan.nvm_mutex);
+
+	return (E1000_SUCCESS);
+}
+
+/*
+ * e1000_release_nvm_ich8lan - Release NVM mutex
+ * @hw: pointer to the HW structure
+ *
+ * Releases the mutex used while performing NVM operations.
+ */
+static void
+e1000_release_nvm_ich8lan(struct e1000_hw *hw)
+{
+	DEBUGFUNC("e1000_release_nvm_ich8lan");
+
+	E1000_MUTEX_UNLOCK(&hw->dev_spec.ich8lan.nvm_mutex);
 }
 
 /*
@@ -406,20 +628,40 @@
 
 	DEBUGFUNC("e1000_acquire_swflag_ich8lan");
 
+	E1000_MUTEX_LOCK(&hw->dev_spec.ich8lan.swflag_mutex);
+
 	while (timeout) {
 		extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
-		extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG;
-		E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
-
-		extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
-		if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)
+		if (!(extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG))
 			break;
+
 		msec_delay_irq(1);
 		timeout--;
 	}
 
 	if (!timeout) {
-		DEBUGOUT("FW or HW has locked the resource for too long.\n");
+		DEBUGOUT("SW/FW/HW has locked the resource for too long.\n");
+		ret_val = -E1000_ERR_CONFIG;
+		goto out;
+	}
+
+	/* In some cases, hardware will take up to 400ms to set the SW flag. */
+	timeout = SW_FLAG_TIMEOUT;
+
+	extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG;
+	E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
+
+	while (timeout) {
+			extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+			if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)
+				break;
+
+		msec_delay_irq(1);
+		timeout--;
+	}
+
+	if (!timeout) {
+		DEBUGOUT("Failed to acquire the semaphore.\n");
 		extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
 		E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
 		ret_val = -E1000_ERR_CONFIG;
@@ -427,6 +669,9 @@
 	}
 
 out:
+	if (ret_val)
+		E1000_MUTEX_UNLOCK(&hw->dev_spec.ich8lan.swflag_mutex);
+
 	return (ret_val);
 }
 
@@ -448,6 +693,8 @@
 	extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
 	extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG;
 	E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
+
+	E1000_MUTEX_UNLOCK(&hw->dev_spec.ich8lan.swflag_mutex);
 }
 
 /*
@@ -493,80 +740,118 @@
 }
 
 /*
- * e1000_phy_force_speed_duplex_ich8lan - Force PHY speed & duplex
- * @hw: pointer to the HW structure
- *
- * Forces the speed and duplex settings of the PHY.
- * This is a function pointer entry point only called by
- * PHY setup routines.
+ * e1000_hv_phy_workarounds_ich8lan - A series of Phy workarounds to be
+ * done after every PHY reset.
  */
 static s32
-e1000_phy_force_speed_duplex_ich8lan(struct e1000_hw *hw)
+e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw)
 {
-	struct e1000_phy_info *phy = &hw->phy;
-	s32 ret_val;
-	u16 data;
-	bool link;
-
-	DEBUGFUNC("e1000_phy_force_speed_duplex_ich8lan");
-
-	if (phy->type != e1000_phy_ife) {
-		ret_val = e1000_phy_force_speed_duplex_igp(hw);
-		goto out;
+	s32 ret_val = E1000_SUCCESS;
+
+	if (hw->mac.type != e1000_pchlan)
+		return (ret_val);
+
+	if (((hw->phy.type == e1000_phy_82577) &&
+	    ((hw->phy.revision == 1) || (hw->phy.revision == 2))) ||
+	    ((hw->phy.type == e1000_phy_82578) && (hw->phy.revision == 1))) {
+		/* Disable generation of early preamble */
+		ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 25), 0x4431);
+		if (ret_val)
+			return (ret_val);
+
+		/* Preamble tuning for SSC */
+		ret_val = hw->phy.ops.write_reg(hw, PHY_REG(770, 16), 0xA204);
+		if (ret_val)
+			return (ret_val);
 	}
-	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &data);
+
+	if (hw->phy.type == e1000_phy_82578) {
+		/*
+		 * Return registers to default by doing a soft reset then
+		 * writing 0x3140 to the control register.
+		 */
+		if (hw->phy.revision < 2) {
+			e1000_phy_sw_reset_generic(hw);
+			ret_val = hw->phy.ops.write_reg(hw, PHY_CONTROL,
+			    0x3140);
+		}
+	}
+
+	/* Select page 0 */
+	ret_val = hw->phy.ops.acquire(hw);
 	if (ret_val)
-		goto out;
-
-	e1000_phy_force_speed_duplex_setup(hw, &data);
-
-	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, data);
+		return (ret_val);
+
+	hw->phy.addr = 1;
+	e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, 0);
+	hw->phy.ops.release(hw);
+
+	return (ret_val);
+}
+
+/*
+ * e1000_hv_phy_tuning_workaround_ich8lan - This is a Phy tuning work around
+ * needed for Nahum3 + Hanksville testing, requested by HW team
+ */
+static s32
+e1000_hv_phy_tuning_workaround_ich8lan(struct e1000_hw *hw)
+{
+	s32 ret_val = E1000_SUCCESS;
+
+	ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 25), 0x4431);
 	if (ret_val)
 		goto out;
 
-	/* Disable MDI-X support for 10/100 */
-	ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data);
-	if (ret_val)
-		goto out;
-
-	data &= ~IFE_PMC_AUTO_MDIX;
-	data &= ~IFE_PMC_FORCE_MDIX;
-
-	ret_val = phy->ops.write_reg(hw, IFE_PHY_MDIX_CONTROL, data);
+	ret_val = hw->phy.ops.write_reg(hw, PHY_REG(770, 16), 0xA204);
 	if (ret_val)
 		goto out;
 
-	DEBUGOUT1("IFE PMC: %X\n", data);
-
-	usec_delay(1);
-
-	if (phy->autoneg_wait_to_complete) {
-		DEBUGOUT("Waiting for forced speed/duplex link on IFE phy.\n");
-
-		ret_val = e1000_phy_has_link_generic(hw,
-		    PHY_FORCE_LIMIT,
-		    100000,
-		    &link);
-		if (ret_val)
-			goto out;
-
-		if (!link)
-			DEBUGOUT("Link taking longer than expected.\n");
-
-		/* Try once more */
-		ret_val = e1000_phy_has_link_generic(hw,
-		    PHY_FORCE_LIMIT,
-		    100000,
-		    &link);
-		if (ret_val)
-			goto out;
-	}
+	ret_val = hw->phy.ops.write_reg(hw, (1 << 6) | 0x29, 0x66C0);
+	if (ret_val)
+		goto out;
+
+	ret_val = hw->phy.ops.write_reg(hw, (1 << 6) | 0x1E, 0xFFFF);
 
 out:
 	return (ret_val);
 }
 
 /*
+ * e1000_lan_init_done_ich8lan - Check for PHY config completion
+ * @hw: pointer to the HW structure
+ *
+ * Check the appropriate indication the MAC has finished configuring the
+ * PHY after a software reset.
+ */
+static void
+e1000_lan_init_done_ich8lan(struct e1000_hw *hw)
+{
+	u32 data, loop = E1000_ICH8_LAN_INIT_TIMEOUT;
+
+	DEBUGFUNC("e1000_lan_init_done_ich8lan");
+
+	/* Wait for basic configuration completes before proceeding */
+	do {
+		data = E1000_READ_REG(hw, E1000_STATUS);
+		data &= E1000_STATUS_LAN_INIT_DONE;
+		usec_delay(100);
+	} while ((!data) && --loop);
+
+	/*
+	 * If basic configuration is incomplete before the above loop
+	 * count reaches 0, loading the configuration from NVM will
+	 * leave the PHY in a bad state possibly resulting in no link.
+	 */
+	if (loop == 0)
+		DEBUGOUT("LAN_INIT_DONE not set, increase timeout\n");
+
+	/* Clear the Init Done bit for the next init event */
+	data = E1000_READ_REG(hw, E1000_STATUS);
+	data &= ~E1000_STATUS_LAN_INIT_DONE;
+	E1000_WRITE_REG(hw, E1000_STATUS, data);
+}
+
+/*
  * e1000_phy_hw_reset_ich8lan - Performs a PHY reset
  * @hw: pointer to the HW structure
  *
@@ -580,7 +865,6 @@
 	struct e1000_phy_info *phy = &hw->phy;
 	u32 i, data, cnf_size, cnf_base_addr, sw_cfg_mask;
 	s32 ret_val;
-	u16 loop = E1000_ICH8_LAN_INIT_TIMEOUT;
 	u16 word_addr, reg_data, reg_addr, phy_page = 0;
 
 	DEBUGFUNC("e1000_phy_hw_reset_ich8lan");
@@ -589,6 +873,21 @@
 	if (ret_val)
 		goto out;
 
+	/* Allow time for h/w to get to a quiescent state after reset */
+	msec_delay(10);
+
+	if (hw->mac.type == e1000_pchlan) {
+		ret_val = e1000_hv_phy_workarounds_ich8lan(hw);
+		if (ret_val)
+			goto out;
+	}
+
+	if (hw->device_id == E1000_DEV_ID_ICH10_HANKSVILLE) {
+		ret_val = e1000_hv_phy_tuning_workaround_ich8lan(hw);
+		if (ret_val)
+			goto out;
+	}
+
 	/*
 	 * Initialize the PHY from the NVM on ICH platforms.  This is needed
 	 * due to an issue where the NVM configuration is not properly
@@ -608,24 +907,7 @@
 			goto out;
 
 		/* Wait for basic configuration completes before proceeding */
-		do {
-			data = E1000_READ_REG(hw, E1000_STATUS);
-			data &= E1000_STATUS_LAN_INIT_DONE;
-			usec_delay(100);
-		} while ((!data) && --loop);
-
-		/*
-		 * If basic configuration is incomplete before the above loop
-		 * count reaches 0, loading the configuration from NVM will
-		 * leave the PHY in a bad state possibly resulting in no link.
-		 */
-		if (loop == 0)
-			DEBUGOUT("LAN_INIT_DONE not set, increase timeout\n");
-
-		/* Clear the Init Done bit for the next init event */
-		data = E1000_READ_REG(hw, E1000_STATUS);
-		data &= ~E1000_STATUS_LAN_INIT_DONE;
-		E1000_WRITE_REG(hw, E1000_STATUS, data);
+		e1000_lan_init_done_ich8lan(hw);
 
 		/*
 		 * Make sure HW does not configure LCD from PHY extended
@@ -705,6 +987,8 @@
 		break;
 	case e1000_phy_igp_3:
 	case e1000_phy_bm:
+	case e1000_phy_82578:
+	case e1000_phy_82577:
 		ret_val = e1000_get_phy_info_igp(hw);
 		break;
 	default:
@@ -749,7 +1033,7 @@
 	    ? false : true;
 
 	if (phy->polarity_correction) {
-		ret_val = e1000_check_polarity_ife_ich8lan(hw);
+		ret_val = e1000_check_polarity_ife(hw);
 		if (ret_val)
 			goto out;
 	} else {
@@ -775,44 +1059,6 @@
 }
 
 /*
- * e1000_check_polarity_ife_ich8lan - Check cable polarity for IFE PHY
- * @hw: pointer to the HW structure
- *
- * Polarity is determined on the polarity reversal feature being enabled.
- * This function is only called by other family-specific
- * routines.
- */
-static s32
-e1000_check_polarity_ife_ich8lan(struct e1000_hw *hw)
-{
-	struct e1000_phy_info *phy = &hw->phy;
-	s32 ret_val;
-	u16 phy_data, offset, mask;
-
-	DEBUGFUNC("e1000_check_polarity_ife_ich8lan");
-
-	/*
-	 * Polarity is determined based on the reversal feature being enabled.
-	 */
-	if (phy->polarity_correction) {
-		offset = IFE_PHY_EXTENDED_STATUS_CONTROL;
-		mask = IFE_PESC_POLARITY_REVERSED;
-	} else {
-		offset = IFE_PHY_SPECIAL_CONTROL;
-		mask = IFE_PSC_FORCE_POLARITY;
-	}
-
-	ret_val = phy->ops.read_reg(hw, offset, &phy_data);
-
-	if (!ret_val)
-		phy->cable_polarity = (phy_data & mask)
-		    ? e1000_rev_polarity_reversed
-		    : e1000_rev_polarity_normal;
-
-	return (ret_val);
-}
-
-/*
  * e1000_set_d0_lplu_state_ich8lan - Set Low Power Linkup D0 state
  * @hw: pointer to the HW structure
  * @active: true to enable LPLU, false to disable
@@ -844,12 +1090,14 @@
 		phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU;
 		E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl);
 
+		if (phy->type != e1000_phy_igp_3)
+			goto out;
+
 		/*
 		 * Call gig speed drop workaround on LPLU before accessing any
 		 * PHY registers
 		 */
-		if ((hw->mac.type == e1000_ich8lan) &&
-		    (hw->phy.type == e1000_phy_igp_3))
+		if (hw->mac.type == e1000_ich8lan)
 			e1000_gig_downshift_workaround_ich8lan(hw);
 
 		/* When LPLU is enabled, we should disable SmartSpeed */
@@ -866,6 +1114,9 @@
 		phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU;
 		E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl);
 
+		if (phy->type != e1000_phy_igp_3)
+			goto out;
+
 		/*
 		 * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
 		 * during Dx states where the power conservation is most
@@ -933,6 +1184,10 @@
 	if (!active) {
 		phy_ctrl &= ~E1000_PHY_CTRL_NOND0A_LPLU;
 		E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl);
+
+		if (phy->type != e1000_phy_igp_3)
+			goto out;
+
 		/*
 		 * LPLU and SmartSpeed are mutually exclusive.  LPLU is used
 		 * during Dx states where the power conservation is most
@@ -972,12 +1227,14 @@
 		phy_ctrl |= E1000_PHY_CTRL_NOND0A_LPLU;
 		E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl);
 
+		if (phy->type != e1000_phy_igp_3)
+			goto out;
+
 		/*
 		 * Call gig speed drop workaround on LPLU before accessing any
 		 * PHY registers
 		 */
-		if ((hw->mac.type == e1000_ich8lan) &&
-		    (hw->phy.type == e1000_phy_igp_3))
+		if (hw->mac.type == e1000_ich8lan)
 			e1000_gig_downshift_workaround_ich8lan(hw);
 
 		/* When LPLU is enabled, we should disable SmartSpeed */
@@ -1003,50 +1260,65 @@
  * @bank:  pointer to the variable that returns the active bank
  *
  * Reads signature byte from the NVM using the flash access registers.
+ * Word 0x13 bits 15:14 = 10b indicate a valid signature for that bank.
  */
 static s32
 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank)
 {
-	s32 ret_val = E1000_SUCCESS;
+	u32 eecd;
 	struct e1000_nvm_info *nvm = &hw->nvm;
-	/* flash bank size is in words */
 	u32 bank1_offset = nvm->flash_bank_size * sizeof (u16);
 	u32 act_offset = E1000_ICH_NVM_SIG_WORD * 2 + 1;
-	u8 bank_high_byte = 0;
-
-	if (hw->mac.type != e1000_ich10lan) {
-		if (E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_SEC1VAL)
-			*bank = 1;
-		else
-			*bank = 0;
-	} else {
-		/*
-		 * Make sure the signature for bank 0 is valid,
-		 * if not check for bank1
-		 */
-		(void) e1000_read_flash_byte_ich8lan(hw,
-		    act_offset, &bank_high_byte);
-		if ((bank_high_byte & 0xC0) == 0x80) {
+	u8 sig_byte = 0;
+	s32 ret_val = E1000_SUCCESS;
+
+	switch (hw->mac.type) {
+	case e1000_ich8lan:
+	case e1000_ich9lan:
+		eecd = E1000_READ_REG(hw, E1000_EECD);
+		if ((eecd & E1000_EECD_SEC1VAL_VALID_MASK) ==
+		    E1000_EECD_SEC1VAL_VALID_MASK) {
+			if (eecd & E1000_EECD_SEC1VAL)
+				*bank = 1;
+			else
+				*bank = 0;
+
+			goto out;
+		}
+		DEBUGOUT("Unable to determine valid NVM bank via EEC - "
+		    "reading flash signature\n");
+		/* fall-thru */
+	default:
+		/* set bank to 0 in case flash read fails */
+		*bank = 0;
+
+		/* Check bank 0 */
+		ret_val = e1000_read_flash_byte_ich8lan(hw, act_offset,
+		    &sig_byte);
+		if (ret_val)
+			goto out;
+		if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
+		    E1000_ICH_NVM_SIG_VALUE) {
 			*bank = 0;
-		} else {
-			/*
-			 * find if segment 1 is valid by verifying
-			 * bit 15:14 = 10b in word 0x13
-			 */
-			(void) e1000_read_flash_byte_ich8lan(hw,
-			    act_offset + bank1_offset,
-			    &bank_high_byte);
-
-			/* bank1 has a valid signature equivalent to SEC1V */
-			if ((bank_high_byte & 0xC0) == 0x80) {
-				*bank = 1;
-			} else {
-				DEBUGOUT("ERROR: EEPROM not present\n");
-				ret_val = -E1000_ERR_NVM;
-			}
+			goto out;
 		}
+
+		/* Check bank 1 */
+		ret_val = e1000_read_flash_byte_ich8lan(hw, act_offset +
+		    bank1_offset, &sig_byte);
+		if (ret_val)
+			goto out;
+		if ((sig_byte & E1000_ICH_NVM_VALID_SIG_MASK) ==
+		    E1000_ICH_NVM_SIG_VALUE) {
+			*bank = 1;
+			goto out;
+		}
+
+		DEBUGOUT("ERROR: No valid NVM bank present\n");
+		ret_val = -E1000_ERR_NVM;
+		break;
 	}
-
+out:
 	return (ret_val);
 }
 
@@ -1079,17 +1351,18 @@
 		goto out;
 	}
 
-	ret_val = nvm->ops.acquire(hw);
-	if (ret_val)
-		goto out;
+	nvm->ops.acquire(hw);
 
 	ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
-	if (ret_val != E1000_SUCCESS)
-		goto out;
+	if (ret_val != E1000_SUCCESS) {
+		DEBUGOUT("Could not detect valid bank, assuming bank 0\n");
+		bank = 0;
+	}
 
 	act_offset = (bank) ? nvm->flash_bank_size : 0;
 	act_offset += offset;
 
+	ret_val = E1000_SUCCESS;
 	for (i = 0; i < words; i++) {
 		if ((dev_spec->shadow_ram) &&
 		    (dev_spec->shadow_ram[offset + i].modified)) {
@@ -1107,6 +1380,9 @@
 	nvm->ops.release(hw);
 
 out:
+	if (ret_val)
+		DEBUGOUT1("NVM read error: %d\n", ret_val);
+
 	return (ret_val);
 }
 
@@ -1388,9 +1664,7 @@
 		goto out;
 	}
 
-	ret_val = nvm->ops.acquire(hw);
-	if (ret_val)
-		goto out;
+	nvm->ops.acquire(hw);
 
 	for (i = 0; i < words; i++) {
 		dev_spec->shadow_ram[offset + i].modified = true;
@@ -1432,9 +1706,7 @@
 	if (nvm->type != e1000_nvm_flash_sw)
 		goto out;
 
-	ret_val = nvm->ops.acquire(hw);
-	if (ret_val)
-		goto out;
+	nvm->ops.acquire(hw);
 
 	/*
 	 * We're writing to the opposite bank so if we're on bank 1, write to
@@ -1442,17 +1714,27 @@
 	 * written
 	 */
 	ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank);
-	if (ret_val != E1000_SUCCESS)
-		goto out;
+	if (ret_val != E1000_SUCCESS) {
+		DEBUGOUT("Could not detect valid bank, assuming bank 0\n");
+		bank = 0;
+	}
 
 	if (bank == 0) {
 		new_bank_offset = nvm->flash_bank_size;
 		old_bank_offset = 0;
-		(void) e1000_erase_flash_bank_ich8lan(hw, 1);
+		ret_val = e1000_erase_flash_bank_ich8lan(hw, 1);
+		if (ret_val) {
+			nvm->ops.release(hw);
+			goto out;
+		}
 	} else {
 		old_bank_offset = nvm->flash_bank_size;
 		new_bank_offset = 0;
-		(void) e1000_erase_flash_bank_ich8lan(hw, 0);
+		ret_val = e1000_erase_flash_bank_ich8lan(hw, 0);
+		if (ret_val) {
+			nvm->ops.release(hw);
+			goto out;
+		}
 	}
 
 	for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) {
@@ -1463,9 +1745,11 @@
 		if (dev_spec->shadow_ram[i].modified) {
 			data = dev_spec->shadow_ram[i].value;
 		} else {
-			(void) e1000_read_flash_word_ich8lan(hw,
+			ret_val = e1000_read_flash_word_ich8lan(hw,
 			    i + old_bank_offset,
 			    &data);
+			if (ret_val)
+				break;
 		}
 
 		/*
@@ -1514,7 +1798,12 @@
 	 * bits are 11 to start with and we need to change bit 14 to 0b
 	 */
 	act_offset = new_bank_offset + E1000_ICH_NVM_SIG_WORD;
-	(void) e1000_read_flash_word_ich8lan(hw, act_offset, &data);
+	ret_val = e1000_read_flash_word_ich8lan(hw, act_offset, &data);
+	if (ret_val) {
+		nvm->ops.release(hw);
+		goto out;
+	}
+
 	data &= 0xBFFF;
 	ret_val = e1000_retry_write_flash_byte_ich8lan(hw,
 	    act_offset * 2 + 1,
@@ -1553,6 +1842,9 @@
 	msec_delay(10);
 
 out:
+	if (ret_val)
+		DEBUGOUT1("NVM update error: %d\n", ret_val);
+
 	return (ret_val);
 }
 
@@ -1782,20 +2074,15 @@
 		break;
 	case 1:
 		sector_size = ICH_FLASH_SEG_SIZE_4K;
-		iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_4K;
+		iteration = 1;
 		break;
 	case 2:
-		if (hw->mac.type == e1000_ich9lan) {
 			sector_size = ICH_FLASH_SEG_SIZE_8K;
-			iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_8K;
-		} else {
-			ret_val = -E1000_ERR_NVM;
-			goto out;
-		}
+			iteration = 1;
 		break;
 	case 3:
 		sector_size = ICH_FLASH_SEG_SIZE_64K;
-		iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_64K;
+		iteration = 1;
 		break;
 	default:
 		ret_val = -E1000_ERR_NVM;
@@ -1804,7 +2091,7 @@
 
 	/* Start with the base address, then add the sector offset. */
 	flash_linear_addr = hw->nvm.flash_base_addr;
-	flash_linear_addr += (bank) ? (sector_size * iteration) : 0;
+	flash_linear_addr += (bank) ? flash_bank_size : 0;
 
 	for (j = 0; j < iteration; j++) {
 		do {
@@ -1888,6 +2175,82 @@
 }
 
 /*
+ * e1000_id_led_init_pchlan - store LED configurations
+ * @hw: pointer to the HW structure
+ *
+ * PCH does not control LEDs via the LEDCTL register, rather it uses
+ * the PHY LED configuration register.
+ *
+ * PCH also does not have an "always on" or "always off" mode which
+ * complicates the ID feature.  Instead of using the "on" mode to indicate
+ * in ledctl_mode2 the LEDs to use for ID (see e1000_id_led_init_generic()),
+ * use "link_up" mode.  The LEDs will still ID on request if there is no
+ * link based on logic in e1000_led_[on|off]_pchlan().
+ */
+static s32
+e1000_id_led_init_pchlan(struct e1000_hw *hw)
+{
+	struct e1000_mac_info *mac = &hw->mac;
+	s32 ret_val;
+	const u32 ledctl_on = E1000_LEDCTL_MODE_LINK_UP;
+	const u32 ledctl_off = E1000_LEDCTL_MODE_LINK_UP | E1000_PHY_LED0_IVRT;
+	u16 data, i, temp, shift;
+
+	DEBUGFUNC("e1000_id_led_init_pchlan");
+
+	/* Get default ID LED modes */
+	ret_val = hw->nvm.ops.valid_led_default(hw, &data);
+	if (ret_val)
+		goto out;
+
+	mac->ledctl_default = E1000_READ_REG(hw, E1000_LEDCTL);
+	mac->ledctl_mode1 = mac->ledctl_default;
+	mac->ledctl_mode2 = mac->ledctl_default;
+
+	for (i = 0; i < 4; i++) {
+		temp = (data >> (i << 2)) & E1000_LEDCTL_LED0_MODE_MASK;
+		shift = (i * 5);
+		switch (temp) {
+		case ID_LED_ON1_DEF2:
+		case ID_LED_ON1_ON2:
+		case ID_LED_ON1_OFF2:
+			mac->ledctl_mode1 &= ~(E1000_PHY_LED0_MASK << shift);
+			mac->ledctl_mode1 |= (ledctl_on << shift);
+			break;
+		case ID_LED_OFF1_DEF2:
+		case ID_LED_OFF1_ON2:
+		case ID_LED_OFF1_OFF2:
+			mac->ledctl_mode1 &= ~(E1000_PHY_LED0_MASK << shift);
+			mac->ledctl_mode1 |= (ledctl_off << shift);
+			break;
+		default:
+			/* Do nothing */
+			break;
+		}
+		switch (temp) {
+		case ID_LED_DEF1_ON2:
+		case ID_LED_ON1_ON2:
+		case ID_LED_OFF1_ON2:
+			mac->ledctl_mode2 &= ~(E1000_PHY_LED0_MASK << shift);
+			mac->ledctl_mode2 |= (ledctl_on << shift);
+			break;
+		case ID_LED_DEF1_OFF2:
+		case ID_LED_ON1_OFF2:
+		case ID_LED_OFF1_OFF2:
+			mac->ledctl_mode2 &= ~(E1000_PHY_LED0_MASK << shift);
+			mac->ledctl_mode2 |= (ledctl_off << shift);
+			break;
+		default:
+			/* Do nothing */
+			break;
+		}
+	}
+
+out:
+	return (ret_val);
+}
+
+/*
  * e1000_get_bus_info_ich8lan - Get/Set the bus type and width
  * @hw: pointer to the HW structure
  *
@@ -1963,6 +2326,13 @@
 	ctrl = E1000_READ_REG(hw, E1000_CTRL);
 
 	if (!hw->phy.ops.check_reset_block(hw) && !hw->phy.reset_disable) {
+		/* Clear PHY Reset Asserted bit */
+		if (hw->mac.type >= e1000_pchlan) {
+			u32 status = E1000_READ_REG(hw, E1000_STATUS);
+			E1000_WRITE_REG(hw, E1000_STATUS, status &
+			    ~E1000_STATUS_PHYRA);
+		}
+
 		/*
 		 * PHY HW reset requires MAC CORE reset at the same time to
 		 * make sure the interface between MAC and the external PHY is
@@ -1972,20 +2342,39 @@
 	}
 
 	ret_val = e1000_acquire_swflag_ich8lan(hw);
-	DEBUGOUT("Issuing a global reset to ich8lan");
+
+	DEBUGOUT("Issuing a global reset to ich8lan\n");
 	E1000_WRITE_REG(hw, E1000_CTRL, (ctrl | E1000_CTRL_RST));
 	msec_delay(20);
 
-	ret_val = e1000_get_auto_rd_done_generic(hw);
-	if (ret_val) {
-		/*
-		 * When auto config read does not complete, do not return with
-		 * an error. This can happen in situations where there is no
-		 * eeprom and prevents getting link.
-		 */
-		DEBUGOUT("Auto Read Done did not complete\n");
+	if (!ret_val)
+		e1000_release_swflag_ich8lan(hw);
+
+	if (ctrl & E1000_CTRL_PHY_RST)
+		ret_val = hw->phy.ops.get_cfg_done(hw);
+
+	if (hw->mac.type >= e1000_ich10lan) {
+		e1000_lan_init_done_ich8lan(hw);
+	} else {
+		ret_val = e1000_get_auto_rd_done_generic(hw);
+		if (ret_val) {
+			/*
+			 * When auto config read does not complete, do not
+			 * return with an error. This can happen in situations
+			 * where there is no eeprom and prevents getting link.
+			 */
+			DEBUGOUT("Auto Read Done did not complete\n");
+		}
 	}
 
+	/*
+	 * For PCH, this write will make sure that any noise
+	 * will be detected as a CRC error and be dropped rather than show up
+	 * as a bad packet to the DMA engine.
+	 */
+	if (hw->mac.type == e1000_pchlan)
+		E1000_WRITE_REG(hw, E1000_CRC_OFFSET, 0x65656565);
+
 	E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
 	(void) E1000_READ_REG(hw, E1000_ICR);
 
@@ -1993,6 +2382,16 @@
 	kab |= E1000_KABGTXD_BGSQLBIAS;
 	E1000_WRITE_REG(hw, E1000_KABGTXD, kab);
 
+	if (hw->mac.type == e1000_pchlan)
+		ret_val = e1000_hv_phy_workarounds_ich8lan(hw);
+
+	if (ret_val)
+		goto out;
+
+	if (hw->device_id == E1000_DEV_ID_ICH10_HANKSVILLE)
+		ret_val = e1000_hv_phy_tuning_workaround_ich8lan(hw);
+
+out:
 	return (ret_val);
 }
 
@@ -2001,12 +2400,12 @@
  * @hw: pointer to the HW structure
  *
  * Prepares the hardware for transmit and receive by doing the following:
- *  - initialize hardware bits
- *  - initialize LED identification
- *  - setup receive address registers
- *  - setup flow control
- *  - setup transmit descriptors
- *  - clear statistics
+ * - initialize hardware bits
+ * - initialize LED identification
+ * - setup receive address registers
+ * - setup flow control
+ * - setup transmit descriptors
+ * - clear statistics
  */
 static s32
 e1000_init_hw_ich8lan(struct e1000_hw *hw)
@@ -2021,11 +2420,10 @@
 	e1000_initialize_hw_bits_ich8lan(hw);
 
 	/* Initialize identification LED */
-	ret_val = e1000_id_led_init_generic(hw);
-	if (ret_val) {
+	ret_val = mac->ops.id_led_init(hw);
+	if (ret_val)
+		/* This is not fatal and we should not stop init due to this */
 		DEBUGOUT("Error initializing identification LED\n");
-		/* This is not fatal and we should not stop init due to this */
-	}
 
 	/* Setup the receive address. */
 	e1000_init_rx_addrs_generic(hw, mac->rar_entry_count);
@@ -2035,6 +2433,18 @@
 	for (i = 0; i < mac->mta_reg_count; i++)
 		E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
 
+	/*
+	 * The 82578 Rx buffer will stall if wakeup is enabled in host and
+	 * the ME.  Reading the BM_WUC register will clear the host wakeup bit.
+	 * Reset the phy after disabling host wakeup to reset the Rx buffer.
+	 */
+	if (hw->phy.type == e1000_phy_82578) {
+		hw->phy.ops.read_reg(hw, BM_WUC, &i);
+		ret_val = e1000_phy_hw_reset_ich8lan(hw);
+		if (ret_val)
+			return (ret_val);
+	}
+
 	/* Setup link and flow control */
 	ret_val = mac->ops.setup_link(hw);
 
@@ -2094,6 +2504,9 @@
 	/* Extended Device Control */
 	reg = E1000_READ_REG(hw, E1000_CTRL_EXT);
 	reg |= (1 << 22);
+	/* Enable PHY low-power state when MAC is at D3 w/o WoL */
+	if (hw->mac.type >= e1000_pchlan)
+		reg |= E1000_CTRL_EXT_PHYPDEN;
 	E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg);
 
 	/* Transmit Descriptor Control 0 */
@@ -2171,6 +2584,14 @@
 		goto out;
 
 	E1000_WRITE_REG(hw, E1000_FCTTV, hw->fc.pause_time);
+	if ((hw->phy.type == e1000_phy_82578) ||
+	    (hw->phy.type == e1000_phy_82577)) {
+		ret_val = hw->phy.ops.write_reg(hw,
+		    PHY_REG(BM_PORT_CTRL_PAGE, 27),
+		    hw->fc.pause_time);
+		if (ret_val)
+			goto out;
+	}
 
 	ret_val = e1000_set_fc_watermarks_generic(hw);
 
@@ -2205,31 +2626,38 @@
 	 * increase the max iterations when polling the phy; this fixes
 	 * erroneous timeouts at 10Mbps.
 	 */
-	ret_val = e1000_write_kmrn_reg_generic(hw, GG82563_REG(0x34, 4),
-	    0xFFFF);
+	ret_val = e1000_write_kmrn_reg_generic(hw,
+	    E1000_KMRNCTRLSTA_TIMEOUTS, 0xFFFF);
 	if (ret_val)
 		goto out;
-	ret_val = e1000_read_kmrn_reg_generic(hw, GG82563_REG(0x34, 9),
-	    &reg_data);
+	ret_val = e1000_read_kmrn_reg_generic(hw,
+	    E1000_KMRNCTRLSTA_INBAND_PARAM, &reg_data);
 	if (ret_val)
 		goto out;
 	reg_data |= 0x3F;
-	ret_val = e1000_write_kmrn_reg_generic(hw, GG82563_REG(0x34, 9),
-	    reg_data);
+	ret_val = e1000_write_kmrn_reg_generic(hw,
+	    E1000_KMRNCTRLSTA_INBAND_PARAM, reg_data);
 	if (ret_val)
 		goto out;
 
-	if (hw->phy.type == e1000_phy_igp_3) {
+	switch (hw->phy.type) {
+	case e1000_phy_igp_3:
 		ret_val = e1000_copper_link_setup_igp(hw);
 		if (ret_val)
 			goto out;
-	} else if (hw->phy.type == e1000_phy_bm) {
+		break;
+	case e1000_phy_bm:
+	case e1000_phy_82578:
 		ret_val = e1000_copper_link_setup_m88(hw);
 		if (ret_val)
 			goto out;
-	}
-
-	if (hw->phy.type == e1000_phy_ife) {
+		break;
+	case e1000_phy_82577:
+		ret_val = e1000_copper_link_setup_82577(hw);
+		if (ret_val)
+			goto out;
+		break;
+	case e1000_phy_ife:
 		ret_val = hw->phy.ops.read_reg(hw, IFE_PHY_MDIX_CONTROL,
 		    &reg_data);
 		if (ret_val)
@@ -2253,6 +2681,9 @@
 		    reg_data);
 		if (ret_val)
 			goto out;
+		break;
+	default:
+		break;
 	}
 	ret_val = e1000_setup_copper_link_generic(hw);
 
@@ -2281,6 +2712,14 @@
 	if (ret_val)
 		goto out;
 
+	if ((hw->mac.type == e1000_pchlan) && (*speed == SPEED_1000)) {
+		ret_val = e1000_write_kmrn_reg_generic(hw,
+		    E1000_KMRNCTRLSTA_K1_CONFIG,
+		    E1000_KMRNCTRLSTA_K1_DISABLE);
+		if (ret_val)
+			goto out;
+	}
+
 	if ((hw->mac.type == e1000_ich8lan) &&
 	    (hw->phy.type == e1000_phy_igp_3) &&
 	    (*speed == SPEED_1000)) {
@@ -2298,12 +2737,12 @@
  * Work-around for 82566 Kumeran PCS lock loss:
  * On link status change (i.e. PCI reset, speed change) and link is up and
  * speed is gigabit-
- *   0) if workaround is optionally disabled do nothing
- *   1) wait 1ms for Kumeran link to come up
- *   2) check Kumeran Diagnostic register PCS lock loss bit
- *   3) if not set the link is locked (all is good), otherwise...
- *   4) reset the PHY
- *   5) repeat up to 10 times
+ * 0) if workaround is optionally disabled do nothing
+ * 1) wait 1ms for Kumeran link to come up
+ * 2) check Kumeran Diagnostic register PCS lock loss bit
+ * 3) if not set the link is locked (all is good), otherwise...
+ * 4) reset the PHY
+ * 5) repeat up to 10 times
  * Note: this is only called for IGP3 copper when speed is 1gb.
  */
 static s32
@@ -2398,9 +2837,9 @@
  * @hw: pointer to the HW structure
  *
  * Workaround for 82566 power-down on D3 entry:
- *   1) disable gigabit link
- *   2) write VR power-down enable
- *   3) read it back
+ * 1) disable gigabit link
+ * 2) write VR power-down enable
+ * 3) read it back
  * Continue if successful, else issue LCD reset and repeat
  */
 void
@@ -2455,8 +2894,8 @@
  *
  * Steps to take when dropping from 1Gb/s (eg. link cable removal (LSC),
  * LPLU, Gig disable, MDIC PHY reset):
- *   1) Set Kumeran Near-end loopback
- *   2) Clear Kumeran Near-end loopback
+ * 1) Set Kumeran Near-end loopback
+ * 2) Clear Kumeran Near-end loopback
  * Should only be called for ICH8[m] devices with IGP_3 Phy.
  */
 void
@@ -2496,19 +2935,27 @@
  * 'LPLU Enabled' and 'Gig Disable' to force link speed negotiation
  * to a lower speed.
  *
- * Should only be called for ICH9 and ICH10 devices.
+ * Should only be called for applicable parts.
  */
 void
 e1000_disable_gig_wol_ich8lan(struct e1000_hw *hw)
 {
 	u32 phy_ctrl;
 
-	if ((hw->mac.type == e1000_ich10lan) ||
-	    (hw->mac.type == e1000_ich9lan)) {
+	switch (hw->mac.type) {
+	case e1000_ich9lan:
+	case e1000_ich10lan:
+	case e1000_pchlan:
 		phy_ctrl = E1000_READ_REG(hw, E1000_PHY_CTRL);
 		phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU |
 		    E1000_PHY_CTRL_GBE_DISABLE;
 		E1000_WRITE_REG(hw, E1000_PHY_CTRL, phy_ctrl);
+
+	/* Workaround SWFLAG unexpectedly set during S0->Sx */
+	if (hw->mac.type == e1000_pchlan)
+		usec_delay(500);
+	default:
+		break;
 	}
 }
 
@@ -2581,6 +3028,104 @@
 }
 
 /*
+ * e1000_setup_led_pchlan - Configures SW controllable LED
+ * @hw: pointer to the HW structure
+ *
+ * This prepares the SW controllable LED for use.
+ */
+static s32
+e1000_setup_led_pchlan(struct e1000_hw *hw)
+{
+	DEBUGFUNC("e1000_setup_led_pchlan");
+
+	return (hw->phy.ops.write_reg(hw, HV_LED_CONFIG,
+	    (u16)hw->mac.ledctl_mode1));
+}
+
+/*
+ * e1000_cleanup_led_pchlan - Restore the default LED operation
+ * @hw: pointer to the HW structure
+ *
+ * Return the LED back to the default configuration.
+ */
+static s32
+e1000_cleanup_led_pchlan(struct e1000_hw *hw)
+{
+	DEBUGFUNC("e1000_cleanup_led_pchlan");
+
+	return (hw->phy.ops.write_reg(hw, HV_LED_CONFIG,
+	    (u16)hw->mac.ledctl_default));
+}
+
+/*
+ * e1000_led_on_pchlan - Turn LEDs on
+ * @hw: pointer to the HW structure
+ *
+ * Turn on the LEDs.
+ */
+static s32
+e1000_led_on_pchlan(struct e1000_hw *hw)
+{
+	u16 data = (u16)hw->mac.ledctl_mode2;
+	u32 i, led;
+
+	DEBUGFUNC("e1000_led_on_pchlan");
+
+	/*
+	 * If no link, then turn LED on by setting the invert bit
+	 * for each LED that's mode is "link_up" in ledctl_mode2.
+	 */
+	if (!(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)) {
+		for (i = 0; i < 3; i++) {
+			led = (data >> (i * 5)) & E1000_PHY_LED0_MASK;
+			if ((led & E1000_PHY_LED0_MODE_MASK) !=
+			    E1000_LEDCTL_MODE_LINK_UP)
+				continue;
+			if (led & E1000_PHY_LED0_IVRT)
+				data &= ~(E1000_PHY_LED0_IVRT << (i * 5));
+			else
+				data |= (E1000_PHY_LED0_IVRT << (i * 5));
+		}
+	}
+
+	return (hw->phy.ops.write_reg(hw, HV_LED_CONFIG, data));
+}
+
+/*
+ * e1000_led_off_pchlan - Turn LEDs off
+ * @hw: pointer to the HW structure
+ *
+ * Turn off the LEDs.
+ */
+static s32
+e1000_led_off_pchlan(struct e1000_hw *hw)
+{
+	u16 data = (u16)hw->mac.ledctl_mode1;
+	u32 i, led;
+
+	DEBUGFUNC("e1000_led_off_pchlan");
+
+	/*
+	 * If no link, then turn LED off by clearing the invert bit
+	 * for each LED that's mode is "link_up" in ledctl_mode1.
+	 */
+	if (!(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)) {
+		for (i = 0; i < 3; i++) {
+			led = (data >> (i * 5)) & E1000_PHY_LED0_MASK;
+			if ((led & E1000_PHY_LED0_MODE_MASK) !=
+			    E1000_LEDCTL_MODE_LINK_UP)
+				continue;
+			if (led & E1000_PHY_LED0_IVRT)
+				data &= ~(E1000_PHY_LED0_IVRT << (i * 5));
+			else
+				data |= (E1000_PHY_LED0_IVRT << (i * 5));
+		}
+	}
+
+	return (hw->phy.ops.write_reg(hw, HV_LED_CONFIG, data));
+}
+
+/*
  * e1000_get_cfg_done_ich8lan - Read config done bit
  * @hw: pointer to the HW structure
  *
@@ -2596,10 +3141,21 @@
 	s32 ret_val = E1000_SUCCESS;
 	u32 bank = 0;
 
+	if (hw->mac.type >= e1000_pchlan) {
+		u32 status = E1000_READ_REG(hw, E1000_STATUS);
+
+		if (status & E1000_STATUS_PHYRA) {
+			E1000_WRITE_REG(hw, E1000_STATUS, status &
+			    ~E1000_STATUS_PHYRA);
+		} else
+		DEBUGOUT("PHY Reset Asserted not set - needs delay\n");
+	}
+
 	(void) e1000_get_cfg_done_generic(hw);
 
 	/* If EEPROM is not marked present, init the IGP 3 PHY manually */
-	if (hw->mac.type != e1000_ich10lan) {
+	if ((hw->mac.type != e1000_ich10lan) &&
+	    (hw->mac.type != e1000_pchlan)) {
 		if (((E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) == 0) &&
 		    (hw->phy.type == e1000_phy_igp_3)) {
 			(void) e1000_phy_init_script_igp3(hw);
@@ -2641,6 +3197,8 @@
 static void
 e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw)
 {
+	u16 phy_data;
+
 	DEBUGFUNC("e1000_clear_hw_cntrs_ich8lan");
 
 	e1000_clear_hw_cntrs_base_generic(hw);
@@ -2658,4 +3216,23 @@
 
 	(void) E1000_READ_REG(hw, E1000_IAC);
 	(void) E1000_READ_REG(hw, E1000_ICRXOC);
+
+	/* Clear PHY statistics registers */
+	if ((hw->phy.type == e1000_phy_82578) ||
+	    (hw->phy.type == e1000_phy_82577)) {
+		(void) hw->phy.ops.read_reg(hw, HV_SCC_UPPER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_SCC_LOWER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_ECOL_UPPER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_ECOL_LOWER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_MCC_UPPER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_MCC_LOWER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_LATECOL_UPPER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_LATECOL_LOWER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_COLC_UPPER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_COLC_LOWER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_DC_UPPER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_DC_LOWER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_TNCRS_UPPER, &phy_data);
+		(void) hw->phy.ops.read_reg(hw, HV_TNCRS_LOWER, &phy_data);
+	}
 }
--- a/usr/src/uts/common/io/e1000g/e1000_ich8lan.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_ich8lan.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.25 sol_anvik_patch
+ * IntelVersion: 1.37 v3-1-3_2009-8-20
  */
 #ifndef _E1000_ICH8LAN_H_
 #define	_E1000_ICH8LAN_H_
@@ -39,9 +39,10 @@
 #define	ICH_FLASH_FADDR			0x0008
 #define	ICH_FLASH_FDATA0		0x0010
 
-#define	ICH_FLASH_READ_COMMAND_TIMEOUT	500
-#define	ICH_FLASH_WRITE_COMMAND_TIMEOUT	500
-#define	ICH_FLASH_ERASE_COMMAND_TIMEOUT	3000000
+/* Requires up to 10 seconds when MNG might be accessing part. */
+#define	ICH_FLASH_READ_COMMAND_TIMEOUT	10000000
+#define	ICH_FLASH_WRITE_COMMAND_TIMEOUT	10000000
+#define	ICH_FLASH_ERASE_COMMAND_TIMEOUT	10000000
 #define	ICH_FLASH_LINEAR_ADDR_MASK	0x00FFFFFF
 #define	ICH_FLASH_CYCLE_REPEAT_COUNT	10
 
@@ -68,12 +69,14 @@
 #define	E1000_ICH_MNG_IAMT_MODE		0x2
 
 #define	ID_LED_DEFAULT_ICH8LAN	((ID_LED_DEF1_DEF2 << 12) | \
-				(ID_LED_DEF1_OFF2 << 8) | \
-				(ID_LED_DEF1_ON2 << 4) | \
+				(ID_LED_OFF1_OFF2 << 8) | \
+				(ID_LED_OFF1_ON2 << 4) | \
 				(ID_LED_DEF1_DEF2))
 
 #define	E1000_ICH_NVM_SIG_WORD		0x13
 #define	E1000_ICH_NVM_SIG_MASK		0xC000
+#define	E1000_ICH_NVM_VALID_SIG_MASK	0xC0
+#define	E1000_ICH_NVM_SIG_VALUE		0x80
 
 #define	E1000_ICH8_LAN_INIT_TIMEOUT	1500
 
@@ -97,6 +100,49 @@
 #define	IGP3_VR_CTRL_MODE_SHUTDOWN		0x0200
 #define	IGP3_PM_CTRL_FORCE_PWR_DOWN		0x0020
 
+/* PHY Wakeup Registers and defines */
+#define	BM_RCTL		PHY_REG(BM_WUC_PAGE, 0)
+#define	BM_WUC		PHY_REG(BM_WUC_PAGE, 1)
+#define	BM_WUFC		PHY_REG(BM_WUC_PAGE, 2)
+#define	BM_WUS		PHY_REG(BM_WUC_PAGE, 3)
+#define	BM_RAR_L(_i)	(BM_PHY_REG(BM_WUC_PAGE, 16 + ((_i) << 2)))
+#define	BM_RAR_M(_i)    (BM_PHY_REG(BM_WUC_PAGE, 17 + ((_i) << 2)))
+#define	BM_RAR_H(_i)    (BM_PHY_REG(BM_WUC_PAGE, 18 + ((_i) << 2)))
+#define	BM_RAR_CTRL(_i) (BM_PHY_REG(BM_WUC_PAGE, 19 + ((_i) << 2)))
+#define	BM_MTA(_i)	(BM_PHY_REG(BM_WUC_PAGE, 128 + ((_i) << 1)))
+
+#define	BM_RCTL_UPE	0x0001	/* Unicast Promiscuous Mode */
+#define	BM_RCTL_MPE	0x0002	/* Multicast Promiscuous Mode */
+#define	BM_RCTL_MO_SHIFT	3	/* Multicast Offset Shift */
+#define	BM_RCTL_MO_MASK	(3 << 3)	/* Multicast Offset Mask */
+#define	BM_RCTL_BAM	0x0020	/* Broadcast Accept Mode */
+#define	BM_RCTL_PMCF	0x0040	/* Pass MAC Control Frames */
+#define	BM_RCTL_RFCE	0x0080	/* Rx Flow Control Enable */
+
+#define	HV_LED_CONFIG		PHY_REG(768, 30) /* LED Configuration */
+#define	HV_MUX_DATA_CTRL	PHY_REG(776, 16)
+#define	HV_MUX_DATA_CTRL_GEN_TO_MAC	0x0400
+#define	HV_MUX_DATA_CTRL_FORCE_SPEED	0x0004
+#define	HV_SCC_UPPER		PHY_REG(778, 16) /* Single Collision Count */
+#define	HV_SCC_LOWER		PHY_REG(778, 17)
+#define	HV_ECOL_UPPER		PHY_REG(778, 18) /* Excessive Collision Count */
+#define	HV_ECOL_LOWER		PHY_REG(778, 19)
+#define	HV_MCC_UPPER		PHY_REG(778, 20) /* Multiple Collision Count */
+#define	HV_MCC_LOWER		PHY_REG(778, 21)
+#define	HV_LATECOL_UPPER	PHY_REG(778, 23) /* Late Collision Count */
+#define	HV_LATECOL_LOWER	PHY_REG(778, 24)
+#define	HV_COLC_UPPER		PHY_REG(778, 25) /* Collision Count */
+#define	HV_COLC_LOWER		PHY_REG(778, 26)
+#define	HV_DC_UPPER		PHY_REG(778, 27) /* Defer Count */
+#define	HV_DC_LOWER		PHY_REG(778, 28)
+#define	HV_TNCRS_UPPER		PHY_REG(778, 29) /* Transmit with no CRS */
+#define	HV_TNCRS_LOWER		PHY_REG(778, 30)
+
+/* PCH Flow Control Refresh Timer Value */
+#define	E1000_FCRTV_PCH		0x05F40
+/* SW Semaphore flag timeout in milliseconds */
+#define	SW_FLAG_TIMEOUT		400
+
 /*
  * Additional interrupts need to be handled for ICH family:
  *  DSW = The FW changed the status of the DISSW bit in FWSM
--- a/usr/src/uts/common/io/e1000g/e1000_mac.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_mac.c	Mon Oct 05 09:17:26 2009 -0400
@@ -24,11 +24,12 @@
  */
 
 /*
- * IntelVersion: 1.92.4.1 sol_anvik_patch
+ * IntelVersion: 1.108 v3-1-3_2009-8-20
  */
 #include "e1000_api.h"
 
 static s32 e1000_validate_mdi_setting_generic(struct e1000_hw *hw);
+static void e1000_set_lan_id_multi_port_pcie(struct e1000_hw *hw);
 
 /*
  * e1000_init_mac_ops_generic - Initialize MAC function pointers
@@ -48,6 +49,7 @@
 	mac->ops.reset_hw = e1000_null_ops_generic;
 	mac->ops.setup_physical_interface = e1000_null_ops_generic;
 	mac->ops.get_bus_info = e1000_null_ops_generic;
+	mac->ops.set_lan_id = e1000_set_lan_id_multi_port_pcie;
 	mac->ops.read_mac_addr = e1000_read_mac_addr_generic;
 	mac->ops.config_collision_dist = e1000_config_collision_dist_generic;
 	mac->ops.clear_hw_cntrs = e1000_null_mac_generic;
@@ -128,10 +130,10 @@
  * @hw: pointer to the HW structure
  */
 void
-e1000_null_update_mc(struct e1000_hw *hw, u8 *h, u32 a, u32 b, u32 c)
+e1000_null_update_mc(struct e1000_hw *hw, u8 *h, u32 a)
 {
 	DEBUGFUNC("e1000_null_update_mc");
-	UNREFERENCED_5PARAMETER(hw, h, a, b, c);
+	UNREFERENCED_3PARAMETER(hw, h, a);
 }
 
 /*
@@ -178,10 +180,10 @@
 s32
 e1000_get_bus_info_pci_generic(struct e1000_hw *hw)
 {
+	struct e1000_mac_info *mac = &hw->mac;
 	struct e1000_bus_info *bus = &hw->bus;
 	u32 status = E1000_READ_REG(hw, E1000_STATUS);
 	s32 ret_val = E1000_SUCCESS;
-	u16 pci_header_type;
 
 	DEBUGFUNC("e1000_get_bus_info_pci_generic");
 
@@ -218,12 +220,7 @@
 	    : e1000_bus_width_32;
 
 	/* Which PCI(-X) function? */
-	e1000_read_pci_cfg(hw, PCI_HEADER_TYPE_REGISTER, &pci_header_type);
-	if (pci_header_type & PCI_HEADER_TYPE_MULTIFUNC)
-		bus->func = (status & E1000_STATUS_FUNC_MASK)
-		    >> E1000_STATUS_FUNC_SHIFT;
-	else
-		bus->func = 0;
+	mac->ops.set_lan_id(hw);
 
 	return (ret_val);
 }
@@ -239,10 +236,10 @@
 s32
 e1000_get_bus_info_pcie_generic(struct e1000_hw *hw)
 {
+	struct e1000_mac_info *mac = &hw->mac;
 	struct e1000_bus_info *bus = &hw->bus;
 	s32 ret_val;
-	u32 status;
-	u16 pcie_link_status, pci_header_type;
+	u16 pcie_link_status;
 
 	DEBUGFUNC("e1000_get_bus_info_pcie_generic");
 
@@ -259,6 +256,46 @@
 		    PCIE_LINK_WIDTH_MASK) >>
 		    PCIE_LINK_WIDTH_SHIFT);
 
+	mac->ops.set_lan_id(hw);
+
+	return (E1000_SUCCESS);
+}
+
+/*
+ * e1000_set_lan_id_multi_port_pcie - Set LAN id for PCIe multiple port devices
+ *
+ * @hw: pointer to the HW structure
+ *
+ * Determines the LAN function id by reading memory-mapped registers
+ * and swaps the port value if requested.
+ */
+static void
+e1000_set_lan_id_multi_port_pcie(struct e1000_hw *hw)
+{
+	struct e1000_bus_info *bus = &hw->bus;
+	u32 reg;
+
+	/*
+	 * The status register reports the correct function number
+	 * for the device regardless of function swap state.
+	 */
+	reg = E1000_READ_REG(hw, E1000_STATUS);
+	bus->func = (reg & E1000_STATUS_FUNC_MASK) >> E1000_STATUS_FUNC_SHIFT;
+}
+
+/*
+ * e1000_set_lan_id_multi_port_pci - Set LAN id for PCI multiple port devices
+ * @hw: pointer to the HW structure
+ *
+ * Determines the LAN function id by reading PCI config space.
+ */
+void
+e1000_set_lan_id_multi_port_pci(struct e1000_hw *hw)
+{
+	struct e1000_bus_info *bus = &hw->bus;
+	u16 pci_header_type;
+	u32 status;
+
 	e1000_read_pci_cfg(hw, PCI_HEADER_TYPE_REGISTER, &pci_header_type);
 	if (pci_header_type & PCI_HEADER_TYPE_MULTIFUNC) {
 		status = E1000_READ_REG(hw, E1000_STATUS);
@@ -267,8 +304,20 @@
 	} else {
 		bus->func = 0;
 	}
+}
 
-	return (E1000_SUCCESS);
+/*
+ * e1000_set_lan_id_single_port - Set LAN id for a single port device
+ * @hw: pointer to the HW structure
+ *
+ * Sets the LAN function id to zero for a single port device.
+ */
+void
+e1000_set_lan_id_single_port(struct e1000_hw *hw)
+{
+	struct e1000_bus_info *bus = &hw->bus;
+
+	bus->func = 0;
 }
 
 /*
@@ -322,6 +371,7 @@
 e1000_init_rx_addrs_generic(struct e1000_hw *hw, u16 rar_count)
 {
 	u32 i;
+	u8 mac_addr[ETH_ADDR_LEN] = {0};
 
 	DEBUGFUNC("e1000_init_rx_addrs_generic");
 
@@ -332,12 +382,9 @@
 
 	/* Zero out the other (rar_entry_count - 1) receive addresses */
 	DEBUGOUT1("Clearing RAR[1-%u]\n", rar_count - 1);
-	for (i = 1; i < rar_count; i++) {
-		E1000_WRITE_REG_ARRAY(hw, E1000_RA, (i << 1), 0);
-		E1000_WRITE_FLUSH(hw);
-		E1000_WRITE_REG_ARRAY(hw, E1000_RA, ((i << 1) + 1), 0);
-		E1000_WRITE_FLUSH(hw);
-	}
+	for (i = 1; i < rar_count; i++)
+		hw->mac.ops.rar_set(hw, mac_addr, i);
+
 }
 
 /*
@@ -347,9 +394,10 @@
  * Checks the nvm for an alternate MAC address.  An alternate MAC address
  * can be setup by pre-boot software and must be treated like a permanent
  * address and must override the actual permanent MAC address.  If an
- * alternate MAC address is found it is saved in the hw struct and
- * programmed into RAR0 and the function returns success, otherwise the
- * function returns an error.
+ * alternate MAC address is found it is programmed into RAR0, replacing
+ * the permanent address that was installed into RAR0 by the Si on reset.
+ * This function will return SUCCESS unless it encounters an error while
+ * reading the EEPROM.
  */
 s32
 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw)
@@ -369,12 +417,12 @@
 	}
 
 	if (nvm_alt_mac_addr_offset == 0xFFFF) {
-		ret_val = -(E1000_NOT_IMPLEMENTED);
+		/* There is no Alternate MAC Address */
 		goto out;
 	}
 
 	if (hw->bus.func == E1000_FUNC_1)
-		nvm_alt_mac_addr_offset += ETH_ADDR_LEN / sizeof (u16);
+		nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN1;
 
 	for (i = 0; i < ETH_ADDR_LEN; i += 2) {
 		offset = nvm_alt_mac_addr_offset + (i >> 1);
@@ -390,14 +438,16 @@
 
 	/* if multicast bit is set, the alternate address will not be used */
 	if (alt_mac_addr[0] & 0x01) {
-		ret_val = -(E1000_NOT_IMPLEMENTED);
+		DEBUGOUT("Ignoring Alternate Mac Address with MC bit set\n");
 		goto out;
 	}
 
-	for (i = 0; i < ETH_ADDR_LEN; i++)
-		hw->mac.addr[i] = hw->mac.perm_addr[i] = alt_mac_addr[i];
-
-	hw->mac.ops.rar_set(hw, hw->mac.perm_addr, 0);
+	/*
+	 * We have a valid alternate MAC address, and we want to treat it the
+	 * same as the normal permanent MAC address stored by the HW into the
+	 * RAR. Do this by mapping this address into RAR0.
+	 */
+	hw->mac.ops.rar_set(hw, alt_mac_addr, 0);
 
 out:
 	return (ret_val);
@@ -435,12 +485,13 @@
 
 	/*
 	 * Some bridges will combine consecutive 32-bit writes into
-	 * a single burst write, which will malfunction on some
-	 * 82546 parts.  The flush avoids this.
+	 * a single burst write, which will malfunction on some parts.
+	 * The flushes avoid this.
 	 */
 	E1000_WRITE_REG(hw, E1000_RAL(index), rar_low);
 	E1000_WRITE_FLUSH(hw);
 	E1000_WRITE_REG(hw, E1000_RAH(index), rar_high);
+	E1000_WRITE_FLUSH(hw);
 }
 
 /*
@@ -485,56 +536,37 @@
  * @hw: pointer to the HW structure
  * @mc_addr_list: array of multicast addresses to program
  * @mc_addr_count: number of multicast addresses to program
- * @rar_used_count: the first RAR register free to program
- * @rar_count: total number of supported Receive Address Registers
  *
- * Updates the Receive Address Registers and Multicast Table Array.
+ * Updates entire Multicast Table Array.
  * The caller must have a packed mc_addr_list of multicast addresses.
- * The parameter rar_count will usually be hw->mac.rar_entry_count
- * unless there are workarounds that change this.
  */
 void
 e1000_update_mc_addr_list_generic(struct e1000_hw *hw,
-    u8 *mc_addr_list, u32 mc_addr_count,
-    u32 rar_used_count, u32 rar_count)
+    u8 *mc_addr_list, u32 mc_addr_count)
 {
-	u32 hash_value;
-	u32 i;
+	u32 hash_value, hash_bit, hash_reg;
+	int i;
 
 	DEBUGFUNC("e1000_update_mc_addr_list_generic");
 
-	/*
-	 * Load the first set of multicast addresses into the exact filters
-	 * (RAR).  If there are not enough to fill the RAR array, clear the
-	 * filters.
-	 */
-	for (i = rar_used_count; i < rar_count; i++) {
-		if (mc_addr_count) {
-			hw->mac.ops.rar_set(hw, mc_addr_list, i);
-			mc_addr_count--;
-			mc_addr_list += ETH_ADDR_LEN;
-		} else {
-			E1000_WRITE_REG_ARRAY(hw, E1000_RA, i << 1, 0);
-			E1000_WRITE_FLUSH(hw);
-			E1000_WRITE_REG_ARRAY(hw, E1000_RA, (i << 1) + 1, 0);
-			E1000_WRITE_FLUSH(hw);
-		}
+	/* clear mta_shadow */
+	memset(&hw->mac.mta_shadow, 0, sizeof (hw->mac.mta_shadow));
+
+	/* update mta_shadow from mc_addr_list */
+	for (i = 0; (u32) i < mc_addr_count; i++) {
+		hash_value = e1000_hash_mc_addr_generic(hw, mc_addr_list);
+
+		hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1);
+		hash_bit = hash_value & 0x1F;
+
+		hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit);
+		mc_addr_list += (ETH_ADDR_LEN);
 	}
 
-	/* Clear the old settings from the MTA */
-	DEBUGOUT("Clearing MTA\n");
-	for (i = 0; i < hw->mac.mta_reg_count; i++) {
-		E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0);
-		E1000_WRITE_FLUSH(hw);
-	}
-
-	/* Load any remaining multicast addresses into the hash table. */
-	for (; mc_addr_count > 0; mc_addr_count--) {
-		hash_value = e1000_hash_mc_addr_generic(hw, mc_addr_list);
-		DEBUGOUT1("Hash value = 0x%03X\n", hash_value);
-		hw->mac.ops.mta_set(hw, hash_value);
-		mc_addr_list += ETH_ADDR_LEN;
-	}
+	/* replace the entire MTA table */
+	for (i = hw->mac.mta_reg_count - 1; i >= 0; i--)
+		E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, hw->mac.mta_shadow[i]);
+	E1000_WRITE_FLUSH(hw);
 }
 
 /*
--- a/usr/src/uts/common/io/e1000g/e1000_mac.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_mac.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.29 sol_anvik_patch
+ * IntelVersion: 1.32 v3-1-3_2009-8-20
  */
 #ifndef _E1000_MAC_H_
 #define	_E1000_MAC_H_
@@ -42,7 +42,7 @@
 s32 e1000_null_ops_generic(struct e1000_hw *hw);
 s32 e1000_null_link_info(struct e1000_hw *hw, u16 *s, u16 *d);
 bool e1000_null_mng_mode(struct e1000_hw *hw);
-void e1000_null_update_mc(struct e1000_hw *hw, u8 *h, u32 a, u32 b, u32 c);
+void e1000_null_update_mc(struct e1000_hw *hw, u8 *h, u32 a);
 void e1000_null_write_vfta(struct e1000_hw *hw, u32 a, u32 b);
 void e1000_null_mta_set(struct e1000_hw *hw, u32 a);
 void e1000_null_rar_set(struct e1000_hw *hw, u8 *h, u32 a);
@@ -59,6 +59,8 @@
 s32 e1000_get_auto_rd_done_generic(struct e1000_hw *hw);
 s32 e1000_get_bus_info_pci_generic(struct e1000_hw *hw);
 s32 e1000_get_bus_info_pcie_generic(struct e1000_hw *hw);
+void e1000_set_lan_id_single_port(struct e1000_hw *hw);
+void e1000_set_lan_id_multi_port_pci(struct e1000_hw *hw);
 s32 e1000_get_hw_semaphore_generic(struct e1000_hw *hw);
 s32 e1000_get_speed_and_duplex_copper_generic(struct e1000_hw *hw, u16 *speed,
     u16 *duplex);
@@ -68,8 +70,7 @@
 s32 e1000_led_on_generic(struct e1000_hw *hw);
 s32 e1000_led_off_generic(struct e1000_hw *hw);
 void e1000_update_mc_addr_list_generic(struct e1000_hw *hw,
-    u8 *mc_addr_list, u32 mc_addr_count,
-    u32 rar_used_count, u32 rar_count);
+    u8 *mc_addr_list, u32 mc_addr_count);
 s32 e1000_set_default_fc_generic(struct e1000_hw *hw);
 s32 e1000_set_fc_watermarks_generic(struct e1000_hw *hw);
 s32 e1000_setup_fiber_serdes_link_generic(struct e1000_hw *hw);
--- a/usr/src/uts/common/io/e1000g/e1000_manage.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_manage.c	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.27 sol_anvik_patch
+ * IntelVersion: 1.27 v3-1-3_2009-8-20
  */
 
 #include "e1000_api.h"
--- a/usr/src/uts/common/io/e1000g/e1000_manage.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_manage.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.18 sol_anvik_patch
+ * IntelVersion: 1.18 v3-1-3_2009-8-20
  */
 #ifndef _E1000_MANAGE_H_
 #define	_E1000_MANAGE_H_
--- a/usr/src/uts/common/io/e1000g/e1000_nvm.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_nvm.c	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.46 sol_anvik_patch
+ * IntelVersion: 1.49 v3-1-3_2009-8-20
  */
 #include "e1000_api.h"
 
@@ -831,31 +831,23 @@
 s32
 e1000_read_mac_addr_generic(struct e1000_hw *hw)
 {
-	s32 ret_val = E1000_SUCCESS;
-	u16 offset, nvm_data, i;
-
-	DEBUGFUNC("e1000_read_mac_addr");
+	u32 rar_high;
+	u32 rar_low;
+	u16 i;
 
-	for (i = 0; i < ETH_ADDR_LEN; i += 2) {
-		offset = i >> 1;
-		ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data);
-		if (ret_val) {
-			DEBUGOUT("NVM Read Error\n");
-			goto out;
-		}
-		hw->mac.perm_addr[i] = (u8)(nvm_data & 0xFF);
-		hw->mac.perm_addr[i + 1] = (u8)(nvm_data >> 8);
-	}
+	rar_high = E1000_READ_REG(hw, E1000_RAH(0));
+	rar_low = E1000_READ_REG(hw, E1000_RAL(0));
 
-	/* Flip last bit of mac address if we're on second port */
-	if (hw->bus.func == E1000_FUNC_1)
-		hw->mac.perm_addr[5] ^= 1;
+	for (i = 0; i < E1000_RAL_MAC_ADDR_LEN; i++)
+		hw->mac.perm_addr[i] = (u8)(rar_low >> (i*8));
+
+	for (i = 0; i < E1000_RAH_MAC_ADDR_LEN; i++)
+		hw->mac.perm_addr[i+4] = (u8)(rar_high >> (i*8));
 
 	for (i = 0; i < ETH_ADDR_LEN; i++)
 		hw->mac.addr[i] = hw->mac.perm_addr[i];
 
-out:
-	return (ret_val);
+	return (E1000_SUCCESS);
 }
 
 /*
--- a/usr/src/uts/common/io/e1000g/e1000_nvm.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_nvm.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.18 sol_anvik_patch
+ * IntelVersion: 1.18 v3-1-3_2009-8-20
  */
 #ifndef _E1000_NVM_H_
 #define	_E1000_NVM_H_
--- a/usr/src/uts/common/io/e1000g/e1000_osdep.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_osdep.c	Mon Oct 05 09:17:26 2009 -0400
@@ -58,7 +58,7 @@
 /*
  * phy_spd_state - set smart-power-down (SPD) state
  *
- * This only acts on the 82541/47 family and the 82571/72 family.
+ * This only acts on the silicon families that have the SPD feature.
  * For any others, return without doing anything.
  */
 void
@@ -78,6 +78,9 @@
 		break;
 	case e1000_82571:
 	case e1000_82572:
+	case e1000_82573:
+	case e1000_82574:
+	case e1000_82583:
 		offset = IGP02E1000_PHY_POWER_MGMT;
 		spd_bit = IGP02E1000_PM_SPD;
 		break;
@@ -112,21 +115,27 @@
 }
 
 /*
- * Enables PCI-Express master access.
- *
- * hw: Struct containing variables accessed by shared code
- *
- * returns: - none.
+ * For some hardware types, access to NVM & PHY need to be serialized by mutex.
+ * The necessary mutexes will have been created by shared code.  Here we destroy
+ * that mutexes for just the hardware types that need it.
  */
 void
-e1000_enable_pciex_master(struct e1000_hw *hw)
+e1000_destroy_hw_mutex(struct e1000_hw *hw)
 {
-	uint32_t ctrl;
+	struct e1000_dev_spec_ich8lan *dev_spec;
 
-	if (hw->bus.type != e1000_bus_type_pci_express)
-		return;
+	switch (hw->mac.type) {
+	case e1000_ich8lan:
+	case e1000_ich9lan:
+	case e1000_ich10lan:
+	case e1000_pchlan:
+		cmn_err(CE_WARN, "destroy mutexes in e1000_destroy_hw_mutex\n");
+		dev_spec = &hw->dev_spec.ich8lan;
+		E1000_MUTEX_DESTROY(&dev_spec->nvm_mutex);
+		E1000_MUTEX_DESTROY(&dev_spec->swflag_mutex);
+		break;
 
-	ctrl = E1000_READ_REG(hw, E1000_CTRL);
-	ctrl &= ~E1000_CTRL_GIO_MASTER_DISABLE;
-	E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+	default:
+		break;	/* no action */
+	}
 }
--- a/usr/src/uts/common/io/e1000g/e1000_osdep.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_osdep.h	Mon Oct 05 09:17:26 2009 -0400
@@ -46,6 +46,7 @@
 #include <sys/pci.h>
 #include <sys/atomic.h>
 #include <sys/note.h>
+#include <sys/mutex.h>
 #include "e1000g_debug.h"
 
 #define	usec_delay(x)		drv_usecwait(x)
@@ -75,6 +76,7 @@
 
 #define	false		0
 #define	true		1
+
 #define	CMD_MEM_WRT_INVALIDATE	0x0010	/* BIT_4 */
 #define	PCI_COMMAND_REGISTER	0x04
 #define	PCI_EX_CONF_CAP		0xE0
@@ -193,6 +195,16 @@
 	struct e1000g *adapter;
 };
 
+/* Shared Code Mutex Defines */
+#define	E1000_MUTEX			kmutex_t
+#define	E1000_MUTEX_INIT(mutex)		mutex_init(mutex, NULL, \
+	MUTEX_DRIVER, NULL)
+#define	E1000_MUTEX_DESTROY(mutex)	mutex_destroy(mutex)
+
+#define	E1000_MUTEX_LOCK(mutex)		mutex_enter(mutex)
+#define	E1000_MUTEX_TRYLOCK(mutex)	mutex_tryenter(mutex)
+#define	E1000_MUTEX_UNLOCK(mutex)	mutex_exit(mutex)
+
 #ifdef __sparc	/* on SPARC, use only memory-mapped routines */
 #define	E1000_WRITE_REG_IO	E1000_WRITE_REG
 #else	/* on x86, use port io routines */
--- a/usr/src/uts/common/io/e1000g/e1000_phy.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_phy.c	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.112 sol_anvik_patch
+ * IntelVersion: 1.143 v3-1-3_2009-8-20
  */
 #include "e1000_api.h"
 
@@ -32,6 +32,10 @@
 static s32 e1000_access_phy_wakeup_reg_bm(struct e1000_hw *hw, u32 offset,
     u16 *data, bool read);
 
+static u32 e1000_get_phy_addr_for_hv_page(u32 page);
+static s32 e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
+    u16 *data, bool read);
+
 /* Cable length tables */
 static const u16 e1000_m88_cable_length_table[] =
 	{0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED};
@@ -168,26 +172,47 @@
 	struct e1000_phy_info *phy = &hw->phy;
 	s32 ret_val = E1000_SUCCESS;
 	u16 phy_id;
+	u16 retry_count = 0;
 
 	DEBUGFUNC("e1000_get_phy_id");
 
 	if (!(phy->ops.read_reg))
 		goto out;
 
-	ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id);
-	if (ret_val)
-		goto out;
-
-	phy->id = (u32)(phy_id << 16);
-	usec_delay(20);
-	ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id);
-	if (ret_val)
-		goto out;
-
-	phy->id |= (u32)(phy_id & PHY_REVISION_MASK);
-	phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK);
-
+	while (retry_count < 2) {
+		ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id);
+		if (ret_val)
+			goto out;
+
+		phy->id = (u32)(phy_id << 16);
+		usec_delay(20);
+		ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id);
+		if (ret_val)
+			goto out;
+
+		phy->id |= (u32)(phy_id & PHY_REVISION_MASK);
+		phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK);
+
+		if (phy->id != 0 && phy->id != PHY_REVISION_MASK)
+			goto out;
+
+		/*
+		 * If the PHY ID is still unknown, we may have an 82577 without
+		 * link. We will try again after setting Slow MDIC mode. No
+		 * harm in trying again in this case since the PHY ID is
+		 * unknown at this point anyway
+		 */
+		ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
+		if (ret_val)
+			goto out;
+
+		retry_count++;
+	}
 out:
+	/* Revert to MDIO fast mode, if applicable */
+	if (retry_count)
+		ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
+
 	return (ret_val);
 }
 
@@ -558,6 +583,51 @@
 }
 
 /*
+ * e1000_copper_link_setup_82577 - Setup 82577 PHY for copper link
+ * @hw: pointer to the HW structure
+ *
+ * Sets up Carrier-sense on Transmit and downshift values.
+ */
+s32
+e1000_copper_link_setup_82577(struct e1000_hw *hw)
+{
+	struct e1000_phy_info *phy = &hw->phy;
+	s32 ret_val;
+	u16 phy_data;
+
+	DEBUGFUNC("e1000_copper_link_setup_82577");
+
+	if (phy->reset_disable) {
+		ret_val = E1000_SUCCESS;
+		goto out;
+	}
+
+	/* Enable CRS on TX. This must be set for half-duplex operation. */
+	ret_val = phy->ops.read_reg(hw, I82577_CFG_REG, &phy_data);
+	if (ret_val)
+		goto out;
+
+	phy_data |= I82577_CFG_ASSERT_CRS_ON_TX;
+
+	/* Enable downshift */
+	phy_data |= I82577_CFG_ENABLE_DOWNSHIFT;
+
+	ret_val = phy->ops.write_reg(hw, I82577_CFG_REG, phy_data);
+	if (ret_val)
+		goto out;
+
+	/* Set number of link attempts before downshift */
+	ret_val = phy->ops.read_reg(hw, I82577_CTRL_REG, &phy_data);
+	if (ret_val)
+		goto out;
+	phy_data &= ~I82577_CTRL_DOWNSHIFT_MASK;
+	ret_val = phy->ops.write_reg(hw, I82577_CTRL_REG, phy_data);
+
+out:
+	return (ret_val);
+}
+
+/*
  * e1000_copper_link_setup_m88 - Setup m88 PHY's for copper link
  * @hw: pointer to the HW structure
  *
@@ -583,8 +653,8 @@
 	if (ret_val)
 		goto out;
 
-	/* For newer PHYs this bit is downshift enable */
-	if (phy->type == e1000_phy_m88)
+	/* For BM PHY this bit is downshift enable */
+	if (phy->type != e1000_phy_bm)
 		phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX;
 
 	/*
@@ -685,6 +755,21 @@
 		goto out;
 	}
 
+	if (phy->type == e1000_phy_82578) {
+		ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
+		    &phy_data);
+		if (ret_val)
+			goto out;
+
+		/* 82578 PHY - set the downshift count to 1x. */
+		phy_data |= I82578_EPSCR_DOWNSHIFT_ENABLE;
+		phy_data &= ~I82578_EPSCR_DOWNSHIFT_COUNTER_MASK;
+		ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL,
+		    phy_data);
+		if (ret_val)
+			goto out;
+}
+
 out:
 	return (ret_val);
 }
@@ -1315,6 +1400,77 @@
 }
 
 /*
+ * e1000_phy_force_speed_duplex_ife - Force PHY speed & duplex
+ * @hw: pointer to the HW structure
+ *
+ * Forces the speed and duplex settings of the PHY.
+ * This is a function pointer entry point only called by
+ * PHY setup routines.
+ */
+s32
+e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw)
+{
+	struct e1000_phy_info *phy = &hw->phy;
+	s32 ret_val;
+	u16 data;
+	bool link;
+
+	DEBUGFUNC("e1000_phy_force_speed_duplex_ife");
+
+	if (phy->type != e1000_phy_ife) {
+		ret_val = e1000_phy_force_speed_duplex_igp(hw);
+		goto out;
+	}
+
+	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &data);
+	if (ret_val)
+		goto out;
+
+	e1000_phy_force_speed_duplex_setup(hw, &data);
+
+	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, data);
+	if (ret_val)
+		goto out;
+
+	/* Disable MDI-X support for 10/100 */
+	ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data);
+	if (ret_val)
+		goto out;
+
+	data &= ~IFE_PMC_AUTO_MDIX;
+	data &= ~IFE_PMC_FORCE_MDIX;
+
+	ret_val = phy->ops.write_reg(hw, IFE_PHY_MDIX_CONTROL, data);
+	if (ret_val)
+		goto out;
+
+	DEBUGOUT1("IFE PMC: %X\n", data);
+
+	usec_delay(1);
+
+	if (phy->autoneg_wait_to_complete) {
+		DEBUGOUT("Waiting for forced speed/duplex link on IFE phy.\n");
+
+		ret_val = e1000_phy_has_link_generic(hw,
+		    PHY_FORCE_LIMIT, 100000, &link);
+		if (ret_val)
+			goto out;
+
+		if (!link)
+			DEBUGOUT("Link taking longer than expected.\n");
+
+		/* Try once more */
+		ret_val = e1000_phy_has_link_generic(hw,
+		    PHY_FORCE_LIMIT, 100000, &link);
+		if (ret_val)
+			goto out;
+	}
+
+out:
+	return (ret_val);
+}
+
+/*
  * e1000_phy_force_speed_duplex_setup - Configure forced PHY speed/duplex
  * @hw: pointer to the HW structure
  * @phy_ctrl: pointer to current value of PHY_CONTROL
@@ -1367,8 +1523,7 @@
 		DEBUGOUT("Forcing 100mb\n");
 	} else {
 		ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100);
-		/* OR 0x0000 constant for documenting intent */
-		/* LINTED E_EXPR_NULL_EFFECT */
+		/* LINTED */
 		*phy_ctrl |= MII_CR_SPEED_10;
 		*phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100);
 		DEBUGOUT("Forcing 10mb\n");
@@ -1497,6 +1652,7 @@
 	case e1000_phy_m88:
 	case e1000_phy_gg82563:
 	case e1000_phy_bm:
+	case e1000_phy_82578:
 		offset = M88E1000_PHY_SPEC_STATUS;
 		mask = M88E1000_PSSR_DOWNSHIFT;
 		break;
@@ -1599,6 +1755,41 @@
 }
 
 /*
+ * e1000_check_polarity_ife - Check cable polarity for IFE PHY
+ * @hw: pointer to the HW structure
+ *
+ * Polarity is determined on the polarity reversal feature being enabled.
+ */
+s32
+e1000_check_polarity_ife(struct e1000_hw *hw)
+{
+	struct e1000_phy_info *phy = &hw->phy;
+	s32 ret_val;
+	u16 phy_data, offset, mask;
+
+	DEBUGFUNC("e1000_check_polarity_ife");
+
+	/*
+	 * Polarity is determined based on the reversal feature being enabled.
+	 */
+	if (phy->polarity_correction) {
+		offset = IFE_PHY_EXTENDED_STATUS_CONTROL;
+		mask = IFE_PESC_POLARITY_REVERSED;
+	} else {
+		offset = IFE_PHY_SPECIAL_CONTROL;
+		mask = IFE_PSC_FORCE_POLARITY;
+	}
+
+	ret_val = phy->ops.read_reg(hw, offset, &phy_data);
+
+	if (!ret_val)
+		phy->cable_polarity = (phy_data & mask)
+		    ? e1000_rev_polarity_reversed : e1000_rev_polarity_normal;
+
+	return (ret_val);
+}
+
+/*
  * e1000_wait_autoneg_generic - Wait for auto-neg completion
  * @hw: pointer to the HW structure
  *
@@ -1664,9 +1855,16 @@
 		 * the board.
 		 */
 		ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
-		if (ret_val)
-			break;
-		ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
+		if (ret_val) {
+			/*
+			 * If the first read fails, another entity may have
+			 * ownership of the resources, wait and try again to
+			 * see if they have relinquished the resources yet.
+			 */
+			usec_delay(usec_interval);
+		}
+		ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS,
+		    &phy_status);
 		if (ret_val)
 			break;
 		if (phy_status & MII_SR_LINK_STATUS)
@@ -1713,15 +1911,14 @@
 	index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
 	    M88E1000_PSSR_CABLE_LENGTH_SHIFT;
 
-	if (index < M88E1000_CABLE_LENGTH_TABLE_SIZE + 1) {
-		phy->min_cable_length = e1000_m88_cable_length_table[index];
-		phy->max_cable_length = e1000_m88_cable_length_table[index + 1];
-
-		phy->cable_length = (phy->min_cable_length +
-		    phy->max_cable_length) / 2;
-	} else {
+	if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) {
 		ret_val = E1000_ERR_PHY;
+		goto out;
 	}
+	phy->min_cable_length = e1000_m88_cable_length_table[index];
+	phy->max_cable_length = e1000_m88_cable_length_table[index + 1];
+
+	phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2;
 
 out:
 	return (ret_val);
@@ -2171,6 +2368,12 @@
 	case BME1000_E_PHY_ID_R2:
 		phy_type = e1000_phy_bm;
 		break;
+	case I82578_E_PHY_ID:
+		phy_type = e1000_phy_82578;
+		break;
+	case I82577_E_PHY_ID:
+		phy_type = e1000_phy_82577;
+		break;
 	default:
 		phy_type = e1000_phy_unknown;
 		break;
@@ -2194,6 +2397,8 @@
 	u32 i;
 	enum e1000_phy_type phy_type = e1000_phy_unknown;
 
+	hw->phy.id = phy_type;
+
 	for (phy_addr = 0; phy_addr < E1000_MAX_PHY_ADDR; phy_addr++) {
 		hw->phy.addr = phy_addr;
 		i = 0;
@@ -2487,11 +2692,17 @@
     u32 offset, u16 *data, bool read)
 {
 	s32 ret_val;
-	u16 reg = ((u16)offset);
+	u16 reg = BM_PHY_REG_NUM(offset);
 	u16 phy_reg = 0;
 	u8 phy_acquired = 1;
 
-	DEBUGFUNC("e1000_read_phy_wakeup_reg_bm");
+	DEBUGFUNC("e1000_access_phy_wakeup_reg_bm");
+
+	/* Gig must be disabled for MDIO accesses to page 800 */
+	if ((hw->mac.type == e1000_pchlan) &&
+	    (!(E1000_READ_REG(hw, E1000_PHY_CTRL) &
+	    E1000_PHY_CTRL_GBE_DISABLE)))
+		DEBUGOUT("Attempting to access page 800 while gig enabled.\n");
 
 	ret_val = hw->phy.ops.acquire(hw);
 	if (ret_val) {
@@ -2546,13 +2757,13 @@
 		ret_val = e1000_read_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE,
 		    data);
 	} else {
-		/* Read the page 800 value using opcode 0x12 */
+		/* Write the page 800 value using opcode 0x12 */
 		ret_val = e1000_write_phy_reg_mdic(hw, BM_WUC_DATA_OPCODE,
 		    *data);
 	}
 
 	if (ret_val) {
-		DEBUGOUT("Could not read data value from page 800\n");
+		DEBUGOUT("Could not access data value from page 800\n");
 		goto out;
 	}
 
@@ -2613,3 +2824,542 @@
 	hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
 	msec_delay(1);
 }
+
+s32
+e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw, bool slow)
+{
+	s32 ret_val = E1000_SUCCESS;
+	u16 data = 0;
+
+	ret_val = hw->phy.ops.acquire(hw);
+	if (ret_val)
+		return (ret_val);
+
+	/* Set MDIO mode - page 769, register 16: 0x2580==slow, 0x2180==fast */
+	hw->phy.addr = 1;
+	ret_val = e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
+	    (BM_PORT_CTRL_PAGE << IGP_PAGE_SHIFT));
+	if (ret_val)
+		goto out;
+
+	ret_val = e1000_write_phy_reg_mdic(hw, BM_CS_CTRL1,
+	    (0x2180 | (slow << 10)));
+
+	if (ret_val)
+		goto out;
+
+	/* dummy read when reverting to fast mode - throw away result */
+	if (!slow)
+		ret_val = e1000_read_phy_reg_mdic(hw, BM_CS_CTRL1, &data);
+
+out:
+	hw->phy.ops.release(hw);
+	return (ret_val);
+}
+
+/*
+ * e1000_read_phy_reg_hv -  Read HV PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Acquires semaphore, if necessary, then reads the PHY register at offset
+ * and storing the retrieved information in data.  Release any acquired
+ * semaphore before exiting.
+ */
+s32
+e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+	s32 ret_val;
+	u16 page = BM_PHY_REG_PAGE(offset);
+	u16 reg = BM_PHY_REG_NUM(offset);
+	bool in_slow_mode = false;
+
+	DEBUGFUNC("e1000_read_phy_reg_hv");
+
+	/* Workaround failure in MDIO access while cable is disconnected */
+	if ((hw->phy.type == e1000_phy_82577) &&
+	    !(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)) {
+		ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
+		if (ret_val)
+			goto out;
+
+		in_slow_mode = true;
+	}
+
+	/* Page 800 works differently than the rest so it has its own func */
+	if (page == BM_WUC_PAGE) {
+		ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset,
+		    data, true);
+		goto out;
+	}
+
+	if (page > 0 && page < HV_INTC_FC_PAGE_START) {
+		ret_val = e1000_access_phy_debug_regs_hv(hw, offset,
+		    data, true);
+		goto out;
+	}
+
+	ret_val = hw->phy.ops.acquire(hw);
+	if (ret_val)
+		goto out;
+
+	hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
+
+	if (page == HV_INTC_FC_PAGE_START)
+		page = 0;
+
+	if (reg > MAX_PHY_MULTI_PAGE_REG) {
+		u32 phy_addr = hw->phy.addr;
+		hw->phy.addr = 1;
+
+		/* Page is shifted left, PHY expects (page x 32) */
+		ret_val = e1000_write_phy_reg_mdic(hw,
+		    IGP01E1000_PHY_PAGE_SELECT,
+		    (page << IGP_PAGE_SHIFT));
+		hw->phy.addr = phy_addr;
+
+		if (ret_val) {
+			hw->phy.ops.release(hw);
+			goto out;
+		}
+	}
+
+	ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
+	    data);
+	hw->phy.ops.release(hw);
+
+out:
+	/* Revert to MDIO fast mode, if applicable */
+	if ((hw->phy.type == e1000_phy_82577) && in_slow_mode)
+		ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
+
+	return (ret_val);
+}
+
+/*
+ * e1000_write_phy_reg_hv - Write HV PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Acquires semaphore, if necessary, then writes the data to PHY register
+ * at the offset.  Release any acquired semaphores before exiting.
+ */
+s32
+e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data)
+{
+	s32 ret_val;
+	u16 page = BM_PHY_REG_PAGE(offset);
+	u16 reg = BM_PHY_REG_NUM(offset);
+	bool in_slow_mode = false;
+
+	DEBUGFUNC("e1000_write_phy_reg_hv");
+
+	/* Workaround failure in MDIO access while cable is disconnected */
+	if ((hw->phy.type == e1000_phy_82577) &&
+	    !(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)) {
+		ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
+		if (ret_val)
+			goto out;
+
+		in_slow_mode = true;
+	}
+
+	/* Page 800 works differently than the rest so it has its own func */
+	if (page == BM_WUC_PAGE) {
+		ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset,
+		    &data, false);
+		goto out;
+	}
+
+	if (page > 0 && page < HV_INTC_FC_PAGE_START) {
+		ret_val = e1000_access_phy_debug_regs_hv(hw, offset,
+		    &data, false);
+		goto out;
+	}
+
+	ret_val = hw->phy.ops.acquire(hw);
+	if (ret_val)
+		goto out;
+
+	hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
+
+	if (page == HV_INTC_FC_PAGE_START)
+		page = 0;
+
+	/*
+	 * Workaround MDIO accesses being disabled after entering IEEE Power
+	 * Down (whenever bit 11 of the PHY Control register is set)
+	 */
+	if ((hw->phy.type == e1000_phy_82578) &&
+	    (hw->phy.revision >= 1) &&
+	    (hw->phy.addr == 2) &&
+	    ((MAX_PHY_REG_ADDRESS & reg) == 0) &&
+	    (data & (1 << 11))) {
+		u16 data2 = 0x7EFF;
+		hw->phy.ops.release(hw);
+		ret_val = e1000_access_phy_debug_regs_hv(hw, (1 << 6) | 0x3,
+		    &data2, false);
+		if (ret_val)
+			goto out;
+
+		ret_val = hw->phy.ops.acquire(hw);
+		if (ret_val)
+			goto out;
+	}
+
+	if (reg > MAX_PHY_MULTI_PAGE_REG) {
+		u32 phy_addr = hw->phy.addr;
+		hw->phy.addr = 1;
+
+		/* Page is shifted left, PHY expects (page x 32) */
+		ret_val = e1000_write_phy_reg_mdic(hw,
+		    IGP01E1000_PHY_PAGE_SELECT,
+		    (page << IGP_PAGE_SHIFT));
+		hw->phy.addr = phy_addr;
+
+		if (ret_val) {
+			hw->phy.ops.release(hw);
+			goto out;
+		}
+	}
+
+	ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
+	    data);
+	hw->phy.ops.release(hw);
+
+out:
+	/* Revert to MDIO fast mode, if applicable */
+	if ((hw->phy.type == e1000_phy_82577) && in_slow_mode)
+		ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
+
+	return (ret_val);
+}
+
+/*
+ * e1000_get_phy_addr_for_hv_page - Get PHY adrress based on page
+ * @page: page to be accessed
+ */
+static u32
+e1000_get_phy_addr_for_hv_page(u32 page)
+{
+	u32 phy_addr = 2;
+
+	if (page >= HV_INTC_FC_PAGE_START)
+		phy_addr = 1;
+
+	return (phy_addr);
+}
+
+/*
+ * e1000_access_phy_debug_regs_hv - Read HV PHY vendor specific high registers
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read or written
+ * @data: pointer to the data to be read or written
+ * @read: determines if operation is read or written
+ *
+ * Acquires semaphore, if necessary, then reads the PHY register at offset
+ * and storing the retreived information in data.  Release any acquired
+ * semaphores before exiting.  Note that the procedure to read these regs
+ * uses the address port and data port to read/write.
+ */
+static s32
+e1000_access_phy_debug_regs_hv(struct e1000_hw *hw, u32 offset,
+    u16 *data, bool read)
+{
+	s32 ret_val;
+	u32 addr_reg = 0;
+	u32 data_reg = 0;
+	u8  phy_acquired = 1;
+
+	DEBUGFUNC("e1000_access_phy_debug_regs_hv");
+
+	/* This takes care of the difference with desktop vs mobile phy */
+	addr_reg = (hw->phy.type == e1000_phy_82578) ?
+	    I82578_ADDR_REG : I82577_ADDR_REG;
+	data_reg = addr_reg + 1;
+
+	ret_val = hw->phy.ops.acquire(hw);
+	if (ret_val) {
+		DEBUGOUT("Could not acquire PHY\n");
+		phy_acquired = 0;
+		goto out;
+	}
+
+	/* All operations in this function are phy address 2 */
+	hw->phy.addr = 2;
+
+	/* masking with 0x3F to remove the page from offset */
+	ret_val = e1000_write_phy_reg_mdic(hw, addr_reg, (u16)offset & 0x3F);
+	if (ret_val) {
+		DEBUGOUT("Could not write PHY the HV address register\n");
+		goto out;
+	}
+
+	/* Read or write the data value next */
+	if (read)
+		ret_val = e1000_read_phy_reg_mdic(hw, data_reg, data);
+	else
+		ret_val = e1000_write_phy_reg_mdic(hw, data_reg, *data);
+
+	if (ret_val) {
+		DEBUGOUT("Could not read data value from HV data register\n");
+		goto out;
+	}
+
+out:
+	if (phy_acquired == 1)
+		hw->phy.ops.release(hw);
+	return (ret_val);
+}
+
+/*
+ * e1000_link_stall_workaround_hv - Si workaround
+ * @hw: pointer to the HW structure
+ *
+ * This function works around a Si bug where the link partner can get
+ * a link up indication before the PHY does.  If small packets are sent
+ * by the link partner they can be placed in the packet buffer without
+ * being properly accounted for by the PHY and will stall preventing
+ * further packets from being received.  The workaround is to clear the
+ * packet buffer after the PHY detects link up.
+ */
+s32
+e1000_link_stall_workaround_hv(struct e1000_hw *hw)
+{
+	s32 ret_val = E1000_SUCCESS;
+	u16 data;
+
+	DEBUGFUNC("e1000_link_stall_workaround_hv");
+
+	if (hw->phy.type != e1000_phy_82578)
+		goto out;
+
+	/* Do not apply workaround if in PHY loopback bit 14 set */
+	hw->phy.ops.read_reg(hw, PHY_CONTROL, &data);
+	if (data & PHY_CONTROL_LB)
+		goto out;
+
+	/* check if link is up and at 1Gbps */
+	ret_val = hw->phy.ops.read_reg(hw, BM_CS_STATUS, &data);
+	if (ret_val)
+		goto out;
+
+	data &= BM_CS_STATUS_LINK_UP |
+	    BM_CS_STATUS_RESOLVED |
+	    BM_CS_STATUS_SPEED_MASK;
+
+	if (data != (BM_CS_STATUS_LINK_UP |
+	    BM_CS_STATUS_RESOLVED |
+	    BM_CS_STATUS_SPEED_1000))
+		goto out;
+
+	msec_delay(200);
+
+	/* flush the packets in the fifo buffer */
+	ret_val = hw->phy.ops.write_reg(hw, HV_MUX_DATA_CTRL,
+	    HV_MUX_DATA_CTRL_GEN_TO_MAC | HV_MUX_DATA_CTRL_FORCE_SPEED);
+	if (ret_val)
+		goto out;
+
+	ret_val = hw->phy.ops.write_reg(hw, HV_MUX_DATA_CTRL,
+	    HV_MUX_DATA_CTRL_GEN_TO_MAC);
+
+out:
+	return (ret_val);
+}
+
+/*
+ * e1000_check_polarity_82577 - Checks the polarity.
+ * @hw: pointer to the HW structure
+ *
+ * Success returns 0, Failure returns -E1000_ERR_PHY (-2)
+ *
+ * Polarity is determined based on the PHY specific status register.
+ */
+s32
+e1000_check_polarity_82577(struct e1000_hw *hw)
+{
+	struct e1000_phy_info *phy = &hw->phy;
+	s32 ret_val;
+	u16 data;
+
+	DEBUGFUNC("e1000_check_polarity_82577");
+
+	ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data);
+
+	if (!ret_val)
+		phy->cable_polarity = (data & I82577_PHY_STATUS2_REV_POLARITY)
+		    ? e1000_rev_polarity_reversed
+		    : e1000_rev_polarity_normal;
+
+	return (ret_val);
+}
+
+/*
+ * e1000_phy_force_speed_duplex_82577 - Force speed/duplex for I82577 PHY
+ * @hw: pointer to the HW structure
+ *
+ * Calls the PHY setup function to force speed and duplex.  Clears the
+ * auto-crossover to force MDI manually.  Waits for link and returns
+ * successful if link up is successful, else -E1000_ERR_PHY (-2).
+ */
+s32
+e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw)
+{
+	struct e1000_phy_info *phy = &hw->phy;
+	s32 ret_val;
+	u16 phy_data;
+	bool link;
+
+	DEBUGFUNC("e1000_phy_force_speed_duplex_82577");
+
+	ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data);
+	if (ret_val)
+		goto out;
+
+	e1000_phy_force_speed_duplex_setup(hw, &phy_data);
+
+	ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data);
+	if (ret_val)
+		goto out;
+
+	/*
+	 * Clear Auto-Crossover to force MDI manually.  82577 requires MDI
+	 * forced whenever speed and duplex are forced.
+	 */
+	ret_val = phy->ops.read_reg(hw, I82577_PHY_CTRL_2, &phy_data);
+	if (ret_val)
+		goto out;
+
+	phy_data &= ~I82577_PHY_CTRL2_AUTO_MDIX;
+	phy_data &= ~I82577_PHY_CTRL2_FORCE_MDI_MDIX;
+
+	ret_val = phy->ops.write_reg(hw, I82577_PHY_CTRL_2, phy_data);
+	if (ret_val)
+		goto out;
+
+	DEBUGOUT1("I82577_PHY_CTRL_2: %X\n", phy_data);
+
+	usec_delay(1);
+
+	if (phy->autoneg_wait_to_complete) {
+		DEBUGOUT("Waiting for forced speed/duplex link on 82577 phy\n");
+
+		ret_val = e1000_phy_has_link_generic(hw,
+		    PHY_FORCE_LIMIT, 100000, &link);
+		if (ret_val)
+			goto out;
+
+		if (!link)
+			DEBUGOUT("Link taking longer than expected.\n");
+
+		/* Try once more */
+		ret_val = e1000_phy_has_link_generic(hw,
+		    PHY_FORCE_LIMIT, 100000, &link);
+		if (ret_val)
+			goto out;
+	}
+
+out:
+	return (ret_val);
+}
+
+/*
+ * e1000_get_phy_info_82577 - Retrieve I82577 PHY information
+ * @hw: pointer to the HW structure
+ *
+ * Read PHY status to determine if link is up.  If link is up, then
+ * set/determine 10base-T extended distance and polarity correction.  Read
+ * PHY port status to determine MDI/MDIx and speed.  Based on the speed,
+ * determine on the cable length, local and remote receiver.
+ */
+s32
+e1000_get_phy_info_82577(struct e1000_hw *hw)
+{
+	struct e1000_phy_info *phy = &hw->phy;
+	s32 ret_val;
+	u16 data;
+	bool link;
+
+	DEBUGFUNC("e1000_get_phy_info_82577");
+
+	ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link);
+	if (ret_val)
+		goto out;
+
+	if (!link) {
+		DEBUGOUT("Phy info is only valid if link is up\n");
+		ret_val = -E1000_ERR_CONFIG;
+		goto out;
+	}
+
+	phy->polarity_correction = true;
+
+	ret_val = e1000_check_polarity_82577(hw);
+	if (ret_val)
+		goto out;
+
+	ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data);
+	if (ret_val)
+		goto out;
+
+	phy->is_mdix = (data & I82577_PHY_STATUS2_MDIX) ? true : false;
+
+	if ((data & I82577_PHY_STATUS2_SPEED_MASK) ==
+	    I82577_PHY_STATUS2_SPEED_1000MBPS) {
+		ret_val = hw->phy.ops.get_cable_length(hw);
+		if (ret_val)
+			goto out;
+
+		ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data);
+		if (ret_val)
+			goto out;
+
+		phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS)
+		    ? e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
+
+		phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS)
+		    ? e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
+	} else {
+		phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED;
+		phy->local_rx = e1000_1000t_rx_status_undefined;
+		phy->remote_rx = e1000_1000t_rx_status_undefined;
+	}
+
+out:
+	return (ret_val);
+}
+
+/*
+ * e1000_get_cable_length_82577 - Determine cable length for 82577 PHY
+ * @hw: pointer to the HW structure
+ *
+ * Reads the diagnostic status register and verifies result is valid before
+ * placing it in the phy_cable_length field.
+ */
+s32
+e1000_get_cable_length_82577(struct e1000_hw *hw)
+{
+	struct e1000_phy_info *phy = &hw->phy;
+	s32 ret_val;
+	u16 phy_data, length;
+
+	DEBUGFUNC("e1000_get_cable_length_82577");
+
+	ret_val = phy->ops.read_reg(hw, I82577_PHY_DIAG_STATUS, &phy_data);
+	if (ret_val)
+		goto out;
+
+	length = (phy_data & I82577_DSTATUS_CABLE_LENGTH) >>
+	    I82577_DSTATUS_CABLE_LENGTH_SHIFT;
+
+	if (length == E1000_CABLE_LENGTH_UNDEFINED)
+		ret_val = E1000_ERR_PHY;
+
+	phy->cable_length = length;
+
+out:
+	return (ret_val);
+}
--- a/usr/src/uts/common/io/e1000g/e1000_phy.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_phy.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.55 sol_anvik_patch
+ * IntelVersion: 1.70 v3-1-3_2009-8-20
  */
 #ifndef _E1000_PHY_H_
 #define	_E1000_PHY_H_
@@ -41,12 +41,14 @@
 s32 e1000_check_downshift_generic(struct e1000_hw *hw);
 s32 e1000_check_polarity_m88(struct e1000_hw *hw);
 s32 e1000_check_polarity_igp(struct e1000_hw *hw);
+s32 e1000_check_polarity_ife(struct e1000_hw *hw);
 s32 e1000_check_reset_block_generic(struct e1000_hw *hw);
 s32 e1000_copper_link_autoneg(struct e1000_hw *hw);
 s32 e1000_copper_link_setup_igp(struct e1000_hw *hw);
 s32 e1000_copper_link_setup_m88(struct e1000_hw *hw);
 s32 e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw);
 s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw);
+s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw);
 s32 e1000_get_cable_length_m88(struct e1000_hw *hw);
 s32 e1000_get_cable_length_igp_2(struct e1000_hw *hw);
 s32 e1000_get_cfg_done_generic(struct e1000_hw *hw);
@@ -81,6 +83,15 @@
 void e1000_power_down_phy_copper(struct e1000_hw *hw);
 s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data);
 s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data);
+s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data);
+s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data);
+s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw, bool slow);
+s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw);
+s32 e1000_copper_link_setup_82577(struct e1000_hw *hw);
+s32 e1000_check_polarity_82577(struct e1000_hw *hw);
+s32 e1000_get_phy_info_82577(struct e1000_hw *hw);
+s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw);
+s32 e1000_get_cable_length_82577(struct e1000_hw *hw);
 
 #define	E1000_MAX_PHY_ADDR		4
 
@@ -97,21 +108,70 @@
 #define	IGP_PAGE_SHIFT			5
 #define	PHY_REG_MASK			0x1F
 
+/* BM/HV Specific Registers */
+#define	BM_PORT_CTRL_PAGE		769
+#define	BM_PCIE_PAGE			770
 #define	BM_WUC_PAGE			800
 #define	BM_WUC_ADDRESS_OPCODE		0x11
 #define	BM_WUC_DATA_OPCODE		0x12
-#define	BM_WUC_ENABLE_PAGE		769
+#define	BM_WUC_ENABLE_PAGE		BM_PORT_CTRL_PAGE
 #define	BM_WUC_ENABLE_REG		17
 #define	BM_WUC_ENABLE_BIT		(1 << 2)
 #define	BM_WUC_HOST_WU_BIT		(1 << 4)
 
+#define	PHY_UPPER_SHIFT			21
+#define	BM_PHY_REG(page, reg) \
+	(((reg) & MAX_PHY_REG_ADDRESS) |\
+	(((page) & 0xFFFF) << PHY_PAGE_SHIFT) |\
+	(((reg) & ~MAX_PHY_REG_ADDRESS) << (PHY_UPPER_SHIFT - PHY_PAGE_SHIFT)))
+#define	BM_PHY_REG_PAGE(offset) \
+	((u16)(((offset) >> PHY_PAGE_SHIFT) & 0xFFFF))
+#define	BM_PHY_REG_NUM(offset) \
+	((u16)(((offset) & MAX_PHY_REG_ADDRESS) |\
+	(((offset) >> (PHY_UPPER_SHIFT - PHY_PAGE_SHIFT)) &\
+	~MAX_PHY_REG_ADDRESS)))
+
+#define	HV_INTC_FC_PAGE_START		768
+#define	I82578_ADDR_REG			29
+#define	I82577_ADDR_REG			16
+#define	I82577_CFG_REG			22
+#define	I82577_CFG_ASSERT_CRS_ON_TX	(1 << 15)
+#define	I82577_CFG_ENABLE_DOWNSHIFT	(3 << 10) /* auto downshift 100/10 */
+#define	I82577_CTRL_REG			23
+#define	I82577_CTRL_DOWNSHIFT_MASK	(7 << 10)
+
+/* 82577 specific PHY registers */
+#define	I82577_PHY_CTRL_2		18
+#define	I82577_PHY_LBK_CTRL		19
+#define	I82577_PHY_STATUS_2		26
+#define	I82577_PHY_DIAG_STATUS		31
+
+/* I82577 PHY Status 2 */
+#define	I82577_PHY_STATUS2_REV_POLARITY		0x0400
+#define	I82577_PHY_STATUS2_MDIX			0x0800
+#define	I82577_PHY_STATUS2_SPEED_MASK		0x0300
+#define	I82577_PHY_STATUS2_SPEED_1000MBPS	0x0200
+#define	I82577_PHY_STATUS2_SPEED_100MBPS	0x0100
+
+/* I82577 PHY Control 2 */
+#define	I82577_PHY_CTRL2_AUTO_MDIX		0x0400
+#define	I82577_PHY_CTRL2_FORCE_MDI_MDIX		0x0200
+
+/* I82577 PHY Diagnostics Status */
+#define	I82577_DSTATUS_CABLE_LENGTH		0x03FC
+#define	I82577_DSTATUS_CABLE_LENGTH_SHIFT	2
+
 /* BM PHY Copper Specific Control 1 */
 #define	BM_CS_CTRL1			16
 #define	BM_CS_CTRL1_ENERGY_DETECT	0x0300	/* Enable Energy Detect */
 
-/* BM PHY Copper Specific States */
+/* BM PHY Copper Specific Status */
 #define	BM_CS_STATUS			17
 #define	BM_CS_STATUS_ENERGY_DETECT	0x0010	/* Energy Detect Status */
+#define	BM_CS_STATUS_LINK_UP		0x0400
+#define	BM_CS_STATUS_RESOLVED		0x0800
+#define	BM_CS_STATUS_SPEED_MASK		0xC000
+#define	BM_CS_STATUS_SPEED_1000		0x8000
 
 #define	IGP01E1000_PHY_PCS_INIT_REG	0x00B4
 #define	IGP01E1000_PHY_POLARITY_MASK	0x0078
@@ -132,7 +192,7 @@
 #define	IGP01E1000_PLHR_SS_DOWNGRADE	0x8000
 
 #define	IGP01E1000_PSSR_POLARITY_REVERSED 0x0002
-#define	IGP01E1000_PSSR_MDIX		0x0008
+#define	IGP01E1000_PSSR_MDIX		0x0800
 #define	IGP01E1000_PSSR_SPEED_MASK	0xC000
 #define	IGP01E1000_PSSR_SPEED_1000MBPS	0xC000
 
@@ -155,7 +215,12 @@
 #define	E1000_KMRNCTRLSTA_OFFSET_SHIFT	16
 #define	E1000_KMRNCTRLSTA_REN		0x00200000
 #define	E1000_KMRNCTRLSTA_DIAG_OFFSET	0x3	/* Kumeran Diagnostic */
+#define	E1000_KMRNCTRLSTA_TIMEOUTS	0x4	/* Kumeran Timeouts */
+#define	E1000_KMRNCTRLSTA_INBAND_PARAM	0x9	/* Kumeran InBand Parameters */
 #define	E1000_KMRNCTRLSTA_DIAG_NELPBK	0x1000	/* Nearend Loopback mode */
+#define	E1000_KMRNCTRLSTA_K1_CONFIG	0x7
+#define	E1000_KMRNCTRLSTA_K1_ENABLE	0x140E
+#define	E1000_KMRNCTRLSTA_K1_DISABLE	0x1400
 
 #define	IFE_PHY_EXTENDED_STATUS_CONTROL	0x10
 #define	IFE_PHY_SPECIAL_CONTROL		0x11 /* 100BaseTx PHY Special Control */
--- a/usr/src/uts/common/io/e1000g/e1000_regs.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_regs.h	Mon Oct 05 09:17:26 2009 -0400
@@ -24,7 +24,7 @@
  */
 
 /*
- * IntelVersion: 1.61 sol_anvik_patch
+ * IntelVersion: 1.76 v3-1-3_2009-8-20
  */
 #ifndef _E1000_REGS_H_
 #define	_E1000_REGS_H_
@@ -56,10 +56,13 @@
 #define	E1000_IMC	0x000D8  /* Interrupt Mask Clear - WO */
 #define	E1000_IAM	0x000E0  /* Interrupt Acknowledge Auto Mask */
 #define	E1000_IVAR	0x000E4  /* Interrupt Vector Allocation Register - RW */
+#define	E1000_SVCR	0x000F0
+#define	E1000_SVT	0x000F4
 #define	E1000_RCTL	0x00100  /* Rx Control - RW */
 #define	E1000_FCTTV	0x00170  /* Flow Control Transmit Timer Value - RW */
 #define	E1000_TXCW	0x00178  /* Tx Configuration Word - RW */
 #define	E1000_RXCW	0x00180  /* Rx Configuration Word - RO */
+#define	E1000_PBA_ECC	0x01100  /* PBA ECC Register */
 #define	E1000_TCTL	0x00400  /* Tx Control - RW */
 #define	E1000_TCTL_EXT	0x00404  /* Extended Tx Control - RW */
 #define	E1000_TIPG	0x00410  /* Tx Inter-packet gap -RW */
@@ -247,6 +250,8 @@
 /* Interrupt Cause Receiver Overrun Count */
 #define	E1000_ICRXOC	0x04124
 
+#define	E1000_CRC_OFFSET	0x05F50	/* CRC Offset register */
+
 #define	E1000_PCS_CFG0	0x04200  /* PCS Configuration 0 - RW */
 #define	E1000_PCS_LCTL	0x04208  /* PCS Link Control - RW */
 #define	E1000_PCS_LSTAT	0x0420C  /* PCS Link Status - RO */
@@ -301,6 +306,7 @@
 #define	E1000_GIOCTL	0x05B44 /* GIO Analog Control Register */
 #define	E1000_SCCTL	0x05B4C /* PCIc PLL Configuration Register */
 #define	E1000_GCR	0x05B00 /* PCI-Ex Control */
+#define	E1000_GCR2	0x05B64 /* PCI-Ex Control #2 */
 #define	E1000_GSCL_1	0x05B10 /* PCI-Ex Statistic Control #1 */
 #define	E1000_GSCL_2	0x05B14 /* PCI-Ex Statistic Control #2 */
 #define	E1000_GSCL_3	0x05B18 /* PCI-Ex Statistic Control #3 */
@@ -308,6 +314,8 @@
 #define	E1000_FACTPS	0x05B30 /* Function Active and Power State to MNG */
 #define	E1000_SWSM	0x05B50 /* SW Semaphore */
 #define	E1000_FWSM	0x05B54 /* FW Semaphore */
+/* Driver-only SW semaphore (not used by BOOT agents) */
+#define	E1000_SWSM2	0x05B58
 #define	E1000_DCA_ID	0x05B70 /* DCA Requester ID Information - RO */
 #define	E1000_DCA_CTRL	0x05B74 /* DCA Control - RW */
 #define	E1000_FFLT_DBG	0x05F04 /* Debug Register */
--- a/usr/src/uts/common/io/e1000g/e1000g.conf	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000g.conf	Mon Oct 05 09:17:26 2009 -0400
@@ -6,7 +6,7 @@
 #
 # CDDL LICENSE SUMMARY
 #
-# Copyright(c) 1999 - 2007 Intel Corporation. All rights reserved.
+# Copyright(c) 1999 - 2009 Intel Corporation. All rights reserved.
 #
 # The contents of this file are subject to the terms of Version
 # 1.0 of the Common Development and Distribution License (the "License").
@@ -18,12 +18,10 @@
 # and limitations under the License.
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms of the CDDLv1.
 #
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # Driver.conf file for Intel e1000g Gigabit Ethernet Adapter
 #
 ForceSpeedDuplex=7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7;
@@ -54,11 +52,17 @@
         # 1 is for upto 4k size frames.
         # 2 is for upto 8k size frames.
         # 3 is for upto 16k size frames.
-        # These are maximum frame limits, not the actual ethernet frame
-        # size. Your actual ethernet frame size would be determined by
-        # protocol stack configuration (please refer to ndd command man pages)
-        # For Jumbo Frame Support (9k ethernet packet) 
-        # use 3 (upto 16k size frames)
+	#
+	# These are maximum frame limits, not the ethernet payload size
+	# (usually called MTU).  Your actual ethernet MTU is determined by frame
+	# size limit and protocol stack configuration (please refer to ndd
+	# command man pages)
+	#
+	# For Jumbo Frame Support (9k ethernet packet) use 3 (upto 16k size
+	# frames).  On PCH adapter type (82577 and 82578) you can configure up
+	# to 4k size frames.  The 4k size is only allowed at 1gig speed, so if
+	# you select 4k frames size, you cannot force or autonegotiate the
+	# 10/100 speed options.
 FlowControl=3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3;
         # 0:  Flow control is completely disabled
         # 1:  Rx flow control is enabled (we can receive pause frames
--- a/usr/src/uts/common/io/e1000g/e1000g_debug.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000g_debug.c	Mon Oct 05 09:17:26 2009 -0400
@@ -6,7 +6,7 @@
  *
  * CDDL LICENSE SUMMARY
  *
- * Copyright(c) 1999 - 2008 Intel Corporation. All rights reserved.
+ * Copyright(c) 1999 - 2009 Intel Corporation. All rights reserved.
  *
  * The contents of this file are subject to the terms of Version
  * 1.0 of the Common Development and Distribution License (the "License").
@@ -19,7 +19,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms of the CDDLv1.
  */
 
@@ -56,8 +56,9 @@
 	char		name[10];
 	uint32_t	offset;
 } Regi_t;
+
 int e1000g_debug = E1000G_WARN_LEVEL;
-#endif
+#endif	/* E1000G_DEBUG */
 int e1000g_log_mode = E1000G_LOG_PRINT;
 
 void
@@ -595,4 +596,4 @@
 		    name, base, tag_type, size, tag_mem);
 	}
 }
-#endif
+#endif	/* E1000G_DEBUG */
--- a/usr/src/uts/common/io/e1000g/e1000g_main.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000g_main.c	Mon Oct 05 09:17:26 2009 -0400
@@ -46,7 +46,7 @@
 
 static char ident[] = "Intel PRO/1000 Ethernet";
 static char e1000g_string[] = "Intel(R) PRO/1000 Network Connection";
-static char e1000g_version[] = "Driver Ver. 5.3.14";
+static char e1000g_version[] = "Driver Ver. 5.3.15";
 
 /*
  * Proto types for DDI entry points
@@ -94,6 +94,8 @@
 static int e1000g_unicst_set(struct e1000g *, const uint8_t *, int);
 static int e1000g_alloc_rx_data(struct e1000g *);
 static void e1000g_release_multicast(struct e1000g *);
+static void e1000g_pch_limits(struct e1000g *);
+static uint32_t e1000g_mtu2maxframe(uint32_t);
 
 /*
  * Local routines
@@ -116,6 +118,7 @@
 static void stop_link_timer(struct e1000g *);
 static void stop_82547_timer(e1000g_tx_ring_t *);
 static void e1000g_force_speed_duplex(struct e1000g *);
+static void e1000g_setup_max_mtu(struct e1000g *);
 static void e1000g_get_max_frame_size(struct e1000g *);
 static boolean_t is_valid_mac_addr(uint8_t *);
 static void e1000g_unattach(dev_info_t *, struct e1000g *);
@@ -668,7 +671,8 @@
 	/* ICH needs to map flash memory */
 	if (hw->mac.type == e1000_ich8lan ||
 	    hw->mac.type == e1000_ich9lan ||
-	    hw->mac.type == e1000_ich10lan) {
+	    hw->mac.type == e1000_ich10lan ||
+	    hw->mac.type == e1000_pchlan) {
 		/* get flash size */
 		if (ddi_dev_regsize(devinfo, ICH_FLASH_REG_SET,
 		    &mem_size) != DDI_SUCCESS) {
@@ -766,6 +770,9 @@
 	Adapter->strip_crc = B_FALSE;
 #endif
 
+	/* setup the maximum MTU size of the chip */
+	e1000g_setup_max_mtu(Adapter);
+
 	/* Get conf file properties */
 	e1000g_get_conf(Adapter);
 
@@ -777,6 +784,9 @@
 	/* Get Jumbo Frames settings in conf file */
 	e1000g_get_max_frame_size(Adapter);
 
+	/* enforce PCH limits */
+	e1000g_pch_limits(Adapter);
+
 	/* Set Rx/Tx buffer size */
 	e1000g_set_bufsize(Adapter);
 
@@ -810,6 +820,47 @@
 }
 
 static void
+e1000g_setup_max_mtu(struct e1000g *Adapter)
+{
+	struct e1000_mac_info *mac = &Adapter->shared.mac;
+	struct e1000_phy_info *phy = &Adapter->shared.phy;
+
+	switch (mac->type) {
+	/* types that do not support jumbo frames */
+	case e1000_ich8lan:
+	case e1000_82573:
+	case e1000_82583:
+		Adapter->max_mtu = ETHERMTU;
+		break;
+	/* ich9 supports jumbo frames except on one phy type */
+	case e1000_ich9lan:
+		if (phy->type == e1000_phy_ife)
+			Adapter->max_mtu = ETHERMTU;
+		else
+			Adapter->max_mtu = MAXIMUM_MTU_9K;
+		break;
+	/* pch can do jumbo frames up to 4K */
+	case e1000_pchlan:
+		Adapter->max_mtu = MAXIMUM_MTU_4K;
+		break;
+	/* types with a special limit */
+	case e1000_82571:
+	case e1000_82572:
+	case e1000_82574:
+	case e1000_80003es2lan:
+	case e1000_ich10lan:
+		Adapter->max_mtu = MAXIMUM_MTU_9K;
+		break;
+	/* default limit is 16K */
+	default:
+		Adapter->max_mtu = FRAME_SIZE_UPTO_16K -
+		    sizeof (struct ether_vlan_header) - ETHERFCSL -
+		    E1000G_IPALIGNPRESERVEROOM;
+		break;
+	}
+}
+
+static void
 e1000g_set_bufsize(struct e1000g *Adapter)
 {
 	struct e1000_mac_info *mac = &Adapter->shared.mac;
@@ -1099,6 +1150,9 @@
 	mutex_destroy(&Adapter->link_lock);
 	mutex_destroy(&Adapter->watchdog_lock);
 	rw_destroy(&Adapter->chip_lock);
+
+	/* destory mutex initialized in shared code */
+	e1000_destroy_hw_mutex(&Adapter->shared);
 }
 
 static int
@@ -1280,6 +1334,8 @@
 		pba = E1000_PBA_10K;
 	} else if (hw->mac.type == e1000_ich10lan) {
 		pba = E1000_PBA_10K;
+	} else if (hw->mac.type == e1000_pchlan) {
+		pba = E1000_PBA_26K;
 	} else {
 		/*
 		 * Total FIFO is 40K
@@ -1378,7 +1434,8 @@
 	 * Setup and initialize the mctable structures.  After this routine
 	 * completes  Multicast table will be set
 	 */
-	e1000g_setup_multicast(Adapter);
+	e1000_update_mc_addr_list(hw,
+	    (uint8_t *)Adapter->mcast_table, Adapter->mcast_count);
 	msec_delay(5);
 
 	/*
@@ -1408,11 +1465,6 @@
 	}
 	mutex_exit(&Adapter->link_lock);
 
-	/* Enable PCI-Ex master */
-	if (hw->bus.type == e1000_bus_type_pci_express) {
-		e1000_enable_pciex_master(hw);
-	}
-
 	/* Save the state of the phy */
 	e1000g_get_phy_state(Adapter);
 
@@ -1497,20 +1549,33 @@
 static boolean_t
 e1000g_link_up(struct e1000g *Adapter)
 {
-	struct e1000_hw *hw;
-	boolean_t link_up;
-
-	hw = &Adapter->shared;
-
-	(void) e1000_check_for_link(hw);
-
-	if ((E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU) ||
-	    ((!hw->mac.get_link_status) && (hw->mac.type == e1000_82543)) ||
-	    ((hw->phy.media_type == e1000_media_type_internal_serdes) &&
-	    (hw->mac.serdes_has_link))) {
-		link_up = B_TRUE;
-	} else {
-		link_up = B_FALSE;
+	struct e1000_hw *hw = &Adapter->shared;
+	boolean_t link_up = B_FALSE;
+
+	/*
+	 * get_link_status is set in the interrupt handler on link-status-change
+	 * or rx sequence error interrupt.  get_link_status will stay
+	 * false until the e1000_check_for_link establishes link only
+	 * for copper adapters.
+	 */
+	switch (hw->phy.media_type) {
+	case e1000_media_type_copper:
+		if (hw->mac.get_link_status) {
+			(void) e1000_check_for_link(hw);
+			link_up = !hw->mac.get_link_status;
+		} else {
+			link_up = B_TRUE;
+		}
+		break;
+	case e1000_media_type_fiber:
+		(void) e1000_check_for_link(hw);
+		link_up = (E1000_READ_REG(hw, E1000_STATUS) &
+		    E1000_STATUS_LU);
+		break;
+	case e1000_media_type_internal_serdes:
+		(void) e1000_check_for_link(hw);
+		link_up = hw->mac.serdes_has_link;
+		break;
 	}
 
 	return (link_up);
@@ -2403,11 +2468,8 @@
 	 */
 	e1000g_clear_interrupt(Adapter);
 
-	e1000g_setup_multicast(Adapter);
-
-	if ((hw->mac.type == e1000_82542) &&
-	    (hw->revision_id == E1000_REVISION_2))
-		e1000g_rx_setup(Adapter);
+	e1000_update_mc_addr_list(hw,
+	    (uint8_t *)Adapter->mcast_table, Adapter->mcast_count);
 
 	e1000g_mask_interrupt(Adapter);
 
@@ -2463,11 +2525,8 @@
 	 */
 	e1000g_clear_interrupt(Adapter);
 
-	e1000g_setup_multicast(Adapter);
-
-	if ((hw->mac.type == e1000_82542) &&
-	    (hw->revision_id == E1000_REVISION_2))
-		e1000g_rx_setup(Adapter);
+	e1000_update_mc_addr_list(hw,
+	    (uint8_t *)Adapter->mcast_table, Adapter->mcast_count);
 
 	e1000g_mask_interrupt(Adapter);
 
@@ -2489,98 +2548,6 @@
 	}
 }
 
-/*
- * e1000g_setup_multicast - setup multicast data structures
- *
- * This routine initializes all of the multicast related structures.
- */
-void
-e1000g_setup_multicast(struct e1000g *Adapter)
-{
-	uint8_t *mc_addr_list;
-	uint32_t mc_addr_count;
-	uint32_t rctl;
-	struct e1000_hw *hw;
-
-	hw = &Adapter->shared;
-
-	/*
-	 * The e1000g has the ability to do perfect filtering of 16
-	 * addresses. The driver uses one of the e1000g's 16 receive
-	 * address registers for its node/network/mac/individual address.
-	 * So, we have room for up to 15 multicast addresses in the CAM,
-	 * additional MC addresses are handled by the MTA (Multicast Table
-	 * Array)
-	 */
-
-	rctl = E1000_READ_REG(hw, E1000_RCTL);
-
-	mc_addr_list = (uint8_t *)Adapter->mcast_table;
-
-	ASSERT(Adapter->mcast_count <= Adapter->mcast_max_num);
-
-	mc_addr_count = Adapter->mcast_count;
-	/*
-	 * The Wiseman 2.0 silicon has an errata by which the receiver will
-	 * hang  while writing to the receive address registers if the receiver
-	 * is not in reset before writing to the registers. Updating the RAR
-	 * is done during the setting up of the multicast table, hence the
-	 * receiver has to be put in reset before updating the multicast table
-	 * and then taken out of reset at the end
-	 */
-	/*
-	 * if WMI was enabled then dis able it before issueing the global
-	 * reset to the hardware.
-	 */
-	/*
-	 * Only required for WISEMAN_2_0
-	 */
-	if ((hw->mac.type == e1000_82542) &&
-	    (hw->revision_id == E1000_REVISION_2)) {
-		e1000_pci_clear_mwi(hw);
-		/*
-		 * The e1000g must be in reset before changing any RA
-		 * registers. Reset receive unit.  The chip will remain in
-		 * the reset state until software explicitly restarts it.
-		 */
-		E1000_WRITE_REG(hw, E1000_RCTL, E1000_RCTL_RST);
-		/* Allow receiver time to go in to reset */
-		msec_delay(5);
-	}
-
-	e1000_update_mc_addr_list(hw, mc_addr_list, mc_addr_count,
-	    Adapter->unicst_total, hw->mac.rar_entry_count);
-
-	/*
-	 * Only for Wiseman_2_0
-	 * If MWI was enabled then re-enable it after issueing (as we
-	 * disabled it up there) the receive reset command.
-	 * Wainwright does not have a receive reset command and only thing
-	 * close to it is global reset which will require tx setup also
-	 */
-	if ((hw->mac.type == e1000_82542) &&
-	    (hw->revision_id == E1000_REVISION_2)) {
-		/*
-		 * if WMI was enabled then reenable it after issueing the
-		 * global or receive reset to the hardware.
-		 */
-
-		/*
-		 * Take receiver out of reset
-		 * clear E1000_RCTL_RST bit (and all others)
-		 */
-		E1000_WRITE_REG(hw, E1000_RCTL, 0);
-		msec_delay(5);
-		if (hw->bus.pci_cmd_word & CMD_MEM_WRT_INVALIDATE)
-			e1000_pci_set_mwi(hw);
-	}
-
-	/*
-	 * Restore original value
-	 */
-	E1000_WRITE_REG(hw, E1000_RCTL, rctl);
-}
-
 int
 e1000g_m_multicst(void *arg, boolean_t add, const uint8_t *addr)
 {
@@ -2995,14 +2962,11 @@
     uint_t pr_valsize, const void *pr_val)
 {
 	struct e1000g *Adapter = arg;
-	struct e1000_mac_info *mac = &Adapter->shared.mac;
-	struct e1000_phy_info *phy = &Adapter->shared.phy;
 	struct e1000_hw *hw = &Adapter->shared;
 	struct e1000_fc_info *fc = &Adapter->shared.fc;
 	int err = 0;
 	link_flowctrl_t flowctrl;
 	uint32_t cur_mtu, new_mtu;
-	uint64_t tmp = 0;
 
 	rw_enter(&Adapter->chip_lock, RW_WRITER);
 
@@ -3092,6 +3056,8 @@
 			}
 reset:
 			if (err == 0) {
+				/* check PCH limits & reset the link */
+				e1000g_pch_limits(Adapter);
 				if (e1000g_reset_link(Adapter) != DDI_SUCCESS)
 					err = EINVAL;
 			}
@@ -3109,43 +3075,40 @@
 			err = ENOTSUP; /* read-only prop. Can't set this. */
 			break;
 		case MAC_PROP_MTU:
+			/* adapter must be stopped for an MTU change */
+			if (Adapter->e1000g_state & E1000G_STARTED) {
+				err = EBUSY;
+				break;
+			}
+
 			cur_mtu = Adapter->default_mtu;
+
+			/* get new requested MTU */
 			bcopy(pr_val, &new_mtu, sizeof (new_mtu));
 			if (new_mtu == cur_mtu) {
 				err = 0;
 				break;
 			}
 
-			tmp = new_mtu + sizeof (struct ether_vlan_header) +
-			    ETHERFCSL;
-			if ((tmp < DEFAULT_FRAME_SIZE) ||
-			    (tmp > MAXIMUM_FRAME_SIZE)) {
-				err = EINVAL;
-				break;
-			}
-
-			/* ich8 does not support jumbo frames */
-			if ((mac->type == e1000_ich8lan) &&
-			    (tmp > DEFAULT_FRAME_SIZE)) {
+			if ((new_mtu < DEFAULT_MTU) ||
+			    (new_mtu > Adapter->max_mtu)) {
 				err = EINVAL;
 				break;
 			}
-			/* ich9 does not do jumbo frames on one phy type */
-			if ((mac->type == e1000_ich9lan) &&
-			    (phy->type == e1000_phy_ife) &&
-			    (tmp > DEFAULT_FRAME_SIZE)) {
-				err = EINVAL;
-				break;
-			}
-			if (Adapter->e1000g_state & E1000G_STARTED) {
-				err = EBUSY;
-				break;
-			}
-
+
+			/* inform MAC framework of new MTU */
 			err = mac_maxsdu_update(Adapter->mh, new_mtu);
+
 			if (err == 0) {
-				Adapter->max_frame_size = (uint32_t)tmp;
 				Adapter->default_mtu = new_mtu;
+				Adapter->max_frame_size =
+				    e1000g_mtu2maxframe(new_mtu);
+
+				/*
+				 * check PCH limits & set buffer sizes to
+				 * match new MTU
+				 */
+				e1000g_pch_limits(Adapter);
 				e1000g_set_bufsize(Adapter);
 			}
 			break;
@@ -3299,7 +3262,7 @@
 			range.mpr_count = 1;
 			range.mpr_type = MAC_PROPVAL_UINT32;
 			range.range_uint32[0].mpur_min = DEFAULT_MTU;
-			range.range_uint32[0].mpur_max = MAXIMUM_MTU;
+			range.range_uint32[0].mpur_max = Adapter->max_mtu;
 			/* following MAC type do not support jumbo frames */
 			if ((mac->type == e1000_ich8lan) ||
 			    ((mac->type == e1000_ich9lan) && (phy->type ==
@@ -4016,12 +3979,10 @@
 
 	if (invalid) {
 		e1000g_log(Adapter, CE_WARN,
-		    "Invalid link sets. Setup link to"
+		    "Invalid link settings. Setup link to "
 		    "support autonegotiation with all link capabilities.");
 		mac->autoneg = B_TRUE;
-		phy->autoneg_advertised = ADVERTISE_1000_FULL |
-		    ADVERTISE_100_FULL | ADVERTISE_100_HALF |
-		    ADVERTISE_10_FULL | ADVERTISE_10_HALF;
+		phy->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT;
 	}
 
 out:
@@ -4259,8 +4220,6 @@
 e1000g_get_max_frame_size(struct e1000g *Adapter)
 {
 	int max_frame;
-	struct e1000_mac_info *mac = &Adapter->shared.mac;
-	struct e1000_phy_info *phy = &Adapter->shared.phy;
 
 	/*
 	 * get value out of config file
@@ -4286,35 +4245,68 @@
 		    E1000G_IPALIGNPRESERVEROOM;
 		break;
 	case 3:
-		if (mac->type >= e1000_82571)
-			Adapter->default_mtu = MAXIMUM_MTU;
-		else
-			Adapter->default_mtu = FRAME_SIZE_UPTO_16K -
-			    sizeof (struct ether_vlan_header) - ETHERFCSL -
-			    E1000G_IPALIGNPRESERVEROOM;
+		Adapter->default_mtu = FRAME_SIZE_UPTO_16K -
+		    sizeof (struct ether_vlan_header) - ETHERFCSL -
+		    E1000G_IPALIGNPRESERVEROOM;
 		break;
 	default:
 		Adapter->default_mtu = ETHERMTU;
 		break;
 	}	/* switch */
 
-	Adapter->max_frame_size = Adapter->default_mtu +
-	    sizeof (struct ether_vlan_header) + ETHERFCSL;
-
-	/* ich8 does not do jumbo frames */
-	if (mac->type == e1000_ich8lan) {
-		Adapter->default_mtu = ETHERMTU;
-		Adapter->max_frame_size = ETHERMTU +
-		    sizeof (struct ether_vlan_header) + ETHERFCSL;
-	}
-
-	/* ich9 does not do jumbo frames on one phy type */
-	if ((mac->type == e1000_ich9lan) &&
-	    (phy->type == e1000_phy_ife)) {
-		Adapter->default_mtu = ETHERMTU;
-		Adapter->max_frame_size = ETHERMTU +
-		    sizeof (struct ether_vlan_header) + ETHERFCSL;
-	}
+	/*
+	 * If the user configed MTU is larger than the deivce's maximum MTU,
+	 * the MTU is set to the deivce's maximum value.
+	 */
+	if (Adapter->default_mtu > Adapter->max_mtu)
+		Adapter->default_mtu = Adapter->max_mtu;
+
+	Adapter->max_frame_size = e1000g_mtu2maxframe(Adapter->default_mtu);
+}
+
+/*
+ * e1000g_pch_limits - Apply limits of the PCH silicon type
+ *
+ * At any frame size larger than the ethernet default,
+ * prevent linking at 10/100 speeds.
+ */
+static void
+e1000g_pch_limits(struct e1000g *Adapter)
+{
+	struct e1000_hw *hw = &Adapter->shared;
+
+	/* only applies to PCH silicon type */
+	if (hw->mac.type != e1000_pchlan)
+		return;
+
+	/* only applies to frames larger than ethernet default */
+	if (Adapter->max_frame_size > DEFAULT_FRAME_SIZE) {
+		hw->mac.autoneg = B_TRUE;
+		hw->phy.autoneg_advertised = ADVERTISE_1000_FULL;
+
+		Adapter->param_adv_autoneg = 1;
+		Adapter->param_adv_1000fdx = 1;
+
+		Adapter->param_adv_100fdx = 0;
+		Adapter->param_adv_100hdx = 0;
+		Adapter->param_adv_10fdx = 0;
+		Adapter->param_adv_10hdx = 0;
+
+		e1000g_param_sync(Adapter);
+	}
+}
+
+/*
+ * e1000g_mtu2maxframe - convert given MTU to maximum frame size
+ */
+static uint32_t
+e1000g_mtu2maxframe(uint32_t mtu)
+{
+	uint32_t maxframe;
+
+	maxframe = mtu + sizeof (struct ether_vlan_header) + ETHERFCSL;
+
+	return (maxframe);
 }
 
 static void
@@ -5290,10 +5282,10 @@
 			ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
 			ctrl_ext |= (E1000_CTRL_EXT_SDP4_DATA |
 			    E1000_CTRL_EXT_SDP6_DATA |
-			    E1000_CTRL_EXT_SDP7_DATA |
+			    E1000_CTRL_EXT_SDP3_DATA |
 			    E1000_CTRL_EXT_SDP4_DIR |
 			    E1000_CTRL_EXT_SDP6_DIR |
-			    E1000_CTRL_EXT_SDP7_DIR);	/* 0x0DD0 */
+			    E1000_CTRL_EXT_SDP3_DIR);	/* 0x0DD0 */
 			E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
 
 			/*
@@ -6204,6 +6196,7 @@
 	case e1000_ich8lan:
 	case e1000_ich9lan:
 	case e1000_ich10lan:
+	case e1000_pchlan:
 		ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
 		E1000_WRITE_REG(hw, E1000_CTRL_EXT,
 		    ctrl_ext | E1000_CTRL_EXT_DRV_LOAD);
@@ -6237,6 +6230,7 @@
 	case e1000_ich8lan:
 	case e1000_ich9lan:
 	case e1000_ich10lan:
+	case e1000_pchlan:
 		ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
 		E1000_WRITE_REG(hw, E1000_CTRL_EXT,
 		    ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD);
--- a/usr/src/uts/common/io/e1000g/e1000g_rx.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000g_rx.c	Mon Oct 05 09:17:26 2009 -0400
@@ -149,6 +149,7 @@
 	uint32_t rctl;
 	uint32_t rxdctl;
 	uint32_t ert;
+	uint16_t phy_data;
 	int i;
 	int size;
 	e1000g_rx_data_t *rx_data;
@@ -320,6 +321,25 @@
 		E1000_WRITE_REG(hw, E1000_ERT, ert);
 	}
 
+	/* Workaround errata on 82577/8 adapters with large frames */
+	if ((hw->mac.type == e1000_pchlan) &&
+	    (Adapter->default_mtu > ETHERMTU)) {
+
+		e1000_read_phy_reg(hw, PHY_REG(770, 26), &phy_data);
+		phy_data &= 0xfff8;
+		phy_data |= (1 << 2);
+		e1000_write_phy_reg(hw, PHY_REG(770, 26), phy_data);
+
+		if (hw->phy.type == e1000_phy_82577) {
+			e1000_read_phy_reg(hw, 22, &phy_data);
+			phy_data &= 0x0fff;
+			phy_data |= (1 << 14);
+			e1000_write_phy_reg(hw, 0x10, 0x2823);
+			e1000_write_phy_reg(hw, 0x11, 0x0003);
+			e1000_write_phy_reg(hw, 22, phy_data);
+		}
+	}
+
 	reg_val =
 	    E1000_RXCSUM_TUOFL |	/* TCP/UDP checksum offload Enable */
 	    E1000_RXCSUM_IPOFL;		/* IP checksum offload Enable */
--- a/usr/src/uts/common/io/e1000g/e1000g_stat.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000g_stat.c	Mon Oct 05 09:17:26 2009 -0400
@@ -36,6 +36,7 @@
 #include "e1000g_debug.h"
 
 static int e1000g_update_stats(kstat_t *ksp, int rw);
+static uint32_t e1000g_read_phy_stat(struct e1000_hw *hw, int reg);
 
 /*
  * e1000_tbi_adjust_stats
@@ -217,7 +218,8 @@
 
 	if ((hw->mac.type != e1000_ich8lan) &&
 	    (hw->mac.type != e1000_ich9lan) &&
-	    (hw->mac.type != e1000_ich10lan)) {
+	    (hw->mac.type != e1000_ich10lan) &&
+	    (hw->mac.type != e1000_pchlan)) {
 		e1000g_ksp->Symerrs.value.ul +=
 		    E1000_READ_REG(hw, E1000_SYMERRS);
 #ifdef E1000G_DEBUG
@@ -256,7 +258,7 @@
 	e1000g_ksp->Roc.value.ul += E1000_READ_REG(hw, E1000_ROC);
 	e1000g_ksp->Rjc.value.ul += E1000_READ_REG(hw, E1000_RJC);
 	e1000g_ksp->Tpr.value.ul += E1000_READ_REG(hw, E1000_TPR);
-	e1000g_ksp->Tncrs.value.ul += E1000_READ_REG(hw, E1000_TNCRS);
+	e1000g_ksp->Tncrs.value.ul += e1000g_read_phy_stat(hw, E1000_TNCRS);
 	e1000g_ksp->Tsctc.value.ul += E1000_READ_REG(hw, E1000_TSCTC);
 	e1000g_ksp->Tsctfc.value.ul += E1000_READ_REG(hw, E1000_TSCTFC);
 
@@ -387,13 +389,13 @@
 
 	case MAC_STAT_OERRORS:
 		e1000g_ksp->Ecol.value.ul +=
-		    E1000_READ_REG(hw, E1000_ECOL);
+		    e1000g_read_phy_stat(hw, E1000_ECOL);
 		*val = e1000g_ksp->Ecol.value.ul;
 		break;
 
 	case MAC_STAT_COLLISIONS:
 		e1000g_ksp->Colc.value.ul +=
-		    E1000_READ_REG(hw, E1000_COLC);
+		    e1000g_read_phy_stat(hw, E1000_COLC);
 		*val = e1000g_ksp->Colc.value.ul;
 		break;
 
@@ -467,31 +469,31 @@
 
 	case ETHER_STAT_EX_COLLISIONS:
 		e1000g_ksp->Ecol.value.ul +=
-		    E1000_READ_REG(hw, E1000_ECOL);
+		    e1000g_read_phy_stat(hw, E1000_ECOL);
 		*val = e1000g_ksp->Ecol.value.ul;
 		break;
 
 	case ETHER_STAT_TX_LATE_COLLISIONS:
 		e1000g_ksp->Latecol.value.ul +=
-		    E1000_READ_REG(hw, E1000_LATECOL);
+		    e1000g_read_phy_stat(hw, E1000_LATECOL);
 		*val = e1000g_ksp->Latecol.value.ul;
 		break;
 
 	case ETHER_STAT_DEFER_XMTS:
 		e1000g_ksp->Dc.value.ul +=
-		    E1000_READ_REG(hw, E1000_DC);
+		    e1000g_read_phy_stat(hw, E1000_DC);
 		*val = e1000g_ksp->Dc.value.ul;
 		break;
 
 	case ETHER_STAT_FIRST_COLLISIONS:
 		e1000g_ksp->Scc.value.ul +=
-		    E1000_READ_REG(hw, E1000_SCC);
+		    e1000g_read_phy_stat(hw, E1000_SCC);
 		*val = e1000g_ksp->Scc.value.ul;
 		break;
 
 	case ETHER_STAT_MULTI_COLLISIONS:
 		e1000g_ksp->Mcc.value.ul +=
-		    E1000_READ_REG(hw, E1000_MCC);
+		    e1000g_read_phy_stat(hw, E1000_MCC);
 		*val = e1000g_ksp->Mcc.value.ul;
 		break;
 
@@ -503,7 +505,7 @@
 
 	case ETHER_STAT_MACXMT_ERRORS:
 		e1000g_ksp->Ecol.value.ul +=
-		    E1000_READ_REG(hw, E1000_ECOL);
+		    e1000g_read_phy_stat(hw, E1000_ECOL);
 		*val = e1000g_ksp->Ecol.value.ul;
 		break;
 
@@ -885,3 +887,79 @@
 
 	return (DDI_SUCCESS);
 }
+
+/*
+ * e1000g_read_phy_stat - read certain PHY statistics
+ *
+ * Certain statistics are read from MAC registers on some silicon types
+ * but are read from the PHY on other silicon types.  This routine
+ * handles that difference as needed.
+ */
+static uint32_t
+e1000g_read_phy_stat(struct e1000_hw *hw, int reg)
+{
+	uint16_t phy_low, phy_high;
+	uint32_t val;
+
+	/* get statistic from PHY in these cases */
+	if ((hw->phy.type == e1000_phy_82578) ||
+	    (hw->phy.type == e1000_phy_82577)) {
+
+		switch (reg) {
+		case E1000_SCC:
+			(void) e1000_read_phy_reg(hw, HV_SCC_UPPER, &phy_high);
+			(void) e1000_read_phy_reg(hw, HV_SCC_LOWER, &phy_low);
+			val = ((uint32_t)phy_high << 16) | (uint32_t)phy_low;
+			break;
+
+		case E1000_MCC:
+			(void) e1000_read_phy_reg(hw, HV_MCC_UPPER, &phy_high);
+			(void) e1000_read_phy_reg(hw, HV_MCC_LOWER, &phy_low);
+			val = ((uint32_t)phy_high << 16) | (uint32_t)phy_low;
+			break;
+
+		case E1000_ECOL:
+			(void) e1000_read_phy_reg(hw, HV_ECOL_UPPER, &phy_high);
+			(void) e1000_read_phy_reg(hw, HV_ECOL_LOWER, &phy_low);
+			val = ((uint32_t)phy_high << 16) | (uint32_t)phy_low;
+			break;
+
+		case E1000_COLC:
+			(void) e1000_read_phy_reg(hw, HV_COLC_UPPER, &phy_high);
+			(void) e1000_read_phy_reg(hw, HV_COLC_LOWER, &phy_low);
+			val = ((uint32_t)phy_high << 16) | (uint32_t)phy_low;
+			break;
+
+		case E1000_LATECOL:
+			(void) e1000_read_phy_reg(hw, HV_LATECOL_UPPER,
+			    &phy_high);
+			(void) e1000_read_phy_reg(hw, HV_LATECOL_LOWER,
+			    &phy_low);
+			val = ((uint32_t)phy_high << 16) | (uint32_t)phy_low;
+			break;
+
+		case E1000_DC:
+			(void) e1000_read_phy_reg(hw, HV_DC_UPPER, &phy_high);
+			(void) e1000_read_phy_reg(hw, HV_DC_LOWER, &phy_low);
+			val = ((uint32_t)phy_high << 16) | (uint32_t)phy_low;
+			break;
+
+		case E1000_TNCRS:
+			(void) e1000_read_phy_reg(hw, HV_TNCRS_UPPER,
+			    &phy_high);
+			(void) e1000_read_phy_reg(hw, HV_TNCRS_LOWER,
+			    &phy_low);
+			val = ((uint32_t)phy_high << 16) | (uint32_t)phy_low;
+			break;
+
+		default:
+			break;
+		}
+
+	/* get statistic from MAC otherwise */
+	} else {
+		val = E1000_READ_REG(hw, reg);
+	}
+
+	return (val);
+}
--- a/usr/src/uts/common/io/e1000g/e1000g_sw.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000g_sw.h	Mon Oct 05 09:17:26 2009 -0400
@@ -102,7 +102,6 @@
 /*
  * constants used in setting flow control thresholds
  */
-#define	E1000_PBA_10K		0x000A
 #define	E1000_PBA_MASK		0xffff
 #define	E1000_PBA_SHIFT		10
 #define	E1000_FC_HIGH_DIFF	0x1638 /* High: 5688 bytes below Rx FIFO size */
@@ -266,8 +265,9 @@
 #define	FRAME_SIZE_UPTO_16K	16384
 #define	FRAME_SIZE_UPTO_9K	9234
 
-#define	MAXIMUM_MTU		9216
 #define	DEFAULT_MTU		ETHERMTU
+#define	MAXIMUM_MTU_4K		4096
+#define	MAXIMUM_MTU_9K		9216
 
 #define	DEFAULT_FRAME_SIZE	\
 	(DEFAULT_MTU + sizeof (struct ether_vlan_header) + ETHERFCSL)
@@ -878,6 +878,7 @@
 	uint32_t intr_throttling_rate;
 
 	uint32_t default_mtu;
+	uint32_t max_mtu;
 	uint32_t max_frame_size;
 	uint32_t min_frame_size;
 
@@ -1018,7 +1019,6 @@
 void e1000g_free_rx_sw_packet(p_rx_sw_packet_t packet, boolean_t full_release);
 void e1000g_tx_setup(struct e1000g *Adapter);
 void e1000g_rx_setup(struct e1000g *Adapter);
-void e1000g_setup_multicast(struct e1000g *Adapter);
 
 int e1000g_recycle(e1000g_tx_ring_t *tx_ring);
 void e1000g_free_tx_swpkt(p_tx_sw_packet_t packet);
@@ -1039,6 +1039,7 @@
 void e1000g_clear_tx_interrupt(struct e1000g *Adapter);
 void e1000g_mask_tx_interrupt(struct e1000g *Adapter);
 void phy_spd_state(struct e1000_hw *hw, boolean_t enable);
+void e1000_destroy_hw_mutex(struct e1000_hw *hw);
 void e1000_enable_pciex_master(struct e1000_hw *hw);
 int e1000g_check_acc_handle(ddi_acc_handle_t handle);
 int e1000g_check_dma_handle(ddi_dma_handle_t handle);
--- a/usr/src/uts/common/io/lvm/md/md_subr.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/lvm/md/md_subr.c	Mon Oct 05 09:17:26 2009 -0400
@@ -1126,8 +1126,20 @@
 
 	/* teardown kstat, return success */
 	if (! (ui->ui_lock & MD_UL_OPEN)) {
-		mutex_exit(&ui->ui_mx);
-		md_kstat_destroy(mnum);
+
+		/*
+		 * We have a race condition inherited from specfs between
+		 * open() and close() calls. This results in the kstat
+		 * for a pending I/O being torn down, and then a panic.
+		 * To avoid this, only tear the kstat down if there are
+		 * no other readers on this device.
+		 */
+		if (ui->ui_readercnt > 1) {
+			mutex_exit(&ui->ui_mx);
+		} else {
+			mutex_exit(&ui->ui_mx);
+			md_kstat_destroy(mnum);
+		}
 		return (0);
 	}
 
--- a/usr/src/uts/common/io/mac/mac_client.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/mac/mac_client.c	Mon Oct 05 09:17:26 2009 -0400
@@ -4244,7 +4244,7 @@
 {
 	mac_impl_t *mip = (mac_impl_t *)mh;
 	uint_t old_mtu;
-	int rv;
+	int rv = 0;
 
 	i_mac_perim_enter(mip);
 
--- a/usr/src/uts/common/io/pshot.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/pshot.c	Mon Oct 05 09:17:26 2009 -0400
@@ -142,7 +142,6 @@
 	{"DDI_NT_FC_ATTACHMENT_POINT", DDI_NT_FC_ATTACHMENT_POINT},
 	{"DDI_NT_USB_ATTACHMENT_POINT", DDI_NT_USB_ATTACHMENT_POINT},
 	{"DDI_NT_BLOCK_FABRIC", DDI_NT_BLOCK_FABRIC},
-	{"DDI_NT_SMARTCARD_READER", DDI_NT_SMARTCARD_READER},
 	{"DDI_NT_AV_ASYNC", DDI_NT_AV_ASYNC},
 	{"DDI_NT_AV_ISOCH", DDI_NT_AV_ISOCH},
 	{ NULL, NULL }
--- a/usr/src/uts/common/io/sata/impl/sata.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/sata/impl/sata.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2035,12 +2035,12 @@
 	}
 
 	if (vid)
-		(void) scsi_hba_prop_update_inqstring(sd, INQUIRY_VENDOR_ID,
+		(void) scsi_device_prop_update_inqstring(sd, INQUIRY_VENDOR_ID,
 		    vid, strlen(vid));
 	if (pid)
-		(void) scsi_hba_prop_update_inqstring(sd, INQUIRY_PRODUCT_ID,
+		(void) scsi_device_prop_update_inqstring(sd, INQUIRY_PRODUCT_ID,
 		    pid, strlen(pid));
-	(void) scsi_hba_prop_update_inqstring(sd, INQUIRY_REVISION_ID,
+	(void) scsi_device_prop_update_inqstring(sd, INQUIRY_REVISION_ID,
 	    fw, strlen(fw));
 
 	return (DDI_SUCCESS);
@@ -11619,8 +11619,10 @@
 
 	mutex_enter(&SATA_CPORT_MUTEX(sata_hba_inst, cport));
 	pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport);
-	if (pmultinfo == NULL)
-		return;
+	if (pmultinfo == NULL) {
+		mutex_exit(&SATA_CPORT_MUTEX(sata_hba_inst, cport));
+		return;
+	}
 
 	gscr0 = pmultinfo->pmult_gscr.gscr0;
 	gscr1 = pmultinfo->pmult_gscr.gscr1;
--- a/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi.c	Mon Oct 05 09:17:26 2009 -0400
@@ -586,10 +586,11 @@
 			    ihp->hba_isid[5]);
 
 			if (ddi_prop_update_string(DDI_DEV_T_NONE, dip,
-			    "initiator-port", init_port_name) !=
+			    SCSI_ADDR_PROP_INITIATOR_PORT, init_port_name) !=
 			    DDI_PROP_SUCCESS) {
 				cmn_err(CE_WARN, "iscsi_attach: Creating "
-				    "initiator-port property on iSCSI "
+				    SCSI_ADDR_PROP_INITIATOR_PORT
+				    " property on iSCSI "
 				    "HBA(%s) with dip(%d) Failed",
 				    (char *)ihp->hba_name,
 				    ddi_get_instance(dip));
@@ -1758,10 +1759,10 @@
 		    ihp->hba_isid[5]);
 
 		if (ddi_prop_update_string(DDI_DEV_T_NONE,
-		    ihp->hba_dip, "initiator-port",
+		    ihp->hba_dip, SCSI_ADDR_PROP_INITIATOR_PORT,
 		    init_port_name) != DDI_PROP_SUCCESS) {
 			cmn_err(CE_WARN, "iscsi_ioctl: Updating "
-			    "initiator-port property on iSCSI "
+			    SCSI_ADDR_PROP_INITIATOR_PORT " property on iSCSI "
 			    "HBA(%s) with dip(%d) Failed",
 			    (char *)ihp->hba_name,
 			    ddi_get_instance(ihp->hba_dip));
@@ -4709,10 +4710,11 @@
 		    ilp->lun_sess->sess_name, ilp->lun_sess->sess_tpgt_conf);
 	}
 
-	if (mdi_prop_update_string(pip, "target-port",
-	    target_port_name) != DDI_PROP_SUCCESS) {
-		cmn_err(CE_WARN, "iscsi_virt_lun_init: Creating 'target-port' "
-		"property on Path(%p) for Target(%s), Lun(%d) Failed",
+	if (mdi_prop_update_string(pip,
+	    SCSI_ADDR_PROP_TARGET_PORT, target_port_name) != DDI_PROP_SUCCESS) {
+		cmn_err(CE_WARN, "iscsi_virt_lun_init: Creating '"
+		    SCSI_ADDR_PROP_TARGET_PORT "' property on Path(%p) "
+		    "for Target(%s), Lun(%d) Failed",
 		    (void *)pip, ilp->lun_sess->sess_name, ilp->lun_num);
 	}
 
@@ -4825,10 +4827,10 @@
 	}
 
 	if (ddi_prop_update_string(DDI_DEV_T_NONE, lun_dip,
-	    "target-port", target_port_name) != DDI_PROP_SUCCESS) {
-		cmn_err(CE_WARN, "iscsi_phys_lun_init: Creating 'target-port' "
-		    "property on Target(%s), Lun(%d) Failed",
-		    ilp->lun_sess->sess_name, ilp->lun_num);
+	    SCSI_ADDR_PROP_TARGET_PORT, target_port_name) != DDI_PROP_SUCCESS) {
+		cmn_err(CE_WARN, "iscsi_phys_lun_init: Creating '"
+		    SCSI_ADDR_PROP_TARGET_PORT "' property on Target(%s), "
+		    "Lun(%d) Failed", ilp->lun_sess->sess_name, ilp->lun_num);
 	}
 
 	return (rtn);
--- a/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c	Mon Oct 05 09:17:26 2009 -0400
@@ -1242,7 +1242,7 @@
 	}
 	hba_attach_setup++;
 
-	mpt->m_smptran = sas_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);
+	mpt->m_smptran = sas_hba_tran_alloc(dip);
 	ASSERT(mpt->m_smptran != NULL);
 	mpt->m_smptran->tran_hba_private = mpt;
 	mpt->m_smptran->tran_smp_start = mptsas_smp_start;
@@ -2664,7 +2664,7 @@
 	    LUN_PROP, 0);
 
 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, lun_dip, DDI_PROP_DONTPASS,
-	    "target-port", &sas_wwn) == DDI_PROP_SUCCESS) {
+	    SCSI_ADDR_PROP_TARGET_PORT, &sas_wwn) == DDI_PROP_SUCCESS) {
 		/*
 		 * Stick in the address of the form "wWWN,LUN"
 		 */
@@ -2748,8 +2748,8 @@
 			return (DDI_FAILURE);
 		}
 
-		if (mdi_prop_lookup_string(pip, "target-port", &psas_wwn) ==
-		    MDI_SUCCESS) {
+		if (mdi_prop_lookup_string(pip, SCSI_ADDR_PROP_TARGET_PORT,
+		    &psas_wwn) == MDI_SUCCESS) {
 			if (scsi_wwnstr_to_wwn(psas_wwn, &sas_wwn)) {
 				sas_wwn = 0;
 			}
@@ -2759,7 +2759,7 @@
 		lun = ddi_prop_get_int(DDI_DEV_T_ANY, tgt_dip,
 		    DDI_PROP_DONTPASS, LUN_PROP, 0);
 		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, tgt_dip,
-		    DDI_PROP_DONTPASS, "target-port", &psas_wwn) ==
+		    DDI_PROP_DONTPASS, SCSI_ADDR_PROP_TARGET_PORT, &psas_wwn) ==
 		    DDI_PROP_SUCCESS) {
 			if (scsi_wwnstr_to_wwn(psas_wwn, &sas_wwn)) {
 				sas_wwn = 0;
@@ -2860,12 +2860,12 @@
 		 * override SCSA "inquiry-*" properties
 		 */
 		if (vid)
-			(void) scsi_hba_prop_update_inqstring(sd,
+			(void) scsi_device_prop_update_inqstring(sd,
 			    INQUIRY_VENDOR_ID, vid, strlen(vid));
 		if (pid)
-			(void) scsi_hba_prop_update_inqstring(sd,
+			(void) scsi_device_prop_update_inqstring(sd,
 			    INQUIRY_PRODUCT_ID, pid, strlen(pid));
-		(void) scsi_hba_prop_update_inqstring(sd,
+		(void) scsi_device_prop_update_inqstring(sd,
 		    INQUIRY_REVISION_ID, fw, strlen(fw));
 
 		if (inq89 != NULL) {
@@ -12295,8 +12295,8 @@
 			mdi_rtn = MDI_FAILURE;
 			goto virt_create_done;
 		}
-		if (sas_wwn && (mdi_prop_update_string(*pip, "target-port",
-		    wwn_str) != DDI_PROP_SUCCESS)) {
+		if (sas_wwn && (mdi_prop_update_string(*pip,
+		    SCSI_ADDR_PROP_TARGET_PORT, wwn_str) != DDI_PROP_SUCCESS)) {
 			mptsas_log(mpt, CE_WARN, "mptsas driver unable to "
 			    "create property for target %d lun %d "
 			    "(target-port)", target, lun);
@@ -12449,7 +12449,7 @@
 		wwn_str = kmem_zalloc(MPTSAS_WWN_STRLEN, KM_SLEEP);
 		(void) sprintf(wwn_str, "%016"PRIx64, sas_wwn);
 		if (sas_wwn && ndi_prop_update_string(DDI_DEV_T_NONE,
-		    *lun_dip, "target-port", wwn_str)
+		    *lun_dip, SCSI_ADDR_PROP_TARGET_PORT, wwn_str)
 		    != DDI_PROP_SUCCESS) {
 			mptsas_log(mpt, CE_WARN, "mptsas unable to "
 			    "create property for SAS target %d lun %d "
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs.conf	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,59 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# As a pHCI driver, pmcs must specify the vHCI class it belongs to (scsi_vhci).
+#
+ddi-vhci-class="scsi_vhci";
+
+#
+# By default, MPxIO will be enabled on all pmcs controllers. To disable MPxIO
+# for all pmcs controllers, set:
+#
+#mpxio-disable="yes";
+
+#
+# You can disable MPxIO on a per-HBA basis. Per port settings override the
+# global setting for the specified ports. To disable MPxIO for the controller
+# whose unit-address is 0 and whose parent is /pci@0/pci10de,5d@e, set:
+#
+#name="pmcs" parent="/pci@0/pci10de,5d@e" unit-address="0" mpxio-disable="yes";
+
+#
+# Global debug settings may be set using the 'pmcs-debug-mask' property.
+# Any combination of values may be set according to the following:
+#
+# 0x0001 - Basic info; shouldn't print anything during normal operation
+# 0x0002 - Small amount of I/O information during normal operation
+# 0x0004 - Much more info during I/O; will impact performance
+# 0x0008 - Very verbose at all times; definitely a performance impact
+# 0x0010 - Debug information with regard to discovery/configuration
+# 0x0020 - Debug information with regard to iport
+# 0x0040 - Debug information with regard to map
+# 0x0080 - Report on SCSI underflow status
+# 0x0100 - Report SCSI status for every command
+# 0x0200 - PHY lock/unlock debug (very noisy)
+# 0x0400 - Debug information with regard to device state
+#
+pmcs-debug-mask=0x71;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs8001fw.version	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,24 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+PMCS_FW_VERSION=0x01080100
+PMCS_FW_VERSION_STRING="1.8.01 Released"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_attach.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,3024 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#include <sys/scsi/adapters/pmcs/pmcs.h>
+
+#define	PMCS_DRIVER_VERSION	"pmcs HBA device driver"
+
+static	char	*pmcs_driver_rev = PMCS_DRIVER_VERSION;
+
+/*
+ * Non-DDI Compliant stuff
+ */
+extern char hw_serial[];
+
+/*
+ * Global driver data
+ */
+void *pmcs_softc_state = NULL;
+void *pmcs_iport_softstate = NULL;
+
+/*
+ * Tracing and Logging info
+ */
+pmcs_tbuf_t *pmcs_tbuf = NULL;
+uint32_t pmcs_tbuf_num_elems = 0;
+pmcs_tbuf_t *pmcs_tbuf_ptr;
+uint32_t pmcs_tbuf_idx = 0;
+boolean_t pmcs_tbuf_wrap = B_FALSE;
+static kmutex_t pmcs_trace_lock;
+
+/*
+ * If pmcs_force_syslog value is non-zero, all messages put in the trace log
+ * will also be sent to system log.
+ */
+int pmcs_force_syslog = 0;
+int pmcs_console = 0;
+
+/*
+ * External References
+ */
+extern int ncpus_online;
+
+/*
+ * Local static data
+ */
+static int fwlog_level = 3;
+static int physpeed = PHY_LINK_ALL;
+static int phymode = PHY_LM_AUTO;
+static int block_mask = 0;
+static int phymap_usec = 3 * MICROSEC;
+static int iportmap_usec = 2 * MICROSEC;
+
+#ifdef DEBUG
+static int debug_mask = 1;
+#else
+static int debug_mask = 0;
+#endif
+
+#ifdef DISABLE_MSIX
+static int disable_msix = 1;
+#else
+static int disable_msix = 0;
+#endif
+
+#ifdef DISABLE_MSI
+static int disable_msi = 1;
+#else
+static int disable_msi = 0;
+#endif
+
+static uint16_t maxqdepth = 0xfffe;
+
+/*
+ * Local prototypes
+ */
+static int pmcs_attach(dev_info_t *, ddi_attach_cmd_t);
+static int pmcs_detach(dev_info_t *, ddi_detach_cmd_t);
+static int pmcs_unattach(pmcs_hw_t *);
+static int pmcs_iport_unattach(pmcs_iport_t *);
+static int pmcs_add_more_chunks(pmcs_hw_t *, unsigned long);
+static void pmcs_watchdog(void *);
+static int pmcs_setup_intr(pmcs_hw_t *);
+static int pmcs_teardown_intr(pmcs_hw_t *);
+
+static uint_t pmcs_nonio_ix(caddr_t, caddr_t);
+static uint_t pmcs_general_ix(caddr_t, caddr_t);
+static uint_t pmcs_event_ix(caddr_t, caddr_t);
+static uint_t pmcs_iodone_ix(caddr_t, caddr_t);
+static uint_t pmcs_fatal_ix(caddr_t, caddr_t);
+static uint_t pmcs_all_intr(caddr_t, caddr_t);
+static int pmcs_quiesce(dev_info_t *dip);
+static boolean_t pmcs_fabricate_wwid(pmcs_hw_t *);
+
+static void pmcs_create_phy_stats(pmcs_iport_t *);
+int pmcs_update_phy_stats(kstat_t *, int);
+static void pmcs_destroy_phy_stats(pmcs_iport_t *);
+
+static void pmcs_fm_fini(pmcs_hw_t *pwp);
+static void pmcs_fm_init(pmcs_hw_t *pwp);
+static int pmcs_fm_error_cb(dev_info_t *dip,
+    ddi_fm_error_t *err, const void *impl_data);
+
+/*
+ * Local configuration data
+ */
+static struct dev_ops pmcs_ops = {
+	DEVO_REV,		/* devo_rev, */
+	0,			/* refcnt */
+	ddi_no_info,		/* info */
+	nulldev,		/* identify */
+	nulldev,		/* probe */
+	pmcs_attach,		/* attach */
+	pmcs_detach,		/* detach */
+	nodev,			/* reset */
+	NULL,			/* driver operations */
+	NULL,			/* bus operations */
+	ddi_power,		/* power management */
+	pmcs_quiesce		/* quiesce */
+};
+
+static struct modldrv modldrv = {
+	&mod_driverops,
+	PMCS_DRIVER_VERSION,
+	&pmcs_ops,	/* driver ops */
+};
+static struct modlinkage modlinkage = {
+	MODREV_1, &modldrv, NULL
+};
+
+const ddi_dma_attr_t pmcs_dattr = {
+	DMA_ATTR_V0,			/* dma_attr version	*/
+	0x0000000000000000ull,		/* dma_attr_addr_lo	*/
+	0xFFFFFFFFFFFFFFFFull,		/* dma_attr_addr_hi	*/
+	0x00000000FFFFFFFFull,		/* dma_attr_count_max	*/
+	0x0000000000000001ull,		/* dma_attr_align	*/
+	0x00000078,			/* dma_attr_burstsizes	*/
+	0x00000001,			/* dma_attr_minxfer	*/
+	0x00000000FFFFFFFFull,		/* dma_attr_maxxfer	*/
+	0x00000000FFFFFFFFull,		/* dma_attr_seg		*/
+	1,				/* dma_attr_sgllen 	*/
+	512,				/* dma_attr_granular 	*/
+	0				/* dma_attr_flags 	*/
+};
+
+static ddi_device_acc_attr_t rattr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_STRUCTURE_LE_ACC,
+	DDI_STRICTORDER_ACC,
+	DDI_DEFAULT_ACC
+};
+
+
+/*
+ * Attach/Detach functions
+ */
+
+int
+_init(void)
+{
+	int ret;
+
+	ret = ddi_soft_state_init(&pmcs_softc_state, sizeof (pmcs_hw_t), 1);
+	if (ret != 0) {
+		cmn_err(CE_WARN, "?soft state init failed for pmcs");
+		return (ret);
+	}
+
+	if ((ret = scsi_hba_init(&modlinkage)) != 0) {
+		cmn_err(CE_WARN, "?scsi_hba_init failed for pmcs");
+		ddi_soft_state_fini(&pmcs_softc_state);
+		return (ret);
+	}
+
+	/*
+	 * Allocate soft state for iports
+	 */
+	ret = ddi_soft_state_init(&pmcs_iport_softstate,
+	    sizeof (pmcs_iport_t), 2);
+	if (ret != 0) {
+		cmn_err(CE_WARN, "?iport soft state init failed for pmcs");
+		ddi_soft_state_fini(&pmcs_softc_state);
+		return (ret);
+	}
+
+	ret = mod_install(&modlinkage);
+	if (ret != 0) {
+		cmn_err(CE_WARN, "?mod_install failed for pmcs (%d)", ret);
+		scsi_hba_fini(&modlinkage);
+		ddi_soft_state_fini(&pmcs_iport_softstate);
+		ddi_soft_state_fini(&pmcs_softc_state);
+		return (ret);
+	}
+
+	/* Initialize the global trace lock */
+	mutex_init(&pmcs_trace_lock, NULL, MUTEX_DRIVER, NULL);
+
+	return (0);
+}
+
+int
+_fini(void)
+{
+	int ret;
+	if ((ret = mod_remove(&modlinkage)) != 0) {
+		return (ret);
+	}
+	scsi_hba_fini(&modlinkage);
+
+	/* Free pmcs log buffer and destroy the global lock */
+	if (pmcs_tbuf) {
+		kmem_free(pmcs_tbuf,
+		    pmcs_tbuf_num_elems * sizeof (pmcs_tbuf_t));
+		pmcs_tbuf = NULL;
+	}
+	mutex_destroy(&pmcs_trace_lock);
+
+	ddi_soft_state_fini(&pmcs_iport_softstate);
+	ddi_soft_state_fini(&pmcs_softc_state);
+	return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+static int
+pmcs_iport_attach(dev_info_t *dip)
+{
+	pmcs_iport_t		*iport;
+	pmcs_hw_t		*pwp;
+	scsi_hba_tran_t		*tran;
+	void			*ua_priv = NULL;
+	char			*iport_ua;
+	char			*init_port;
+	int			hba_inst;
+	int			inst;
+
+	hba_inst = ddi_get_instance(ddi_get_parent(dip));
+	inst = ddi_get_instance(dip);
+
+	pwp = ddi_get_soft_state(pmcs_softc_state, hba_inst);
+	if (pwp == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: iport%d attach invoked with NULL parent (HBA) node)",
+		    __func__, inst);
+		return (DDI_FAILURE);
+	}
+
+	if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) {
+		return (DDI_FAILURE);
+	}
+
+	if ((iport_ua = scsi_hba_iport_unit_address(dip)) == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: invoked with NULL unit address, inst (%d)",
+		    __func__, inst);
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_soft_state_zalloc(pmcs_iport_softstate, inst) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "Failed to alloc soft state for iport %d", inst);
+		return (DDI_FAILURE);
+	}
+
+	iport = ddi_get_soft_state(pmcs_iport_softstate, inst);
+	if (iport == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "cannot get iport soft state");
+		goto iport_attach_fail1;
+	}
+
+	mutex_init(&iport->lock, NULL, MUTEX_DRIVER,
+	    DDI_INTR_PRI(pwp->intr_pri));
+	cv_init(&iport->refcnt_cv, NULL, CV_DEFAULT, NULL);
+	mutex_init(&iport->refcnt_lock, NULL, MUTEX_DRIVER,
+	    DDI_INTR_PRI(pwp->intr_pri));
+
+	/* Set some data on the iport handle */
+	iport->dip = dip;
+	iport->pwp = pwp;
+
+	/* Dup the UA into the iport handle */
+	iport->ua = strdup(iport_ua);
+
+	tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
+	tran->tran_hba_private = iport;
+
+	list_create(&iport->phys, sizeof (pmcs_phy_t),
+	    offsetof(pmcs_phy_t, list_node));
+
+	/*
+	 * If our unit address is active in the phymap, configure our
+	 * iport's phylist.
+	 */
+	mutex_enter(&iport->lock);
+	ua_priv = sas_phymap_lookup_uapriv(pwp->hss_phymap, iport->ua);
+	if (ua_priv) {
+		/* Non-NULL private data indicates the unit address is active */
+		iport->ua_state = UA_ACTIVE;
+		if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: failed to "
+			    "configure phys on iport handle (0x%p), "
+			    " unit address [%s]", __func__,
+			    (void *)iport, iport_ua);
+			mutex_exit(&iport->lock);
+			goto iport_attach_fail2;
+		}
+	} else {
+		iport->ua_state = UA_INACTIVE;
+	}
+	mutex_exit(&iport->lock);
+
+	/* Allocate string-based soft state pool for targets */
+	iport->tgt_sstate = NULL;
+	if (ddi_soft_state_bystr_init(&iport->tgt_sstate,
+	    sizeof (pmcs_xscsi_t), PMCS_TGT_SSTATE_SZ) != 0) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "cannot get iport tgt soft state");
+		goto iport_attach_fail2;
+	}
+
+	/* Create this iport's target map */
+	if (pmcs_iport_tgtmap_create(iport) == B_FALSE) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "Failed to create tgtmap on iport %d", inst);
+		goto iport_attach_fail3;
+	}
+
+	/* Set up the 'initiator-port' DDI property on this iport */
+	init_port = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
+	if (pwp->separate_ports) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: separate ports not "
+		    "supported", __func__);
+	} else {
+		/* Set initiator-port value to the HBA's base WWN */
+		(void) scsi_wwn_to_wwnstr(pwp->sas_wwns[0], 1,
+		    init_port);
+	}
+	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_STRING,
+	    SCSI_ADDR_PROP_INITIATOR_PORT, init_port);
+	kmem_free(init_port, PMCS_MAX_UA_SIZE);
+
+	/* Set up a 'num-phys' DDI property for the iport node */
+	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
+	    &iport->nphy);
+
+	/* Create kstats for each of the phys in this port */
+	pmcs_create_phy_stats(iport);
+
+	/*
+	 * Insert this iport handle into our list and set
+	 * iports_attached on the HBA node.
+	 */
+	rw_enter(&pwp->iports_lock, RW_WRITER);
+	ASSERT(!list_link_active(&iport->list_node));
+	list_insert_tail(&pwp->iports, iport);
+	pwp->iports_attached = 1;
+	pwp->num_iports++;
+	rw_exit(&pwp->iports_lock);
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, "iport%d attached", inst);
+	ddi_report_dev(dip);
+	return (DDI_SUCCESS);
+
+	/* teardown and fail */
+iport_attach_fail3:
+	ddi_soft_state_bystr_fini(&iport->tgt_sstate);
+iport_attach_fail2:
+	list_destroy(&iport->phys);
+	strfree(iport->ua);
+	mutex_destroy(&iport->refcnt_lock);
+	cv_destroy(&iport->refcnt_cv);
+	mutex_destroy(&iport->lock);
+iport_attach_fail1:
+	ddi_soft_state_free(pmcs_iport_softstate, inst);
+	return (DDI_FAILURE);
+}
+
+static int
+pmcs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	scsi_hba_tran_t *tran;
+	char chiprev, *fwsupport, hw_rev[24], fw_rev[24];
+	off_t set3size;
+	int inst, i;
+	int sm_hba = 1;
+	int protocol = 0;
+	int num_phys = 0;
+	pmcs_hw_t *pwp;
+	pmcs_phy_t *phyp;
+	uint32_t num_threads;
+	char buf[64];
+
+	switch (cmd) {
+	case DDI_ATTACH:
+		break;
+
+	case DDI_PM_RESUME:
+	case DDI_RESUME:
+		tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
+		if (!tran) {
+			return (DDI_FAILURE);
+		}
+		/* No DDI_?_RESUME on iport nodes */
+		if (scsi_hba_iport_unit_address(dip) != NULL) {
+			return (DDI_SUCCESS);
+		}
+		pwp = TRAN2PMC(tran);
+		if (pwp == NULL) {
+			return (DDI_FAILURE);
+		}
+
+		mutex_enter(&pwp->lock);
+		pwp->suspended = 0;
+		if (pwp->tq) {
+			ddi_taskq_resume(pwp->tq);
+		}
+		mutex_exit(&pwp->lock);
+		return (DDI_SUCCESS);
+
+	default:
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * If this is an iport node, invoke iport attach.
+	 */
+	if (scsi_hba_iport_unit_address(dip) != NULL) {
+		return (pmcs_iport_attach(dip));
+	}
+
+	/*
+	 * From here on is attach for the HBA node
+	 */
+
+#ifdef	DEBUG
+	/*
+	 * Check to see if this unit is to be disabled.  We can't disable
+	 * on a per-iport node.  It's either the entire HBA or nothing.
+	 */
+	(void) snprintf(buf, sizeof (buf),
+	    "disable-instance-%d", ddi_get_instance(dip));
+	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, buf, 0)) {
+		cmn_err(CE_NOTE, "pmcs%d: disabled by configuration",
+		    ddi_get_instance(dip));
+		return (DDI_FAILURE);
+	}
+#endif
+
+	/*
+	 * Allocate softstate
+	 */
+	inst = ddi_get_instance(dip);
+	if (ddi_soft_state_zalloc(pmcs_softc_state, inst) != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "pmcs%d: Failed to alloc soft state", inst);
+		return (DDI_FAILURE);
+	}
+
+	pwp = ddi_get_soft_state(pmcs_softc_state, inst);
+	if (pwp == NULL) {
+		cmn_err(CE_WARN, "pmcs%d: cannot get soft state", inst);
+		ddi_soft_state_free(pmcs_softc_state, inst);
+		return (DDI_FAILURE);
+	}
+	pwp->dip = dip;
+	STAILQ_INIT(&pwp->dq);
+	STAILQ_INIT(&pwp->cq);
+	STAILQ_INIT(&pwp->wf);
+	STAILQ_INIT(&pwp->pf);
+	/*
+	 * Create the list for iports
+	 */
+	list_create(&pwp->iports, sizeof (pmcs_iport_t),
+	    offsetof(pmcs_iport_t, list_node));
+
+	pwp->state = STATE_PROBING;
+
+	/*
+	 * Get driver.conf properties
+	 */
+	pwp->debug_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-debug-mask",
+	    debug_mask);
+	pwp->phyid_block_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phyid-block-mask",
+	    block_mask);
+	pwp->physpeed = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-physpeed", physpeed);
+	pwp->phymode = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phymode", phymode);
+	pwp->fwlog = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fwlog", fwlog_level);
+	if (pwp->fwlog > PMCS_FWLOG_MAX) {
+		pwp->fwlog = PMCS_FWLOG_MAX;
+	}
+
+	mutex_enter(&pmcs_trace_lock);
+	if (pmcs_tbuf == NULL) {
+		/* Allocate trace buffer */
+		pmcs_tbuf_num_elems = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-tbuf-num-elems",
+		    PMCS_TBUF_NUM_ELEMS_DEF);
+		if ((pmcs_tbuf_num_elems == DDI_PROP_NOT_FOUND) ||
+		    (pmcs_tbuf_num_elems == 0)) {
+			pmcs_tbuf_num_elems = PMCS_TBUF_NUM_ELEMS_DEF;
+		}
+
+		pmcs_tbuf = kmem_zalloc(pmcs_tbuf_num_elems *
+		    sizeof (pmcs_tbuf_t), KM_SLEEP);
+		pmcs_tbuf_ptr = pmcs_tbuf;
+		pmcs_tbuf_idx = 0;
+	}
+	mutex_exit(&pmcs_trace_lock);
+
+	disable_msix = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msix",
+	    disable_msix);
+	disable_msi = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msi",
+	    disable_msi);
+	maxqdepth = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-maxqdepth", maxqdepth);
+	pwp->fw_force_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fw-force-update", 0);
+	if (pwp->fw_force_update == 0) {
+		pwp->fw_disable_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+		    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+		    "pmcs-fw-disable-update", 0);
+	}
+	pwp->ioq_depth = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-num-io-qentries",
+	    PMCS_NQENTRY);
+
+	/*
+	 * Initialize FMA
+	 */
+	pwp->dev_acc_attr = pwp->reg_acc_attr = rattr;
+	pwp->iqp_dma_attr = pwp->oqp_dma_attr =
+	    pwp->regdump_dma_attr = pwp->cip_dma_attr =
+	    pwp->fwlog_dma_attr = pmcs_dattr;
+	pwp->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, pwp->dip,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "fm-capable",
+	    DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
+	    DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE);
+	pmcs_fm_init(pwp);
+
+	/*
+	 * Map registers
+	 */
+	if (pci_config_setup(dip, &pwp->pci_acc_handle)) {
+		pmcs_prt(pwp, PMCS_PRT_WARN, "pci config setup failed");
+		ddi_soft_state_free(pmcs_softc_state, inst);
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Get the size of register set 3.
+	 */
+	if (ddi_dev_regsize(dip, PMCS_REGSET_3, &set3size) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "unable to get size of register set %d", PMCS_REGSET_3);
+		pci_config_teardown(&pwp->pci_acc_handle);
+		ddi_soft_state_free(pmcs_softc_state, inst);
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Map registers
+	 */
+	pwp->reg_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
+
+	if (ddi_regs_map_setup(dip, PMCS_REGSET_0, (caddr_t *)&pwp->msg_regs,
+	    0, 0, &pwp->reg_acc_attr, &pwp->msg_acc_handle)) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "failed to map Message Unit registers");
+		pci_config_teardown(&pwp->pci_acc_handle);
+		ddi_soft_state_free(pmcs_softc_state, inst);
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_regs_map_setup(dip, PMCS_REGSET_1, (caddr_t *)&pwp->top_regs,
+	    0, 0, &pwp->reg_acc_attr, &pwp->top_acc_handle)) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "failed to map TOP registers");
+		ddi_regs_map_free(&pwp->msg_acc_handle);
+		pci_config_teardown(&pwp->pci_acc_handle);
+		ddi_soft_state_free(pmcs_softc_state, inst);
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_regs_map_setup(dip, PMCS_REGSET_2, (caddr_t *)&pwp->gsm_regs,
+	    0, 0, &pwp->reg_acc_attr, &pwp->gsm_acc_handle)) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "failed to map GSM registers");
+		ddi_regs_map_free(&pwp->top_acc_handle);
+		ddi_regs_map_free(&pwp->msg_acc_handle);
+		pci_config_teardown(&pwp->pci_acc_handle);
+		ddi_soft_state_free(pmcs_softc_state, inst);
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_regs_map_setup(dip, PMCS_REGSET_3, (caddr_t *)&pwp->mpi_regs,
+	    0, 0, &pwp->reg_acc_attr, &pwp->mpi_acc_handle)) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "failed to map MPI registers");
+		ddi_regs_map_free(&pwp->top_acc_handle);
+		ddi_regs_map_free(&pwp->gsm_acc_handle);
+		ddi_regs_map_free(&pwp->msg_acc_handle);
+		pci_config_teardown(&pwp->pci_acc_handle);
+		ddi_soft_state_free(pmcs_softc_state, inst);
+		return (DDI_FAILURE);
+	}
+	pwp->mpibar =
+	    (((5U << 2) + 0x10) << PMCS_MSGU_MPI_BAR_SHIFT) | set3size;
+
+	/*
+	 * Make sure we can support this card.
+	 */
+	pwp->chiprev = pmcs_rd_topunit(pwp, PMCS_DEVICE_REVISION);
+
+	switch (pwp->chiprev) {
+	case PMCS_PM8001_REV_A:
+	case PMCS_PM8001_REV_B:
+		pmcs_prt(pwp, PMCS_PRT_ERR,
+		    "Rev A/B Card no longer supported");
+		goto failure;
+	case PMCS_PM8001_REV_C:
+		break;
+	default:
+		pmcs_prt(pwp, PMCS_PRT_ERR,
+		    "Unknown chip revision (%d)", pwp->chiprev);
+		goto failure;
+	}
+
+	/*
+	 * Allocate DMA addressable area for Inbound and Outbound Queue indices
+	 * that the chip needs to access plus a space for scratch usage
+	 */
+	pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t);
+	if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pwp->cip_acchdls,
+	    &pwp->cip_handles, ptob(1), (caddr_t *)&pwp->cip,
+	    &pwp->ciaddr) == B_FALSE) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "Failed to setup DMA for index/scratch");
+		goto failure;
+	}
+
+	bzero(pwp->cip, ptob(1));
+	pwp->scratch = &pwp->cip[PMCS_INDICES_SIZE];
+	pwp->scratch_dma = pwp->ciaddr + PMCS_INDICES_SIZE;
+
+	/*
+	 * Allocate DMA S/G list chunks
+	 */
+	(void) pmcs_add_more_chunks(pwp, ptob(1) * PMCS_MIN_CHUNK_PAGES);
+
+	/*
+	 * Allocate a DMA addressable area for the firmware log (if needed)
+	 */
+	if (pwp->fwlog) {
+		/*
+		 * Align to event log header and entry size
+		 */
+		pwp->fwlog_dma_attr.dma_attr_align = 32;
+		if (pmcs_dma_setup(pwp, &pwp->fwlog_dma_attr,
+		    &pwp->fwlog_acchdl,
+		    &pwp->fwlog_hndl, PMCS_FWLOG_SIZE,
+		    (caddr_t *)&pwp->fwlogp,
+		    &pwp->fwaddr) == B_FALSE) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "Failed to setup DMA for fwlog area");
+			pwp->fwlog = 0;
+		} else {
+			bzero(pwp->fwlogp, PMCS_FWLOG_SIZE);
+		}
+	}
+
+	if (pwp->flash_chunk_addr == NULL) {
+		pwp->regdump_dma_attr.dma_attr_align = PMCS_FLASH_CHUNK_SIZE;
+		if (pmcs_dma_setup(pwp, &pwp->regdump_dma_attr,
+		    &pwp->regdump_acchdl,
+		    &pwp->regdump_hndl, PMCS_FLASH_CHUNK_SIZE,
+		    (caddr_t *)&pwp->flash_chunkp, &pwp->flash_chunk_addr) ==
+		    B_FALSE) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "Failed to setup DMA for register dump area");
+			goto failure;
+		}
+		bzero(pwp->flash_chunkp, PMCS_FLASH_CHUNK_SIZE);
+	}
+
+	/*
+	 * More bits of local initialization...
+	 */
+	pwp->tq = ddi_taskq_create(dip, "_tq", 4, TASKQ_DEFAULTPRI, 0);
+	if (pwp->tq == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "unable to create worker taskq");
+		goto failure;
+	}
+
+	/*
+	 * Cache of structures for dealing with I/O completion callbacks.
+	 */
+	(void) snprintf(buf, sizeof (buf), "pmcs_iocomp_cb_cache%d", inst);
+	pwp->iocomp_cb_cache = kmem_cache_create(buf,
+	    sizeof (pmcs_iocomp_cb_t), 16, NULL, NULL, NULL, NULL, NULL, 0);
+
+	/*
+	 * Cache of PHY structures
+	 */
+	(void) snprintf(buf, sizeof (buf), "pmcs_phy_cache%d", inst);
+	pwp->phy_cache = kmem_cache_create(buf, sizeof (pmcs_phy_t), 8,
+	    pmcs_phy_constructor, pmcs_phy_destructor, NULL, (void *)pwp,
+	    NULL, 0);
+
+	/*
+	 * Allocate space for the I/O completion threads
+	 */
+	num_threads = ncpus_online;
+	if (num_threads > PMCS_MAX_CQ_THREADS) {
+		num_threads = PMCS_MAX_CQ_THREADS;
+	}
+
+	pwp->cq_info.cq_thr_info = kmem_zalloc(sizeof (pmcs_cq_thr_info_t) *
+	    num_threads, KM_SLEEP);
+	pwp->cq_info.cq_threads = num_threads;
+	pwp->cq_info.cq_next_disp_thr = 0;
+	pwp->cq_info.cq_stop = B_FALSE;
+
+	/*
+	 * Set the quantum value in clock ticks for the I/O interrupt
+	 * coalescing timer.
+	 */
+	pwp->io_intr_coal.quantum = drv_usectohz(PMCS_QUANTUM_TIME_USECS);
+
+	/*
+	 * We have a delicate dance here. We need to set up
+	 * interrupts so we know how to set up some OQC
+	 * tables. However, while we're setting up table
+	 * access, we may need to flash new firmware and
+	 * reset the card, which will take some finessing.
+	 */
+
+	/*
+	 * Set up interrupts here.
+	 */
+	switch (pmcs_setup_intr(pwp)) {
+	case 0:
+		break;
+	case EIO:
+		pwp->stuck = 1;
+		/* FALLTHROUGH */
+	default:
+		goto failure;
+	}
+
+	/*
+	 * Set these up now becuase they are used to initialize the OQC tables.
+	 *
+	 * If we have MSI or MSI-X interrupts set up and we have enough
+	 * vectors for each OQ, the Outbound Queue vectors can all be the
+	 * same as the appropriate interrupt routine will have been called
+	 * and the doorbell register automatically cleared.
+	 * This keeps us from having to check the Outbound Doorbell register
+	 * when the routines for these interrupts are called.
+	 *
+	 * If we have Legacy INT-X interrupts set up or we didn't have enough
+	 * MSI/MSI-X vectors to uniquely identify each OQ, we point these
+	 * vectors to the bits we would like to have set in the Outbound
+	 * Doorbell register because pmcs_all_intr will read the doorbell
+	 * register to find out why we have an interrupt and write the
+	 * corresponding 'clear' bit for that interrupt.
+	 */
+
+	switch (pwp->intr_cnt) {
+	case 1:
+		/*
+		 * Only one vector, so we must check all OQs for MSI.  For
+		 * INT-X, there's only one vector anyway, so we can just
+		 * use the outbound queue bits to keep from having to
+		 * check each queue for each interrupt.
+		 */
+		if (pwp->int_type == PMCS_INT_FIXED) {
+			pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
+			pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
+			pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS;
+		} else {
+			pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
+			pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_IODONE;
+			pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_IODONE;
+		}
+		break;
+	case 2:
+		/* With 2, we can at least isolate IODONE */
+		pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
+		pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
+		pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_GENERAL;
+		break;
+	case 4:
+		/* With 4 vectors, everybody gets one */
+		pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE;
+		pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL;
+		pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS;
+		break;
+	}
+
+	/*
+	 * Do the first part of setup
+	 */
+	if (pmcs_setup(pwp)) {
+		goto failure;
+	}
+	pmcs_report_fwversion(pwp);
+
+	/*
+	 * Now do some additonal allocations based upon information
+	 * gathered during MPI setup.
+	 */
+	pwp->root_phys = kmem_zalloc(pwp->nphy * sizeof (pmcs_phy_t), KM_SLEEP);
+	ASSERT(pwp->nphy < SAS2_PHYNUM_MAX);
+	phyp = pwp->root_phys;
+	for (i = 0; i < pwp->nphy; i++) {
+		if (i < pwp->nphy-1) {
+			phyp->sibling = (phyp + 1);
+		}
+		mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER,
+		    DDI_INTR_PRI(pwp->intr_pri));
+		phyp->phynum = i & SAS2_PHYNUM_MASK;
+		pmcs_phy_name(pwp, phyp, phyp->path, sizeof (phyp->path));
+		phyp->pwp = pwp;
+		phyp->device_id = PMCS_INVALID_DEVICE_ID;
+		phyp++;
+	}
+
+	pwp->work = kmem_zalloc(pwp->max_cmd * sizeof (pmcwork_t), KM_SLEEP);
+	for (i = 0; i < pwp->max_cmd - 1; i++) {
+		pmcwork_t *pwrk = &pwp->work[i];
+		mutex_init(&pwrk->lock, NULL, MUTEX_DRIVER,
+		    DDI_INTR_PRI(pwp->intr_pri));
+		cv_init(&pwrk->sleep_cv, NULL, CV_DRIVER, NULL);
+		STAILQ_INSERT_TAIL(&pwp->wf, pwrk, next);
+
+	}
+	pwp->targets = (pmcs_xscsi_t **)
+	    kmem_zalloc(pwp->max_dev * sizeof (pmcs_xscsi_t *), KM_SLEEP);
+
+	pwp->iqpt = (pmcs_iqp_trace_t *)
+	    kmem_zalloc(sizeof (pmcs_iqp_trace_t), KM_SLEEP);
+	pwp->iqpt->head = kmem_zalloc(PMCS_IQP_TRACE_BUFFER_SIZE, KM_SLEEP);
+	pwp->iqpt->curpos = pwp->iqpt->head;
+	pwp->iqpt->size_left = PMCS_IQP_TRACE_BUFFER_SIZE;
+
+	/*
+	 * Start MPI communication.
+	 */
+	if (pmcs_start_mpi(pwp)) {
+		if (pmcs_soft_reset(pwp, B_FALSE)) {
+			goto failure;
+		}
+	}
+
+	/*
+	 * Do some initial acceptance tests.
+	 * This tests interrupts and queues.
+	 */
+	if (pmcs_echo_test(pwp)) {
+		goto failure;
+	}
+
+	/* Read VPD - if it exists */
+	if (pmcs_get_nvmd(pwp, PMCS_NVMD_VPD, PMCIN_NVMD_VPD, 0, NULL, 0)) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Unable to read VPD: "
+		    "attempting to fabricate", __func__);
+		/*
+		 * When we release, this must goto failure and the call
+		 * to pmcs_fabricate_wwid is removed.
+		 */
+		/* goto failure; */
+		if (!pmcs_fabricate_wwid(pwp)) {
+			goto failure;
+		}
+	}
+
+	/*
+	 * We're now officially running
+	 */
+	pwp->state = STATE_RUNNING;
+
+	/*
+	 * Check firmware versions and load new firmware
+	 * if needed and reset.
+	 */
+	if (pmcs_firmware_update(pwp)) {
+		pmcs_prt(pwp, PMCS_PRT_WARN, "%s: Firmware update failed",
+		    __func__);
+		goto failure;
+	}
+
+	/*
+	 * Create completion threads.
+	 */
+	for (i = 0; i < pwp->cq_info.cq_threads; i++) {
+		pwp->cq_info.cq_thr_info[i].cq_pwp = pwp;
+		pwp->cq_info.cq_thr_info[i].cq_thread =
+		    thread_create(NULL, 0, pmcs_scsa_cq_run,
+		    &pwp->cq_info.cq_thr_info[i], 0, &p0, TS_RUN, minclsyspri);
+	}
+
+	/*
+	 * Create one thread to deal with the updating of the interrupt
+	 * coalescing timer.
+	 */
+	pwp->ict_thread = thread_create(NULL, 0, pmcs_check_intr_coal,
+	    pwp, 0, &p0, TS_RUN, minclsyspri);
+
+	/*
+	 * Kick off the watchdog
+	 */
+	pwp->wdhandle = timeout(pmcs_watchdog, pwp,
+	    drv_usectohz(PMCS_WATCH_INTERVAL));
+	/*
+	 * Do the SCSI attachment code (before starting phys)
+	 */
+	if (pmcs_scsa_init(pwp, &pmcs_dattr)) {
+		goto failure;
+	}
+	pwp->hba_attached = 1;
+
+	/*
+	 * Initialize the rwlock for the iport elements.
+	 */
+	rw_init(&pwp->iports_lock, NULL, RW_DRIVER, NULL);
+
+	/* Check all acc & dma handles allocated in attach */
+	if (pmcs_check_acc_dma_handle(pwp)) {
+		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
+		goto failure;
+	}
+
+	/*
+	 * Create the phymap for this HBA instance
+	 */
+	if (sas_phymap_create(dip, phymap_usec, PHYMAP_MODE_SIMPLE, NULL,
+	    pwp, pmcs_phymap_activate, pmcs_phymap_deactivate,
+	    &pwp->hss_phymap) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: pmcs%d phymap_create failed",
+		    __func__, inst);
+		goto failure;
+	}
+	ASSERT(pwp->hss_phymap);
+
+	/*
+	 * Create the iportmap for this HBA instance
+	 */
+	if (scsi_hba_iportmap_create(dip, iportmap_usec, pwp->nphy,
+	    &pwp->hss_iportmap) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: pmcs%d iportmap_create "
+		    "failed", __func__, inst);
+		goto failure;
+	}
+	ASSERT(pwp->hss_iportmap);
+
+	/*
+	 * Start the PHYs.
+	 */
+	if (pmcs_start_phys(pwp)) {
+		goto failure;
+	}
+
+	/*
+	 * From this point on, we can't fail.
+	 */
+	ddi_report_dev(dip);
+
+	/* SM-HBA */
+	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SMHBA_SUPPORTED,
+	    &sm_hba);
+
+	/* SM-HBA */
+	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_DRV_VERSION,
+	    pmcs_driver_rev);
+
+	/* SM-HBA */
+	chiprev = 'A' + pwp->chiprev;
+	(void) snprintf(hw_rev, 2, "%s", &chiprev);
+	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_HWARE_VERSION,
+	    hw_rev);
+
+	/* SM-HBA */
+	switch (PMCS_FW_TYPE(pwp)) {
+	case PMCS_FW_TYPE_RELEASED:
+		fwsupport = "Released";
+		break;
+	case PMCS_FW_TYPE_DEVELOPMENT:
+		fwsupport = "Development";
+		break;
+	case PMCS_FW_TYPE_ALPHA:
+		fwsupport = "Alpha";
+		break;
+	case PMCS_FW_TYPE_BETA:
+		fwsupport = "Beta";
+		break;
+	default:
+		fwsupport = "Special";
+		break;
+	}
+	(void) snprintf(fw_rev, sizeof (fw_rev), "%x.%x.%x %s",
+	    PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), PMCS_FW_MICRO(pwp),
+	    fwsupport);
+	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_FWARE_VERSION,
+	    fw_rev);
+
+	/* SM-HBA */
+	num_phys = pwp->nphy;
+	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_NUM_PHYS_HBA,
+	    &num_phys);
+
+	/* SM-HBA */
+	protocol = SAS_SSP_SUPPORT | SAS_SATA_SUPPORT | SAS_SMP_SUPPORT;
+	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SUPPORTED_PROTOCOL,
+	    &protocol);
+
+	return (DDI_SUCCESS);
+
+failure:
+	if (pmcs_unattach(pwp)) {
+		pwp->stuck = 1;
+	}
+	return (DDI_FAILURE);
+}
+
+int
+pmcs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	int inst = ddi_get_instance(dip);
+	pmcs_iport_t	*iport = NULL;
+	pmcs_hw_t	*pwp = NULL;
+	scsi_hba_tran_t	*tran;
+
+	if (scsi_hba_iport_unit_address(dip) != NULL) {
+		/* iport node */
+		iport = ddi_get_soft_state(pmcs_iport_softstate, inst);
+		ASSERT(iport);
+		if (iport == NULL) {
+			return (DDI_FAILURE);
+		}
+		pwp = iport->pwp;
+	} else {
+		/* hba node */
+		pwp = (pmcs_hw_t *)ddi_get_soft_state(pmcs_softc_state, inst);
+		ASSERT(pwp);
+		if (pwp == NULL) {
+			return (DDI_FAILURE);
+		}
+	}
+
+	switch (cmd) {
+	case DDI_DETACH:
+		if (iport) {
+			/* iport detach */
+			if (pmcs_iport_unattach(iport)) {
+				return (DDI_FAILURE);
+			}
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "iport%d detached", inst);
+			return (DDI_SUCCESS);
+		} else {
+			/* HBA detach */
+			if (pmcs_unattach(pwp)) {
+				return (DDI_FAILURE);
+			}
+			return (DDI_SUCCESS);
+		}
+
+	case DDI_SUSPEND:
+	case DDI_PM_SUSPEND:
+		/* No DDI_SUSPEND on iport nodes */
+		if (iport) {
+			return (DDI_SUCCESS);
+		}
+
+		if (pwp->stuck) {
+			return (DDI_FAILURE);
+		}
+		tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
+		if (!tran) {
+			return (DDI_FAILURE);
+		}
+
+		pwp = TRAN2PMC(tran);
+		if (pwp == NULL) {
+			return (DDI_FAILURE);
+		}
+		mutex_enter(&pwp->lock);
+		if (pwp->tq) {
+			ddi_taskq_suspend(pwp->tq);
+		}
+		pwp->suspended = 1;
+		mutex_exit(&pwp->lock);
+		pmcs_prt(pwp, PMCS_PRT_INFO, "PMC8X6G suspending");
+		return (DDI_SUCCESS);
+
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+static int
+pmcs_iport_unattach(pmcs_iport_t *iport)
+{
+	pmcs_hw_t	*pwp = iport->pwp;
+
+	/*
+	 * First, check if there are still any configured targets on this
+	 * iport.  If so, we fail detach.
+	 */
+	if (pmcs_iport_has_targets(pwp, iport)) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, "iport%d detach failure: "
+		    "iport has targets (luns)", ddi_get_instance(iport->dip));
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Remove this iport from our list if it is inactive in the phymap.
+	 */
+	rw_enter(&pwp->iports_lock, RW_WRITER);
+	mutex_enter(&iport->lock);
+
+	if (iport->ua_state == UA_ACTIVE) {
+		mutex_exit(&iport->lock);
+		rw_exit(&pwp->iports_lock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, "iport%d detach failure: "
+		    "iport unit address active in phymap",
+		    ddi_get_instance(iport->dip));
+		return (DDI_FAILURE);
+	}
+
+	/* If it's our only iport, clear iports_attached */
+	ASSERT(pwp->num_iports >= 1);
+	if (--pwp->num_iports == 0) {
+		pwp->iports_attached = 0;
+	}
+
+	ASSERT(list_link_active(&iport->list_node));
+	list_remove(&pwp->iports, iport);
+	rw_exit(&pwp->iports_lock);
+
+	/*
+	 * We have removed the iport handle from the HBA's iports list,
+	 * there will be no new references to it. Two things must be
+	 * guarded against here.  First, we could have PHY up events,
+	 * adding themselves to the iport->phys list and grabbing ref's
+	 * on our iport handle.  Second, we could have existing references
+	 * to this iport handle from a point in time prior to the list
+	 * removal above.
+	 *
+	 * So first, destroy the phys list. Remove any phys that have snuck
+	 * in after the phymap deactivate, dropping the refcnt accordingly.
+	 * If these PHYs are still up if and when the phymap reactivates
+	 * (i.e. when this iport reattaches), we'll populate the list with
+	 * them and bump the refcnt back up.
+	 */
+	pmcs_remove_phy_from_iport(iport, NULL);
+	ASSERT(list_is_empty(&iport->phys));
+	list_destroy(&iport->phys);
+	mutex_exit(&iport->lock);
+
+	/*
+	 * Second, wait for any other references to this iport to be
+	 * dropped, then continue teardown.
+	 */
+	mutex_enter(&iport->refcnt_lock);
+	while (iport->refcnt != 0) {
+		cv_wait(&iport->refcnt_cv, &iport->refcnt_lock);
+	}
+	mutex_exit(&iport->refcnt_lock);
+
+	/* Delete kstats */
+	pmcs_destroy_phy_stats(iport);
+
+	/* Destroy the iport target map */
+	if (pmcs_iport_tgtmap_destroy(iport) == B_FALSE) {
+		return (DDI_FAILURE);
+	}
+
+	/* Free the tgt soft state */
+	if (iport->tgt_sstate != NULL) {
+		ddi_soft_state_bystr_fini(&iport->tgt_sstate);
+	}
+
+	/* Free our unit address string */
+	strfree(iport->ua);
+
+	/* Finish teardown and free the softstate */
+	mutex_destroy(&iport->refcnt_lock);
+	ASSERT(iport->refcnt == 0);
+	cv_destroy(&iport->refcnt_cv);
+	mutex_destroy(&iport->lock);
+	ddi_soft_state_free(pmcs_iport_softstate, ddi_get_instance(iport->dip));
+
+	return (DDI_SUCCESS);
+}
+
+static int
+pmcs_unattach(pmcs_hw_t *pwp)
+{
+	int i;
+	enum pwpstate curstate;
+	pmcs_cq_thr_info_t *cqti;
+
+	/*
+	 * Tear down the interrupt infrastructure.
+	 */
+	if (pmcs_teardown_intr(pwp)) {
+		pwp->stuck = 1;
+	}
+	pwp->intr_cnt = 0;
+
+	/*
+	 * Grab a lock, if initted, to set state.
+	 */
+	if (pwp->locks_initted) {
+		mutex_enter(&pwp->lock);
+		if (pwp->state != STATE_DEAD) {
+			pwp->state = STATE_UNPROBING;
+		}
+		curstate = pwp->state;
+		mutex_exit(&pwp->lock);
+
+		/*
+		 * Stop the I/O completion threads.
+		 */
+		mutex_enter(&pwp->cq_lock);
+		pwp->cq_info.cq_stop = B_TRUE;
+		for (i = 0; i < pwp->cq_info.cq_threads; i++) {
+			if (pwp->cq_info.cq_thr_info[i].cq_thread) {
+				cqti = &pwp->cq_info.cq_thr_info[i];
+				mutex_enter(&cqti->cq_thr_lock);
+				cv_signal(&cqti->cq_cv);
+				mutex_exit(&cqti->cq_thr_lock);
+				mutex_exit(&pwp->cq_lock);
+				thread_join(cqti->cq_thread->t_did);
+				mutex_enter(&pwp->cq_lock);
+			}
+		}
+		mutex_exit(&pwp->cq_lock);
+
+		/*
+		 * Stop the interrupt coalescing timer thread
+		 */
+		if (pwp->ict_thread) {
+			mutex_enter(&pwp->ict_lock);
+			pwp->io_intr_coal.stop_thread = B_TRUE;
+			cv_signal(&pwp->ict_cv);
+			mutex_exit(&pwp->ict_lock);
+			thread_join(pwp->ict_thread->t_did);
+		}
+	} else {
+		if (pwp->state != STATE_DEAD) {
+			pwp->state = STATE_UNPROBING;
+		}
+		curstate = pwp->state;
+	}
+
+	if (&pwp->iports != NULL) {
+		/* Destroy the iports lock */
+		rw_destroy(&pwp->iports_lock);
+		/* Destroy the iports list */
+		ASSERT(list_is_empty(&pwp->iports));
+		list_destroy(&pwp->iports);
+	}
+
+	if (pwp->hss_iportmap != NULL) {
+		/* Destroy the iportmap */
+		scsi_hba_iportmap_destroy(pwp->hss_iportmap);
+	}
+
+	if (pwp->hss_phymap != NULL) {
+		/* Destroy the phymap */
+		sas_phymap_destroy(pwp->hss_phymap);
+	}
+
+	/*
+	 * Make sure that any pending watchdog won't
+	 * be called from this point on out.
+	 */
+	(void) untimeout(pwp->wdhandle);
+	/*
+	 * After the above action, the watchdog
+	 * timer that starts up the worker task
+	 * may trigger but will exit immediately
+	 * on triggering.
+	 *
+	 * Now that this is done, we can destroy
+	 * the task queue, which will wait if we're
+	 * running something on it.
+	 */
+	if (pwp->tq) {
+		ddi_taskq_destroy(pwp->tq);
+		pwp->tq = NULL;
+	}
+
+	pmcs_fm_fini(pwp);
+
+	if (pwp->hba_attached) {
+		(void) scsi_hba_detach(pwp->dip);
+		pwp->hba_attached = 0;
+	}
+
+	/*
+	 * If the chip hasn't been marked dead, shut it down now
+	 * to bring it back to a known state without attempting
+	 * a soft reset.
+	 */
+	if (curstate != STATE_DEAD && pwp->locks_initted) {
+		/*
+		 * De-register all registered devices
+		 */
+		pmcs_deregister_devices(pwp, pwp->root_phys);
+
+		/*
+		 * Stop all the phys.
+		 */
+		pmcs_stop_phys(pwp);
+
+		/*
+		 * Shut Down Message Passing
+		 */
+		(void) pmcs_stop_mpi(pwp);
+
+		/*
+		 * Reset chip
+		 */
+		(void) pmcs_soft_reset(pwp, B_FALSE);
+	}
+
+	/*
+	 * Turn off interrupts on the chip
+	 */
+	if (pwp->mpi_acc_handle) {
+		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
+	}
+
+	/* Destroy pwp's lock */
+	if (pwp->locks_initted) {
+		mutex_destroy(&pwp->lock);
+		mutex_destroy(&pwp->dma_lock);
+		mutex_destroy(&pwp->axil_lock);
+		mutex_destroy(&pwp->cq_lock);
+		mutex_destroy(&pwp->config_lock);
+		mutex_destroy(&pwp->ict_lock);
+		mutex_destroy(&pwp->wfree_lock);
+		mutex_destroy(&pwp->pfree_lock);
+		mutex_destroy(&pwp->dead_phylist_lock);
+#ifdef	DEBUG
+		mutex_destroy(&pwp->dbglock);
+#endif
+		cv_destroy(&pwp->ict_cv);
+		cv_destroy(&pwp->drain_cv);
+		pwp->locks_initted = 0;
+	}
+
+	/*
+	 * Free DMA handles and associated consistent memory
+	 */
+	if (pwp->regdump_hndl) {
+		if (ddi_dma_unbind_handle(pwp->regdump_hndl) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check failed "
+			    "at %s():%d", __func__, __LINE__);
+		}
+		ddi_dma_free_handle(&pwp->regdump_hndl);
+		ddi_dma_mem_free(&pwp->regdump_acchdl);
+		pwp->regdump_hndl = 0;
+	}
+	if (pwp->fwlog_hndl) {
+		if (ddi_dma_unbind_handle(pwp->fwlog_hndl) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check failed "
+			    "at %s():%d", __func__, __LINE__);
+		}
+		ddi_dma_free_handle(&pwp->fwlog_hndl);
+		ddi_dma_mem_free(&pwp->fwlog_acchdl);
+		pwp->fwlog_hndl = 0;
+	}
+	if (pwp->cip_handles) {
+		if (ddi_dma_unbind_handle(pwp->cip_handles) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check failed "
+			    "at %s():%d", __func__, __LINE__);
+		}
+		ddi_dma_free_handle(&pwp->cip_handles);
+		ddi_dma_mem_free(&pwp->cip_acchdls);
+		pwp->cip_handles = 0;
+	}
+	for (i = 0; i < PMCS_NOQ; i++) {
+		if (pwp->oqp_handles[i]) {
+			if (ddi_dma_unbind_handle(pwp->oqp_handles[i]) !=
+			    DDI_SUCCESS) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check "
+				    "failed at %s():%d", __func__, __LINE__);
+			}
+			ddi_dma_free_handle(&pwp->oqp_handles[i]);
+			ddi_dma_mem_free(&pwp->oqp_acchdls[i]);
+			pwp->oqp_handles[i] = 0;
+		}
+	}
+	for (i = 0; i < PMCS_NIQ; i++) {
+		if (pwp->iqp_handles[i]) {
+			if (ddi_dma_unbind_handle(pwp->iqp_handles[i]) !=
+			    DDI_SUCCESS) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check "
+				    "failed at %s():%d", __func__, __LINE__);
+			}
+			ddi_dma_free_handle(&pwp->iqp_handles[i]);
+			ddi_dma_mem_free(&pwp->iqp_acchdls[i]);
+			pwp->iqp_handles[i] = 0;
+		}
+	}
+
+	pmcs_free_dma_chunklist(pwp);
+
+	/*
+	 * Unmap registers and destroy access handles
+	 */
+	if (pwp->mpi_acc_handle) {
+		ddi_regs_map_free(&pwp->mpi_acc_handle);
+		pwp->mpi_acc_handle = 0;
+	}
+	if (pwp->top_acc_handle) {
+		ddi_regs_map_free(&pwp->top_acc_handle);
+		pwp->top_acc_handle = 0;
+	}
+	if (pwp->gsm_acc_handle) {
+		ddi_regs_map_free(&pwp->gsm_acc_handle);
+		pwp->gsm_acc_handle = 0;
+	}
+	if (pwp->msg_acc_handle) {
+		ddi_regs_map_free(&pwp->msg_acc_handle);
+		pwp->msg_acc_handle = 0;
+	}
+	if (pwp->pci_acc_handle) {
+		pci_config_teardown(&pwp->pci_acc_handle);
+		pwp->pci_acc_handle = 0;
+	}
+
+	/*
+	 * Do memory allocation cleanup.
+	 */
+	while (pwp->dma_freelist) {
+		pmcs_dmachunk_t *this = pwp->dma_freelist;
+		pwp->dma_freelist = this->nxt;
+		kmem_free(this, sizeof (pmcs_dmachunk_t));
+	}
+
+	/*
+	 * Free pools
+	 */
+	if (pwp->iocomp_cb_cache) {
+		kmem_cache_destroy(pwp->iocomp_cb_cache);
+	}
+
+	/*
+	 * Free all PHYs (at level > 0), then free the cache
+	 */
+	pmcs_free_all_phys(pwp, pwp->root_phys);
+	if (pwp->phy_cache) {
+		kmem_cache_destroy(pwp->phy_cache);
+	}
+
+	/*
+	 * Free root PHYs
+	 */
+	if (pwp->root_phys) {
+		pmcs_phy_t *phyp = pwp->root_phys;
+		for (i = 0; i < pwp->nphy; i++) {
+			mutex_destroy(&phyp->phy_lock);
+			phyp = phyp->sibling;
+		}
+		kmem_free(pwp->root_phys, pwp->nphy * sizeof (pmcs_phy_t));
+		pwp->root_phys = NULL;
+		pwp->nphy = 0;
+	}
+
+	/* Free the targets list */
+	if (pwp->targets) {
+		kmem_free(pwp->targets,
+		    sizeof (pmcs_xscsi_t *) * pwp->max_dev);
+	}
+
+	/*
+	 * Free work structures
+	 */
+
+	if (pwp->work && pwp->max_cmd) {
+		for (i = 0; i < pwp->max_cmd - 1; i++) {
+			pmcwork_t *pwrk = &pwp->work[i];
+			mutex_destroy(&pwrk->lock);
+			cv_destroy(&pwrk->sleep_cv);
+		}
+		kmem_free(pwp->work, sizeof (pmcwork_t) * pwp->max_cmd);
+		pwp->work = NULL;
+		pwp->max_cmd = 0;
+	}
+
+	/*
+	 * Do last property and SCSA cleanup
+	 */
+	if (pwp->tran) {
+		scsi_hba_tran_free(pwp->tran);
+		pwp->tran = NULL;
+	}
+	if (pwp->reset_notify_listf) {
+		scsi_hba_reset_notify_tear_down(pwp->reset_notify_listf);
+		pwp->reset_notify_listf = NULL;
+	}
+	ddi_prop_remove_all(pwp->dip);
+	if (pwp->stuck) {
+		return (-1);
+	}
+
+	/* Free register dump area if allocated */
+	if (pwp->regdumpp) {
+		kmem_free(pwp->regdumpp, PMCS_REG_DUMP_SIZE);
+		pwp->regdumpp = NULL;
+	}
+	if (pwp->iqpt && pwp->iqpt->head) {
+		kmem_free(pwp->iqpt->head, PMCS_IQP_TRACE_BUFFER_SIZE);
+		pwp->iqpt->head = pwp->iqpt->curpos = NULL;
+	}
+	if (pwp->iqpt) {
+		kmem_free(pwp->iqpt, sizeof (pmcs_iqp_trace_t));
+		pwp->iqpt = NULL;
+	}
+
+	ddi_soft_state_free(pmcs_softc_state, ddi_get_instance(pwp->dip));
+	return (0);
+}
+
+/*
+ * quiesce (9E) entry point
+ *
+ * This function is called when the system is single-threaded at high PIL
+ * with preemption disabled. Therefore, the function must not block/wait/sleep.
+ *
+ * Returns DDI_SUCCESS or DDI_FAILURE.
+ *
+ */
+static int
+pmcs_quiesce(dev_info_t *dip)
+{
+	pmcs_hw_t	*pwp;
+	scsi_hba_tran_t	*tran;
+
+	if ((tran = ddi_get_driver_private(dip)) == NULL)
+		return (DDI_SUCCESS);
+
+	/* No quiesce necessary on a per-iport basis */
+	if (scsi_hba_iport_unit_address(dip) != NULL) {
+		return (DDI_SUCCESS);
+	}
+
+	if ((pwp = TRAN2PMC(tran)) == NULL)
+		return (DDI_SUCCESS);
+
+	/* Stop MPI & Reset chip (no need to re-initialize) */
+	(void) pmcs_stop_mpi(pwp);
+	(void) pmcs_soft_reset(pwp, B_TRUE);
+
+	return (DDI_SUCCESS);
+}
+
+/*
+ * Called with xp->statlock and PHY lock and scratch acquired.
+ */
+static int
+pmcs_add_sata_device(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
+{
+	ata_identify_t *ati;
+	int result, i;
+	pmcs_phy_t *pptr;
+	uint16_t *a;
+	union {
+		uint8_t nsa[8];
+		uint16_t nsb[4];
+	} u;
+
+	/*
+	 * Safe defaults - use only if this target is brand new (i.e. doesn't
+	 * already have these settings configured)
+	 */
+	if (xp->capacity == 0) {
+		xp->capacity = (uint64_t)-1;
+		xp->ca = 1;
+		xp->qdepth = 1;
+		xp->pio = 1;
+	}
+
+	pptr = xp->phy;
+
+	/*
+	 * We only try and issue an IDENTIFY for first level
+	 * (direct attached) devices. We don't try and
+	 * set other quirks here (this will happen later,
+	 * if the device is fully configured)
+	 */
+	if (pptr->level) {
+		return (0);
+	}
+
+	mutex_exit(&xp->statlock);
+	result = pmcs_sata_identify(pwp, pptr);
+	mutex_enter(&xp->statlock);
+
+	if (result) {
+		return (result);
+	}
+	ati = pwp->scratch;
+	a = &ati->word108;
+	for (i = 0; i < 4; i++) {
+		u.nsb[i] = ddi_swap16(*a++);
+	}
+
+	/*
+	 * Check the returned data for being a valid (NAA=5) WWN.
+	 * If so, use that and override the SAS address we were
+	 * given at Link Up time.
+	 */
+	if ((u.nsa[0] >> 4) == 5) {
+		(void) memcpy(pptr->sas_address, u.nsa, 8);
+	}
+	pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: %s has SAS ADDRESS " SAS_ADDR_FMT,
+	    __func__, pptr->path, SAS_ADDR_PRT(pptr->sas_address));
+	return (0);
+}
+
+/*
+ * Called with PHY lock and target statlock held and scratch acquired
+ */
+static boolean_t
+pmcs_add_new_device(pmcs_hw_t *pwp, pmcs_xscsi_t *target)
+{
+	ASSERT(target != NULL);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: target = 0x%p",
+	    __func__, (void *) target);
+
+	switch (target->phy->dtype) {
+	case SATA:
+		if (pmcs_add_sata_device(pwp, target) != 0) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: add_sata_device failed for tgt 0x%p",
+			    __func__, (void *) target);
+			return (B_FALSE);
+		}
+		break;
+	case SAS:
+		target->qdepth = maxqdepth;
+		break;
+	case EXPANDER:
+		target->qdepth = 1;
+		break;
+	}
+
+	target->new = 0;
+	target->assigned = 1;
+	target->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
+	target->dtype = target->phy->dtype;
+
+	/*
+	 * Set the PHY's config stop time to 0.  This is one of the final
+	 * stops along the config path, so we're indicating that we
+	 * successfully configured the PHY.
+	 */
+	target->phy->config_stop = 0;
+
+	return (B_TRUE);
+}
+
+
+static void
+pmcs_rem_old_devices(pmcs_hw_t *pwp)
+{
+	pmcs_xscsi_t *xp;
+	int i;
+
+	mutex_enter(&pwp->lock);
+	for (i = 0; i < pwp->max_dev; i++) {
+		xp = pwp->targets[i];
+		if (xp == NULL) {
+			continue;
+		}
+		mutex_exit(&pwp->lock);
+
+		mutex_enter(&xp->statlock);
+		if (xp->dying && (xp->dip != NULL)) {
+			pmcs_clear_xp(pwp, xp);
+			/* Target is now gone */
+		}
+		mutex_exit(&xp->statlock);
+		mutex_enter(&pwp->lock);
+	}
+	mutex_exit(&pwp->lock);
+}
+
+
+void
+pmcs_worker(void *arg)
+{
+	pmcs_hw_t *pwp = arg;
+	ulong_t work_flags;
+
+	DTRACE_PROBE2(pmcs__worker, ulong_t, pwp->work_flags, boolean_t,
+	    pwp->config_changed);
+
+	if (pwp->state != STATE_RUNNING) {
+		return;
+	}
+
+	work_flags = atomic_swap_ulong(&pwp->work_flags, 0);
+
+	if (work_flags & PMCS_WORK_FLAG_SAS_HW_ACK) {
+		pmcs_ack_events(pwp);
+	}
+
+	if (work_flags & PMCS_WORK_FLAG_SPINUP_RELEASE) {
+		mutex_enter(&pwp->lock);
+		pmcs_spinup_release(pwp, NULL);
+		mutex_exit(&pwp->lock);
+	}
+
+	if (work_flags & PMCS_WORK_FLAG_SSP_EVT_RECOVERY) {
+		pmcs_ssp_event_recovery(pwp);
+	}
+
+	if (work_flags & PMCS_WORK_FLAG_DS_ERR_RECOVERY) {
+		pmcs_dev_state_recovery(pwp, NULL);
+	}
+
+	if (work_flags & PMCS_WORK_FLAG_REM_DEVICES) {
+		pmcs_rem_old_devices(pwp);
+	}
+
+	if (work_flags & PMCS_WORK_FLAG_DISCOVER) {
+		pmcs_discover(pwp);
+	}
+
+	if (work_flags & PMCS_WORK_FLAG_ABORT_HANDLE) {
+		if (pmcs_abort_handler(pwp)) {
+			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
+		}
+	}
+
+	if (work_flags & PMCS_WORK_FLAG_SATA_RUN) {
+		pmcs_sata_work(pwp);
+	}
+
+	if (work_flags & PMCS_WORK_FLAG_RUN_QUEUES) {
+		pmcs_scsa_wq_run(pwp);
+		mutex_enter(&pwp->lock);
+		PMCS_CQ_RUN(pwp);
+		mutex_exit(&pwp->lock);
+	}
+
+	if (work_flags & PMCS_WORK_FLAG_ADD_DMA_CHUNKS) {
+		if (pmcs_add_more_chunks(pwp,
+		    ptob(1) * PMCS_ADDTL_CHUNK_PAGES)) {
+			SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
+		} else {
+			SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
+		}
+	}
+}
+
+static int
+pmcs_add_more_chunks(pmcs_hw_t *pwp, unsigned long nsize)
+{
+	pmcs_dmachunk_t *dc;
+	unsigned long dl;
+	pmcs_chunk_t	*pchunk = NULL;
+
+	pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t);
+
+	pchunk = kmem_zalloc(sizeof (pmcs_chunk_t), KM_SLEEP);
+	if (pchunk == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "Not enough memory for DMA chunks");
+		return (-1);
+	}
+
+	if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pchunk->acc_handle,
+	    &pchunk->dma_handle, nsize, (caddr_t *)&pchunk->addrp,
+	    &pchunk->dma_addr) == B_FALSE) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Failed to setup DMA for chunks");
+		kmem_free(pchunk, sizeof (pmcs_chunk_t));
+		return (-1);
+	}
+
+	if ((pmcs_check_acc_handle(pchunk->acc_handle) != DDI_SUCCESS) ||
+	    (pmcs_check_dma_handle(pchunk->dma_handle) != DDI_SUCCESS)) {
+		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED);
+		return (-1);
+	}
+
+	bzero(pchunk->addrp, nsize);
+	dc = NULL;
+	for (dl = 0; dl < (nsize / PMCS_SGL_CHUNKSZ); dl++) {
+		pmcs_dmachunk_t *tmp;
+		tmp = kmem_alloc(sizeof (pmcs_dmachunk_t), KM_SLEEP);
+		tmp->nxt = dc;
+		dc = tmp;
+	}
+	mutex_enter(&pwp->dma_lock);
+	pmcs_idma_chunks(pwp, dc, pchunk, nsize);
+	pwp->nchunks++;
+	mutex_exit(&pwp->dma_lock);
+	return (0);
+}
+
+
+static void
+pmcs_check_commands(pmcs_hw_t *pwp)
+{
+	pmcs_cmd_t *sp;
+	size_t amt;
+	char path[32];
+	pmcwork_t *pwrk;
+	pmcs_xscsi_t *target;
+	pmcs_phy_t *phyp;
+
+	for (pwrk = pwp->work; pwrk < &pwp->work[pwp->max_cmd]; pwrk++) {
+		mutex_enter(&pwrk->lock);
+
+		/*
+		 * If the command isn't active, we can't be timing it still.
+		 * Active means the tag is not free and the state is "on chip".
+		 */
+		if (!PMCS_COMMAND_ACTIVE(pwrk)) {
+			mutex_exit(&pwrk->lock);
+			continue;
+		}
+
+		/*
+		 * No timer active for this command.
+		 */
+		if (pwrk->timer == 0) {
+			mutex_exit(&pwrk->lock);
+			continue;
+		}
+
+		/*
+		 * Knock off bits for the time interval.
+		 */
+		if (pwrk->timer >= US2WT(PMCS_WATCH_INTERVAL)) {
+			pwrk->timer -= US2WT(PMCS_WATCH_INTERVAL);
+		} else {
+			pwrk->timer = 0;
+		}
+		if (pwrk->timer > 0) {
+			mutex_exit(&pwrk->lock);
+			continue;
+		}
+
+		/*
+		 * The command has now officially timed out.
+		 * Get the path for it. If it doesn't have
+		 * a phy pointer any more, it's really dead
+		 * and can just be put back on the free list.
+		 * There should *not* be any commands associated
+		 * with it any more.
+		 */
+		if (pwrk->phy == NULL) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "dead command with gone phy being recycled");
+			ASSERT(pwrk->xp == NULL);
+			pmcs_pwork(pwp, pwrk);
+			continue;
+		}
+		amt = sizeof (path);
+		amt = min(sizeof (pwrk->phy->path), amt);
+		(void) memcpy(path, pwrk->phy->path, amt);
+
+		/*
+		 * If this is a non-SCSA command, stop here. Eventually
+		 * we might do something with non-SCSA commands here-
+		 * but so far their timeout mechanisms are handled in
+		 * the WAIT_FOR macro.
+		 */
+		if (pwrk->xp == NULL) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: non-SCSA cmd tag 0x%x timed out",
+			    path, pwrk->htag);
+			mutex_exit(&pwrk->lock);
+			continue;
+		}
+
+		sp = pwrk->arg;
+		ASSERT(sp != NULL);
+
+		/*
+		 * Mark it as timed out.
+		 */
+		CMD2PKT(sp)->pkt_reason = CMD_TIMEOUT;
+		CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
+#ifdef	DEBUG
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: SCSA cmd tag 0x%x timed out (state %x) onwire=%d",
+		    path, pwrk->htag, pwrk->state, pwrk->onwire);
+#else
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: SCSA cmd tag 0x%x timed out (state %x)",
+		    path, pwrk->htag, pwrk->state);
+#endif
+		/*
+		 * Mark the work structure as timed out.
+		 */
+		pwrk->state = PMCS_WORK_STATE_TIMED_OUT;
+		phyp = pwrk->phy;
+		target = pwrk->xp;
+		mutex_exit(&pwrk->lock);
+
+		pmcs_lock_phy(phyp);
+		mutex_enter(&target->statlock);
+
+		/*
+		 * No point attempting recovery if the device is gone
+		 */
+		if (pwrk->xp->dev_gone) {
+			mutex_exit(&target->statlock);
+			pmcs_unlock_phy(phyp);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: tgt(0x%p) is gone. Returning CMD_DEV_GONE "
+			    "for htag 0x%08x", __func__,
+			    (void *)pwrk->xp, pwrk->htag);
+			mutex_enter(&pwrk->lock);
+			if (!PMCS_COMMAND_DONE(pwrk)) {
+				/* Complete this command here */
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: "
+				    "Completing cmd (htag 0x%08x) "
+				    "anyway", __func__, pwrk->htag);
+				pwrk->dead = 1;
+				CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
+				CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
+				pmcs_complete_work_impl(pwp, pwrk, NULL, 0);
+			} else {
+				mutex_exit(&pwrk->lock);
+			}
+			continue;
+		}
+
+		/*
+		 * See if we're already waiting for device state recovery
+		 */
+		if (target->recover_wait) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+			    "%s: Target %p already in recovery", __func__,
+			    (void *)target);
+			mutex_exit(&target->statlock);
+			pmcs_unlock_phy(phyp);
+			continue;
+		}
+
+		pmcs_start_dev_state_recovery(target, phyp);
+		mutex_exit(&target->statlock);
+		pmcs_unlock_phy(phyp);
+	}
+	/*
+	 * Run any completions that may have been queued up.
+	 */
+	PMCS_CQ_RUN(pwp);
+}
+
+static void
+pmcs_watchdog(void *arg)
+{
+	pmcs_hw_t *pwp = arg;
+
+	DTRACE_PROBE2(pmcs__watchdog, ulong_t, pwp->work_flags, boolean_t,
+	    pwp->config_changed);
+
+	mutex_enter(&pwp->lock);
+
+	if (pwp->state != STATE_RUNNING) {
+		mutex_exit(&pwp->lock);
+		return;
+	}
+
+	if (atomic_cas_ulong(&pwp->work_flags, 0, 0) != 0) {
+		if (ddi_taskq_dispatch(pwp->tq, pmcs_worker, pwp,
+		    DDI_NOSLEEP) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "Could not dispatch to worker thread");
+		}
+	}
+	pwp->wdhandle = timeout(pmcs_watchdog, pwp,
+	    drv_usectohz(PMCS_WATCH_INTERVAL));
+	mutex_exit(&pwp->lock);
+	pmcs_check_commands(pwp);
+	pmcs_handle_dead_phys(pwp);
+}
+
+static int
+pmcs_remove_ihandlers(pmcs_hw_t *pwp, int icnt)
+{
+	int i, r, rslt = 0;
+	for (i = 0; i < icnt; i++) {
+		r = ddi_intr_remove_handler(pwp->ih_table[i]);
+		if (r == DDI_SUCCESS) {
+			continue;
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: unable to remove interrupt handler %d", __func__, i);
+		rslt = -1;
+		break;
+	}
+	return (rslt);
+}
+
+static int
+pmcs_disable_intrs(pmcs_hw_t *pwp, int icnt)
+{
+	if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) {
+		int r = ddi_intr_block_disable(&pwp->ih_table[0],
+		    pwp->intr_cnt);
+		if (r != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "unable to disable interrupt block");
+			return (-1);
+		}
+	} else {
+		int i;
+		for (i = 0; i < icnt; i++) {
+			if (ddi_intr_disable(pwp->ih_table[i]) == DDI_SUCCESS) {
+				continue;
+			}
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "unable to disable interrupt %d", i);
+			return (-1);
+		}
+	}
+	return (0);
+}
+
+static int
+pmcs_free_intrs(pmcs_hw_t *pwp, int icnt)
+{
+	int i;
+	for (i = 0; i < icnt; i++) {
+		if (ddi_intr_free(pwp->ih_table[i]) == DDI_SUCCESS) {
+			continue;
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "unable to free interrupt %d", i);
+		return (-1);
+	}
+	kmem_free(pwp->ih_table, pwp->ih_table_size);
+	pwp->ih_table_size = 0;
+	return (0);
+}
+
+/*
+ * Try to set up interrupts of type "type" with a minimum number of interrupts
+ * of "min".
+ */
+static void
+pmcs_setup_intr_impl(pmcs_hw_t *pwp, int type, int min)
+{
+	int rval, avail, count, actual, max;
+
+	rval = ddi_intr_get_nintrs(pwp->dip, type, &count);
+	if ((rval != DDI_SUCCESS) || (count < min)) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: get_nintrs failed; type: %d rc: %d count: %d min: %d",
+		    __func__, type, rval, count, min);
+		return;
+	}
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+	    "%s: nintrs = %d for type: %d", __func__, count, type);
+
+	rval = ddi_intr_get_navail(pwp->dip, type, &avail);
+	if ((rval != DDI_SUCCESS) || (avail < min)) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: get_navail failed; type: %d rc: %d avail: %d min: %d",
+		    __func__, type, rval, avail, min);
+		return;
+	}
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+	    "%s: navail = %d for type: %d", __func__, avail, type);
+
+	pwp->ih_table_size = avail * sizeof (ddi_intr_handle_t);
+	pwp->ih_table = kmem_alloc(pwp->ih_table_size, KM_SLEEP);
+
+	switch (type) {
+	case DDI_INTR_TYPE_MSIX:
+		pwp->int_type = PMCS_INT_MSIX;
+		max = PMCS_MAX_MSIX;
+		break;
+	case DDI_INTR_TYPE_MSI:
+		pwp->int_type = PMCS_INT_MSI;
+		max = PMCS_MAX_MSI;
+		break;
+	case DDI_INTR_TYPE_FIXED:
+	default:
+		pwp->int_type = PMCS_INT_FIXED;
+		max = PMCS_MAX_FIXED;
+		break;
+	}
+
+	rval = ddi_intr_alloc(pwp->dip, pwp->ih_table, type, 0, max, &actual,
+	    DDI_INTR_ALLOC_NORMAL);
+	if (rval != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: ddi_intr_alloc failed; type: %d rc: %d",
+		    __func__, type, rval);
+		kmem_free(pwp->ih_table, pwp->ih_table_size);
+		pwp->ih_table = NULL;
+		pwp->ih_table_size = 0;
+		pwp->intr_cnt = 0;
+		pwp->int_type = PMCS_INT_NONE;
+		return;
+	}
+
+	pwp->intr_cnt = actual;
+}
+
+/*
+ * Set up interrupts.
+ * We return one of three values:
+ *
+ * 0 - success
+ * EAGAIN - failure to set up interrupts
+ * EIO - "" + we're now stuck partly enabled
+ *
+ * If EIO is returned, we can't unload the driver.
+ */
+static int
+pmcs_setup_intr(pmcs_hw_t *pwp)
+{
+	int i, r, itypes, oqv_count;
+	ddi_intr_handler_t **iv_table;
+	size_t iv_table_size;
+	uint_t pri;
+
+	if (ddi_intr_get_supported_types(pwp->dip, &itypes) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "cannot get interrupt types");
+		return (EAGAIN);
+	}
+
+	if (disable_msix) {
+		itypes &= ~DDI_INTR_TYPE_MSIX;
+	}
+	if (disable_msi) {
+		itypes &= ~DDI_INTR_TYPE_MSI;
+	}
+
+	/*
+	 * We won't know what firmware we're running until we call pmcs_setup,
+	 * and we can't call pmcs_setup until we establish interrupts.
+	 */
+
+	pwp->int_type = PMCS_INT_NONE;
+
+	/*
+	 * We want PMCS_MAX_MSIX vectors for MSI-X.  Anything less would be
+	 * uncivilized.
+	 */
+	if (itypes & DDI_INTR_TYPE_MSIX) {
+		pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSIX, PMCS_MAX_MSIX);
+		if (pwp->int_type == PMCS_INT_MSIX) {
+			itypes = 0;
+		}
+	}
+
+	if (itypes & DDI_INTR_TYPE_MSI) {
+		pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSI, 1);
+		if (pwp->int_type == PMCS_INT_MSI) {
+			itypes = 0;
+		}
+	}
+
+	if (itypes & DDI_INTR_TYPE_FIXED) {
+		pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_FIXED, 1);
+		if (pwp->int_type == PMCS_INT_FIXED) {
+			itypes = 0;
+		}
+	}
+
+	if (pwp->intr_cnt == 0) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, "No interrupts available");
+		return (EAGAIN);
+	}
+
+	iv_table_size = sizeof (ddi_intr_handler_t *) * pwp->intr_cnt;
+	iv_table = kmem_alloc(iv_table_size, KM_SLEEP);
+
+	/*
+	 * Get iblock cookie and add handlers.
+	 */
+	switch (pwp->intr_cnt) {
+	case 1:
+		iv_table[0] = pmcs_all_intr;
+		break;
+	case 2:
+		iv_table[0] = pmcs_iodone_ix;
+		iv_table[1] = pmcs_nonio_ix;
+		break;
+	case 4:
+		iv_table[PMCS_MSIX_GENERAL] = pmcs_general_ix;
+		iv_table[PMCS_MSIX_IODONE] = pmcs_iodone_ix;
+		iv_table[PMCS_MSIX_EVENTS] = pmcs_event_ix;
+		iv_table[PMCS_MSIX_FATAL] = pmcs_fatal_ix;
+		break;
+	default:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: intr_cnt = %d - unexpected", __func__, pwp->intr_cnt);
+		kmem_free(iv_table, iv_table_size);
+		return (EAGAIN);
+	}
+
+	for (i = 0; i < pwp->intr_cnt; i++) {
+		r = ddi_intr_add_handler(pwp->ih_table[i], iv_table[i],
+		    (caddr_t)pwp, NULL);
+		if (r != DDI_SUCCESS) {
+			kmem_free(iv_table, iv_table_size);
+			if (pmcs_remove_ihandlers(pwp, i)) {
+				return (EIO);
+			}
+			if (pmcs_free_intrs(pwp, i)) {
+				return (EIO);
+			}
+			pwp->intr_cnt = 0;
+			return (EAGAIN);
+		}
+	}
+
+	kmem_free(iv_table, iv_table_size);
+
+	if (ddi_intr_get_cap(pwp->ih_table[0], &pwp->intr_cap) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "unable to get int capabilities");
+		if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
+			return (EIO);
+		}
+		if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
+			return (EIO);
+		}
+		pwp->intr_cnt = 0;
+		return (EAGAIN);
+	}
+
+	if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) {
+		r = ddi_intr_block_enable(&pwp->ih_table[0], pwp->intr_cnt);
+		if (r != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "intr blk enable failed");
+			if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
+				return (EIO);
+			}
+			if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
+				return (EIO);
+			}
+			pwp->intr_cnt = 0;
+			return (EFAULT);
+		}
+	} else {
+		for (i = 0; i < pwp->intr_cnt; i++) {
+			r = ddi_intr_enable(pwp->ih_table[i]);
+			if (r == DDI_SUCCESS) {
+				continue;
+			}
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "unable to enable interrupt %d", i);
+			if (pmcs_disable_intrs(pwp, i)) {
+				return (EIO);
+			}
+			if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
+				return (EIO);
+			}
+			if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
+				return (EIO);
+			}
+			pwp->intr_cnt = 0;
+			return (EAGAIN);
+		}
+	}
+
+	/*
+	 * Set up locks.
+	 */
+	if (ddi_intr_get_pri(pwp->ih_table[0], &pri) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "unable to get interrupt priority");
+		if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) {
+			return (EIO);
+		}
+		if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
+			return (EIO);
+		}
+		if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
+			return (EIO);
+		}
+		pwp->intr_cnt = 0;
+		return (EAGAIN);
+	}
+
+	pwp->locks_initted = 1;
+	pwp->intr_pri = pri;
+	mutex_init(&pwp->lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
+	mutex_init(&pwp->dma_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
+	mutex_init(&pwp->axil_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
+	mutex_init(&pwp->cq_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
+	mutex_init(&pwp->ict_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
+	mutex_init(&pwp->config_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
+	mutex_init(&pwp->wfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
+	mutex_init(&pwp->pfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
+	mutex_init(&pwp->dead_phylist_lock, NULL, MUTEX_DRIVER,
+	    DDI_INTR_PRI(pri));
+#ifdef	DEBUG
+	mutex_init(&pwp->dbglock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri));
+#endif
+	cv_init(&pwp->ict_cv, NULL, CV_DRIVER, NULL);
+	cv_init(&pwp->drain_cv, NULL, CV_DRIVER, NULL);
+	for (i = 0; i < PMCS_NIQ; i++) {
+		mutex_init(&pwp->iqp_lock[i], NULL,
+		    MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri));
+	}
+	for (i = 0; i < pwp->cq_info.cq_threads; i++) {
+		mutex_init(&pwp->cq_info.cq_thr_info[i].cq_thr_lock, NULL,
+		    MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri));
+		cv_init(&pwp->cq_info.cq_thr_info[i].cq_cv, NULL,
+		    CV_DRIVER, NULL);
+	}
+
+	pmcs_prt(pwp, PMCS_PRT_INFO, "%d %s interrup%s configured",
+	    pwp->intr_cnt, (pwp->int_type == PMCS_INT_MSIX)? "MSI-X" :
+	    ((pwp->int_type == PMCS_INT_MSI)? "MSI" : "INT-X"),
+	    pwp->intr_cnt == 1? "t" : "ts");
+
+
+	/*
+	 * Enable Interrupts
+	 */
+	if (pwp->intr_cnt > PMCS_NOQ) {
+		oqv_count = pwp->intr_cnt;
+	} else {
+		oqv_count = PMCS_NOQ;
+	}
+	for (pri = 0xffffffff, i = 0; i < oqv_count; i++) {
+		pri ^= (1 << i);
+	}
+
+	mutex_enter(&pwp->lock);
+	pwp->intr_mask = pri;
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask);
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
+	mutex_exit(&pwp->lock);
+
+	return (0);
+}
+
+static int
+pmcs_teardown_intr(pmcs_hw_t *pwp)
+{
+	if (pwp->intr_cnt) {
+		if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) {
+			return (EIO);
+		}
+		if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) {
+			return (EIO);
+		}
+		if (pmcs_free_intrs(pwp, pwp->intr_cnt)) {
+			return (EIO);
+		}
+		pwp->intr_cnt = 0;
+	}
+	return (0);
+}
+
+static uint_t
+pmcs_general_ix(caddr_t arg1, caddr_t arg2)
+{
+	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
+	_NOTE(ARGUNUSED(arg2));
+	pmcs_general_intr(pwp);
+	return (DDI_INTR_CLAIMED);
+}
+
+static uint_t
+pmcs_event_ix(caddr_t arg1, caddr_t arg2)
+{
+	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
+	_NOTE(ARGUNUSED(arg2));
+	pmcs_event_intr(pwp);
+	return (DDI_INTR_CLAIMED);
+}
+
+static uint_t
+pmcs_iodone_ix(caddr_t arg1, caddr_t arg2)
+{
+	_NOTE(ARGUNUSED(arg2));
+	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
+
+	/*
+	 * It's possible that if we just turned interrupt coalescing off
+	 * (and thus, re-enabled auto clear for interrupts on the I/O outbound
+	 * queue) that there was an interrupt already pending.  We use
+	 * io_intr_coal.int_cleared to ensure that we still drop in here and
+	 * clear the appropriate interrupt bit one last time.
+	 */
+	mutex_enter(&pwp->ict_lock);
+	if (pwp->io_intr_coal.timer_on ||
+	    (pwp->io_intr_coal.int_cleared == B_FALSE)) {
+		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
+		    (1 << PMCS_OQ_IODONE));
+		pwp->io_intr_coal.int_cleared = B_TRUE;
+	}
+	mutex_exit(&pwp->ict_lock);
+
+	pmcs_iodone_intr(pwp);
+
+	return (DDI_INTR_CLAIMED);
+}
+
+static uint_t
+pmcs_fatal_ix(caddr_t arg1, caddr_t arg2)
+{
+	pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1);
+	_NOTE(ARGUNUSED(arg2));
+	pmcs_fatal_handler(pwp);
+	return (DDI_INTR_CLAIMED);
+}
+
+static uint_t
+pmcs_nonio_ix(caddr_t arg1, caddr_t arg2)
+{
+	_NOTE(ARGUNUSED(arg2));
+	pmcs_hw_t *pwp = (void *)arg1;
+	uint32_t obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB);
+
+	/*
+	 * Check for Fatal Interrupts
+	 */
+	if (obdb & (1 << PMCS_FATAL_INTERRUPT)) {
+		pmcs_fatal_handler(pwp);
+		return (DDI_INTR_CLAIMED);
+	}
+
+	if (obdb & (1 << PMCS_OQ_GENERAL)) {
+		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
+		    (1 << PMCS_OQ_GENERAL));
+		pmcs_general_intr(pwp);
+		pmcs_event_intr(pwp);
+	}
+
+	return (DDI_INTR_CLAIMED);
+}
+
+static uint_t
+pmcs_all_intr(caddr_t arg1, caddr_t arg2)
+{
+	_NOTE(ARGUNUSED(arg2));
+	pmcs_hw_t *pwp = (void *) arg1;
+	uint32_t obdb;
+	int handled = 0;
+
+	obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB);
+
+	/*
+	 * Check for Fatal Interrupts
+	 */
+	if (obdb & (1 << PMCS_FATAL_INTERRUPT)) {
+		pmcs_fatal_handler(pwp);
+		return (DDI_INTR_CLAIMED);
+	}
+
+	/*
+	 * Check for Outbound Queue service needed
+	 */
+	if (obdb & (1 << PMCS_OQ_IODONE)) {
+		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
+		    (1 << PMCS_OQ_IODONE));
+		obdb ^= (1 << PMCS_OQ_IODONE);
+		handled++;
+		pmcs_iodone_intr(pwp);
+	}
+	if (obdb & (1 << PMCS_OQ_GENERAL)) {
+		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
+		    (1 << PMCS_OQ_GENERAL));
+		obdb ^= (1 << PMCS_OQ_GENERAL);
+		handled++;
+		pmcs_general_intr(pwp);
+	}
+	if (obdb & (1 << PMCS_OQ_EVENTS)) {
+		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR,
+		    (1 << PMCS_OQ_EVENTS));
+		obdb ^= (1 << PMCS_OQ_EVENTS);
+		handled++;
+		pmcs_event_intr(pwp);
+	}
+	if (obdb) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "interrupt bits not handled (0x%x)", obdb);
+		pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, obdb);
+		handled++;
+	}
+	if (pwp->int_type == PMCS_INT_MSI) {
+		handled++;
+	}
+	return (handled? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
+}
+
+void
+pmcs_fatal_handler(pmcs_hw_t *pwp)
+{
+	pmcs_prt(pwp, PMCS_PRT_ERR, "Fatal Interrupt caught");
+	mutex_enter(&pwp->lock);
+	pwp->state = STATE_DEAD;
+	pmcs_register_dump_int(pwp);
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
+	mutex_exit(&pwp->lock);
+	pmcs_fm_ereport(pwp, DDI_FM_DEVICE_NO_RESPONSE);
+	ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
+
+#ifdef	DEBUG
+	cmn_err(CE_PANIC, "PMCS Fatal Firmware Error");
+#endif
+}
+
+/*
+ * Called with PHY lock and target statlock held and scratch acquired.
+ */
+boolean_t
+pmcs_assign_device(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt)
+{
+	pmcs_phy_t *pptr = tgt->phy;
+
+	switch (pptr->dtype) {
+	case SAS:
+	case EXPANDER:
+		break;
+	case SATA:
+		tgt->ca = 1;
+		break;
+	default:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: Target %p has PHY %p with invalid dtype",
+		    __func__, (void *)tgt, (void *)pptr);
+		return (B_FALSE);
+	}
+
+	tgt->new = 1;
+	tgt->dev_gone = 0;
+	tgt->dying = 0;
+	tgt->recover_wait = 0;
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+	    "%s: config %s vtgt %u for " SAS_ADDR_FMT, __func__,
+	    pptr->path, tgt->target_num, SAS_ADDR_PRT(pptr->sas_address));
+
+	if (pmcs_add_new_device(pwp, tgt) != B_TRUE) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: Failed for vtgt %u / WWN " SAS_ADDR_FMT, __func__,
+		    tgt->target_num, SAS_ADDR_PRT(pptr->sas_address));
+		mutex_destroy(&tgt->statlock);
+		mutex_destroy(&tgt->wqlock);
+		mutex_destroy(&tgt->aqlock);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+/*
+ * Called with softstate lock held
+ */
+void
+pmcs_remove_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	pmcs_xscsi_t *xp;
+	unsigned int vtgt;
+
+	ASSERT(mutex_owned(&pwp->lock));
+
+	for (vtgt = 0; vtgt < pwp->max_dev; vtgt++) {
+		xp = pwp->targets[vtgt];
+		if (xp == NULL) {
+			continue;
+		}
+
+		mutex_enter(&xp->statlock);
+		if (xp->phy == pptr) {
+			if (xp->new) {
+				xp->new = 0;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+				    "cancel config of vtgt %u", vtgt);
+			} else {
+				xp->assigned = 0;
+				xp->dying = 1;
+				SCHEDULE_WORK(pwp, PMCS_WORK_REM_DEVICES);
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+				    "Scheduling removal of tgt 0x%p vtgt %u",
+				    (void *)xp, vtgt);
+			}
+			mutex_exit(&xp->statlock);
+			break;
+		}
+		mutex_exit(&xp->statlock);
+	}
+}
+
+void
+pmcs_prt_impl(pmcs_hw_t *pwp, pmcs_prt_level_t level, const char *fmt, ...)
+{
+	va_list	ap;
+	int written = 0;
+	char *ptr;
+	uint32_t elem_size = PMCS_TBUF_ELEM_SIZE - 1;
+	boolean_t system_log;
+	int system_log_level;
+
+	switch (level) {
+	case PMCS_PRT_DEBUG_DEVEL:
+	case PMCS_PRT_DEBUG_DEV_STATE:
+	case PMCS_PRT_DEBUG_PHY_LOCKING:
+	case PMCS_PRT_DEBUG_SCSI_STATUS:
+	case PMCS_PRT_DEBUG_UNDERFLOW:
+	case PMCS_PRT_DEBUG_CONFIG:
+	case PMCS_PRT_DEBUG_IPORT:
+	case PMCS_PRT_DEBUG_MAP:
+	case PMCS_PRT_DEBUG3:
+	case PMCS_PRT_DEBUG2:
+	case PMCS_PRT_DEBUG1:
+	case PMCS_PRT_DEBUG:
+		system_log = B_FALSE;
+		break;
+	case PMCS_PRT_INFO:
+		system_log = B_TRUE;
+		system_log_level = CE_CONT;
+		break;
+	case PMCS_PRT_WARN:
+		system_log = B_TRUE;
+		system_log_level = CE_NOTE;
+		break;
+	case PMCS_PRT_ERR:
+		system_log = B_TRUE;
+		system_log_level = CE_WARN;
+		break;
+	default:
+		return;
+	}
+
+	mutex_enter(&pmcs_trace_lock);
+	gethrestime(&pmcs_tbuf_ptr->timestamp);
+	ptr = pmcs_tbuf_ptr->buf;
+	written += snprintf(ptr, elem_size, "pmcs%d:%d: ",
+	    ddi_get_instance(pwp->dip), level);
+	ptr += strlen(ptr);
+	va_start(ap, fmt);
+	written += vsnprintf(ptr, elem_size - written, fmt, ap);
+	va_end(ap);
+	if (written > elem_size - 1) {
+		/* Indicate truncation */
+		pmcs_tbuf_ptr->buf[elem_size - 1] = '+';
+	}
+	if (++pmcs_tbuf_idx == pmcs_tbuf_num_elems) {
+		pmcs_tbuf_ptr = pmcs_tbuf;
+		pmcs_tbuf_wrap = B_TRUE;
+		pmcs_tbuf_idx = 0;
+	} else {
+		++pmcs_tbuf_ptr;
+	}
+	mutex_exit(&pmcs_trace_lock);
+
+	/*
+	 * When pmcs_force_syslog in non-zero, everything goes also
+	 * to syslog, at CE_CONT level.
+	 */
+	if (pmcs_force_syslog) {
+		system_log = B_TRUE;
+		system_log_level = CE_CONT;
+	}
+
+	/*
+	 * Anything that comes in with PMCS_PRT_INFO, WARN, or ERR also
+	 * goes to syslog.
+	 */
+	if (system_log) {
+		char local[196];
+
+		switch (system_log_level) {
+		case CE_CONT:
+			(void) snprintf(local, sizeof (local), "%sINFO: ",
+			    pmcs_console ? "" : "?");
+			break;
+		case CE_NOTE:
+		case CE_WARN:
+			local[0] = 0;
+			break;
+		default:
+			return;
+		}
+
+		ptr = local;
+		ptr += strlen(local);
+		(void) snprintf(ptr, (sizeof (local)) -
+		    ((size_t)ptr - (size_t)local), "pmcs%d: ",
+		    ddi_get_instance(pwp->dip));
+		ptr += strlen(ptr);
+		va_start(ap, fmt);
+		(void) vsnprintf(ptr,
+		    (sizeof (local)) - ((size_t)ptr - (size_t)local), fmt, ap);
+		va_end(ap);
+		if (level == CE_CONT) {
+			(void) strlcat(local, "\n", sizeof (local));
+		}
+		cmn_err(system_log_level, local);
+	}
+
+}
+
+/*
+ * pmcs_acquire_scratch
+ *
+ * If "wait" is true, the caller will wait until it can acquire the scratch.
+ * This implies the caller needs to be in a context where spinning for an
+ * indeterminate amount of time is acceptable.
+ */
+int
+pmcs_acquire_scratch(pmcs_hw_t *pwp, boolean_t wait)
+{
+	int rval;
+
+	if (!wait) {
+		return (atomic_swap_8(&pwp->scratch_locked, 1));
+	}
+
+	/*
+	 * Caller will wait for scratch.
+	 */
+	while ((rval = atomic_swap_8(&pwp->scratch_locked, 1)) != 0) {
+		drv_usecwait(100);
+	}
+
+	return (rval);
+}
+
+void
+pmcs_release_scratch(pmcs_hw_t *pwp)
+{
+	pwp->scratch_locked = 0;
+}
+
+static void
+pmcs_create_phy_stats(pmcs_iport_t *iport)
+{
+	sas_phy_stats_t		*ps;
+	pmcs_hw_t		*pwp;
+	pmcs_phy_t		*phyp;
+	int			ndata;
+	char			ks_name[KSTAT_STRLEN];
+
+	ASSERT(iport != NULL);
+	pwp = iport->pwp;
+	ASSERT(pwp != NULL);
+
+	mutex_enter(&iport->lock);
+
+	for (phyp = list_head(&iport->phys);
+	    phyp != NULL;
+	    phyp = list_next(&iport->phys, phyp)) {
+
+		pmcs_lock_phy(phyp);
+
+		if (phyp->phy_stats != NULL) {
+			pmcs_unlock_phy(phyp);
+			/* We've already created this kstat instance */
+			continue;
+		}
+
+		ndata = (sizeof (sas_phy_stats_t)/sizeof (kstat_named_t));
+
+		(void) snprintf(ks_name, sizeof (ks_name),
+		    "%s.%llx.%d.%d", ddi_driver_name(iport->dip),
+		    (longlong_t)pwp->sas_wwns[0],
+		    ddi_get_instance(iport->dip), phyp->phynum);
+
+		phyp->phy_stats = kstat_create("pmcs",
+		    ddi_get_instance(iport->dip), ks_name, KSTAT_SAS_PHY_CLASS,
+		    KSTAT_TYPE_NAMED, ndata, 0);
+
+		if (phyp->phy_stats == NULL) {
+			pmcs_unlock_phy(phyp);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: Failed to create %s kstats", __func__,
+			    ks_name);
+			continue;
+		}
+
+		ps = (sas_phy_stats_t *)phyp->phy_stats->ks_data;
+
+		kstat_named_init(&ps->seconds_since_last_reset,
+		    "SecondsSinceLastReset", KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&ps->tx_frames,
+		    "TxFrames", KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&ps->rx_frames,
+		    "RxFrames", KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&ps->tx_words,
+		    "TxWords", KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&ps->rx_words,
+		    "RxWords", KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&ps->invalid_dword_count,
+		    "InvalidDwordCount", KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&ps->running_disparity_error_count,
+		    "RunningDisparityErrorCount", KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&ps->loss_of_dword_sync_count,
+		    "LossofDwordSyncCount", KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&ps->phy_reset_problem_count,
+		    "PhyResetProblemCount", KSTAT_DATA_ULONGLONG);
+
+		phyp->phy_stats->ks_private = phyp;
+		phyp->phy_stats->ks_update = pmcs_update_phy_stats;
+		kstat_install(phyp->phy_stats);
+		pmcs_unlock_phy(phyp);
+	}
+
+	mutex_exit(&iport->lock);
+}
+
+int
+pmcs_update_phy_stats(kstat_t *ks, int rw)
+{
+	int		val, ret = DDI_FAILURE;
+	pmcs_phy_t	*pptr = (pmcs_phy_t *)ks->ks_private;
+	pmcs_hw_t	*pwp = pptr->pwp;
+	sas_phy_stats_t	*ps = ks->ks_data;
+
+	_NOTE(ARGUNUSED(rw));
+	ASSERT((pptr != NULL) && (pwp != NULL));
+
+	/*
+	 * We just want to lock against other invocations of kstat;
+	 * we don't need to pmcs_lock_phy() for this.
+	 */
+	mutex_enter(&pptr->phy_lock);
+
+	/* Get Stats from Chip */
+	val = pmcs_get_diag_report(pwp, PMCS_INVALID_DWORD_CNT, pptr->phynum);
+	if (val == DDI_FAILURE)
+		goto fail;
+	ps->invalid_dword_count.value.ull = (unsigned long long)val;
+
+	val = pmcs_get_diag_report(pwp, PMCS_DISPARITY_ERR_CNT, pptr->phynum);
+	if (val == DDI_FAILURE)
+		goto fail;
+	ps->running_disparity_error_count.value.ull = (unsigned long long)val;
+
+	val = pmcs_get_diag_report(pwp, PMCS_LOST_DWORD_SYNC_CNT, pptr->phynum);
+	if (val == DDI_FAILURE)
+		goto fail;
+	ps->loss_of_dword_sync_count.value.ull = (unsigned long long)val;
+
+	val = pmcs_get_diag_report(pwp, PMCS_RESET_FAILED_CNT, pptr->phynum);
+	if (val == DDI_FAILURE)
+		goto fail;
+	ps->phy_reset_problem_count.value.ull = (unsigned long long)val;
+
+	ret = DDI_SUCCESS;
+fail:
+	mutex_exit(&pptr->phy_lock);
+	return (ret);
+}
+
+static void
+pmcs_destroy_phy_stats(pmcs_iport_t *iport)
+{
+	pmcs_phy_t		*phyp;
+
+	ASSERT(iport != NULL);
+	mutex_enter(&iport->lock);
+	phyp = iport->pptr;
+	if (phyp == NULL) {
+		mutex_exit(&iport->lock);
+		return;
+	}
+
+	pmcs_lock_phy(phyp);
+	if (phyp->phy_stats != NULL) {
+		kstat_delete(phyp->phy_stats);
+		phyp->phy_stats = NULL;
+	}
+	pmcs_unlock_phy(phyp);
+
+	mutex_exit(&iport->lock);
+}
+
+/*ARGSUSED*/
+static int
+pmcs_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
+{
+	/*
+	 * as the driver can always deal with an error in any dma or
+	 * access handle, we can just return the fme_status value.
+	 */
+	pci_ereport_post(dip, err, NULL);
+	return (err->fme_status);
+}
+
+static void
+pmcs_fm_init(pmcs_hw_t *pwp)
+{
+	ddi_iblock_cookie_t	fm_ibc;
+
+	/* Only register with IO Fault Services if we have some capability */
+	if (pwp->fm_capabilities) {
+		/* Adjust access and dma attributes for FMA */
+		pwp->reg_acc_attr.devacc_attr_access |= DDI_FLAGERR_ACC;
+		pwp->dev_acc_attr.devacc_attr_access |= DDI_FLAGERR_ACC;
+		pwp->iqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
+		pwp->oqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
+		pwp->cip_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
+		pwp->fwlog_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
+
+		/*
+		 * Register capabilities with IO Fault Services.
+		 */
+		ddi_fm_init(pwp->dip, &pwp->fm_capabilities, &fm_ibc);
+
+		/*
+		 * Initialize pci ereport capabilities if ereport
+		 * capable (should always be.)
+		 */
+		if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) ||
+		    DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
+			pci_ereport_setup(pwp->dip);
+		}
+
+		/*
+		 * Register error callback if error callback capable.
+		 */
+		if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
+			ddi_fm_handler_register(pwp->dip,
+			    pmcs_fm_error_cb, (void *) pwp);
+		}
+	}
+}
+
+static void
+pmcs_fm_fini(pmcs_hw_t *pwp)
+{
+	/* Only unregister FMA capabilities if registered */
+	if (pwp->fm_capabilities) {
+		/*
+		 * Un-register error callback if error callback capable.
+		 */
+		if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
+			ddi_fm_handler_unregister(pwp->dip);
+		}
+
+		/*
+		 * Release any resources allocated by pci_ereport_setup()
+		 */
+		if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) ||
+		    DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) {
+			pci_ereport_teardown(pwp->dip);
+		}
+
+		/* Unregister from IO Fault Services */
+		ddi_fm_fini(pwp->dip);
+
+		/* Adjust access and dma attributes for FMA */
+		pwp->reg_acc_attr.devacc_attr_access &= ~DDI_FLAGERR_ACC;
+		pwp->dev_acc_attr.devacc_attr_access &= ~DDI_FLAGERR_ACC;
+		pwp->iqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
+		pwp->oqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
+		pwp->cip_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
+		pwp->fwlog_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR;
+	}
+}
+
+static boolean_t
+pmcs_fabricate_wwid(pmcs_hw_t *pwp)
+{
+	char *cp, c;
+	uint64_t adr;
+	int i;
+
+	cp = &c;
+	(void) ddi_strtoul(hw_serial, &cp, 10, (unsigned long *)&adr);
+	if (adr == 0) {
+		static const char foo[] = __DATE__ __TIME__;
+		/* Oh, dear, we're toast */
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: No serial number available to fabricate WWN",
+		    __func__);
+		for (i = 0; foo[i]; i++) {
+			adr += foo[i];
+		}
+	}
+	adr <<= 8;
+	adr |= ((uint64_t)ddi_get_instance(pwp->dip) << 52);
+	adr |= (5ULL << 60);
+	for (i = 0; i < PMCS_MAX_PORTS; i++) {
+		pwp->sas_wwns[i] = adr + i;
+	}
+
+	return (B_TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_fwlog.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,1024 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains firmware log routines.
+ */
+
+#include <sys/scsi/adapters/pmcs/pmcs.h>
+
+static int pmcs_dump_ioqs(pmcs_hw_t *, caddr_t, uint32_t);
+static int pmcs_dump_spc_ver(pmcs_hw_t *, caddr_t, uint32_t);
+static int pmcs_dump_mpi_table(pmcs_hw_t *, caddr_t, uint32_t);
+static int pmcs_dump_gsm_conf(pmcs_hw_t *, caddr_t, uint32_t);
+static int pmcs_dump_pcie_conf(pmcs_hw_t *, caddr_t, uint32_t);
+static uint32_t pmcs_get_axil(pmcs_hw_t *);
+static boolean_t pmcs_shift_axil(pmcs_hw_t *, uint32_t);
+static void pmcs_restore_axil(pmcs_hw_t *, uint32_t);
+static int pmcs_dump_gsm(pmcs_hw_t *, caddr_t, uint32_t);
+static int pmcs_dump_fwlog(pmcs_hw_t *, caddr_t, uint32_t);
+
+/*
+ * Dump internal registers. Used after a firmware crash.
+ * Here dump various registers for firmware forensics,
+ * including MPI, GSM configuration, firmware log, IO Queues etc.
+ */
+void
+pmcs_register_dump_int(pmcs_hw_t *pwp)
+{
+	int n = 0;
+	uint32_t size_left = 0;
+	uint8_t slice = 0;
+	caddr_t buf = NULL;
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG, "pmcs%d: Internal register dump",
+	    ddi_get_instance(pwp->dip));
+	ASSERT(mutex_owned(&pwp->lock));
+
+	if (pwp->regdumpp == NULL) {
+		pwp->regdumpp =
+		    kmem_zalloc(PMCS_REG_DUMP_SIZE, KM_NOSLEEP);
+		if (pwp->regdumpp == NULL) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: register"
+			    " dump memory not allocated", __func__);
+			return;
+		}
+	}
+	buf = pwp->regdumpp;
+	size_left = PMCS_REG_DUMP_SIZE - 1;
+
+	n = pmcs_dump_spc_ver(pwp, buf, size_left);
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+	n = pmcs_dump_gsm_conf(pwp, buf, size_left);
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+	n = pmcs_dump_pcie_conf(pwp, buf, size_left);
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+	n = pmcs_dump_mpi_table(pwp, buf, size_left);
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+	n = pmcs_dump_ioqs(pwp, buf, size_left);
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+
+	mutex_exit(&pwp->lock);
+	slice = (PMCS_REGISTER_DUMP_FLASH_SIZE / PMCS_FLASH_CHUNK_SIZE);
+	n = snprintf(buf, size_left, "\nDump AAP1 register: \n"
+	    "-----------------\n");
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+	for (uint8_t j = 0; j < slice; j++) {
+		n = pmcs_get_nvmd(pwp, PMCS_NVMD_REG_DUMP,
+		    PMCIN_NVMD_AAP1, (j * PMCS_FLASH_CHUNK_SIZE),
+		    buf, size_left);
+		if (n == PMCS_FLASH_CHUNK_SIZE) {
+			ASSERT(size_left >= n);
+			buf += n; size_left -= n;
+		} else if ((n < PMCS_FLASH_CHUNK_SIZE) && (n > 0)) {
+			ASSERT(size_left >= n);
+			buf += n; size_left -= n;
+			break;
+		} else if (n == 0) {
+			n = snprintf(buf, size_left, "AAP1: Content of "
+			    "register dump on flash is NULL\n");
+			ASSERT(size_left >= n);
+			buf += n; size_left -= n;
+			break;
+		} else {
+			n = snprintf(buf, size_left,
+			    "AAP1: Unable to obtain internal register dump\n");
+			ASSERT(size_left >= n);
+			buf += n; size_left -= n;
+			break;
+		}
+
+	}
+
+	n = snprintf(buf, size_left, "\nDump IOP register: \n"
+	    "-----------------\n");
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+	for (uint8_t j = 0; j < slice; j++) {
+		n = pmcs_get_nvmd(pwp, PMCS_NVMD_REG_DUMP,
+		    PMCIN_NVMD_IOP, (j * PMCS_FLASH_CHUNK_SIZE),
+		    buf, size_left);
+		if (n == PMCS_FLASH_CHUNK_SIZE) {
+			ASSERT(size_left >= n);
+			buf += n; size_left -= n;
+		} else if ((n < PMCS_FLASH_CHUNK_SIZE) && (n > 0)) {
+			ASSERT(size_left >= n);
+			buf += n; size_left -= n;
+			break;
+		} else if (n == 0) {
+			n = snprintf(buf, size_left,
+			    "IOP: Content of internal register dump is NULL\n");
+			ASSERT(size_left >= n);
+			buf += n; size_left -= n;
+			break;
+		} else {
+			n = snprintf(buf, size_left,
+			    "IOP: Unable to obtain internal register dump\n");
+			ASSERT(size_left >= n);
+			buf += n; size_left -= n;
+			break;
+		}
+
+	}
+
+	n = snprintf(buf, size_left, "\nDump AAP1 event log: \n"
+	    "-----------------\n");
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+	for (uint8_t j = 0; j < slice; j++) {
+		n = pmcs_get_nvmd(pwp, PMCS_NVMD_EVENT_LOG,
+		    PMCIN_NVMD_AAP1, (j * PMCS_FLASH_CHUNK_SIZE),
+		    buf, size_left);
+		if (n > 0) {
+			ASSERT(size_left >= n);
+			buf += n; size_left -= n;
+		} else {
+			n = snprintf(buf, size_left,
+			    "AAP1: Unable to obtain event log on flash\n");
+			ASSERT(size_left >= n);
+			buf += n; size_left -= n;
+			break;
+		}
+	}
+
+	n = snprintf(buf, size_left, "\nDump IOP event log: \n"
+	    "-----------------\n");
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+	for (uint8_t j = 0; j < slice; j++) {
+		n = pmcs_get_nvmd(pwp, PMCS_NVMD_EVENT_LOG,
+		    PMCIN_NVMD_IOP, (j * PMCS_FLASH_CHUNK_SIZE),
+		    buf, size_left);
+		if (n > 0) {
+			ASSERT(size_left >= n);
+			buf += n; size_left -= n;
+		} else {
+			n = snprintf(buf, size_left,
+			    "IOP: Unable to obtain event log dump\n");
+			ASSERT(size_left >= n);
+			buf += n; size_left -= n;
+			break;
+		}
+	}
+	mutex_enter(&pwp->lock);
+
+	n = snprintf(buf, size_left, "\nDump firmware log: \n"
+	    "-----------------\n");
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+
+	n = pmcs_dump_fwlog(pwp, buf, size_left);
+
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+	n = snprintf(buf, size_left, "-----------------\n"
+	    "\n------------ Dump internal registers end  -------------\n");
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+
+	n = pmcs_dump_gsm(pwp, buf, size_left);
+	ASSERT(size_left >= n);
+	buf += n; size_left -= n;
+}
+
+static int
+pmcs_dump_fwlog(pmcs_hw_t *pwp, caddr_t buf, uint32_t size_left)
+{
+	pmcs_fw_event_hdr_t *evl_hdr;
+	int n = 0, retries = 0;
+	uint32_t evlog_latest_idx;
+	boolean_t log_is_current = B_FALSE;
+
+	if (pwp->fwlogp == NULL) {
+		n = snprintf(buf, size_left, "\nFirmware logging "
+		    "not enabled\n");
+		return (n);
+	}
+
+	/*
+	 * First, check to make sure all entries have been DMAed to the
+	 * log buffer.
+	 *
+	 * We'll wait the required 50ms, but if the latest entry keeps
+	 * changing, we'll only retry twice
+	 */
+	evl_hdr = (pmcs_fw_event_hdr_t *)pwp->fwlogp;
+	evlog_latest_idx = evl_hdr->fw_el_latest_idx;
+
+	while ((log_is_current == B_FALSE) && (retries < 3)) {
+		drv_usecwait(50 * 1000);
+		if (evl_hdr->fw_el_latest_idx == evlog_latest_idx) {
+			log_is_current = B_TRUE;
+		} else {
+			++retries;
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: event log is still being updated... waiting",
+			    __func__);
+			evlog_latest_idx = evl_hdr->fw_el_latest_idx;
+		}
+	}
+
+	n = pmcs_dump_binary(pwp, pwp->fwlogp, 0, (PMCS_FWLOG_SIZE >> 2),
+	    buf, size_left);
+
+	return (n);
+}
+
+/*
+ * Dump Inbound and Outbound Queues.
+ */
+static int
+pmcs_dump_ioqs(pmcs_hw_t *pwp, caddr_t buf, uint32_t size_left)
+{
+	uint8_t i = 0, k = 0;
+	uint32_t j = 0, depth = 0;
+	int n = 0;
+	uint32_t *ptr = NULL;
+
+	n += snprintf(&buf[n], (size_left - n), "\nDump I/O queues: \n"
+	    "-----------------\n");
+	for (i = 0; i < PMCS_NIQ; i++) {
+		depth = PMCS_IQDX(pmcs_rd_iqc_tbl(pwp, PMCS_IQC_PARMX(i)));
+		n += snprintf(&buf[n], (size_left - n),
+		    "IQ[%d] Details:\n-----------------\n", i);
+		n += snprintf(&buf[n], (size_left - n),
+		    "    depth = 0x%04x\n", depth);
+		n += snprintf(&buf[n], (size_left - n),
+		    "    latest ci = 0x%02x\n", pmcs_rd_iqci(pwp, i));
+		n += snprintf(&buf[n], (size_left - n),
+		    "    latest pi = 0x%02x\n", pmcs_rd_iqpi(pwp, i));
+		for (j = 0; j < depth; j++) {
+			n += snprintf(&buf[n], (size_left - n),
+			    "IOMB[%d]:\n", j);
+			ptr = &pwp->iqp[i][(j * PMCS_QENTRY_SIZE) >> 2];
+			for (k = 0; k < (PMCS_QENTRY_SIZE / sizeof (uint32_t));
+			    k += 8) {
+				n += snprintf(&buf[n], (size_left - n),
+				    "0x%08x 0x%08x 0x%08x 0x%08x "
+				    "0x%08x 0x%08x 0x%08x 0x%08x\n",
+				    LE_32(ptr[k]), LE_32(ptr[k+1]),
+				    LE_32(ptr[k+2]), LE_32(ptr[k+3]),
+				    LE_32(ptr[k+4]), LE_32(ptr[k+5]),
+				    LE_32(ptr[k+6]), LE_32(ptr[k+7]));
+			}
+		}
+	}
+	for (i = 0; i < PMCS_NOQ; i++) {
+		depth = PMCS_OQDX(pmcs_rd_oqc_tbl(pwp, PMCS_OQC_PARMX(i)));
+		n += snprintf(&buf[n], (size_left - n),
+		    "OQ[%d] Details:\n", i);
+		n += snprintf(&buf[n], (size_left - n),
+		    "    depth = 0x%04x\n", depth);
+		n += snprintf(&buf[n], (size_left - n),
+		    "    latest ci = 0x%02x\n", pmcs_rd_oqci(pwp, i));
+		n += snprintf(&buf[n], (size_left - n),
+		    "    latest pi = 0x%02x\n", pmcs_rd_oqpi(pwp, i));
+		for (j = 0; j < depth; j++) {
+			n += snprintf(&buf[n], (size_left - n),
+			    "IOMB[%d]:\n", j);
+			ptr = &pwp->oqp[i][(j * PMCS_QENTRY_SIZE) >> 2];
+			for (k = 0; k < (PMCS_QENTRY_SIZE / sizeof (uint32_t));
+			    k += 8) {
+				n += snprintf(&buf[n], (size_left - n),
+				    "0x%08x 0x%08x 0x%08x 0x%08x "
+				    "0x%08x 0x%08x 0x%08x 0x%08x\n",
+				    LE_32(ptr[k]), LE_32(ptr[k+1]),
+				    LE_32(ptr[k+2]), LE_32(ptr[k+3]),
+				    LE_32(ptr[k+4]), LE_32(ptr[k+5]),
+				    LE_32(ptr[k+6]), LE_32(ptr[k+7]));
+			}
+		}
+
+	}
+	n += snprintf(&buf[n], (size_left - n), "-----------------\n"
+	    "Dump I/O queues end \n");
+	return (n);
+}
+
+/*
+ * Dump SPC Version.
+ */
+static int
+pmcs_dump_spc_ver(pmcs_hw_t *pwp, caddr_t buf, uint32_t size_left)
+{
+	int n = 0;
+
+	n += snprintf(&buf[n], (size_left - n), "\nDump SPC version: \n"
+	    "-----------------\n");
+	n += snprintf(&buf[n], (size_left - n), "Firmware Release Type = "
+	    "0x%02x\n", PMCS_FW_TYPE(pwp));
+	n += snprintf(&buf[n], (size_left - n), "    Sub-Minor Release "
+	    "Number = 0x%02x\n", PMCS_FW_MICRO(pwp));
+	n += snprintf(&buf[n], (size_left - n), "    Minor Release "
+	    "Number = 0x%02x\n", PMCS_FW_MINOR(pwp));
+	n += snprintf(&buf[n], (size_left - n), "    Major Release "
+	    "Number = 0x%02x\n", PMCS_FW_MAJOR(pwp));
+	n += snprintf(&buf[n], (size_left - n), "SPC DeviceID = 0x%04x\n",
+	    pmcs_rd_topunit(pwp, PMCS_SPC_DEVICE_ID));
+	n += snprintf(&buf[n], (size_left - n), "SPC Device Revision = "
+	    "0x%08x\n", pmcs_rd_topunit(pwp, PMCS_DEVICE_REVISION));
+	n += snprintf(&buf[n], (size_left - n), "SPC BootStrap Register = "
+	    "0x%08x\n", pmcs_rd_topunit(pwp, PMCS_SPC_BOOT_STRAP));
+	n += snprintf(&buf[n], (size_left - n), "SPC Reset Register = 0x%08x\n",
+	    pmcs_rd_topunit(pwp, PMCS_SPC_RESET));
+	n += snprintf(&buf[n], (size_left - n), "-----------------\n"
+	    "Dump SPC version end \n");
+	return (n);
+}
+
+/*
+ * Dump MPI Table.
+ */
+static int
+pmcs_dump_mpi_table(pmcs_hw_t *pwp, caddr_t buf, uint32_t size_left)
+{
+	int n = 0;
+
+	n += snprintf(&buf[n], (size_left - n), "\nDump MSGU registers: \n"
+	    "-----------------\n");
+	n += snprintf(&buf[n], (size_left - n), "inb_doorbell = 0x%08x\n",
+	    pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB));
+	n += snprintf(&buf[n], (size_left - n), "inb_doorbell_clear = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB_CLEAR));
+	n += snprintf(&buf[n], (size_left - n), "outb_doorbell = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB));
+	n += snprintf(&buf[n], (size_left - n), "outb_doorbell_clear = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR));
+	n += snprintf(&buf[n], (size_left - n), "scratch_pad0 = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0));
+	n += snprintf(&buf[n], (size_left - n), "scratch_pad1 = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1));
+	n += snprintf(&buf[n], (size_left - n), "scratch_pad2 = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2));
+	n += snprintf(&buf[n], (size_left - n), "scratch_pad3 = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH3));
+	n += snprintf(&buf[n], (size_left - n), "host_scratch_pad0 = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0));
+	n += snprintf(&buf[n], (size_left - n), "host_scratch_pad1 = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH1));
+	n += snprintf(&buf[n], (size_left - n), "host_scratch_pad2 = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH2));
+	n += snprintf(&buf[n], (size_left - n), "host_scratch_pad3 = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH3));
+	n += snprintf(&buf[n], (size_left - n), "host_scratch_pad4 = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH4));
+	n += snprintf(&buf[n], (size_left - n), "host_scratch_pad5 = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH5));
+	n += snprintf(&buf[n], (size_left - n), "host_scratch_pad6 = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH6));
+	n += snprintf(&buf[n], (size_left - n), "host_scratch_pad7 = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH7));
+	n += snprintf(&buf[n], (size_left - n), "outb_doorbell_mask = 0x%08x"
+	    "\n", pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_MASK));
+
+	n += snprintf(&buf[n], (size_left - n), "MPI Configuration Table: \n"
+	    "-----------------\n");
+	n += snprintf(&buf[n], (size_left - n), "ASCII Signature = 0x%08x\n",
+	    pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS));
+	n += snprintf(&buf[n], (size_left - n), "Firmware Release Type = "
+	    "0x%08x\n", PMCS_FW_TYPE(pwp));
+	n += snprintf(&buf[n], (size_left - n), "Firmware Release Variant = "
+	    "0x%08x\n", PMCS_FW_VARIANT(pwp));
+	n += snprintf(&buf[n], (size_left - n), "Firmware Sub-Minor Release "
+	    "Number = 0x%08x\n", PMCS_FW_MICRO(pwp));
+	n += snprintf(&buf[n], (size_left - n), "Firmware Minor Release "
+	    "Number = 0x%08x\n", PMCS_FW_MINOR(pwp));
+	n += snprintf(&buf[n], (size_left - n), "Firmware Major Release "
+	    "Number = 0x%08x\n", PMCS_FW_MAJOR(pwp));
+	n += snprintf(&buf[n], (size_left - n), "Maximum Outstanding I/Os "
+	    "supported = 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MOIO));
+	n += snprintf(&buf[n], (size_left - n), "Maximum Scatter-Gather List "
+	    "Elements = 0x%08x\n",
+	    PMCS_MSGL(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO0)));
+	n += snprintf(&buf[n], (size_left - n), "Maximum number of devices "
+	    "connected to the SPC = 0x%08x\n",
+	    PMCS_MD(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO0)));
+	n += snprintf(&buf[n], (size_left - n), "Maximum Number of IQs "
+	    "supported = 0x%08x\n",
+	    PMCS_MNIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)));
+	n += snprintf(&buf[n], (size_left - n), "Maximum Number of OQs "
+	    "supported = 0x%08x\n",
+	    PMCS_MNOQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)));
+	n += snprintf(&buf[n], (size_left - n), "High Priority Queue supported"
+	    " = 0x%08x\n", PMCS_HPIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)));
+	n += snprintf(&buf[n], (size_left - n), "Interrupt Coalescing supported"
+	    " = 0x%08x\n", PMCS_ICS(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)));
+	n += snprintf(&buf[n], (size_left - n), "Number of Phys = "
+	    "0x%08x\n", PMCS_NPHY(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)));
+	n += snprintf(&buf[n], (size_left - n), "SAS Revision Specification = "
+	    "0x%08x\n", PMCS_SASREV(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1)));
+	n += snprintf(&buf[n], (size_left - n), "General Status Table Offset = "
+	    "0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_GSTO));
+	n += snprintf(&buf[n], (size_left - n), "Inbound Queue Configuration "
+	    "Table Offset = 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IQCTO));
+	n += snprintf(&buf[n], (size_left - n), "Outbound Queue Configuration "
+	    "Table Offset = 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_OQCTO));
+	n += snprintf(&buf[n], (size_left - n), "Inbound Queue Normal/High "
+	    "Priority Processing Depth = 0x%02x 0x%02x\n",
+	    (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO2) & IQ_NORMAL_PRI_DEPTH_MASK),
+	    ((pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO2) &
+	    IQ_HIPRI_PRI_DEPTH_MASK) >> IQ_HIPRI_PRI_DEPTH_SHIFT));
+	n += snprintf(&buf[n], (size_left - n), "General Event Notification "
+	    "Queue = 0x%02x\n", (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO2) &
+	    GENERAL_EVENT_OQ_MASK) >> GENERAL_EVENT_OQ_SHIFT);
+	n += snprintf(&buf[n], (size_left - n), "Device Handle Removed "
+	    "Notification Queue = 0x%02x\n",
+	    (uint32_t)(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO2) &
+	    DEVICE_HANDLE_REMOVED_MASK) >> DEVICE_HANDLE_REMOVED_SHIFT);
+	for (uint8_t i = 0; i < pwp->nphy; i++) {
+		uint32_t woff = i / 4;
+		uint32_t shf = (i % 4) * 8;
+		n += snprintf(&buf[n], (size_left - n), "SAS HW Event "
+		    "Notification Queue - PHY ID %d = 0x%02x\n", i,
+		    (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_EVQS + (woff << 2)) >> shf)
+		    & 0xff);
+	}
+	for (uint8_t i = 0; i < pwp->nphy; i++) {
+		uint32_t woff = i / 4;
+		uint32_t shf = (i % 4) * 8;
+		n += snprintf(&buf[n], (size_left - n), "SATA NCQ Error "
+		    "Event Notification Queue - PHY ID %d = 0x%02x\n", i,
+		    (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_SNCQ + (woff << 2)) >> shf)
+		    & 0xff);
+	}
+	for (uint8_t i = 0; i < pwp->nphy; i++) {
+		uint32_t woff = i / 4;
+		uint32_t shf = (i % 4) * 8;
+		n += snprintf(&buf[n], (size_left - n), "I_T Nexus Target "
+		    "Event Notification Queue - PHY ID %d = 0x%02x\n", i,
+		    (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IT_NTENQ +
+		    (woff << 2)) >> shf) & 0xff);
+	}
+	for (uint8_t i = 0; i < pwp->nphy; i++) {
+		uint32_t woff = i / 4;
+		uint32_t shf = (i % 4) * 8;
+		n += snprintf(&buf[n], (size_left - n), "SSP Target "
+		    "Event Notification Queue - PHY ID %d = 0x%02x\n", i,
+		    (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_SSP_TENQ +
+		    (woff << 2)) >> shf) & 0xff);
+	}
+	for (uint8_t i = 0; i < pwp->nphy; i++) {
+		uint32_t woff = i / 4;
+		uint32_t shf = (i % 4) * 8;
+		n += snprintf(&buf[n], (size_left - n), "SMP Target "
+		    "Event Notification Queue - PHY ID %d = 0x%02x\n", i,
+		    (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_SMP_TENQ +
+		    (woff << 2)) >> shf) & 0xff);
+	}
+	n += snprintf(&buf[n], (size_left - n), "MSGU Event Log Buffer Address "
+	    "Higher = 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MELBAH));
+	n += snprintf(&buf[n], (size_left - n), "MSGU Event Log Buffer Address "
+	    "Lower = 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MELBAL));
+	n += snprintf(&buf[n], (size_left - n), "MSGU Event Log Buffer Size "
+	    "= 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MELBS));
+	n += snprintf(&buf[n], (size_left - n), "MSGU Event Log Severity "
+	    "= 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MELSEV));
+	n += snprintf(&buf[n], (size_left - n), "IOP Event Log Buffer Address "
+	    "Higher = 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IELBAH));
+	n += snprintf(&buf[n], (size_left - n), "IOP Event Log Buffer Address "
+	    "Lower = 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IELBAL));
+	n += snprintf(&buf[n], (size_left - n), "IOP Event Log Buffer Size "
+	    "= 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IELBS));
+	n += snprintf(&buf[n], (size_left - n), "IOP Event Log Severity "
+	    "= 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IELSEV));
+	n += snprintf(&buf[n], (size_left - n), "Fatal Error Interrupt "
+	    "= 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FERR));
+	n += snprintf(&buf[n], (size_left - n),
+	    "Fatal Error Register Dump Offset "
+	    "For MSGU = 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_FERDOMSGU));
+	n += snprintf(&buf[n], (size_left - n),
+	    "Fatal Error Register Dump Length "
+	    "For MSGU = 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_FERDLMSGU));
+	n += snprintf(&buf[n], (size_left - n),
+	    "Fatal Error Register Dump Offset "
+	    "For IOP = 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_FERDOIOP));
+	n += snprintf(&buf[n], (size_left - n),
+	    "Fatal Error Register Dump Length "
+	    "For IOP = 0x%08x\n", pmcs_rd_mpi_tbl(pwp, PMCS_FERDLIOP));
+
+	n += snprintf(&buf[n], (size_left - n), "Dump GS Table: \n"
+	    "-----------------\n");
+	n += snprintf(&buf[n], (size_left - n),  "GST MPI State: 0x%08x\n",
+	    pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE));
+	n += snprintf(&buf[n], (size_left - n),  "Inbound Queue Freeze State 0 "
+	    "= 0x%08x\n", pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ0));
+	n += snprintf(&buf[n], (size_left - n), "Inbound Queue Freeze State 1 "
+	    "= 0x%08x\n", pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ1));
+	n += snprintf(&buf[n], (size_left - n), "MSGU Tick Count = 0x%08x \n",
+	    pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK));
+	n += snprintf(&buf[n], (size_left - n), "IOP Tick Count = 0x%08x\n",
+	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK));
+	for (uint8_t i = 0; i < pwp->nphy; i++) {
+		n += snprintf(&buf[n], (size_left - n), " Phy %d state = "
+		    "0x%08x\n", i, pmcs_rd_gst_tbl(pwp, PMCS_GST_PHY_INFO(i)));
+	}
+	for (uint8_t i = 0; i < pwp->nphy; i++) {
+		n += snprintf(&buf[n], (size_left - n), " Recoverable Error "
+		    "Information %d = 0x%08x\n", i,
+		    pmcs_rd_gst_tbl(pwp, PMCS_GST_RERR_INFO(i)));
+	}
+
+	n += snprintf(&buf[n], (size_left - n), "Dump IQCT Table\n"
+	    "-----------------\n");
+	for (uint8_t i = 0; i < PMCS_NIQ; i++) {
+		n += snprintf(&buf[n], (size_left - n), "Inbound Queue "
+		    "Configuration Table - [%d]:\n", i);
+		n += snprintf(&buf[n], (size_left - n), "    Inbound Queue "
+		    "Depth = 0x%08x\n",
+		    PMCS_IQDX(pmcs_rd_iqc_tbl(pwp, PMCS_IQC_PARMX(i))));
+		n += snprintf(&buf[n], (size_left - n), "    Inbound Queue "
+		    "Element Size and Priority = 0x%08x 0x%08x\n",
+		    PMCS_IQESX(pmcs_rd_iqc_tbl(pwp, PMCS_IQC_PARMX(i))),
+		    PMCS_IQPX(pmcs_rd_iqc_tbl(pwp, PMCS_IQC_PARMX(i))));
+		n += snprintf(&buf[n], (size_left - n), "    Inbound Queue "
+		    "Base Address High = 0x%08x\n",
+		    pmcs_rd_iqc_tbl(pwp, PMCS_IQBAHX(i)));
+		n += snprintf(&buf[n], (size_left - n), "    Inbound Queue "
+		    "Base Address Low = 0x%08x\n",
+		    pmcs_rd_iqc_tbl(pwp, PMCS_IQBALX(i)));
+		n += snprintf(&buf[n], (size_left - n), "    Inbound Queue "
+		    "Consumer Index Base Address High = 0x%08x\n",
+		    pmcs_rd_iqc_tbl(pwp, PMCS_IQCIBAHX(i)));
+		n += snprintf(&buf[n], (size_left - n), "    Inbound Queue "
+		    "Consumer Index Base Address Low = 0x%08x\n",
+		    pmcs_rd_iqc_tbl(pwp, PMCS_IQCIBALX(i)));
+		n += snprintf(&buf[n], (size_left - n), "    Inbound Queue "
+		    "Producer Index PCI BAR = 0x%08x\n",
+		    pmcs_rd_iqc_tbl(pwp, PMCS_IQPIBARX(i)));
+		n += snprintf(&buf[n], (size_left - n), "    Inbound Queue "
+		    "Producer Index PCI BAR offset = 0x%08x\n",
+		    pmcs_rd_iqc_tbl(pwp, PMCS_IQPIOFFX(i)));
+	}
+
+	n += snprintf(&buf[n], (size_left - n), "Dump OQCT Table: \n"
+	    "-----------------\n");
+	for (uint8_t i = 0; i < PMCS_NOQ; i++) {
+		n += snprintf(&buf[n], (size_left - n), "Outbound Queue "
+		    "Configuration Table - [%d]:\n", i);
+		n += snprintf(&buf[n], (size_left - n), "    Outbound Queue "
+		    "Depth = 0x%08x\n",
+		    PMCS_OQDX(pmcs_rd_oqc_tbl(pwp, PMCS_OQC_PARMX(i))));
+		n += snprintf(&buf[n], (size_left - n), "    Outbound Queue "
+		    "Element Size = 0x%08x\n",
+		    PMCS_OQESX(pmcs_rd_oqc_tbl(pwp, PMCS_OQC_PARMX(i))));
+		n += snprintf(&buf[n], (size_left - n), "    Outbound Queue "
+		    "Base Address High = 0x%08x\n",
+		    pmcs_rd_oqc_tbl(pwp, PMCS_OQBAHX(i)));
+		n += snprintf(&buf[n], (size_left - n), "    Outbound Queue "
+		    "Base Address Low = 0x%08x\n",
+		    pmcs_rd_oqc_tbl(pwp, PMCS_OQBALX(i)));
+		n += snprintf(&buf[n], (size_left - n), "    Outbound Queue "
+		    "Producer Index Base Address High = 0x%08x\n",
+		    pmcs_rd_oqc_tbl(pwp, PMCS_OQPIBAHX(i)));
+		n += snprintf(&buf[n], (size_left - n), "    Outbound Queue "
+		    "Producer Index Base Address Low = 0x%08x\n",
+		    pmcs_rd_oqc_tbl(pwp, PMCS_OQPIBALX(i)));
+		n += snprintf(&buf[n], (size_left - n), "    Outbound Queue "
+		    "Consumer Index PCI BAR = 0x%08x\n",
+		    pmcs_rd_oqc_tbl(pwp, PMCS_OQCIBARX(i)));
+		n += snprintf(&buf[n], (size_left - n), "    Outbound Queue "
+		    "Consumer Index PCI BAR offset = 0x%08x\n",
+		    pmcs_rd_oqc_tbl(pwp, PMCS_OQCIOFFX(i)));
+
+		n += snprintf(&buf[n], (size_left - n), "    Outbound Queue "
+		    "Interrupt Coalescing Timeout = 0x%08x\n",
+		    PMCS_OQICT(pmcs_rd_oqc_tbl(pwp, PMCS_OQIPARM(i))));
+		n += snprintf(&buf[n], (size_left - n), "    Outbound Queue "
+		    "Interrupt Coalescing Count = 0x%08x\n",
+		    PMCS_OQICC(pmcs_rd_oqc_tbl(pwp, PMCS_OQIPARM(i))));
+		n += snprintf(&buf[n], (size_left - n), "    Outbound Queue "
+		    "Interrupt Vector =  0x%08x\n",
+		    PMCS_OQIV(pmcs_rd_oqc_tbl(pwp, PMCS_OQIPARM(i))));
+		n += snprintf(&buf[n], (size_left - n), "    Outbound Queue "
+		    "Dynamic Interrupt Coalescing Timeout = 0x%08x\n",
+		    pmcs_rd_oqc_tbl(pwp, PMCS_OQDICX(i)));
+
+	}
+	n += snprintf(&buf[n], (size_left - n), "-----------------\n"
+	    "Dump MPI Table end\n");
+	return (n);
+}
+
+/*ARGSUSED*/
+int
+pmcs_dump_binary(pmcs_hw_t *pwp, uint32_t *addr, uint32_t off,
+    uint32_t words_to_read, caddr_t buf, uint32_t size_left)
+{
+	uint32_t i;
+	int n = 0;
+	char c = ' ';
+
+	for (i = 0, n = 0; i < words_to_read; i++) {
+		if ((i & 7) == 0) {
+			n += snprintf(&buf[n], (size_left - n),
+			    "%08x: ", (i << 2) + off);
+		}
+		if ((i + 1) & 7) {
+			c = ' ';
+		} else {
+			c = '\n';
+		}
+		n += snprintf(&buf[n], (size_left - n), "%08x%c", addr[i], c);
+	}
+	return (n);
+}
+
+/*
+ * Dump Global Shared Memory Configuration Registers
+ */
+static int
+pmcs_dump_gsm_conf(pmcs_hw_t *pwp, caddr_t buf, uint32_t size_left)
+{
+	int n = 0;
+
+	n += snprintf(&buf[n], (size_left - n), "\nDump GSM configuration "
+	    "registers: \n -----------------\n");
+	n += snprintf(&buf[n], (size_left - n), "RB6 Access Register = "
+	    "0x%08x\n", pmcs_rd_gsm_reg(pwp, RB6_ACCESS));
+	n += snprintf(&buf[n], (size_left - n), "CFG and RST = 0x%08x\n",
+	    pmcs_rd_gsm_reg(pwp, GSM_CFG_AND_RESET));
+	n += snprintf(&buf[n], (size_left - n), "RAM ECC ERR INDICATOR= "
+	    "0x%08x\n", pmcs_rd_gsm_reg(pwp, RAM_ECC_DOUBLE_ERROR_INDICATOR));
+	n += snprintf(&buf[n], (size_left - n), "READ ADR PARITY CHK EN = "
+	    "0x%08x\n", pmcs_rd_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN));
+	n += snprintf(&buf[n], (size_left - n), "WRITE ADR PARITY CHK EN = "
+	    "0x%08x\n", pmcs_rd_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN));
+	n += snprintf(&buf[n], (size_left - n), "WRITE DATA PARITY CHK EN= "
+	    "0x%08x\n", pmcs_rd_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN));
+	n += snprintf(&buf[n], (size_left - n),
+	    "READ ADR PARITY ERROR INDICATOR = 0x%08x\n",
+	    pmcs_rd_gsm_reg(pwp, READ_ADR_PARITY_ERROR_INDICATOR));
+	n += snprintf(&buf[n], (size_left - n),
+	    "WRITE ADR PARITY ERROR INDICATOR = 0x%08x\n",
+	    pmcs_rd_gsm_reg(pwp, WRITE_ADR_PARITY_ERROR_INDICATOR));
+	n += snprintf(&buf[n], (size_left - n),
+	    "WRITE DATA PARITY ERROR INDICATOR = 0x%08x\n",
+	    pmcs_rd_gsm_reg(pwp, WRITE_DATA_PARITY_ERROR_INDICATOR));
+	n += snprintf(&buf[n], (size_left - n), "NMI Enable VPE0 IOP Register"
+	    " = 0x%08x\n", pmcs_rd_gsm_reg(pwp, NMI_EN_VPE0_IOP));
+	n += snprintf(&buf[n], (size_left - n), "NMI Enable VPE0 AAP1 Register"
+	    " = 0x%08x\n", pmcs_rd_gsm_reg(pwp, NMI_EN_VPE0_AAP1));
+	n += snprintf(&buf[n], (size_left - n), "-----------------\n"
+	    "Dump GSM configuration registers end \n");
+	return (n);
+}
+
+/*
+ * Dump PCIe Configuration Registers.
+ */
+static int
+pmcs_dump_pcie_conf(pmcs_hw_t *pwp, caddr_t buf, uint32_t size_left)
+{
+	int n = 0;
+	uint32_t i = 0;
+
+	n += snprintf(&buf[n], (size_left - n), "\nDump PCIe configuration "
+	    "registers: \n -----------------\n");
+	n += snprintf(&buf[n], (size_left - n), "VENID = 0x%04x\n",
+	    pci_config_get16(pwp->pci_acc_handle, PCI_CONF_VENID));
+	n += snprintf(&buf[n], (size_left - n), "DEVICE_ID = 0x%04x\n",
+	    pci_config_get16(pwp->pci_acc_handle, PCI_CONF_DEVID));
+	n += snprintf(&buf[n], (size_left - n), "CFGCMD = 0x%04x\n",
+	    pci_config_get16(pwp->pci_acc_handle, PCI_CONF_COMM));
+	n += snprintf(&buf[n], (size_left - n), "CFGSTAT = 0x%04x\n",
+	    pci_config_get16(pwp->pci_acc_handle, PCI_CONF_STAT));
+	n += snprintf(&buf[n], (size_left - n), "CLSCODE and REVID = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PCI_CONF_REVID));
+	n += snprintf(&buf[n], (size_left - n), "BIST HDRTYPE LATTIM CLSIZE = "
+	    "0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PCI_CONF_CACHE_LINESZ));
+	n += snprintf(&buf[n], (size_left - n), "MEMBASE-I LOWER = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PCI_CONF_BASE0));
+	n += snprintf(&buf[n], (size_left - n), "MEMBASE-I UPPER = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PCI_CONF_BASE1));
+	n += snprintf(&buf[n], (size_left - n), "MEMBASE-II LOWER = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PCI_CONF_BASE2));
+	n += snprintf(&buf[n], (size_left - n), "MEMBASE-II UPPER = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PCI_CONF_BASE3));
+	n += snprintf(&buf[n], (size_left - n), "MEMBASE-III = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PCI_CONF_BASE4));
+	n += snprintf(&buf[n], (size_left - n), "MEMBASE-IV = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PCI_CONF_BASE5));
+	n += snprintf(&buf[n], (size_left - n), "SVID = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PCI_CONF_SUBVENID));
+	n += snprintf(&buf[n], (size_left - n), "ROMBASE = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PCI_CONF_ROM));
+	n += snprintf(&buf[n], (size_left - n), "CAP_PTR = 0x%02x\n",
+	    pci_config_get8(pwp->pci_acc_handle, PCI_CONF_CAP_PTR));
+	n += snprintf(&buf[n], (size_left - n), "MAXLAT MINGNT INTPIN "
+	    "INTLINE = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PCI_CONF_ILINE));
+	n += snprintf(&buf[n], (size_left - n), "PMC PM_NEXT_CAP PM_CAP_ID = "
+	    "0x%08x\n", pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_PMC));
+	n += snprintf(&buf[n], (size_left - n), "PMCSR = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_PMCSR));
+	n += snprintf(&buf[n], (size_left - n),
+	    "MC MSI_NEXT_CAP MSI_CAP_ID = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_MSI));
+	n += snprintf(&buf[n], (size_left - n), "MAL = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_MAL));
+	n += snprintf(&buf[n], (size_left - n), "MAU = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_MAU));
+	n += snprintf(&buf[n], (size_left - n), "MD = 0x%04x\n",
+	    pci_config_get16(pwp->pci_acc_handle, PMCS_PCI_MD));
+	n += snprintf(&buf[n], (size_left - n),
+	    "PCIE_CAP PCIE_NEXT_CAP PCIE_CAP_ID = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_PCIE));
+	n += snprintf(&buf[n], (size_left - n), "DEVICE_CAP = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_DEV_CAP));
+	n += snprintf(&buf[n], (size_left - n),
+	    "DEVICE_STAT DEVICE_CTRL = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_DEV_CTRL));
+	n += snprintf(&buf[n], (size_left - n), "LINK_CAP = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_LINK_CAP));
+	n += snprintf(&buf[n], (size_left - n),
+	    "LINK_STAT LINK_CTRL = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_LINK_CTRL));
+	n += snprintf(&buf[n], (size_left - n), "MSIX_CAP = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_MSIX_CAP));
+	n += snprintf(&buf[n], (size_left - n), "TBL_OFFSET = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_TBL_OFFSET));
+	n += snprintf(&buf[n], (size_left - n), "PBA_OFFSET = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_PBA_OFFSET));
+	n += snprintf(&buf[n], (size_left - n), "PCIE_CAP_HD = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_PCIE_CAP_HD));
+	n += snprintf(&buf[n], (size_left - n), "UE_STAT = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_UE_STAT));
+	n += snprintf(&buf[n], (size_left - n), "UE_MASK = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_UE_MASK));
+	n += snprintf(&buf[n], (size_left - n), "UE_SEV = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_UE_SEV));
+	n += snprintf(&buf[n], (size_left - n), "CE_STAT = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_CE_STAT));
+	n += snprintf(&buf[n], (size_left - n), "CE_MASK = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_CE_MASK));
+	n += snprintf(&buf[n], (size_left - n), "ADV_ERR_CTRL = 0x%08x\n",
+	    pci_config_get32(pwp->pci_acc_handle, PMCS_PCI_ADV_ERR_CTRL));
+	for (i = 0; i < 4; i++) {
+		n += snprintf(&buf[n], (size_left - n), "HD_LOG_DW%d = "
+		    "0x%08x\n", i, pci_config_get32(pwp->pci_acc_handle,
+		    (PMCS_PCI_HD_LOG_DW + i * 4)));
+	}
+	n += snprintf(&buf[n], (size_left - n), "-----------------\n"
+	    "Dump PCIe configuration registers end \n");
+	return (n);
+}
+/*
+ * Called with axil_lock held
+ */
+static boolean_t
+pmcs_shift_axil(pmcs_hw_t *pwp, uint32_t offset)
+{
+	uint32_t newaxil = offset & ~GSM_BASE_MASK;
+
+	ASSERT(mutex_owned(&pwp->axil_lock));
+	ddi_put32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
+	drv_usecwait(10);
+
+	if (ddi_get32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register update failed");
+		return (B_FALSE);
+	}
+	return (B_TRUE);
+}
+
+static uint32_t
+pmcs_get_axil(pmcs_hw_t *pwp)
+{
+	uint32_t regval = 0;
+	mutex_enter(&pwp->axil_lock);
+	regval = ddi_get32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]);
+	mutex_exit(&pwp->axil_lock);
+	return (regval);
+}
+
+static void
+pmcs_restore_axil(pmcs_hw_t *pwp, uint32_t oldaxil)
+{
+	mutex_enter(&pwp->axil_lock);
+	ddi_put32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
+	drv_usecwait(10);
+
+	if (ddi_get32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register restore failed");
+	}
+	mutex_exit(&pwp->axil_lock);
+}
+
+/*
+ * Dump GSM Memory Regions.
+ */
+static int
+pmcs_dump_gsm(pmcs_hw_t *pwp, caddr_t buf, uint32_t size_left)
+{
+	int n = 0;
+	uint32_t i = 0;
+	uint32_t oldaxil = 0;
+	uint32_t gsm_addr = 0;
+	uint32_t *local_buf = NULL;
+
+	local_buf = kmem_zalloc(GSM_SM_BLKSZ, KM_NOSLEEP);
+	if (local_buf == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: local_buf memory not allocated", __func__);
+		return (0);
+	}
+
+	oldaxil = pmcs_get_axil(pwp);
+	mutex_enter(&pwp->axil_lock);
+	n += snprintf(&buf[n], (size_left - n), "\nDump GSM IO Status Table: \n"
+	    " -----------------\n");
+	for (i = 0; i < 4; i++) {
+		gsm_addr = IO_STATUS_TABLE_BASE + GSM_SM_BLKSZ * i;
+		if (pmcs_shift_axil(pwp, gsm_addr) == B_TRUE) {
+			gsm_addr &= GSM_BASE_MASK;
+			ddi_rep_get32(pwp->gsm_acc_handle, local_buf,
+			    &pwp->gsm_regs[gsm_addr >> 2], GSM_SM_BLKSZ >> 2,
+			    DDI_DEV_AUTOINCR);
+			n += pmcs_dump_binary(pwp, local_buf, i * GSM_SM_BLKSZ,
+			    GSM_SM_BLKSZ >> 2, &buf[n], size_left - n);
+		}
+	}
+	n += snprintf(&buf[n], (size_left - n), "\n-----------------\n"
+	    "Dump GSM IO Status Table end \n");
+	n += snprintf(&buf[n], (size_left - n), "\nDump Ring Buffer Storage: \n"
+	    " -----------------\n");
+	for (i = 0; i < 2; i++) {
+		gsm_addr = RING_BUF_STORAGE_0 + GSM_SM_BLKSZ * i;
+		if (pmcs_shift_axil(pwp, gsm_addr) == B_TRUE) {
+			gsm_addr &= GSM_BASE_MASK;
+			ddi_rep_get32(pwp->gsm_acc_handle, local_buf,
+			    &pwp->gsm_regs[gsm_addr >> 2], GSM_SM_BLKSZ >> 2,
+			    DDI_DEV_AUTOINCR);
+			n += pmcs_dump_binary(pwp, local_buf, i * GSM_SM_BLKSZ,
+			    GSM_SM_BLKSZ >> 2, &buf[n], size_left - n);
+		}
+	}
+	n += snprintf(&buf[n], (size_left - n), "\n-----------------\n"
+	    "Dump Ring Buffer Storage end \n");
+
+	n += snprintf(&buf[n], (size_left - n), "\nDump Ring Buffer Pointers:\n"
+	    " -----------------\n");
+		gsm_addr = RING_BUF_PTR_ACC_BASE + RING_BUF_PTR_OFF;
+		if (pmcs_shift_axil(pwp, gsm_addr) == B_TRUE) {
+			gsm_addr &= GSM_BASE_MASK;
+			ddi_rep_get32(pwp->gsm_acc_handle, local_buf,
+			    &pwp->gsm_regs[gsm_addr >> 2],
+			    RING_BUF_PTR_SIZE >> 2, DDI_DEV_AUTOINCR);
+			n += pmcs_dump_binary(pwp, local_buf, 0,
+			    RING_BUF_PTR_SIZE >> 2, &buf[n], size_left - n);
+		}
+	n += snprintf(&buf[n], (size_left - n), "\n-----------------\n"
+	    "Dump Ring Buffer Pointers end \n");
+
+	n += snprintf(&buf[n], (size_left - n), "\nDump Ring Buffer Access: \n"
+	    " -----------------\n");
+		gsm_addr = RING_BUF_PTR_ACC_BASE + RING_BUF_ACC_OFF;
+		if (pmcs_shift_axil(pwp, gsm_addr) == B_TRUE) {
+			gsm_addr &= GSM_BASE_MASK;
+			ddi_rep_get32(pwp->gsm_acc_handle, local_buf,
+			    &pwp->gsm_regs[gsm_addr >> 2],
+			    RING_BUF_ACC_SIZE >> 2, DDI_DEV_AUTOINCR);
+			n += pmcs_dump_binary(pwp, local_buf, 0,
+			    RING_BUF_ACC_SIZE >> 2, &buf[n], size_left - n);
+		}
+	n += snprintf(&buf[n], (size_left - n), "\n-----------------\n"
+	    "Dump Ring Buffer Access end \n");
+
+	n += snprintf(&buf[n], (size_left - n), "\nDump GSM SM: \n"
+	    " -----------------\n");
+	for (i = 0; i < 16; i++) {
+		gsm_addr = GSM_SM_BASE + GSM_SM_BLKSZ * i;
+		if (pmcs_shift_axil(pwp, gsm_addr) == B_TRUE) {
+			gsm_addr &= GSM_BASE_MASK;
+			ddi_rep_get32(pwp->gsm_acc_handle, local_buf,
+			    &pwp->gsm_regs[gsm_addr >> 2],
+			    GSM_SM_BLKSZ >> 2, DDI_DEV_AUTOINCR);
+			n += pmcs_dump_binary(pwp, local_buf, i * GSM_SM_BLKSZ,
+			    GSM_SM_BLKSZ >> 2, &buf[n], size_left - n);
+		}
+	}
+	mutex_exit(&pwp->axil_lock);
+	pmcs_restore_axil(pwp, oldaxil);
+
+	n += snprintf(&buf[n], (size_left - n), "\n-----------------\n"
+	    "Dump GSM SM end \n");
+	n += snprintf(&buf[n], (size_left - n), "-----------------\n"
+	    "\n------------ Dump GSM Memory Regions end  -------------\n");
+	if (local_buf) {
+		kmem_free(local_buf, GSM_SM_BLKSZ);
+	}
+	return (n);
+}
+
+/*
+ * Trace current Inbound Message host sent to SPC.
+ */
+void
+pmcs_iqp_trace(pmcs_hw_t *pwp, uint32_t qnum)
+{
+	uint32_t k = 0, n = 0;
+	uint32_t *ptr = NULL;
+	char *tbuf = pwp->iqpt->curpos;
+	uint32_t size_left = pwp->iqpt->size_left;
+
+	if (tbuf == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: trace buffer is not ready,"
+		    " Inbound Message from host to SPC is not traced",
+		    __func__);
+		return;
+	} else if (size_left < PMCS_QENTRY_SIZE * PMCS_QENTRY_SIZE) {
+		tbuf = pwp->iqpt->curpos = pwp->iqpt->head;
+		size_left = pwp->iqpt->size_left = PMCS_IQP_TRACE_BUFFER_SIZE;
+	}
+
+	ptr = &pwp->iqp[qnum][pwp->shadow_iqpi[qnum] *
+	    (PMCS_QENTRY_SIZE >> 2)];
+	for (k = 0; k < (PMCS_QENTRY_SIZE / sizeof (uint32_t));
+	    k += 8) {
+		n += snprintf(&tbuf[n], (size_left - n),
+		    "0x%08x 0x%08x 0x%08x 0x%08x "
+		    "0x%08x 0x%08x 0x%08x 0x%08x\n",
+		    LE_32(ptr[k]), LE_32(ptr[k+1]),
+		    LE_32(ptr[k+2]), LE_32(ptr[k+3]),
+		    LE_32(ptr[k+4]), LE_32(ptr[k+5]),
+		    LE_32(ptr[k+6]), LE_32(ptr[k+7]));
+	}
+	pwp->iqpt->size_left -= n;
+	if (pwp->iqpt->size_left > 0) {
+		pwp->iqpt->curpos += n;
+	} else {
+		pwp->iqpt->curpos =
+		    pwp->iqpt->head + PMCS_IQP_TRACE_BUFFER_SIZE - 1;
+	}
+}
+
+/*
+ * Dump fatal error register content from GSM.
+ */
+int
+pmcs_dump_feregs(pmcs_hw_t *pwp, uint32_t *addr, uint8_t nvmd,
+    caddr_t buf, uint32_t size_left)
+{
+	uint32_t offset = 0, length = 0;
+	int i = 0;
+	uint8_t *ptr = (uint8_t *)addr;
+
+	if ((addr == NULL) || (buf == NULL)) {
+		return (0);
+	}
+	switch (nvmd) {
+		case PMCIN_NVMD_AAP1:
+			offset = pmcs_rd_mpi_tbl(pwp, PMCS_FERDOMSGU);
+			length = pmcs_rd_mpi_tbl(pwp, PMCS_FERDLMSGU);
+			break;
+		case PMCIN_NVMD_IOP:
+			offset = pmcs_rd_mpi_tbl(pwp, PMCS_FERDOIOP);
+			length = pmcs_rd_mpi_tbl(pwp, PMCS_FERDLIOP);
+			break;
+		default:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "UNKNOWN NVMD DEVICE"
+			    "%s():%d", __func__, __LINE__);
+			return (0);
+	}
+
+	while ((i < length) && (ptr[i + offset] != 0xff) &&
+	    (ptr[i + offset] != '\0')) {
+		i += snprintf(&buf[i], (size_left - i),
+		    "%c", ptr[i + offset]);
+	}
+	return (i);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_intr.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,1654 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains functions that are called via interrupts.
+ */
+
+#include <sys/scsi/adapters/pmcs/pmcs.h>
+
+#ifdef	DEBUG
+#define	VALID_IOMB_CHECK(p, w, m, b, c)					\
+	if (!(w & PMCS_IOMB_VALID)) {					\
+		char l[64];						\
+		(void) snprintf(l, sizeof (l),				\
+		    "%s: INVALID IOMB (oq_ci=%u oq_pi=%u)", __func__, b, c); \
+		pmcs_print_entry(pwp, PMCS_PRT_DEBUG, l, m);		\
+		STEP_OQ_ENTRY(pwp, PMCS_OQ_EVENTS, b, 1);		\
+		continue;						\
+	}
+#define	WRONG_OBID_CHECK(pwp, w, q)	\
+	if (((w & PMCS_IOMB_OBID_MASK) >> PMCS_IOMB_OBID_SHIFT) != q) {	\
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,				\
+		    "%s: COMPLETION WITH WRONG OBID (0x%x)", __func__,	\
+		    (w & PMCS_IOMB_OBID_MASK) >> PMCS_IOMB_OBID_SHIFT);	\
+	}
+#else
+#define	VALID_IOMB_CHECK(a, b, c, d, e)
+#define	WRONG_OBID_CHECK(a, b, c)
+#endif
+
+#define	OQLIM_CHECK(p, l)				\
+	if (++l == (p)->ioq_depth) {			\
+		pmcs_prt(p, PMCS_PRT_DEBUG,		\
+		    "%s: possible ob queue overflow",	\
+		    __func__);				\
+		break;					\
+	}
+
+#define	COPY_OUTBOUND(p, w, l, n, a, x, q, c)				\
+	n = ((w & PMCS_IOMB_BC_MASK) >> PMCS_IOMB_BC_SHIFT);		\
+	a = PMCS_QENTRY_SIZE;						\
+	(void) memcpy(l, x, PMCS_QENTRY_SIZE);				\
+	if (n > 1) {							\
+		a <<= 1;						\
+		(void) memcpy(&l[PMCS_QENTRY_SIZE],			\
+		    GET_OQ_ENTRY(p, q, c, 1), PMCS_QENTRY_SIZE);	\
+	}								\
+	pmcs_prt(p, PMCS_PRT_DEBUG3,					\
+	    "%s: ptr %p ci %d w0 %x nbuf %d",				\
+	    __func__, (void *)x, ci, w0, n)
+
+#define	EVT_PRT(hwp, msg, phy)	\
+	pmcs_prt(hwp, PMCS_PRT_INFO, "Phy 0x%x: %s", phy, # msg)
+
+
+/*
+ * Map the link rate reported in the event to the SAS link rate value
+ */
+static uint8_t
+pmcs_link_rate(uint32_t event_link_rate)
+{
+	uint8_t sas_link_rate = 0;
+
+	switch (event_link_rate) {
+	case 1:
+		sas_link_rate = SAS_LINK_RATE_1_5GBIT;
+		break;
+	case 2:
+		sas_link_rate = SAS_LINK_RATE_3GBIT;
+		break;
+	case 4:
+		sas_link_rate = SAS_LINK_RATE_6GBIT;
+		break;
+	}
+
+	return (sas_link_rate);
+}
+
+/*
+ * Called with pwrk lock
+ */
+static void
+pmcs_complete_work(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb, size_t amt)
+{
+#ifdef	DEBUG
+	pwp->ltime[pwp->lti] = gethrtime();
+	pwp->ltags[pwp->lti++] = pwrk->htag;
+#endif
+	pwrk->htag |= PMCS_TAG_DONE;
+
+	/*
+	 * If the command has timed out, leave it in that state.
+	 */
+	if (pwrk->state != PMCS_WORK_STATE_TIMED_OUT) {
+		pwrk->state = PMCS_WORK_STATE_INTR;
+	}
+
+	pmcs_complete_work_impl(pwp, pwrk, iomb, amt);
+}
+
+static void
+pmcs_work_not_found(pmcs_hw_t *pwp, uint32_t htag, uint32_t *iomb)
+{
+#ifdef	DEBUG
+	int i;
+	hrtime_t now;
+	char buf[64];
+
+	(void) snprintf(buf, sizeof (buf),
+	    "unable to find work structure for tag 0x%x", htag);
+
+	pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, iomb);
+	if (htag == 0) {
+		return;
+	}
+	now = gethrtime();
+	for (i = 0; i < 256; i++) {
+		mutex_enter(&pwp->dbglock);
+		if (pwp->ltags[i] == htag) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "same tag already completed (%llu us ago)",
+			    (unsigned long long) (now - pwp->ltime[i]));
+		}
+		if (pwp->ftags[i] == htag) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "same tag started (line %d) (%llu ns ago)",
+			    pwp->ftag_lines[i], (unsigned long long)
+			    (now - pwp->ftime[i]));
+		}
+		mutex_exit(&pwp->dbglock);
+	}
+#else
+	char buf[64];
+	(void) snprintf(buf, sizeof (buf),
+	    "unable to find work structure for tag 0x%x", htag);
+	pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, iomb);
+#endif
+}
+
+
+static void
+pmcs_process_io_completion(pmcs_hw_t *pwp, pmcs_iocomp_cb_t *ioccb, size_t amt)
+{
+	pmcwork_t *pwrk;
+	uint32_t tag_type;
+	uint32_t htag = LE_32(((uint32_t *)((void *)ioccb->iomb))[1]);
+
+	pwrk = pmcs_tag2wp(pwp, htag);
+	if (pwrk == NULL) {
+		pmcs_work_not_found(pwp, htag, (void *)&ioccb->iomb);
+		kmem_cache_free(pwp->iocomp_cb_cache, ioccb);
+		return;
+	}
+
+	pwrk->htag |= PMCS_TAG_DONE;
+
+	/*
+	 * If the command has timed out, leave it in that state.
+	 */
+	if (pwrk->state != PMCS_WORK_STATE_TIMED_OUT) {
+		pwrk->state = PMCS_WORK_STATE_INTR;
+	}
+
+	/*
+	 * Some SATA and SAS commands are run in "WAIT" mode.
+	 * We can tell this from the tag type. In this case,
+	 * we just do a wakeup (not a callback).
+	 */
+	tag_type = PMCS_TAG_TYPE(pwrk->htag);
+	if (tag_type == PMCS_TAG_TYPE_WAIT) {
+		ASSERT(PMCS_TAG_TYPE(pwrk->htag) == PMCS_TAG_TYPE_WAIT);
+		if (pwrk->arg && amt) {
+			(void) memcpy(pwrk->arg, ioccb->iomb, amt);
+		}
+		cv_signal(&pwrk->sleep_cv);
+		mutex_exit(&pwrk->lock);
+		kmem_cache_free(pwp->iocomp_cb_cache, ioccb);
+		return;
+	}
+	ASSERT(tag_type == PMCS_TAG_TYPE_CBACK);
+
+#ifdef	DEBUG
+	pwp->ltime[pwp->lti] = gethrtime();
+	pwp->ltags[pwp->lti++] = pwrk->htag;
+#endif
+
+	ioccb->pwrk = pwrk;
+
+	/*
+	 * Only update state to IOCOMPQ if we were in the INTR state.
+	 * Any other state (e.g. TIMED_OUT, ABORTED) needs to remain.
+	 */
+	if (pwrk->state == PMCS_WORK_STATE_INTR) {
+		pwrk->state = PMCS_WORK_STATE_IOCOMPQ;
+	}
+
+	mutex_enter(&pwp->cq_lock);
+	if (pwp->iocomp_cb_tail) {
+		pwp->iocomp_cb_tail->next = ioccb;
+		pwp->iocomp_cb_tail = ioccb;
+	} else {
+		pwp->iocomp_cb_head = ioccb;
+		pwp->iocomp_cb_tail = ioccb;
+	}
+	ioccb->next = NULL;
+	mutex_exit(&pwp->cq_lock);
+
+	mutex_exit(&pwrk->lock);
+	/* Completion queue will be run at end of pmcs_iodone_intr */
+}
+
+
+static void
+pmcs_process_completion(pmcs_hw_t *pwp, void *iomb, size_t amt)
+{
+	pmcwork_t *pwrk;
+	uint32_t htag = LE_32(((uint32_t *)iomb)[1]);
+
+	pwrk = pmcs_tag2wp(pwp, htag);
+	if (pwrk == NULL) {
+		pmcs_work_not_found(pwp, htag, iomb);
+		return;
+	}
+
+	pmcs_complete_work(pwp, pwrk, iomb, amt);
+	/*
+	 * The pwrk lock is now released
+	 */
+}
+
+static void
+pmcs_kill_port(pmcs_hw_t *pwp, int portid)
+{
+	pmcs_phy_t *pptr = pwp->ports[portid];
+
+	if (pptr == NULL) {
+		return;
+	}
+
+	/*
+	 * Clear any subsidiary phys
+	 */
+	mutex_enter(&pwp->lock);
+
+	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
+		pmcs_lock_phy(pptr);
+		if (pptr->link_rate && pptr->portid == portid &&
+		    pptr->subsidiary) {
+			pmcs_clear_phy(pwp, pptr);
+		}
+		pmcs_unlock_phy(pptr);
+	}
+
+	pptr = pwp->ports[portid];
+	pwp->ports[portid] = NULL;
+	mutex_exit(&pwp->lock);
+
+	pmcs_lock_phy(pptr);
+	pmcs_kill_changed(pwp, pptr, 0);
+	pmcs_unlock_phy(pptr);
+
+	RESTART_DISCOVERY(pwp);
+	pmcs_prt(pwp, PMCS_PRT_INFO, "PortID 0x%x Cleared", portid);
+}
+
+void
+pmcs_process_sas_hw_event(pmcs_hw_t *pwp, void *iomb, size_t amt)
+{
+	uint32_t w1 = LE_32(((uint32_t *)iomb)[1]);
+	uint32_t w3 = LE_32(((uint32_t *)iomb)[3]);
+	char buf[32];
+	uint8_t phynum = IOP_EVENT_PHYNUM(w1);
+	uint8_t portid = IOP_EVENT_PORTID(w1);
+	pmcs_iport_t *iport;
+	pmcs_phy_t *pptr, *subphy, *tphyp;
+	int need_ack = 0;
+	int primary;
+
+	switch (IOP_EVENT_EVENT(w1)) {
+	case IOP_EVENT_PHY_STOP_STATUS:
+		if (IOP_EVENT_STATUS(w1)) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "PORT %d failed to stop (0x%x)",
+			    phynum, IOP_EVENT_STATUS(w1));
+		} else {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "PHY 0x%x Stopped",
+			    phynum);
+			mutex_enter(&pwp->lock);
+			pptr = pwp->root_phys + phynum;
+			pmcs_lock_phy(pptr);
+			mutex_exit(&pwp->lock);
+			if (pptr->configured) {
+				pmcs_kill_changed(pwp, pptr, 0);
+			} else {
+				pmcs_set_changed(pwp, pptr, B_TRUE, 0);
+			}
+			pmcs_unlock_phy(pptr);
+			RESTART_DISCOVERY(pwp);
+		}
+		/* Reposition htag to the 'expected' position. */
+		((uint32_t *)iomb)[1] = ((uint32_t *)iomb)[2];
+		pmcs_process_completion(pwp, iomb, amt);
+		break;
+	case IOP_EVENT_SAS_PHY_UP:
+	{
+		static const uint8_t sas_identify_af_endian_xfvec[] = {
+			0x5c, 0x5a, 0x56, 0x00
+		};
+		pmcs_phy_t *rp;
+		sas_identify_af_t af;
+
+		/*
+		 * If we're not at running state, don't do anything
+		 */
+		mutex_enter(&pwp->lock);
+		if (pwp->state != STATE_RUNNING) {
+			mutex_exit(&pwp->lock);
+			break;
+		}
+		pptr = pwp->root_phys + phynum;
+		pmcs_lock_phy(pptr);
+
+		rp = pwp->ports[portid];
+
+		/* rp and pptr may be the same */
+		if (rp && (rp != pptr)) {
+			pmcs_lock_phy(rp);
+		}
+		mutex_exit(&pwp->lock);
+
+		pmcs_endian_transform(pwp, &af, &((uint32_t *)iomb)[4],
+		    sas_identify_af_endian_xfvec);
+
+		/* Copy the remote address into our phy handle */
+		(void) memcpy(pptr->sas_address, af.sas_address, 8);
+
+		/*
+		 * Check to see if there is a PortID already active.
+		 */
+		if (rp) {
+			if (rp->portid != portid) {
+				if (rp != pptr) {
+					pmcs_unlock_phy(rp);
+				}
+				pmcs_unlock_phy(pptr);
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "PortID 0x%x"
+				    ": PHY 0x%x SAS LINK UP IS FOR A "
+				    "DIFFERENT PORTID 0x%x", rp->portid,
+				    phynum, portid);
+				break;
+			}
+
+			/*
+			 * If the dtype isn't NOTHING, then this is actually
+			 * the primary PHY for this port.  It probably went
+			 * down and came back up, so be sure not to mark it
+			 * as a subsidiary.
+			 */
+			if (pptr->dtype == NOTHING) {
+				pptr->subsidiary = 1;
+			}
+			pptr->link_rate =
+			    pmcs_link_rate(IOP_EVENT_LINK_RATE(w1));
+			pptr->portid = portid;
+			pptr->dead = 0;
+
+			if (pptr != rp) {
+				pmcs_unlock_phy(pptr);
+			}
+
+			rp->width = IOP_EVENT_NPIP(w3);
+
+			/* Add this PHY to the phymap */
+			if (sas_phymap_phy_add(pwp->hss_phymap, phynum,
+			    pwp->sas_wwns[0],
+			    pmcs_barray2wwn(pptr->sas_address)) !=
+			    DDI_SUCCESS) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "Unable to add phy %u for 0x%" PRIx64 ".0x%"
+				    PRIx64, phynum, pwp->sas_wwns[rp->phynum],
+				    pmcs_barray2wwn(pptr->sas_address));
+			}
+
+			/* Get our iport, if attached, and set it up */
+			if (pptr != rp) {
+				pmcs_lock_phy(pptr);
+			}
+			iport = pmcs_get_iport_by_phy(pwp, pptr);
+			if (iport) {
+				pptr->iport = iport;
+				primary = !pptr->subsidiary;
+
+				mutex_enter(&iport->lock);
+				if (primary) {
+					iport->pptr = pptr;
+				}
+				if (iport->ua_state == UA_ACTIVE) {
+					pmcs_add_phy_to_iport(iport, pptr);
+				}
+				mutex_exit(&iport->lock);
+				pmcs_rele_iport(iport);
+			}
+
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "PortID 0x%x: PHY"
+			    " 0x%x SAS LINK UP WIDENS PORT TO %d PHYS",
+			    portid, phynum, rp->width);
+
+			if (pptr != rp) {
+				pmcs_unlock_phy(pptr);
+			}
+			pmcs_unlock_phy(rp);
+			break;
+		}
+
+		/*
+		 * Check to see if anything is here already
+		 */
+		if (pptr->dtype != NOTHING && pptr->configured) {
+			pmcs_unlock_phy(pptr);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "PortID 0x%x: SAS PHY 0x%x "
+			    "UP HITS EXISTING CONFIGURED TREE", portid, phynum);
+			break;
+		}
+
+		if (af.address_frame_type != SAS_AF_IDENTIFY) {
+			pmcs_unlock_phy(pptr);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "SAS link up on phy 0x%x, "
+			    "but unexpected frame type 0x%x found", phynum,
+			    af.address_frame_type);
+			break;
+		}
+		pptr->width = IOP_EVENT_NPIP(w3);
+		pptr->portid = portid;
+		pptr->dead = 0;
+		pptr->link_rate = pmcs_link_rate(IOP_EVENT_LINK_RATE(w1));
+
+		/*
+		 * Check to see whether this is an expander or an endpoint
+		 */
+		switch (af.device_type) {
+		case SAS_IF_DTYPE_ENDPOINT:
+			pptr->pend_dtype = SAS;
+			pptr->dtype = SAS;
+			break;
+		case SAS_IF_DTYPE_EDGE:
+		case SAS_IF_DTYPE_FANOUT:
+			pptr->pend_dtype = EXPANDER;
+			pptr->dtype = EXPANDER;
+			break;
+		default:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "unknown device type 0x%x", af.device_type);
+			pptr->pend_dtype = NOTHING;
+			pptr->dtype = NOTHING;
+			break;
+		}
+
+		/*
+		 * If this is a direct-attached SAS drive, do the spinup
+		 * release now.
+		 */
+		if (pptr->dtype == SAS) {
+			pptr->spinup_hold = 1;
+			pmcs_spinup_release(pwp, pptr);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "Release spinup hold on PHY 0x%x", phynum);
+		}
+
+		pmcs_set_changed(pwp, pptr, B_TRUE, 0);
+		if (pptr->width > 1) {
+			pmcs_prt(pwp, PMCS_PRT_INFO, "PortID 0x%x: PHY 0x%x SAS"
+			    " LINK UP @ %s Gb with %d phys/s", portid, phynum,
+			    pmcs_get_rate(pptr->link_rate), pptr->width);
+		} else {
+			pmcs_prt(pwp, PMCS_PRT_INFO, "PortID 0x%x: PHY 0x%x SAS"
+			    " LINK UP @ %s Gb/s", portid, phynum,
+			    pmcs_get_rate(pptr->link_rate));
+		}
+		pmcs_unlock_phy(pptr);
+
+		/* Add this PHY to the phymap */
+		if (sas_phymap_phy_add(pwp->hss_phymap, phynum,
+		    pwp->sas_wwns[0],
+		    pmcs_barray2wwn(pptr->sas_address)) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "Unable to add phy %u for 0x%" PRIx64 ".0x%"
+			    PRIx64, phynum, pwp->sas_wwns[pptr->phynum],
+			    pmcs_barray2wwn(pptr->sas_address));
+		}
+
+		/* Get a pointer to our iport and set it up if attached */
+		pmcs_lock_phy(pptr);
+		iport = pmcs_get_iport_by_phy(pwp, pptr);
+		if (iport) {
+			pptr->iport = iport;
+			primary = !pptr->subsidiary;
+
+			mutex_enter(&iport->lock);
+			if (primary) {
+				iport->pptr = pptr;
+			}
+			if (iport->ua_state == UA_ACTIVE) {
+				pmcs_add_phy_to_iport(iport, pptr);
+			}
+			mutex_exit(&iport->lock);
+			pmcs_rele_iport(iport);
+		}
+
+		pmcs_smhba_log_sysevent(pwp, ESC_SAS_PHY_EVENT,
+		    SAS_PHY_ONLINE, pptr);
+		pmcs_unlock_phy(pptr);
+
+		mutex_enter(&pwp->lock);
+		pwp->ports[portid] = pptr;
+		mutex_exit(&pwp->lock);
+		RESTART_DISCOVERY(pwp);
+
+		break;
+	}
+	case IOP_EVENT_SATA_PHY_UP:
+		/*
+		 * If we're not at running state, don't do anything
+		 */
+		mutex_enter(&pwp->lock);
+		if (pwp->state != STATE_RUNNING) {
+			mutex_exit(&pwp->lock);
+			break;
+		}
+
+		/*
+		 * Check to see if anything is here already
+		 */
+		pmcs_lock_phy(pwp->root_phys + phynum);
+		pptr = pwp->root_phys + phynum;
+		mutex_exit(&pwp->lock);
+
+		if (pptr->dtype != NOTHING && pptr->configured) {
+			pmcs_unlock_phy(pptr);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "PortID 0x%x: SATA PHY 0x%x"
+			    " UP HITS EXISTING CONFIGURED TREE",
+			    portid, phynum);
+			break;
+		}
+
+		pptr->width = 1;
+		pptr->dead = 0;
+
+		/*
+		 * Install the PHY number in the least significant byte
+		 * with a NAA=3 (locally assigned address) in the most
+		 * significant nubble.
+		 *
+		 * Later, we'll either use that or dig a
+		 * WWN out of words 108..111.
+		 */
+		pptr->sas_address[0] = 0x30;
+		pptr->sas_address[1] = 0;
+		pptr->sas_address[2] = 0;
+		pptr->sas_address[3] = 0;
+		pptr->sas_address[4] = 0;
+		pptr->sas_address[5] = 0;
+		pptr->sas_address[6] = 0;
+		pptr->sas_address[7] = phynum;
+		pptr->portid = portid;
+		pptr->link_rate = pmcs_link_rate(IOP_EVENT_LINK_RATE(w1));
+		pptr->dtype = SATA;
+		pmcs_set_changed(pwp, pptr, B_TRUE, 0);
+		pmcs_prt(pwp, PMCS_PRT_INFO,
+		    "PortID 0x%x: PHY 0x%x SATA LINK UP @ %s Gb/s",
+		    pptr->portid, phynum, pmcs_get_rate(pptr->link_rate));
+		pmcs_unlock_phy(pptr);
+
+		/* Add this PHY to the phymap */
+		if (sas_phymap_phy_add(pwp->hss_phymap, phynum,
+		    pwp->sas_wwns[0],
+		    pmcs_barray2wwn(pptr->sas_address)) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "Unable to add phy %u for 0x%" PRIx64 ".0x%"
+			    PRIx64, phynum, pwp->sas_wwns[pptr->phynum],
+			    pmcs_barray2wwn(pptr->sas_address));
+		}
+
+		/* Get our iport, if attached, and set it up */
+		pmcs_lock_phy(pptr);
+		iport = pmcs_get_iport_by_phy(pwp, pptr);
+		if (iport) {
+			pptr->iport = iport;
+
+			mutex_enter(&iport->lock);
+			iport->pptr = pptr;
+			if (iport->ua_state == UA_ACTIVE) {
+				pmcs_add_phy_to_iport(iport, pptr);
+				ASSERT(iport->nphy == 1);
+				iport->nphy = 1;
+			}
+			mutex_exit(&iport->lock);
+			pmcs_rele_iport(iport);
+		}
+
+		pmcs_smhba_log_sysevent(pwp, ESC_SAS_PHY_EVENT,
+		    SAS_PHY_ONLINE, pptr);
+		pmcs_unlock_phy(pptr);
+
+		mutex_enter(&pwp->lock);
+		pwp->ports[pptr->portid] = pptr;
+		mutex_exit(&pwp->lock);
+		RESTART_DISCOVERY(pwp);
+		break;
+
+	case IOP_EVENT_SATA_SPINUP_HOLD:
+		tphyp = (pmcs_phy_t *)(pwp->root_phys + phynum);
+		/*
+		 * No need to lock the entire tree for this
+		 */
+		mutex_enter(&tphyp->phy_lock);
+		tphyp->spinup_hold = 1;
+		pmcs_spinup_release(pwp, tphyp);
+		mutex_exit(&tphyp->phy_lock);
+		break;
+	case IOP_EVENT_PHY_DOWN:
+		/*
+		 * If we're not at running state, don't do anything
+		 */
+		mutex_enter(&pwp->lock);
+		if (pwp->state != STATE_RUNNING) {
+			mutex_exit(&pwp->lock);
+			break;
+		}
+		pptr = pwp->ports[portid];
+
+		subphy = pwp->root_phys + phynum;
+		/*
+		 * subphy is a pointer to the PHY corresponding to the incoming
+		 * event. pptr points to the primary PHY for the corresponding
+		 * port.  So, subphy and pptr may or may not be the same PHY,
+		 * but that doesn't change what we need to do with each.
+		 */
+		ASSERT(subphy);
+		mutex_exit(&pwp->lock);
+
+		if (pptr == NULL) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "PortID 0x%x: PHY 0x%x "
+			    "LINK DOWN- no portid pointer", portid, phynum);
+			break;
+		}
+		if (IOP_EVENT_PORT_STATE(w3) == IOP_EVENT_PS_NIL) {
+			pmcs_prt(pwp, PMCS_PRT_INFO,
+			    "PortID 0x%x: PHY 0x%x NOT VALID YET",
+			    portid, phynum);
+			need_ack = 1;
+			break;
+		}
+		if (IOP_EVENT_PORT_STATE(w3) == IOP_EVENT_PS_IN_RESET) {
+			pmcs_prt(pwp, PMCS_PRT_INFO,
+			    "PortID 0x%x: PHY 0x%x IN RESET",
+			    portid, phynum);
+			break;
+		}
+		if (IOP_EVENT_PORT_STATE(w3) == IOP_EVENT_PS_LOSTCOMM) {
+			pmcs_prt(pwp, PMCS_PRT_INFO,
+			    "PortID 0x%x: PHY 0x%x TEMPORARILY DOWN",
+			    portid, phynum);
+			need_ack = 1;
+			break;
+		}
+
+		if (IOP_EVENT_PORT_STATE(w3) == IOP_EVENT_PS_VALID) {
+			/*
+			 * Drop port width on the primary phy handle
+			 * No need to lock the entire tree for this
+			 */
+			mutex_enter(&pptr->phy_lock);
+			pptr->width = IOP_EVENT_NPIP(w3);
+			mutex_exit(&pptr->phy_lock);
+
+			/* Clear the iport reference on the subphy */
+			mutex_enter(&subphy->phy_lock);
+			iport = subphy->iport;
+			subphy->iport = NULL;
+			mutex_exit(&subphy->phy_lock);
+
+			/*
+			 * If the iport was set on this phy, decrement its
+			 * nphy count and remove this phy from the phys list.
+			 */
+			if (iport) {
+				mutex_enter(&iport->lock);
+				pmcs_remove_phy_from_iport(iport, subphy);
+				mutex_exit(&iport->lock);
+			}
+
+			pmcs_lock_phy(subphy);
+			if (subphy->subsidiary)
+				pmcs_clear_phy(pwp, subphy);
+			pmcs_unlock_phy(subphy);
+
+			/* Remove this PHY from the phymap */
+			if (sas_phymap_phy_rem(pwp->hss_phymap, phynum) !=
+			    DDI_SUCCESS) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "Unable to remove phy %u for 0x%" PRIx64
+				    ".0x%" PRIx64, phynum,
+				    pwp->sas_wwns[pptr->phynum],
+				    pmcs_barray2wwn((pwp->root_phys +
+				    pptr->phynum)-> sas_address));
+			}
+
+			pmcs_prt(pwp, PMCS_PRT_INFO, "PortID 0x%x: PHY 0x%x "
+			    "LINK DOWN NARROWS PORT TO %d PHYS",
+			    portid, phynum, pptr->width);
+			break;
+		}
+		if (IOP_EVENT_PORT_STATE(w3) != IOP_EVENT_PS_INVALID) {
+			pmcs_prt(pwp, PMCS_PRT_INFO, "PortID 0x%x: PHY 0x"
+			    "%x LINK DOWN NOT HANDLED (state 0x%x)",
+			    portid, phynum, IOP_EVENT_PORT_STATE(w3));
+			need_ack = 1;
+			break;
+		}
+		/* Remove this PHY from the phymap */
+		if (sas_phymap_phy_rem(pwp->hss_phymap, phynum) !=
+		    DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "Unable to remove phy %u for 0x%" PRIx64
+			    ".0x%" PRIx64, phynum,
+			    pwp->sas_wwns[pptr->phynum],
+			    pmcs_barray2wwn(
+			    (pwp->root_phys + pptr->phynum)->sas_address));
+		}
+
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "PortID 0x%x: PHY 0x%x LINK DOWN (port invalid)",
+		    portid, phynum);
+
+		/*
+		 * Last PHY on the port.
+		 * Assumption: pptr and subphy are both "valid"
+		 *
+		 * Drop port width on the primary phy handle
+		 * Report the event while we've got the lock
+		 */
+		mutex_enter(&pptr->phy_lock);
+		pptr->width = 0;
+		pmcs_smhba_log_sysevent(pwp, ESC_SAS_PHY_EVENT,
+		    SAS_PHY_OFFLINE, pptr);
+		mutex_exit(&pptr->phy_lock);
+
+		/* Clear the iport reference on the subphy */
+		mutex_enter(&subphy->phy_lock);
+		iport = subphy->iport;
+		subphy->iport = NULL;
+		mutex_exit(&subphy->phy_lock);
+
+		/*
+		 * If the iport was set on this phy, decrement its
+		 * nphy count and remove this phy from the phys list.
+		 * Also, clear the iport's pptr as this port is now
+		 * down.
+		 */
+		if (iport) {
+			mutex_enter(&iport->lock);
+			pmcs_remove_phy_from_iport(iport, subphy);
+			iport->pptr = NULL;
+			iport->ua_state = UA_PEND_DEACTIVATE;
+			mutex_exit(&iport->lock);
+		}
+
+		pmcs_lock_phy(subphy);
+		if (subphy->subsidiary)
+			pmcs_clear_phy(pwp, subphy);
+		pmcs_unlock_phy(subphy);
+
+		/*
+		 * Since we're now really dead, it's time to clean up.
+		 */
+		pmcs_kill_port(pwp, portid);
+		need_ack = 1;
+
+		break;
+	case IOP_EVENT_BROADCAST_CHANGE:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "PortID 0x%x: PHY 0x%x Broadcast Change", portid, phynum);
+		need_ack = 1;
+		mutex_enter(&pwp->lock);
+		pptr = pwp->ports[portid];
+		if (pptr) {
+			pmcs_lock_phy(pptr);
+			if (pptr->phynum == phynum) {
+				pmcs_set_changed(pwp, pptr, B_TRUE, 0);
+			}
+			pmcs_smhba_log_sysevent(pwp, ESC_SAS_HBA_PORT_BROADCAST,
+			    SAS_PORT_BROADCAST_CHANGE, pptr);
+			pmcs_unlock_phy(pptr);
+		}
+		mutex_exit(&pwp->lock);
+		RESTART_DISCOVERY(pwp);
+		break;
+	case IOP_EVENT_BROADCAST_SES:
+		EVT_PRT(pwp, IOP_EVENT_BROADCAST_SES, phynum);
+		mutex_enter(&pwp->lock);
+		pptr = pwp->ports[portid];
+		mutex_exit(&pwp->lock);
+		if (pptr) {
+			pmcs_lock_phy(pptr);
+			pmcs_smhba_log_sysevent(pwp, ESC_SAS_HBA_PORT_BROADCAST,
+			    SAS_PORT_BROADCAST_SES, pptr);
+			pmcs_unlock_phy(pptr);
+		}
+		break;
+	case IOP_EVENT_PHY_ERR_INBOUND_CRC:
+	{
+		char buf[32];
+		(void) snprintf(buf, sizeof (buf), "Inbound PHY CRC error");
+		need_ack = 1;
+		break;
+	}
+	case IOP_EVENT_HARD_RESET_RECEIVED:
+		EVT_PRT(pwp, IOP_EVENT_HARD_RESET_RECEIVED, phynum);
+		break;
+	case IOP_EVENT_EVENT_ID_FRAME_TIMO:
+		EVT_PRT(pwp, IOP_EVENT_EVENT_ID_FRAME_TIMO, phynum);
+		break;
+	case IOP_EVENT_BROADCAST_EXP:
+		pmcs_prt(pwp, PMCS_PRT_INFO,
+		    "PortID 0x%x: PHY 0x%x Broadcast Exp Change",
+		    portid, phynum);
+		/*
+		 * Comparing Section 6.8.1.4 of SMHBA (rev 7) spec and Section
+		 * 7.2.3 of SAS2 (Rev 15) spec,
+		 * _BROADCAST_EXPANDER event corresponds to _D01_4 primitive
+		 */
+		mutex_enter(&pwp->lock);
+		pptr = pwp->ports[portid];
+		mutex_exit(&pwp->lock);
+		if (pptr) {
+			pmcs_lock_phy(pptr);
+			pmcs_smhba_log_sysevent(pwp, ESC_SAS_HBA_PORT_BROADCAST,
+			    SAS_PORT_BROADCAST_D01_4, pptr);
+			pmcs_unlock_phy(pptr);
+		}
+		break;
+	case IOP_EVENT_PHY_START_STATUS:
+		switch (IOP_EVENT_STATUS(w1)) {
+		case IOP_PHY_START_OK:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "PHY 0x%x Started",
+			    phynum);
+			break;
+		case IOP_PHY_START_ALREADY:
+			pmcs_prt(pwp, PMCS_PRT_INFO,
+			    "PHY 0x%x Started (Already)", phynum);
+			break;
+		case IOP_PHY_START_INVALID:
+			pmcs_prt(pwp, PMCS_PRT_WARN,
+			    "PHY 0x%x failed to start (invalid phy)", phynum);
+			break;
+		case IOP_PHY_START_ERROR:
+			pmcs_prt(pwp, PMCS_PRT_WARN,
+			    "PHY 0x%x Start Error", phynum);
+			break;
+		default:
+			pmcs_prt(pwp, PMCS_PRT_WARN, "PHY 0x%x failed to start "
+			    "(0x%x)", phynum, IOP_EVENT_STATUS(w1));
+			break;
+		}
+		/* Reposition htag to the 'expected' position. */
+		((uint32_t *)iomb)[1] = ((uint32_t *)iomb)[2];
+		pmcs_process_completion(pwp, iomb, amt);
+		break;
+	case IOP_EVENT_PHY_ERR_INVALID_DWORD:
+		need_ack = 1;
+		EVT_PRT(pwp, IOP_EVENT_PHY_ERR_INVALID_DWORD, phynum);
+		break;
+	case IOP_EVENT_PHY_ERR_DISPARITY_ERROR:
+		need_ack = 1;
+		EVT_PRT(pwp, IOP_EVENT_PHY_ERR_DISPARITY_ERROR, phynum);
+		break;
+	case IOP_EVENT_PHY_ERR_CODE_VIOLATION:
+		need_ack = 1;
+		EVT_PRT(pwp, IOP_EVENT_PHY_ERR_CODE_VIOLATION, phynum);
+		break;
+	case IOP_EVENT_PHY_ERR_LOSS_OF_DWORD_SYN:
+		need_ack = 1;
+		EVT_PRT(pwp, IOP_EVENT_PHY_ERR_LOSS_OF_DWORD_SYN, phynum);
+		break;
+	case IOP_EVENT_PHY_ERR_PHY_RESET_FAILD:
+		need_ack = 1;
+		EVT_PRT(pwp, IOP_EVENT_PHY_ERR_PHY_RESET_FAILD, phynum);
+		break;
+	case IOP_EVENT_PORT_RECOVERY_TIMER_TMO:
+		EVT_PRT(pwp, IOP_EVENT_PORT_RECOVERY_TIMER_TMO, phynum);
+		break;
+	case IOP_EVENT_PORT_RECOVER:
+		EVT_PRT(pwp, IOP_EVENT_PORT_RECOVER, phynum);
+		break;
+	case IOP_EVENT_PORT_INVALID:
+		mutex_enter(&pwp->lock);
+		if (pwp->state != STATE_RUNNING) {
+			mutex_exit(&pwp->lock);
+			break;
+		}
+		mutex_exit(&pwp->lock);
+		pmcs_kill_port(pwp, portid);
+		pmcs_prt(pwp, PMCS_PRT_INFO, "PortID 0x%x: PORT Now Invalid",
+		    portid);
+		break;
+	case IOP_EVENT_PORT_RESET_TIMER_TMO:
+		EVT_PRT(pwp, IOP_EVENT_PORT_RESET_TIMER_TMO, phynum);
+		break;
+	case IOP_EVENT_PORT_RESET_COMPLETE:
+		EVT_PRT(pwp, IOP_EVENT_PORT_RESET_COMPLETE, phynum);
+		break;
+	case IOP_EVENT_BROADCAST_ASYNC_EVENT:
+		EVT_PRT(pwp, IOP_EVENT_BROADCAST_ASYNC_EVENT, phynum);
+		/*
+		 * Comparing Section 6.8.1.4 of SMHBA (rev 7) spec and Section
+		 * 7.2.3 of SAS2 (Rev 15) spec,
+		 * _BROADCAST_ASYNC event corresponds to _D04_7 primitive
+		 */
+		mutex_enter(&pwp->lock);
+		pptr = pwp->ports[portid];
+		mutex_exit(&pwp->lock);
+		if (pptr) {
+			pmcs_lock_phy(pptr);
+			pmcs_smhba_log_sysevent(pwp, ESC_SAS_HBA_PORT_BROADCAST,
+			    SAS_PORT_BROADCAST_D04_7, pptr);
+			pmcs_unlock_phy(pptr);
+		}
+		break;
+	default:
+		(void) snprintf(buf, sizeof (buf),
+		    "unknown SAS H/W Event PHY 0x%x", phynum);
+		pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, iomb);
+		break;
+	}
+	if (need_ack) {
+		mutex_enter(&pwp->lock);
+		/*
+		 * Don't lock the entire tree for this.  Just grab the mutex
+		 * on the root PHY.
+		 */
+		tphyp = pwp->root_phys + phynum;
+		mutex_enter(&tphyp->phy_lock);
+		tphyp->hw_event_ack = w1;
+		mutex_exit(&tphyp->phy_lock);
+		mutex_exit(&pwp->lock);
+		pmcs_ack_events(pwp);
+	}
+}
+
+static void
+pmcs_process_echo_completion(pmcs_hw_t *pwp, void *iomb, size_t amt)
+{
+	echo_test_t fred;
+	pmcwork_t *pwrk;
+	uint32_t *msg = iomb, htag = LE_32(msg[1]);
+	pwrk = pmcs_tag2wp(pwp, htag);
+	if (pwrk) {
+		(void) memcpy(&fred, &((uint32_t *)iomb)[2], sizeof (fred));
+		fred.ptr[0]++;
+		msg[2] = LE_32(PMCOUT_STATUS_OK);
+		pmcs_complete_work(pwp, pwrk, msg, amt);
+	} else {
+		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
+		    "ECHO completion with no work structure", iomb);
+	}
+}
+
+static void
+pmcs_process_ssp_event(pmcs_hw_t *pwp, void *iomb, size_t amt)
+{
+	_NOTE(ARGUNUSED(amt));
+	uint32_t status, htag, *w;
+	pmcwork_t *pwrk;
+	char *path;
+
+	w = iomb;
+	htag = LE_32(w[1]);
+	status = LE_32(w[2]);
+
+
+	pwrk = pmcs_tag2wp(pwp, htag);
+	if (pwrk == NULL) {
+		path = "????";
+	} else {
+		path = pwrk->phy->path;
+	}
+
+	if (status != PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED) {
+		char buf[20];
+		const char *emsg = pmcs_status_str(status);
+
+		if (emsg == NULL) {
+			(void) snprintf(buf, sizeof (buf), "Status 0x%x",
+			    status);
+			emsg = buf;
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Bad SAS Status (tag 0x%x) "
+		    "%s on %s", __func__, htag, emsg, path);
+		if (pwrk != NULL) {
+			/*
+			 * There may be pending command on a target device.
+			 * Or, it may be a double fault.
+			 */
+			pmcs_start_ssp_event_recovery(pwp, pwrk, iomb, amt);
+		}
+	} else {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+		    "%s: tag %x put onto the wire for %s",
+		    __func__, htag, path);
+		if (pwrk) {
+			pwrk->onwire = 1;
+			mutex_exit(&pwrk->lock);
+		}
+	}
+}
+
+static void
+pmcs_process_sata_event(pmcs_hw_t *pwp, void *iomb, size_t amt)
+{
+	_NOTE(ARGUNUSED(amt));
+	pmcwork_t *pwrk = NULL;
+	pmcs_phy_t *pptr;
+	uint32_t status, htag, *w;
+	char *path;
+
+	w = iomb;
+	htag = LE_32(w[1]);
+	status = LE_32(w[2]);
+
+	/*
+	 * If the status is PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE,
+	 * we have to issue a READ LOG EXT ATA (page 0x10) command
+	 * to the device. In this case, htag is not valid.
+	 *
+	 * If the status is PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED, we're
+	 * just noting that an I/O got put onto the wire.
+	 *
+	 * Othewise, other errors are indicative that things need to
+	 * be aborted.
+	 */
+	path = NULL;
+	if (htag) {
+		pwrk = pmcs_tag2wp(pwp, htag);
+		if (pwrk) {
+			pmcs_lock_phy(pwrk->phy);
+			pptr = pwrk->phy;
+			path = pptr->path;
+		}
+	}
+	if (path == NULL) {
+		mutex_enter(&pwp->lock);
+		pptr = pmcs_find_phy_by_devid(pwp, LE_32(w[4]));
+		/* This PHY is now locked */
+		mutex_exit(&pwp->lock);
+		if (pptr) {
+			path = pptr->path;
+		} else {
+			path = "????";
+		}
+	}
+
+	if (status != PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED) {
+		char buf[20];
+		const char *emsg = pmcs_status_str(status);
+
+		ASSERT(pptr != NULL);
+		if (emsg == NULL) {
+			(void) snprintf(buf, sizeof (buf), "Status 0x%x",
+			    status);
+			emsg = buf;
+		}
+		if (status == PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE) {
+			ASSERT(pptr != NULL);
+			pptr->need_rl_ext = 1;
+			htag = 0;
+		} else {
+			pptr->abort_pending = 1;
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Bad SATA Status (tag 0x%x) "
+		    "%s on %s", __func__, htag, emsg, path);
+		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
+		/*
+		 * Unlike SSP devices, we let the abort we
+		 * schedule above force the completion of
+		 * problem commands.
+		 */
+		if (pwrk) {
+			mutex_exit(&pwrk->lock);
+		}
+	} else if (status == PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+		    "%s: tag %x put onto the wire for %s",
+		    __func__, htag, path);
+		if (pwrk) {
+			pwrk->onwire = 1;
+			mutex_exit(&pwrk->lock);
+		}
+	}
+
+	if (pptr) {
+		pmcs_unlock_phy(pptr);
+	}
+}
+
+static void
+pmcs_process_abort_completion(pmcs_hw_t *pwp, void *iomb, size_t amt)
+{
+	pmcs_phy_t *pptr;
+	struct pmcwork *pwrk;
+	uint32_t rtag;
+	uint32_t htag = LE_32(((uint32_t *)iomb)[1]);
+	uint32_t status = LE_32(((uint32_t *)iomb)[2]);
+	uint32_t scp = LE_32(((uint32_t *)iomb)[3]) & 0x1;
+	char *path;
+
+	pwrk = pmcs_tag2wp(pwp, htag);
+	if (pwrk == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: cannot find work structure for ABORT", __func__);
+		return;
+	}
+	if (pwrk->ptr) {
+		rtag = *((uint32_t *)pwrk->ptr);
+	} else {
+		rtag = 0;
+	}
+
+	pptr = pwrk->phy;
+	if (pptr) {
+		pmcs_lock_phy(pptr);
+		pptr->abort_pending = 0;
+		pptr->abort_sent = 0;
+
+		/*
+		 * Don't do this if the status was ABORT_IN_PROGRESS and
+		 * the scope bit was set
+		 */
+		if ((status != PMCOUT_STATUS_IO_ABORT_IN_PROGRESS) || !scp) {
+			pptr->abort_all_start = 0;
+			cv_signal(&pptr->abort_all_cv);
+		}
+		path = pptr->path;
+		pmcs_unlock_phy(pptr);
+	} else {
+		path = "(no phy)";
+	}
+
+	switch (status) {
+	case PMCOUT_STATUS_OK:
+		if (scp) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: abort all succeeded for %s. (htag=0x%x)",
+			    __func__, path, htag);
+		} else {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: abort tag 0x%x succeeded for %s. (htag=0x%x)",
+			    __func__, rtag, path, htag);
+		}
+		break;
+
+	case PMCOUT_STATUS_IO_NOT_VALID:
+		if (scp) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: ABORT %s failed (DEV NOT VALID) for %s. "
+			    "(htag=0x%x)", __func__, scp ? "all" : "tag",
+			    path, htag);
+		} else {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: ABORT %s failed (I/O NOT VALID) for %s. "
+			    "(htag=0x%x)", __func__, scp ? "all" : "tag",
+			    path, htag);
+		}
+		break;
+
+	case PMCOUT_STATUS_IO_ABORT_IN_PROGRESS:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ABORT %s failed for "
+		    "%s, htag 0x%x (ABORT IN PROGRESS)", __func__,
+		    scp ? "all" : "tag", path, htag);
+		break;
+
+	default:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Unknown status %d for ABORT"
+		    " %s, htag 0x%x, PHY %s", __func__, status,
+		    scp ? "all" : "tag", htag, path);
+		break;
+	}
+
+	pmcs_complete_work(pwp, pwrk, iomb, amt);
+}
+
+static void
+pmcs_process_general_event(pmcs_hw_t *pwp, uint32_t *iomb)
+{
+	uint32_t htag;
+	char local[60];
+	struct pmcwork *pwrk;
+	int i;
+
+	if (LE_32(iomb[1]) == INBOUND_IOMB_V_BIT_NOT_SET) {
+		(void) snprintf(local, sizeof (local),
+		    "VALID bit not set on INBOUND IOMB");
+	} else if (LE_32(iomb[1]) ==
+	    INBOUND_IOMB_OPC_NOT_SUPPORTED) {
+		(void) snprintf(local, sizeof (local),
+		    "opcode not set on inbound IOMB");
+	} else {
+		(void) snprintf(local, sizeof (local),
+		    "unknown GENERAL EVENT status (0x%x)",
+		    LE_32(iomb[1]));
+	}
+	/* Pull up bad IOMB into usual position */
+	for (i = 0; i < PMCS_MSG_SIZE - 2; i++) {
+		iomb[i] = iomb[i+2];
+	}
+	/* overwrite status with an error */
+	iomb[2] = LE_32(PMCOUT_STATUS_PROG_ERROR);
+	iomb[PMCS_MSG_SIZE - 2] = 0;
+	iomb[PMCS_MSG_SIZE - 1] = 0;
+	htag = LE_32(iomb[1]);
+	pmcs_print_entry(pwp, PMCS_PRT_DEBUG, local, iomb);
+	pwrk = pmcs_tag2wp(pwp, htag);
+	if (pwrk) {
+		pmcs_complete_work(pwp, pwrk, iomb, PMCS_QENTRY_SIZE);
+	}
+}
+
+void
+pmcs_general_intr(pmcs_hw_t *pwp)
+{
+	char local[PMCS_QENTRY_SIZE << 1];
+	uint32_t w0, pi, ci;
+	uint32_t *ptr, nbuf, lim = 0;
+	size_t amt;
+
+	ci = pmcs_rd_oqci(pwp, PMCS_OQ_GENERAL);
+	pi = pmcs_rd_oqpi(pwp, PMCS_OQ_GENERAL);
+
+	while (ci != pi) {
+		OQLIM_CHECK(pwp, lim);
+		ptr = GET_OQ_ENTRY(pwp, PMCS_OQ_GENERAL, ci, 0);
+		w0 = LE_32(ptr[0]);
+		VALID_IOMB_CHECK(pwp, w0, ptr, ci, pi);
+		WRONG_OBID_CHECK(pwp, w0, PMCS_OQ_GENERAL);
+		COPY_OUTBOUND(pwp, w0, local, nbuf, amt, ptr,
+		    PMCS_OQ_GENERAL, ci);
+
+		switch (w0 & PMCS_IOMB_OPCODE_MASK) {
+		case PMCOUT_SSP_COMPLETION:
+			/*
+			 * We only get SSP completion here for Task Management
+			 * completions.
+			 */
+		case PMCOUT_SMP_COMPLETION:
+		case PMCOUT_LOCAL_PHY_CONTROL:
+		case PMCOUT_DEVICE_REGISTRATION:
+		case PMCOUT_DEREGISTER_DEVICE_HANDLE:
+		case PMCOUT_GET_NVMD_DATA:
+		case PMCOUT_SET_NVMD_DATA:
+		case PMCOUT_GET_DEVICE_STATE:
+		case PMCOUT_SET_DEVICE_STATE:
+			pmcs_process_completion(pwp, local, amt);
+			break;
+		case PMCOUT_SSP_ABORT:
+		case PMCOUT_SATA_ABORT:
+		case PMCOUT_SMP_ABORT:
+			pmcs_process_abort_completion(pwp, local, amt);
+			break;
+		case PMCOUT_SSP_EVENT:
+			pmcs_process_ssp_event(pwp, local, amt);
+			break;
+		case PMCOUT_ECHO:
+			pmcs_process_echo_completion(pwp, local, amt);
+			break;
+		case PMCOUT_SAS_HW_EVENT_ACK_ACK:
+			if (LE_32(ptr[2]) != SAS_HW_EVENT_ACK_OK) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "SAS H/W EVENT ACK/ACK Status=0x%b",
+				    LE_32(ptr[2]), "\020\4InvParm\3"
+				    "InvPort\2InvPhy\1InvSEA");
+			}
+			pmcs_process_completion(pwp, local, amt);
+			break;
+		case PMCOUT_SKIP_ENTRIES:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG3, "%s: skip %d entries",
+			    __func__, nbuf);
+			break;
+		default:
+			(void) snprintf(local, sizeof (local),
+			    "%s: unhandled message", __func__);
+			pmcs_print_entry(pwp, PMCS_PRT_DEBUG, local, ptr);
+			break;
+		}
+		STEP_OQ_ENTRY(pwp, PMCS_OQ_GENERAL, ci, nbuf);
+	}
+	if (lim) {
+		SYNC_OQ_ENTRY(pwp, PMCS_OQ_GENERAL, ci, pi);
+	}
+}
+
+/*
+ * pmcs_check_intr_coal
+ *
+ * This function makes a determination on the dynamic value of the
+ * interrupt coalescing timer register.  We only use this for I/O
+ * completions.
+ *
+ * The basic algorithm is as follows:
+ *
+ * PMCS_MAX_IO_COMPS_PER_INTR: The maximum number of I/O completions per
+ * I/O completion interrupt.  We won't increase the interrupt coalescing
+ * timer if we're already processing this many completions per interrupt
+ * beyond the threshold.
+ *
+ * Values in io_intr_coal structure:
+ *
+ * intr_latency: The average number of nsecs between interrupts during
+ * the echo test.  Used to help determine whether to increase the coalescing
+ * timer.
+ *
+ * intr_threshold: Calculated number of interrupts beyond which we may
+ * increase the timer.  This value is calculated based on the calculated
+ * interrupt latency during the ECHO test and the current value of the
+ * coalescing timer.
+ *
+ * nsecs_between_intrs: Total number of nsecs between all the interrupts
+ * in the current timeslice.
+ *
+ * last_io_comp: Time of the last I/O interrupt.
+ *
+ * num_io_completions: Number of I/O completions during the slice
+ *
+ * num_intrs: Number of I/O completion interrupts during the slice
+ *
+ * max_io_completions: Number of times we hit >= PMCS_MAX_IO_COMPS_PER_INTR
+ * during interrupt processing.
+ *
+ * PMCS_MAX_IO_COMPS_LOWAT_SHIFT/HIWAT_SHIFT
+ * Low and high marks used to determine whether we processed enough interrupts
+ * that contained the maximum number of I/O completions to warrant increasing
+ * the timer
+ *
+ * intr_coal_timer: The current value of the register (in usecs)
+ *
+ * timer_on: B_TRUE means we are using the timer
+ *
+ * The timer is increased if we processed more than intr_threshold interrupts
+ * during the quantum and the number of interrupts containing the maximum
+ * number of I/O completions is between PMCS_MAX_IO_COMPS_LOWAT_SHIFT and
+ * _HIWAT_SHIFT
+ *
+ * If the average time between completions is greater than twice
+ * the current timer value, the timer value is decreased.
+ *
+ * If we did not take any interrupts during a quantum, we turn the timer off.
+ */
+void
+pmcs_check_intr_coal(void *arg)
+{
+	pmcs_hw_t	*pwp = (pmcs_hw_t *)arg;
+	uint32_t	avg_nsecs;
+	pmcs_io_intr_coal_t *ici;
+
+	ici = &pwp->io_intr_coal;
+	mutex_enter(&pwp->ict_lock);
+
+	while (ici->stop_thread == B_FALSE) {
+		/*
+		 * Wait for next time quantum... collect stats
+		 */
+		(void) cv_timedwait(&pwp->ict_cv, &pwp->ict_lock,
+		    ddi_get_lbolt() + ici->quantum);
+
+		if (ici->stop_thread == B_TRUE) {
+			continue;
+		}
+
+		DTRACE_PROBE1(pmcs__check__intr__coal, pmcs_io_intr_coal_t *,
+		    &pwp->io_intr_coal);
+
+		/*
+		 * Determine whether to adjust timer
+		 */
+		if (ici->num_intrs == 0) {
+			/*
+			 * If timer is off, nothing more to do.
+			 */
+			if (!pwp->io_intr_coal.timer_on) {
+				continue;
+			}
+
+			/*
+			 * No interrupts.  Turn off the timer.
+			 */
+			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 0);
+
+			if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
+				pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
+				    pwp->odb_auto_clear);
+			}
+
+			ici->timer_on = B_FALSE;
+			ici->max_io_completions = 0;
+			ici->num_intrs = 0;
+			ici->int_cleared = B_FALSE;
+			ici->num_io_completions = 0;
+			DTRACE_PROBE1(pmcs__intr__coalesce__timer__off,
+			    pmcs_io_intr_coal_t *, ici);
+			continue;
+		}
+
+		avg_nsecs = ici->nsecs_between_intrs / ici->num_intrs;
+
+		if ((ici->num_intrs > ici->intr_threshold) &&
+		    (ici->max_io_completions > (ici->num_intrs >>
+		    PMCS_MAX_IO_COMPS_LOWAT_SHIFT)) &&
+		    (ici->max_io_completions < (ici->num_intrs >>
+		    PMCS_MAX_IO_COMPS_HIWAT_SHIFT))) {
+			pmcs_set_intr_coal_timer(pwp, INCREASE_TIMER);
+		} else if (avg_nsecs >
+		    (ici->intr_coal_timer * 1000 * 2)) {
+			pmcs_set_intr_coal_timer(pwp, DECREASE_TIMER);
+		}
+
+		/*
+		 * Reset values for new sampling period.
+		 */
+		ici->max_io_completions = 0;
+		ici->nsecs_between_intrs = 0;
+		ici->num_intrs = 0;
+		ici->num_io_completions = 0;
+	}
+
+	mutex_exit(&pwp->ict_lock);
+	thread_exit();
+}
+
+void
+pmcs_iodone_intr(pmcs_hw_t *pwp)
+{
+	char local[PMCS_QENTRY_SIZE << 1];
+	pmcs_iocomp_cb_t *ioccb;
+	uint32_t w0, ci, pi, nbuf, lim = 0, niodone = 0, iomb_opcode;
+	size_t amt;
+	uint32_t *ptr;
+	hrtime_t curtime = gethrtime();
+
+	ci = pmcs_rd_oqci(pwp, PMCS_OQ_IODONE);
+	pi = pmcs_rd_oqpi(pwp, PMCS_OQ_IODONE);
+
+	while (ci != pi) {
+		OQLIM_CHECK(pwp, lim);
+		ptr = GET_OQ_ENTRY(pwp, PMCS_OQ_IODONE, ci, 0);
+		w0 = LE_32(ptr[0]);
+		VALID_IOMB_CHECK(pwp, w0, ptr, ci, pi);
+		WRONG_OBID_CHECK(pwp, w0, PMCS_OQ_IODONE);
+		iomb_opcode = (w0 & PMCS_IOMB_OPCODE_MASK);
+
+		if ((iomb_opcode == PMCOUT_SSP_COMPLETION) ||
+		    (iomb_opcode == PMCOUT_SATA_COMPLETION)) {
+			ioccb =
+			    kmem_cache_alloc(pwp->iocomp_cb_cache, KM_NOSLEEP);
+			if (ioccb == NULL) {
+				pmcs_prt(pwp, PMCS_PRT_WARN, "%s: "
+				    "kmem_cache_alloc failed", __func__);
+				break;
+			}
+
+			COPY_OUTBOUND(pwp, w0, ioccb->iomb, nbuf, amt, ptr,
+			    PMCS_OQ_IODONE, ci);
+
+			niodone++;
+			pmcs_process_io_completion(pwp, ioccb, amt);
+		} else {
+			COPY_OUTBOUND(pwp, w0, local, nbuf, amt, ptr,
+			    PMCS_OQ_IODONE, ci);
+
+			switch (iomb_opcode) {
+			case PMCOUT_ECHO:
+				pmcs_process_echo_completion(pwp, local, amt);
+				break;
+			case PMCOUT_SATA_EVENT:
+				pmcs_process_sata_event(pwp, local, amt);
+				break;
+			case PMCOUT_SSP_EVENT:
+				pmcs_process_ssp_event(pwp, local, amt);
+				break;
+			case PMCOUT_SKIP_ENTRIES:
+				pmcs_prt(pwp, PMCS_PRT_DEBUG3,
+				    "%s: skip %d entries", __func__, nbuf);
+				break;
+			default:
+				(void) snprintf(local, sizeof (local),
+				    "%s: unhandled message", __func__);
+				pmcs_print_entry(pwp, PMCS_PRT_DEBUG, local,
+				    ptr);
+				break;
+			}
+		}
+
+		STEP_OQ_ENTRY(pwp, PMCS_OQ_IODONE, ci, nbuf);
+	}
+
+	if (lim != 0) {
+		SYNC_OQ_ENTRY(pwp, PMCS_OQ_IODONE, ci, pi);
+	}
+
+	/*
+	 * Update the interrupt coalescing timer check stats and run
+	 * completions for queued up commands.
+	 */
+
+	if (niodone > 0) {
+		/*
+		 * If we can't get the lock, then completions are either
+		 * already running or will be scheduled to do so shortly.
+		 */
+		if (mutex_tryenter(&pwp->cq_lock) != 0) {
+			PMCS_CQ_RUN_LOCKED(pwp);
+			mutex_exit(&pwp->cq_lock);
+		}
+
+		mutex_enter(&pwp->ict_lock);
+		pwp->io_intr_coal.nsecs_between_intrs +=
+		    curtime - pwp->io_intr_coal.last_io_comp;
+		pwp->io_intr_coal.num_intrs++;
+		pwp->io_intr_coal.num_io_completions += niodone;
+		if (niodone >= PMCS_MAX_IO_COMPS_PER_INTR) {
+			pwp->io_intr_coal.max_io_completions++;
+		}
+		pwp->io_intr_coal.last_io_comp = gethrtime();
+		mutex_exit(&pwp->ict_lock);
+	}
+}
+
+void
+pmcs_event_intr(pmcs_hw_t *pwp)
+{
+	char local[PMCS_QENTRY_SIZE << 1];
+	uint32_t w0, ci, pi, nbuf, lim =  0;
+	size_t amt;
+	uint32_t *ptr;
+
+	ci = pmcs_rd_oqci(pwp, PMCS_OQ_EVENTS);
+	pi = pmcs_rd_oqpi(pwp, PMCS_OQ_EVENTS);
+
+	while (ci != pi) {
+		OQLIM_CHECK(pwp, lim);
+		ptr = GET_OQ_ENTRY(pwp, PMCS_OQ_EVENTS, ci, 0);
+		w0 = LE_32(ptr[0]);
+		VALID_IOMB_CHECK(pwp, w0, ptr, ci, pi);
+		WRONG_OBID_CHECK(pwp, w0, PMCS_OQ_EVENTS);
+		COPY_OUTBOUND(pwp, w0, local, nbuf, amt, ptr,
+		    PMCS_OQ_EVENTS, ci);
+
+		switch (w0 & PMCS_IOMB_OPCODE_MASK) {
+		case PMCOUT_ECHO:
+			pmcs_process_echo_completion(pwp, local, amt);
+			break;
+		case PMCOUT_SATA_EVENT:
+			pmcs_process_sata_event(pwp, local, amt);
+			break;
+		case PMCOUT_SSP_EVENT:
+			pmcs_process_ssp_event(pwp, local, amt);
+			break;
+		case PMCOUT_GENERAL_EVENT:
+			pmcs_process_general_event(pwp, ptr);
+			break;
+		case PMCOUT_DEVICE_HANDLE_REMOVED:
+		{
+			uint32_t port = IOP_EVENT_PORTID(LE_32(ptr[1]));
+			uint32_t did = LE_32(ptr[2]);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "PortID 0x%x device_id 0x%x removed", port, did);
+			break;
+		}
+		case PMCOUT_SAS_HW_EVENT:
+			if (nbuf > 1) {
+				pmcs_prt(pwp, PMCS_PRT_INFO,
+				    "multiple SAS HW_EVENT (%d) responses "
+				    "in EVENT OQ", nbuf);
+			}
+			pmcs_process_sas_hw_event(pwp, local, PMCS_QENTRY_SIZE);
+			break;
+		case PMCOUT_FW_FLASH_UPDATE:
+		case PMCOUT_GET_TIME_STAMP:
+		case PMCOUT_GET_DEVICE_STATE:
+		case PMCOUT_SET_DEVICE_STATE:
+		case PMCOUT_SAS_DIAG_EXECUTE:
+			pmcs_process_completion(pwp, local, amt);
+			break;
+		case PMCOUT_SKIP_ENTRIES:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG3, "%s: skip %d entries",
+			    __func__, nbuf);
+			break;
+		default:
+			(void) snprintf(local, sizeof (local),
+			    "%s: unhandled message", __func__);
+			pmcs_print_entry(pwp, PMCS_PRT_DEBUG, local, ptr);
+			break;
+		}
+		STEP_OQ_ENTRY(pwp, PMCS_OQ_EVENTS, ci, nbuf);
+	}
+	if (lim) {
+		SYNC_OQ_ENTRY(pwp, PMCS_OQ_EVENTS, ci, pi);
+	}
+}
+
+void
+pmcs_timed_out(pmcs_hw_t *pwp, uint32_t htag, const char *func)
+{
+#ifdef	DEBUG
+	hrtime_t now = gethrtime();
+	int i;
+
+	for (i = 0; i < 256; i++) {
+		if (pwp->ftags[i] == htag) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "Inbound msg (tag 0x%8x) timed out - "
+			    "was started %llu ns ago in %s:%d",
+			    htag, (unsigned long long) (now - pwp->ftime[i]),
+			    func, pwp->ftag_lines[i]);
+			return;
+		}
+	}
+#endif
+	pmcs_prt(pwp, PMCS_PRT_DEBUG,
+	    "Inbound Message (tag 0x%08x) timed out- was started in %s",
+	    htag, func);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_nvram.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,823 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains various support routines.
+ */
+
+#include <sys/scsi/adapters/pmcs/pmcs.h>
+
+/*
+ * SAS Topology Configuration
+ */
+static int pmcs_flash_chunk(pmcs_hw_t *, uint8_t *);
+
+/*
+ * Check current firmware version for correctness
+ * and try to flash the correct firmware if what is
+ * running isn't correct.
+ *
+ * Must be called after setup and MPI setup and
+ * interrupts are enabled.
+ */
+
+int
+pmcs_firmware_update(pmcs_hw_t *pwp)
+{
+	ddi_modhandle_t modhp;
+	char buf[64];
+	int errno;
+	uint8_t *cstart, *cend;		/* Firmware image file */
+	uint8_t *istart, *iend; 	/* ila */
+	uint8_t *sstart, *send;		/* SPCBoot */
+	uint32_t *fwvp;
+	int defret = 0;
+
+	/*
+	 * If updating is disabled, we're done.
+	 */
+	if (pwp->fw_disable_update) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "Firmware update disabled by conf file");
+		return (0);
+	}
+
+	/*
+	 * If we're already running the right firmware, we're done.
+	 */
+	if (pwp->fw == PMCS_FIRMWARE_VERSION) {
+		if (pwp->fw_force_update == 0) {
+			return (0);
+		}
+
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "Firmware version matches, but still forcing update");
+	} else {
+		pmcs_prt(pwp, PMCS_PRT_WARN,
+		    "Upgrading firmware on card from 0x%x to 0x%x",
+		    pwp->fw, PMCS_FIRMWARE_VERSION);
+	}
+
+	modhp = ddi_modopen(PMCS_FIRMWARE_FILENAME, KRTLD_MODE_FIRST, &errno);
+	if (errno) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Firmware module not available; will not upgrade",
+		    __func__);
+		return (defret);
+	}
+
+	fwvp = ddi_modsym(modhp, PMCS_FIRMWARE_VERSION_NAME, &errno);
+	if (errno) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
+		    __func__, PMCS_FIRMWARE_VERSION_NAME);
+		(void) ddi_modclose(modhp);
+		return (defret);
+	}
+
+	/*
+	 * If the firmware version from the module isn't what we expect,
+	 * and force updating is disabled, return the default (for this
+	 * mode of operation) value.
+	 */
+	if (*fwvp != PMCS_FIRMWARE_VERSION) {
+		if (pwp->fw_force_update == 0) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: firmware module version wrong (0x%x)",
+			    __func__, *fwvp);
+			(void) ddi_modclose(modhp);
+			return (defret);
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: firmware module version wrong (0x%x) - update forced",
+		    __func__, *fwvp);
+	}
+
+	(void) snprintf(buf, sizeof (buf),
+	    PMCS_FIRMWARE_CODE_NAME PMCS_FIRMWARE_START_SUF);
+	cstart = ddi_modsym(modhp, buf, &errno);
+	if (errno) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
+		    __func__, buf);
+		(void) ddi_modclose(modhp);
+		return (defret);
+	}
+
+	(void) snprintf(buf, sizeof (buf),
+	    PMCS_FIRMWARE_CODE_NAME PMCS_FIRMWARE_END_SUF);
+	cend = ddi_modsym(modhp, buf, &errno);
+	if (errno) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
+		    __func__, buf);
+		(void) ddi_modclose(modhp);
+		return (defret);
+	}
+
+	(void) snprintf(buf, sizeof (buf),
+	    PMCS_FIRMWARE_ILA_NAME PMCS_FIRMWARE_START_SUF);
+	istart = ddi_modsym(modhp, buf, &errno);
+	if (errno) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
+		    __func__, buf);
+		(void) ddi_modclose(modhp);
+		return (defret);
+	}
+
+	(void) snprintf(buf, sizeof (buf),
+	    PMCS_FIRMWARE_ILA_NAME PMCS_FIRMWARE_END_SUF);
+	iend = ddi_modsym(modhp, buf, &errno);
+	if (errno) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
+		    __func__, buf);
+		(void) ddi_modclose(modhp);
+		return (defret);
+	}
+
+	(void) snprintf(buf, sizeof (buf),
+	    PMCS_FIRMWARE_SPCBOOT_NAME PMCS_FIRMWARE_START_SUF);
+	sstart = ddi_modsym(modhp, buf, &errno);
+	if (errno) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
+		    __func__, buf);
+		(void) ddi_modclose(modhp);
+		return (defret);
+	}
+
+	(void) snprintf(buf, sizeof (buf),
+	    PMCS_FIRMWARE_SPCBOOT_NAME PMCS_FIRMWARE_END_SUF);
+	send = ddi_modsym(modhp, buf, &errno);
+	if (errno) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: unable to find symbol '%s'",
+		    __func__, buf);
+		(void) ddi_modclose(modhp);
+		return (defret);
+	}
+
+	/*
+	 * The SPCBoot image must be updated first, and this is written to
+	 * SEEPROM, not flash.
+	 */
+	if (pmcs_set_nvmd(pwp, PMCS_NVMD_SPCBOOT, sstart,
+	    (size_t)((size_t)send - (size_t)sstart)) == B_FALSE) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: unable to flash '%s' segment",
+		    __func__, PMCS_FIRMWARE_SPCBOOT_NAME);
+		(void) ddi_modclose(modhp);
+		return (-1);
+	}
+
+	if (pmcs_fw_flash(pwp, (void *)istart,
+	    (uint32_t)((size_t)iend - (size_t)istart))) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: unable to flash '%s' segment",
+		    __func__, PMCS_FIRMWARE_ILA_NAME);
+		(void) ddi_modclose(modhp);
+		return (-1);
+	}
+
+	if (pmcs_fw_flash(pwp, (void *)cstart,
+	    (uint32_t)((size_t)cend - (size_t)cstart))) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: unable to flash '%s' segment",
+		    __func__, PMCS_FIRMWARE_CODE_NAME);
+		(void) ddi_modclose(modhp);
+		return (-1);
+	}
+
+	(void) ddi_modclose(modhp);
+
+	if (pmcs_soft_reset(pwp, B_FALSE)) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: soft reset after flash update failed", __func__);
+		return (-1);
+	} else {
+		pmcs_prt(pwp, PMCS_PRT_WARN,
+		    "%s: Firmware successfully upgraded.", __func__);
+	}
+	return (0);
+}
+
+/*
+ * Flash firmware support
+ * Called unlocked.
+ */
+int
+pmcs_fw_flash(pmcs_hw_t *pwp, pmcs_fw_hdr_t *hdr, uint32_t length)
+{
+	pmcs_fw_hdr_t *hp;
+	uint8_t *wrk, *base;
+
+	/*
+	 * Step 1- Validate firmware chunks within passed pointer.
+	 */
+	hp = hdr;
+	wrk = (uint8_t *)hdr;
+	base = wrk;
+	for (;;) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG1,
+		    "%s: partition 0x%x, Length 0x%x", __func__,
+		    hp->destination_partition, ntohl(hp->firmware_length));
+		if (ntohl(hp->firmware_length) == 0) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: bad firmware length 0x%x",
+			    __func__, ntohl(hp->firmware_length));
+			return (EINVAL);
+		}
+		wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
+		if (wrk == base + length) {
+			break;
+		}
+		if (wrk > base + length) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: out of bounds firmware length", __func__);
+			return (EINVAL);
+		}
+		hp = (void *)wrk;
+	}
+
+	/*
+	 * Step 2- acquire scratch
+	 */
+	(void) pmcs_acquire_scratch(pwp, B_TRUE);
+
+	/*
+	 * Step 3- loop through firmware chunks and send each one
+	 * down to be flashed.
+	 */
+	hp = hdr;
+	wrk = (uint8_t *)hdr;
+	base = wrk;
+	for (;;) {
+		if (pmcs_flash_chunk(pwp, wrk)) {
+			pmcs_release_scratch(pwp);
+			return (EIO);
+		}
+		wrk += (sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length));
+		if (wrk == base + length) {
+			break;
+		}
+		hp = (void *) wrk;
+	}
+	pmcs_release_scratch(pwp);
+	return (0);
+}
+
+static int
+pmcs_flash_chunk(pmcs_hw_t *pwp, uint8_t *chunk)
+{
+	pmcs_fw_hdr_t *hp;
+	pmcwork_t *pwrk;
+	uint32_t len, seg, off, result, amt, msg[PMCS_MSG_SIZE], *ptr;
+
+	hp = (void *)chunk;
+	len = sizeof (pmcs_fw_hdr_t) + ntohl(hp->firmware_length);
+
+	seg = off = 0;
+	while (off < len) {
+		amt = PMCS_SCRATCH_SIZE;
+		if (off + amt > len) {
+			amt = len - off;
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG1,
+		    "%s: segment %d offset %u length %u",
+		    __func__, seg, off, amt);
+		(void) memcpy(pwp->scratch, &chunk[off], amt);
+		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
+		if (pwrk == NULL) {
+			return (ENOMEM);
+		}
+		pwrk->arg = msg;
+		msg[0] = LE_32(PMCS_HIPRI(pwp,
+		    PMCS_OQ_EVENTS, PMCIN_FW_FLASH_UPDATE));
+		msg[1] = LE_32(pwrk->htag);
+		msg[2] = LE_32(off);
+		msg[3] = LE_32(amt);
+		if (off == 0) {
+			msg[4] = LE_32(len);
+		} else {
+			msg[4] = 0;
+		}
+		msg[5] = 0;
+		msg[6] = 0;
+		msg[7] = 0;
+		msg[8] = 0;
+		msg[9] = 0;
+		msg[10] = 0;
+		msg[11] = 0;
+		msg[12] = LE_32(DWORD0(pwp->scratch_dma));
+		msg[13] = LE_32(DWORD1(pwp->scratch_dma));
+		msg[14] = LE_32(amt);
+		msg[15] = 0;
+		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+		if (ptr == NULL) {
+			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+			pmcs_pwork(pwp, pwrk);
+			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
+			return (ENOMEM);
+		}
+		COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
+		(void) memset(msg, 0xaf, sizeof (msg));
+		pwrk->state = PMCS_WORK_STATE_ONCHIP;
+		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+		WAIT_FOR(pwrk, 5000, result);
+		pmcs_pwork(pwp, pwrk);
+		if (result) {
+			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__);
+			return (EIO);
+		}
+		switch (LE_32(msg[2])) {
+		case FLASH_UPDATE_COMPLETE_PENDING_REBOOT:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
+			    "%s: segment %d complete pending reboot",
+			    __func__, seg);
+			break;
+		case FLASH_UPDATE_IN_PROGRESS:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
+			    "%s: segment %d downloaded", __func__, seg);
+			break;
+		case FLASH_UPDATE_HDR_ERR:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: segment %d header error", __func__, seg);
+			return (EIO);
+		case FLASH_UPDATE_OFFSET_ERR:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: segment %d offset error", __func__, seg);
+			return (EIO);
+		case FLASH_UPDATE_UPDATE_CRC_ERR:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: segment %d update crc error", __func__, seg);
+			return (EIO);
+		case FLASH_UPDATE_LENGTH_ERR:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: segment %d length error", __func__, seg);
+			return (EIO);
+		case FLASH_UPDATE_HW_ERR:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: segment %d hw error", __func__, seg);
+			return (EIO);
+		case FLASH_UPDATE_DNLD_NOT_SUPPORTED:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: segment %d download not supported error",
+			    __func__, seg);
+			return (EIO);
+		case FLASH_UPDATE_DISABLED:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: segment %d update disabled error",
+			    __func__, seg);
+			return (EIO);
+		default:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: segment %d unknown error %x",
+			    __func__, seg, msg[2]);
+			return (EIO);
+		}
+		off += amt;
+		seg++;
+	}
+	return (0);
+}
+
+/*
+ * pmcs_validate_vpd
+ *
+ * Input: softstate pointer and pointer to vpd data buffer
+ * Returns: B_TRUE if VPD data looks OK, B_FALSE otherwise
+ */
+static boolean_t
+pmcs_validate_vpd(pmcs_hw_t *pwp, uint8_t *data)
+{
+	pmcs_vpd_header_t *vpd_header;
+	uint8_t *bufp, kv_len, *chksump, chksum = 0;
+	char tbuf[80];
+	char prop[24];
+	int idx, str_len;
+	uint16_t strid_length, chksum_len;
+	uint64_t wwid;
+	pmcs_vpd_kv_t *vkvp;
+
+	vpd_header = (pmcs_vpd_header_t *)data;
+
+	/*
+	 * Make sure we understand the format of this data
+	 */
+
+	if (vpd_header->eeprom_version < PMCS_VPD_VERSION) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: VPD version(%d) out-of-date; (%d) required."
+		    " Thebe card needs to be flashed.",
+		    __func__, vpd_header->eeprom_version, PMCS_VPD_VERSION);
+	}
+	if ((vpd_header->eeprom_version != PMCS_VPD_VERSION) &&
+	    (vpd_header->eeprom_version != (PMCS_VPD_VERSION - 1))) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: VPD version mismatch (%d != %d)",
+		    __func__, vpd_header->eeprom_version, PMCS_VPD_VERSION);
+		return (B_FALSE);
+	}
+
+	/*
+	 * Do we have a valid SAS WWID?
+	 */
+	if (((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4) != NAA_IEEE_REG) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: SAS WWN has invalid NAA (%d)", __func__,
+		    ((vpd_header->hba_sas_wwid[0] & 0xf0) >> 4));
+		return (B_FALSE);
+	}
+	wwid = pmcs_barray2wwn(vpd_header->hba_sas_wwid);
+	for (idx = 0; idx < PMCS_MAX_PORTS; idx++) {
+		pwp->sas_wwns[idx] = wwid + idx;
+	}
+
+	if (vpd_header->vpd_start_byte != PMCS_VPD_START) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Didn't see VPD start byte", __func__);
+		return (B_FALSE);
+	}
+
+	/*
+	 * We only checksum the VPD data between (and including) VPD Start byte
+	 * and the checksum value byte. The length of this data for CRC is
+	 * 15 less than the length indicated in vpd_length field of the header.
+	 * 8 (SAS WWN) + 2 (subsystem ID) + 2 (subsystem vendor ID) +
+	 * 1 (end tag) + 2 (hex byte CRC, different from this one) = 15 bytes
+	 */
+	/*
+	 * VPD length (little endian format) is represented as byte-array field
+	 * & read the following way to avoid alignment issues (in SPARC)
+	 */
+	chksum_len = ((vpd_header->vpd_length[1] << 8) |
+	    (vpd_header->vpd_length[0])) - 15;
+	/* Validate VPD data checksum */
+	chksump = (uint8_t *)&vpd_header->vpd_start_byte;
+	ASSERT (*chksump == PMCS_VPD_START);
+	for (idx = 0; idx < chksum_len; idx++, chksump++) {
+		chksum += *chksump;
+	}
+	ASSERT (*chksump == PMCS_VPD_END);
+	if (chksum) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: VPD checksum(%d) non-zero."
+		    " Checksum validation failed.", __func__, chksum);
+	}
+
+	/*
+	 * Get length of string ID tag and read it.
+	 */
+	bufp = (uint8_t *)&vpd_header->vpd_start_byte;
+	bufp += 3;		/* Skip the start byte and length */
+	/*
+	 * String ID tag length (little endian format) is represented as
+	 * byte-array & read the following way to avoid alignment issues
+	 * (in SPARC)
+	 */
+	strid_length = (vpd_header->strid_length[1] << 8) |
+	    (vpd_header->strid_length[0]);
+	if (strid_length > 79) {
+		strid_length = 79;
+	}
+	bcopy(bufp, tbuf, strid_length);
+	tbuf[strid_length] = 0;
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+	    "%s: Product Name: '%s'", __func__, tbuf);
+	pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_MODEL_NAME, tbuf);
+
+	/*
+	 * Skip VPD-R tag and length of read-only tag, then start reading
+	 * keyword/value pairs
+	 */
+	bufp += strid_length;	/* Skip to VPD-R tag */
+	bufp += 3;		/* Skip VPD-R tag and length of VPD-R data */
+
+	vkvp = (pmcs_vpd_kv_t *)bufp;
+
+	while (vkvp->keyword[0] != PMCS_VPD_END) {
+		tbuf[0] = 0;
+		str_len = snprintf(tbuf, 80, "VPD: %c%c = <",
+		    vkvp->keyword[0], vkvp->keyword[1]);
+
+		kv_len = vkvp->value_length;
+		for (idx = 0; idx < kv_len; idx++) {
+			tbuf[str_len + idx] = vkvp->value[idx];
+			prop[idx] = vkvp->value[idx];
+		}
+		prop[idx] = '\0';
+		str_len += kv_len;
+		tbuf[str_len] = '>';
+		tbuf[str_len + 1] = 0;
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s (Len: 0x%x)", tbuf, kv_len);
+
+		/* Keyword is Manufacturer */
+		if ((vkvp->keyword[0] == 'M') && (vkvp->keyword[1] == 'N')) {
+			pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING,
+			    PMCS_MANUFACTURER, prop);
+		}
+		/* Keyword is Serial Number */
+		if ((vkvp->keyword[0] == 'S') && (vkvp->keyword[1] == 'N')) {
+			pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING,
+			    PMCS_SERIAL_NUMBER, prop);
+		}
+
+		vkvp = (pmcs_vpd_kv_t *)(bufp + 3 + kv_len);
+		bufp += kv_len + 3;
+	}
+
+	return (B_TRUE);
+}
+
+/*
+ * pmcs_get_nvmd
+ *
+ * This function will read the requested data from the non-volatile
+ * storage on the card.  This could mean SEEPROM, VPD, or other areas
+ * as defined by the PM8001 programmer's manual.
+ *
+ * nvmd_type: The data type being requested
+ * nvmd: NVM device to access (IOP/AAP1)
+ * offset: Must be 4K alignment
+ * buf: Pointer to memory region for retrieved data
+ * size_left: Total available bytes left in buf
+ *
+ * Returns: non-negative on success, -1 on failure
+ */
+
+/*ARGSUSED*/
+int
+pmcs_get_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t nvmd,
+    uint32_t offset, char *buf, uint32_t size_left)
+{
+	pmcs_get_nvmd_cmd_t iomb;
+	pmcwork_t *workp;
+	uint8_t *chunkp;
+	uint32_t *ptr, ibq, *iombp;
+	uint32_t dlen;
+	uint16_t status;
+	uint8_t tdas_nvmd, ip, tda, tbn_tdps;
+	uint8_t	doa[3];
+	int32_t result = -1, i = 0;
+
+	switch (nvmd_type) {
+	case PMCS_NVMD_VPD:
+		tdas_nvmd = PMCIN_NVMD_TDPS_1 | PMCIN_NVMD_TWI;
+		tda = PMCIN_TDA_PAGE(2);
+		tbn_tdps = PMCIN_NVMD_TBN(0) | PMCIN_NVMD_TDPS_8;
+		ip = PMCIN_NVMD_INDIRECT_PLD;
+		dlen = LE_32(PMCS_SEEPROM_PAGE_SIZE);
+		doa[0] = 0;
+		doa[1] = 0;
+		doa[2] = 0;
+		break;
+	case PMCS_NVMD_REG_DUMP:
+		tdas_nvmd = nvmd;
+		tda = 0;
+		tbn_tdps = 0;
+		ip = PMCIN_NVMD_INDIRECT_PLD;
+		dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE);
+		doa[0] = offset & 0xff;
+		doa[1] = (offset >> 8) & 0xff;
+		doa[2] = (offset >> 16) & 0xff;
+		break;
+	case PMCS_NVMD_EVENT_LOG:
+		tdas_nvmd = nvmd;
+		tda = 0;
+		tbn_tdps = 0;
+		ip = PMCIN_NVMD_INDIRECT_PLD;
+		dlen = LE_32(PMCS_REGISTER_DUMP_BLOCK_SIZE);
+		offset = offset + PMCS_NVMD_EVENT_LOG_OFFSET;
+		doa[0] = offset & 0xff;
+		doa[1] = (offset >> 8) & 0xff;
+		doa[2] = (offset >> 16) & 0xff;
+		break;
+	default:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Invalid nvmd type: %d", __func__, nvmd_type);
+		return (-1);
+	}
+
+	workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
+	if (workp == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_WARN,
+		    "%s: Unable to get work struct", __func__);
+		return (-1);
+	}
+
+	ptr = &iomb.header;
+	bzero(ptr, sizeof (pmcs_get_nvmd_cmd_t));
+	*ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_GET_NVMD_DATA));
+	workp->arg = (void *)&iomb;
+	iomb.htag = LE_32(workp->htag);
+	iomb.ip = ip;
+	iomb.tbn_tdps = tbn_tdps;
+	iomb.tda = tda;
+	iomb.tdas_nvmd = tdas_nvmd;
+	iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr));
+	iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr));
+	iomb.ipdl = dlen;
+	iomb.doa[0] = doa[0];
+	iomb.doa[1] = doa[1];
+	iomb.doa[2] = doa[2];
+
+	/*
+	 * ptr will now point to the inbound queue message
+	 */
+	GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq);
+	if (ptr == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, "!%s: Unable to get IQ entry",
+		    __func__);
+		pmcs_pwork(pwp, workp);
+		return (-1);
+	}
+
+	bzero(ptr, PMCS_MSG_SIZE << 2);	/* PMCS_MSG_SIZE is in dwords */
+	iombp = (uint32_t *)&iomb;
+	COPY_MESSAGE(ptr, iombp, sizeof (pmcs_get_nvmd_cmd_t) >> 2);
+	workp->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, ibq);
+
+	WAIT_FOR(workp, 1000, result);
+	ptr = workp->arg;
+	if (result) {
+		pmcs_timed_out(pwp, workp->htag, __func__);
+		pmcs_pwork(pwp, workp);
+		return (-1);
+	}
+	status = LE_32(*(ptr + 3)) & 0xffff;
+	if (status != PMCS_NVMD_STAT_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Error, status = 0x%04x",
+		    __func__, status);
+		pmcs_pwork(pwp, workp);
+		return (-1);
+	}
+
+	pmcs_pwork(pwp, workp);
+
+	if (ddi_dma_sync(pwp->cip_handles, 0, 0,
+	    DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check failed at "
+		    "%s():%d", __func__, __LINE__);
+	}
+	chunkp = (uint8_t *)pwp->flash_chunkp;
+
+	switch (nvmd) {
+	case PMCIN_NVMD_VPD:
+		if (pmcs_validate_vpd(pwp, chunkp)) {
+			result = 0;
+		} else {
+			result = -1;
+		}
+		break;
+	case PMCIN_NVMD_AAP1:
+	case PMCIN_NVMD_IOP:
+		ASSERT(buf);
+		i = 0;
+		if (nvmd_type == PMCS_NVMD_REG_DUMP) {
+			while ((i < PMCS_FLASH_CHUNK_SIZE) &&
+			    (chunkp[i] != 0xff) && (chunkp[i] != '\0')) {
+				(void) snprintf(&buf[i], (size_left - i),
+				    "%c", chunkp[i]);
+				i++;
+			}
+		} else if (nvmd_type == PMCS_NVMD_EVENT_LOG) {
+			i = pmcs_dump_binary(pwp, pwp->flash_chunkp, 0,
+			    (PMCS_FLASH_CHUNK_SIZE >> 2), buf, size_left);
+		}
+		result = i;
+		break;
+	default:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "UNKNOWN NVMD DEVICE");
+		return (-1);
+	}
+
+	return (result);
+}
+
+/*
+ * pmcs_set_nvmd
+ *
+ * This function will write the requested data to non-volatile storage
+ * on the HBA.  This could mean SEEPROM, VPD, or other areas as defined by
+ * the PM8001 programmer's manual.
+ *
+ * nvmd_type: The data type to be written
+ * buf: Pointer to memory region for data to write
+ * len: Length of the data buffer
+ *
+ * Returns: B_TRUE on success, B_FALSE on failure
+ */
+
+boolean_t
+pmcs_set_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t *buf,
+    size_t len)
+{
+	pmcs_set_nvmd_cmd_t iomb;
+	pmcwork_t *workp;
+	uint32_t *ptr, ibq, *iombp;
+	uint32_t dlen;
+	uint16_t status;
+	uint8_t tdas_nvmd, ip;
+	int result;
+
+	switch (nvmd_type) {
+	case PMCS_NVMD_SPCBOOT:
+		tdas_nvmd = PMCIN_NVMD_SEEPROM;
+		ip = PMCIN_NVMD_INDIRECT_PLD;
+		ASSERT((len >= PMCS_SPCBOOT_MIN_SIZE) &&
+		    (len <= PMCS_SPCBOOT_MAX_SIZE));
+		dlen = LE_32(len);
+		break;
+	default:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Invalid nvmd type: %d", __func__, nvmd_type);
+		return (B_FALSE);
+	}
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_DEVEL, "%s: Request for nvmd type: %d",
+	    __func__, nvmd_type);
+
+	workp = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
+	if (workp == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_WARN,
+		    "%s: Unable to get work struct", __func__);
+		return (B_FALSE);
+	}
+
+	ptr = &iomb.header;
+	bzero(ptr, sizeof (pmcs_set_nvmd_cmd_t));
+	*ptr = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_GENERAL, PMCIN_SET_NVMD_DATA));
+	workp->arg = (void *)&iomb;
+	iomb.htag = LE_32(workp->htag);
+	iomb.ip = ip;
+	iomb.tdas_nvmd = tdas_nvmd;
+	iomb.signature = LE_32(PMCS_SEEPROM_SIGNATURE);
+	iomb.ipbal = LE_32(DWORD0(pwp->flash_chunk_addr));
+	iomb.ipbah = LE_32(DWORD1(pwp->flash_chunk_addr));
+	iomb.ipdl = dlen;
+
+	pmcs_print_entry(pwp, PMCS_PRT_DEBUG_DEVEL,
+	    "PMCIN_SET_NVMD_DATA iomb", (void *)&iomb);
+
+	bcopy(buf, pwp->flash_chunkp, len);
+	if (ddi_dma_sync(pwp->cip_handles, 0, 0,
+	    DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check failed at "
+		    "%s():%d", __func__, __LINE__);
+	}
+
+	/*
+	 * ptr will now point to the inbound queue message
+	 */
+	GET_IO_IQ_ENTRY(pwp, ptr, 0, ibq);
+	if (ptr == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, "!%s: Unable to get IQ entry",
+		    __func__);
+		pmcs_pwork(pwp, workp);
+		return (B_FALSE);
+	}
+
+	bzero(ptr, PMCS_MSG_SIZE << 2);	/* PMCS_MSG_SIZE is in dwords */
+	iombp = (uint32_t *)&iomb;
+	COPY_MESSAGE(ptr, iombp, sizeof (pmcs_set_nvmd_cmd_t) >> 2);
+	workp->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, ibq);
+
+	WAIT_FOR(workp, 2000, result);
+
+	if (result) {
+		pmcs_timed_out(pwp, workp->htag, __func__);
+		pmcs_pwork(pwp, workp);
+		return (B_FALSE);
+	}
+
+	pmcs_pwork(pwp, workp);
+
+	status = LE_32(*(ptr + 3)) & 0xffff;
+	if (status != PMCS_NVMD_STAT_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Error, status = 0x%04x",
+		    __func__, status);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_sata.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,709 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * SATA midlayer interface for PMC drier.
+ */
+
+#include <sys/scsi/adapters/pmcs/pmcs.h>
+
+static void
+SATAcopy(pmcs_cmd_t *sp, void *kbuf, uint32_t amt)
+{
+	struct buf *bp = scsi_pkt2bp(CMD2PKT(sp));
+
+	bp_mapin(scsi_pkt2bp(CMD2PKT(sp)));
+	/* There is only one direction currently */
+	(void) memcpy(bp->b_un.b_addr, kbuf, amt);
+	CMD2PKT(sp)->pkt_resid -= amt;
+	CMD2PKT(sp)->pkt_state |= STATE_XFERRED_DATA;
+	bp_mapout(scsi_pkt2bp(CMD2PKT(sp)));
+}
+
+/*
+ * Run a non block-io command. Some commands are interpreted
+ * out of extant data. Some imply actually running a SATA command.
+ *
+ * Returns zero if we were able to run.
+ *
+ * Returns -1 only if other commands are active, either another
+ * command here or regular I/O active.
+ *
+ * Called with PHY lock and xp statlock held.
+ */
+#define	SRESPSZ	128
+
+static int
+pmcs_sata_special_work(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
+{
+	int i;
+	int saq;
+	pmcs_cmd_t *sp;
+	struct scsi_pkt *pkt;
+	pmcs_phy_t *pptr;
+	uint8_t rp[SRESPSZ];
+	ata_identify_t *id;
+	uint32_t amt = 0;
+	uint8_t key = 0x05;	/* illegal command */
+	uint8_t asc = 0;
+	uint8_t ascq = 0;
+	uint8_t status = STATUS_GOOD;
+
+	if (xp->actv_cnt) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG1, "%s: target %p actv count %u",
+		    __func__, (void *)xp, xp->actv_cnt);
+		return (-1);
+	}
+	if (xp->special_running) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: target %p special running already",
+		    __func__, (void *)xp);
+		return (-1);
+	}
+	xp->special_needed = 0;
+
+	/*
+	 * We're now running special.
+	 */
+	xp->special_running = 1;
+	pptr = xp->phy;
+
+	sp = STAILQ_FIRST(&xp->sq);
+	if (sp == NULL) {
+		xp->special_running = 0;
+		return (0);
+	}
+
+	pkt = CMD2PKT(sp);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+	    "%s: target %p cmd %p cdb0 %x with actv_cnt %u",
+	    __func__, (void *)xp, (void *)sp, pkt->pkt_cdbp[0], xp->actv_cnt);
+
+	if (pkt->pkt_cdbp[0] == SCMD_INQUIRY ||
+	    pkt->pkt_cdbp[0] == SCMD_READ_CAPACITY) {
+		int retval;
+
+		if (pmcs_acquire_scratch(pwp, B_FALSE)) {
+			xp->special_running = 0;
+			return (-1);
+		}
+		saq = 1;
+
+		mutex_exit(&xp->statlock);
+		retval = pmcs_sata_identify(pwp, pptr);
+		mutex_enter(&xp->statlock);
+
+		if (retval) {
+			pmcs_release_scratch(pwp);
+			xp->special_running = 0;
+
+			pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+			    "%s: target %p identify failed %x",
+			    __func__, (void *)xp, retval);
+			/*
+			 * If the failure is due to not being
+			 * able to get resources, return such
+			 * that we'll try later. Otherwise,
+			 * fail current command.
+			 */
+			if (retval == ENOMEM) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "%s: sata identify failed (ENOMEM) for "
+				    "cmd %p", __func__, (void *)sp);
+				return (-1);
+			}
+			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
+			    STATE_SENT_CMD;
+			if (retval == ETIMEDOUT) {
+				pkt->pkt_reason = CMD_TIMEOUT;
+				pkt->pkt_statistics |= STAT_TIMEOUT;
+			} else {
+				pkt->pkt_reason = CMD_TRAN_ERR;
+			}
+			goto out;
+		}
+
+		id = pwp->scratch;
+
+		/*
+		 * Check to see if this device is an NCQ capable device.
+		 * Yes, we'll end up doing this check for every INQUIRY
+		 * if indeed we *are* only a pio device, but this is so
+		 * infrequent that it's not really worth an extra bitfield.
+		 *
+		 * Note that PIO mode here means that the PMCS firmware
+		 * performs PIO- not us.
+		 */
+		if (xp->ncq == 0) {
+			/*
+			 * Reset existing stuff.
+			 */
+			xp->pio = 0;
+			xp->qdepth = 1;
+			xp->tagmap = 0;
+
+			if (id->word76 != 0 && id->word76 != 0xffff &&
+			    (LE_16(id->word76) & (1 << 8))) {
+				xp->ncq = 1;
+				xp->qdepth = (LE_16(id->word75) & 0x1f) + 1;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+				    "%s: device %s supports NCQ %u deep",
+				    __func__, xp->phy->path, xp->qdepth);
+			} else {
+				/*
+				 * Default back to PIO.
+				 *
+				 * Note that non-FPDMA would still be possible,
+				 * but for this specific configuration, if it's
+				 * not NCQ it's safest to assume PIO.
+				 */
+				xp->pio = 1;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+				    "%s: device %s assumed PIO",
+				    __func__, xp->phy->path);
+			}
+		}
+	} else {
+		saq = 0;
+		id = NULL;
+	}
+
+	bzero(rp, SRESPSZ);
+
+	switch (pkt->pkt_cdbp[0]) {
+	case SCMD_INQUIRY:
+	{
+		struct scsi_inquiry *inqp;
+		uint16_t *a, *b;
+
+		/* Check for illegal bits */
+		if ((pkt->pkt_cdbp[1] & 0xfc) || pkt->pkt_cdbp[5]) {
+			status = STATUS_CHECK;
+			asc = 0x24;	/* invalid field in cdb */
+			break;
+		}
+		if (pkt->pkt_cdbp[1] & 0x1) {
+			switch (pkt->pkt_cdbp[2]) {
+			case 0x0:
+				rp[3] = 3;
+				rp[5] = 0x80;
+				rp[6] = 0x83;
+				amt = 7;
+				break;
+			case 0x80:
+				rp[1] = 0x80;
+				rp[3] = 0x14;
+				a = (void *) &rp[4];
+				b = id->model_number;
+				for (i = 0; i < 5; i++) {
+					*a = ddi_swap16(*b);
+					a++;
+					b++;
+				}
+				amt = 24;
+				break;
+			case 0x83:
+				rp[1] = 0x83;
+				if ((LE_16(id->word87) & 0x100) &&
+				    (LE_16(id->word108) >> 12) == 5)  {
+					rp[3] = 12;
+					rp[4] = 1;
+					rp[5] = 3;
+					rp[7] = 8;
+					rp[8] = LE_16(id->word108) >> 8;
+					rp[9] = LE_16(id->word108);
+					rp[10] = LE_16(id->word109) >> 8;
+					rp[11] = LE_16(id->word109);
+					rp[12] = LE_16(id->word110) >> 8;
+					rp[13] = LE_16(id->word110);
+					rp[14] = LE_16(id->word111) >> 8;
+					rp[15] = LE_16(id->word111);
+					amt = 16;
+				} else {
+					rp[3] = 64;
+					rp[4] = 2;
+					rp[5] = 1;
+					rp[7] = 60;
+					rp[8] = 'A';
+					rp[9] = 'T';
+					rp[10] = 'A';
+					rp[11] = ' ';
+					rp[12] = ' ';
+					rp[13] = ' ';
+					rp[14] = ' ';
+					rp[15] = ' ';
+					a = (void *) &rp[16];
+					b = id->model_number;
+					for (i = 0; i < 20; i++) {
+						*a = ddi_swap16(*b);
+						a++;
+						b++;
+					}
+					a = (void *) &rp[40];
+					b = id->serial_number;
+					for (i = 0; i < 10; i++) {
+						*a = ddi_swap16(*b);
+						a++;
+						b++;
+					}
+					amt = 68;
+				}
+				break;
+			default:
+				status = STATUS_CHECK;
+				asc = 0x24;	/* invalid field in cdb */
+				break;
+			}
+		} else {
+			inqp = (struct scsi_inquiry *)rp;
+			inqp->inq_qual = (LE_16(id->word0) & 0x80) ? 0x80 : 0;
+			inqp->inq_ansi = 5;	/* spc3 */
+			inqp->inq_rdf = 2;	/* response format 2 */
+			inqp->inq_len = 32;
+
+			if (xp->ncq && (xp->qdepth > 1)) {
+				inqp->inq_cmdque = 1;
+			}
+
+			(void) memcpy(inqp->inq_vid, "ATA     ", 8);
+
+			a = (void *)inqp->inq_pid;
+			b = id->model_number;
+			for (i = 0; i < 8; i++) {
+				*a = ddi_swap16(*b);
+				a++;
+				b++;
+			}
+			if (id->firmware_revision[2] == 0x2020 &&
+			    id->firmware_revision[3] == 0x2020) {
+				inqp->inq_revision[0] =
+				    ddi_swap16(id->firmware_revision[0]) >> 8;
+				inqp->inq_revision[1] =
+				    ddi_swap16(id->firmware_revision[0]);
+				inqp->inq_revision[2] =
+				    ddi_swap16(id->firmware_revision[1]) >> 8;
+				inqp->inq_revision[3] =
+				    ddi_swap16(id->firmware_revision[1]);
+			} else {
+				inqp->inq_revision[0] =
+				    ddi_swap16(id->firmware_revision[2]) >> 8;
+				inqp->inq_revision[1] =
+				    ddi_swap16(id->firmware_revision[2]);
+				inqp->inq_revision[2] =
+				    ddi_swap16(id->firmware_revision[3]) >> 8;
+				inqp->inq_revision[3] =
+				    ddi_swap16(id->firmware_revision[3]);
+			}
+			amt = 36;
+		}
+		amt = pmcs_set_resid(pkt, amt, pkt->pkt_cdbp[4]);
+		if (amt) {
+			if (xp->actv_cnt) {
+				xp->special_needed = 1;
+				xp->special_running = 0;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: @ line %d",
+				    __func__, __LINE__);
+				if (saq) {
+					pmcs_release_scratch(pwp);
+				}
+				return (-1);
+			}
+			SATAcopy(sp, rp, amt);
+		}
+		break;
+	}
+	case SCMD_READ_CAPACITY:
+	{
+		uint64_t last_block;
+		uint32_t block_size = 512;	/* XXXX */
+
+		xp->capacity = LBA_CAPACITY(id);
+		last_block = xp->capacity - 1;
+		/* Check for illegal bits */
+		if ((pkt->pkt_cdbp[1] & 0xfe) || pkt->pkt_cdbp[6] ||
+		    (pkt->pkt_cdbp[8] & 0xfe) || pkt->pkt_cdbp[7] ||
+		    pkt->pkt_cdbp[9]) {
+			status = STATUS_CHECK;
+			asc = 0x24;	/* invalid field in cdb */
+			break;
+		}
+		for (i = 1; i < 10; i++) {
+			if (pkt->pkt_cdbp[i]) {
+				status = STATUS_CHECK;
+				asc = 0x24;	/* invalid field in cdb */
+				break;
+			}
+		}
+		if (status != STATUS_GOOD) {
+			break;
+		}
+		if (last_block > 0xffffffffULL) {
+			last_block = 0xffffffffULL;
+		}
+		rp[0] = (last_block >> 24) & 0xff;
+		rp[1] = (last_block >> 16) & 0xff;
+		rp[2] = (last_block >>  8) & 0xff;
+		rp[3] = (last_block) & 0xff;
+		rp[4] = (block_size >> 24) & 0xff;
+		rp[5] = (block_size >> 16) & 0xff;
+		rp[6] = (block_size >>  8) & 0xff;
+		rp[7] = (block_size) & 0xff;
+		amt = 8;
+		amt = pmcs_set_resid(pkt, amt, 8);
+		if (amt) {
+			if (xp->actv_cnt) {
+				xp->special_needed = 1;
+				xp->special_running = 0;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: @ line %d",
+				    __func__, __LINE__);
+				if (saq) {
+					pmcs_release_scratch(pwp);
+				}
+				return (-1);
+			}
+			SATAcopy(sp, rp, amt);
+		}
+		break;
+	}
+	case SCMD_REPORT_LUNS: {
+		int rl_len;
+
+		/* Check for illegal bits */
+		if (pkt->pkt_cdbp[1] || pkt->pkt_cdbp[3] || pkt->pkt_cdbp[4] ||
+		    pkt->pkt_cdbp[5] || pkt->pkt_cdbp[10] ||
+		    pkt->pkt_cdbp[11]) {
+			status = STATUS_CHECK;
+			asc = 0x24;	/* invalid field in cdb */
+			break;
+		}
+
+		rp[3] = 8;
+		rl_len = 16;	/* list length (4) + reserved (4) + 1 LUN (8) */
+		amt = rl_len;
+		amt = pmcs_set_resid(pkt, amt, rl_len);
+
+		if (amt) {
+			if (xp->actv_cnt) {
+				xp->special_needed = 1;
+				xp->special_running = 0;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: @ line %d",
+				    __func__, __LINE__);
+				if (saq) {
+					pmcs_release_scratch(pwp);
+				}
+				return (-1);
+			}
+			SATAcopy(sp, rp, rl_len);
+		}
+		break;
+	}
+
+	case SCMD_REQUEST_SENSE:
+		/* Check for illegal bits */
+		if ((pkt->pkt_cdbp[1] & 0xfe) || pkt->pkt_cdbp[2] ||
+		    pkt->pkt_cdbp[3] || pkt->pkt_cdbp[5]) {
+			status = STATUS_CHECK;
+			asc = 0x24;	/* invalid field in cdb */
+			break;
+		}
+		rp[0] = 0xf0;
+		amt = 18;
+		amt = pmcs_set_resid(pkt, amt, pkt->pkt_cdbp[4]);
+		if (amt) {
+			if (xp->actv_cnt) {
+				xp->special_needed = 1;
+				xp->special_running = 0;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: @ line %d",
+				    __func__, __LINE__);
+				if (saq) {
+					pmcs_release_scratch(pwp);
+				}
+				return (-1);
+			}
+			SATAcopy(sp, rp, 18);
+		}
+		break;
+	case SCMD_START_STOP:
+		/* Check for illegal bits */
+		if ((pkt->pkt_cdbp[1] & 0xfe) || pkt->pkt_cdbp[2] ||
+		    (pkt->pkt_cdbp[3] & 0xf0) || (pkt->pkt_cdbp[4] & 0x08) ||
+		    pkt->pkt_cdbp[5]) {
+			status = STATUS_CHECK;
+			asc = 0x24;	/* invalid field in cdb */
+			break;
+		}
+		break;
+	case SCMD_SYNCHRONIZE_CACHE:
+		/* Check for illegal bits */
+		if ((pkt->pkt_cdbp[1] & 0xf8) || (pkt->pkt_cdbp[6] & 0xe0) ||
+		    pkt->pkt_cdbp[9]) {
+			status = STATUS_CHECK;
+			asc = 0x24;	/* invalid field in cdb */
+			break;
+		}
+		break;
+	case SCMD_TEST_UNIT_READY:
+		/* Check for illegal bits */
+		if (pkt->pkt_cdbp[1] || pkt->pkt_cdbp[2] || pkt->pkt_cdbp[3] ||
+		    pkt->pkt_cdbp[4] || pkt->pkt_cdbp[5]) {
+			status = STATUS_CHECK;
+			asc = 0x24;	/* invalid field in cdb */
+			break;
+		}
+		if (xp->ca) {
+			status = STATUS_CHECK;
+			key = 0x6;
+			asc = 0x28;
+			xp->ca = 0;
+		}
+		break;
+	default:
+		asc = 0x20;	/* invalid operation command code */
+		status = STATUS_CHECK;
+		break;
+	}
+	if (status != STATUS_GOOD) {
+		bzero(rp, 18);
+		rp[0] = 0xf0;
+		rp[2] = key;
+		rp[12] = asc;
+		rp[13] = ascq;
+		pmcs_latch_status(pwp, sp, status, rp, 18, pptr->path);
+	} else {
+		pmcs_latch_status(pwp, sp, status, NULL, 0, pptr->path);
+	}
+
+out:
+	STAILQ_REMOVE_HEAD(&xp->sq, cmd_next);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+	    "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
+	    __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
+	    pkt->pkt_state, pkt->pkt_resid, status);
+
+	if (saq) {
+		pmcs_release_scratch(pwp);
+	}
+
+	if (xp->draining) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: waking up drain waiters", __func__);
+		cv_signal(&pwp->drain_cv);
+	}
+
+	mutex_exit(&xp->statlock);
+	mutex_enter(&pwp->cq_lock);
+	STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+	PMCS_CQ_RUN_LOCKED(pwp);
+	mutex_exit(&pwp->cq_lock);
+	mutex_enter(&xp->statlock);
+	xp->special_running = 0;
+	return (0);
+}
+
+/*
+ * Run all special commands queued up for a SATA device.
+ * We're only called if the caller knows we have work to do.
+ *
+ * We can't run them if things are still active for the device,
+ * return saying we didn't run anything.
+ *
+ * When we finish, wake up anyone waiting for active commands
+ * to go to zero.
+ *
+ * Called with PHY lock and xp statlock held.
+ */
+int
+pmcs_run_sata_special(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
+{
+	while (!STAILQ_EMPTY(&xp->sq)) {
+		if (pmcs_sata_special_work(pwp, xp)) {
+			return (-1);
+		}
+	}
+	return (0);
+}
+
+/*
+ * Search for SATA special commands to run and run them.
+ * If we succeed in running the special command(s), kick
+ * the normal commands into operation again. Call completion
+ * for any commands that were completed while we were here.
+ *
+ * Called unlocked.
+ */
+void
+pmcs_sata_work(pmcs_hw_t *pwp)
+{
+	pmcs_xscsi_t *xp;
+	int spinagain = 0;
+	uint16_t target;
+
+	for (target = 0; target < pwp->max_dev; target++) {
+		xp = pwp->targets[target];
+		if ((xp == NULL) || (xp->phy == NULL)) {
+			continue;
+		}
+		pmcs_lock_phy(xp->phy);
+		mutex_enter(&xp->statlock);
+		if (STAILQ_EMPTY(&xp->sq)) {
+			mutex_exit(&xp->statlock);
+			pmcs_unlock_phy(xp->phy);
+			continue;
+		}
+		if (xp->actv_cnt) {
+			xp->special_needed = 1;
+			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
+			    "%s: deferring until drained", __func__);
+			spinagain++;
+		} else {
+			if (pmcs_run_sata_special(pwp, xp)) {
+				spinagain++;
+			}
+		}
+		mutex_exit(&xp->statlock);
+		pmcs_unlock_phy(xp->phy);
+	}
+
+	if (spinagain) {
+		SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
+	} else {
+		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
+	}
+
+	/*
+	 * Run completion on any commands ready for it.
+	 */
+	PMCS_CQ_RUN(pwp);
+}
+
+/*
+ * Called with PHY lock held and scratch acquired
+ */
+int
+pmcs_sata_identify(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	fis_t fis;
+	fis[0] = (IDENTIFY_DEVICE << 16) | (1 << 15) | FIS_REG_H2DEV;
+	fis[1] = 0;
+	fis[2] = 0;
+	fis[3] = 0;
+	fis[4] = 0;
+	return (pmcs_run_sata_cmd(pwp, pptr, fis, SATA_PROTOCOL_PIO,
+	    PMCIN_DATADIR_2_INI, sizeof (ata_identify_t)));
+}
+
+/*
+ * Called with PHY lock held and scratch held
+ */
+int
+pmcs_run_sata_cmd(pmcs_hw_t *pwp, pmcs_phy_t *pptr, fis_t fis, uint32_t mode,
+    uint32_t ddir, uint32_t dlen)
+{
+	struct pmcwork *pwrk;
+	uint32_t *ptr, msg[PMCS_MSG_SIZE];
+	uint32_t iq, htag;
+	int i, result = 0;
+
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
+	if (pwrk == NULL) {
+		return (ENOMEM);
+	}
+
+	msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE,
+	    PMCIN_SATA_HOST_IO_START));
+	htag = pwrk->htag;
+	pwrk->arg = msg;
+	pwrk->dtype = SATA;
+	msg[1] = LE_32(pwrk->htag);
+	msg[2] = LE_32(pptr->device_id);
+	msg[3] = LE_32(dlen);
+	msg[4] = LE_32(mode | ddir);
+	if (dlen) {
+		if (ddir == PMCIN_DATADIR_2_DEV) {
+			if (ddi_dma_sync(pwp->cip_handles, 0, 0,
+			    DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check "
+				    "failed at %s():%d", __func__, __LINE__);
+			}
+		}
+		msg[12] = LE_32(DWORD0(pwp->scratch_dma));
+		msg[13] = LE_32(DWORD1(pwp->scratch_dma));
+		msg[14] = LE_32(dlen);
+		msg[15] = 0;
+	} else {
+		msg[12] = 0;
+		msg[13] = 0;
+		msg[14] = 0;
+		msg[15] = 0;
+	}
+	for (i = 0; i < 5; i++) {
+		msg[5+i] = LE_32(fis[i]);
+	}
+	msg[10] = 0;
+	msg[11] = 0;
+	GET_IO_IQ_ENTRY(pwp, ptr, pptr->device_id, iq);
+	if (ptr == NULL) {
+		pmcs_pwork(pwp, pwrk);
+		return (ENOMEM);
+	}
+	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, iq);
+
+	pmcs_unlock_phy(pptr);
+	WAIT_FOR(pwrk, 1000, result);
+	pmcs_pwork(pwp, pwrk);
+	pmcs_lock_phy(pptr);
+
+	if (result) {
+		pmcs_timed_out(pwp, htag, __func__);
+		if (pmcs_abort(pwp, pptr, htag, 0, 1)) {
+			pptr->abort_pending = 1;
+			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
+		}
+		return (ETIMEDOUT);
+	}
+
+	if (LE_32(msg[2]) != PMCOUT_STATUS_OK) {
+		return (EIO);
+	}
+	if (LE_32(ptr[3]) != 0) {
+		size_t j, amt = LE_32(ptr[3]);
+		if (amt > sizeof (fis_t)) {
+			amt = sizeof (fis_t);
+		}
+		amt >>= 2;
+		for (j = 0; j < amt; j++) {
+			fis[j] = LE_32(msg[4 + j]);
+		}
+	}
+	if (dlen && ddir == PMCIN_DATADIR_2_INI) {
+		if (ddi_dma_sync(pwp->cip_handles, 0, 0,
+		    DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition check "
+			    "failed at %s():%d", __func__, __LINE__);
+		}
+	}
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_scsa.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,2983 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * SCSI (SCSA) midlayer interface for PMC drier.
+ */
+
+#include <sys/scsi/adapters/pmcs/pmcs.h>
+
+extern scsi_lun_t scsi_lun64_to_lun(scsi_lun64_t lun64);
+
+static int pmcs_scsa_tran_tgt_init(dev_info_t *, dev_info_t *,
+    scsi_hba_tran_t *, struct scsi_device *);
+static void pmcs_scsa_tran_tgt_free(dev_info_t *, dev_info_t *,
+    scsi_hba_tran_t *, struct scsi_device *);
+static int pmcs_scsa_start(struct scsi_address *, struct scsi_pkt *);
+static int pmcs_scsa_abort(struct scsi_address *, struct scsi_pkt *);
+static int pmcs_scsa_reset(struct scsi_address *, int);
+static int pmcs_scsi_reset_notify(struct scsi_address *, int,
+    void (*)(caddr_t), caddr_t);
+static int pmcs_scsa_getcap(struct scsi_address *, char *, int);
+static int pmcs_scsa_setcap(struct scsi_address *, char *, int, int);
+static int pmcs_scsa_setup_pkt(struct scsi_pkt *, int (*)(caddr_t), caddr_t);
+static void pmcs_scsa_teardown_pkt(struct scsi_pkt *);
+static int pmcs_smp_start(struct smp_pkt *);
+static int pmcs_smp_getcap(struct sas_addr *, char *);
+static int pmcs_smp_init(dev_info_t *, dev_info_t *, sas_hba_tran_t *,
+    smp_device_t *);
+static void pmcs_smp_free(dev_info_t *, dev_info_t *, sas_hba_tran_t *,
+    smp_device_t *);
+
+static int pmcs_scsi_quiesce(dev_info_t *);
+static int pmcs_scsi_unquiesce(dev_info_t *);
+
+static int pmcs_cap(struct scsi_address *, char *, int, int, int);
+static pmcs_xscsi_t *
+    pmcs_addr2xp(struct scsi_address *, uint64_t *, pmcs_cmd_t *);
+static int pmcs_SAS_run(pmcs_cmd_t *, pmcwork_t *);
+static void pmcs_SAS_done(pmcs_hw_t *, pmcwork_t *, uint32_t *);
+
+static int pmcs_SATA_run(pmcs_cmd_t *, pmcwork_t *);
+static void pmcs_SATA_done(pmcs_hw_t *, pmcwork_t *, uint32_t *);
+static uint8_t pmcs_SATA_rwparm(uint8_t *, uint32_t *, uint64_t *, uint64_t);
+
+static void pmcs_ioerror(pmcs_hw_t *, pmcs_dtype_t pmcs_dtype,
+    pmcwork_t *, uint32_t *);
+
+
+int
+pmcs_scsa_init(pmcs_hw_t *pwp, const ddi_dma_attr_t *ap)
+{
+	scsi_hba_tran_t *tran;
+	ddi_dma_attr_t pmcs_scsa_dattr;
+	int flags;
+
+	(void) memcpy(&pmcs_scsa_dattr, ap, sizeof (ddi_dma_attr_t));
+	pmcs_scsa_dattr.dma_attr_sgllen =
+	    ((PMCS_SGL_NCHUNKS - 1) * (PMCS_MAX_CHUNKS - 1)) + PMCS_SGL_NCHUNKS;
+	pmcs_scsa_dattr.dma_attr_flags = DDI_DMA_RELAXED_ORDERING;
+	pmcs_scsa_dattr.dma_attr_flags |= DDI_DMA_FLAGERR;
+
+	/*
+	 * Allocate a transport structure
+	 */
+	tran = scsi_hba_tran_alloc(pwp->dip, SCSI_HBA_CANSLEEP);
+	if (tran == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "scsi_hba_tran_alloc failed");
+		return (DDI_FAILURE);
+	}
+
+	tran->tran_hba_private		= pwp;
+	tran->tran_tgt_init		= pmcs_scsa_tran_tgt_init;
+	tran->tran_tgt_free		= pmcs_scsa_tran_tgt_free;
+	tran->tran_start		= pmcs_scsa_start;
+	tran->tran_abort		= pmcs_scsa_abort;
+	tran->tran_reset		= pmcs_scsa_reset;
+	tran->tran_reset_notify		= pmcs_scsi_reset_notify;
+	tran->tran_getcap		= pmcs_scsa_getcap;
+	tran->tran_setcap		= pmcs_scsa_setcap;
+	tran->tran_setup_pkt		= pmcs_scsa_setup_pkt;
+	tran->tran_teardown_pkt		= pmcs_scsa_teardown_pkt;
+	tran->tran_quiesce		= pmcs_scsi_quiesce;
+	tran->tran_unquiesce		= pmcs_scsi_unquiesce;
+	tran->tran_interconnect_type	= INTERCONNECT_SAS;
+	tran->tran_hba_len		= sizeof (pmcs_cmd_t);
+
+	/*
+	 * Attach this instance of the hba
+	 */
+
+	flags = SCSI_HBA_TRAN_SCB | SCSI_HBA_TRAN_CDB | SCSI_HBA_ADDR_COMPLEX |
+	    SCSI_HBA_TRAN_PHCI | SCSI_HBA_HBA;
+
+	if (scsi_hba_attach_setup(pwp->dip, &pmcs_scsa_dattr, tran, flags)) {
+		scsi_hba_tran_free(tran);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "scsi_hba_attach failed");
+		return (DDI_FAILURE);
+	}
+	pwp->tran = tran;
+
+	/*
+	 * Attach the SMP part of this hba
+	 */
+	pwp->smp_tran = sas_hba_tran_alloc(pwp->dip);
+	ASSERT(pwp->smp_tran != NULL);
+	pwp->smp_tran->tran_hba_private = pwp;
+	pwp->smp_tran->tran_smp_start = pmcs_smp_start;
+	pwp->smp_tran->tran_sas_getcap = pmcs_smp_getcap;
+	pwp->smp_tran->tran_smp_init = pmcs_smp_init;
+	pwp->smp_tran->tran_smp_free = pmcs_smp_free;
+
+	if (sas_hba_attach_setup(pwp->dip, pwp->smp_tran) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "sas_hba_attach failed");
+		sas_hba_tran_free(pwp->smp_tran);
+		pwp->smp_tran = NULL;
+		scsi_hba_tran_free(tran);
+		return (DDI_FAILURE);
+	}
+
+	return (DDI_SUCCESS);
+}
+
+/*
+ * SCSA entry points
+ */
+
+static int
+pmcs_scsa_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
+    scsi_hba_tran_t *tran, struct scsi_device *sd)
+{
+	pmcs_hw_t	*pwp = NULL;
+	int		rval;
+	char		*variant_prop = "sata";
+	char		*tgt_port = NULL, *ua = NULL;
+	pmcs_xscsi_t	*tgt = NULL;
+	pmcs_iport_t	*iport;
+	pmcs_lun_t	*lun = NULL;
+	pmcs_phy_t	*phyp = NULL;
+	uint64_t	lun_num;
+	boolean_t	got_scratch = B_FALSE;
+
+	/*
+	 * First, make sure we're an iport and get the pointer to the HBA
+	 * node's softstate
+	 */
+	if (scsi_hba_iport_unit_address(hba_dip) == NULL) {
+		pmcs_prt(TRAN2PMC(tran), PMCS_PRT_DEBUG_CONFIG,
+		    "%s: We don't enumerate devices on the HBA node", __func__);
+		goto tgt_init_fail;
+	}
+
+	pwp = ITRAN2PMC(tran);
+	iport = ITRAN2IPORT(tran);
+
+	/*
+	 * Get the target address
+	 */
+	rval = scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port);
+	if (rval != DDI_PROP_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Couldn't get target UA");
+		pwp = NULL;
+		goto tgt_init_fail;
+	}
+	pmcs_prt(pwp, PMCS_PRT_DEBUG3, "got tgt_port '%s'", tgt_port);
+
+	/*
+	 * Validate that this tran_tgt_init is for an active iport.
+	 */
+	if (iport->ua_state == UA_INACTIVE) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Got tran_tgt_init on inactive iport for '%s'",
+		    __func__, tgt_port);
+		pwp = NULL;
+		goto tgt_init_fail;
+	}
+
+	/*
+	 * Since we're going to wait for scratch, be sure to acquire it while
+	 * we're not holding any other locks
+	 */
+	(void) pmcs_acquire_scratch(pwp, B_TRUE);
+	got_scratch = B_TRUE;
+
+	mutex_enter(&pwp->lock);
+
+	/*
+	 * See if there's already a target softstate.  If not, allocate one.
+	 */
+	tgt = pmcs_get_target(iport, tgt_port);
+
+	if (tgt == NULL) {
+		goto tgt_init_fail;
+	}
+
+	phyp = tgt->phy;
+	if (!IS_ROOT_PHY(phyp)) {
+		pmcs_inc_phy_ref_count(phyp);
+	}
+	ASSERT(mutex_owned(&phyp->phy_lock));
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "tgt = 0x%p, dip = 0x%p",
+	    (void *)tgt, (void *)tgt_dip);
+
+	/*
+	 * Now get the full "w<WWN>,LUN" unit-address (including LU).
+	 */
+	ua = scsi_device_unit_address(sd);
+	if (ua == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "Couldn't get LU unit address");
+		goto tgt_init_fail;
+	}
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "got lun ua '%s'", ua);
+
+	lun_num = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_LUN64, SCSI_LUN64_ILLEGAL);
+	if (lun_num == SCSI_LUN64_ILLEGAL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "No LUN for tgt %p",
+		    (void *)tgt);
+		goto tgt_init_fail;
+	}
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: @%s tgt 0x%p phy 0x%p (%s)",
+	    __func__, ua, (void *)tgt, (void *)phyp, phyp->path);
+
+	mutex_enter(&tgt->statlock);
+	tgt->dtype = phyp->dtype;
+	if (tgt->dtype != SAS && tgt->dtype != SATA) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "PHY 0x%p went away?",
+		    (void *)phyp);
+		goto tgt_init_fail;
+	}
+
+	/* We don't support SATA devices at LUN > 0. */
+	if ((tgt->dtype == SATA) && (lun_num > 0)) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: No support for SATA devices at LUN > 0 "
+		    "(target = 0x%p)", __func__, (void *)tgt);
+		goto tgt_init_fail;
+	}
+
+	/*
+	 * Allocate LU soft state. We use ddi_soft_state_bystr_zalloc instead
+	 * of kmem_alloc because ddi_soft_state_bystr_zalloc allows us to
+	 * verify that the framework never tries to initialize two scsi_device
+	 * structures with the same unit-address at the same time.
+	 */
+	if (ddi_soft_state_bystr_zalloc(tgt->lun_sstate, ua) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+		    "Couldn't allocate LU soft state");
+		goto tgt_init_fail;
+	}
+
+	lun = ddi_soft_state_bystr_get(tgt->lun_sstate, ua);
+	if (lun == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2, "Couldn't get LU soft state");
+		goto tgt_init_fail;
+	}
+	scsi_device_hba_private_set(sd, lun);
+	lun->lun_num = lun_num;
+
+	/* convert the scsi_lun64_t value to SCSI standard form */
+	lun->scsi_lun = scsi_lun64_to_lun(lun_num);
+
+	ASSERT(strlen(ua) < (PMCS_MAX_UA_SIZE - 1));
+	bcopy(ua, lun->unit_address, strnlen(ua, PMCS_MAX_UA_SIZE - 1));
+
+	lun->target = tgt;
+
+	/*
+	 * If this is the first tran_tgt_init, add this target to our list
+	 */
+	if (tgt->target_num == PMCS_INVALID_TARGET_NUM) {
+		int target;
+		for (target = 0; target < pwp->max_dev; target++) {
+			if (pwp->targets[target] != NULL) {
+				continue;
+			}
+
+			pwp->targets[target] = tgt;
+			tgt->target_num = (uint16_t)target;
+			break;
+		}
+
+		if (target == pwp->max_dev) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "Target list full.");
+			goto tgt_init_fail;
+		}
+	}
+
+	tgt->dip = sd->sd_dev;
+
+	if (!pmcs_assign_device(pwp, tgt)) {
+		pmcs_release_scratch(pwp);
+		pwp->targets[tgt->target_num] = NULL;
+		tgt->target_num = PMCS_INVALID_TARGET_NUM;
+		tgt->phy = NULL;
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: pmcs_assign_device failed for target 0x%p",
+		    __func__, (void *)tgt);
+		goto tgt_init_fail;
+	}
+
+	pmcs_release_scratch(pwp);
+	tgt->ref_count++;
+
+	(void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_TARGET, (uint32_t)(tgt->target_num));
+
+	/* SM-HBA */
+	if (tgt->dtype == SATA) {
+		/* TCR in PSARC/1997/281 opinion */
+		(void) scsi_device_prop_update_string(sd,
+		    SCSI_DEVICE_PROP_PATH, "variant", variant_prop);
+	}
+
+	tgt->phy_addressable = PMCS_PHY_ADDRESSABLE(phyp);
+
+	if (tgt->phy_addressable) {
+		(void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH,
+		    SCSI_ADDR_PROP_SATA_PHY, phyp->phynum);
+	}
+
+	/* SM-HBA */
+	(void) pmcs_smhba_set_scsi_device_props(pwp, phyp, sd);
+
+	mutex_exit(&tgt->statlock);
+	pmcs_unlock_phy(phyp);
+	mutex_exit(&pwp->lock);
+	scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
+	return (DDI_SUCCESS);
+
+tgt_init_fail:
+	if (got_scratch) {
+		pmcs_release_scratch(pwp);
+	}
+	if (lun) {
+		ddi_soft_state_bystr_free(tgt->lun_sstate, ua);
+	}
+	if (phyp) {
+		mutex_exit(&tgt->statlock);
+		pmcs_unlock_phy(phyp);
+		/*
+		 * phyp's ref count was incremented in pmcs_new_tport.
+		 * We're failing configuration, we now need to decrement it.
+		 */
+		if (!IS_ROOT_PHY(phyp)) {
+			pmcs_dec_phy_ref_count(phyp);
+		}
+		phyp->target = NULL;
+	}
+	if (tgt && tgt->ref_count == 0) {
+		ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port);
+	}
+	if (pwp) {
+		mutex_exit(&pwp->lock);
+	}
+	if (tgt_port) {
+		scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
+	}
+	return (DDI_FAILURE);
+}
+
+static void
+pmcs_scsa_tran_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
+    scsi_hba_tran_t *tran, struct scsi_device *sd)
+{
+	_NOTE(ARGUNUSED(hba_dip, tgt_dip));
+	pmcs_hw_t	*pwp;
+	pmcs_lun_t	*lun;
+	pmcs_xscsi_t	*target;
+	char		*unit_address;
+	pmcs_phy_t	*phyp;
+
+	if (scsi_hba_iport_unit_address(hba_dip) == NULL) {
+		pwp = TRAN2PMC(tran);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: We don't enumerate devices on the HBA node", __func__);
+		return;
+	}
+
+	lun = (pmcs_lun_t *)scsi_device_hba_private_get(sd);
+
+	ASSERT((lun != NULL) && (lun->target != NULL));
+	ASSERT(lun->target->ref_count > 0);
+
+	target = lun->target;
+
+	unit_address = lun->unit_address;
+	ddi_soft_state_bystr_free(lun->target->lun_sstate, unit_address);
+
+	pwp = ITRAN2PMC(tran);
+	mutex_enter(&pwp->lock);
+	mutex_enter(&target->statlock);
+	ASSERT(target->phy);
+	phyp = target->phy;
+
+	/*
+	 * If this target still has a PHY pointer and that PHY's target pointer
+	 * has been cleared, then that PHY has been reaped. In that case, there
+	 * would be no need to decrement the reference count
+	 */
+	if (phyp && !IS_ROOT_PHY(phyp) && phyp->target) {
+		pmcs_dec_phy_ref_count(phyp);
+	}
+
+	if (--target->ref_count == 0) {
+		/*
+		 * Remove this target from our list.  The target soft
+		 * state will remain, and the device will remain registered
+		 * with the hardware unless/until we're told the device
+		 * physically went away.
+		 */
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: Free target 0x%p (vtgt %d)", __func__, (void *)target,
+		    target->target_num);
+		pwp->targets[target->target_num] = NULL;
+		target->target_num = PMCS_INVALID_TARGET_NUM;
+		/*
+		 * If the target still has a PHY pointer, break the linkage
+		 */
+		if (phyp) {
+			phyp->target = NULL;
+		}
+		target->phy = NULL;
+		pmcs_destroy_target(target);
+	} else {
+		mutex_exit(&target->statlock);
+	}
+
+	mutex_exit(&pwp->lock);
+}
+
+static int
+pmcs_scsa_start(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	pmcs_cmd_t *sp = PKT2CMD(pkt);
+	pmcs_hw_t *pwp = ADDR2PMC(ap);
+	pmcs_xscsi_t *xp;
+	boolean_t blocked;
+	uint32_t hba_state;
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s: pkt %p sd %p cdb0=0x%02x dl=%lu",
+	    __func__, (void *)pkt,
+	    (void *)scsi_address_device(&pkt->pkt_address),
+	    pkt->pkt_cdbp[0] & 0xff, pkt->pkt_dma_len);
+
+	if (pkt->pkt_flags & FLAG_NOINTR) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG3, "%s: nointr pkt", __func__);
+		return (TRAN_BADPKT);
+	}
+
+	sp->cmd_tag = 0;
+	pkt->pkt_state = pkt->pkt_statistics = 0;
+	pkt->pkt_reason = CMD_INCOMPLETE;
+
+	mutex_enter(&pwp->lock);
+	hba_state = pwp->state;
+	blocked = pwp->blocked;
+	mutex_exit(&pwp->lock);
+
+	if (hba_state != STATE_RUNNING) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: hba dead", __func__);
+		return (TRAN_FATAL_ERROR);
+	}
+
+	xp = pmcs_addr2xp(ap, NULL, sp);
+	if (xp == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+		    "%s: dropping due to null target", __func__);
+		goto dead_duck;
+	}
+	ASSERT(mutex_owned(&xp->statlock));
+
+	/*
+	 * First, check to see if we're dying or unassigned.
+	 */
+	if (xp->dying || xp->dev_gone) {
+		mutex_exit(&xp->statlock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG3,
+		    "%s: dropping due to dying/dead target 0x%p",
+		    __func__, (void *)xp);
+		goto dead_duck;
+	}
+
+	/*
+	 * If we're blocked (quiesced) just return.
+	 */
+	if (blocked) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: hba blocked", __func__);
+		mutex_exit(&xp->statlock);
+		mutex_enter(&xp->wqlock);
+		STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
+		mutex_exit(&xp->wqlock);
+		return (TRAN_ACCEPT);
+	}
+
+	/*
+	 * If we're draining or resetting, queue and return.
+	 */
+	if (xp->draining || xp->resetting || xp->recover_wait) {
+		mutex_exit(&xp->statlock);
+		mutex_enter(&xp->wqlock);
+		STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
+		mutex_exit(&xp->wqlock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG1,
+		    "%s: draining/resetting/recovering (cnt %u)",
+		    __func__, xp->actv_cnt);
+		/*
+		 * By the time we get here, draining or
+		 * resetting may have come and gone, not
+		 * yet noticing that we had put something
+		 * on the wait queue, so schedule a worker
+		 * to look at this later.
+		 */
+		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
+		return (TRAN_ACCEPT);
+	}
+	mutex_exit(&xp->statlock);
+
+	/*
+	 * Queue this command to the tail of the wait queue.
+	 * This keeps us getting commands out of order.
+	 */
+	mutex_enter(&xp->wqlock);
+	STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
+	mutex_exit(&xp->wqlock);
+
+	/*
+	 * Now run the queue for this device.
+	 */
+	(void) pmcs_scsa_wq_run_one(pwp, xp);
+
+	return (TRAN_ACCEPT);
+
+dead_duck:
+	pkt->pkt_state = STATE_GOT_BUS;
+	pkt->pkt_reason = CMD_DEV_GONE;
+	mutex_enter(&pwp->cq_lock);
+	STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+	PMCS_CQ_RUN_LOCKED(pwp);
+	mutex_exit(&pwp->cq_lock);
+	return (TRAN_ACCEPT);
+}
+
+static int
+pmcs_scsa_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
+{
+	pmcs_hw_t *pwp = ADDR2PMC(ap);
+	pmcs_cmd_t *sp = PKT2CMD(pkt);
+	pmcs_xscsi_t *xp = sp->cmd_target;
+	pmcs_phy_t *pptr;
+	uint32_t tag;
+	uint64_t lun;
+	pmcwork_t *pwrk;
+
+	mutex_enter(&pwp->lock);
+	if (pwp->state != STATE_RUNNING) {
+		mutex_exit(&pwp->lock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: hba dead", __func__);
+		return (0);
+	}
+	mutex_exit(&pwp->lock);
+
+	if (sp->cmd_lun) {
+		lun = sp->cmd_lun->lun_num;
+	} else {
+		lun = 0;
+	}
+	if (xp == NULL) {
+		return (0);
+	}
+
+	/*
+	 * See if we have a real work structure associated with this cmd.
+	 */
+	pwrk = pmcs_tag2wp(pwp, sp->cmd_tag);
+	if (pwrk && pwrk->arg == sp) {
+		tag = pwrk->htag;
+		pptr = pwrk->phy;
+		pwrk->timer = 0;	/* we don't time this here */
+		ASSERT(pwrk->state == PMCS_WORK_STATE_ONCHIP);
+		mutex_exit(&pwrk->lock);
+		pmcs_lock_phy(pptr);
+		if (pptr->dtype == SAS) {
+			if (pmcs_ssp_tmf(pwp, pptr, SAS_ABORT_TASK, tag, lun,
+			    NULL)) {
+				pptr->abort_pending = 1;
+				pmcs_unlock_phy(pptr);
+				SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
+				return (0);
+			}
+		} else {
+			/*
+			 * XXX: Was the command that was active an
+			 * NCQ I/O command?
+			 */
+			pptr->need_rl_ext = 1;
+			if (pmcs_sata_abort_ncq(pwp, pptr)) {
+				pptr->abort_pending = 1;
+				pmcs_unlock_phy(pptr);
+				SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
+				return (0);
+			}
+		}
+		pptr->abort_pending = 1;
+		pmcs_unlock_phy(pptr);
+		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
+		return (1);
+	}
+	if (pwrk) {
+		mutex_exit(&pwrk->lock);
+	}
+	/*
+	 * Okay, those weren't the droids we were looking for.
+	 * See if the command is on any of the wait queues.
+	 */
+	mutex_enter(&xp->wqlock);
+	sp = NULL;
+	STAILQ_FOREACH(sp, &xp->wq, cmd_next) {
+		if (sp == PKT2CMD(pkt)) {
+			STAILQ_REMOVE(&xp->wq, sp, pmcs_cmd, cmd_next);
+			break;
+		}
+	}
+	mutex_exit(&xp->wqlock);
+	if (sp) {
+		pkt->pkt_reason = CMD_ABORTED;
+		pkt->pkt_statistics |= STAT_ABORTED;
+		mutex_enter(&pwp->cq_lock);
+		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+		PMCS_CQ_RUN_LOCKED(pwp);
+		mutex_exit(&pwp->cq_lock);
+		return (1);
+	}
+	return (0);
+}
+
+/*
+ * SCSA reset functions
+ */
+static int
+pmcs_scsa_reset(struct scsi_address *ap, int level)
+{
+	pmcs_hw_t *pwp = ADDR2PMC(ap);
+	pmcs_phy_t *pptr;
+	pmcs_xscsi_t *xp;
+	uint64_t lun = (uint64_t)-1, *lp = NULL;
+	int rval;
+
+	mutex_enter(&pwp->lock);
+	if (pwp->state != STATE_RUNNING) {
+		mutex_exit(&pwp->lock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: hba dead", __func__);
+		return (0);
+	}
+	mutex_exit(&pwp->lock);
+
+	switch (level)  {
+	case RESET_ALL:
+		rval = 0;
+		break;
+	case RESET_LUN:
+		/*
+		 * Point lp at lun so that pmcs_addr2xp
+		 * will fill out the 64 bit lun number.
+		 */
+		lp = &lun;
+		/* FALLTHROUGH */
+	case RESET_TARGET:
+		xp = pmcs_addr2xp(ap, lp, NULL);
+		if (xp == NULL) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: no xp found for this scsi address", __func__);
+			return (0);
+		}
+
+		if (xp->dying || xp->dev_gone) {
+			mutex_exit(&xp->statlock);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: Target 0x%p has gone away", __func__,
+			    (void *)xp);
+			return (0);
+		}
+
+		/*
+		 * If we're already performing this action, or if device
+		 * state recovery is already running, just return failure.
+		 */
+		if (xp->resetting || xp->recover_wait) {
+			mutex_exit(&xp->statlock);
+			return (0);
+		}
+		xp->reset_wait = 0;
+		xp->reset_success = 0;
+		xp->resetting = 1;
+		pptr = xp->phy;
+		mutex_exit(&xp->statlock);
+
+		if (pmcs_reset_dev(pwp, pptr, lun)) {
+			rval = 0;
+		} else {
+			rval = 1;
+		}
+
+		mutex_enter(&xp->statlock);
+		if (rval == 1) {
+			xp->reset_success = 1;
+		}
+		if (xp->reset_wait) {
+			xp->reset_wait = 0;
+			cv_signal(&xp->reset_cv);
+		}
+		xp->resetting = 0;
+		mutex_exit(&xp->statlock);
+		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
+		break;
+	default:
+		rval = 0;
+		break;
+	}
+
+	return (rval);
+}
+
+static int
+pmcs_scsi_reset_notify(struct scsi_address *ap, int flag,
+    void (*callback)(caddr_t), caddr_t arg)
+{
+	pmcs_hw_t *pwp = ADDR2PMC(ap);
+	return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
+	    &pwp->lock, &pwp->reset_notify_listf));
+}
+
+
+static int
+pmcs_cap(struct scsi_address *ap, char *cap, int val, int tonly, int set)
+{
+	_NOTE(ARGUNUSED(val, tonly));
+	int cidx, rval = 0;
+	pmcs_xscsi_t *xp;
+
+	cidx = scsi_hba_lookup_capstr(cap);
+	if (cidx == -1) {
+		return (-1);
+	}
+
+	xp = pmcs_addr2xp(ap, NULL, NULL);
+	if (xp == NULL) {
+		return (-1);
+	}
+
+	switch (cidx) {
+	case SCSI_CAP_DMA_MAX:
+	case SCSI_CAP_INITIATOR_ID:
+		if (set == 0) {
+			rval = INT_MAX;	/* argh */
+		}
+		break;
+	case SCSI_CAP_DISCONNECT:
+	case SCSI_CAP_SYNCHRONOUS:
+	case SCSI_CAP_WIDE_XFER:
+	case SCSI_CAP_PARITY:
+	case SCSI_CAP_ARQ:
+	case SCSI_CAP_UNTAGGED_QING:
+		if (set == 0) {
+			rval = 1;
+		}
+		break;
+
+	case SCSI_CAP_TAGGED_QING:
+		rval = 1;
+		break;
+
+	case SCSI_CAP_MSG_OUT:
+	case SCSI_CAP_RESET_NOTIFICATION:
+	case SCSI_CAP_QFULL_RETRIES:
+	case SCSI_CAP_QFULL_RETRY_INTERVAL:
+		break;
+	case SCSI_CAP_SCSI_VERSION:
+		if (set == 0) {
+			rval = SCSI_VERSION_3;
+		}
+		break;
+	case SCSI_CAP_INTERCONNECT_TYPE:
+		if (set) {
+			break;
+		}
+		if (xp->phy_addressable) {
+			rval = INTERCONNECT_SATA;
+		} else {
+			rval = INTERCONNECT_SAS;
+		}
+		break;
+	case SCSI_CAP_CDB_LEN:
+		if (set == 0) {
+			rval = 16;
+		}
+		break;
+	case SCSI_CAP_LUN_RESET:
+		if (set) {
+			break;
+		}
+		if (xp->dtype == SATA) {
+			rval = 0;
+		} else {
+			rval = 1;
+		}
+		break;
+	default:
+		rval = -1;
+		break;
+	}
+	mutex_exit(&xp->statlock);
+	pmcs_prt(ADDR2PMC(ap), PMCS_PRT_DEBUG3,
+	    "%s: cap %s val %d set %d rval %d",
+	    __func__, cap, val, set, rval);
+	return (rval);
+}
+
+/*
+ * Returns with statlock held if the xp is found.
+ * Fills in pmcs_cmd_t with values if pmcs_cmd_t pointer non-NULL.
+ */
+static pmcs_xscsi_t *
+pmcs_addr2xp(struct scsi_address *ap, uint64_t *lp, pmcs_cmd_t *sp)
+{
+	pmcs_xscsi_t *xp;
+	pmcs_lun_t *lun = (pmcs_lun_t *)
+	    scsi_device_hba_private_get(scsi_address_device(ap));
+
+	if ((lun == NULL) || (lun->target == NULL)) {
+		return (NULL);
+	}
+	xp = lun->target;
+	mutex_enter(&xp->statlock);
+
+	if (xp->dying || xp->dev_gone || (xp->phy == NULL)) {
+		mutex_exit(&xp->statlock);
+		return (NULL);
+	}
+
+	if (sp != NULL) {
+		sp->cmd_target = xp;
+		sp->cmd_lun = lun;
+	}
+	if (lp) {
+		*lp = lun->lun_num;
+	}
+	return (xp);
+}
+
+static int
+pmcs_scsa_getcap(struct scsi_address *ap, char *cap, int whom)
+{
+	int r;
+	if (cap == NULL) {
+		return (-1);
+	}
+	r = pmcs_cap(ap, cap, 0, whom, 0);
+	return (r);
+}
+
+static int
+pmcs_scsa_setcap(struct scsi_address *ap, char *cap, int value, int whom)
+{
+	int r;
+	if (cap == NULL) {
+		return (-1);
+	}
+	r = pmcs_cap(ap, cap, value, whom, 1);
+	return (r);
+}
+
+static int
+pmcs_scsa_setup_pkt(struct scsi_pkt *pkt, int (*callback)(caddr_t),
+    caddr_t cbarg)
+{
+	_NOTE(ARGUNUSED(callback, cbarg));
+	pmcs_cmd_t *sp = pkt->pkt_ha_private;
+
+	bzero(sp, sizeof (pmcs_cmd_t));
+	sp->cmd_pkt = pkt;
+	return (0);
+}
+
+static void
+pmcs_scsa_teardown_pkt(struct scsi_pkt *pkt)
+{
+	pmcs_cmd_t *sp = pkt->pkt_ha_private;
+	sp->cmd_target = NULL;
+	sp->cmd_lun = NULL;
+}
+
+static int
+pmcs_smp_getcap(struct sas_addr *ap, char *cap)
+{
+	_NOTE(ARGUNUSED(ap));
+	int ckey = -1;
+	int ret = EINVAL;
+
+	ckey = sas_hba_lookup_capstr(cap);
+	if (ckey == -1)
+		return (EINVAL);
+
+	switch (ckey) {
+	case SAS_CAP_SMP_CRC:
+		ret = 0;
+		break;
+	default:
+		ret = EINVAL;
+		break;
+	}
+	return (ret);
+}
+
+static int
+pmcs_smp_start(struct smp_pkt *pktp)
+{
+	struct pmcwork *pwrk;
+	const uint_t rdoff = SAS_SMP_MAX_PAYLOAD;
+	uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status;
+	uint64_t wwn;
+	pmcs_hw_t *pwp = pktp->pkt_address->a_hba_tran->tran_hba_private;
+	pmcs_phy_t *pptr;
+	pmcs_xscsi_t *xp;
+	uint_t reqsz, rspsz, will_retry;
+	int result;
+
+	bcopy(pktp->pkt_address->a_wwn, &wwn, SAS_WWN_BYTE_SIZE);
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG1, "%s: starting for wwn 0x%" PRIx64,
+	    __func__, wwn);
+
+	will_retry = pktp->pkt_will_retry;
+
+	(void) pmcs_acquire_scratch(pwp, B_TRUE);
+	reqsz = pktp->pkt_reqsize;
+	if (reqsz > SAS_SMP_MAX_PAYLOAD) {
+		reqsz = SAS_SMP_MAX_PAYLOAD;
+	}
+	(void) memcpy(pwp->scratch, pktp->pkt_req, reqsz);
+
+	rspsz = pktp->pkt_rspsize;
+	if (rspsz > SAS_SMP_MAX_PAYLOAD) {
+		rspsz = SAS_SMP_MAX_PAYLOAD;
+	}
+
+	/*
+	 * The request size from the SMP driver always includes 4 bytes
+	 * for the CRC. The PMCS chip, however, doesn't want to see those
+	 * counts as part of the transfer size.
+	 */
+	reqsz -= 4;
+
+	pptr = pmcs_find_phy_by_wwn(pwp, wwn);
+	/* PHY is now locked */
+	if (pptr == NULL || pptr->dtype != EXPANDER) {
+		if (pptr) {
+			pmcs_unlock_phy(pptr);
+		}
+		pmcs_release_scratch(pwp);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: could not find phy",
+		    __func__);
+		pktp->pkt_reason = ENXIO;
+		return (DDI_FAILURE);
+	}
+
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
+	if (pwrk == NULL) {
+		pmcs_unlock_phy(pptr);
+		pmcs_release_scratch(pwp);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: could not get work structure", __func__);
+		pktp->pkt_reason = will_retry ? EAGAIN :EBUSY;
+		return (DDI_FAILURE);
+	}
+
+	pwrk->arg = msg;
+	pwrk->dtype = EXPANDER;
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (ptr == NULL) {
+		pmcs_pwork(pwp, pwrk);
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		pmcs_unlock_phy(pptr);
+		pmcs_release_scratch(pwp);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: could not get IQ entry",
+		    __func__);
+		pktp->pkt_reason = will_retry ? EAGAIN :EBUSY;
+		return (DDI_FAILURE);
+	}
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
+	msg[1] = LE_32(pwrk->htag);
+	msg[2] = LE_32(pptr->device_id);
+	msg[3] = LE_32(SMP_INDIRECT_RESPONSE | SMP_INDIRECT_REQUEST);
+	msg[8] = LE_32(DWORD0(pwp->scratch_dma));
+	msg[9] = LE_32(DWORD1(pwp->scratch_dma));
+	msg[10] = LE_32(reqsz);
+	msg[11] = 0;
+	msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
+	msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
+	msg[14] = LE_32(rspsz);
+	msg[15] = 0;
+
+	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	htag = pwrk->htag;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	pmcs_unlock_phy(pptr);
+	WAIT_FOR(pwrk, pktp->pkt_timeout * 1000, result);
+	pmcs_pwork(pwp, pwrk);
+	pmcs_lock_phy(pptr);
+
+	if (result) {
+		pmcs_timed_out(pwp, htag, __func__);
+		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: Unable to issue SMP ABORT for htag 0x%08x",
+			    __func__, htag);
+		} else {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: Issuing SMP ABORT for htag 0x%08x",
+			    __func__, htag);
+		}
+		pmcs_unlock_phy(pptr);
+		pmcs_release_scratch(pwp);
+		pktp->pkt_reason = ETIMEDOUT;
+		return (DDI_FAILURE);
+	}
+	status = LE_32(msg[2]);
+	if (status == PMCOUT_STATUS_OVERFLOW) {
+		status = PMCOUT_STATUS_OK;
+		pktp->pkt_reason = EOVERFLOW;
+	}
+	if (status != PMCOUT_STATUS_OK) {
+		const char *emsg = pmcs_status_str(status);
+		if (emsg == NULL) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "SMP operation failed (0x%x)", status);
+		} else {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "SMP operation failed (%s)", emsg);
+		}
+
+		if ((status == PMCOUT_STATUS_ERROR_HW_TIMEOUT) ||
+		    (status == PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT)) {
+			pktp->pkt_reason = will_retry ? EAGAIN : ETIMEDOUT;
+			result = DDI_FAILURE;
+		} else if (status ==
+		    PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS) {
+			xp = pptr->target;
+			if (xp == NULL) {
+				pktp->pkt_reason = EIO;
+				result = DDI_FAILURE;
+				goto out;
+			}
+			if (xp->dev_state !=
+			    PMCS_DEVICE_STATE_NON_OPERATIONAL) {
+				xp->dev_state =
+				    PMCS_DEVICE_STATE_NON_OPERATIONAL;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+				    "%s: Got _IT_NEXUS_LOSS SMP status. "
+				    "Tgt(0x%p) dev_state set to "
+				    "_NON_OPERATIONAL", __func__,
+				    (void *)xp);
+			}
+			/* ABORT any pending commands related to this device */
+			if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) != 0) {
+				pptr->abort_pending = 1;
+				pktp->pkt_reason = EIO;
+				result = DDI_FAILURE;
+			}
+		} else {
+			pktp->pkt_reason = will_retry ? EAGAIN : EIO;
+			result = DDI_FAILURE;
+		}
+	} else {
+		(void) memcpy(pktp->pkt_rsp,
+		    &((uint8_t *)pwp->scratch)[rdoff], rspsz);
+		if (pktp->pkt_reason == EOVERFLOW) {
+			result = DDI_FAILURE;
+		} else {
+			result = DDI_SUCCESS;
+		}
+	}
+out:
+	pmcs_unlock_phy(pptr);
+	pmcs_release_scratch(pwp);
+	return (result);
+}
+
+static int
+pmcs_smp_init(dev_info_t *self, dev_info_t *child,
+    sas_hba_tran_t *tran, smp_device_t *smp)
+{
+	_NOTE(ARGUNUSED(tran, smp));
+	pmcs_iport_t *iport;
+	pmcs_hw_t *pwp;
+	pmcs_xscsi_t *tgt;
+	pmcs_phy_t *phy, *pphy;
+	uint64_t wwn;
+	char *addr, *tgt_port;
+	int ua_form = 1;
+
+	iport = ddi_get_soft_state(pmcs_iport_softstate,
+	    ddi_get_instance(self));
+	ASSERT(iport);
+	if (iport == NULL)
+		return (DDI_FAILURE);
+	pwp = iport->pwp;
+	ASSERT(pwp);
+	if (pwp == NULL)
+		return (DDI_FAILURE);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s", __func__,
+	    ddi_get_name(child));
+
+	/* Get "target-port" prop from devinfo node */
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Failed to lookup prop ("
+		    SCSI_ADDR_PROP_TARGET_PORT")", __func__);
+		/* Dont fail _smp_init() because we couldnt get/set a prop */
+		return (DDI_SUCCESS);
+	}
+
+	/*
+	 * Validate that this tran_tgt_init is for an active iport.
+	 */
+	if (iport->ua_state == UA_INACTIVE) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Init on inactive iport for '%s'",
+		    __func__, tgt_port);
+		ddi_prop_free(tgt_port);
+		return (DDI_FAILURE);
+	}
+
+	mutex_enter(&pwp->lock);
+
+	/* Retrieve softstate using unit-address */
+	tgt = pmcs_get_target(iport, tgt_port);
+	if (tgt == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: tgt softstate not found",
+		    __func__);
+		ddi_prop_free(tgt_port);
+		mutex_exit(&pwp->lock);
+		return (DDI_FAILURE);
+	}
+
+	phy = tgt->phy;
+	ASSERT(mutex_owned(&phy->phy_lock));
+
+	if (IS_ROOT_PHY(phy)) {
+		/* Expander attached to HBA - don't ref_count it */
+		wwn = pwp->sas_wwns[0];
+	} else {
+		pmcs_inc_phy_ref_count(phy);
+
+		/*
+		 * Parent (in topology) is also an expander
+		 * Now that we've increased the ref count on phy, it's OK
+		 * to drop the lock so we can acquire the parent's lock.
+		 */
+
+		pphy = phy->parent;
+		pmcs_unlock_phy(phy);
+		pmcs_lock_phy(pphy);
+		wwn = pmcs_barray2wwn(pphy->sas_address);
+		pmcs_unlock_phy(pphy);
+		pmcs_lock_phy(phy);
+	}
+
+	/*
+	 * If this is the 1st smp_init, add this to our list.
+	 */
+	if (tgt->target_num == PMCS_INVALID_TARGET_NUM) {
+		int target;
+		for (target = 0; target < pwp->max_dev; target++) {
+			if (pwp->targets[target] != NULL) {
+				continue;
+			}
+
+			pwp->targets[target] = tgt;
+			tgt->target_num = (uint16_t)target;
+			tgt->assigned = 1;
+			tgt->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
+			break;
+		}
+
+		if (target == pwp->max_dev) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "Target list full.");
+			goto smp_init_fail;
+		}
+	}
+
+	if (!pmcs_assign_device(pwp, tgt)) {
+		pwp->targets[tgt->target_num] = NULL;
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: pmcs_assign_device failed for target 0x%p",
+		    __func__, (void *)tgt);
+		goto smp_init_fail;
+	}
+
+	pmcs_unlock_phy(phy);
+	mutex_exit(&pwp->lock);
+
+	tgt->ref_count++;
+	tgt->dtype = phy->dtype;
+
+	addr = scsi_wwn_to_wwnstr(wwn, ua_form, NULL);
+	/* XXX: Update smp devinfo node using ndi_xxx */
+	if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
+	    SCSI_ADDR_PROP_ATTACHED_PORT, addr) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Failed to set prop ("
+		    SCSI_ADDR_PROP_ATTACHED_PORT")", __func__);
+	}
+	(void) scsi_free_wwnstr(addr);
+	ddi_prop_free(tgt_port);
+	return (DDI_SUCCESS);
+
+smp_init_fail:
+	tgt->phy = NULL;
+	tgt->target_num = PMCS_INVALID_TARGET_NUM;
+	phy->target = NULL;
+	if (!IS_ROOT_PHY(phy)) {
+		pmcs_dec_phy_ref_count(phy);
+	}
+	pmcs_unlock_phy(phy);
+	mutex_exit(&pwp->lock);
+	ddi_soft_state_bystr_free(iport->tgt_sstate, tgt->unit_address);
+	ddi_prop_free(tgt_port);
+	return (DDI_FAILURE);
+}
+
+static void
+pmcs_smp_free(dev_info_t *self, dev_info_t *child,
+    sas_hba_tran_t *tran, smp_device_t *smp)
+{
+	_NOTE(ARGUNUSED(tran, smp));
+	pmcs_iport_t *iport;
+	pmcs_hw_t *pwp;
+	pmcs_xscsi_t *tgt;
+	char *tgt_port;
+
+	iport = ddi_get_soft_state(pmcs_iport_softstate,
+	    ddi_get_instance(self));
+	ASSERT(iport);
+	if (iport == NULL)
+		return;
+
+	pwp = iport->pwp;
+	if (pwp == NULL)
+		return;
+	ASSERT(pwp);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s", __func__,
+	    ddi_get_name(child));
+
+	/* Get "target-port" prop from devinfo node */
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Failed to lookup prop ("
+		    SCSI_ADDR_PROP_TARGET_PORT")", __func__);
+		return;
+	}
+	/* Retrieve softstate using unit-address */
+	tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port);
+	ddi_prop_free(tgt_port);
+
+	if (tgt == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: tgt softstate not found",
+		    __func__);
+		return;
+	}
+
+	mutex_enter(&pwp->lock);
+	mutex_enter(&tgt->statlock);
+	if (tgt->phy) {
+		if (!IS_ROOT_PHY(tgt->phy)) {
+			pmcs_dec_phy_ref_count(tgt->phy);
+		}
+	}
+
+	if (--tgt->ref_count == 0) {
+		/*
+		 * Remove this target from our list. The softstate
+		 * will remain, and the device will remain registered
+		 * with the hardware unless/until we're told that the
+		 * device physically went away.
+		 */
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "Removing target 0x%p (vtgt %d) from target list",
+		    (void *)tgt, tgt->target_num);
+		pwp->targets[tgt->target_num] = NULL;
+		tgt->target_num = PMCS_INVALID_TARGET_NUM;
+		tgt->phy->target = NULL;
+		tgt->phy = NULL;
+	}
+
+	mutex_exit(&tgt->statlock);
+	mutex_exit(&pwp->lock);
+}
+
+static int
+pmcs_scsi_quiesce(dev_info_t *dip)
+{
+	pmcs_hw_t *pwp;
+	int totactive = -1;
+	pmcs_xscsi_t *xp;
+	uint16_t target;
+
+	if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip)))
+		return (0);		/* iport */
+
+	pwp  = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip));
+	if (pwp == NULL) {
+		return (-1);
+	}
+	mutex_enter(&pwp->lock);
+	if (pwp->state != STATE_RUNNING) {
+		mutex_exit(&pwp->lock);
+		return (-1);
+	}
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s called", __func__);
+	pwp->blocked = 1;
+	while (totactive) {
+		totactive = 0;
+		for (target = 0; target < pwp->max_dev; target++) {
+			xp = pwp->targets[target];
+			if (xp == NULL) {
+				continue;
+			}
+			mutex_enter(&xp->statlock);
+			if (xp->actv_cnt) {
+				totactive += xp->actv_cnt;
+				xp->draining = 1;
+			}
+			mutex_exit(&xp->statlock);
+		}
+		if (totactive) {
+			cv_wait(&pwp->drain_cv, &pwp->lock);
+		}
+		/*
+		 * The pwp->blocked may have been reset. e.g a SCSI bus reset
+		 */
+		pwp->blocked = 1;
+	}
+
+	for (target = 0; target < pwp->max_dev; target++) {
+		xp = pwp->targets[target];
+		if (xp == NULL) {
+			continue;
+		}
+		mutex_enter(&xp->statlock);
+		xp->draining = 0;
+		mutex_exit(&xp->statlock);
+	}
+
+	mutex_exit(&pwp->lock);
+	if (totactive == 0) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s drain complete", __func__);
+	}
+	return (0);
+}
+
+static int
+pmcs_scsi_unquiesce(dev_info_t *dip)
+{
+	pmcs_hw_t *pwp;
+
+	if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip)))
+		return (0);		/* iport */
+
+	pwp  = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip));
+	if (pwp == NULL) {
+		return (-1);
+	}
+	mutex_enter(&pwp->lock);
+	if (pwp->state != STATE_RUNNING) {
+		mutex_exit(&pwp->lock);
+		return (-1);
+	}
+	pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s called", __func__);
+	pwp->blocked = 0;
+	mutex_exit(&pwp->lock);
+
+	/*
+	 * Run all pending commands.
+	 */
+	pmcs_scsa_wq_run(pwp);
+
+	/*
+	 * Complete all completed commands.
+	 * This also unlocks us.
+	 */
+	PMCS_CQ_RUN(pwp);
+	return (0);
+}
+
+/*
+ * Start commands for a particular device
+ * If the actual start of a command fails, return B_FALSE.  Any other result
+ * is a B_TRUE return.
+ */
+boolean_t
+pmcs_scsa_wq_run_one(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
+{
+	pmcs_cmd_t *sp;
+	pmcs_phy_t *phyp;
+	pmcwork_t *pwrk;
+	boolean_t run_one, blocked;
+	int rval;
+
+	/*
+	 * First, check to see if we're blocked or resource limited
+	 */
+	mutex_enter(&pwp->lock);
+	blocked = pwp->blocked;
+	/*
+	 * If resource_limited is set, we're resource constrained and
+	 * we will run only one work request for this target.
+	 */
+	run_one = pwp->resource_limited;
+	mutex_exit(&pwp->lock);
+
+	if (blocked) {
+		/* Queues will get restarted when we get unblocked */
+		return (B_TRUE);
+	}
+
+	/*
+	 * Might as well verify the queue is not empty before moving on
+	 */
+	mutex_enter(&xp->wqlock);
+	if (STAILQ_EMPTY(&xp->wq)) {
+		mutex_exit(&xp->wqlock);
+		return (B_TRUE);
+	}
+	mutex_exit(&xp->wqlock);
+
+	/*
+	 * If we're draining or resetting, just reschedule work queue and bail.
+	 */
+	mutex_enter(&xp->statlock);
+	if (xp->draining || xp->resetting || xp->special_running ||
+	    xp->special_needed) {
+		mutex_exit(&xp->statlock);
+		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
+		return (B_TRUE);
+	}
+
+	/*
+	 * Next, check to see if the target is alive
+	 */
+	if (xp->dying || xp->dev_gone) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Flushing wait queue for dying/dead tgt 0x%p", __func__,
+		    (void *)xp);
+		pmcs_flush_target_queues(pwp, xp, PMCS_TGT_WAIT_QUEUE);
+		mutex_exit(&xp->statlock);
+		return (B_TRUE);
+	}
+
+	/*
+	 * Increment the PHY's ref_count now so we know it won't go away
+	 * after we drop the target lock.  Drop it before returning.  If the
+	 * PHY dies, the commands we attempt to send will fail, but at least
+	 * we know we have a real PHY pointer.
+	 */
+	phyp = xp->phy;
+	pmcs_inc_phy_ref_count(phyp);
+	mutex_exit(&xp->statlock);
+
+	mutex_enter(&xp->wqlock);
+	while ((sp = STAILQ_FIRST(&xp->wq)) != NULL) {
+		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_CBACK, phyp);
+		if (pwrk == NULL) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: out of work structures", __func__);
+			SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
+			break;
+		}
+		STAILQ_REMOVE_HEAD(&xp->wq, cmd_next);
+		mutex_exit(&xp->wqlock);
+
+		pwrk->xp = xp;
+		pwrk->arg = sp;
+		sp->cmd_tag = pwrk->htag;
+		pwrk->timer = US2WT(CMD2PKT(sp)->pkt_time * 1000000);
+		if (pwrk->timer == 0) {
+			pwrk->timer = US2WT(1000000);
+		}
+
+		pwrk->dtype = xp->dtype;
+
+		if (xp->dtype == SAS) {
+			pwrk->ptr = (void *) pmcs_SAS_done;
+			if ((rval = pmcs_SAS_run(sp, pwrk)) != 0) {
+				sp->cmd_tag = NULL;
+				pmcs_dec_phy_ref_count(phyp);
+				pmcs_pwork(pwp, pwrk);
+				SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
+				if (rval == PMCS_WQ_RUN_FAIL_RES) {
+					return (B_FALSE);
+				} else {
+					return (B_TRUE);
+				}
+			}
+		} else {
+			ASSERT(xp->dtype == SATA);
+			pwrk->ptr = (void *) pmcs_SATA_done;
+			if ((rval = pmcs_SATA_run(sp, pwrk)) != 0) {
+				sp->cmd_tag = NULL;
+				pmcs_dec_phy_ref_count(phyp);
+				pmcs_pwork(pwp, pwrk);
+				SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
+				if (rval == PMCS_WQ_RUN_FAIL_RES) {
+					return (B_FALSE);
+				} else {
+					return (B_TRUE);
+				}
+			}
+		}
+
+		if (run_one) {
+			goto wq_out;
+		}
+		mutex_enter(&xp->wqlock);
+	}
+
+	mutex_exit(&xp->wqlock);
+
+wq_out:
+	pmcs_dec_phy_ref_count(phyp);
+	return (B_TRUE);
+}
+
+/*
+ * Start commands for all devices.
+ */
+void
+pmcs_scsa_wq_run(pmcs_hw_t *pwp)
+{
+	pmcs_xscsi_t *xp;
+	uint16_t target_start, target;
+	boolean_t	rval = B_TRUE;
+
+	mutex_enter(&pwp->lock);
+	target_start = pwp->last_wq_dev;
+	target = target_start;
+
+	do {
+		xp = pwp->targets[target];
+		if (xp == NULL) {
+			if (++target == pwp->max_dev) {
+				target = 0;
+			}
+			continue;
+		}
+
+		mutex_exit(&pwp->lock);
+		rval = pmcs_scsa_wq_run_one(pwp, xp);
+		if (rval == B_FALSE) {
+			mutex_enter(&pwp->lock);
+			break;
+		}
+		mutex_enter(&pwp->lock);
+		if (++target == pwp->max_dev) {
+			target = 0;
+		}
+	} while (target != target_start);
+
+	if (rval) {
+		pwp->resource_limited = 0; /* Not resource-constrained */
+	} else {
+		pwp->resource_limited = 1; /* Give others a chance */
+	}
+
+	pwp->last_wq_dev = target;
+	mutex_exit(&pwp->lock);
+}
+
+/*
+ * Pull the completion queue, drop the lock and complete all elements.
+ */
+
+void
+pmcs_scsa_cq_run(void *arg)
+{
+	pmcs_cq_thr_info_t *cqti = (pmcs_cq_thr_info_t *)arg;
+	pmcs_hw_t *pwp = cqti->cq_pwp;
+	pmcs_cmd_t *sp, *nxt;
+	struct scsi_pkt *pkt;
+	pmcs_iocomp_cb_t *ioccb, *ioccb_next;
+	pmcs_cb_t callback;
+	uint32_t niodone;
+
+	DTRACE_PROBE1(pmcs__scsa__cq__run__start, pmcs_cq_thr_info_t *, cqti);
+
+	mutex_enter(&pwp->cq_lock);
+
+	while (!pwp->cq_info.cq_stop) {
+		/*
+		 * First, check the I/O completion callback queue.
+		 */
+
+		ioccb = pwp->iocomp_cb_head;
+		pwp->iocomp_cb_head = NULL;
+		pwp->iocomp_cb_tail = NULL;
+		mutex_exit(&pwp->cq_lock);
+
+		niodone = 0;
+
+		while (ioccb) {
+			niodone++;
+			/*
+			 * Grab the lock on the work structure. The callback
+			 * routine is responsible for clearing it.
+			 */
+			mutex_enter(&ioccb->pwrk->lock);
+			ioccb_next = ioccb->next;
+			callback = (pmcs_cb_t)ioccb->pwrk->ptr;
+			(*callback)(pwp, ioccb->pwrk,
+			    (uint32_t *)((void *)ioccb->iomb));
+			kmem_cache_free(pwp->iocomp_cb_cache, ioccb);
+			ioccb = ioccb_next;
+		}
+
+		/*
+		 * Next, run the completion queue
+		 */
+
+		mutex_enter(&pwp->cq_lock);
+		sp = STAILQ_FIRST(&pwp->cq);
+		STAILQ_INIT(&pwp->cq);
+		mutex_exit(&pwp->cq_lock);
+
+		DTRACE_PROBE1(pmcs__scsa__cq__run__start__loop,
+		    pmcs_cq_thr_info_t *, cqti);
+
+		if (sp && pmcs_check_acc_dma_handle(pwp)) {
+			ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED);
+		}
+
+		while (sp) {
+			nxt = STAILQ_NEXT(sp, cmd_next);
+			pkt = CMD2PKT(sp);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG3,
+			    "%s: calling completion on %p for tgt %p", __func__,
+			    (void *)sp, (void *)sp->cmd_target);
+			scsi_hba_pkt_comp(pkt);
+			sp = nxt;
+		}
+
+		DTRACE_PROBE1(pmcs__scsa__cq__run__end__loop,
+		    pmcs_cq_thr_info_t *, cqti);
+
+		mutex_enter(&cqti->cq_thr_lock);
+		cv_wait(&cqti->cq_cv, &cqti->cq_thr_lock);
+		mutex_exit(&cqti->cq_thr_lock);
+
+		mutex_enter(&pwp->cq_lock);
+	}
+
+	mutex_exit(&pwp->cq_lock);
+	DTRACE_PROBE1(pmcs__scsa__cq__run__stop, pmcs_cq_thr_info_t *, cqti);
+	thread_exit();
+}
+
+/*
+ * Run a SAS command.  Called with pwrk->lock held, returns unlocked.
+ */
+static int
+pmcs_SAS_run(pmcs_cmd_t *sp, pmcwork_t *pwrk)
+{
+	pmcs_hw_t *pwp = CMD2PMC(sp);
+	struct scsi_pkt *pkt = CMD2PKT(sp);
+	pmcs_xscsi_t *xp = pwrk->xp;
+	uint32_t iq, *ptr;
+	sas_ssp_cmd_iu_t sc;
+
+	mutex_enter(&xp->statlock);
+	if (xp->dying || !xp->assigned) {
+		mutex_exit(&xp->statlock);
+		return (PMCS_WQ_RUN_FAIL_OTHER);
+	}
+	if ((xp->actv_cnt >= xp->qdepth) || xp->recover_wait) {
+		mutex_exit(&xp->statlock);
+		mutex_enter(&xp->wqlock);
+		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
+		mutex_exit(&xp->wqlock);
+		return (PMCS_WQ_RUN_FAIL_OTHER);
+	}
+	GET_IO_IQ_ENTRY(pwp, ptr, pwrk->phy->device_id, iq);
+	if (ptr == NULL) {
+		mutex_exit(&xp->statlock);
+		/*
+		 * This is a temporary failure not likely to unblocked by
+		 * commands completing as the test for scheduling the
+		 * restart of work is a per-device test.
+		 */
+		mutex_enter(&xp->wqlock);
+		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
+		mutex_exit(&xp->wqlock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Failed to get IO IQ entry for tgt %d",
+		    __func__, xp->target_num);
+		return (PMCS_WQ_RUN_FAIL_RES);
+
+	}
+
+	ptr[0] =
+	    LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SSP_INI_IO_START));
+	ptr[1] = LE_32(pwrk->htag);
+	ptr[2] = LE_32(pwrk->phy->device_id);
+	ptr[3] = LE_32(pkt->pkt_dma_len);
+	if (ptr[3]) {
+		ASSERT(pkt->pkt_numcookies);
+		if (pkt->pkt_dma_flags & DDI_DMA_READ) {
+			ptr[4] = LE_32(PMCIN_DATADIR_2_INI);
+		} else {
+			ptr[4] = LE_32(PMCIN_DATADIR_2_DEV);
+		}
+		if (pmcs_dma_load(pwp, sp, ptr)) {
+			mutex_exit(&pwp->iqp_lock[iq]);
+			mutex_exit(&xp->statlock);
+			mutex_enter(&xp->wqlock);
+			if (STAILQ_EMPTY(&xp->wq)) {
+				STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
+				mutex_exit(&xp->wqlock);
+			} else {
+				mutex_exit(&xp->wqlock);
+				CMD2PKT(sp)->pkt_scbp[0] = STATUS_QFULL;
+				CMD2PKT(sp)->pkt_reason = CMD_CMPLT;
+				CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS |
+				    STATE_GOT_TARGET | STATE_SENT_CMD |
+				    STATE_GOT_STATUS;
+				mutex_enter(&pwp->cq_lock);
+				STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+				mutex_exit(&pwp->cq_lock);
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "%s: Failed to dma_load for tgt %d (QF)",
+				    __func__, xp->target_num);
+			}
+			return (PMCS_WQ_RUN_FAIL_RES);
+		}
+	} else {
+		ptr[4] = LE_32(PMCIN_DATADIR_NONE);
+		CLEAN_MESSAGE(ptr, 12);
+	}
+	xp->actv_cnt++;
+	if (xp->actv_cnt > xp->maxdepth) {
+		xp->maxdepth = xp->actv_cnt;
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s: max depth now %u",
+		    pwrk->phy->path, xp->maxdepth);
+	}
+	mutex_exit(&xp->statlock);
+
+
+#ifdef	DEBUG
+	/*
+	 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
+	 * event when this goes out on the wire.
+	 */
+	ptr[4] |= PMCIN_MESSAGE_REPORT;
+#endif
+	/*
+	 * Fill in the SSP IU
+	 */
+
+	bzero(&sc, sizeof (sas_ssp_cmd_iu_t));
+	bcopy((uint8_t *)&sp->cmd_lun->scsi_lun, sc.lun, sizeof (scsi_lun_t));
+
+	switch (pkt->pkt_flags & FLAG_TAGMASK) {
+	case FLAG_HTAG:
+		sc.task_attribute = SAS_CMD_TASK_ATTR_HEAD;
+		break;
+	case FLAG_OTAG:
+		sc.task_attribute = SAS_CMD_TASK_ATTR_ORDERED;
+		break;
+	case FLAG_STAG:
+	default:
+		sc.task_attribute = SAS_CMD_TASK_ATTR_SIMPLE;
+		break;
+	}
+	(void) memcpy(sc.cdb, pkt->pkt_cdbp,
+	    min(SCSA_CDBLEN(sp), sizeof (sc.cdb)));
+	(void) memcpy(&ptr[5], &sc, sizeof (sas_ssp_cmd_iu_t));
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	mutex_exit(&pwrk->lock);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+	    "%s: giving pkt %p (tag %x) to the hardware", __func__,
+	    (void *)pkt, pwrk->htag);
+#ifdef DEBUG
+	pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SAS INI Message", ptr);
+#endif
+	mutex_enter(&xp->aqlock);
+	STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next);
+	mutex_exit(&xp->aqlock);
+	INC_IQ_ENTRY(pwp, iq);
+
+	/*
+	 * If we just submitted the last command queued from device state
+	 * recovery, clear the wq_recovery_tail pointer.
+	 */
+	mutex_enter(&xp->wqlock);
+	if (xp->wq_recovery_tail == sp) {
+		xp->wq_recovery_tail = NULL;
+	}
+	mutex_exit(&xp->wqlock);
+
+	return (PMCS_WQ_RUN_SUCCESS);
+}
+
+/*
+ * Complete a SAS command
+ *
+ * Called with pwrk lock held.
+ * The free of pwrk releases the lock.
+ */
+
+static void
+pmcs_SAS_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg)
+{
+	pmcs_cmd_t *sp = pwrk->arg;
+	pmcs_phy_t *pptr = pwrk->phy;
+	pmcs_xscsi_t *xp = pwrk->xp;
+	struct scsi_pkt *pkt = CMD2PKT(sp);
+	int dead;
+	uint32_t sts;
+	boolean_t aborted = B_FALSE;
+	boolean_t do_ds_recovery = B_FALSE;
+
+	ASSERT(xp != NULL);
+	ASSERT(sp != NULL);
+	ASSERT(pptr != NULL);
+
+	DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int,
+	    (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start,
+	    hrtime_t, gethrtime());
+
+	dead = pwrk->dead;
+
+	if (msg) {
+		sts = LE_32(msg[2]);
+	} else {
+		sts = 0;
+	}
+
+	if (dead != 0) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: dead cmd tag 0x%x for %s",
+		    __func__, pwrk->htag, pptr->path);
+		goto out;
+	}
+
+	if (sts == PMCOUT_STATUS_ABORTED) {
+		aborted = B_TRUE;
+	}
+
+	if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: cmd 0x%p (tag 0x%x) timed out for %s",
+		    __func__, (void *)sp, pwrk->htag, pptr->path);
+		do_ds_recovery = B_TRUE;
+		goto out;
+	}
+
+	/*
+	 * If the status isn't okay but not underflow,
+	 * step to the side and parse the (possible) error.
+	 */
+#ifdef DEBUG
+	if (msg) {
+		pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg);
+	}
+#endif
+	if (!msg) {
+		goto out;
+	}
+
+	switch (sts) {
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+	case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
+	case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: PHY %s requires device state recovery (status=%d)",
+		    __func__, pptr->path, sts);
+		do_ds_recovery = B_TRUE;
+		break;
+	case PMCOUT_STATUS_UNDERFLOW:
+		(void) pmcs_set_resid(pkt, pkt->pkt_dma_len, LE_32(msg[3]));
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW,
+		    "%s: underflow %u for cdb 0x%x",
+		    __func__, LE_32(msg[3]), pkt->pkt_cdbp[0] & 0xff);
+		sts = PMCOUT_STATUS_OK;
+		msg[3] = 0;
+		break;
+	case PMCOUT_STATUS_OK:
+		pkt->pkt_resid = 0;
+		break;
+	}
+
+	if (sts != PMCOUT_STATUS_OK) {
+		pmcs_ioerror(pwp, SAS, pwrk, msg);
+	} else {
+		if (msg[3]) {
+			uint8_t local[PMCS_QENTRY_SIZE << 1], *xd;
+			sas_ssp_rsp_iu_t *rptr = (void *)local;
+			const int lim =
+			    (PMCS_QENTRY_SIZE << 1) - SAS_RSP_HDR_SIZE;
+			static const uint8_t ssp_rsp_evec[] = {
+				0x58, 0x61, 0x56, 0x72, 0x00
+			};
+
+			/*
+			 * Transform the the first part of the response
+			 * to host canonical form. This gives us enough
+			 * information to figure out what to do with the
+			 * rest (which remains unchanged in the incoming
+			 * message which can be up to two queue entries
+			 * in length).
+			 */
+			pmcs_endian_transform(pwp, local, &msg[5],
+			    ssp_rsp_evec);
+			xd = (uint8_t *)(&msg[5]);
+			xd += SAS_RSP_HDR_SIZE;
+
+			if (rptr->datapres == SAS_RSP_DATAPRES_RESPONSE_DATA) {
+				if (rptr->response_data_length != 4) {
+					pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
+					    "Bad SAS RESPONSE DATA LENGTH",
+					    msg);
+					pkt->pkt_reason = CMD_TRAN_ERR;
+					goto out;
+				}
+				(void) memcpy(&sts, xd, sizeof (uint32_t));
+				sts = BE_32(sts);
+				/*
+				 * The only response code we should legally get
+				 * here is an INVALID FRAME response code.
+				 */
+				if (sts == SAS_RSP_INVALID_FRAME) {
+					pmcs_prt(pwp, PMCS_PRT_DEBUG,
+					    "%s: pkt %p tgt %u path %s "
+					    "completed: INVALID FRAME response",
+					    __func__, (void *)pkt,
+					    xp->target_num, pptr->path);
+				} else {
+					pmcs_prt(pwp, PMCS_PRT_DEBUG,
+					    "%s: pkt %p tgt %u path %s "
+					    "completed: illegal response 0x%x",
+					    __func__, (void *)pkt,
+					    xp->target_num, pptr->path, sts);
+				}
+				pkt->pkt_reason = CMD_TRAN_ERR;
+				goto out;
+			}
+			if (rptr->datapres == SAS_RSP_DATAPRES_SENSE_DATA) {
+				uint32_t slen;
+				slen = rptr->sense_data_length;
+				if (slen > lim) {
+					slen = lim;
+				}
+				pmcs_latch_status(pwp, sp, rptr->status, xd,
+				    slen, pptr->path);
+			} else if (rptr->datapres == SAS_RSP_DATAPRES_NO_DATA) {
+				/*
+				 * This is the case for a plain SCSI status.
+				 */
+				pmcs_latch_status(pwp, sp, rptr->status, NULL,
+				    0, pptr->path);
+			} else {
+				pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
+				    "illegal SAS response", msg);
+				pkt->pkt_reason = CMD_TRAN_ERR;
+				goto out;
+			}
+		} else {
+			pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
+			    pptr->path);
+		}
+		if (pkt->pkt_dma_len) {
+			pkt->pkt_state |= STATE_XFERRED_DATA;
+		}
+	}
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+	    "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
+	    __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
+	    pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]);
+
+	if (pwrk->state == PMCS_WORK_STATE_ABORTED) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
+		    __func__, (void *)pkt, pptr->path, (void *)pwrk);
+		aborted = B_TRUE;
+	}
+
+out:
+	pmcs_pwork(pwp, pwrk);
+	pmcs_dma_unload(pwp, sp);
+
+	mutex_enter(&xp->statlock);
+	if (xp->dev_gone) {
+		mutex_exit(&xp->statlock);
+		mutex_enter(&pwp->cq_lock);
+		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+		mutex_exit(&pwp->cq_lock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+		    "%s: Completing command for dead target 0x%p", __func__,
+		    (void *)xp);
+		return;
+	}
+
+	ASSERT(xp->actv_cnt > 0);
+	if (--(xp->actv_cnt) == 0) {
+		if (xp->draining) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
+			    "%s: waking up drain waiters", __func__);
+			cv_signal(&pwp->drain_cv);
+		}
+	}
+	mutex_exit(&xp->statlock);
+	if (dead == 0) {
+#ifdef	DEBUG
+		pmcs_cmd_t *wp;
+		mutex_enter(&xp->aqlock);
+		STAILQ_FOREACH(wp, &xp->aq, cmd_next) {
+			if (wp == sp) {
+				break;
+			}
+		}
+		ASSERT(wp != NULL);
+#else
+		mutex_enter(&xp->aqlock);
+#endif
+		STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
+		if (aborted) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: Aborted cmd for tgt 0x%p, signaling waiters",
+			    __func__, (void *)xp);
+			cv_signal(&xp->abort_cv);
+		}
+		mutex_exit(&xp->aqlock);
+	}
+
+	/*
+	 * If do_ds_recovery is set, we need to initiate device state
+	 * recovery.  In this case, we put this I/O back on the head of
+	 * the wait queue to run again after recovery is complete
+	 */
+	if (do_ds_recovery) {
+		mutex_enter(&xp->statlock);
+		pmcs_start_dev_state_recovery(xp, pptr);
+		mutex_exit(&xp->statlock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG1, "%s: Putting cmd 0x%p back on "
+		    "wq during recovery for tgt 0x%p", __func__, (void *)sp,
+		    (void *)xp);
+		mutex_enter(&xp->wqlock);
+		if (xp->wq_recovery_tail == NULL) {
+			STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
+		} else {
+			/*
+			 * If there are other I/Os waiting at the head due to
+			 * device state recovery, add this one in the right spot
+			 * to maintain proper order.
+			 */
+			STAILQ_INSERT_AFTER(&xp->wq, xp->wq_recovery_tail, sp,
+			    cmd_next);
+		}
+		xp->wq_recovery_tail = sp;
+		mutex_exit(&xp->wqlock);
+	} else {
+		/*
+		 * If we're not initiating device state recovery and this
+		 * command was not "dead", put it on the completion queue
+		 */
+		if (!dead) {
+			mutex_enter(&pwp->cq_lock);
+			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+			mutex_exit(&pwp->cq_lock);
+		}
+	}
+}
+
+/*
+ * Run a SATA command (normal reads and writes),
+ * or block and schedule a SATL interpretation
+ * of the command.
+ *
+ * Called with pwrk lock held, returns unlocked.
+ */
+
+static int
+pmcs_SATA_run(pmcs_cmd_t *sp, pmcwork_t *pwrk)
+{
+	pmcs_hw_t *pwp = CMD2PMC(sp);
+	struct scsi_pkt *pkt = CMD2PKT(sp);
+	pmcs_xscsi_t *xp;
+	uint8_t cdb_base, asc, tag;
+	uint32_t *ptr, iq, nblk, i, mtype;
+	fis_t fis;
+	size_t amt;
+	uint64_t lba;
+
+	xp = pwrk->xp;
+
+	/*
+	 * First, see if this is just a plain read/write command.
+	 * If not, we have to queue it up for processing, block
+	 * any additional commands from coming in, and wake up
+	 * the thread that will process this command.
+	 */
+	cdb_base = pkt->pkt_cdbp[0] & 0x1f;
+	if (cdb_base != SCMD_READ && cdb_base != SCMD_WRITE) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG1, "%s: special SATA cmd %p",
+		    __func__, (void *)sp);
+
+		ASSERT(xp->phy != NULL);
+		pmcs_pwork(pwp, pwrk);
+		pmcs_lock_phy(xp->phy);
+		mutex_enter(&xp->statlock);
+		xp->special_needed = 1; /* Set the special_needed flag */
+		STAILQ_INSERT_TAIL(&xp->sq, sp, cmd_next);
+		if (pmcs_run_sata_special(pwp, xp)) {
+			SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
+		}
+		mutex_exit(&xp->statlock);
+		pmcs_unlock_phy(xp->phy);
+
+		return (PMCS_WQ_RUN_SUCCESS);
+	}
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s: regular cmd", __func__);
+
+	mutex_enter(&xp->statlock);
+	if (xp->dying || !xp->assigned) {
+		mutex_exit(&xp->statlock);
+		return (PMCS_WQ_RUN_FAIL_OTHER);
+	}
+	if (xp->special_running || xp->special_needed || xp->recover_wait) {
+		mutex_exit(&xp->statlock);
+		mutex_enter(&xp->wqlock);
+		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
+		mutex_exit(&xp->wqlock);
+		/*
+		 * By the time we get here the special
+		 * commands running or waiting to be run
+		 * may have come and gone, so kick our
+		 * worker to run the waiting queues
+		 * just in case.
+		 */
+		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
+		return (PMCS_WQ_RUN_FAIL_OTHER);
+	}
+	lba = xp->capacity;
+	mutex_exit(&xp->statlock);
+
+	/*
+	 * Extract data length and lba parameters out of the command. The
+	 * function pmcs_SATA_rwparm returns a non-zero ASC value if the CDB
+	 * values are considered illegal.
+	 */
+	asc = pmcs_SATA_rwparm(pkt->pkt_cdbp, &nblk, &lba, lba);
+	if (asc) {
+		uint8_t sns[18];
+		bzero(sns, sizeof (sns));
+		sns[0] = 0xf0;
+		sns[2] = 0x5;
+		sns[12] = asc;
+		pmcs_latch_status(pwp, sp, STATUS_CHECK, sns, sizeof (sns),
+		    pwrk->phy->path);
+		pmcs_pwork(pwp, pwrk);
+		mutex_enter(&pwp->cq_lock);
+		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+		PMCS_CQ_RUN_LOCKED(pwp);
+		mutex_exit(&pwp->cq_lock);
+		return (PMCS_WQ_RUN_SUCCESS);
+	}
+
+	/*
+	 * If the command decodes as not moving any data, complete it here.
+	 */
+	amt = nblk;
+	amt <<= 9;
+	amt = pmcs_set_resid(pkt, amt, nblk << 9);
+	if (amt == 0) {
+		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
+		    pwrk->phy->path);
+		pmcs_pwork(pwp, pwrk);
+		mutex_enter(&pwp->cq_lock);
+		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+		PMCS_CQ_RUN_LOCKED(pwp);
+		mutex_exit(&pwp->cq_lock);
+		return (PMCS_WQ_RUN_SUCCESS);
+	}
+
+	/*
+	 * Get an inbound queue entry for this I/O
+	 */
+	GET_IO_IQ_ENTRY(pwp, ptr, xp->phy->device_id, iq);
+	if (ptr == NULL) {
+		/*
+		 * This is a temporary failure not likely to unblocked by
+		 * commands completing as the test for scheduling the
+		 * restart of work is a per-device test.
+		 */
+		mutex_enter(&xp->wqlock);
+		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
+		mutex_exit(&xp->wqlock);
+		pmcs_dma_unload(pwp, sp);
+		SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Failed to get IO IQ entry for tgt %d",
+		    __func__, xp->target_num);
+		return (PMCS_WQ_RUN_FAIL_RES);
+	}
+
+	/*
+	 * Get a tag.  At this point, hold statlock until the tagmap is
+	 * updated (just prior to sending the cmd to the hardware).
+	 */
+	mutex_enter(&xp->statlock);
+	for (tag = 0; tag < xp->qdepth; tag++) {
+		if ((xp->tagmap & (1 << tag)) == 0) {
+			break;
+		}
+	}
+
+	if (tag == xp->qdepth) {
+		mutex_exit(&xp->statlock);
+		mutex_exit(&pwp->iqp_lock[iq]);
+		mutex_enter(&xp->wqlock);
+		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
+		mutex_exit(&xp->wqlock);
+		return (PMCS_WQ_RUN_FAIL_OTHER);
+	}
+
+	sp->cmd_satltag = (uint8_t)tag;
+
+	/*
+	 * Set up the command
+	 */
+	bzero(fis, sizeof (fis));
+	ptr[0] =
+	    LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SATA_HOST_IO_START));
+	ptr[1] = LE_32(pwrk->htag);
+	ptr[2] = LE_32(pwrk->phy->device_id);
+	ptr[3] = LE_32(amt);
+
+	if (xp->ncq) {
+		mtype = SATA_PROTOCOL_FPDMA | (tag << 16);
+		fis[0] = ((nblk & 0xff) << 24) | (C_BIT << 8) | FIS_REG_H2DEV;
+		if (cdb_base == SCMD_READ) {
+			fis[0] |= (READ_FPDMA_QUEUED << 16);
+		} else {
+			fis[0] |= (WRITE_FPDMA_QUEUED << 16);
+		}
+		fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff);
+		fis[2] = ((nblk & 0xff00) << 16) | ((lba >> 24) & 0xffffff);
+		fis[3] = tag << 3;
+	} else {
+		int op;
+		fis[0] = (C_BIT << 8) | FIS_REG_H2DEV;
+		if (xp->pio) {
+			mtype = SATA_PROTOCOL_PIO;
+			if (cdb_base == SCMD_READ) {
+				op = READ_SECTORS_EXT;
+			} else {
+				op = WRITE_SECTORS_EXT;
+			}
+		} else {
+			mtype = SATA_PROTOCOL_DMA;
+			if (cdb_base == SCMD_READ) {
+				op = READ_DMA_EXT;
+			} else {
+				op = WRITE_DMA_EXT;
+			}
+		}
+		fis[0] |= (op << 16);
+		fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff);
+		fis[2] = (lba >> 24) & 0xffffff;
+		fis[3] = nblk;
+	}
+
+	if (cdb_base == SCMD_READ) {
+		ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_INI);
+	} else {
+		ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_DEV);
+	}
+#ifdef	DEBUG
+	/*
+	 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
+	 * event when this goes out on the wire.
+	 */
+	ptr[4] |= PMCIN_MESSAGE_REPORT;
+#endif
+	for (i = 0; i < (sizeof (fis_t))/(sizeof (uint32_t)); i++) {
+		ptr[i+5] = LE_32(fis[i]);
+	}
+	if (pmcs_dma_load(pwp, sp, ptr)) {
+		mutex_exit(&xp->statlock);
+		mutex_exit(&pwp->iqp_lock[iq]);
+		mutex_enter(&xp->wqlock);
+		STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
+		mutex_exit(&xp->wqlock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Failed to dma_load for tgt %d",
+		    __func__, xp->target_num);
+		return (PMCS_WQ_RUN_FAIL_RES);
+
+	}
+
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	mutex_exit(&pwrk->lock);
+	xp->tagmap |= (1 << tag);
+	xp->actv_cnt++;
+	if (xp->actv_cnt > xp->maxdepth) {
+		xp->maxdepth = xp->actv_cnt;
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s: max depth now %u",
+		    pwrk->phy->path, xp->maxdepth);
+	}
+	mutex_exit(&xp->statlock);
+	mutex_enter(&xp->aqlock);
+	STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next);
+	mutex_exit(&xp->aqlock);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s: giving pkt %p to hardware",
+	    __func__, (void *)pkt);
+#ifdef DEBUG
+	pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SATA INI Message", ptr);
+#endif
+	INC_IQ_ENTRY(pwp, iq);
+
+	return (PMCS_WQ_RUN_SUCCESS);
+}
+
+/*
+ * Complete a SATA command.  Called with pwrk lock held.
+ */
+void
+pmcs_SATA_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg)
+{
+	pmcs_cmd_t *sp = pwrk->arg;
+	struct scsi_pkt *pkt = CMD2PKT(sp);
+	pmcs_phy_t *pptr = pwrk->phy;
+	int dead;
+	uint32_t sts;
+	pmcs_xscsi_t *xp;
+	boolean_t aborted = B_FALSE;
+
+	xp = pwrk->xp;
+	ASSERT(xp != NULL);
+
+	DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int,
+	    (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start,
+	    hrtime_t, gethrtime());
+
+	dead = pwrk->dead;
+
+	if (msg) {
+		sts = LE_32(msg[2]);
+	} else {
+		sts = 0;
+	}
+
+	if (dead != 0) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: dead cmd tag 0x%x for %s",
+		    __func__, pwrk->htag, pptr->path);
+		goto out;
+	}
+	if ((pwrk->state == PMCS_WORK_STATE_TIMED_OUT) &&
+	    (sts != PMCOUT_STATUS_ABORTED)) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: cmd 0x%p (tag 0x%x) timed out for %s",
+		    __func__, (void *)sp, pwrk->htag, pptr->path);
+		CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD;
+		/* pkt_reason already set to CMD_TIMEOUT */
+		ASSERT(CMD2PKT(sp)->pkt_reason == CMD_TIMEOUT);
+		CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
+		    STATE_SENT_CMD;
+		CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
+		goto out;
+	}
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s: pkt %p tgt %u done",
+	    __func__, (void *)pkt, xp->target_num);
+
+	/*
+	 * If the status isn't okay but not underflow,
+	 * step to the side and parse the (possible) error.
+	 */
+#ifdef DEBUG
+	if (msg) {
+		pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg);
+	}
+#endif
+	if (!msg) {
+		goto out;
+	}
+
+	/*
+	 * If the status isn't okay or we got a FIS response of some kind,
+	 * step to the side and parse the (possible) error.
+	 */
+	if ((sts != PMCOUT_STATUS_OK) || (LE_32(msg[3]) != 0)) {
+		if (sts == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) {
+			mutex_exit(&pwrk->lock);
+			pmcs_lock_phy(pptr);
+			mutex_enter(&xp->statlock);
+			if ((xp->resetting == 0) && (xp->reset_success != 0) &&
+			    (xp->reset_wait == 0)) {
+				mutex_exit(&xp->statlock);
+				if (pmcs_reset_phy(pwp, pptr,
+				    PMCS_PHYOP_LINK_RESET) != 0) {
+					pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: PHY "
+					    "(%s) Local Control/Link Reset "
+					    "FAILED as part of error recovery",
+					    __func__, pptr->path);
+				}
+				mutex_enter(&xp->statlock);
+			}
+			mutex_exit(&xp->statlock);
+			pmcs_unlock_phy(pptr);
+			mutex_enter(&pwrk->lock);
+		}
+		pmcs_ioerror(pwp, SATA, pwrk, msg);
+	} else {
+		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
+		    pwrk->phy->path);
+		pkt->pkt_state |= STATE_XFERRED_DATA;
+		pkt->pkt_resid = 0;
+	}
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+	    "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
+	    __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
+	    pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]);
+
+	if (pwrk->state == PMCS_WORK_STATE_ABORTED) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
+		    __func__, (void *)pkt, pptr->path, (void *)pwrk);
+		aborted = B_TRUE;
+	}
+
+out:
+	pmcs_pwork(pwp, pwrk);
+	pmcs_dma_unload(pwp, sp);
+
+	mutex_enter(&xp->statlock);
+	xp->tagmap &= ~(1 << sp->cmd_satltag);
+
+	if (xp->dev_gone) {
+		mutex_exit(&xp->statlock);
+		mutex_enter(&pwp->cq_lock);
+		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+		mutex_exit(&pwp->cq_lock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+		    "%s: Completing command for dead target 0x%p", __func__,
+		    (void *)xp);
+		return;
+	}
+
+	ASSERT(xp->actv_cnt > 0);
+	if (--(xp->actv_cnt) == 0) {
+		if (xp->draining) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
+			    "%s: waking up drain waiters", __func__);
+			cv_signal(&pwp->drain_cv);
+		} else if (xp->special_needed) {
+			SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
+		}
+	}
+	mutex_exit(&xp->statlock);
+
+	if (dead == 0) {
+#ifdef	DEBUG
+		pmcs_cmd_t *wp;
+		mutex_enter(&xp->aqlock);
+		STAILQ_FOREACH(wp, &xp->aq, cmd_next) {
+			if (wp == sp) {
+				break;
+			}
+		}
+		ASSERT(wp != NULL);
+#else
+		mutex_enter(&xp->aqlock);
+#endif
+		STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
+		if (aborted) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: Aborted cmd for tgt 0x%p, signaling waiters",
+			    __func__, (void *)xp);
+			cv_signal(&xp->abort_cv);
+		}
+		mutex_exit(&xp->aqlock);
+		mutex_enter(&pwp->cq_lock);
+		STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+		mutex_exit(&pwp->cq_lock);
+	}
+}
+
+static uint8_t
+pmcs_SATA_rwparm(uint8_t *cdb, uint32_t *xfr, uint64_t *lba, uint64_t lbamax)
+{
+	uint8_t asc = 0;
+	switch (cdb[0]) {
+	case SCMD_READ_G5:
+	case SCMD_WRITE_G5:
+		*xfr =
+		    (((uint32_t)cdb[10]) <<  24) |
+		    (((uint32_t)cdb[11]) <<  16) |
+		    (((uint32_t)cdb[12]) <<   8) |
+		    ((uint32_t)cdb[13]);
+		*lba =
+		    (((uint64_t)cdb[2]) << 56) |
+		    (((uint64_t)cdb[3]) << 48) |
+		    (((uint64_t)cdb[4]) << 40) |
+		    (((uint64_t)cdb[5]) << 32) |
+		    (((uint64_t)cdb[6]) << 24) |
+		    (((uint64_t)cdb[7]) << 16) |
+		    (((uint64_t)cdb[8]) <<  8) |
+		    ((uint64_t)cdb[9]);
+		/* Check for illegal bits */
+		if (cdb[15]) {
+			asc = 0x24;	/* invalid field in cdb */
+		}
+		break;
+	case SCMD_READ_G4:
+	case SCMD_WRITE_G4:
+		*xfr =
+		    (((uint32_t)cdb[6]) <<  16) |
+		    (((uint32_t)cdb[7]) <<   8) |
+		    ((uint32_t)cdb[8]);
+		*lba =
+		    (((uint32_t)cdb[2]) << 24) |
+		    (((uint32_t)cdb[3]) << 16) |
+		    (((uint32_t)cdb[4]) <<  8) |
+		    ((uint32_t)cdb[5]);
+		/* Check for illegal bits */
+		if (cdb[11]) {
+			asc = 0x24;	/* invalid field in cdb */
+		}
+		break;
+	case SCMD_READ_G1:
+	case SCMD_WRITE_G1:
+		*xfr = (((uint32_t)cdb[7]) <<  8) | ((uint32_t)cdb[8]);
+		*lba =
+		    (((uint32_t)cdb[2]) << 24) |
+		    (((uint32_t)cdb[3]) << 16) |
+		    (((uint32_t)cdb[4]) <<  8) |
+		    ((uint32_t)cdb[5]);
+		/* Check for illegal bits */
+		if (cdb[9]) {
+			asc = 0x24;	/* invalid field in cdb */
+		}
+		break;
+	case SCMD_READ:
+	case SCMD_WRITE:
+		*xfr = cdb[4];
+		if (*xfr == 0) {
+			*xfr = 256;
+		}
+		*lba =
+		    (((uint32_t)cdb[1] & 0x1f) << 16) |
+		    (((uint32_t)cdb[2]) << 8) |
+		    ((uint32_t)cdb[3]);
+		/* Check for illegal bits */
+		if (cdb[5]) {
+			asc = 0x24;	/* invalid field in cdb */
+		}
+		break;
+	}
+
+	if (asc == 0) {
+		if ((*lba + *xfr) > lbamax) {
+			asc = 0x21;	/* logical block out of range */
+		}
+	}
+	return (asc);
+}
+
+/*
+ * Called with pwrk lock held.
+ */
+static void
+pmcs_ioerror(pmcs_hw_t *pwp, pmcs_dtype_t t, pmcwork_t *pwrk, uint32_t *w)
+{
+	static uint8_t por[] = {
+	    0xf0, 0x0, 0x6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28
+	};
+	static uint8_t parity[] = {
+	    0xf0, 0x0, 0xb, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x47, 5
+	};
+	const char *msg;
+	char buf[20];
+	pmcs_cmd_t *sp = pwrk->arg;
+	pmcs_phy_t *phyp = pwrk->phy;
+	struct scsi_pkt *pkt = CMD2PKT(sp);
+	uint32_t status;
+	uint32_t resid;
+
+	ASSERT(w != NULL);
+	status = LE_32(w[2]);
+	resid = LE_32(w[3]);
+
+	msg = pmcs_status_str(status);
+	if (msg == NULL) {
+		(void) snprintf(buf, sizeof (buf), "Error 0x%x", status);
+		msg = buf;
+	}
+
+	if (status != PMCOUT_STATUS_OK) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+		    "%s: device %s tag 0x%x status %s @ %llu", __func__,
+		    phyp->path, pwrk->htag, msg,
+		    (unsigned long long)gethrtime());
+	}
+
+	pkt->pkt_reason = CMD_CMPLT;		/* default reason */
+
+	switch (status) {
+	case PMCOUT_STATUS_OK:
+		if (t == SATA) {
+			int i;
+			fis_t fis;
+			for (i = 0; i < sizeof (fis) / sizeof (fis[0]); i++) {
+				fis[i] = LE_32(w[4+i]);
+			}
+			if ((fis[0] & 0xff) != FIS_REG_D2H) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "unexpected fis code 0x%x", fis[0] & 0xff);
+			} else {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "FIS ERROR");
+				pmcs_fis_dump(pwp, fis);
+			}
+			pkt->pkt_reason = CMD_TRAN_ERR;
+			break;
+		}
+		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path);
+		break;
+
+	case PMCOUT_STATUS_ABORTED:
+		/*
+		 * Command successfully aborted.
+		 */
+		if (phyp->dead) {
+			pkt->pkt_reason = CMD_DEV_GONE;
+			pkt->pkt_state = STATE_GOT_BUS;
+		} else if (pwrk->ssp_event != 0) {
+			pkt->pkt_reason = CMD_TRAN_ERR;
+			pkt->pkt_state = STATE_GOT_BUS;
+		} else if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) {
+			pkt->pkt_reason = CMD_TIMEOUT;
+			pkt->pkt_statistics |= STAT_TIMEOUT;
+			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
+			    STATE_SENT_CMD;
+		} else {
+			pkt->pkt_reason = CMD_ABORTED;
+			pkt->pkt_statistics |= STAT_ABORTED;
+			pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
+			    STATE_SENT_CMD;
+		}
+
+		/*
+		 * PMCS_WORK_STATE_TIMED_OUT doesn't need to be preserved past
+		 * this point, so go ahead and mark it as aborted.
+		 */
+		pwrk->state = PMCS_WORK_STATE_ABORTED;
+		break;
+
+	case PMCOUT_STATUS_UNDERFLOW:
+		/*
+		 * This will only get called for SATA
+		 */
+		pkt->pkt_resid = resid;
+		if (pkt->pkt_dma_len < pkt->pkt_resid) {
+			(void) pmcs_set_resid(pkt, pkt->pkt_dma_len, resid);
+		}
+		pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path);
+		break;
+
+	case PMCOUT_STATUS_NO_DEVICE:
+	case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT:
+		pkt->pkt_reason = CMD_DEV_GONE;
+		break;
+
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
+		/*
+		 * Need to do rediscovery. We probably have
+		 * the wrong device (disk swap), so kill
+		 * this one.
+		 */
+	case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_EROOR:
+		/*
+		 * Need to do rediscovery.
+		 */
+		if (!phyp->dead) {
+			mutex_exit(&pwrk->lock);
+			pmcs_lock_phy(pwrk->phy);
+			pmcs_kill_changed(pwp, pwrk->phy, 0);
+			pmcs_unlock_phy(pwrk->phy);
+			mutex_enter(&pwrk->lock);
+			pkt->pkt_reason = CMD_INCOMPLETE;
+			pkt->pkt_state = STATE_GOT_BUS;
+		} else {
+			pkt->pkt_reason = CMD_DEV_GONE;
+		}
+		break;
+
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+	case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
+	case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
+		/* cmd is pending on the target */
+	case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH:
+	case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE:
+		/* transitory - commands sent while in NCQ failure mode */
+	case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE:
+		/* NCQ failure */
+	case PMCOUT_STATUS_IO_PORT_IN_RESET:
+	case PMCOUT_STATUS_XFER_ERR_BREAK:
+	case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
+		pkt->pkt_reason = CMD_INCOMPLETE;
+		pkt->pkt_state = STATE_GOT_BUS;
+		break;
+
+	case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
+		pmcs_latch_status(pwp, sp, STATUS_BUSY, NULL, 0, phyp->path);
+		break;
+
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
+		/* synthesize a RESERVATION CONFLICT */
+		pmcs_latch_status(pwp, sp, STATUS_RESERVATION_CONFLICT, NULL,
+		    0, phyp->path);
+		break;
+
+	case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST:
+		/* synthesize a power-on/reset */
+		pmcs_latch_status(pwp, sp, STATUS_CHECK, por, sizeof (por),
+		    phyp->path);
+		break;
+
+	case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE:
+	case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN:
+	case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED:
+	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
+	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK:
+	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK:
+		/* synthesize a PARITY ERROR */
+		pmcs_latch_status(pwp, sp, STATUS_CHECK, parity,
+		    sizeof (parity), phyp->path);
+		break;
+
+	case PMCOUT_STATUS_IO_XFER_ERROR_DMA:
+	case PMCOUT_STATUS_IO_NOT_VALID:
+	case PMCOUT_STATUS_PROG_ERROR:
+	case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED:
+	case PMCOUT_STATUS_XFER_ERROR_SATA: /* non-NCQ failure */
+	default:
+		pkt->pkt_reason = CMD_TRAN_ERR;
+		break;
+	}
+}
+
+/*
+ * Latch up SCSI status
+ */
+
+void
+pmcs_latch_status(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint8_t status,
+    uint8_t *snsp, size_t snslen, char *path)
+{
+	static const char c1[] =
+	    "%s: Status Byte 0x%02x for CDB0=0x%02x (%02x %02x %02x) "
+	    "HTAG 0x%x @ %llu";
+	static const char c2[] =
+	    "%s: Status Byte 0x%02x for CDB0=0x%02x HTAG 0x%x @ %llu";
+
+	CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
+	    STATE_SENT_CMD | STATE_GOT_STATUS;
+	CMD2PKT(sp)->pkt_scbp[0] = status;
+
+	if (status == STATUS_CHECK && snsp &&
+	    (size_t)SCSA_STSLEN(sp) >= sizeof (struct scsi_arq_status)) {
+		struct scsi_arq_status *aqp =
+		    (void *) CMD2PKT(sp)->pkt_scbp;
+		size_t amt = sizeof (struct scsi_extended_sense);
+		uint8_t key = scsi_sense_key(snsp);
+		uint8_t asc = scsi_sense_asc(snsp);
+		uint8_t ascq = scsi_sense_ascq(snsp);
+		if (amt > snslen) {
+			amt = snslen;
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, c1, path, status,
+		    CMD2PKT(sp)->pkt_cdbp[0] & 0xff, key, asc, ascq,
+		    sp->cmd_tag, (unsigned long long)gethrtime());
+		CMD2PKT(sp)->pkt_state |= STATE_ARQ_DONE;
+		(*(uint8_t *)&aqp->sts_rqpkt_status) = STATUS_GOOD;
+		aqp->sts_rqpkt_statistics = 0;
+		aqp->sts_rqpkt_reason = CMD_CMPLT;
+		aqp->sts_rqpkt_state = STATE_GOT_BUS |
+		    STATE_GOT_TARGET | STATE_SENT_CMD |
+		    STATE_XFERRED_DATA | STATE_GOT_STATUS;
+		(void) memcpy(&aqp->sts_sensedata, snsp, amt);
+		if (aqp->sts_sensedata.es_class != CLASS_EXTENDED_SENSE) {
+			aqp->sts_rqpkt_reason = CMD_TRAN_ERR;
+			aqp->sts_rqpkt_state = 0;
+			aqp->sts_rqpkt_resid =
+			    sizeof (struct scsi_extended_sense);
+		} else {
+			aqp->sts_rqpkt_resid =
+			    sizeof (struct scsi_extended_sense) - amt;
+		}
+	} else if (status) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, c2,
+		    path, status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff,
+		    sp->cmd_tag, (unsigned long long)gethrtime());
+	}
+
+	CMD2PKT(sp)->pkt_reason = CMD_CMPLT;
+}
+
+/*
+ * Calculate and set packet residual and return the amount
+ * left over after applying various filters.
+ */
+size_t
+pmcs_set_resid(struct scsi_pkt *pkt, size_t amt, uint32_t cdbamt)
+{
+	pkt->pkt_resid = cdbamt;
+	if (amt > pkt->pkt_resid) {
+		amt = pkt->pkt_resid;
+	}
+	if (amt > pkt->pkt_dma_len) {
+		amt = pkt->pkt_dma_len;
+	}
+	return (amt);
+}
+
+/*
+ * Return the existing target softstate if there is one.  If there is,
+ * the PHY is locked as well and that lock must be freed by the caller
+ * after the target/PHY linkage is established.
+ */
+pmcs_xscsi_t *
+pmcs_get_target(pmcs_iport_t *iport, char *tgt_port)
+{
+	pmcs_hw_t *pwp = iport->pwp;
+	pmcs_phy_t *phyp;
+	pmcs_xscsi_t *tgt;
+	uint64_t wwn;
+	char unit_address[PMCS_MAX_UA_SIZE];
+	int ua_form = 1;
+
+	/*
+	 * Find the PHY for this target
+	 */
+	phyp = pmcs_find_phy_by_sas_address(pwp, iport, NULL, tgt_port);
+	if (phyp == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG3, "%s: No PHY for target @ %s",
+		    __func__, tgt_port);
+		return (NULL);
+	}
+
+	tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port);
+
+	if (tgt) {
+		/*
+		 * There's already a target.  Check its PHY pointer to see
+		 * if we need to clear the old linkages
+		 */
+		if (tgt->phy && (tgt->phy != phyp)) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: Target PHY updated from %p to %p", __func__,
+			    (void *)tgt->phy, (void *)phyp);
+			if (!IS_ROOT_PHY(tgt->phy)) {
+				pmcs_dec_phy_ref_count(tgt->phy);
+				pmcs_inc_phy_ref_count(phyp);
+			}
+			tgt->phy->target = NULL;
+		}
+
+		tgt->phy = phyp;
+		phyp->target = tgt;
+		return (tgt);
+	}
+
+	/*
+	 * Make sure the PHY we found is on the correct iport
+	 */
+	if (phyp->iport != iport) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: No target at %s on this iport", __func__, tgt_port);
+		pmcs_unlock_phy(phyp);
+		return (NULL);
+	}
+
+	/*
+	 * Allocate the new softstate
+	 */
+	wwn = pmcs_barray2wwn(phyp->sas_address);
+	(void) scsi_wwn_to_wwnstr(wwn, ua_form, unit_address);
+
+	if (ddi_soft_state_bystr_zalloc(iport->tgt_sstate, unit_address) !=
+	    DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: Couldn't alloc softstate for device at %s",
+		    __func__, unit_address);
+		pmcs_unlock_phy(phyp);
+		return (NULL);
+	}
+
+	tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, unit_address);
+	STAILQ_INIT(&tgt->wq);
+	STAILQ_INIT(&tgt->aq);
+	STAILQ_INIT(&tgt->sq);
+	mutex_init(&tgt->statlock, NULL, MUTEX_DRIVER,
+	    DDI_INTR_PRI(pwp->intr_pri));
+	mutex_init(&tgt->wqlock, NULL, MUTEX_DRIVER,
+	    DDI_INTR_PRI(pwp->intr_pri));
+	mutex_init(&tgt->aqlock, NULL, MUTEX_DRIVER,
+	    DDI_INTR_PRI(pwp->intr_pri));
+	cv_init(&tgt->reset_cv, NULL, CV_DRIVER, NULL);
+	cv_init(&tgt->abort_cv, NULL, CV_DRIVER, NULL);
+	tgt->qdepth = 1;
+	tgt->target_num = PMCS_INVALID_TARGET_NUM;
+	bcopy(unit_address, tgt->unit_address, PMCS_MAX_UA_SIZE);
+	tgt->pwp = pwp;
+	tgt->ua = strdup(iport->ua);
+	tgt->phy = phyp;
+	ASSERT((phyp->target == NULL) || (phyp->target == tgt));
+	if (phyp->target == NULL) {
+		phyp->target = tgt;
+	}
+
+	/*
+	 * Don't allocate LUN softstate for SMP targets
+	 */
+	if (phyp->dtype == EXPANDER) {
+		return (tgt);
+	}
+
+	if (ddi_soft_state_bystr_init(&tgt->lun_sstate,
+	    sizeof (pmcs_lun_t), PMCS_LUN_SSTATE_SZ) != 0) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: LUN soft_state_bystr_init failed", __func__);
+		ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port);
+		pmcs_unlock_phy(phyp);
+		return (NULL);
+	}
+
+	return (tgt);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_smhba.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,287 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file contains SM-HBA support for PMC-S driver
+ */
+
+#include <sys/scsi/adapters/pmcs/pmcs.h>
+
+
+void
+pmcs_smhba_add_hba_prop(pmcs_hw_t *pwp, data_type_t dt,
+    char *prop_name, void *prop_val)
+{
+	ASSERT(pwp != NULL);
+
+	switch (dt) {
+	case DATA_TYPE_INT32:
+		if (ddi_prop_update_int(DDI_DEV_T_NONE, pwp->dip,
+		    prop_name, *(int *)prop_val)) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: %s prop update failed", __func__, prop_name);
+		}
+		break;
+	case DATA_TYPE_STRING:
+		if (ddi_prop_update_string(DDI_DEV_T_NONE, pwp->dip,
+		    prop_name, (char *)prop_val)) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: %s prop update failed", __func__, prop_name);
+		}
+		break;
+	default:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Unhandled datatype(%d) for "
+		    "(%s). Skipping prop update.", __func__, dt, prop_name);
+	}
+}
+
+
+void
+pmcs_smhba_add_iport_prop(pmcs_iport_t *iport, data_type_t dt,
+    char *prop_name, void *prop_val)
+{
+	ASSERT(iport != NULL);
+
+	switch (dt) {
+	case DATA_TYPE_INT32:
+		if (ddi_prop_update_int(DDI_DEV_T_NONE, iport->dip,
+		    prop_name, *(int *)prop_val)) {
+			pmcs_prt(iport->pwp, PMCS_PRT_DEBUG,
+			    "%s: %s prop update failed", __func__, prop_name);
+		}
+		break;
+	case DATA_TYPE_STRING:
+		if (ddi_prop_update_string(DDI_DEV_T_NONE, iport->dip,
+		    prop_name, (char *)prop_val)) {
+			pmcs_prt(iport->pwp, PMCS_PRT_DEBUG,
+			    "%s: %s prop update failed", __func__, prop_name);
+		}
+		break;
+	default:
+		pmcs_prt(iport->pwp, PMCS_PRT_DEBUG, "%s: Unhandled "
+		    "datatype(%d) for(%s). Skipping prop update.",
+		    __func__, dt, prop_name);
+	}
+}
+
+
+void
+pmcs_smhba_add_tgt_prop(pmcs_xscsi_t *tgt, data_type_t dt,
+    char *prop_name, void *prop_val)
+{
+	ASSERT(tgt != NULL);
+
+	switch (dt) {
+	case DATA_TYPE_INT32:
+		if (ddi_prop_update_int(DDI_DEV_T_NONE, tgt->dip,
+		    prop_name, *(int *)prop_val)) {
+			pmcs_prt(tgt->pwp, PMCS_PRT_DEBUG,
+			    "%s: %s prop update failed", __func__, prop_name);
+		}
+		break;
+	case DATA_TYPE_STRING:
+		if (ddi_prop_update_string(DDI_DEV_T_NONE, tgt->dip,
+		    prop_name, (char *)prop_val)) {
+			pmcs_prt(tgt->pwp, PMCS_PRT_DEBUG,
+			    "%s: %s prop update failed", __func__, prop_name);
+		}
+		break;
+	default:
+		pmcs_prt(tgt->pwp, PMCS_PRT_DEBUG, "%s: Unhandled datatype(%d) "
+		    "for (%s). Skipping prop update.", __func__, dt, prop_name);
+	}
+}
+
+/* ARGSUSED */
+void
+pmcs_smhba_set_scsi_device_props(pmcs_hw_t *pwp, pmcs_phy_t *pptr,
+    struct scsi_device *sd)
+{
+	char		*addr;
+	int		ua_form = 1;
+	uint64_t	wwn;
+	pmcs_phy_t	*pphy;
+
+	pphy = pptr->parent;
+
+	if (pphy != NULL) {
+		addr = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
+		wwn = pmcs_barray2wwn(pphy->sas_address);
+		(void) scsi_wwn_to_wwnstr(wwn, ua_form, addr);
+
+		if (pphy->dtype == SATA) {
+			(void) scsi_device_prop_update_string(sd,
+			    SCSI_DEVICE_PROP_PATH,
+			    SCSI_ADDR_PROP_BRIDGE_PORT, addr);
+		}
+		if (pphy->dtype == EXPANDER) {
+			(void) scsi_device_prop_update_string(sd,
+			    SCSI_DEVICE_PROP_PATH,
+			    SCSI_ADDR_PROP_ATTACHED_PORT, addr);
+		}
+		kmem_free(addr, PMCS_MAX_UA_SIZE);
+	}
+}
+
+void
+pmcs_smhba_set_phy_props(pmcs_iport_t *iport)
+{
+	int		i;
+	size_t		packed_size;
+	char		*packed_data;
+	pmcs_hw_t	*pwp = iport->pwp;
+	pmcs_phy_t	*phy_ptr;
+	nvlist_t	**phy_props;
+	nvlist_t	*nvl;
+
+	mutex_enter(&iport->lock);
+	if (iport->nphy == 0) {
+		mutex_exit(&iport->lock);
+		return;
+	}
+
+	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: nvlist_alloc() failed",
+		    __func__);
+	}
+
+	phy_props = kmem_zalloc(sizeof (nvlist_t *) * iport->nphy, KM_SLEEP);
+
+	for (phy_ptr = list_head(&iport->phys), i = 0;
+	    phy_ptr != NULL;
+	    phy_ptr = list_next(&iport->phys, phy_ptr), i++) {
+		pmcs_lock_phy(phy_ptr);
+
+		(void) nvlist_alloc(&phy_props[i], NV_UNIQUE_NAME, 0);
+
+		(void) nvlist_add_uint8(phy_props[i], SAS_PHY_ID,
+		    phy_ptr->phynum);
+		(void) nvlist_add_int8(phy_props[i], SAS_NEG_LINK_RATE,
+		    phy_ptr->link_rate);
+		(void) nvlist_add_int8(phy_props[i], SAS_PROG_MIN_LINK_RATE,
+		    phy_ptr->state.prog_min_rate);
+		(void) nvlist_add_int8(phy_props[i], SAS_HW_MIN_LINK_RATE,
+		    phy_ptr->state.hw_min_rate);
+		(void) nvlist_add_int8(phy_props[i], SAS_PROG_MAX_LINK_RATE,
+		    phy_ptr->state.prog_max_rate);
+		(void) nvlist_add_int8(phy_props[i], SAS_HW_MAX_LINK_RATE,
+		    phy_ptr->state.hw_max_rate);
+
+		pmcs_unlock_phy(phy_ptr);
+	}
+
+	(void) nvlist_add_nvlist_array(nvl, SAS_PHY_INFO_NVL, phy_props,
+	    iport->nphy);
+
+	(void) nvlist_size(nvl, &packed_size, NV_ENCODE_NATIVE);
+	packed_data = kmem_zalloc(packed_size, KM_SLEEP);
+	(void) nvlist_pack(nvl, &packed_data, &packed_size,
+	    NV_ENCODE_NATIVE, 0);
+
+	(void) ddi_prop_update_byte_array(DDI_DEV_T_NONE, iport->dip,
+	    SAS_PHY_INFO, (uchar_t *)packed_data, packed_size);
+
+	for (i = 0; i < iport->nphy && phy_props[i] != NULL; i++) {
+		nvlist_free(phy_props[i]);
+	}
+	nvlist_free(nvl);
+	kmem_free(phy_props, sizeof (nvlist_t *) * iport->nphy);
+	mutex_exit(&iport->lock);
+	kmem_free(packed_data, packed_size);
+}
+
+/*
+ * Called with PHY lock held on phyp
+ */
+void
+pmcs_smhba_log_sysevent(pmcs_hw_t *pwp, char *subclass, char *etype,
+    pmcs_phy_t *phyp)
+{
+	nvlist_t	*attr_list;
+	char		*pname;
+	char		sas_addr[PMCS_MAX_UA_SIZE];
+	uint8_t		phynum = 0;
+	uint8_t		lrate = 0;
+	uint64_t	wwn;
+	int		ua_form = 0;
+
+	if (pwp->dip == NULL)
+		return;
+	if (phyp == NULL)
+		return;
+
+	pname = kmem_zalloc(MAXPATHLEN, KM_NOSLEEP);
+	if (pname == NULL)
+		return;
+
+	if ((strcmp(subclass, ESC_SAS_PHY_EVENT) == 0) ||
+	    (strcmp(subclass, ESC_SAS_HBA_PORT_BROADCAST) == 0)) {
+		ASSERT(phyp != NULL);
+		(void) strncpy(pname, phyp->path, strlen(phyp->path));
+		phynum = phyp->phynum;
+		wwn = pmcs_barray2wwn(phyp->sas_address);
+		(void) scsi_wwn_to_wwnstr(wwn, ua_form, sas_addr);
+		if (strcmp(etype, SAS_PHY_ONLINE) == 0) {
+			lrate = phyp->link_rate;
+		}
+	}
+	if (strcmp(subclass, ESC_SAS_HBA_PORT_BROADCAST) == 0) {
+		(void) ddi_pathname(pwp->dip, pname);
+	}
+
+	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, 0) != 0) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Failed to post sysevent",
+		    __func__);
+		kmem_free(pname, MAXPATHLEN);
+		return;
+	}
+
+	if (nvlist_add_int32(attr_list, SAS_DRV_INST,
+	    ddi_get_instance(pwp->dip)) != 0)
+		goto fail;
+
+	if (nvlist_add_string(attr_list, SAS_PORT_ADDR, sas_addr) != 0)
+		goto fail;
+
+	if (nvlist_add_string(attr_list, SAS_DEVFS_PATH, pname) != 0)
+		goto fail;
+
+	if (nvlist_add_uint8(attr_list, SAS_PHY_ID, phynum) != 0)
+		goto fail;
+
+	if (strcmp(etype, SAS_PHY_ONLINE) == 0) {
+		if (nvlist_add_uint8(attr_list, SAS_LINK_RATE, lrate) != 0)
+			goto fail;
+	}
+
+	if (nvlist_add_string(attr_list, SAS_EVENT_TYPE, etype) != 0)
+		goto fail;
+
+	(void) ddi_log_sysevent(pwp->dip, DDI_VENDOR_SUNW, EC_HBA, subclass,
+	    attr_list, NULL, DDI_NOSLEEP);
+
+fail:
+	kmem_free(pname, MAXPATHLEN);
+	nvlist_free(attr_list);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/scsi/adapters/pmcs/pmcs_subr.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,8258 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains various support routines.
+ */
+
+#include <sys/scsi/adapters/pmcs/pmcs.h>
+
+/*
+ * Local static data
+ */
+static int tgtmap_usec = MICROSEC;
+
+/*
+ * SAS Topology Configuration
+ */
+static void pmcs_new_tport(pmcs_hw_t *, pmcs_phy_t *);
+static void pmcs_configure_expander(pmcs_hw_t *, pmcs_phy_t *, pmcs_iport_t *);
+
+static boolean_t pmcs_check_expanders(pmcs_hw_t *, pmcs_phy_t *);
+static void pmcs_check_expander(pmcs_hw_t *, pmcs_phy_t *);
+static void pmcs_clear_expander(pmcs_hw_t *, pmcs_phy_t *, int);
+
+static int pmcs_expander_get_nphy(pmcs_hw_t *, pmcs_phy_t *);
+static int pmcs_expander_content_discover(pmcs_hw_t *, pmcs_phy_t *,
+    pmcs_phy_t *);
+
+static int pmcs_smp_function_result(pmcs_hw_t *, smp_response_frame_t *);
+static boolean_t pmcs_validate_devid(pmcs_phy_t *, pmcs_phy_t *, uint32_t);
+static void pmcs_clear_phys(pmcs_hw_t *, pmcs_phy_t *);
+static int pmcs_configure_new_devices(pmcs_hw_t *, pmcs_phy_t *);
+static boolean_t pmcs_report_observations(pmcs_hw_t *);
+static boolean_t pmcs_report_iport_observations(pmcs_hw_t *, pmcs_iport_t *,
+    pmcs_phy_t *);
+static pmcs_phy_t *pmcs_find_phy_needing_work(pmcs_hw_t *, pmcs_phy_t *);
+static int pmcs_kill_devices(pmcs_hw_t *, pmcs_phy_t *);
+static void pmcs_lock_phy_impl(pmcs_phy_t *, int);
+static void pmcs_unlock_phy_impl(pmcs_phy_t *, int);
+static pmcs_phy_t *pmcs_clone_phy(pmcs_phy_t *);
+static boolean_t pmcs_configure_phy(pmcs_hw_t *, pmcs_phy_t *);
+static void pmcs_reap_dead_phy(pmcs_phy_t *);
+static pmcs_iport_t *pmcs_get_iport_by_ua(pmcs_hw_t *, char *);
+static boolean_t pmcs_phy_target_match(pmcs_phy_t *);
+static void pmcs_handle_ds_recovery_error(pmcs_phy_t *phyp,
+    pmcs_xscsi_t *tgt, pmcs_hw_t *pwp, const char *func_name, int line,
+    char *reason_string);
+
+/*
+ * Often used strings
+ */
+const char pmcs_nowrk[] = "%s: unable to get work structure";
+const char pmcs_nomsg[] = "%s: unable to get Inbound Message entry";
+const char pmcs_timeo[] = "!%s: command timed out";
+
+extern const ddi_dma_attr_t pmcs_dattr;
+
+/*
+ * Some Initial setup steps.
+ */
+
+int
+pmcs_setup(pmcs_hw_t *pwp)
+{
+	uint32_t barval = pwp->mpibar;
+	uint32_t i, scratch, regbar, regoff, barbar, baroff;
+	uint32_t new_ioq_depth, ferr = 0;
+
+	/*
+	 * Check current state. If we're not at READY state,
+	 * we can't go further.
+	 */
+	scratch = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
+	if ((scratch & PMCS_MSGU_AAP_STATE_MASK) == PMCS_MSGU_AAP_STATE_ERROR) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: AAP Error State (0x%x)",
+		    __func__, pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
+		    PMCS_MSGU_AAP_ERROR_MASK);
+		pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
+		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
+		return (-1);
+	}
+	if ((scratch & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: AAP unit not ready (state 0x%x)",
+		    __func__, scratch & PMCS_MSGU_AAP_STATE_MASK);
+		pmcs_fm_ereport(pwp, DDI_FM_DEVICE_INVAL_STATE);
+		ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST);
+		return (-1);
+	}
+
+	/*
+	 * Read the offset from the Message Unit scratchpad 0 register.
+	 * This allows us to read the MPI Configuration table.
+	 *
+	 * Check its signature for validity.
+	 */
+	baroff = barval;
+	barbar = barval >> PMCS_MSGU_MPI_BAR_SHIFT;
+	baroff &= PMCS_MSGU_MPI_OFFSET_MASK;
+
+	regoff = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0);
+	regbar = regoff >> PMCS_MSGU_MPI_BAR_SHIFT;
+	regoff &= PMCS_MSGU_MPI_OFFSET_MASK;
+
+	if (regoff > baroff) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad MPI Table Length "
+		    "(register offset=0x%08x, passed offset=0x%08x)", __func__,
+		    regoff, baroff);
+		return (-1);
+	}
+	if (regbar != barbar) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad MPI BAR (register "
+		    "BAROFF=0x%08x, passed BAROFF=0x%08x)", __func__,
+		    regbar, barbar);
+		return (-1);
+	}
+	pwp->mpi_offset = regoff;
+	if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS) != PMCS_SIGNATURE) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Bad MPI Configuration Table Signature 0x%x", __func__,
+		    pmcs_rd_mpi_tbl(pwp, PMCS_MPI_AS));
+		return (-1);
+	}
+
+	if (pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR) != PMCS_MPI_REVISION1) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Bad MPI Configuration Revision 0x%x", __func__,
+		    pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IR));
+		return (-1);
+	}
+
+	/*
+	 * Generate offsets for the General System, Inbound Queue Configuration
+	 * and Outbound Queue configuration tables. This way the macros to
+	 * access those tables will work correctly.
+	 */
+	pwp->mpi_gst_offset =
+	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_GSTO);
+	pwp->mpi_iqc_offset =
+	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_IQCTO);
+	pwp->mpi_oqc_offset =
+	    pwp->mpi_offset + pmcs_rd_mpi_tbl(pwp, PMCS_MPI_OQCTO);
+
+	pwp->fw = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FW);
+
+	pwp->max_cmd = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_MOIO);
+	pwp->max_dev = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO0) >> 16;
+
+	pwp->max_iq = PMCS_MNIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
+	pwp->max_oq = PMCS_MNOQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
+	pwp->nphy = PMCS_NPHY(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1));
+	if (pwp->max_iq <= PMCS_NIQ) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: not enough Inbound Queues "
+		    "supported (need %d, max_oq=%d)", __func__, pwp->max_iq,
+		    PMCS_NIQ);
+		return (-1);
+	}
+	if (pwp->max_oq <= PMCS_NOQ) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: not enough Outbound Queues "
+		    "supported (need %d, max_oq=%d)", __func__, pwp->max_oq,
+		    PMCS_NOQ);
+		return (-1);
+	}
+	if (pwp->nphy == 0) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: zero phys reported",
+		    __func__);
+		return (-1);
+	}
+	if (PMCS_HPIQ(pmcs_rd_mpi_tbl(pwp, PMCS_MPI_INFO1))) {
+		pwp->hipri_queue = (1 << PMCS_IQ_OTHER);
+	}
+
+
+	for (i = 0; i < pwp->nphy; i++) {
+		PMCS_MPI_EVQSET(pwp, PMCS_OQ_EVENTS, i);
+		PMCS_MPI_NCQSET(pwp, PMCS_OQ_EVENTS, i);
+	}
+
+	pmcs_wr_mpi_tbl(pwp, PMCS_MPI_INFO2,
+	    (PMCS_OQ_EVENTS << GENERAL_EVENT_OQ_SHIFT) |
+	    (PMCS_OQ_EVENTS << DEVICE_HANDLE_REMOVED_SHIFT));
+
+	/*
+	 * Verify that ioq_depth is valid (> 0 and not so high that it
+	 * would cause us to overrun the chip with commands).
+	 */
+	if (pwp->ioq_depth == 0) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: I/O queue depth set to 0. Setting to %d",
+		    __func__, PMCS_NQENTRY);
+		pwp->ioq_depth = PMCS_NQENTRY;
+	}
+
+	if (pwp->ioq_depth < PMCS_MIN_NQENTRY) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: I/O queue depth set too low (%d). Setting to %d",
+		    __func__, pwp->ioq_depth, PMCS_MIN_NQENTRY);
+		pwp->ioq_depth = PMCS_MIN_NQENTRY;
+	}
+
+	if (pwp->ioq_depth > (pwp->max_cmd / (PMCS_IO_IQ_MASK + 1))) {
+		new_ioq_depth = pwp->max_cmd / (PMCS_IO_IQ_MASK + 1);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: I/O queue depth set too high (%d). Setting to %d",
+		    __func__, pwp->ioq_depth, new_ioq_depth);
+		pwp->ioq_depth = new_ioq_depth;
+	}
+
+	/*
+	 * Allocate consistent memory for OQs and IQs.
+	 */
+	pwp->iqp_dma_attr = pwp->oqp_dma_attr = pmcs_dattr;
+	pwp->iqp_dma_attr.dma_attr_align =
+	    pwp->oqp_dma_attr.dma_attr_align = PMCS_QENTRY_SIZE;
+
+	/*
+	 * The Rev C chip has the ability to do PIO to or from consistent
+	 * memory anywhere in a 64 bit address space, but the firmware is
+	 * not presently set up to do so.
+	 */
+	pwp->iqp_dma_attr.dma_attr_addr_hi =
+	    pwp->oqp_dma_attr.dma_attr_addr_hi = 0x000000FFFFFFFFFFull;
+
+	for (i = 0; i < PMCS_NIQ; i++) {
+		if (pmcs_dma_setup(pwp, &pwp->iqp_dma_attr,
+		    &pwp->iqp_acchdls[i],
+		    &pwp->iqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
+		    (caddr_t *)&pwp->iqp[i], &pwp->iqaddr[i]) == B_FALSE) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "Failed to setup DMA for iqp[%d]", i);
+			return (-1);
+		}
+		bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
+	}
+
+	for (i = 0; i < PMCS_NOQ; i++) {
+		if (pmcs_dma_setup(pwp, &pwp->oqp_dma_attr,
+		    &pwp->oqp_acchdls[i],
+		    &pwp->oqp_handles[i], PMCS_QENTRY_SIZE * pwp->ioq_depth,
+		    (caddr_t *)&pwp->oqp[i], &pwp->oqaddr[i]) == B_FALSE) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "Failed to setup DMA for oqp[%d]", i);
+			return (-1);
+		}
+		bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
+	}
+
+	/*
+	 * Install the IQ and OQ addresses (and null out the rest).
+	 */
+	for (i = 0; i < pwp->max_iq; i++) {
+		pwp->iqpi_offset[i] = pmcs_rd_iqc_tbl(pwp, PMCS_IQPIOFFX(i));
+		if (i < PMCS_NIQ) {
+			if (i != PMCS_IQ_OTHER) {
+				pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
+				    pwp->ioq_depth | (PMCS_QENTRY_SIZE << 16));
+			} else {
+				pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i),
+				    (1 << 30) | pwp->ioq_depth |
+				    (PMCS_QENTRY_SIZE << 16));
+			}
+			pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i),
+			    DWORD1(pwp->iqaddr[i]));
+			pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i),
+			    DWORD0(pwp->iqaddr[i]));
+			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i),
+			    DWORD1(pwp->ciaddr+IQ_OFFSET(i)));
+			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i),
+			    DWORD0(pwp->ciaddr+IQ_OFFSET(i)));
+		} else {
+			pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
+			pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
+			pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
+			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
+			pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
+		}
+	}
+
+	for (i = 0; i < pwp->max_oq; i++) {
+		pwp->oqci_offset[i] = pmcs_rd_oqc_tbl(pwp, PMCS_OQCIOFFX(i));
+		if (i < PMCS_NOQ) {
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), pwp->ioq_depth |
+			    (PMCS_QENTRY_SIZE << 16) | OQIEX);
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i),
+			    DWORD1(pwp->oqaddr[i]));
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i),
+			    DWORD0(pwp->oqaddr[i]));
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i),
+			    DWORD1(pwp->ciaddr+OQ_OFFSET(i)));
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i),
+			    DWORD0(pwp->ciaddr+OQ_OFFSET(i)));
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i),
+			    pwp->oqvec[i] << 24);
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
+		} else {
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
+			pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
+		}
+	}
+
+	/*
+	 * Set up logging, if defined.
+	 */
+	if (pwp->fwlog) {
+		uint64_t logdma = pwp->fwaddr;
+		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAH, DWORD1(logdma));
+		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBAL, DWORD0(logdma));
+		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELBS, PMCS_FWLOG_SIZE >> 1);
+		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_MELSEV, pwp->fwlog);
+		logdma += (PMCS_FWLOG_SIZE >> 1);
+		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAH, DWORD1(logdma));
+		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBAL, DWORD0(logdma));
+		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELBS, PMCS_FWLOG_SIZE >> 1);
+		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_IELSEV, pwp->fwlog);
+	}
+
+	/*
+	 * Interrupt vectors, outbound queues, and odb_auto_clear
+	 *
+	 * MSI/MSI-X:
+	 * If we got 4 interrupt vectors, we'll assign one to each outbound
+	 * queue as well as the fatal interrupt, and auto clear can be set
+	 * for each.
+	 *
+	 * If we only got 2 vectors, one will be used for I/O completions
+	 * and the other for the other two vectors.  In this case, auto_
+	 * clear can only be set for I/Os, which is fine.  The fatal
+	 * interrupt will be mapped to the PMCS_FATAL_INTERRUPT bit, which
+	 * is not an interrupt vector.
+	 *
+	 * MSI/MSI-X/INT-X:
+	 * If we only got 1 interrupt vector, auto_clear must be set to 0,
+	 * and again the fatal interrupt will be mapped to the
+	 * PMCS_FATAL_INTERRUPT bit (again, not an interrupt vector).
+	 */
+
+	switch (pwp->int_type) {
+	case PMCS_INT_MSIX:
+	case PMCS_INT_MSI:
+		switch (pwp->intr_cnt) {
+		case 1:
+			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
+			    (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
+			pwp->odb_auto_clear = 0;
+			break;
+		case 2:
+			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
+			    (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
+			pwp->odb_auto_clear = (1 << PMCS_FATAL_INTERRUPT) |
+			    (1 << PMCS_MSIX_IODONE);
+			break;
+		case 4:
+			pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, PMCS_FERRIE |
+			    (PMCS_MSIX_FATAL << PMCS_FERIV_SHIFT));
+			pwp->odb_auto_clear = (1 << PMCS_MSIX_FATAL) |
+			    (1 << PMCS_MSIX_GENERAL) | (1 << PMCS_MSIX_IODONE) |
+			    (1 << PMCS_MSIX_EVENTS);
+			break;
+		}
+		break;
+
+	case PMCS_INT_FIXED:
+		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR,
+		    PMCS_FERRIE | (PMCS_FATAL_INTERRUPT << PMCS_FERIV_SHIFT));
+		pwp->odb_auto_clear = 0;
+		break;
+	}
+
+	/*
+	 * Enable Interrupt Reassertion
+	 * Default Delay 1000us
+	 */
+	ferr = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_FERR);
+	if ((ferr & PMCS_MPI_IRAE) == 0) {
+		ferr &= ~(PMCS_MPI_IRAU | PMCS_MPI_IRAD_MASK);
+		pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, ferr | PMCS_MPI_IRAE);
+	}
+
+	pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR, pwp->odb_auto_clear);
+	pwp->mpi_table_setup = 1;
+	return (0);
+}
+
+/*
+ * Start the Message Passing protocol with the PMC chip.
+ */
+int
+pmcs_start_mpi(pmcs_hw_t *pwp)
+{
+	int i;
+
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPIINI);
+	for (i = 0; i < 1000; i++) {
+		if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
+		    PMCS_MSGU_IBDB_MPIINI) == 0) {
+			break;
+		}
+		drv_usecwait(1000);
+	}
+	if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPIINI) {
+		return (-1);
+	}
+	drv_usecwait(500000);
+
+	/*
+	 * Check to make sure we got to INIT state.
+	 */
+	if (PMCS_MPI_S(pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE)) !=
+	    PMCS_MPI_STATE_INIT) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: MPI launch failed (GST 0x%x "
+		    "DBCLR 0x%x)", __func__,
+		    pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE),
+		    pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB_CLEAR));
+		return (-1);
+	}
+	return (0);
+}
+
+/*
+ * Stop the Message Passing protocol with the PMC chip.
+ */
+int
+pmcs_stop_mpi(pmcs_hw_t *pwp)
+{
+	int i;
+
+	for (i = 0; i < pwp->max_iq; i++) {
+		pmcs_wr_iqc_tbl(pwp, PMCS_IQC_PARMX(i), 0);
+		pmcs_wr_iqc_tbl(pwp, PMCS_IQBAHX(i), 0);
+		pmcs_wr_iqc_tbl(pwp, PMCS_IQBALX(i), 0);
+		pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBAHX(i), 0);
+		pmcs_wr_iqc_tbl(pwp, PMCS_IQCIBALX(i), 0);
+	}
+	for (i = 0; i < pwp->max_oq; i++) {
+		pmcs_wr_oqc_tbl(pwp, PMCS_OQC_PARMX(i), 0);
+		pmcs_wr_oqc_tbl(pwp, PMCS_OQBAHX(i), 0);
+		pmcs_wr_oqc_tbl(pwp, PMCS_OQBALX(i), 0);
+		pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBAHX(i), 0);
+		pmcs_wr_oqc_tbl(pwp, PMCS_OQPIBALX(i), 0);
+		pmcs_wr_oqc_tbl(pwp, PMCS_OQIPARM(i), 0);
+		pmcs_wr_oqc_tbl(pwp, PMCS_OQDICX(i), 0);
+	}
+	pmcs_wr_mpi_tbl(pwp, PMCS_MPI_FERR, 0);
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_IBDB, PMCS_MSGU_IBDB_MPICTU);
+	for (i = 0; i < 2000; i++) {
+		if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) &
+		    PMCS_MSGU_IBDB_MPICTU) == 0) {
+			break;
+		}
+		drv_usecwait(1000);
+	}
+	if (pmcs_rd_msgunit(pwp, PMCS_MSGU_IBDB) & PMCS_MSGU_IBDB_MPICTU) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: MPI stop failed", __func__);
+		return (-1);
+	}
+	return (0);
+}
+
+/*
+ * Do a sequence of ECHO messages to test for MPI functionality,
+ * all inbound and outbound queue functionality and interrupts.
+ */
+int
+pmcs_echo_test(pmcs_hw_t *pwp)
+{
+	echo_test_t fred;
+	struct pmcwork *pwrk;
+	uint32_t *msg, count;
+	int iqe = 0, iqo = 0, result, rval = 0;
+	int iterations;
+	hrtime_t echo_start, echo_end, echo_total;
+
+	ASSERT(pwp->max_cmd > 0);
+
+	/*
+	 * We want iterations to be max_cmd * 3 to ensure that we run the
+	 * echo test enough times to iterate through every inbound queue
+	 * at least twice.
+	 */
+	iterations = pwp->max_cmd * 3;
+
+	echo_total = 0;
+	count = 0;
+
+	while (count < iterations) {
+		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
+		if (pwrk == NULL) {
+			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
+			rval = -1;
+			break;
+		}
+
+		mutex_enter(&pwp->iqp_lock[iqe]);
+		msg = GET_IQ_ENTRY(pwp, iqe);
+		if (msg == NULL) {
+			mutex_exit(&pwp->iqp_lock[iqe]);
+			pmcs_pwork(pwp, pwrk);
+			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
+			rval = -1;
+			break;
+		}
+
+		bzero(msg, PMCS_QENTRY_SIZE);
+
+		if (iqe == PMCS_IQ_OTHER) {
+			/* This is on the high priority queue */
+			msg[0] = LE_32(PMCS_HIPRI(pwp, iqo, PMCIN_ECHO));
+		} else {
+			msg[0] = LE_32(PMCS_IOMB_IN_SAS(iqo, PMCIN_ECHO));
+		}
+		msg[1] = LE_32(pwrk->htag);
+		fred.signature = 0xdeadbeef;
+		fred.count = count;
+		fred.ptr = &count;
+		(void) memcpy(&msg[2], &fred, sizeof (fred));
+		pwrk->state = PMCS_WORK_STATE_ONCHIP;
+
+		INC_IQ_ENTRY(pwp, iqe);
+
+		echo_start = gethrtime();
+		DTRACE_PROBE2(pmcs__echo__test__wait__start,
+		    hrtime_t, echo_start, uint32_t, pwrk->htag);
+
+		if (++iqe == PMCS_NIQ) {
+			iqe = 0;
+		}
+		if (++iqo == PMCS_NOQ) {
+			iqo = 0;
+		}
+
+		WAIT_FOR(pwrk, 250, result);
+
+		echo_end = gethrtime();
+		DTRACE_PROBE2(pmcs__echo__test__wait__end,
+		    hrtime_t, echo_end, int, result);
+
+		echo_total += (echo_end - echo_start);
+
+		pmcs_pwork(pwp, pwrk);
+		if (result) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: command timed out on echo test #%d",
+			    __func__, count);
+			rval = -1;
+			break;
+		}
+	}
+
+	/*
+	 * The intr_threshold is adjusted by PMCS_INTR_THRESHOLD in order to
+	 * remove the overhead of things like the delay in getting signaled
+	 * for completion.
+	 */
+	if (echo_total != 0) {
+		pwp->io_intr_coal.intr_latency =
+		    (echo_total / iterations) / 2;
+		pwp->io_intr_coal.intr_threshold =
+		    PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
+		    pwp->io_intr_coal.intr_latency);
+	}
+
+	return (rval);
+}
+
+/*
+ * Start the (real) phys
+ */
+int
+pmcs_start_phy(pmcs_hw_t *pwp, int phynum, int linkmode, int speed)
+{
+	int result;
+	uint32_t *msg;
+	struct pmcwork *pwrk;
+	pmcs_phy_t *pptr;
+	sas_identify_af_t sap;
+
+	mutex_enter(&pwp->lock);
+	pptr = pwp->root_phys + phynum;
+	if (pptr == NULL) {
+		mutex_exit(&pwp->lock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: cannot find port %d",
+		    __func__, phynum);
+		return (0);
+	}
+
+	pmcs_lock_phy(pptr);
+	mutex_exit(&pwp->lock);
+
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
+	if (pwrk == NULL) {
+		pmcs_unlock_phy(pptr);
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
+		return (-1);
+	}
+
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	if (msg == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		pmcs_unlock_phy(pptr);
+		pmcs_pwork(pwp, pwrk);
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
+		return (-1);
+	}
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_START));
+	msg[1] = LE_32(pwrk->htag);
+	msg[2] = LE_32(linkmode | speed | phynum);
+	bzero(&sap, sizeof (sap));
+	sap.device_type = SAS_IF_DTYPE_ENDPOINT;
+	sap.ssp_ini_port = 1;
+
+	if (pwp->separate_ports) {
+		pmcs_wwn2barray(pwp->sas_wwns[phynum], sap.sas_address);
+	} else {
+		pmcs_wwn2barray(pwp->sas_wwns[0], sap.sas_address);
+	}
+
+	ASSERT(phynum < SAS2_PHYNUM_MAX);
+	sap.phy_identifier = phynum & SAS2_PHYNUM_MASK;
+	(void) memcpy(&msg[3], &sap, sizeof (sas_identify_af_t));
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	pptr->state.prog_min_rate = (lowbit((ulong_t)speed) - 1);
+	pptr->state.prog_max_rate = (highbit((ulong_t)speed) - 1);
+	pptr->state.hw_min_rate = PMCS_HW_MIN_LINK_RATE;
+	pptr->state.hw_max_rate = PMCS_HW_MAX_LINK_RATE;
+
+	pmcs_unlock_phy(pptr);
+	WAIT_FOR(pwrk, 1000, result);
+	pmcs_pwork(pwp, pwrk);
+
+	if (result) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__);
+	} else {
+		mutex_enter(&pwp->lock);
+		pwp->phys_started |= (1 << phynum);
+		mutex_exit(&pwp->lock);
+	}
+
+	return (0);
+}
+
+int
+pmcs_start_phys(pmcs_hw_t *pwp)
+{
+	int i;
+
+	for (i = 0; i < pwp->nphy; i++) {
+		if ((pwp->phyid_block_mask & (1 << i)) == 0) {
+			if (pmcs_start_phy(pwp, i,
+			    (pwp->phymode << PHY_MODE_SHIFT),
+			    pwp->physpeed << PHY_LINK_SHIFT)) {
+				return (-1);
+			}
+			if (pmcs_clear_diag_counters(pwp, i)) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: failed to "
+				    "reset counters on PHY (%d)", __func__, i);
+			}
+		}
+	}
+	return (0);
+}
+
+/*
+ * Called with PHY locked
+ */
+int
+pmcs_reset_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t type)
+{
+	uint32_t *msg;
+	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
+	const char *mbar;
+	uint32_t amt;
+	uint32_t pdevid;
+	uint32_t stsoff;
+	uint32_t status;
+	int result, level, phynum;
+	struct pmcwork *pwrk;
+	uint32_t htag;
+
+	ASSERT(mutex_owned(&pptr->phy_lock));
+
+	bzero(iomb, PMCS_QENTRY_SIZE);
+	phynum = pptr->phynum;
+	level = pptr->level;
+	if (level > 0) {
+		pdevid = pptr->parent->device_id;
+	}
+
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
+
+	if (pwrk == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
+		return (ENOMEM);
+	}
+
+	pwrk->arg = iomb;
+
+	/*
+	 * If level > 0, we need to issue an SMP_REQUEST with a PHY_CONTROL
+	 * function to do either a link reset or hard reset.  If level == 0,
+	 * then we do a LOCAL_PHY_CONTROL IOMB to do link/hard reset to the
+	 * root (local) PHY
+	 */
+	if (level) {
+		stsoff = 2;
+		iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
+		    PMCIN_SMP_REQUEST));
+		iomb[1] = LE_32(pwrk->htag);
+		iomb[2] = LE_32(pdevid);
+		iomb[3] = LE_32(40 << SMP_REQUEST_LENGTH_SHIFT);
+		/*
+		 * Send SMP PHY CONTROL/HARD or LINK RESET
+		 */
+		iomb[4] = BE_32(0x40910000);
+		iomb[5] = 0;
+
+		if (type == PMCS_PHYOP_HARD_RESET) {
+			mbar = "SMP PHY CONTROL/HARD RESET";
+			iomb[6] = BE_32((phynum << 24) |
+			    (PMCS_PHYOP_HARD_RESET << 16));
+		} else {
+			mbar = "SMP PHY CONTROL/LINK RESET";
+			iomb[6] = BE_32((phynum << 24) |
+			    (PMCS_PHYOP_LINK_RESET << 16));
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: sending %s to %s for phy 0x%x",
+		    __func__, mbar, pptr->parent->path, pptr->phynum);
+		amt = 7;
+	} else {
+		/*
+		 * Unlike most other Outbound messages, status for
+		 * a local phy operation is in DWORD 3.
+		 */
+		stsoff = 3;
+		iomb[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
+		    PMCIN_LOCAL_PHY_CONTROL));
+		iomb[1] = LE_32(pwrk->htag);
+		if (type == PMCS_PHYOP_LINK_RESET) {
+			mbar = "LOCAL PHY LINK RESET";
+			iomb[2] = LE_32((PMCS_PHYOP_LINK_RESET << 8) | phynum);
+		} else {
+			mbar = "LOCAL PHY HARD RESET";
+			iomb[2] = LE_32((PMCS_PHYOP_HARD_RESET << 8) | phynum);
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: sending %s to %s", __func__, mbar, pptr->path);
+		amt = 3;
+	}
+
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (msg == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		pmcs_pwork(pwp, pwrk);
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
+		return (ENOMEM);
+	}
+	COPY_MESSAGE(msg, iomb, amt);
+	htag = pwrk->htag;
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	pmcs_unlock_phy(pptr);
+	WAIT_FOR(pwrk, 1000, result);
+	pmcs_pwork(pwp, pwrk);
+	pmcs_lock_phy(pptr);
+
+	if (result) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__);
+
+		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: Unable to issue SMP abort for htag 0x%08x",
+			    __func__, htag);
+		} else {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: Issuing SMP ABORT for htag 0x%08x",
+			    __func__, htag);
+		}
+		return (EIO);
+	}
+	status = LE_32(iomb[stsoff]);
+
+	if (status != PMCOUT_STATUS_OK) {
+		char buf[32];
+		const char *es =  pmcs_status_str(status);
+		if (es == NULL) {
+			(void) snprintf(buf, sizeof (buf), "Status 0x%x",
+			    status);
+			es = buf;
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: %s action returned %s for %s", __func__, mbar, es,
+		    pptr->path);
+		return (EIO);
+	}
+
+	return (0);
+}
+
+/*
+ * Stop the (real) phys.  No PHY or softstate locks are required as this only
+ * happens during detach.
+ */
+void
+pmcs_stop_phy(pmcs_hw_t *pwp, int phynum)
+{
+	int result;
+	pmcs_phy_t *pptr;
+	uint32_t *msg;
+	struct pmcwork *pwrk;
+
+	pptr =  pwp->root_phys + phynum;
+	if (pptr == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: unable to find port %d", __func__, phynum);
+		return;
+	}
+
+	if (pwp->phys_started & (1 << phynum)) {
+		pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
+
+		if (pwrk == NULL) {
+			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
+			return;
+		}
+
+		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+		if (msg == NULL) {
+			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+			pmcs_pwork(pwp, pwrk);
+			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
+			return;
+		}
+
+		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_PHY_STOP));
+		msg[1] = LE_32(pwrk->htag);
+		msg[2] = LE_32(phynum);
+		pwrk->state = PMCS_WORK_STATE_ONCHIP;
+		/*
+		 * Make this unconfigured now.
+		 */
+		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+		WAIT_FOR(pwrk, 1000, result);
+
+		pmcs_pwork(pwp, pwrk);
+		if (result) {
+			pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__);
+		}
+
+		pwp->phys_started &= ~(1 << phynum);
+	}
+
+	pptr->configured = 0;
+}
+
+/*
+ * No locks should be required as this is only called during detach
+ */
+void
+pmcs_stop_phys(pmcs_hw_t *pwp)
+{
+	int i;
+	for (i = 0; i < pwp->nphy; i++) {
+		if ((pwp->phyid_block_mask & (1 << i)) == 0) {
+			pmcs_stop_phy(pwp, i);
+		}
+	}
+}
+
+/*
+ * Run SAS_DIAG_EXECUTE with cmd and cmd_desc passed.
+ * 	ERR_CNT_RESET: return status of cmd
+ *	DIAG_REPORT_GET: return value of the counter
+ */
+int
+pmcs_sas_diag_execute(pmcs_hw_t *pwp, uint32_t cmd, uint32_t cmd_desc,
+    uint8_t phynum)
+{
+	uint32_t htag, *ptr, status, msg[PMCS_MSG_SIZE << 1];
+	int result;
+	struct pmcwork *pwrk;
+
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
+	if (pwrk == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
+		return (DDI_FAILURE);
+	}
+	pwrk->arg = msg;
+	htag = pwrk->htag;
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_SAS_DIAG_EXECUTE));
+	msg[1] = LE_32(htag);
+	msg[2] = LE_32((cmd << PMCS_DIAG_CMD_SHIFT) |
+	    (cmd_desc << PMCS_DIAG_CMD_DESC_SHIFT) | phynum);
+
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (ptr == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		pmcs_pwork(pwp, pwrk);
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
+		return (DDI_FAILURE);
+	}
+	COPY_MESSAGE(ptr, msg, 3);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	WAIT_FOR(pwrk, 1000, result);
+
+	pmcs_pwork(pwp, pwrk);
+
+	if (result) {
+		pmcs_timed_out(pwp, htag, __func__);
+		return (DDI_FAILURE);
+	}
+
+	status = LE_32(msg[3]);
+
+	/* Return for counter reset */
+	if (cmd == PMCS_ERR_CNT_RESET)
+		return (status);
+
+	/* Return for counter value */
+	if (status) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: failed, status (0x%x)",
+		    __func__, status);
+		return (DDI_FAILURE);
+	}
+	return (LE_32(msg[4]));
+}
+
+/* Get the current value of the counter for desc on phynum and return it. */
+int
+pmcs_get_diag_report(pmcs_hw_t *pwp, uint32_t desc, uint8_t phynum)
+{
+	return (pmcs_sas_diag_execute(pwp, PMCS_DIAG_REPORT_GET, desc, phynum));
+}
+
+/* Clear all of the counters for phynum. Returns the status of the command. */
+int
+pmcs_clear_diag_counters(pmcs_hw_t *pwp, uint8_t phynum)
+{
+	uint32_t	cmd = PMCS_ERR_CNT_RESET;
+	uint32_t	cmd_desc;
+
+	cmd_desc = PMCS_INVALID_DWORD_CNT;
+	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
+		return (DDI_FAILURE);
+
+	cmd_desc = PMCS_DISPARITY_ERR_CNT;
+	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
+		return (DDI_FAILURE);
+
+	cmd_desc = PMCS_LOST_DWORD_SYNC_CNT;
+	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
+		return (DDI_FAILURE);
+
+	cmd_desc = PMCS_RESET_FAILED_CNT;
+	if (pmcs_sas_diag_execute(pwp, cmd, cmd_desc, phynum))
+		return (DDI_FAILURE);
+
+	return (DDI_SUCCESS);
+}
+
+/*
+ * Get firmware timestamp
+ */
+int
+pmcs_get_time_stamp(pmcs_hw_t *pwp, uint64_t *ts)
+{
+	uint32_t htag, *ptr, msg[PMCS_MSG_SIZE << 1];
+	int result;
+	struct pmcwork *pwrk;
+
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, NULL);
+	if (pwrk == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
+		return (-1);
+	}
+	pwrk->arg = msg;
+	htag = pwrk->htag;
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_EVENTS, PMCIN_GET_TIME_STAMP));
+	msg[1] = LE_32(pwrk->htag);
+
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (ptr == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		pmcs_pwork(pwp, pwrk);
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
+		return (-1);
+	}
+	COPY_MESSAGE(ptr, msg, 2);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	WAIT_FOR(pwrk, 1000, result);
+
+	pmcs_pwork(pwp, pwrk);
+
+	if (result) {
+		pmcs_timed_out(pwp, htag, __func__);
+		return (-1);
+	}
+	*ts = LE_32(msg[2]) | (((uint64_t)LE_32(msg[3])) << 32);
+	return (0);
+}
+
+/*
+ * Dump all pertinent registers
+ */
+
+void
+pmcs_register_dump(pmcs_hw_t *pwp)
+{
+	int i;
+	uint32_t val;
+
+	pmcs_prt(pwp, PMCS_PRT_INFO, "pmcs%d: Register dump start",
+	    ddi_get_instance(pwp->dip));
+	pmcs_prt(pwp, PMCS_PRT_INFO,
+	    "OBDB (intr): 0x%08x (mask): 0x%08x (clear): 0x%08x",
+	    pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB),
+	    pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_MASK),
+	    pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR));
+	pmcs_prt(pwp, PMCS_PRT_INFO, "SCRATCH0: 0x%08x",
+	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH0));
+	pmcs_prt(pwp, PMCS_PRT_INFO, "SCRATCH1: 0x%08x",
+	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1));
+	pmcs_prt(pwp, PMCS_PRT_INFO, "SCRATCH2: 0x%08x",
+	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2));
+	pmcs_prt(pwp, PMCS_PRT_INFO, "SCRATCH3: 0x%08x",
+	    pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH3));
+	for (i = 0; i < PMCS_NIQ; i++) {
+		pmcs_prt(pwp, PMCS_PRT_INFO, "IQ %d: CI %u PI %u",
+		    i, pmcs_rd_iqci(pwp, i), pmcs_rd_iqpi(pwp, i));
+	}
+	for (i = 0; i < PMCS_NOQ; i++) {
+		pmcs_prt(pwp, PMCS_PRT_INFO, "OQ %d: CI %u PI %u",
+		    i, pmcs_rd_oqci(pwp, i), pmcs_rd_oqpi(pwp, i));
+	}
+	val = pmcs_rd_gst_tbl(pwp, PMCS_GST_BASE);
+	pmcs_prt(pwp, PMCS_PRT_INFO,
+	    "GST TABLE BASE: 0x%08x (STATE=0x%x QF=%d GSTLEN=%d HMI_ERR=0x%x)",
+	    val, PMCS_MPI_S(val), PMCS_QF(val), PMCS_GSTLEN(val) * 4,
+	    PMCS_HMI_ERR(val));
+	pmcs_prt(pwp, PMCS_PRT_INFO, "GST TABLE IQFRZ0: 0x%08x",
+	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ0));
+	pmcs_prt(pwp, PMCS_PRT_INFO, "GST TABLE IQFRZ1: 0x%08x",
+	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IQFRZ1));
+	pmcs_prt(pwp, PMCS_PRT_INFO, "GST TABLE MSGU TICK: 0x%08x",
+	    pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK));
+	pmcs_prt(pwp, PMCS_PRT_INFO, "GST TABLE IOP TICK: 0x%08x",
+	    pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK));
+	for (i = 0; i < pwp->nphy; i++) {
+		uint32_t rerrf, pinfo, started = 0, link = 0;
+		pinfo = pmcs_rd_gst_tbl(pwp, PMCS_GST_PHY_INFO(i));
+		if (pinfo & 1) {
+			started = 1;
+			link = pinfo & 2;
+		}
+		rerrf = pmcs_rd_gst_tbl(pwp, PMCS_GST_RERR_INFO(i));
+		pmcs_prt(pwp, PMCS_PRT_INFO,
+		    "GST TABLE PHY%d STARTED=%d LINK=%d RERR=0x%08x",
+		    i, started, link, rerrf);
+	}
+	pmcs_prt(pwp, PMCS_PRT_INFO, "pmcs%d: Register dump end",
+	    ddi_get_instance(pwp->dip));
+}
+
+/*
+ * Handle SATA Abort and other error processing
+ */
+int
+pmcs_abort_handler(pmcs_hw_t *pwp)
+{
+	pmcs_phy_t *pptr, *pnext, *pnext_uplevel[PMCS_MAX_XPND];
+	int r, level = 0;
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s", __func__);
+
+	mutex_enter(&pwp->lock);
+	pptr = pwp->root_phys;
+	mutex_exit(&pwp->lock);
+
+	while (pptr) {
+		/*
+		 * XXX: Need to make sure this doesn't happen
+		 * XXX: when non-NCQ commands are running.
+		 */
+		pmcs_lock_phy(pptr);
+		if (pptr->need_rl_ext) {
+			ASSERT(pptr->dtype == SATA);
+			if (pmcs_acquire_scratch(pwp, B_FALSE)) {
+				goto next_phy;
+			}
+			r = pmcs_sata_abort_ncq(pwp, pptr);
+			pmcs_release_scratch(pwp);
+			if (r == ENOMEM) {
+				goto next_phy;
+			}
+			if (r) {
+				r = pmcs_reset_phy(pwp, pptr,
+				    PMCS_PHYOP_LINK_RESET);
+				if (r == ENOMEM) {
+					goto next_phy;
+				}
+				/* what if other failures happened? */
+				pptr->abort_pending = 1;
+				pptr->abort_sent = 0;
+			}
+		}
+		if (pptr->abort_pending == 0 || pptr->abort_sent) {
+			goto next_phy;
+		}
+		pptr->abort_pending = 0;
+		if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) == ENOMEM) {
+			pptr->abort_pending = 1;
+			goto next_phy;
+		}
+		pptr->abort_sent = 1;
+
+next_phy:
+		if (pptr->children) {
+			pnext = pptr->children;
+			pnext_uplevel[level++] = pptr->sibling;
+		} else {
+			pnext = pptr->sibling;
+			while ((pnext == NULL) && (level > 0)) {
+				pnext = pnext_uplevel[--level];
+			}
+		}
+
+		pmcs_unlock_phy(pptr);
+		pptr = pnext;
+	}
+
+	return (0);
+}
+
+/*
+ * Register a device (get a device handle for it).
+ * Called with PHY lock held.
+ */
+int
+pmcs_register_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	struct pmcwork *pwrk;
+	int result = 0;
+	uint32_t *msg;
+	uint32_t tmp, status;
+	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
+
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	if (msg == NULL ||
+	    (pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		result = ENOMEM;
+		goto out;
+	}
+
+	pwrk->arg = iomb;
+	pwrk->dtype = pptr->dtype;
+
+	msg[1] = LE_32(pwrk->htag);
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_REGISTER_DEVICE));
+	tmp = PMCS_DEVREG_TLR |
+	    (pptr->link_rate << PMCS_DEVREG_LINK_RATE_SHIFT);
+	if (IS_ROOT_PHY(pptr)) {
+		msg[2] = LE_32(pptr->portid |
+		    (pptr->phynum << PMCS_PHYID_SHIFT));
+	} else {
+		msg[2] = LE_32(pptr->portid);
+	}
+	if (pptr->dtype == SATA) {
+		if (IS_ROOT_PHY(pptr)) {
+			tmp |= PMCS_DEVREG_TYPE_SATA_DIRECT;
+		} else {
+			tmp |= PMCS_DEVREG_TYPE_SATA;
+		}
+	} else {
+		tmp |= PMCS_DEVREG_TYPE_SAS;
+	}
+	msg[3] = LE_32(tmp);
+	msg[4] = LE_32(PMCS_DEVREG_IT_NEXUS_TIMEOUT);
+	(void) memcpy(&msg[5], pptr->sas_address, 8);
+
+	CLEAN_MESSAGE(msg, 7);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	pmcs_unlock_phy(pptr);
+	WAIT_FOR(pwrk, 250, result);
+	pmcs_lock_phy(pptr);
+	pmcs_pwork(pwp, pwrk);
+
+	if (result) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__);
+		result = ETIMEDOUT;
+		goto out;
+	}
+	status = LE_32(iomb[2]);
+	tmp = LE_32(iomb[3]);
+	switch (status) {
+	case PMCS_DEVREG_OK:
+	case PMCS_DEVREG_DEVICE_ALREADY_REGISTERED:
+	case PMCS_DEVREG_PHY_ALREADY_REGISTERED:
+		if (pmcs_validate_devid(pwp->root_phys, pptr, tmp) == B_FALSE) {
+			result = EEXIST;
+			goto out;
+		} else if (status != PMCS_DEVREG_OK) {
+			if (tmp == 0xffffffff) {	/* F/W bug */
+				pmcs_prt(pwp, PMCS_PRT_INFO,
+				    "%s: phy %s already has bogus devid 0x%x",
+				    __func__, pptr->path, tmp);
+				result = EIO;
+				goto out;
+			} else {
+				pmcs_prt(pwp, PMCS_PRT_INFO,
+				    "%s: phy %s already has a device id 0x%x",
+				    __func__, pptr->path, tmp);
+			}
+		}
+		break;
+	default:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: status 0x%x when trying to "
+		    "register device %s", __func__, status, pptr->path);
+		result = EIO;
+		goto out;
+	}
+	pptr->device_id = tmp;
+	pptr->valid_device_id = 1;
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Phy %s/" SAS_ADDR_FMT
+	    " registered with device_id 0x%x (portid %d)", pptr->path,
+	    SAS_ADDR_PRT(pptr->sas_address), tmp, pptr->portid);
+out:
+	return (result);
+}
+
+/*
+ * Deregister a device (remove a device handle).
+ * Called with PHY locked.
+ */
+void
+pmcs_deregister_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	struct pmcwork *pwrk;
+	uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
+	uint32_t iomb[(PMCS_QENTRY_SIZE << 1) >> 2];
+	int result;
+
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
+	if (pwrk == NULL) {
+		return;
+	}
+
+	pwrk->arg = iomb;
+	pwrk->dtype = pptr->dtype;
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (ptr == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		pmcs_pwork(pwp, pwrk);
+		return;
+	}
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
+	    PMCIN_DEREGISTER_DEVICE_HANDLE));
+	msg[1] = LE_32(pwrk->htag);
+	msg[2] = LE_32(pptr->device_id);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	COPY_MESSAGE(ptr, msg, 3);
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	pmcs_unlock_phy(pptr);
+	WAIT_FOR(pwrk, 250, result);
+	pmcs_pwork(pwp, pwrk);
+	pmcs_lock_phy(pptr);
+
+	if (result) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_timeo, __func__);
+		return;
+	}
+	status = LE_32(iomb[2]);
+	if (status != PMCOUT_STATUS_OK) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: status 0x%x when trying to "
+		    "deregister device %s", __func__, status, pptr->path);
+	} else {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: device %s deregistered",
+		    __func__, pptr->path);
+		pptr->valid_device_id = 0;
+		pptr->device_id = PMCS_INVALID_DEVICE_ID;
+	}
+}
+
+/*
+ * Deregister all registered devices.
+ */
+void
+pmcs_deregister_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
+{
+	/*
+	 * Start at the maximum level and walk back to level 0.  This only
+	 * gets done during detach after all threads and timers have been
+	 * destroyed, so there's no need to hold the softstate or PHY lock.
+	 */
+	while (phyp) {
+		if (phyp->children) {
+			pmcs_deregister_devices(pwp, phyp->children);
+		}
+		if (phyp->valid_device_id) {
+			pmcs_deregister_device(pwp, phyp);
+		}
+		phyp = phyp->sibling;
+	}
+}
+
+/*
+ * Perform a 'soft' reset on the PMC chip
+ */
+int
+pmcs_soft_reset(pmcs_hw_t *pwp, boolean_t no_restart)
+{
+	uint32_t s2, sfrbits, gsm, rapchk, wapchk, wdpchk, spc, tsmode;
+	pmcs_phy_t *pptr;
+	char *msg = NULL;
+	int i;
+
+	/*
+	 * Disable interrupts
+	 */
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
+
+	pmcs_prt(pwp, PMCS_PRT_INFO, "%s", __func__);
+
+	if (pwp->locks_initted) {
+		mutex_enter(&pwp->lock);
+	}
+	pwp->blocked = 1;
+
+	/*
+	 * Step 1
+	 */
+	s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2);
+	if ((s2 & PMCS_MSGU_HOST_SOFT_RESET_READY) == 0) {
+		pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE);
+		pmcs_wr_gsm_reg(pwp, RB6_ACCESS, RB6_NMI_SIGNATURE);
+		for (i = 0; i < 100; i++) {
+			s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
+			    PMCS_MSGU_HOST_SOFT_RESET_READY;
+			if (s2) {
+				break;
+			}
+			drv_usecwait(10000);
+		}
+		s2 = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
+		    PMCS_MSGU_HOST_SOFT_RESET_READY;
+		if (s2 == 0) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: PMCS_MSGU_HOST_"
+			    "SOFT_RESET_READY never came ready", __func__);
+			pmcs_register_dump(pwp);
+			if ((pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
+			    PMCS_MSGU_CPU_SOFT_RESET_READY) == 0 ||
+			    (pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH2) &
+			    PMCS_MSGU_CPU_SOFT_RESET_READY) == 0) {
+				pwp->state = STATE_DEAD;
+				pwp->blocked = 0;
+				if (pwp->locks_initted) {
+					mutex_exit(&pwp->lock);
+				}
+				return (-1);
+			}
+		}
+	}
+
+	/*
+	 * Step 2
+	 */
+	pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_IOP, 0);
+	drv_usecwait(10);
+	pmcs_wr_gsm_reg(pwp, NMI_EN_VPE0_AAP1, 0);
+	drv_usecwait(10);
+	pmcs_wr_topunit(pwp, PMCS_EVENT_INT_ENABLE, 0);
+	drv_usecwait(10);
+	pmcs_wr_topunit(pwp, PMCS_EVENT_INT_STAT,
+	    pmcs_rd_topunit(pwp, PMCS_EVENT_INT_STAT));
+	drv_usecwait(10);
+	pmcs_wr_topunit(pwp, PMCS_ERROR_INT_ENABLE, 0);
+	drv_usecwait(10);
+	pmcs_wr_topunit(pwp, PMCS_ERROR_INT_STAT,
+	    pmcs_rd_topunit(pwp, PMCS_ERROR_INT_STAT));
+	drv_usecwait(10);
+
+	sfrbits = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
+	    PMCS_MSGU_AAP_SFR_PROGRESS;
+	sfrbits ^= PMCS_MSGU_AAP_SFR_PROGRESS;
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "PMCS_MSGU_HOST_SCRATCH0 %08x -> %08x",
+	    pmcs_rd_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0), HST_SFT_RESET_SIG);
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_HOST_SCRATCH0, HST_SFT_RESET_SIG);
+
+	/*
+	 * Step 3
+	 */
+	gsm = pmcs_rd_gsm_reg(pwp, GSM_CFG_AND_RESET);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "GSM %08x -> %08x", gsm,
+	    gsm & ~PMCS_SOFT_RESET_BITS);
+	pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm & ~PMCS_SOFT_RESET_BITS);
+
+	/*
+	 * Step 4
+	 */
+	rapchk = pmcs_rd_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "READ_ADR_PARITY_CHK_EN %08x -> %08x",
+	    rapchk, 0);
+	pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, 0);
+	wapchk = pmcs_rd_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "WRITE_ADR_PARITY_CHK_EN %08x -> %08x",
+	    wapchk, 0);
+	pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, 0);
+	wdpchk = pmcs_rd_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "WRITE_DATA_PARITY_CHK_EN %08x -> %08x",
+	    wdpchk, 0);
+	pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, 0);
+
+	/*
+	 * Step 5
+	 */
+	drv_usecwait(100);
+
+	/*
+	 * Step 5.5 (Temporary workaround for 1.07.xx Beta)
+	 */
+	tsmode = pmcs_rd_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "GPIO TSMODE %08x -> %08x", tsmode,
+	    tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1));
+	pmcs_wr_gsm_reg(pwp, PMCS_GPIO_TRISTATE_MODE_ADDR,
+	    tsmode & ~(PMCS_GPIO_TSMODE_BIT0|PMCS_GPIO_TSMODE_BIT1));
+	drv_usecwait(10);
+
+	/*
+	 * Step 6
+	 */
+	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "SPC_RESET %08x -> %08x", spc,
+	    spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
+	pmcs_wr_topunit(pwp, PMCS_SPC_RESET,
+	    spc & ~(PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
+	drv_usecwait(10);
+
+	/*
+	 * Step 7
+	 */
+	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "SPC_RESET %08x -> %08x", spc,
+	    spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
+	pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc & ~(BDMA_CORE_RSTB|OSSP_RSTB));
+
+	/*
+	 * Step 8
+	 */
+	drv_usecwait(100);
+
+	/*
+	 * Step 9
+	 */
+	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "SPC_RESET %08x -> %08x", spc,
+	    spc | (BDMA_CORE_RSTB|OSSP_RSTB));
+	pmcs_wr_topunit(pwp, PMCS_SPC_RESET, spc | (BDMA_CORE_RSTB|OSSP_RSTB));
+
+	/*
+	 * Step 10
+	 */
+	drv_usecwait(100);
+
+	/*
+	 * Step 11
+	 */
+	gsm = pmcs_rd_gsm_reg(pwp, GSM_CFG_AND_RESET);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "GSM %08x -> %08x", gsm,
+	    gsm | PMCS_SOFT_RESET_BITS);
+	pmcs_wr_gsm_reg(pwp, GSM_CFG_AND_RESET, gsm | PMCS_SOFT_RESET_BITS);
+	drv_usecwait(10);
+
+	/*
+	 * Step 12
+	 */
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "READ_ADR_PARITY_CHK_EN %08x -> %08x",
+	    pmcs_rd_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN), rapchk);
+	pmcs_wr_gsm_reg(pwp, READ_ADR_PARITY_CHK_EN, rapchk);
+	drv_usecwait(10);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "WRITE_ADR_PARITY_CHK_EN %08x -> %08x",
+	    pmcs_rd_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN), wapchk);
+	pmcs_wr_gsm_reg(pwp, WRITE_ADR_PARITY_CHK_EN, wapchk);
+	drv_usecwait(10);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "WRITE_DATA_PARITY_CHK_EN %08x -> %08x",
+	    pmcs_rd_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN), wapchk);
+	pmcs_wr_gsm_reg(pwp, WRITE_DATA_PARITY_CHK_EN, wdpchk);
+	drv_usecwait(10);
+
+	/*
+	 * Step 13
+	 */
+	spc = pmcs_rd_topunit(pwp, PMCS_SPC_RESET);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "SPC_RESET %08x -> %08x", spc,
+	    spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
+	pmcs_wr_topunit(pwp, PMCS_SPC_RESET,
+	    spc | (PCS_IOP_SS_RSTB|PCS_AAP1_SS_RSTB));
+
+	/*
+	 * Step 14
+	 */
+	drv_usecwait(100);
+
+	/*
+	 * Step 15
+	 */
+	for (spc = 0, i = 0; i < 1000; i++) {
+		drv_usecwait(1000);
+		spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
+		if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) == sfrbits) {
+			break;
+		}
+	}
+
+	if ((spc & PMCS_MSGU_AAP_SFR_PROGRESS) != sfrbits) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "SFR didn't toggle (sfr 0x%x)", spc);
+		pwp->state = STATE_DEAD;
+		pwp->blocked = 0;
+		if (pwp->locks_initted) {
+			mutex_exit(&pwp->lock);
+		}
+		return (-1);
+	}
+
+	/*
+	 * Step 16
+	 */
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff);
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
+
+	/*
+	 * Wait for up to 5 seconds for AAP state to come either ready or error.
+	 */
+	for (i = 0; i < 50; i++) {
+		spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1) &
+		    PMCS_MSGU_AAP_STATE_MASK;
+		if (spc == PMCS_MSGU_AAP_STATE_ERROR ||
+		    spc == PMCS_MSGU_AAP_STATE_READY) {
+			break;
+		}
+		drv_usecwait(100000);
+	}
+	spc = pmcs_rd_msgunit(pwp, PMCS_MSGU_SCRATCH1);
+	if ((spc & PMCS_MSGU_AAP_STATE_MASK) != PMCS_MSGU_AAP_STATE_READY) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "soft reset failed (state 0x%x)", spc);
+		pwp->state = STATE_DEAD;
+		pwp->blocked = 0;
+		if (pwp->locks_initted) {
+			mutex_exit(&pwp->lock);
+		}
+		return (-1);
+	}
+
+
+	if (pwp->state == STATE_DEAD || pwp->state == STATE_UNPROBING ||
+	    pwp->state == STATE_PROBING || pwp->locks_initted == 0) {
+		pwp->blocked = 0;
+		if (pwp->locks_initted) {
+			mutex_exit(&pwp->lock);
+		}
+		return (0);
+	}
+
+	/*
+	 * Return at this point if we dont need to startup.
+	 */
+	if (no_restart) {
+		return (0);
+	}
+
+	ASSERT(pwp->locks_initted != 0);
+
+	/*
+	 * Clean up various soft state.
+	 */
+	bzero(pwp->ports, sizeof (pwp->ports));
+
+	pmcs_free_all_phys(pwp, pwp->root_phys);
+
+	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
+		pmcs_lock_phy(pptr);
+		pmcs_clear_phy(pwp, pptr);
+		pmcs_unlock_phy(pptr);
+	}
+
+	if (pwp->targets) {
+		for (i = 0; i < pwp->max_dev; i++) {
+			pmcs_xscsi_t *xp = pwp->targets[i];
+
+			if (xp == NULL) {
+				continue;
+			}
+			mutex_enter(&xp->statlock);
+			if (xp->assigned == 0 && xp->dying == 0) {
+				if (xp->new) {
+					xp->new = 0;
+					xp->ca = 0;
+					xp->qdepth = 0;
+					xp->phy = NULL;
+				}
+				mutex_exit(&xp->statlock);
+				continue;
+			}
+			xp->tagmap = 0;
+			xp->dying = 1;
+			xp->assigned = 0;
+			mutex_exit(&xp->statlock);
+			SCHEDULE_WORK(pwp, PMCS_WORK_REM_DEVICES);
+		}
+	}
+
+	bzero(pwp->shadow_iqpi, sizeof (pwp->shadow_iqpi));
+	for (i = 0; i < PMCS_NIQ; i++) {
+		if (pwp->iqp[i]) {
+			bzero(pwp->iqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
+			pmcs_wr_iqpi(pwp, i, 0);
+			pmcs_wr_iqci(pwp, i, 0);
+		}
+	}
+	for (i = 0; i < PMCS_NOQ; i++) {
+		if (pwp->oqp[i]) {
+			bzero(pwp->oqp[i], PMCS_QENTRY_SIZE * pwp->ioq_depth);
+			pmcs_wr_oqpi(pwp, i, 0);
+			pmcs_wr_oqci(pwp, i, 0);
+		}
+
+	}
+	if (pwp->fwlogp) {
+		bzero(pwp->fwlogp, PMCS_FWLOG_SIZE);
+	}
+	STAILQ_INIT(&pwp->wf);
+	bzero(pwp->work, sizeof (pmcwork_t) * pwp->max_cmd);
+	for (i = 0; i < pwp->max_cmd - 1; i++) {
+		pmcwork_t *pwrk = &pwp->work[i];
+		STAILQ_INSERT_TAIL(&pwp->wf, pwrk, next);
+	}
+
+	/*
+	 * Clear out any leftover commands sitting in the work list
+	 */
+	for (i = 0; i < pwp->max_cmd; i++) {
+		pmcwork_t *pwrk = &pwp->work[i];
+		mutex_enter(&pwrk->lock);
+		if (pwrk->state == PMCS_WORK_STATE_ONCHIP) {
+			switch (PMCS_TAG_TYPE(pwrk->htag)) {
+			case PMCS_TAG_TYPE_WAIT:
+				mutex_exit(&pwrk->lock);
+				break;
+			case PMCS_TAG_TYPE_CBACK:
+			case PMCS_TAG_TYPE_NONE:
+				pmcs_pwork(pwp, pwrk);
+				break;
+			default:
+				break;
+			}
+		} else if (pwrk->state == PMCS_WORK_STATE_IOCOMPQ) {
+			pwrk->dead = 1;
+			mutex_exit(&pwrk->lock);
+		} else {
+			/*
+			 * The other states of NIL, READY and INTR
+			 * should not be visible outside of a lock being held.
+			 */
+			pmcs_pwork(pwp, pwrk);
+		}
+	}
+
+	/*
+	 * Restore Interrupt Mask
+	 */
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask);
+	pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff);
+
+	pwp->blocked = 0;
+	pwp->mpi_table_setup = 0;
+	mutex_exit(&pwp->lock);
+
+	/*
+	 * Set up MPI again.
+	 */
+	if (pmcs_setup(pwp)) {
+		msg = "unable to setup MPI tables again";
+		goto fail_restart;
+	}
+	pmcs_report_fwversion(pwp);
+
+	/*
+	 * Restart MPI
+	 */
+	if (pmcs_start_mpi(pwp)) {
+		msg = "unable to restart MPI again";
+		goto fail_restart;
+	}
+
+	mutex_enter(&pwp->lock);
+	pwp->blocked = 0;
+	SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
+	mutex_exit(&pwp->lock);
+
+	/*
+	 * Run any completions
+	 */
+	PMCS_CQ_RUN(pwp);
+
+	/*
+	 * Delay
+	 */
+	drv_usecwait(1000000);
+	return (0);
+
+fail_restart:
+	mutex_enter(&pwp->lock);
+	pwp->state = STATE_DEAD;
+	mutex_exit(&pwp->lock);
+	pmcs_prt(pwp, PMCS_PRT_ERR, "%s: Failed: %s", __func__, msg);
+	return (-1);
+}
+
+/*
+ * Reset a device or a logical unit.
+ */
+int
+pmcs_reset_dev(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint64_t lun)
+{
+	int rval = 0;
+
+	if (pptr == NULL) {
+		return (ENXIO);
+	}
+
+	pmcs_lock_phy(pptr);
+	if (pptr->dtype == SAS) {
+		/*
+		 * Some devices do not support SAS_I_T_NEXUS_RESET as
+		 * it is not a mandatory (in SAM4) task management
+		 * function, while LOGIC_UNIT_RESET is mandatory.
+		 *
+		 * The problem here is that we need to iterate over
+		 * all known LUNs to emulate the semantics of
+		 * "RESET_TARGET".
+		 *
+		 * XXX: FIX ME
+		 */
+		if (lun == (uint64_t)-1) {
+			lun = 0;
+		}
+		rval = pmcs_ssp_tmf(pwp, pptr, SAS_LOGICAL_UNIT_RESET, 0, lun,
+		    NULL);
+	} else if (pptr->dtype == SATA) {
+		if (lun != 0ull) {
+			pmcs_unlock_phy(pptr);
+			return (EINVAL);
+		}
+		rval = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_LINK_RESET);
+	} else {
+		pmcs_unlock_phy(pptr);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: cannot reset a SMP device yet (%s)",
+		    __func__, pptr->path);
+		return (EINVAL);
+	}
+
+	/*
+	 * Now harvest any commands killed by this action
+	 * by issuing an ABORT for all commands on this device.
+	 *
+	 * We do this even if the the tmf or reset fails (in case there
+	 * are any dead commands around to be harvested *anyway*).
+	 * We don't have to await for the abort to complete.
+	 */
+	if (pmcs_abort(pwp, pptr, 0, 1, 0)) {
+		pptr->abort_pending = 1;
+		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
+	}
+
+	pmcs_unlock_phy(pptr);
+	return (rval);
+}
+
+/*
+ * Called with PHY locked.
+ */
+static int
+pmcs_get_device_handle(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	if (pptr->valid_device_id == 0) {
+		int result = pmcs_register_device(pwp, pptr);
+
+		/*
+		 * If we changed while registering, punt
+		 */
+		if (pptr->changed) {
+			RESTART_DISCOVERY(pwp);
+			return (-1);
+		}
+
+		/*
+		 * If we had a failure to register, check against errors.
+		 * An ENOMEM error means we just retry (temp resource shortage).
+		 */
+		if (result == ENOMEM) {
+			PHY_CHANGED(pwp, pptr);
+			RESTART_DISCOVERY(pwp);
+			return (-1);
+		}
+
+		/*
+		 * An ETIMEDOUT error means we retry (if our counter isn't
+		 * exhausted)
+		 */
+		if (result == ETIMEDOUT) {
+			if (ddi_get_lbolt() < pptr->config_stop) {
+				PHY_CHANGED(pwp, pptr);
+				RESTART_DISCOVERY(pwp);
+			} else {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+				    "%s: Retries exhausted for %s, killing",
+				    __func__, pptr->path);
+				pptr->config_stop = 0;
+				pmcs_kill_changed(pwp, pptr, 0);
+			}
+			return (-1);
+		}
+		/*
+		 * Other errors or no valid device id is fatal, but don't
+		 * preclude a future action.
+		 */
+		if (result || pptr->valid_device_id == 0) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s could not "
+			    "be registered", __func__,  pptr->path);
+			return (-1);
+		}
+	}
+	return (0);
+}
+
+int
+pmcs_iport_tgtmap_create(pmcs_iport_t *iport)
+{
+	ASSERT(iport);
+	if (iport == NULL)
+		return (B_FALSE);
+
+	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, "%s", __func__);
+
+	/* create target map */
+	if (scsi_hba_tgtmap_create(iport->dip, SCSI_TM_FULLSET, tgtmap_usec,
+	    2048, NULL, NULL, NULL, &iport->iss_tgtmap) != DDI_SUCCESS) {
+		pmcs_prt(iport->pwp, PMCS_PRT_DEBUG,
+		    "%s: failed to create tgtmap", __func__);
+		return (B_FALSE);
+	}
+	return (B_TRUE);
+}
+
+int
+pmcs_iport_tgtmap_destroy(pmcs_iport_t *iport)
+{
+	ASSERT(iport && iport->iss_tgtmap);
+	if ((iport == NULL) || (iport->iss_tgtmap == NULL))
+		return (B_FALSE);
+
+	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_MAP, "%s", __func__);
+
+	/* destroy target map */
+	scsi_hba_tgtmap_destroy(iport->iss_tgtmap);
+	return (B_TRUE);
+}
+
+/*
+ * Query the phymap and populate the iport handle passed in.
+ * Called with iport lock held.
+ */
+int
+pmcs_iport_configure_phys(pmcs_iport_t *iport)
+{
+	pmcs_hw_t		*pwp;
+	pmcs_phy_t		*pptr;
+	sas_phymap_phys_t	*phys;
+	int			phynum;
+	int			inst;
+
+	ASSERT(iport);
+	ASSERT(mutex_owned(&iport->lock));
+	pwp = iport->pwp;
+	ASSERT(pwp);
+	inst = ddi_get_instance(iport->dip);
+
+	mutex_enter(&pwp->lock);
+	ASSERT(pwp->root_phys != NULL);
+
+	/*
+	 * Query the phymap regarding the phys in this iport and populate
+	 * the iport's phys list. Hereafter this list is maintained via
+	 * port up and down events in pmcs_intr.c
+	 */
+	ASSERT(list_is_empty(&iport->phys));
+	phys = sas_phymap_ua2phys(pwp->hss_phymap, iport->ua);
+	while ((phynum = sas_phymap_phys_next(phys)) != -1) {
+		/* Grab the phy pointer from root_phys */
+		pptr = pwp->root_phys + phynum;
+		ASSERT(pptr);
+		pmcs_lock_phy(pptr);
+		ASSERT(pptr->phynum == phynum);
+
+		/*
+		 * Set a back pointer in the phy to this iport.
+		 */
+		pptr->iport = iport;
+
+		/*
+		 * If this phy is the primary, set a pointer to it on our
+		 * iport handle, and set our portid from it.
+		 */
+		if (!pptr->subsidiary) {
+			iport->pptr = pptr;
+			iport->portid = pptr->portid;
+		}
+
+		/*
+		 * Finally, insert the phy into our list
+		 */
+		pmcs_add_phy_to_iport(iport, pptr);
+		pmcs_unlock_phy(pptr);
+
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: found phy %d [0x%p] "
+		    "on iport%d, refcnt(%d)", __func__, phynum,
+		    (void *)pptr, inst, iport->refcnt);
+	}
+	mutex_exit(&pwp->lock);
+	sas_phymap_phys_free(phys);
+	RESTART_DISCOVERY(pwp);
+	return (DDI_SUCCESS);
+}
+
+/*
+ * Return the iport that ua is associated with, or NULL.  If an iport is
+ * returned, it will be held and the caller must release the hold.
+ */
+static pmcs_iport_t *
+pmcs_get_iport_by_ua(pmcs_hw_t *pwp, char *ua)
+{
+	pmcs_iport_t	*iport = NULL;
+
+	rw_enter(&pwp->iports_lock, RW_READER);
+	for (iport = list_head(&pwp->iports);
+	    iport != NULL;
+	    iport = list_next(&pwp->iports, iport)) {
+		mutex_enter(&iport->lock);
+		if (strcmp(iport->ua, ua) == 0) {
+			mutex_exit(&iport->lock);
+			mutex_enter(&iport->refcnt_lock);
+			iport->refcnt++;
+			mutex_exit(&iport->refcnt_lock);
+			break;
+		}
+		mutex_exit(&iport->lock);
+	}
+	rw_exit(&pwp->iports_lock);
+
+	return (iport);
+}
+
+/*
+ * Return the iport that pptr is associated with, or NULL.
+ * If an iport is returned, there is a hold that the caller must release.
+ */
+pmcs_iport_t *
+pmcs_get_iport_by_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	pmcs_iport_t	*iport = NULL;
+	char		*ua;
+
+	ua = sas_phymap_lookup_ua(pwp->hss_phymap, pwp->sas_wwns[0],
+	    pmcs_barray2wwn(pptr->sas_address));
+	if (ua) {
+		iport = pmcs_get_iport_by_ua(pwp, ua);
+		if (iport) {
+			mutex_enter(&iport->lock);
+			iport->ua_state = UA_ACTIVE;
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: "
+			    "found iport [0x%p] on ua (%s) for phy [0x%p], "
+			    "refcnt (%d)", __func__, (void *)iport, ua,
+			    (void *)pptr, iport->refcnt);
+			mutex_exit(&iport->lock);
+		}
+	}
+
+	return (iport);
+}
+
+void
+pmcs_rele_iport(pmcs_iport_t *iport)
+{
+	/*
+	 * Release a refcnt on this iport. If this is the last reference,
+	 * signal the potential waiter in pmcs_iport_unattach().
+	 */
+	ASSERT(iport->refcnt > 0);
+	mutex_enter(&iport->refcnt_lock);
+	iport->refcnt--;
+	mutex_exit(&iport->refcnt_lock);
+	if (iport->refcnt == 0) {
+		cv_signal(&iport->refcnt_cv);
+	}
+	pmcs_prt(iport->pwp, PMCS_PRT_DEBUG_CONFIG, "%s: iport [0x%p] "
+	    "refcnt (%d)", __func__, (void *)iport, iport->refcnt);
+}
+
+void
+pmcs_phymap_activate(void *arg, char *ua, void **privp)
+{
+	_NOTE(ARGUNUSED(privp));
+	pmcs_hw_t	*pwp = arg;
+	pmcs_iport_t	*iport = NULL;
+
+	mutex_enter(&pwp->lock);
+	if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) {
+		mutex_exit(&pwp->lock);
+		return;
+	}
+	pwp->phymap_active++;
+	mutex_exit(&pwp->lock);
+
+	if (scsi_hba_iportmap_iport_add(pwp->hss_iportmap, ua, NULL) !=
+	    DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, "%s: failed to add "
+		    "iport handle on unit address [%s]", __func__, ua);
+	} else {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, "%s: phymap_active count "
+		    "(%d), added iport handle on unit address [%s]", __func__,
+		    pwp->phymap_active, ua);
+	}
+
+	/* Set the HBA softstate as our private data for this unit address */
+	*privp = (void *)pwp;
+
+	/*
+	 * We are waiting on attach for this iport node, unless it is still
+	 * attached. This can happen if a consumer has an outstanding open
+	 * on our iport node, but the port is down.  If this is the case, we
+	 * need to configure our iport here for reuse.
+	 */
+	iport = pmcs_get_iport_by_ua(pwp, ua);
+	if (iport) {
+		mutex_enter(&iport->lock);
+		if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: "
+			    "failed to configure phys on iport [0x%p] at "
+			    "unit address (%s)", __func__, (void *)iport, ua);
+		}
+		iport->ua_state = UA_ACTIVE;
+		pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
+		    &iport->nphy);
+		mutex_exit(&iport->lock);
+		pmcs_rele_iport(iport);
+	}
+
+}
+
+void
+pmcs_phymap_deactivate(void *arg, char *ua, void *privp)
+{
+	_NOTE(ARGUNUSED(privp));
+	pmcs_hw_t	*pwp = arg;
+	pmcs_iport_t	*iport;
+
+	mutex_enter(&pwp->lock);
+	pwp->phymap_active--;
+	mutex_exit(&pwp->lock);
+
+	if (scsi_hba_iportmap_iport_remove(pwp->hss_iportmap, ua) !=
+	    DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, "%s: failed to remove "
+		    "iport handle on unit address [%s]", __func__, ua);
+	} else {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP, "%s: phymap_active "
+		    "count (%d), removed iport handle on unit address [%s]",
+		    __func__, pwp->phymap_active, ua);
+	}
+
+	iport = pmcs_get_iport_by_ua(pwp, ua);
+
+	if (iport == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: failed lookup of "
+		    "iport handle on unit address (%s)", __func__, ua);
+		return;
+	}
+
+	mutex_enter(&iport->lock);
+	iport->ua_state = UA_INACTIVE;
+	iport->portid = PMCS_IPORT_INVALID_PORT_ID;
+	pmcs_remove_phy_from_iport(iport, NULL);
+	mutex_exit(&iport->lock);
+	pmcs_rele_iport(iport);
+}
+
+/*
+ * Top-level discovery function
+ */
+void
+pmcs_discover(pmcs_hw_t *pwp)
+{
+	pmcs_phy_t		*pptr;
+	pmcs_phy_t		*root_phy;
+
+	DTRACE_PROBE2(pmcs__discover__entry, ulong_t, pwp->work_flags,
+	    boolean_t, pwp->config_changed);
+
+	mutex_enter(&pwp->lock);
+
+	if (pwp->state != STATE_RUNNING) {
+		mutex_exit(&pwp->lock);
+		return;
+	}
+
+	/* Ensure we have at least one phymap active */
+	if (pwp->phymap_active == 0) {
+		mutex_exit(&pwp->lock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: phymap inactive, exiting", __func__);
+		return;
+	}
+
+	mutex_exit(&pwp->lock);
+
+	/*
+	 * If no iports have attached, but we have PHYs that are up, we
+	 * are waiting for iport attach to complete.  Restart discovery.
+	 */
+	rw_enter(&pwp->iports_lock, RW_READER);
+	if (!pwp->iports_attached) {
+		rw_exit(&pwp->iports_lock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: no iports attached, retry discovery", __func__);
+		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
+		return;
+	}
+	rw_exit(&pwp->iports_lock);
+
+	mutex_enter(&pwp->config_lock);
+	if (pwp->configuring) {
+		mutex_exit(&pwp->config_lock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: configuration already in progress", __func__);
+		return;
+	}
+
+	if (pmcs_acquire_scratch(pwp, B_FALSE)) {
+		mutex_exit(&pwp->config_lock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: cannot allocate scratch", __func__);
+		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
+		return;
+	}
+
+	pwp->configuring = 1;
+	pwp->config_changed = B_FALSE;
+	mutex_exit(&pwp->config_lock);
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Discovery begin");
+
+	/*
+	 * The order of the following traversals is important.
+	 *
+	 * The first one checks for changed expanders.
+	 *
+	 * The second one aborts commands for dead devices and deregisters them.
+	 *
+	 * The third one clears the contents of dead expanders from the tree
+	 *
+	 * The fourth one clears now dead devices in expanders that remain.
+	 */
+
+	/*
+	 * 1. Check expanders marked changed (but not dead) to see if they still
+	 * have the same number of phys and the same SAS address. Mark them,
+	 * their subsidiary phys (if wide) and their descendents dead if
+	 * anything has changed. Check the devices they contain to see if
+	 * *they* have changed. If they've changed from type NOTHING we leave
+	 * them marked changed to be configured later (picking up a new SAS
+	 * address and link rate if possible). Otherwise, any change in type,
+	 * SAS address or removal of target role will cause us to mark them
+	 * (and their descendents) as dead (and cause any pending commands
+	 * and associated devices to be removed).
+	 */
+	root_phy = pwp->root_phys;
+	if (pmcs_check_expanders(pwp, root_phy) == B_TRUE) {
+		goto out;
+	}
+
+	/*
+	 * 2. Descend the tree looking for dead devices and kill them
+	 * by aborting all active commands and then deregistering them.
+	 */
+	if (pmcs_kill_devices(pwp, root_phy)) {
+		goto out;
+	}
+
+	/*
+	 * 3. Check for dead expanders and remove their children from the tree.
+	 * By the time we get here, the devices and commands for them have
+	 * already been terminated and removed.
+	 *
+	 * We do this independent of the configuration count changing so we can
+	 * free any dead device PHYs that were discovered while checking
+	 * expanders. We ignore any subsidiary phys as pmcs_clear_expander
+	 * will take care of those.
+	 *
+	 * NOTE: pmcs_clear_expander requires softstate lock
+	 */
+	mutex_enter(&pwp->lock);
+	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
+		/*
+		 * Call pmcs_clear_expander for every root PHY.  It will
+		 * recurse and determine which (if any) expanders actually
+		 * need to be cleared.
+		 */
+		pmcs_lock_phy(pptr);
+		pmcs_clear_expander(pwp, pptr, 0);
+		pmcs_unlock_phy(pptr);
+	}
+	mutex_exit(&pwp->lock);
+
+	/*
+	 * 4. Check for dead devices and nullify them. By the time we get here,
+	 * the devices and commands for them have already been terminated
+	 * and removed. This is different from step 2 in that this just nulls
+	 * phys that are part of expanders that are still here but used to
+	 * be something but are no longer something (e.g., after a pulled
+	 * disk drive). Note that dead expanders had their contained phys
+	 * removed from the tree- here, the expanders themselves are
+	 * nullified (unless they were removed by being contained in another
+	 * expander phy).
+	 */
+	pmcs_clear_phys(pwp, root_phy);
+
+	/*
+	 * 5. Now check for and configure new devices.
+	 */
+	if (pmcs_configure_new_devices(pwp, root_phy)) {
+		goto restart;
+	}
+
+out:
+	DTRACE_PROBE2(pmcs__discover__exit, ulong_t, pwp->work_flags,
+	    boolean_t, pwp->config_changed);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Discovery end");
+
+	mutex_enter(&pwp->config_lock);
+
+	if (pwp->config_changed == B_FALSE) {
+		/*
+		 * Observation is stable, report what we currently see to
+		 * the tgtmaps for delta processing. Start by setting
+		 * BEGIN on all tgtmaps.
+		 */
+		mutex_exit(&pwp->config_lock);
+		if (pmcs_report_observations(pwp) == B_FALSE) {
+			goto restart;
+		}
+		mutex_enter(&pwp->config_lock);
+	} else {
+		/*
+		 * If config_changed is TRUE, we need to reschedule
+		 * discovery now.
+		 */
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: Config has changed, will re-run discovery", __func__);
+		SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
+	}
+
+	pmcs_release_scratch(pwp);
+	pwp->configuring = 0;
+	mutex_exit(&pwp->config_lock);
+
+#ifdef DEBUG
+	pptr = pmcs_find_phy_needing_work(pwp, pwp->root_phys);
+	if (pptr != NULL) {
+		if (!WORK_IS_SCHEDULED(pwp, PMCS_WORK_DISCOVER)) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "PHY %s dead=%d changed=%d configured=%d "
+			    "but no work scheduled", pptr->path, pptr->dead,
+			    pptr->changed, pptr->configured);
+		}
+		pmcs_unlock_phy(pptr);
+	}
+#endif
+
+	return;
+
+restart:
+	/* Clean up and restart discovery */
+	pmcs_release_scratch(pwp);
+	mutex_enter(&pwp->config_lock);
+	pwp->configuring = 0;
+	RESTART_DISCOVERY_LOCKED(pwp);
+	mutex_exit(&pwp->config_lock);
+}
+
+/*
+ * Return any PHY that needs to have scheduled work done.  The PHY is returned
+ * locked.
+ */
+static pmcs_phy_t *
+pmcs_find_phy_needing_work(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	pmcs_phy_t *cphyp, *pnext;
+
+	while (pptr) {
+		pmcs_lock_phy(pptr);
+
+		if (pptr->changed || (pptr->dead && pptr->valid_device_id)) {
+			return (pptr);
+		}
+
+		pnext = pptr->sibling;
+
+		if (pptr->children) {
+			cphyp = pptr->children;
+			pmcs_unlock_phy(pptr);
+			cphyp = pmcs_find_phy_needing_work(pwp, cphyp);
+			if (cphyp) {
+				return (cphyp);
+			}
+		} else {
+			pmcs_unlock_phy(pptr);
+		}
+
+		pptr = pnext;
+	}
+
+	return (NULL);
+}
+
+/*
+ * Report current observations to SCSA.
+ */
+static boolean_t
+pmcs_report_observations(pmcs_hw_t *pwp)
+{
+	pmcs_iport_t		*iport;
+	scsi_hba_tgtmap_t	*tgtmap;
+	char			*ap;
+	pmcs_phy_t		*pptr;
+	uint64_t		wwn;
+
+	/*
+	 * Observation is stable, report what we currently see to the tgtmaps
+	 * for delta processing. Start by setting BEGIN on all tgtmaps.
+	 */
+	rw_enter(&pwp->iports_lock, RW_READER);
+	for (iport = list_head(&pwp->iports); iport != NULL;
+	    iport = list_next(&pwp->iports, iport)) {
+		/*
+		 * Unless we have at least one phy up, skip this iport.
+		 * Note we don't need to lock the iport for report_skip
+		 * since it is only used here.  We are doing the skip so that
+		 * the phymap and iportmap stabilization times are honored -
+		 * giving us the ability to recover port operation within the
+		 * stabilization time without unconfiguring targets using the
+		 * port.
+		 */
+		if (!sas_phymap_uahasphys(pwp->hss_phymap, iport->ua)) {
+			iport->report_skip = 1;
+			continue;		/* skip set_begin */
+		}
+		iport->report_skip = 0;
+
+		tgtmap = iport->iss_tgtmap;
+		ASSERT(tgtmap);
+		if (scsi_hba_tgtmap_set_begin(tgtmap) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,
+			    "%s: cannot set_begin tgtmap ", __func__);
+			rw_exit(&pwp->iports_lock);
+			return (B_FALSE);
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,
+		    "%s: set begin on tgtmap [0x%p]", __func__,
+		    (void *)tgtmap);
+	}
+	rw_exit(&pwp->iports_lock);
+
+	/*
+	 * Now, cycle through all levels of all phys and report
+	 * observations into their respective tgtmaps.
+	 */
+	pptr = pwp->root_phys;
+
+	while (pptr) {
+		pmcs_lock_phy(pptr);
+
+		/*
+		 * Skip PHYs that have nothing attached or are dead.
+		 */
+		if ((pptr->dtype == NOTHING) || pptr->dead) {
+			pmcs_unlock_phy(pptr);
+			pptr = pptr->sibling;
+			continue;
+		}
+
+		if (pptr->changed) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: oops, PHY %s changed; restart discovery",
+			    __func__, pptr->path);
+			pmcs_unlock_phy(pptr);
+			return (B_FALSE);
+		}
+
+		/*
+		 * Get the iport for this root PHY, then call the helper
+		 * to report observations for this iport's targets
+		 */
+		iport = pmcs_get_iport_by_phy(pwp, pptr);
+		if (iport == NULL) {
+			/* No iport for this tgt */
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: no iport for this target",
+			    __func__);
+			pmcs_unlock_phy(pptr);
+			pptr = pptr->sibling;
+			continue;
+		}
+
+		if (!iport->report_skip) {
+			if (pmcs_report_iport_observations(
+			    pwp, iport, pptr) == B_FALSE) {
+				pmcs_rele_iport(iport);
+				pmcs_unlock_phy(pptr);
+				return (B_FALSE);
+			}
+		}
+		pmcs_rele_iport(iport);
+		pmcs_unlock_phy(pptr);
+		pptr = pptr->sibling;
+	}
+
+	/*
+	 * The observation is complete, end sets. Note we will skip any
+	 * iports that are active, but have no PHYs in them (i.e. awaiting
+	 * unconfigure). Set to restart discovery if we find this.
+	 */
+	rw_enter(&pwp->iports_lock, RW_READER);
+	for (iport = list_head(&pwp->iports);
+	    iport != NULL;
+	    iport = list_next(&pwp->iports, iport)) {
+
+		if (iport->report_skip)
+			continue;		/* skip set_end */
+
+		tgtmap = iport->iss_tgtmap;
+		ASSERT(tgtmap);
+		if (scsi_hba_tgtmap_set_end(tgtmap, 0) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,
+			    "%s: cannot set_end tgtmap ", __func__);
+			rw_exit(&pwp->iports_lock);
+			return (B_FALSE);
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,
+		    "%s: set end on tgtmap [0x%p]", __func__,
+		    (void *)tgtmap);
+	}
+
+	/*
+	 * Now that discovery is complete, set up the necessary
+	 * DDI properties on each iport node.
+	 */
+	for (iport = list_head(&pwp->iports); iport != NULL;
+	    iport = list_next(&pwp->iports, iport)) {
+		/* Set up the DDI properties on each phy */
+		pmcs_smhba_set_phy_props(iport);
+
+		/* Set up the 'attached-port' property on the iport */
+		ap = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP);
+		mutex_enter(&iport->lock);
+		pptr = iport->pptr;
+		mutex_exit(&iport->lock);
+		if (pptr == NULL) {
+			/*
+			 * This iport is down, but has not been
+			 * removed from our list (unconfigured).
+			 * Set our value to '0'.
+			 */
+			(void) snprintf(ap, 1, "%s", "0");
+		} else {
+			/* Otherwise, set it to remote phy's wwn */
+			pmcs_lock_phy(pptr);
+			wwn = pmcs_barray2wwn(pptr->sas_address);
+			(void) scsi_wwn_to_wwnstr(wwn, 1, ap);
+			pmcs_unlock_phy(pptr);
+		}
+		if (ndi_prop_update_string(DDI_DEV_T_NONE, iport->dip,
+		    SCSI_ADDR_PROP_ATTACHED_PORT,  ap) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Failed to "
+			    "set prop ("SCSI_ADDR_PROP_ATTACHED_PORT")",
+			    __func__);
+		}
+		kmem_free(ap, PMCS_MAX_UA_SIZE);
+	}
+	rw_exit(&pwp->iports_lock);
+
+	return (B_TRUE);
+}
+
+/*
+ * Report observations into a particular iport's target map
+ *
+ * Called with phyp (and all descendents) locked
+ */
+static boolean_t
+pmcs_report_iport_observations(pmcs_hw_t *pwp, pmcs_iport_t *iport,
+    pmcs_phy_t *phyp)
+{
+	pmcs_phy_t		*lphyp;
+	scsi_hba_tgtmap_t	*tgtmap;
+	scsi_tgtmap_tgt_type_t	tgt_type;
+	char			*ua;
+	uint64_t		wwn;
+
+	tgtmap = iport->iss_tgtmap;
+	ASSERT(tgtmap);
+
+	lphyp = phyp;
+	while (lphyp) {
+		switch (lphyp->dtype) {
+		default:		/* Skip unknown PHYs. */
+			/* for non-root phys, skip to sibling */
+			goto next_phy;
+
+		case SATA:
+		case SAS:
+			tgt_type = SCSI_TGT_SCSI_DEVICE;
+			break;
+
+		case EXPANDER:
+			tgt_type = SCSI_TGT_SMP_DEVICE;
+			break;
+		}
+
+		if (lphyp->dead) {
+			goto next_phy;
+		}
+
+		wwn = pmcs_barray2wwn(lphyp->sas_address);
+		ua = scsi_wwn_to_wwnstr(wwn, 1, NULL);
+
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,
+		    "iport_observation: adding %s on tgtmap [0x%p] phy [0x%p]",
+		    ua, (void *)tgtmap, (void*)lphyp);
+
+		if (scsi_hba_tgtmap_set_add(tgtmap, tgt_type, ua, NULL) !=
+		    DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_MAP,
+			    "%s: failed to add address %s", __func__, ua);
+			scsi_free_wwnstr(ua);
+			return (B_FALSE);
+		}
+		scsi_free_wwnstr(ua);
+
+		if (lphyp->children) {
+			if (pmcs_report_iport_observations(pwp, iport,
+			    lphyp->children) == B_FALSE) {
+				return (B_FALSE);
+			}
+		}
+
+		/* for non-root phys, report siblings too */
+next_phy:
+		if (IS_ROOT_PHY(lphyp)) {
+			lphyp = NULL;
+		} else {
+			lphyp = lphyp->sibling;
+		}
+	}
+
+	return (B_TRUE);
+}
+
+/*
+ * Check for and configure new devices.
+ *
+ * If the changed device is a SATA device, add a SATA device.
+ *
+ * If the changed device is a SAS device, add a SAS device.
+ *
+ * If the changed device is an EXPANDER device, do a REPORT
+ * GENERAL SMP command to find out the number of contained phys.
+ *
+ * For each number of contained phys, allocate a phy, do a
+ * DISCOVERY SMP command to find out what kind of device it
+ * is and add it to the linked list of phys on the *next* level.
+ *
+ * NOTE: pptr passed in by the caller will be a root PHY
+ */
+static int
+pmcs_configure_new_devices(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	int rval = 0;
+	pmcs_iport_t *iport;
+	pmcs_phy_t *pnext, *orig_pptr = pptr, *root_phy, *pchild;
+
+	/*
+	 * First, walk through each PHY at this level
+	 */
+	while (pptr) {
+		pmcs_lock_phy(pptr);
+		pnext = pptr->sibling;
+
+		/*
+		 * Set the new dtype if it has changed
+		 */
+		if ((pptr->pend_dtype != NEW) &&
+		    (pptr->pend_dtype != pptr->dtype)) {
+			pptr->dtype = pptr->pend_dtype;
+		}
+
+		if (pptr->changed == 0 || pptr->dead || pptr->configured) {
+			goto next_phy;
+		}
+
+		/*
+		 * Confirm that this target's iport is configured
+		 */
+		root_phy = pmcs_get_root_phy(pptr);
+		iport = pmcs_get_iport_by_phy(pwp, root_phy);
+		if (iport == NULL) {
+			/* No iport for this tgt, restart */
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: iport not yet configured, "
+			    "retry discovery", __func__);
+			pnext = NULL;
+			rval = -1;
+			goto next_phy;
+		}
+
+		switch (pptr->dtype) {
+		case NOTHING:
+			pptr->changed = 0;
+			break;
+		case SATA:
+		case SAS:
+			pptr->iport = iport;
+			pmcs_new_tport(pwp, pptr);
+			break;
+		case EXPANDER:
+			pmcs_configure_expander(pwp, pptr, iport);
+			break;
+		}
+		pmcs_rele_iport(iport);
+
+		mutex_enter(&pwp->config_lock);
+		if (pwp->config_changed) {
+			mutex_exit(&pwp->config_lock);
+			pnext = NULL;
+			goto next_phy;
+		}
+		mutex_exit(&pwp->config_lock);
+
+next_phy:
+		pmcs_unlock_phy(pptr);
+		pptr = pnext;
+	}
+
+	if (rval != 0) {
+		return (rval);
+	}
+
+	/*
+	 * Now walk through each PHY again, recalling ourselves if they
+	 * have children
+	 */
+	pptr = orig_pptr;
+	while (pptr) {
+		pmcs_lock_phy(pptr);
+		pnext = pptr->sibling;
+		pchild = pptr->children;
+		pmcs_unlock_phy(pptr);
+
+		if (pchild) {
+			rval = pmcs_configure_new_devices(pwp, pchild);
+			if (rval != 0) {
+				break;
+			}
+		}
+
+		pptr = pnext;
+	}
+
+	return (rval);
+}
+
+/*
+ * Set all phys and descendent phys as changed if changed == B_TRUE, otherwise
+ * mark them all as not changed.
+ *
+ * Called with parent PHY locked.
+ */
+void
+pmcs_set_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, boolean_t changed,
+    int level)
+{
+	pmcs_phy_t *pptr;
+
+	if (level == 0) {
+		if (changed) {
+			PHY_CHANGED(pwp, parent);
+		} else {
+			parent->changed = 0;
+		}
+		if (parent->dtype == EXPANDER && parent->level) {
+			parent->width = 1;
+		}
+		if (parent->children) {
+			pmcs_set_changed(pwp, parent->children, changed,
+			    level + 1);
+		}
+	} else {
+		pptr = parent;
+		while (pptr) {
+			if (changed) {
+				PHY_CHANGED(pwp, pptr);
+			} else {
+				pptr->changed = 0;
+			}
+			if (pptr->dtype == EXPANDER && pptr->level) {
+				pptr->width = 1;
+			}
+			if (pptr->children) {
+				pmcs_set_changed(pwp, pptr->children, changed,
+				    level + 1);
+			}
+			pptr = pptr->sibling;
+		}
+	}
+}
+
+/*
+ * Take the passed phy mark it and its descendants as dead.
+ * Fire up reconfiguration to abort commands and bury it.
+ *
+ * Called with the parent PHY locked.
+ */
+void
+pmcs_kill_changed(pmcs_hw_t *pwp, pmcs_phy_t *parent, int level)
+{
+	pmcs_phy_t *pptr = parent;
+
+	while (pptr) {
+		pptr->link_rate = 0;
+		pptr->abort_sent = 0;
+		pptr->abort_pending = 1;
+		SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
+		pptr->need_rl_ext = 0;
+
+		if (pptr->dead == 0) {
+			PHY_CHANGED(pwp, pptr);
+			RESTART_DISCOVERY(pwp);
+		}
+
+		pptr->dead = 1;
+
+		if (pptr->children) {
+			pmcs_kill_changed(pwp, pptr->children, level + 1);
+		}
+
+		/*
+		 * Only kill siblings at level > 0
+		 */
+		if (level == 0) {
+			return;
+		}
+
+		pptr = pptr->sibling;
+	}
+}
+
+/*
+ * Go through every PHY and clear any that are dead (unless they're expanders)
+ */
+static void
+pmcs_clear_phys(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	pmcs_phy_t *pnext, *phyp;
+
+	phyp = pptr;
+	while (phyp) {
+		if (IS_ROOT_PHY(phyp)) {
+			pmcs_lock_phy(phyp);
+		}
+
+		if ((phyp->dtype != EXPANDER) && phyp->dead) {
+			pmcs_clear_phy(pwp, phyp);
+		}
+
+		if (phyp->children) {
+			pmcs_clear_phys(pwp, phyp->children);
+		}
+
+		pnext = phyp->sibling;
+
+		if (IS_ROOT_PHY(phyp)) {
+			pmcs_unlock_phy(phyp);
+		}
+
+		phyp = pnext;
+	}
+}
+
+/*
+ * Clear volatile parts of a phy.  Called with PHY locked.
+ */
+void
+pmcs_clear_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s", __func__, pptr->path);
+	ASSERT(mutex_owned(&pptr->phy_lock));
+	/* keep sibling */
+	/* keep children */
+	/* keep parent */
+	pptr->device_id = PMCS_INVALID_DEVICE_ID;
+	/* keep hw_event_ack */
+	pptr->ncphy = 0;
+	/* keep phynum */
+	pptr->width = 0;
+	pptr->ds_recovery_retries = 0;
+	/* keep dtype */
+	pptr->config_stop = 0;
+	pptr->spinup_hold = 0;
+	pptr->atdt = 0;
+	/* keep portid */
+	pptr->link_rate = 0;
+	pptr->valid_device_id = 0;
+	pptr->abort_sent = 0;
+	pptr->abort_pending = 0;
+	pptr->need_rl_ext = 0;
+	pptr->subsidiary = 0;
+	pptr->configured = 0;
+	/* Only mark dead if it's not a root PHY and its dtype isn't NOTHING */
+	/* XXX: What about directly attached disks? */
+	if (!IS_ROOT_PHY(pptr) && (pptr->dtype != NOTHING))
+		pptr->dead = 1;
+	pptr->changed = 0;
+	/* keep SAS address */
+	/* keep path */
+	/* keep ref_count */
+	/* Don't clear iport on root PHYs - they are handled in pmcs_intr.c */
+	if (!IS_ROOT_PHY(pptr)) {
+		pptr->iport = NULL;
+	}
+}
+
+/*
+ * Allocate softstate for this target if there isn't already one.  If there
+ * is, just redo our internal configuration.  If it is actually "new", we'll
+ * soon get a tran_tgt_init for it.
+ *
+ * Called with PHY locked.
+ */
+static void
+pmcs_new_tport(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: phy 0x%p @ %s", __func__,
+	    (void *)pptr, pptr->path);
+
+	if (pmcs_configure_phy(pwp, pptr) == B_FALSE) {
+		/*
+		 * If the config failed, mark the PHY as changed.
+		 */
+		PHY_CHANGED(pwp, pptr);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: pmcs_configure_phy failed for phy 0x%p", __func__,
+		    (void *)pptr);
+		return;
+	}
+
+	/* Mark PHY as no longer changed */
+	pptr->changed = 0;
+
+	/*
+	 * If the PHY has no target pointer, see if there's a dead PHY that
+	 * matches.
+	 */
+	if (pptr->target == NULL) {
+		pmcs_reap_dead_phy(pptr);
+	}
+
+	/*
+	 * Only assign the device if there is a target for this PHY with a
+	 * matching SAS address.  If an iport is disconnected from one piece
+	 * of storage and connected to another within the iport stabilization
+	 * time, we can get the PHY/target mismatch situation.
+	 *
+	 * Otherwise, it'll get done in tran_tgt_init.
+	 */
+	if (pptr->target) {
+		mutex_enter(&pptr->target->statlock);
+		if (pmcs_phy_target_match(pptr) == B_FALSE) {
+			mutex_exit(&pptr->target->statlock);
+			if (!IS_ROOT_PHY(pptr)) {
+				pmcs_dec_phy_ref_count(pptr);
+			}
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: Not assigning existing tgt %p for PHY %p "
+			    "(WWN mismatch)", __func__, (void *)pptr->target,
+			    (void *)pptr);
+			pptr->target = NULL;
+			return;
+		}
+
+		if (!pmcs_assign_device(pwp, pptr->target)) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: pmcs_assign_device failed for target 0x%p",
+			    __func__, (void *)pptr->target);
+		}
+		mutex_exit(&pptr->target->statlock);
+	}
+}
+
+/*
+ * Called with PHY lock held.
+ */
+static boolean_t
+pmcs_configure_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	char *dtype;
+
+	ASSERT(mutex_owned(&pptr->phy_lock));
+
+	/*
+	 * Mark this device as no longer changed.
+	 */
+	pptr->changed = 0;
+
+	/*
+	 * If we don't have a device handle, get one.
+	 */
+	if (pmcs_get_device_handle(pwp, pptr)) {
+		return (B_FALSE);
+	}
+
+	pptr->configured = 1;
+
+	switch (pptr->dtype) {
+	case SAS:
+		dtype = "SAS";
+		break;
+	case SATA:
+		dtype = "SATA";
+		break;
+	case EXPANDER:
+		dtype = "SMP";
+		break;
+	default:
+		dtype = "???";
+	}
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "config_dev: %s dev %s "
+	    SAS_ADDR_FMT " dev id 0x%x lr 0x%x", dtype, pptr->path,
+	    SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate);
+
+	return (B_TRUE);
+}
+
+/*
+ * Called with PHY locked
+ */
+static void
+pmcs_configure_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, pmcs_iport_t *iport)
+{
+	pmcs_phy_t *ctmp, *clist = NULL, *cnext;
+	int result, i, nphy = 0;
+	boolean_t root_phy = B_FALSE;
+
+	ASSERT(iport);
+
+	/*
+	 * Step 1- clear our "changed" bit. If we need to retry/restart due
+	 * to resource shortages, we'll set it again. While we're doing
+	 * configuration, other events may set it again as well.  If the PHY
+	 * is a root PHY and is currently marked as having changed, reset the
+	 * config_stop timer as well.
+	 */
+	if (IS_ROOT_PHY(pptr) && pptr->changed) {
+		pptr->config_stop = ddi_get_lbolt() +
+		    drv_usectohz(PMCS_MAX_CONFIG_TIME);
+	}
+	pptr->changed = 0;
+
+	/*
+	 * Step 2- make sure we don't overflow
+	 */
+	if (pptr->level == PMCS_MAX_XPND-1) {
+		pmcs_prt(pwp, PMCS_PRT_WARN,
+		    "%s: SAS expansion tree too deep", __func__);
+		return;
+	}
+
+	/*
+	 * Step 3- Check if this expander is part of a wide phy that has
+	 * already been configured.
+	 *
+	 * This is known by checking this level for another EXPANDER device
+	 * with the same SAS address and isn't already marked as a subsidiary
+	 * phy and a parent whose SAS address is the same as our SAS address
+	 * (if there are parents).
+	 */
+	if (!IS_ROOT_PHY(pptr)) {
+		/*
+		 * No need to lock the parent here because we're in discovery
+		 * and the only time a PHY's children pointer can change is
+		 * in discovery; either in pmcs_clear_expander (which has
+		 * already been called) or here, down below.  Plus, trying to
+		 * grab the parent's lock here can cause deadlock.
+		 */
+		ctmp = pptr->parent->children;
+	} else {
+		ctmp = pwp->root_phys;
+		root_phy = B_TRUE;
+	}
+
+	while (ctmp) {
+		/*
+		 * If we've checked all PHYs up to pptr, we stop. Otherwise,
+		 * we'll be checking for a primary PHY with a higher PHY
+		 * number than pptr, which will never happen.  The primary
+		 * PHY on non-root expanders will ALWAYS be the lowest
+		 * numbered PHY.
+		 */
+		if (ctmp == pptr) {
+			break;
+		}
+
+		/*
+		 * If pptr and ctmp are root PHYs, just grab the mutex on
+		 * ctmp.  No need to lock the entire tree.  If they are not
+		 * root PHYs, there is no need to lock since a non-root PHY's
+		 * SAS address and other characteristics can only change in
+		 * discovery anyway.
+		 */
+		if (root_phy) {
+			mutex_enter(&ctmp->phy_lock);
+		}
+
+		if (ctmp->dtype == EXPANDER && ctmp->width &&
+		    memcmp(ctmp->sas_address, pptr->sas_address, 8) == 0) {
+			int widephy = 0;
+			/*
+			 * If these phys are not root PHYs, compare their SAS
+			 * addresses too.
+			 */
+			if (!root_phy) {
+				if (memcmp(ctmp->parent->sas_address,
+				    pptr->parent->sas_address, 8) == 0) {
+					widephy = 1;
+				}
+			} else {
+				widephy = 1;
+			}
+			if (widephy) {
+				ctmp->width++;
+				pptr->subsidiary = 1;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: PHY "
+				    "%s part of wide PHY %s (now %d wide)",
+				    __func__, pptr->path, ctmp->path,
+				    ctmp->width);
+				if (root_phy) {
+					mutex_exit(&ctmp->phy_lock);
+				}
+				return;
+			}
+		}
+
+		cnext = ctmp->sibling;
+		if (root_phy) {
+			mutex_exit(&ctmp->phy_lock);
+		}
+		ctmp = cnext;
+	}
+
+	/*
+	 * Step 4- If we don't have a device handle, get one.  Since this
+	 * is the primary PHY, make sure subsidiary is cleared.
+	 */
+	pptr->subsidiary = 0;
+	if (pmcs_get_device_handle(pwp, pptr)) {
+		goto out;
+	}
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "Config expander %s "
+	    SAS_ADDR_FMT " dev id 0x%x lr 0x%x", pptr->path,
+	    SAS_ADDR_PRT(pptr->sas_address), pptr->device_id, pptr->link_rate);
+
+	/*
+	 * Step 5- figure out how many phys are in this expander.
+	 */
+	nphy = pmcs_expander_get_nphy(pwp, pptr);
+	if (nphy <= 0) {
+		if (nphy == 0 && ddi_get_lbolt() < pptr->config_stop) {
+			PHY_CHANGED(pwp, pptr);
+			RESTART_DISCOVERY(pwp);
+		} else {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: Retries exhausted for %s, killing", __func__,
+			    pptr->path);
+			pptr->config_stop = 0;
+			pmcs_kill_changed(pwp, pptr, 0);
+		}
+		goto out;
+	}
+
+	/*
+	 * Step 6- Allocate a list of phys for this expander and figure out
+	 * what each one is.
+	 */
+	for (i = 0; i < nphy; i++) {
+		ctmp = kmem_cache_alloc(pwp->phy_cache, KM_SLEEP);
+		bzero(ctmp, sizeof (pmcs_phy_t));
+		ctmp->device_id = PMCS_INVALID_DEVICE_ID;
+		ctmp->sibling = clist;
+		ctmp->pend_dtype = NEW;	/* Init pending dtype */
+		ctmp->config_stop = ddi_get_lbolt() +
+		    drv_usectohz(PMCS_MAX_CONFIG_TIME);
+		clist = ctmp;
+	}
+
+	mutex_enter(&pwp->config_lock);
+	if (pwp->config_changed) {
+		RESTART_DISCOVERY_LOCKED(pwp);
+		mutex_exit(&pwp->config_lock);
+		/*
+		 * Clean up the newly allocated PHYs and return
+		 */
+		while (clist) {
+			ctmp = clist->sibling;
+			kmem_cache_free(pwp->phy_cache, clist);
+			clist = ctmp;
+		}
+		return;
+	}
+	mutex_exit(&pwp->config_lock);
+
+	/*
+	 * Step 7- Now fill in the rest of the static portions of the phy.
+	 */
+	for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) {
+		ctmp->parent = pptr;
+		ctmp->pwp = pwp;
+		ctmp->level = pptr->level+1;
+		ctmp->portid = pptr->portid;
+		if (ctmp->tolerates_sas2) {
+			ASSERT(i < SAS2_PHYNUM_MAX);
+			ctmp->phynum = i & SAS2_PHYNUM_MASK;
+		} else {
+			ASSERT(i < SAS_PHYNUM_MAX);
+			ctmp->phynum = i & SAS_PHYNUM_MASK;
+		}
+		pmcs_phy_name(pwp, ctmp, ctmp->path, sizeof (ctmp->path));
+		pmcs_lock_phy(ctmp);
+	}
+
+	/*
+	 * Step 8- Discover things about each phy in the expander.
+	 */
+	for (i = 0, ctmp = clist; ctmp; ctmp = ctmp->sibling, i++) {
+		result = pmcs_expander_content_discover(pwp, pptr, ctmp);
+		if (result <= 0) {
+			if (ddi_get_lbolt() < pptr->config_stop) {
+				PHY_CHANGED(pwp, pptr);
+				RESTART_DISCOVERY(pwp);
+			} else {
+				pptr->config_stop = 0;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+				    "%s: Retries exhausted for %s, killing",
+				    __func__, pptr->path);
+				pmcs_kill_changed(pwp, pptr, 0);
+			}
+			goto out;
+		}
+
+		/* Set pend_dtype to dtype for 1st time initialization */
+		ctmp->pend_dtype = ctmp->dtype;
+	}
+
+	/*
+	 * Step 9- Install the new list on the next level. There should be
+	 * no children pointer on this PHY.  If there is, we'd need to know
+	 * how it happened (The expander suddenly got more PHYs?).
+	 */
+	ASSERT(pptr->children == NULL);
+	if (pptr->children != NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Already child PHYs attached "
+		    " to PHY %s: This should never happen", __func__,
+		    pptr->path);
+		goto out;
+	} else {
+		pptr->children = clist;
+	}
+
+	clist = NULL;
+	pptr->ncphy = nphy;
+	pptr->configured = 1;
+
+	/*
+	 * We only set width if we're greater than level 0.
+	 */
+	if (pptr->level) {
+		pptr->width = 1;
+	}
+
+	/*
+	 * Now tell the rest of the world about us, as an SMP node.
+	 */
+	pptr->iport = iport;
+	pmcs_new_tport(pwp, pptr);
+
+out:
+	while (clist) {
+		ctmp = clist->sibling;
+		pmcs_unlock_phy(clist);
+		kmem_cache_free(pwp->phy_cache, clist);
+		clist = ctmp;
+	}
+}
+
+/*
+ * 2. Check expanders marked changed (but not dead) to see if they still have
+ * the same number of phys and the same SAS address. Mark them, their subsidiary
+ * phys (if wide) and their descendents dead if anything has changed. Check the
+ * the devices they contain to see if *they* have changed. If they've changed
+ * from type NOTHING we leave them marked changed to be configured later
+ * (picking up a new SAS address and link rate if possible). Otherwise, any
+ * change in type, SAS address or removal of target role will cause us to
+ * mark them (and their descendents) as dead and cause any pending commands
+ * and associated devices to be removed.
+ *
+ * Called with PHY (pptr) locked.
+ */
+
+static void
+pmcs_check_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	int nphy, result;
+	pmcs_phy_t *ctmp, *local, *local_list = NULL, *local_tail = NULL;
+	boolean_t kill_changed, changed;
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+	    "%s: check %s", __func__, pptr->path);
+
+	/*
+	 * Step 1: Mark phy as not changed. We will mark it changed if we need
+	 * to retry.
+	 */
+	pptr->changed = 0;
+
+	/*
+	 * Reset the config_stop time. Although we're not actually configuring
+	 * anything here, we do want some indication of when to give up trying
+	 * if we can't communicate with the expander.
+	 */
+	pptr->config_stop = ddi_get_lbolt() +
+	    drv_usectohz(PMCS_MAX_CONFIG_TIME);
+
+	/*
+	 * Step 2: Figure out how many phys are in this expander. If
+	 * pmcs_expander_get_nphy returns 0 we ran out of resources,
+	 * so reschedule and try later. If it returns another error,
+	 * just return.
+	 */
+	nphy = pmcs_expander_get_nphy(pwp, pptr);
+	if (nphy <= 0) {
+		if ((nphy == 0) && (ddi_get_lbolt() < pptr->config_stop)) {
+			PHY_CHANGED(pwp, pptr);
+			RESTART_DISCOVERY(pwp);
+		} else {
+			pptr->config_stop = 0;
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: Retries exhausted for %s, killing", __func__,
+			    pptr->path);
+			pmcs_kill_changed(pwp, pptr, 0);
+		}
+		return;
+	}
+
+	/*
+	 * Step 3: If the number of phys don't agree, kill the old sub-tree.
+	 */
+	if (nphy != pptr->ncphy) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: number of contained phys for %s changed from %d to %d",
+		    __func__, pptr->path, pptr->ncphy, nphy);
+		/*
+		 * Force a rescan of this expander after dead contents
+		 * are cleared and removed.
+		 */
+		pmcs_kill_changed(pwp, pptr, 0);
+		return;
+	}
+
+	/*
+	 * Step 4: if we're at the bottom of the stack, we're done
+	 * (we can't have any levels below us)
+	 */
+	if (pptr->level == PMCS_MAX_XPND-1) {
+		return;
+	}
+
+	/*
+	 * Step 5: Discover things about each phy in this expander.  We do
+	 * this by walking the current list of contained phys and doing a
+	 * content discovery for it to a local phy.
+	 */
+	ctmp = pptr->children;
+	ASSERT(ctmp);
+	if (ctmp == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: No children attached to expander @ %s?", __func__,
+		    pptr->path);
+		return;
+	}
+
+	while (ctmp) {
+		/*
+		 * Allocate a local PHY to contain the proposed new contents
+		 * and link it to the rest of the local PHYs so that they
+		 * can all be freed later.
+		 */
+		local = pmcs_clone_phy(ctmp);
+
+		if (local_list == NULL) {
+			local_list = local;
+			local_tail = local;
+		} else {
+			local_tail->sibling = local;
+			local_tail = local;
+		}
+
+		/*
+		 * Need to lock the local PHY since pmcs_expander_content_
+		 * discovery may call pmcs_clear_phy on it, which expects
+		 * the PHY to be locked.
+		 */
+		pmcs_lock_phy(local);
+		result = pmcs_expander_content_discover(pwp, pptr, local);
+		pmcs_unlock_phy(local);
+		if (result <= 0) {
+			if (ddi_get_lbolt() < pptr->config_stop) {
+				PHY_CHANGED(pwp, pptr);
+				RESTART_DISCOVERY(pwp);
+			} else {
+				pptr->config_stop = 0;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+				    "%s: Retries exhausted for %s, killing",
+				    __func__, pptr->path);
+				pmcs_kill_changed(pwp, pptr, 0);
+			}
+
+			/*
+			 * Release all the local PHYs that we allocated.
+			 */
+			pmcs_free_phys(pwp, local_list);
+			return;
+		}
+
+		ctmp = ctmp->sibling;
+	}
+
+	/*
+	 * Step 6: Compare the local PHY's contents to our current PHY.  If
+	 * there are changes, take the appropriate action.
+	 * This is done in two steps (step 5 above, and 6 here) so that if we
+	 * have to bail during this process (e.g. pmcs_expander_content_discover
+	 * fails), we haven't actually changed the state of any of the real
+	 * PHYs.  Next time we come through here, we'll be starting over from
+	 * scratch.  This keeps us from marking a changed PHY as no longer
+	 * changed, but then having to bail only to come back next time and
+	 * think that the PHY hadn't changed.  If this were to happen, we
+	 * would fail to properly configure the device behind this PHY.
+	 */
+	local = local_list;
+	ctmp = pptr->children;
+
+	while (ctmp) {
+		changed = B_FALSE;
+		kill_changed = B_FALSE;
+
+		/*
+		 * We set local to local_list prior to this loop so that we
+		 * can simply walk the local_list while we walk this list.  The
+		 * two lists should be completely in sync.
+		 *
+		 * Clear the changed flag here.
+		 */
+		ctmp->changed = 0;
+
+		if (ctmp->dtype != local->dtype) {
+			if (ctmp->dtype != NOTHING) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s "
+				    "type changed from %s to %s (killing)",
+				    __func__, ctmp->path, PHY_TYPE(ctmp),
+				    PHY_TYPE(local));
+				/*
+				 * Force a rescan of this expander after dead
+				 * contents are cleared and removed.
+				 */
+				changed = B_TRUE;
+				kill_changed = B_TRUE;
+			} else {
+				changed = B_TRUE;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+				    "%s: %s type changed from NOTHING to %s",
+				    __func__, ctmp->path, PHY_TYPE(local));
+			}
+
+		} else if (ctmp->atdt != local->atdt) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s attached "
+			    "device type changed from %d to %d (killing)",
+			    __func__, ctmp->path, ctmp->atdt, local->atdt);
+			/*
+			 * Force a rescan of this expander after dead
+			 * contents are cleared and removed.
+			 */
+			changed = B_TRUE;
+
+			if (local->atdt == 0) {
+				kill_changed = B_TRUE;
+			}
+		} else if (ctmp->link_rate != local->link_rate) {
+			pmcs_prt(pwp, PMCS_PRT_INFO, "%s: %s changed speed from"
+			    " %s to %s", __func__, ctmp->path,
+			    pmcs_get_rate(ctmp->link_rate),
+			    pmcs_get_rate(local->link_rate));
+			/* If the speed changed from invalid, force rescan */
+			if (!PMCS_VALID_LINK_RATE(ctmp->link_rate)) {
+				changed = B_TRUE;
+				RESTART_DISCOVERY(pwp);
+			} else {
+				/* Just update to the new link rate */
+				ctmp->link_rate = local->link_rate;
+			}
+
+			if (!PMCS_VALID_LINK_RATE(local->link_rate)) {
+				kill_changed = B_TRUE;
+			}
+		} else if (memcmp(ctmp->sas_address, local->sas_address,
+		    sizeof (ctmp->sas_address)) != 0) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: SASAddr "
+			    "for %s changed from " SAS_ADDR_FMT " to "
+			    SAS_ADDR_FMT " (kill old tree)", __func__,
+			    ctmp->path, SAS_ADDR_PRT(ctmp->sas_address),
+			    SAS_ADDR_PRT(local->sas_address));
+			/*
+			 * Force a rescan of this expander after dead
+			 * contents are cleared and removed.
+			 */
+			changed = B_TRUE;
+		} else {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: %s looks the same (type %s)",
+			    __func__, ctmp->path, PHY_TYPE(ctmp));
+			/*
+			 * If EXPANDER, still mark it changed so we
+			 * re-evaluate its contents.  If it's not an expander,
+			 * but it hasn't been configured, also mark it as
+			 * changed so that it will undergo configuration.
+			 */
+			if (ctmp->dtype == EXPANDER) {
+				changed = B_TRUE;
+			} else if ((ctmp->dtype != NOTHING) &&
+			    !ctmp->configured) {
+				ctmp->changed = 1;
+			} else {
+				/* It simply hasn't changed */
+				ctmp->changed = 0;
+			}
+		}
+
+		/*
+		 * If the PHY changed, call pmcs_kill_changed if indicated,
+		 * update its contents to reflect its current state and mark it
+		 * as changed.
+		 */
+		if (changed) {
+			/*
+			 * pmcs_kill_changed will mark the PHY as changed, so
+			 * only do PHY_CHANGED if we did not do kill_changed.
+			 */
+			if (kill_changed) {
+				pmcs_kill_changed(pwp, ctmp, 0);
+			} else {
+				/*
+				 * If we're not killing the device, it's not
+				 * dead.  Mark the PHY as changed.
+				 */
+				PHY_CHANGED(pwp, ctmp);
+
+				if (ctmp->dead) {
+					pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+					    "%s: Unmarking PHY %s dead, "
+					    "restarting discovery",
+					    __func__, ctmp->path);
+					ctmp->dead = 0;
+					RESTART_DISCOVERY(pwp);
+				}
+			}
+
+			/*
+			 * If the dtype of this PHY is now NOTHING, mark it as
+			 * unconfigured.  Set pend_dtype to what the new dtype
+			 * is.  It'll get updated at the end of the discovery
+			 * process.
+			 */
+			if (local->dtype == NOTHING) {
+				bzero(ctmp->sas_address,
+				    sizeof (local->sas_address));
+				ctmp->atdt = 0;
+				ctmp->link_rate = 0;
+				ctmp->pend_dtype = NOTHING;
+				ctmp->configured = 0;
+			} else {
+				(void) memcpy(ctmp->sas_address,
+				    local->sas_address,
+				    sizeof (local->sas_address));
+				ctmp->atdt = local->atdt;
+				ctmp->link_rate = local->link_rate;
+				ctmp->pend_dtype = local->dtype;
+			}
+		}
+
+		local = local->sibling;
+		ctmp = ctmp->sibling;
+	}
+
+	/*
+	 * If we got to here, that means we were able to see all the PHYs
+	 * and we can now update all of the real PHYs with the information
+	 * we got on the local PHYs.  Once that's done, free all the local
+	 * PHYs.
+	 */
+
+	pmcs_free_phys(pwp, local_list);
+}
+
+/*
+ * Top level routine to check expanders.  We call pmcs_check_expander for
+ * each expander.  Since we're not doing any configuration right now, it
+ * doesn't matter if this is breadth-first.
+ */
+static boolean_t
+pmcs_check_expanders(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	pmcs_phy_t *phyp, *pnext, *pchild;
+	boolean_t config_changed = B_FALSE;
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s", __func__, pptr->path);
+
+	/*
+	 * Check each expander at this level
+	 */
+	phyp = pptr;
+	while (phyp && !config_changed) {
+		pmcs_lock_phy(phyp);
+
+		if ((phyp->dtype == EXPANDER) && phyp->changed &&
+		    !phyp->dead && !phyp->subsidiary &&
+		    phyp->configured) {
+			pmcs_check_expander(pwp, phyp);
+		}
+
+		pnext = phyp->sibling;
+		pmcs_unlock_phy(phyp);
+
+		mutex_enter(&pwp->config_lock);
+		config_changed = pwp->config_changed;
+		mutex_exit(&pwp->config_lock);
+
+		phyp = pnext;
+	}
+
+	if (config_changed) {
+		return (config_changed);
+	}
+
+	/*
+	 * Now check the children
+	 */
+	phyp = pptr;
+	while (phyp && !config_changed) {
+		pmcs_lock_phy(phyp);
+		pnext = phyp->sibling;
+		pchild = phyp->children;
+		pmcs_unlock_phy(phyp);
+
+		if (pchild) {
+			(void) pmcs_check_expanders(pwp, pchild);
+		}
+
+		mutex_enter(&pwp->config_lock);
+		config_changed = pwp->config_changed;
+		mutex_exit(&pwp->config_lock);
+
+		phyp = pnext;
+	}
+
+	/*
+	 * We're done
+	 */
+	return (config_changed);
+}
+
+/*
+ * Called with softstate and PHY locked
+ */
+static void
+pmcs_clear_expander(pmcs_hw_t *pwp, pmcs_phy_t *pptr, int level)
+{
+	pmcs_phy_t *ctmp;
+
+	ASSERT(mutex_owned(&pwp->lock));
+	ASSERT(mutex_owned(&pptr->phy_lock));
+	ASSERT(pptr->level < PMCS_MAX_XPND - 1);
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: checking %s", __func__,
+	    pptr->path);
+
+	ctmp = pptr->children;
+	while (ctmp) {
+		/*
+		 * If the expander is dead, mark its children dead
+		 */
+		if (pptr->dead) {
+			ctmp->dead = 1;
+		}
+		if (ctmp->dtype == EXPANDER) {
+			pmcs_clear_expander(pwp, ctmp, level + 1);
+		}
+		ctmp = ctmp->sibling;
+	}
+
+	/*
+	 * If this expander is not dead, we're done here.
+	 */
+	if (!pptr->dead) {
+		return;
+	}
+
+	/*
+	 * Now snip out the list of children below us and release them
+	 */
+	ctmp = pptr->children;
+	while (ctmp) {
+		pmcs_phy_t *nxt = ctmp->sibling;
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: dead PHY 0x%p (%s) (ref_count %d)", __func__,
+		    (void *)ctmp, ctmp->path, ctmp->ref_count);
+		/*
+		 * Put this PHY on the dead PHY list for the watchdog to
+		 * clean up after any outstanding work has completed.
+		 */
+		mutex_enter(&pwp->dead_phylist_lock);
+		ctmp->dead_next = pwp->dead_phys;
+		pwp->dead_phys = ctmp;
+		mutex_exit(&pwp->dead_phylist_lock);
+		pmcs_unlock_phy(ctmp);
+		ctmp = nxt;
+	}
+
+	pptr->children = NULL;
+
+	/*
+	 * Clear subsidiary phys as well.  Getting the parent's PHY lock
+	 * is only necessary if level == 0 since otherwise the parent is
+	 * already locked.
+	 */
+	if (!IS_ROOT_PHY(pptr)) {
+		if (level == 0) {
+			mutex_enter(&pptr->parent->phy_lock);
+		}
+		ctmp = pptr->parent->children;
+		if (level == 0) {
+			mutex_exit(&pptr->parent->phy_lock);
+		}
+	} else {
+		ctmp = pwp->root_phys;
+	}
+
+	while (ctmp) {
+		if (ctmp == pptr) {
+			ctmp = ctmp->sibling;
+			continue;
+		}
+		/*
+		 * We only need to lock subsidiary PHYs on the level 0
+		 * expander.  Any children of that expander, subsidiaries or
+		 * not, will already be locked.
+		 */
+		if (level == 0) {
+			pmcs_lock_phy(ctmp);
+		}
+		if (ctmp->dtype != EXPANDER || ctmp->subsidiary == 0 ||
+		    memcmp(ctmp->sas_address, pptr->sas_address,
+		    sizeof (ctmp->sas_address)) != 0) {
+			if (level == 0) {
+				pmcs_unlock_phy(ctmp);
+			}
+			ctmp = ctmp->sibling;
+			continue;
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: subsidiary %s",
+		    __func__, ctmp->path);
+		pmcs_clear_phy(pwp, ctmp);
+		if (level == 0) {
+			pmcs_unlock_phy(ctmp);
+		}
+		ctmp = ctmp->sibling;
+	}
+
+	pmcs_clear_phy(pwp, pptr);
+}
+
+/*
+ * Called with PHY locked and with scratch acquired. We return 0 if
+ * we fail to allocate resources or notice that the configuration
+ * count changed while we were running the command. We return
+ * less than zero if we had an I/O error or received an unsupported
+ * configuration. Otherwise we return the number of phys in the
+ * expander.
+ */
+#define	DFM(m, y) if (m == NULL) m = y
+static int
+pmcs_expander_get_nphy(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	struct pmcwork *pwrk;
+	char buf[64];
+	const uint_t rdoff = 0x100;	/* returned data offset */
+	smp_response_frame_t *srf;
+	smp_report_general_resp_t *srgr;
+	uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status, ival;
+	int result;
+
+	ival = 0x40001100;
+again:
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
+	if (pwrk == NULL) {
+		result = 0;
+		goto out;
+	}
+	(void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE);
+	pwrk->arg = pwp->scratch;
+	pwrk->dtype = pptr->dtype;
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (ptr == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s: GET_IQ_ENTRY failed",
+		    __func__);
+		pmcs_pwork(pwp, pwrk);
+		result = 0;
+		goto out;
+	}
+
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
+	msg[1] = LE_32(pwrk->htag);
+	msg[2] = LE_32(pptr->device_id);
+	msg[3] = LE_32((4 << SMP_REQUEST_LENGTH_SHIFT) | SMP_INDIRECT_RESPONSE);
+	/*
+	 * Send SMP REPORT GENERAL (of either SAS1.1 or SAS2 flavors).
+	 */
+	msg[4] = BE_32(ival);
+	msg[5] = 0;
+	msg[6] = 0;
+	msg[7] = 0;
+	msg[8] = 0;
+	msg[9] = 0;
+	msg[10] = 0;
+	msg[11] = 0;
+	msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
+	msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
+	msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff);
+	msg[15] = 0;
+
+	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	htag = pwrk->htag;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	pmcs_unlock_phy(pptr);
+	WAIT_FOR(pwrk, 1000, result);
+	pmcs_lock_phy(pptr);
+	pmcs_pwork(pwp, pwrk);
+
+	mutex_enter(&pwp->config_lock);
+	if (pwp->config_changed) {
+		RESTART_DISCOVERY_LOCKED(pwp);
+		mutex_exit(&pwp->config_lock);
+		result = 0;
+		goto out;
+	}
+	mutex_exit(&pwp->config_lock);
+
+	if (result) {
+		pmcs_timed_out(pwp, htag, __func__);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: Issuing SMP ABORT for htag 0x%08x", __func__, htag);
+		if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: Unable to issue SMP ABORT for htag 0x%08x",
+			    __func__, htag);
+		} else {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: Issuing SMP ABORT for htag 0x%08x",
+			    __func__, htag);
+		}
+		result = 0;
+		goto out;
+	}
+	ptr = (void *)pwp->scratch;
+	status = LE_32(ptr[2]);
+	if (status == PMCOUT_STATUS_UNDERFLOW ||
+	    status == PMCOUT_STATUS_OVERFLOW) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW,
+		    "%s: over/underflow", __func__);
+		status = PMCOUT_STATUS_OK;
+	}
+	srf = (smp_response_frame_t *)&((uint32_t *)pwp->scratch)[rdoff >> 2];
+	srgr = (smp_report_general_resp_t *)
+	    &((uint32_t *)pwp->scratch)[(rdoff >> 2)+1];
+
+	if (status != PMCOUT_STATUS_OK) {
+		char *nag = NULL;
+		(void) snprintf(buf, sizeof (buf),
+		    "%s: SMP op failed (0x%x)", __func__, status);
+		switch (status) {
+		case PMCOUT_STATUS_IO_PORT_IN_RESET:
+			DFM(nag, "I/O Port In Reset");
+			/* FALLTHROUGH */
+		case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
+			DFM(nag, "Hardware Timeout");
+			/* FALLTHROUGH */
+		case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
+			DFM(nag, "Internal SMP Resource Failure");
+			/* FALLTHROUGH */
+		case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
+			DFM(nag, "PHY Not Ready");
+			/* FALLTHROUGH */
+		case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+			DFM(nag, "Connection Rate Not Supported");
+			/* FALLTHROUGH */
+		case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
+			DFM(nag, "Open Retry Timeout");
+			/* FALLTHROUGH */
+		case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
+			DFM(nag, "Response Connection Error");
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: expander %s SMP operation failed (%s)",
+			    __func__, pptr->path, nag);
+			break;
+
+		/*
+		 * For the IO_DS_NON_OPERATIONAL case, we need to kick off
+		 * device state recovery and return 0 so that the caller
+		 * doesn't assume this expander is dead for good.
+		 */
+		case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: {
+			pmcs_xscsi_t *xp = pptr->target;
+
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+			    "%s: expander %s device state non-operational",
+			    __func__, pptr->path);
+
+			if (xp == NULL) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+				    "%s: No target to do DS recovery for PHY "
+				    "%p (%s), attempting PHY hard reset",
+				    __func__, (void *)pptr, pptr->path);
+				(void) pmcs_reset_phy(pwp, pptr,
+				    PMCS_PHYOP_HARD_RESET);
+				break;
+			}
+
+			mutex_enter(&xp->statlock);
+			pmcs_start_dev_state_recovery(xp, pptr);
+			mutex_exit(&xp->statlock);
+			break;
+		}
+
+		default:
+			pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr);
+			result = -EIO;
+			break;
+		}
+	} else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: bad response frame type 0x%x",
+		    __func__, srf->srf_frame_type);
+		result = -EINVAL;
+	} else if (srf->srf_function != SMP_FUNC_REPORT_GENERAL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad response function 0x%x",
+		    __func__, srf->srf_function);
+		result = -EINVAL;
+	} else if (srf->srf_result != 0) {
+		/*
+		 * Check to see if we have a value of 3 for failure and
+		 * whether we were using a SAS2.0 allocation length value
+		 * and retry without it.
+		 */
+		if (srf->srf_result == 3 && (ival & 0xff00)) {
+			ival &= ~0xff00;
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: err 0x%x with SAS2 request- retry with SAS1",
+			    __func__, srf->srf_result);
+			goto again;
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad response 0x%x",
+		    __func__, srf->srf_result);
+		result = -EINVAL;
+	} else if (srgr->srgr_configuring) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: expander at phy %s is still configuring",
+		    __func__, pptr->path);
+		result = 0;
+	} else {
+		result = srgr->srgr_number_of_phys;
+		if (ival & 0xff00) {
+			pptr->tolerates_sas2 = 1;
+		}
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s has %d phys and %s SAS2", pptr->path, result,
+		    pptr->tolerates_sas2? "tolerates" : "does not tolerate");
+	}
+out:
+	return (result);
+}
+
+/*
+ * Called with expander locked (and thus, pptr) as well as all PHYs up to
+ * the root, and scratch acquired. Return 0 if we fail to allocate resources
+ * or notice that the configuration changed while we were running the command.
+ *
+ * We return less than zero if we had an I/O error or received an
+ * unsupported configuration.
+ */
+static int
+pmcs_expander_content_discover(pmcs_hw_t *pwp, pmcs_phy_t *expander,
+    pmcs_phy_t *pptr)
+{
+	struct pmcwork *pwrk;
+	char buf[64];
+	uint8_t sas_address[8];
+	uint8_t att_sas_address[8];
+	smp_response_frame_t *srf;
+	smp_discover_resp_t *sdr;
+	const uint_t rdoff = 0x100;	/* returned data offset */
+	uint8_t *roff;
+	uint32_t status, *ptr, msg[PMCS_MSG_SIZE], htag;
+	int result;
+	uint8_t	ini_support;
+	uint8_t	tgt_support;
+
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, expander);
+	if (pwrk == NULL) {
+		result = 0;
+		goto out;
+	}
+	(void) memset(pwp->scratch, 0x77, PMCS_SCRATCH_SIZE);
+	pwrk->arg = pwp->scratch;
+	pwrk->dtype = expander->dtype;
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
+	msg[1] = LE_32(pwrk->htag);
+	msg[2] = LE_32(expander->device_id);
+	msg[3] = LE_32((12 << SMP_REQUEST_LENGTH_SHIFT) |
+	    SMP_INDIRECT_RESPONSE);
+	/*
+	 * Send SMP DISCOVER (of either SAS1.1 or SAS2 flavors).
+	 */
+	if (expander->tolerates_sas2) {
+		msg[4] = BE_32(0x40101B00);
+	} else {
+		msg[4] = BE_32(0x40100000);
+	}
+	msg[5] = 0;
+	msg[6] = BE_32((pptr->phynum << 16));
+	msg[7] = 0;
+	msg[8] = 0;
+	msg[9] = 0;
+	msg[10] = 0;
+	msg[11] = 0;
+	msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
+	msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
+	msg[14] = LE_32(PMCS_SCRATCH_SIZE - rdoff);
+	msg[15] = 0;
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (ptr == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		result = 0;
+		goto out;
+	}
+
+	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	htag = pwrk->htag;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	/*
+	 * Drop PHY lock while waiting so other completions aren't potentially
+	 * blocked.
+	 */
+	pmcs_unlock_phy(expander);
+	WAIT_FOR(pwrk, 1000, result);
+	pmcs_lock_phy(expander);
+	pmcs_pwork(pwp, pwrk);
+
+	mutex_enter(&pwp->config_lock);
+	if (pwp->config_changed) {
+		RESTART_DISCOVERY_LOCKED(pwp);
+		mutex_exit(&pwp->config_lock);
+		result = 0;
+		goto out;
+	}
+	mutex_exit(&pwp->config_lock);
+
+	if (result) {
+		pmcs_prt(pwp, PMCS_PRT_WARN, pmcs_timeo, __func__);
+		if (pmcs_abort(pwp, expander, htag, 0, 0)) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: Unable to issue SMP ABORT for htag 0x%08x",
+			    __func__, htag);
+		} else {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+			    "%s: Issuing SMP ABORT for htag 0x%08x",
+			    __func__, htag);
+		}
+		result = -ETIMEDOUT;
+		goto out;
+	}
+	ptr = (void *)pwp->scratch;
+	/*
+	 * Point roff to the DMA offset for returned data
+	 */
+	roff = pwp->scratch;
+	roff += rdoff;
+	srf = (smp_response_frame_t *)roff;
+	sdr = (smp_discover_resp_t *)(roff+4);
+	status = LE_32(ptr[2]);
+	if (status == PMCOUT_STATUS_UNDERFLOW ||
+	    status == PMCOUT_STATUS_OVERFLOW) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW,
+		    "%s: over/underflow", __func__);
+		status = PMCOUT_STATUS_OK;
+	}
+	if (status != PMCOUT_STATUS_OK) {
+		char *nag = NULL;
+		(void) snprintf(buf, sizeof (buf),
+		    "%s: SMP op failed (0x%x)", __func__, status);
+		switch (status) {
+		case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
+			DFM(nag, "Hardware Timeout");
+			/* FALLTHROUGH */
+		case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
+			DFM(nag, "Internal SMP Resource Failure");
+			/* FALLTHROUGH */
+		case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
+			DFM(nag, "PHY Not Ready");
+			/* FALLTHROUGH */
+		case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+			DFM(nag, "Connection Rate Not Supported");
+			/* FALLTHROUGH */
+		case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
+			DFM(nag, "Open Retry Timeout");
+			/* FALLTHROUGH */
+		case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
+			DFM(nag, "Response Connection Error");
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: expander %s SMP operation failed (%s)",
+			    __func__, pptr->path, nag);
+			break;
+		default:
+			pmcs_print_entry(pwp, PMCS_PRT_DEBUG, buf, ptr);
+			result = -EIO;
+			break;
+		}
+		goto out;
+	} else if (srf->srf_frame_type != SMP_FRAME_TYPE_RESPONSE) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: bad response frame type 0x%x",
+		    __func__, srf->srf_frame_type);
+		result = -EINVAL;
+		goto out;
+	} else if (srf->srf_function != SMP_FUNC_DISCOVER) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad response function 0x%x",
+		    __func__, srf->srf_function);
+		result = -EINVAL;
+		goto out;
+	} else if (srf->srf_result != SMP_RES_FUNCTION_ACCEPTED) {
+		result = pmcs_smp_function_result(pwp, srf);
+		/* Need not fail if PHY is Vacant */
+		if (result != SMP_RES_PHY_VACANT) {
+			result = -EINVAL;
+			goto out;
+		}
+	}
+
+	ini_support = (sdr->sdr_attached_sata_host |
+	    (sdr->sdr_attached_smp_initiator << 1) |
+	    (sdr->sdr_attached_stp_initiator << 2) |
+	    (sdr->sdr_attached_ssp_initiator << 3));
+
+	tgt_support = (sdr->sdr_attached_sata_device |
+	    (sdr->sdr_attached_smp_target << 1) |
+	    (sdr->sdr_attached_stp_target << 2) |
+	    (sdr->sdr_attached_ssp_target << 3));
+
+	pmcs_wwn2barray(BE_64(sdr->sdr_sas_addr), sas_address);
+	pmcs_wwn2barray(BE_64(sdr->sdr_attached_sas_addr), att_sas_address);
+
+	switch (sdr->sdr_attached_device_type) {
+	case SAS_IF_DTYPE_ENDPOINT:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS="
+		    SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x",
+		    pptr->path,
+		    sdr->sdr_attached_device_type,
+		    sdr->sdr_negotiated_logical_link_rate,
+		    ini_support,
+		    tgt_support,
+		    SAS_ADDR_PRT(sas_address),
+		    SAS_ADDR_PRT(att_sas_address),
+		    sdr->sdr_attached_phy_identifier);
+
+		if (sdr->sdr_attached_sata_device ||
+		    sdr->sdr_attached_stp_target) {
+			pptr->dtype = SATA;
+		} else if (sdr->sdr_attached_ssp_target) {
+			pptr->dtype = SAS;
+		} else if (tgt_support || ini_support) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s: %s has "
+			    "tgt support=%x init support=(%x)",
+			    __func__, pptr->path, tgt_support, ini_support);
+		}
+		break;
+	case SAS_IF_DTYPE_EDGE:
+	case SAS_IF_DTYPE_FANOUT:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "exp_content: %s atdt=0x%x lr=%x is=%x ts=%x SAS="
+		    SAS_ADDR_FMT " attSAS=" SAS_ADDR_FMT " atPHY=%x",
+		    pptr->path,
+		    sdr->sdr_attached_device_type,
+		    sdr->sdr_negotiated_logical_link_rate,
+		    ini_support,
+		    tgt_support,
+		    SAS_ADDR_PRT(sas_address),
+		    SAS_ADDR_PRT(att_sas_address),
+		    sdr->sdr_attached_phy_identifier);
+		if (sdr->sdr_attached_smp_target) {
+			/*
+			 * Avoid configuring phys that just point back
+			 * at a parent phy
+			 */
+			if (expander->parent &&
+			    memcmp(expander->parent->sas_address,
+			    att_sas_address,
+			    sizeof (expander->parent->sas_address)) == 0) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG3,
+				    "%s: skipping port back to parent "
+				    "expander (%s)", __func__, pptr->path);
+				pptr->dtype = NOTHING;
+				break;
+			}
+			pptr->dtype = EXPANDER;
+
+		} else if (tgt_support || ini_support) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s has "
+			    "tgt support=%x init support=(%x)",
+			    pptr->path, tgt_support, ini_support);
+			pptr->dtype = EXPANDER;
+		}
+		break;
+	default:
+		pptr->dtype = NOTHING;
+		break;
+	}
+	if (pptr->dtype != NOTHING) {
+		pmcs_phy_t *ctmp;
+
+		/*
+		 * If the attached device is a SATA device and the expander
+		 * is (possibly) a SAS2 compliant expander, check for whether
+		 * there is a NAA=5 WWN field starting at this offset and
+		 * use that for the SAS Address for this device.
+		 */
+		if (expander->tolerates_sas2 && pptr->dtype == SATA &&
+		    (roff[SAS_ATTACHED_NAME_OFFSET] >> 8) == 0x5) {
+			(void) memcpy(pptr->sas_address,
+			    &roff[SAS_ATTACHED_NAME_OFFSET], 8);
+		} else {
+			(void) memcpy(pptr->sas_address, att_sas_address, 8);
+		}
+		pptr->atdt = (sdr->sdr_attached_device_type);
+		/*
+		 * Now run up from the expander's parent up to the top to
+		 * make sure we only use the least common link_rate.
+		 */
+		for (ctmp = expander->parent; ctmp; ctmp = ctmp->parent) {
+			if (ctmp->link_rate <
+			    sdr->sdr_negotiated_logical_link_rate) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+				    "%s: derating link rate from %x to %x due "
+				    "to %s being slower", pptr->path,
+				    sdr->sdr_negotiated_logical_link_rate,
+				    ctmp->link_rate,
+				    ctmp->path);
+				sdr->sdr_negotiated_logical_link_rate =
+				    ctmp->link_rate;
+			}
+		}
+		pptr->link_rate = sdr->sdr_negotiated_logical_link_rate;
+		pptr->state.prog_min_rate = sdr->sdr_prog_min_phys_link_rate;
+		pptr->state.hw_min_rate = sdr->sdr_hw_min_phys_link_rate;
+		pptr->state.prog_max_rate = sdr->sdr_prog_max_phys_link_rate;
+		pptr->state.hw_max_rate = sdr->sdr_hw_max_phys_link_rate;
+		PHY_CHANGED(pwp, pptr);
+	} else {
+		pmcs_clear_phy(pwp, pptr);
+	}
+	result = 1;
+out:
+	return (result);
+}
+
+/*
+ * Get a work structure and assign it a tag with type and serial number
+ * If a structure is returned, it is returned locked.
+ */
+pmcwork_t *
+pmcs_gwork(pmcs_hw_t *pwp, uint32_t tag_type, pmcs_phy_t *phyp)
+{
+	pmcwork_t *p;
+	uint16_t snum;
+	uint32_t off;
+
+	mutex_enter(&pwp->wfree_lock);
+	p = STAILQ_FIRST(&pwp->wf);
+	if (p == NULL) {
+		/*
+		 * If we couldn't get a work structure, it's time to bite
+		 * the bullet, grab the pfree_lock and copy over all the
+		 * work structures from the pending free list to the actual
+		 * free list.  This shouldn't happen all that often.
+		 */
+		mutex_enter(&pwp->pfree_lock);
+		pwp->wf.stqh_first = pwp->pf.stqh_first;
+		pwp->wf.stqh_last = pwp->pf.stqh_last;
+		STAILQ_INIT(&pwp->pf);
+		mutex_exit(&pwp->pfree_lock);
+
+		p = STAILQ_FIRST(&pwp->wf);
+		if (p == NULL) {
+			mutex_exit(&pwp->wfree_lock);
+			return (NULL);
+		}
+	}
+	STAILQ_REMOVE(&pwp->wf, p, pmcwork, next);
+	snum = pwp->wserno++;
+	mutex_exit(&pwp->wfree_lock);
+
+	off = p - pwp->work;
+
+	mutex_enter(&p->lock);
+	ASSERT(p->state == PMCS_WORK_STATE_NIL);
+	ASSERT(p->htag == PMCS_TAG_FREE);
+	p->htag = (tag_type << PMCS_TAG_TYPE_SHIFT) & PMCS_TAG_TYPE_MASK;
+	p->htag |= ((snum << PMCS_TAG_SERNO_SHIFT) & PMCS_TAG_SERNO_MASK);
+	p->htag |= ((off << PMCS_TAG_INDEX_SHIFT) & PMCS_TAG_INDEX_MASK);
+	p->start = gethrtime();
+	p->state = PMCS_WORK_STATE_READY;
+	p->ssp_event = 0;
+	p->dead = 0;
+
+	if (phyp) {
+		p->phy = phyp;
+		pmcs_inc_phy_ref_count(phyp);
+	}
+
+	return (p);
+}
+
+/*
+ * Called with pwrk lock held.  Returned with lock released.
+ */
+void
+pmcs_pwork(pmcs_hw_t *pwp, pmcwork_t *p)
+{
+	ASSERT(p != NULL);
+	ASSERT(mutex_owned(&p->lock));
+
+#ifdef DEBUG
+	p->last_ptr = p->ptr;
+	p->last_arg = p->arg;
+	p->last_phy = p->phy;
+	p->last_xp = p->xp;
+	p->last_htag = p->htag;
+	p->last_state = p->state;
+#endif
+	p->finish = gethrtime();
+
+	if (p->phy) {
+		pmcs_dec_phy_ref_count(p->phy);
+	}
+
+	p->state = PMCS_WORK_STATE_NIL;
+	p->htag = PMCS_TAG_FREE;
+	p->xp = NULL;
+	p->ptr = NULL;
+	p->arg = NULL;
+	p->phy = NULL;
+	p->timer = 0;
+	mutex_exit(&p->lock);
+
+	if (mutex_tryenter(&pwp->wfree_lock) == 0) {
+		mutex_enter(&pwp->pfree_lock);
+		STAILQ_INSERT_TAIL(&pwp->pf, p, next);
+		mutex_exit(&pwp->pfree_lock);
+	} else {
+		STAILQ_INSERT_TAIL(&pwp->wf, p, next);
+		mutex_exit(&pwp->wfree_lock);
+	}
+}
+
+/*
+ * Find a work structure based upon a tag and make sure that the tag
+ * serial number matches the work structure we've found.
+ * If a structure is found, its lock is held upon return.
+ */
+pmcwork_t *
+pmcs_tag2wp(pmcs_hw_t *pwp, uint32_t htag)
+{
+	pmcwork_t *p;
+	uint32_t idx = PMCS_TAG_INDEX(htag);
+
+	p = &pwp->work[idx];
+
+	mutex_enter(&p->lock);
+	if (p->htag == htag) {
+		return (p);
+	}
+	mutex_exit(&p->lock);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2, "INDEX 0x%x HTAG 0x%x got p->htag 0x%x",
+	    idx, htag, p->htag);
+	return (NULL);
+}
+
+/*
+ * Issue an abort for a command or for all commands.
+ *
+ * Since this can be called from interrupt context,
+ * we don't wait for completion if wait is not set.
+ *
+ * Called with PHY lock held.
+ */
+int
+pmcs_abort(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint32_t tag, int all_cmds,
+    int wait)
+{
+	pmcwork_t *pwrk;
+	pmcs_xscsi_t *tgt;
+	uint32_t msg[PMCS_MSG_SIZE], *ptr;
+	int result, abt_type;
+	uint32_t abt_htag, status;
+
+	if (pptr->abort_all_start) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ABORT_ALL for (%s) already"
+		    " in progress.", __func__, pptr->path);
+		return (EBUSY);
+	}
+
+	switch (pptr->dtype) {
+	case SAS:
+		abt_type = PMCIN_SSP_ABORT;
+		break;
+	case SATA:
+		abt_type = PMCIN_SATA_ABORT;
+		break;
+	case EXPANDER:
+		abt_type = PMCIN_SMP_ABORT;
+		break;
+	default:
+		return (0);
+	}
+
+	pwrk = pmcs_gwork(pwp, wait ? PMCS_TAG_TYPE_WAIT : PMCS_TAG_TYPE_NONE,
+	    pptr);
+
+	if (pwrk == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
+		return (ENOMEM);
+	}
+
+	pwrk->dtype = pptr->dtype;
+	if (wait) {
+		pwrk->arg = msg;
+	}
+	if (pptr->valid_device_id == 0) {
+		pmcs_pwork(pwp, pwrk);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Invalid DeviceID", __func__);
+		return (ENODEV);
+	}
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, abt_type));
+	msg[1] = LE_32(pwrk->htag);
+	msg[2] = LE_32(pptr->device_id);
+	if (all_cmds) {
+		msg[3] = 0;
+		msg[4] = LE_32(1);
+		pwrk->ptr = NULL;
+		pptr->abort_all_start = gethrtime();
+	} else {
+		msg[3] = LE_32(tag);
+		msg[4] = 0;
+		pwrk->ptr = &tag;
+	}
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (ptr == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		pmcs_pwork(pwp, pwrk);
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
+		return (ENOMEM);
+	}
+
+	COPY_MESSAGE(ptr, msg, 5);
+	if (all_cmds) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: aborting all commands for %s device %s. (htag=0x%x)",
+		    __func__, pmcs_get_typename(pptr->dtype), pptr->path,
+		    msg[1]);
+	} else {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: aborting tag 0x%x for %s device %s. (htag=0x%x)",
+		    __func__, tag, pmcs_get_typename(pptr->dtype), pptr->path,
+		    msg[1]);
+	}
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (!wait) {
+		mutex_exit(&pwrk->lock);
+		return (0);
+	}
+
+	abt_htag = pwrk->htag;
+	pmcs_unlock_phy(pwrk->phy);
+	WAIT_FOR(pwrk, 1000, result);
+	pmcs_lock_phy(pwrk->phy);
+
+	tgt = pwrk->xp;
+	pmcs_pwork(pwp, pwrk);
+
+	if (tgt != NULL) {
+		mutex_enter(&tgt->aqlock);
+		if (!STAILQ_EMPTY(&tgt->aq)) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: Abort complete (result=0x%x), but "
+			    "aq not empty (tgt 0x%p), waiting",
+			    __func__, result, (void *)tgt);
+			cv_wait(&tgt->abort_cv, &tgt->aqlock);
+		}
+		mutex_exit(&tgt->aqlock);
+	}
+
+	if (all_cmds) {
+		pptr->abort_all_start = 0;
+		cv_signal(&pptr->abort_all_cv);
+	}
+
+	if (result) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Abort (htag 0x%08x) request timed out",
+		    __func__, abt_htag);
+		if (tgt != NULL) {
+			mutex_enter(&tgt->statlock);
+			if ((tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) &&
+			    (tgt->dev_state !=
+			    PMCS_DEVICE_STATE_NON_OPERATIONAL)) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "%s: Trying DS error recovery for tgt 0x%p",
+				    __func__, (void *)tgt);
+				(void) pmcs_send_err_recovery_cmd(pwp,
+				    PMCS_DEVICE_STATE_IN_RECOVERY, tgt);
+			}
+			mutex_exit(&tgt->statlock);
+		}
+		return (ETIMEDOUT);
+	}
+
+	status = LE_32(msg[2]);
+	if (status != PMCOUT_STATUS_OK) {
+		/*
+		 * The only non-success status are IO_NOT_VALID &
+		 * IO_ABORT_IN_PROGRESS.
+		 * In case of IO_ABORT_IN_PROGRESS, the other ABORT cmd's
+		 * status is of concern and this duplicate cmd status can
+		 * be ignored.
+		 * If IO_NOT_VALID, that's not an error per-se.
+		 * For abort of single I/O complete the command anyway.
+		 * If, however, we were aborting all, that is a problem
+		 * as IO_NOT_VALID really means that the IO or device is
+		 * not there. So, discovery process will take of the cleanup.
+		 */
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: abort result 0x%x",
+		    __func__, LE_32(msg[2]));
+		if (all_cmds) {
+			PHY_CHANGED(pwp, pptr);
+			RESTART_DISCOVERY(pwp);
+		} else {
+			return (EINVAL);
+		}
+
+		return (0);
+	}
+
+	if (tgt != NULL) {
+		mutex_enter(&tgt->statlock);
+		if (tgt->dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: Restoring OPERATIONAL dev_state for tgt 0x%p",
+			    __func__, (void *)tgt);
+			(void) pmcs_send_err_recovery_cmd(pwp,
+			    PMCS_DEVICE_STATE_OPERATIONAL, tgt);
+		}
+		mutex_exit(&tgt->statlock);
+	}
+
+	return (0);
+}
+
+/*
+ * Issue a task management function to an SSP device.
+ *
+ * Called with PHY lock held.
+ * statlock CANNOT be held upon entry.
+ */
+int
+pmcs_ssp_tmf(pmcs_hw_t *pwp, pmcs_phy_t *pptr, uint8_t tmf, uint32_t tag,
+    uint64_t lun, uint32_t *response)
+{
+	int result, ds;
+	uint8_t local[PMCS_QENTRY_SIZE << 1], *xd;
+	sas_ssp_rsp_iu_t *rptr = (void *)local;
+	static const uint8_t ssp_rsp_evec[] = {
+		0x58, 0x61, 0x56, 0x72, 0x00
+	};
+	uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
+	struct pmcwork *pwrk;
+	pmcs_xscsi_t *xp;
+
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
+	if (pwrk == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
+		return (ENOMEM);
+	}
+	/*
+	 * NB: We use the PMCS_OQ_GENERAL outbound queue
+	 * NB: so as to not get entangled in normal I/O
+	 * NB: processing.
+	 */
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
+	    PMCIN_SSP_INI_TM_START));
+	msg[1] = LE_32(pwrk->htag);
+	msg[2] = LE_32(pptr->device_id);
+	if (tmf == SAS_ABORT_TASK || tmf == SAS_QUERY_TASK) {
+		msg[3] = LE_32(tag);
+	} else {
+		msg[3] = 0;
+	}
+	msg[4] = LE_32(tmf);
+	msg[5] = BE_32((uint32_t)lun);
+	msg[6] = BE_32((uint32_t)(lun >> 32));
+	msg[7] = LE_32(PMCIN_MESSAGE_REPORT);
+
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (ptr == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		pmcs_pwork(pwp, pwrk);
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
+		return (ENOMEM);
+	}
+	COPY_MESSAGE(ptr, msg, 7);
+	pwrk->arg = msg;
+	pwrk->dtype = pptr->dtype;
+
+	xp = pptr->target;
+	if (xp != NULL) {
+		mutex_enter(&xp->statlock);
+		if (xp->dev_state == PMCS_DEVICE_STATE_NON_OPERATIONAL) {
+			mutex_exit(&xp->statlock);
+			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+			pmcs_pwork(pwp, pwrk);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Not sending '%s'"
+			    " because DS is '%s'", __func__, pmcs_tmf2str(tmf),
+			    pmcs_status_str
+			    (PMCOUT_STATUS_IO_DS_NON_OPERATIONAL));
+			return (EIO);
+		}
+		mutex_exit(&xp->statlock);
+	}
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG,
+	    "%s: sending '%s' to %s (lun %llu) tag 0x%x", __func__,
+	    pmcs_tmf2str(tmf), pptr->path, (unsigned long long) lun, tag);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	pmcs_unlock_phy(pptr);
+	/*
+	 * This is a command sent to the target device, so it can take
+	 * significant amount of time to complete when path & device is busy.
+	 * Set a timeout to 20 seconds
+	 */
+	WAIT_FOR(pwrk, 20000, result);
+	pmcs_lock_phy(pptr);
+	pmcs_pwork(pwp, pwrk);
+
+	if (result) {
+		if (xp == NULL) {
+			return (ETIMEDOUT);
+		}
+
+		mutex_enter(&xp->statlock);
+		pmcs_start_dev_state_recovery(xp, pptr);
+		mutex_exit(&xp->statlock);
+		return (ETIMEDOUT);
+	}
+
+	status = LE_32(msg[2]);
+	if (status != PMCOUT_STATUS_OK) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: status %s for TMF %s action to %s, lun %llu",
+		    __func__, pmcs_status_str(status),  pmcs_tmf2str(tmf),
+		    pptr->path, (unsigned long long) lun);
+		if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) ||
+		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) ||
+		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) {
+			ds = PMCS_DEVICE_STATE_NON_OPERATIONAL;
+		} else if (status == PMCOUT_STATUS_IO_DS_IN_RECOVERY) {
+			/*
+			 * If the status is IN_RECOVERY, it's an indication
+			 * that it's now time for us to request to have the
+			 * device state set to OPERATIONAL since we're the ones
+			 * that requested recovery to begin with.
+			 */
+			ds = PMCS_DEVICE_STATE_OPERATIONAL;
+		} else {
+			ds = PMCS_DEVICE_STATE_IN_RECOVERY;
+		}
+		if (xp != NULL) {
+			mutex_enter(&xp->statlock);
+			if (xp->dev_state != ds) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "%s: Sending err recovery cmd"
+				    " for tgt 0x%p (status = %s)",
+				    __func__, (void *)xp,
+				    pmcs_status_str(status));
+				(void) pmcs_send_err_recovery_cmd(pwp, ds, xp);
+			}
+			mutex_exit(&xp->statlock);
+		}
+		return (EIO);
+	} else {
+		ds = PMCS_DEVICE_STATE_OPERATIONAL;
+		if (xp != NULL) {
+			mutex_enter(&xp->statlock);
+			if (xp->dev_state != ds) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "%s: Sending err recovery cmd"
+				    " for tgt 0x%p (status = %s)",
+				    __func__, (void *)xp,
+				    pmcs_status_str(status));
+				(void) pmcs_send_err_recovery_cmd(pwp, ds, xp);
+			}
+			mutex_exit(&xp->statlock);
+		}
+	}
+	if (LE_32(msg[3]) == 0) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "TMF completed with no response");
+		return (EIO);
+	}
+	pmcs_endian_transform(pwp, local, &msg[5], ssp_rsp_evec);
+	xd = (uint8_t *)(&msg[5]);
+	xd += SAS_RSP_HDR_SIZE;
+	if (rptr->datapres != SAS_RSP_DATAPRES_RESPONSE_DATA) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: TMF response not RESPONSE DATA (0x%x)",
+		    __func__, rptr->datapres);
+		return (EIO);
+	}
+	if (rptr->response_data_length != 4) {
+		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
+		    "Bad SAS RESPONSE DATA LENGTH", msg);
+		return (EIO);
+	}
+	(void) memcpy(&status, xd, sizeof (uint32_t));
+	status = BE_32(status);
+	if (response != NULL)
+		*response = status;
+	/*
+	 * The status is actually in the low-order byte.  The upper three
+	 * bytes contain additional information for the TMFs that support them.
+	 * However, at this time we do not issue any of those.  In the other
+	 * cases, the upper three bytes are supposed to be 0, but it appears
+	 * they aren't always.  Just mask them off.
+	 */
+	switch (status & 0xff) {
+	case SAS_RSP_TMF_COMPLETE:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: TMF complete", __func__);
+		result = 0;
+		break;
+	case SAS_RSP_TMF_SUCCEEDED:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: TMF succeeded", __func__);
+		result = 0;
+		break;
+	case SAS_RSP_INVALID_FRAME:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: TMF returned INVALID FRAME", __func__);
+		result = EIO;
+		break;
+	case SAS_RSP_TMF_NOT_SUPPORTED:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: TMF returned TMF NOT SUPPORTED", __func__);
+		result = EIO;
+		break;
+	case SAS_RSP_TMF_FAILED:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: TMF returned TMF FAILED", __func__);
+		result = EIO;
+		break;
+	case SAS_RSP_TMF_INCORRECT_LUN:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: TMF returned INCORRECT LUN", __func__);
+		result = EIO;
+		break;
+	case SAS_RSP_OVERLAPPED_OIPTTA:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: TMF returned OVERLAPPED INITIATOR PORT TRANSFER TAG "
+		    "ATTEMPTED", __func__);
+		result = EIO;
+		break;
+	default:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: TMF returned unknown code 0x%x", __func__, status);
+		result = EIO;
+		break;
+	}
+	return (result);
+}
+
+/*
+ * Called with PHY lock held and scratch acquired
+ */
+int
+pmcs_sata_abort_ncq(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	const char *utag_fail_fmt = "%s: untagged NCQ command failure";
+	const char *tag_fail_fmt = "%s: NCQ command failure (tag 0x%x)";
+	uint32_t msg[PMCS_QENTRY_SIZE], *ptr, result, status;
+	uint8_t *fp = pwp->scratch, ds;
+	fis_t fis;
+	pmcwork_t *pwrk;
+	pmcs_xscsi_t *tgt;
+
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
+	if (pwrk == NULL) {
+		return (ENOMEM);
+	}
+	msg[0] = LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE,
+	    PMCIN_SATA_HOST_IO_START));
+	msg[1] = LE_32(pwrk->htag);
+	msg[2] = LE_32(pptr->device_id);
+	msg[3] = LE_32(512);
+	msg[4] = LE_32(SATA_PROTOCOL_PIO | PMCIN_DATADIR_2_INI);
+	msg[5] = LE_32((READ_LOG_EXT << 16) | (C_BIT << 8) | FIS_REG_H2DEV);
+	msg[6] = LE_32(0x10);
+	msg[8] = LE_32(1);
+	msg[9] = 0;
+	msg[10] = 0;
+	msg[11] = 0;
+	msg[12] = LE_32(DWORD0(pwp->scratch_dma));
+	msg[13] = LE_32(DWORD1(pwp->scratch_dma));
+	msg[14] = LE_32(512);
+	msg[15] = 0;
+
+	pwrk->arg = msg;
+	pwrk->dtype = pptr->dtype;
+
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (ptr == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		pmcs_pwork(pwp, pwrk);
+		return (ENOMEM);
+	}
+	COPY_MESSAGE(ptr, msg, PMCS_QENTRY_SIZE);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	pmcs_unlock_phy(pptr);
+	WAIT_FOR(pwrk, 250, result);
+	pmcs_lock_phy(pptr);
+	pmcs_pwork(pwp, pwrk);
+
+	if (result) {
+		pmcs_prt(pwp, PMCS_PRT_INFO, pmcs_timeo, __func__);
+		return (EIO);
+	}
+	status = LE_32(msg[2]);
+	if (status != PMCOUT_STATUS_OK || LE_32(msg[3])) {
+		tgt = pptr->target;
+		if (tgt == NULL) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: cannot find target for phy 0x%p for "
+			    "dev state recovery", __func__, (void *)pptr);
+			return (EIO);
+		}
+
+		mutex_enter(&tgt->statlock);
+
+		pmcs_print_entry(pwp, PMCS_PRT_DEBUG, "READ LOG EXT", msg);
+		if ((status == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) ||
+		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK) ||
+		    (status == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS)) {
+			ds = PMCS_DEVICE_STATE_NON_OPERATIONAL;
+		} else {
+			ds = PMCS_DEVICE_STATE_IN_RECOVERY;
+		}
+		if (tgt->dev_state != ds) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Trying SATA DS Error"
+			    " Recovery for tgt(0x%p) for status(%s)",
+			    __func__, (void *)tgt, pmcs_status_str(status));
+			(void) pmcs_send_err_recovery_cmd(pwp, ds, tgt);
+		}
+
+		mutex_exit(&tgt->statlock);
+		return (EIO);
+	}
+	fis[0] = (fp[4] << 24) | (fp[3] << 16) | (fp[2] << 8) | FIS_REG_D2H;
+	fis[1] = (fp[8] << 24) | (fp[7] << 16) | (fp[6] << 8) | fp[5];
+	fis[2] = (fp[12] << 24) | (fp[11] << 16) | (fp[10] << 8) | fp[9];
+	fis[3] = (fp[16] << 24) | (fp[15] << 16) | (fp[14] << 8) | fp[13];
+	fis[4] = 0;
+	if (fp[0] & 0x80) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, utag_fail_fmt, __func__);
+	} else {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, tag_fail_fmt, __func__,
+		    fp[0] & 0x1f);
+	}
+	pmcs_fis_dump(pwp, fis);
+	pptr->need_rl_ext = 0;
+	return (0);
+}
+
+/*
+ * Transform a structure from CPU to Device endian format, or
+ * vice versa, based upon a transformation vector.
+ *
+ * A transformation vector is an array of bytes, each byte
+ * of which is defined thusly:
+ *
+ *  bit 7: from CPU to desired endian, otherwise from desired endian
+ *	   to CPU format
+ *  bit 6: Big Endian, else Little Endian
+ *  bits 5-4:
+ *       00 Undefined
+ *       01 One Byte quantities
+ *       02 Two Byte quantities
+ *       03 Four Byte quantities
+ *
+ *  bits 3-0:
+ *       00 Undefined
+ *       Number of quantities to transform
+ *
+ * The vector is terminated by a 0 value.
+ */
+
+void
+pmcs_endian_transform(pmcs_hw_t *pwp, void *orig_out, void *orig_in,
+    const uint8_t *xfvec)
+{
+	uint8_t c, *out = orig_out, *in = orig_in;
+
+	if (xfvec == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: null xfvec", __func__);
+		return;
+	}
+	if (out == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: null out", __func__);
+		return;
+	}
+	if (in == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: null in", __func__);
+		return;
+	}
+	while ((c = *xfvec++) != 0) {
+		int nbyt = (c & 0xf);
+		int size = (c >> 4) & 0x3;
+		int bige = (c >> 4) & 0x4;
+
+		switch (size) {
+		case 1:
+		{
+			while (nbyt-- > 0) {
+				*out++ = *in++;
+			}
+			break;
+		}
+		case 2:
+		{
+			uint16_t tmp;
+			while (nbyt-- > 0) {
+				(void) memcpy(&tmp, in, sizeof (uint16_t));
+				if (bige) {
+					tmp = BE_16(tmp);
+				} else {
+					tmp = LE_16(tmp);
+				}
+				(void) memcpy(out, &tmp, sizeof (uint16_t));
+				out += sizeof (uint16_t);
+				in += sizeof (uint16_t);
+			}
+			break;
+		}
+		case 3:
+		{
+			uint32_t tmp;
+			while (nbyt-- > 0) {
+				(void) memcpy(&tmp, in, sizeof (uint32_t));
+				if (bige) {
+					tmp = BE_32(tmp);
+				} else {
+					tmp = LE_32(tmp);
+				}
+				(void) memcpy(out, &tmp, sizeof (uint32_t));
+				out += sizeof (uint32_t);
+				in += sizeof (uint32_t);
+			}
+			break;
+		}
+		default:
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: bad size", __func__);
+			return;
+		}
+	}
+}
+
+const char *
+pmcs_get_rate(unsigned int linkrt)
+{
+	const char *rate;
+	switch (linkrt) {
+	case SAS_LINK_RATE_1_5GBIT:
+		rate = "1.5";
+		break;
+	case SAS_LINK_RATE_3GBIT:
+		rate = "3.0";
+		break;
+	case SAS_LINK_RATE_6GBIT:
+		rate = "6.0";
+		break;
+	default:
+		rate = "???";
+		break;
+	}
+	return (rate);
+}
+
+const char *
+pmcs_get_typename(pmcs_dtype_t type)
+{
+	switch (type) {
+	case NOTHING:
+		return ("NIL");
+	case SATA:
+		return ("SATA");
+	case SAS:
+		return ("SSP");
+	case EXPANDER:
+		return ("EXPANDER");
+	}
+	return ("????");
+}
+
+const char *
+pmcs_tmf2str(int tmf)
+{
+	switch (tmf) {
+	case SAS_ABORT_TASK:
+		return ("Abort Task");
+	case SAS_ABORT_TASK_SET:
+		return ("Abort Task Set");
+	case SAS_CLEAR_TASK_SET:
+		return ("Clear Task Set");
+	case SAS_LOGICAL_UNIT_RESET:
+		return ("Logical Unit Reset");
+	case SAS_I_T_NEXUS_RESET:
+		return ("I_T Nexus Reset");
+	case SAS_CLEAR_ACA:
+		return ("Clear ACA");
+	case SAS_QUERY_TASK:
+		return ("Query Task");
+	case SAS_QUERY_TASK_SET:
+		return ("Query Task Set");
+	case SAS_QUERY_UNIT_ATTENTION:
+		return ("Query Unit Attention");
+	default:
+		return ("Unknown");
+	}
+}
+
+const char *
+pmcs_status_str(uint32_t status)
+{
+	switch (status) {
+	case PMCOUT_STATUS_OK:
+		return ("OK");
+	case PMCOUT_STATUS_ABORTED:
+		return ("ABORTED");
+	case PMCOUT_STATUS_OVERFLOW:
+		return ("OVERFLOW");
+	case PMCOUT_STATUS_UNDERFLOW:
+		return ("UNDERFLOW");
+	case PMCOUT_STATUS_FAILED:
+		return ("FAILED");
+	case PMCOUT_STATUS_ABORT_RESET:
+		return ("ABORT_RESET");
+	case PMCOUT_STATUS_IO_NOT_VALID:
+		return ("IO_NOT_VALID");
+	case PMCOUT_STATUS_NO_DEVICE:
+		return ("NO_DEVICE");
+	case PMCOUT_STATUS_ILLEGAL_PARAMETER:
+		return ("ILLEGAL_PARAMETER");
+	case PMCOUT_STATUS_LINK_FAILURE:
+		return ("LINK_FAILURE");
+	case PMCOUT_STATUS_PROG_ERROR:
+		return ("PROG_ERROR");
+	case PMCOUT_STATUS_EDC_IN_ERROR:
+		return ("EDC_IN_ERROR");
+	case PMCOUT_STATUS_EDC_OUT_ERROR:
+		return ("EDC_OUT_ERROR");
+	case PMCOUT_STATUS_ERROR_HW_TIMEOUT:
+		return ("ERROR_HW_TIMEOUT");
+	case PMCOUT_STATUS_XFER_ERR_BREAK:
+		return ("XFER_ERR_BREAK");
+	case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
+		return ("XFER_ERR_PHY_NOT_READY");
+	case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
+		return ("OPEN_CNX_PROTOCOL_NOT_SUPPORTED");
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
+		return ("OPEN_CNX_ERROR_ZONE_VIOLATION");
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
+		return ("OPEN_CNX_ERROR_BREAK");
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+		return ("OPEN_CNX_ERROR_IT_NEXUS_LOSS");
+	case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
+		return ("OPENCNX_ERROR_BAD_DESTINATION");
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+		return ("OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED");
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
+		return ("OPEN_CNX_ERROR_STP_RESOURCES_BUSY");
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
+		return ("OPEN_CNX_ERROR_WRONG_DESTINATION");
+	case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_EROOR:
+		return ("OPEN_CNX_ERROR_UNKNOWN_EROOR");
+	case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
+		return ("IO_XFER_ERROR_NAK_RECEIVED");
+	case PMCOUT_STATUS_XFER_ERROR_ACK_NAK_TIMEOUT:
+		return ("XFER_ERROR_ACK_NAK_TIMEOUT");
+	case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED:
+		return ("XFER_ERROR_PEER_ABORTED");
+	case PMCOUT_STATUS_XFER_ERROR_RX_FRAME:
+		return ("XFER_ERROR_RX_FRAME");
+	case PMCOUT_STATUS_IO_XFER_ERROR_DMA:
+		return ("IO_XFER_ERROR_DMA");
+	case PMCOUT_STATUS_XFER_ERROR_CREDIT_TIMEOUT:
+		return ("XFER_ERROR_CREDIT_TIMEOUT");
+	case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT:
+		return ("XFER_ERROR_SATA_LINK_TIMEOUT");
+	case PMCOUT_STATUS_XFER_ERROR_SATA:
+		return ("XFER_ERROR_SATA");
+	case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE:
+		return ("XFER_ERROR_REJECTED_NCQ_MODE");
+	case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST:
+		return ("XFER_ERROR_ABORTED_DUE_TO_SRST");
+	case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE:
+		return ("XFER_ERROR_ABORTED_NCQ_MODE");
+	case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
+		return ("IO_XFER_OPEN_RETRY_TIMEOUT");
+	case PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR:
+		return ("SMP_RESP_CONNECTION_ERROR");
+	case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE:
+		return ("XFER_ERROR_UNEXPECTED_PHASE");
+	case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN:
+		return ("XFER_ERROR_RDY_OVERRUN");
+	case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED:
+		return ("XFER_ERROR_RDY_NOT_EXPECTED");
+	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
+		return ("XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT");
+	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK:
+		return ("XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK");
+	case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK:
+		return ("XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK");
+	case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH:
+		return ("XFER_ERROR_OFFSET_MISMATCH");
+	case PMCOUT_STATUS_XFER_ERROR_ZERO_DATA_LEN:
+		return ("XFER_ERROR_ZERO_DATA_LEN");
+	case PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED:
+		return ("XFER_CMD_FRAME_ISSUED");
+	case PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE:
+		return ("ERROR_INTERNAL_SMP_RESOURCE");
+	case PMCOUT_STATUS_IO_PORT_IN_RESET:
+		return ("IO_PORT_IN_RESET");
+	case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
+		return ("DEVICE STATE NON-OPERATIONAL");
+	case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
+		return ("DEVICE STATE IN RECOVERY");
+	default:
+		return (NULL);
+	}
+}
+
+uint64_t
+pmcs_barray2wwn(uint8_t ba[8])
+{
+	uint64_t result = 0;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		result <<= 8;
+		result |= ba[i];
+	}
+	return (result);
+}
+
+void
+pmcs_wwn2barray(uint64_t wwn, uint8_t ba[8])
+{
+	int i;
+	for (i = 0; i < 8; i++) {
+		ba[7 - i] = wwn & 0xff;
+		wwn >>= 8;
+	}
+}
+
+void
+pmcs_report_fwversion(pmcs_hw_t *pwp)
+{
+	const char *fwsupport;
+	switch (PMCS_FW_TYPE(pwp)) {
+	case PMCS_FW_TYPE_RELEASED:
+		fwsupport = "Released";
+		break;
+	case PMCS_FW_TYPE_DEVELOPMENT:
+		fwsupport = "Development";
+		break;
+	case PMCS_FW_TYPE_ALPHA:
+		fwsupport = "Alpha";
+		break;
+	case PMCS_FW_TYPE_BETA:
+		fwsupport = "Beta";
+		break;
+	default:
+		fwsupport = "Special";
+		break;
+	}
+	pmcs_prt(pwp, PMCS_PRT_INFO,
+	    "Chip Revision: %c; F/W Revision %x.%x.%x %s", 'A' + pwp->chiprev,
+	    PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), PMCS_FW_MICRO(pwp),
+	    fwsupport);
+}
+
+void
+pmcs_phy_name(pmcs_hw_t *pwp, pmcs_phy_t *pptr, char *obuf, size_t olen)
+{
+	if (pptr->parent) {
+		pmcs_phy_name(pwp, pptr->parent, obuf, olen);
+		(void) snprintf(obuf, olen, "%s.%02x", obuf, pptr->phynum);
+	} else {
+		(void) snprintf(obuf, olen, "pp%02x", pptr->phynum);
+	}
+}
+
+/*
+ * Implementation for pmcs_find_phy_by_devid.
+ * If the PHY is found, it is returned locked.
+ */
+static pmcs_phy_t *
+pmcs_find_phy_by_devid_impl(pmcs_phy_t *phyp, uint32_t device_id)
+{
+	pmcs_phy_t *match, *cphyp, *nphyp;
+
+	ASSERT(!mutex_owned(&phyp->phy_lock));
+
+	while (phyp) {
+		pmcs_lock_phy(phyp);
+
+		if ((phyp->valid_device_id) && (phyp->device_id == device_id)) {
+			return (phyp);
+		}
+		if (phyp->children) {
+			cphyp = phyp->children;
+			pmcs_unlock_phy(phyp);
+			match = pmcs_find_phy_by_devid_impl(cphyp, device_id);
+			if (match) {
+				ASSERT(mutex_owned(&match->phy_lock));
+				return (match);
+			}
+			pmcs_lock_phy(phyp);
+		}
+
+		if (IS_ROOT_PHY(phyp)) {
+			pmcs_unlock_phy(phyp);
+			phyp = NULL;
+		} else {
+			nphyp = phyp->sibling;
+			pmcs_unlock_phy(phyp);
+			phyp = nphyp;
+		}
+	}
+
+	return (NULL);
+}
+
+/*
+ * If the PHY is found, it is returned locked
+ */
+pmcs_phy_t *
+pmcs_find_phy_by_devid(pmcs_hw_t *pwp, uint32_t device_id)
+{
+	pmcs_phy_t *phyp, *match = NULL;
+
+	phyp = pwp->root_phys;
+
+	while (phyp) {
+		match = pmcs_find_phy_by_devid_impl(phyp, device_id);
+		if (match) {
+			ASSERT(mutex_owned(&match->phy_lock));
+			return (match);
+		}
+		phyp = phyp->sibling;
+	}
+
+	return (NULL);
+}
+
+/*
+ * This function is called as a sanity check to ensure that a newly registered
+ * PHY doesn't have a device_id that exists with another registered PHY.
+ */
+static boolean_t
+pmcs_validate_devid(pmcs_phy_t *parent, pmcs_phy_t *phyp, uint32_t device_id)
+{
+	pmcs_phy_t *pptr;
+	boolean_t rval;
+
+	pptr = parent;
+
+	while (pptr) {
+		if (pptr->valid_device_id && (pptr != phyp) &&
+		    (pptr->device_id == device_id)) {
+			pmcs_prt(pptr->pwp, PMCS_PRT_DEBUG,
+			    "%s: phy %s already exists as %s with "
+			    "device id 0x%x", __func__, phyp->path,
+			    pptr->path, device_id);
+			return (B_FALSE);
+		}
+
+		if (pptr->children) {
+			rval = pmcs_validate_devid(pptr->children, phyp,
+			    device_id);
+			if (rval == B_FALSE) {
+				return (rval);
+			}
+		}
+
+		pptr = pptr->sibling;
+	}
+
+	/* This PHY and device_id are valid */
+	return (B_TRUE);
+}
+
+/*
+ * If the PHY is found, it is returned locked
+ */
+static pmcs_phy_t *
+pmcs_find_phy_by_wwn_impl(pmcs_phy_t *phyp, uint8_t *wwn)
+{
+	pmcs_phy_t *matched_phy, *cphyp, *nphyp;
+
+	ASSERT(!mutex_owned(&phyp->phy_lock));
+
+	while (phyp) {
+		pmcs_lock_phy(phyp);
+
+		if (phyp->valid_device_id) {
+			if (memcmp(phyp->sas_address, wwn, 8) == 0) {
+				return (phyp);
+			}
+		}
+
+		if (phyp->children) {
+			cphyp = phyp->children;
+			pmcs_unlock_phy(phyp);
+			matched_phy = pmcs_find_phy_by_wwn_impl(cphyp, wwn);
+			if (matched_phy) {
+				ASSERT(mutex_owned(&matched_phy->phy_lock));
+				return (matched_phy);
+			}
+			pmcs_lock_phy(phyp);
+		}
+
+		/*
+		 * Only iterate through non-root PHYs
+		 */
+		if (IS_ROOT_PHY(phyp)) {
+			pmcs_unlock_phy(phyp);
+			phyp = NULL;
+		} else {
+			nphyp = phyp->sibling;
+			pmcs_unlock_phy(phyp);
+			phyp = nphyp;
+		}
+	}
+
+	return (NULL);
+}
+
+pmcs_phy_t *
+pmcs_find_phy_by_wwn(pmcs_hw_t *pwp, uint64_t wwn)
+{
+	uint8_t ebstr[8];
+	pmcs_phy_t *pptr, *matched_phy;
+
+	pmcs_wwn2barray(wwn, ebstr);
+
+	pptr = pwp->root_phys;
+	while (pptr) {
+		matched_phy = pmcs_find_phy_by_wwn_impl(pptr, ebstr);
+		if (matched_phy) {
+			ASSERT(mutex_owned(&matched_phy->phy_lock));
+			return (matched_phy);
+		}
+
+		pptr = pptr->sibling;
+	}
+
+	return (NULL);
+}
+
+
+/*
+ * pmcs_find_phy_by_sas_address
+ *
+ * Find a PHY that both matches "sas_addr" and is on "iport".
+ * If a matching PHY is found, it is returned locked.
+ */
+pmcs_phy_t *
+pmcs_find_phy_by_sas_address(pmcs_hw_t *pwp, pmcs_iport_t *iport,
+    pmcs_phy_t *root, char *sas_addr)
+{
+	int ua_form = 1;
+	uint64_t wwn;
+	char addr[PMCS_MAX_UA_SIZE];
+	pmcs_phy_t *pptr, *pnext, *pchild;
+
+	if (root == NULL) {
+		pptr = pwp->root_phys;
+	} else {
+		pptr = root;
+	}
+
+	while (pptr) {
+		pmcs_lock_phy(pptr);
+		/*
+		 * If the PHY is dead or does not have a valid device ID,
+		 * skip it.
+		 */
+		if ((pptr->dead) || (!pptr->valid_device_id)) {
+			goto next_phy;
+		}
+
+		if (pptr->iport != iport) {
+			goto next_phy;
+		}
+
+		wwn = pmcs_barray2wwn(pptr->sas_address);
+		(void *) scsi_wwn_to_wwnstr(wwn, ua_form, addr);
+		if (strncmp(addr, sas_addr, strlen(addr)) == 0) {
+			return (pptr);
+		}
+
+		if (pptr->children) {
+			pchild = pptr->children;
+			pmcs_unlock_phy(pptr);
+			pnext = pmcs_find_phy_by_sas_address(pwp, iport, pchild,
+			    sas_addr);
+			if (pnext) {
+				return (pnext);
+			}
+			pmcs_lock_phy(pptr);
+		}
+
+next_phy:
+		pnext = pptr->sibling;
+		pmcs_unlock_phy(pptr);
+		pptr = pnext;
+	}
+
+	return (NULL);
+}
+
+void
+pmcs_fis_dump(pmcs_hw_t *pwp, fis_t fis)
+{
+	switch (fis[0] & 0xff) {
+	case FIS_REG_H2DEV:
+		pmcs_prt(pwp, PMCS_PRT_INFO, "FIS REGISTER HOST TO DEVICE: "
+		    "OP=0x%02x Feature=0x%04x Count=0x%04x Device=0x%02x "
+		    "LBA=%llu", BYTE2(fis[0]), BYTE3(fis[2]) << 8 |
+		    BYTE3(fis[0]), WORD0(fis[3]), BYTE3(fis[1]),
+		    (unsigned long long)
+		    (((uint64_t)fis[2] & 0x00ffffff) << 24 |
+		    ((uint64_t)fis[1] & 0x00ffffff)));
+		break;
+	case FIS_REG_D2H:
+		pmcs_prt(pwp, PMCS_PRT_INFO, "FIS REGISTER DEVICE TO HOST: Stat"
+		    "us=0x%02x Error=0x%02x Dev=0x%02x Count=0x%04x LBA=%llu",
+		    BYTE2(fis[0]), BYTE3(fis[0]), BYTE3(fis[1]), WORD0(fis[3]),
+		    (unsigned long long)(((uint64_t)fis[2] & 0x00ffffff) << 24 |
+		    ((uint64_t)fis[1] & 0x00ffffff)));
+		break;
+	default:
+		pmcs_prt(pwp, PMCS_PRT_INFO, "FIS: 0x%08x 0x%08x 0x%08x 0x%08x "
+		    "0x%08x 0x%08x 0x%08x",
+		    fis[0], fis[1], fis[2], fis[3], fis[4], fis[5], fis[6]);
+		break;
+	}
+}
+
+void
+pmcs_print_entry(pmcs_hw_t *pwp, int level, char *msg, void *arg)
+{
+	uint32_t *mb = arg;
+	size_t i;
+
+	pmcs_prt(pwp, level, msg);
+	for (i = 0; i < (PMCS_QENTRY_SIZE / sizeof (uint32_t)); i += 4) {
+		pmcs_prt(pwp, level, "Offset %2lu: 0x%08x 0x%08x 0x%08"
+		    "x 0x%08x", i * sizeof (uint32_t), LE_32(mb[i]),
+		    LE_32(mb[i+1]), LE_32(mb[i+2]),
+		    LE_32(mb[i+3]));
+	}
+}
+
+/*
+ * If phyp == NULL we're being called from the worker thread, in which
+ * case we need to check all the PHYs.  In this case, the softstate lock
+ * will be held.
+ * If phyp is non-NULL, just issue the spinup release for the specified PHY
+ * (which will already be locked).
+ */
+void
+pmcs_spinup_release(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
+{
+	uint32_t *msg;
+	struct pmcwork *pwrk;
+	pmcs_phy_t *tphyp;
+
+	if (phyp != NULL) {
+		ASSERT(mutex_owned(&phyp->phy_lock));
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: Issuing spinup release only for PHY %s", __func__,
+		    phyp->path);
+		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+		if (msg == NULL || (pwrk =
+		    pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
+			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+			SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE);
+			return;
+		}
+
+		phyp->spinup_hold = 0;
+		bzero(msg, PMCS_QENTRY_SIZE);
+		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
+		    PMCIN_LOCAL_PHY_CONTROL));
+		msg[1] = LE_32(pwrk->htag);
+		msg[2] = LE_32((0x10 << 8) | phyp->phynum);
+
+		pwrk->dtype = phyp->dtype;
+		pwrk->state = PMCS_WORK_STATE_ONCHIP;
+		mutex_exit(&pwrk->lock);
+		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+		return;
+	}
+
+	ASSERT(mutex_owned(&pwp->lock));
+
+	tphyp = pwp->root_phys;
+	while (tphyp) {
+		pmcs_lock_phy(tphyp);
+		if (tphyp->spinup_hold == 0) {
+			pmcs_unlock_phy(tphyp);
+			tphyp = tphyp->sibling;
+			continue;
+		}
+
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: Issuing spinup release for PHY %s", __func__,
+		    phyp->path);
+
+		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		msg = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+		if (msg == NULL || (pwrk =
+		    pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
+			pmcs_unlock_phy(tphyp);
+			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+			SCHEDULE_WORK(pwp, PMCS_WORK_SPINUP_RELEASE);
+			break;
+		}
+
+		tphyp->spinup_hold = 0;
+		bzero(msg, PMCS_QENTRY_SIZE);
+		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
+		    PMCIN_LOCAL_PHY_CONTROL));
+		msg[1] = LE_32(pwrk->htag);
+		msg[2] = LE_32((0x10 << 8) | tphyp->phynum);
+
+		pwrk->dtype = phyp->dtype;
+		pwrk->state = PMCS_WORK_STATE_ONCHIP;
+		mutex_exit(&pwrk->lock);
+		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+		pmcs_unlock_phy(tphyp);
+
+		tphyp = tphyp->sibling;
+	}
+}
+
+/*
+ * Abort commands on dead PHYs and deregister them as well as removing
+ * the associated targets.
+ */
+static int
+pmcs_kill_devices(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
+{
+	pmcs_phy_t *pnext, *pchild;
+	boolean_t remove_device;
+	int rval = 0;
+
+	while (phyp) {
+		pmcs_lock_phy(phyp);
+		pchild = phyp->children;
+		pnext = phyp->sibling;
+		pmcs_unlock_phy(phyp);
+
+		if (pchild) {
+			rval = pmcs_kill_devices(pwp, pchild);
+			if (rval) {
+				return (rval);
+			}
+		}
+
+		/*
+		 * pmcs_remove_device requires the softstate lock.
+		 */
+		mutex_enter(&pwp->lock);
+		pmcs_lock_phy(phyp);
+		if (phyp->dead && phyp->valid_device_id) {
+			remove_device = B_TRUE;
+		} else {
+			remove_device = B_FALSE;
+		}
+
+		if (remove_device) {
+			pmcs_remove_device(pwp, phyp);
+			mutex_exit(&pwp->lock);
+
+			rval = pmcs_kill_device(pwp, phyp);
+
+			if (rval) {
+				pmcs_unlock_phy(phyp);
+				return (rval);
+			}
+		} else {
+			mutex_exit(&pwp->lock);
+		}
+
+		pmcs_unlock_phy(phyp);
+		phyp = pnext;
+	}
+
+	return (rval);
+}
+
+/*
+ * Called with PHY locked
+ */
+int
+pmcs_kill_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr)
+{
+	int r, result;
+	uint32_t msg[PMCS_MSG_SIZE], *ptr, status;
+	struct pmcwork *pwrk;
+	pmcs_xscsi_t *tgt;
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG, "kill %s device @ %s",
+	    pmcs_get_typename(pptr->dtype), pptr->path);
+
+	/*
+	 * There may be an outstanding ABORT_ALL running, which we wouldn't
+	 * know just by checking abort_pending.  We can, however, check
+	 * abort_all_start.  If it's non-zero, there is one, and we'll just
+	 * sit here and wait for it to complete.  If we don't, we'll remove
+	 * the device while there are still commands pending.
+	 */
+	if (pptr->abort_all_start) {
+		while (pptr->abort_all_start) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: Waiting for outstanding ABORT_ALL on PHY 0x%p",
+			    __func__, (void *)pptr);
+			cv_wait(&pptr->abort_all_cv, &pptr->phy_lock);
+		}
+	} else if (pptr->abort_pending) {
+		r = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1);
+
+		if (r) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: ABORT_ALL returned non-zero status (%d) for "
+			    "PHY 0x%p", __func__, r, (void *)pptr);
+			return (r);
+		}
+		pptr->abort_pending = 0;
+	}
+
+	/*
+	 * Now that everything is aborted from the chip's perspective (or even
+	 * if it is not), flush out the wait queue.  We won't flush the active
+	 * queue since it is possible that abort completions may follow after
+	 * the notification that the abort all has completed.
+	 */
+	tgt = pptr->target;
+	if (tgt) {
+		mutex_enter(&tgt->statlock);
+		pmcs_flush_target_queues(pwp, tgt, PMCS_TGT_WAIT_QUEUE);
+		mutex_exit(&tgt->statlock);
+	}
+
+	if (pptr->valid_device_id == 0) {
+		return (0);
+	}
+
+	if ((pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr)) == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
+		return (ENOMEM);
+	}
+	pwrk->arg = msg;
+	pwrk->dtype = pptr->dtype;
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
+	    PMCIN_DEREGISTER_DEVICE_HANDLE));
+	msg[1] = LE_32(pwrk->htag);
+	msg[2] = LE_32(pptr->device_id);
+
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (ptr == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		mutex_exit(&pwrk->lock);
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
+		return (ENOMEM);
+	}
+
+	COPY_MESSAGE(ptr, msg, 3);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	pmcs_unlock_phy(pptr);
+	WAIT_FOR(pwrk, 250, result);
+	pmcs_lock_phy(pptr);
+	pmcs_pwork(pwp, pwrk);
+
+	if (result) {
+		return (ETIMEDOUT);
+	}
+	status = LE_32(msg[2]);
+	if (status != PMCOUT_STATUS_OK) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: status 0x%x when trying to deregister device %s",
+		    __func__, status, pptr->path);
+	}
+
+	pptr->device_id = PMCS_INVALID_DEVICE_ID;
+	PHY_CHANGED(pwp, pptr);
+	RESTART_DISCOVERY(pwp);
+	pptr->valid_device_id = 0;
+	return (0);
+}
+
+/*
+ * Acknowledge the SAS h/w events that need acknowledgement.
+ * This is only needed for first level PHYs.
+ */
+void
+pmcs_ack_events(pmcs_hw_t *pwp)
+{
+	uint32_t msg[PMCS_MSG_SIZE], *ptr;
+	struct pmcwork *pwrk;
+	pmcs_phy_t *pptr;
+
+	for (pptr = pwp->root_phys; pptr; pptr = pptr->sibling) {
+		pmcs_lock_phy(pptr);
+		if (pptr->hw_event_ack == 0) {
+			pmcs_unlock_phy(pptr);
+			continue;
+		}
+		mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+		if ((ptr == NULL) || (pwrk =
+		    pmcs_gwork(pwp, PMCS_TAG_TYPE_NONE, NULL)) == NULL) {
+			mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+			pmcs_unlock_phy(pptr);
+			SCHEDULE_WORK(pwp, PMCS_WORK_SAS_HW_ACK);
+			break;
+		}
+
+		msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
+		    PMCIN_SAW_HW_EVENT_ACK));
+		msg[1] = LE_32(pwrk->htag);
+		msg[2] = LE_32(pptr->hw_event_ack);
+
+		mutex_exit(&pwrk->lock);
+		pwrk->dtype = pptr->dtype;
+		pptr->hw_event_ack = 0;
+		COPY_MESSAGE(ptr, msg, 3);
+		INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+		pmcs_unlock_phy(pptr);
+	}
+}
+
+/*
+ * Load DMA
+ */
+int
+pmcs_dma_load(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint32_t *msg)
+{
+	ddi_dma_cookie_t *sg;
+	pmcs_dmachunk_t *tc;
+	pmcs_dmasgl_t *sgl, *prior;
+	int seg, tsc;
+	uint64_t sgl_addr;
+
+	/*
+	 * If we have no data segments, we're done.
+	 */
+	if (CMD2PKT(sp)->pkt_numcookies == 0) {
+		return (0);
+	}
+
+	/*
+	 * Get the S/G list pointer.
+	 */
+	sg = CMD2PKT(sp)->pkt_cookies;
+
+	/*
+	 * If we only have one dma segment, we can directly address that
+	 * data within the Inbound message itself.
+	 */
+	if (CMD2PKT(sp)->pkt_numcookies == 1) {
+		msg[12] = LE_32(DWORD0(sg->dmac_laddress));
+		msg[13] = LE_32(DWORD1(sg->dmac_laddress));
+		msg[14] = LE_32(sg->dmac_size);
+		msg[15] = 0;
+		return (0);
+	}
+
+	/*
+	 * Otherwise, we'll need one or more external S/G list chunks.
+	 * Get the first one and its dma address into the Inbound message.
+	 */
+	mutex_enter(&pwp->dma_lock);
+	tc = pwp->dma_freelist;
+	if (tc == NULL) {
+		SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
+		mutex_exit(&pwp->dma_lock);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG2, "%s: out of SG lists", __func__);
+		return (-1);
+	}
+	pwp->dma_freelist = tc->nxt;
+	mutex_exit(&pwp->dma_lock);
+
+	tc->nxt = NULL;
+	sp->cmd_clist = tc;
+	sgl = tc->chunks;
+	(void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ);
+	sgl_addr = tc->addr;
+	msg[12] = LE_32(DWORD0(sgl_addr));
+	msg[13] = LE_32(DWORD1(sgl_addr));
+	msg[14] = 0;
+	msg[15] = LE_32(PMCS_DMASGL_EXTENSION);
+
+	prior = sgl;
+	tsc = 0;
+
+	for (seg = 0; seg < CMD2PKT(sp)->pkt_numcookies; seg++) {
+		/*
+		 * If the current segment count for this chunk is one less than
+		 * the number s/g lists per chunk and we have more than one seg
+		 * to go, we need another chunk. Get it, and make sure that the
+		 * tail end of the the previous chunk points the new chunk
+		 * (if remembering an offset can be called 'pointing to').
+		 *
+		 * Note that we can store the offset into our command area that
+		 * represents the new chunk in the length field of the part
+		 * that points the PMC chip at the next chunk- the PMC chip
+		 * ignores this field when the EXTENSION bit is set.
+		 *
+		 * This is required for dma unloads later.
+		 */
+		if (tsc == (PMCS_SGL_NCHUNKS - 1) &&
+		    seg < (CMD2PKT(sp)->pkt_numcookies - 1)) {
+			mutex_enter(&pwp->dma_lock);
+			tc = pwp->dma_freelist;
+			if (tc == NULL) {
+				SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS);
+				mutex_exit(&pwp->dma_lock);
+				pmcs_dma_unload(pwp, sp);
+				pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+				    "%s: out of SG lists", __func__);
+				return (-1);
+			}
+			pwp->dma_freelist = tc->nxt;
+			tc->nxt = sp->cmd_clist;
+			mutex_exit(&pwp->dma_lock);
+
+			sp->cmd_clist = tc;
+			(void) memset(tc->chunks, 0, PMCS_SGL_CHUNKSZ);
+			sgl = tc->chunks;
+			sgl_addr = tc->addr;
+			prior[PMCS_SGL_NCHUNKS-1].sglal =
+			    LE_32(DWORD0(sgl_addr));
+			prior[PMCS_SGL_NCHUNKS-1].sglah =
+			    LE_32(DWORD1(sgl_addr));
+			prior[PMCS_SGL_NCHUNKS-1].sglen = 0;
+			prior[PMCS_SGL_NCHUNKS-1].flags =
+			    LE_32(PMCS_DMASGL_EXTENSION);
+			prior = sgl;
+			tsc = 0;
+		}
+		sgl[tsc].sglal = LE_32(DWORD0(sg->dmac_laddress));
+		sgl[tsc].sglah = LE_32(DWORD1(sg->dmac_laddress));
+		sgl[tsc].sglen = LE_32(sg->dmac_size);
+		sgl[tsc++].flags = 0;
+		sg++;
+	}
+	return (0);
+}
+
+/*
+ * Unload DMA
+ */
+void
+pmcs_dma_unload(pmcs_hw_t *pwp, pmcs_cmd_t *sp)
+{
+	pmcs_dmachunk_t *cp;
+
+	mutex_enter(&pwp->dma_lock);
+	while ((cp = sp->cmd_clist) != NULL) {
+		sp->cmd_clist = cp->nxt;
+		cp->nxt = pwp->dma_freelist;
+		pwp->dma_freelist = cp;
+	}
+	mutex_exit(&pwp->dma_lock);
+}
+
+/*
+ * Take a chunk of consistent memory that has just been allocated and inserted
+ * into the cip indices and prepare it for DMA chunk usage and add it to the
+ * freelist.
+ *
+ * Called with dma_lock locked (except during attach when it's unnecessary)
+ */
+void
+pmcs_idma_chunks(pmcs_hw_t *pwp, pmcs_dmachunk_t *dcp,
+    pmcs_chunk_t *pchunk, unsigned long lim)
+{
+	unsigned long off, n;
+	pmcs_dmachunk_t *np = dcp;
+	pmcs_chunk_t *tmp_chunk;
+
+	if (pwp->dma_chunklist == NULL) {
+		pwp->dma_chunklist = pchunk;
+	} else {
+		tmp_chunk = pwp->dma_chunklist;
+		while (tmp_chunk->next) {
+			tmp_chunk = tmp_chunk->next;
+		}
+		tmp_chunk->next = pchunk;
+	}
+
+	/*
+	 * Install offsets into chunk lists.
+	 */
+	for (n = 0, off = 0; off < lim; off += PMCS_SGL_CHUNKSZ, n++) {
+		np->chunks = (void *)&pchunk->addrp[off];
+		np->addr = pchunk->dma_addr + off;
+		np->acc_handle = pchunk->acc_handle;
+		np->dma_handle = pchunk->dma_handle;
+		if ((off + PMCS_SGL_CHUNKSZ) < lim) {
+			np = np->nxt;
+		}
+	}
+	np->nxt = pwp->dma_freelist;
+	pwp->dma_freelist = dcp;
+	pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+	    "added %lu DMA chunks ", n);
+}
+
+/*
+ * Change the value of the interrupt coalescing timer.  This is done currently
+ * only for I/O completions.  If we're using the "auto clear" feature, it can
+ * be turned back on when interrupt coalescing is turned off and must be
+ * turned off when the coalescing timer is on.
+ * NOTE: PMCS_MSIX_GENERAL and PMCS_OQ_IODONE are the same value.  As long
+ * as that's true, we don't need to distinguish between them.
+ */
+
+void
+pmcs_set_intr_coal_timer(pmcs_hw_t *pwp, pmcs_coal_timer_adj_t adj)
+{
+	if (adj == DECREASE_TIMER) {
+		/* If the timer is already off, nothing to do. */
+		if (pwp->io_intr_coal.timer_on == B_FALSE) {
+			return;
+		}
+
+		pwp->io_intr_coal.intr_coal_timer -= PMCS_COAL_TIMER_GRAN;
+
+		if (pwp->io_intr_coal.intr_coal_timer == 0) {
+			/* Disable the timer */
+			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL, 0);
+
+			if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
+				pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
+				    pwp->odb_auto_clear);
+			}
+
+			pwp->io_intr_coal.timer_on = B_FALSE;
+			pwp->io_intr_coal.max_io_completions = B_FALSE;
+			pwp->io_intr_coal.num_intrs = 0;
+			pwp->io_intr_coal.int_cleared = B_FALSE;
+			pwp->io_intr_coal.num_io_completions = 0;
+
+			DTRACE_PROBE1(pmcs__intr__coalesce__timer__off,
+			    pmcs_io_intr_coal_t *, &pwp->io_intr_coal);
+		} else {
+			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER,
+			    pwp->io_intr_coal.intr_coal_timer);
+		}
+	} else {
+		/*
+		 * If the timer isn't on yet, do the setup for it now.
+		 */
+		if (pwp->io_intr_coal.timer_on == B_FALSE) {
+			/* If auto clear is being used, turn it off. */
+			if (pwp->odb_auto_clear & (1 << PMCS_MSIX_IODONE)) {
+				pmcs_wr_topunit(pwp, PMCS_OBDB_AUTO_CLR,
+				    (pwp->odb_auto_clear &
+				    ~(1 << PMCS_MSIX_IODONE)));
+			}
+
+			pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_CONTROL,
+			    (1 << PMCS_MSIX_IODONE));
+			pwp->io_intr_coal.timer_on = B_TRUE;
+			pwp->io_intr_coal.intr_coal_timer =
+			    PMCS_COAL_TIMER_GRAN;
+
+			DTRACE_PROBE1(pmcs__intr__coalesce__timer__on,
+			    pmcs_io_intr_coal_t *, &pwp->io_intr_coal);
+		} else {
+			pwp->io_intr_coal.intr_coal_timer +=
+			    PMCS_COAL_TIMER_GRAN;
+		}
+
+		if (pwp->io_intr_coal.intr_coal_timer > PMCS_MAX_COAL_TIMER) {
+			pwp->io_intr_coal.intr_coal_timer = PMCS_MAX_COAL_TIMER;
+		}
+
+		pmcs_wr_topunit(pwp, PMCS_INT_COALESCING_TIMER,
+		    pwp->io_intr_coal.intr_coal_timer);
+	}
+
+	/*
+	 * Adjust the interrupt threshold based on the current timer value
+	 */
+	pwp->io_intr_coal.intr_threshold =
+	    PMCS_INTR_THRESHOLD(PMCS_QUANTUM_TIME_USECS * 1000 /
+	    (pwp->io_intr_coal.intr_latency +
+	    (pwp->io_intr_coal.intr_coal_timer * 1000)));
+}
+
+/*
+ * Register Access functions
+ */
+uint32_t
+pmcs_rd_iqci(pmcs_hw_t *pwp, uint32_t qnum)
+{
+	uint32_t iqci;
+
+	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) !=
+	    DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ddi_dma_sync failed?",
+		    __func__);
+	}
+
+	iqci = LE_32(
+	    ((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2]);
+
+	return (iqci);
+}
+
+uint32_t
+pmcs_rd_oqpi(pmcs_hw_t *pwp, uint32_t qnum)
+{
+	uint32_t oqpi;
+
+	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORKERNEL) !=
+	    DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ddi_dma_sync failed?",
+		    __func__);
+	}
+
+	oqpi = LE_32(
+	    ((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2]);
+
+	return (oqpi);
+}
+
+uint32_t
+pmcs_rd_gsm_reg(pmcs_hw_t *pwp, uint32_t off)
+{
+	uint32_t rv, newaxil, oldaxil;
+
+	newaxil = off & ~GSM_BASE_MASK;
+	off &= GSM_BASE_MASK;
+	mutex_enter(&pwp->axil_lock);
+	oldaxil = ddi_get32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]);
+	ddi_put32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
+	drv_usecwait(10);
+	if (ddi_get32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register update failed");
+	}
+	rv = ddi_get32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2]);
+	ddi_put32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
+	drv_usecwait(10);
+	if (ddi_get32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register restore failed");
+	}
+	mutex_exit(&pwp->axil_lock);
+	return (rv);
+}
+
+void
+pmcs_wr_gsm_reg(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
+{
+	uint32_t newaxil, oldaxil;
+
+	newaxil = off & ~GSM_BASE_MASK;
+	off &= GSM_BASE_MASK;
+	mutex_enter(&pwp->axil_lock);
+	oldaxil = ddi_get32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]);
+	ddi_put32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], newaxil);
+	drv_usecwait(10);
+	if (ddi_get32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != newaxil) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register update failed");
+	}
+	ddi_put32(pwp->gsm_acc_handle, &pwp->gsm_regs[off >> 2], val);
+	ddi_put32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2], oldaxil);
+	drv_usecwait(10);
+	if (ddi_get32(pwp->top_acc_handle,
+	    &pwp->top_regs[PMCS_AXI_TRANS >> 2]) != oldaxil) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "AXIL register restore failed");
+	}
+	mutex_exit(&pwp->axil_lock);
+}
+
+uint32_t
+pmcs_rd_topunit(pmcs_hw_t *pwp, uint32_t off)
+{
+	switch (off) {
+	case PMCS_SPC_RESET:
+	case PMCS_SPC_BOOT_STRAP:
+	case PMCS_SPC_DEVICE_ID:
+	case PMCS_DEVICE_REVISION:
+		off = pmcs_rd_gsm_reg(pwp, off);
+		break;
+	default:
+		off = ddi_get32(pwp->top_acc_handle,
+		    &pwp->top_regs[off >> 2]);
+		break;
+	}
+	return (off);
+}
+
+void
+pmcs_wr_topunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
+{
+	switch (off) {
+	case PMCS_SPC_RESET:
+	case PMCS_DEVICE_REVISION:
+		pmcs_wr_gsm_reg(pwp, off, val);
+		break;
+	default:
+		ddi_put32(pwp->top_acc_handle, &pwp->top_regs[off >> 2], val);
+		break;
+	}
+}
+
+uint32_t
+pmcs_rd_msgunit(pmcs_hw_t *pwp, uint32_t off)
+{
+	return (ddi_get32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2]));
+}
+
+uint32_t
+pmcs_rd_mpi_tbl(pmcs_hw_t *pwp, uint32_t off)
+{
+	return (ddi_get32(pwp->mpi_acc_handle,
+	    &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2]));
+}
+
+uint32_t
+pmcs_rd_gst_tbl(pmcs_hw_t *pwp, uint32_t off)
+{
+	return (ddi_get32(pwp->mpi_acc_handle,
+	    &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2]));
+}
+
+uint32_t
+pmcs_rd_iqc_tbl(pmcs_hw_t *pwp, uint32_t off)
+{
+	return (ddi_get32(pwp->mpi_acc_handle,
+	    &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2]));
+}
+
+uint32_t
+pmcs_rd_oqc_tbl(pmcs_hw_t *pwp, uint32_t off)
+{
+	return (ddi_get32(pwp->mpi_acc_handle,
+	    &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2]));
+}
+
+uint32_t
+pmcs_rd_iqpi(pmcs_hw_t *pwp, uint32_t qnum)
+{
+	return (ddi_get32(pwp->mpi_acc_handle,
+	    &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2]));
+}
+
+uint32_t
+pmcs_rd_oqci(pmcs_hw_t *pwp, uint32_t qnum)
+{
+	return (ddi_get32(pwp->mpi_acc_handle,
+	    &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2]));
+}
+
+void
+pmcs_wr_msgunit(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
+{
+	ddi_put32(pwp->msg_acc_handle, &pwp->msg_regs[off >> 2], val);
+}
+
+void
+pmcs_wr_mpi_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
+{
+	ddi_put32(pwp->mpi_acc_handle,
+	    &pwp->mpi_regs[(pwp->mpi_offset + off) >> 2], (val));
+}
+
+void
+pmcs_wr_gst_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
+{
+	ddi_put32(pwp->mpi_acc_handle,
+	    &pwp->mpi_regs[(pwp->mpi_gst_offset + off) >> 2], val);
+}
+
+void
+pmcs_wr_iqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
+{
+	ddi_put32(pwp->mpi_acc_handle,
+	    &pwp->mpi_regs[(pwp->mpi_iqc_offset + off) >> 2], val);
+}
+
+void
+pmcs_wr_oqc_tbl(pmcs_hw_t *pwp, uint32_t off, uint32_t val)
+{
+	ddi_put32(pwp->mpi_acc_handle,
+	    &pwp->mpi_regs[(pwp->mpi_oqc_offset + off) >> 2], val);
+}
+
+void
+pmcs_wr_iqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
+{
+	((uint32_t *)((void *)pwp->cip))[IQ_OFFSET(qnum) >> 2] = val;
+	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) !=
+	    DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ddi_dma_sync failed?",
+		    __func__);
+	}
+}
+
+void
+pmcs_wr_iqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
+{
+	ddi_put32(pwp->mpi_acc_handle,
+	    &pwp->mpi_regs[pwp->iqpi_offset[qnum] >> 2], val);
+}
+
+void
+pmcs_wr_oqci(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
+{
+	ddi_put32(pwp->mpi_acc_handle,
+	    &pwp->mpi_regs[pwp->oqci_offset[qnum] >> 2], val);
+}
+
+void
+pmcs_wr_oqpi(pmcs_hw_t *pwp, uint32_t qnum, uint32_t val)
+{
+	((uint32_t *)((void *)pwp->cip))[OQ_OFFSET(qnum) >> 2] = val;
+	if (ddi_dma_sync(pwp->cip_handles, 0, 0, DDI_DMA_SYNC_FORDEV) !=
+	    DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: ddi_dma_sync failed?",
+		    __func__);
+	}
+}
+
+/*
+ * Check the status value of an outbound IOMB and report anything bad
+ */
+
+void
+pmcs_check_iomb_status(pmcs_hw_t *pwp, uint32_t *iomb)
+{
+	uint16_t 	opcode;
+	int		offset;
+
+	if (iomb == NULL) {
+		return;
+	}
+
+	opcode = LE_32(iomb[0]) & 0xfff;
+
+	switch (opcode) {
+		/*
+		 * The following have no status field, so ignore them
+		 */
+	case PMCOUT_ECHO:
+	case PMCOUT_SAS_HW_EVENT:
+	case PMCOUT_GET_DEVICE_HANDLE:
+	case PMCOUT_SATA_EVENT:
+	case PMCOUT_SSP_EVENT:
+	case PMCOUT_DEVICE_HANDLE_ARRIVED:
+	case PMCOUT_SMP_REQUEST_RECEIVED:
+	case PMCOUT_GPIO:
+	case PMCOUT_GPIO_EVENT:
+	case PMCOUT_GET_TIME_STAMP:
+	case PMCOUT_SKIP_ENTRIES:
+	case PMCOUT_GET_NVMD_DATA:	/* Actually lower 16 bits of word 3 */
+	case PMCOUT_SET_NVMD_DATA:	/* but ignore - we don't use these */
+	case PMCOUT_DEVICE_HANDLE_REMOVED:
+	case PMCOUT_SSP_REQUEST_RECEIVED:
+		return;
+
+	case PMCOUT_GENERAL_EVENT:
+		offset = 1;
+		break;
+
+	case PMCOUT_SSP_COMPLETION:
+	case PMCOUT_SMP_COMPLETION:
+	case PMCOUT_DEVICE_REGISTRATION:
+	case PMCOUT_DEREGISTER_DEVICE_HANDLE:
+	case PMCOUT_SATA_COMPLETION:
+	case PMCOUT_DEVICE_INFO:
+	case PMCOUT_FW_FLASH_UPDATE:
+	case PMCOUT_SSP_ABORT:
+	case PMCOUT_SATA_ABORT:
+	case PMCOUT_SAS_DIAG_MODE_START_END:
+	case PMCOUT_SAS_HW_EVENT_ACK_ACK:
+	case PMCOUT_SMP_ABORT:
+	case PMCOUT_SET_DEVICE_STATE:
+	case PMCOUT_GET_DEVICE_STATE:
+	case PMCOUT_SET_DEVICE_INFO:
+		offset = 2;
+		break;
+
+	case PMCOUT_LOCAL_PHY_CONTROL:
+	case PMCOUT_SAS_DIAG_EXECUTE:
+	case PMCOUT_PORT_CONTROL:
+		offset = 3;
+		break;
+
+	case PMCOUT_GET_INFO:
+	case PMCOUT_GET_VPD:
+	case PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT:
+	case PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT:
+	case PMCOUT_SET_VPD:
+	case PMCOUT_TWI:
+		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
+		    "Got response for deprecated opcode", iomb);
+		return;
+
+	default:
+		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
+		    "Got response for unknown opcode", iomb);
+		return;
+	}
+
+	if (LE_32(iomb[offset]) != PMCOUT_STATUS_OK) {
+		pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
+		    "bad status on TAG_TYPE_NONE command", iomb);
+	}
+}
+
+/*
+ * Called with statlock held
+ */
+void
+pmcs_clear_xp(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
+{
+	_NOTE(ARGUNUSED(pwp));
+
+	ASSERT(mutex_owned(&xp->statlock));
+	ASSERT(xp->dying);
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Device 0x%p is gone.", __func__,
+	    (void *)xp);
+
+	/*
+	 * Clear the dip now.  This keeps pmcs_rem_old_devices from attempting
+	 * to call us on the same device while we're still flushing queues.
+	 * The only side effect is we can no longer update SM-HBA properties,
+	 * but this device is going away anyway, so no matter.
+	 */
+	xp->dip = NULL;
+
+	/*
+	 * Flush all target queues
+	 */
+	pmcs_flush_target_queues(pwp, xp, PMCS_TGT_ALL_QUEUES);
+
+	xp->special_running = 0;
+	xp->recovering = 0;
+	xp->recover_wait = 0;
+	xp->draining = 0;
+	xp->dying = 0;
+	xp->new = 0;
+	xp->assigned = 0;
+	xp->dev_state = 0;
+	xp->tagmap = 0;
+	xp->dev_gone = 1;
+	xp->event_recovery = 0;
+	xp->dtype = NOTHING;
+	xp->wq_recovery_tail = NULL;
+	/* Don't clear xp->phy */
+	/* Don't clear xp->actv_cnt */
+}
+
+static int
+pmcs_smp_function_result(pmcs_hw_t *pwp, smp_response_frame_t *srf)
+{
+	int result = srf->srf_result;
+
+	switch (result) {
+	case SMP_RES_UNKNOWN_FUNCTION:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
+		    "Function Result: Unknown SMP Function(0x%x)",
+		    __func__, result);
+		break;
+	case SMP_RES_FUNCTION_FAILED:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
+		    "Function Result: SMP Function Failed(0x%x)",
+		    __func__, result);
+		break;
+	case SMP_RES_INVALID_REQUEST_FRAME_LENGTH:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
+		    "Function Result: Invalid Request Frame Length(0x%x)",
+		    __func__, result);
+		break;
+	case SMP_RES_INCOMPLETE_DESCRIPTOR_LIST:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
+		    "Function Result: Incomplete Descriptor List(0x%x)",
+		    __func__, result);
+		break;
+	case SMP_RES_PHY_DOES_NOT_EXIST:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
+		    "Function Result: PHY does not exist(0x%x)",
+		    __func__, result);
+		break;
+	case SMP_RES_PHY_VACANT:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
+		    "Function Result: PHY Vacant(0x%x)",
+		    __func__, result);
+		break;
+	default:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: SMP DISCOVER Response "
+		    "Function Result: (0x%x)",
+		    __func__, result);
+		break;
+	}
+
+	return (result);
+}
+
+/*
+ * Do all the repetitive stuff necessary to setup for DMA
+ *
+ * pwp: Used for dip
+ * dma_attr: ddi_dma_attr_t to use for the mapping
+ * acch: ddi_acc_handle_t to use for the mapping
+ * dmah: ddi_dma_handle_t to use
+ * length: Amount of memory for mapping
+ * kvp: Pointer filled in with kernel virtual address on successful return
+ * dma_addr: Pointer filled in with DMA address on successful return
+ */
+boolean_t
+pmcs_dma_setup(pmcs_hw_t *pwp, ddi_dma_attr_t *dma_attr, ddi_acc_handle_t *acch,
+    ddi_dma_handle_t *dmah, size_t length, caddr_t *kvp, uint64_t *dma_addr)
+{
+	dev_info_t		*dip = pwp->dip;
+	ddi_dma_cookie_t	cookie;
+	size_t			real_length;
+	uint_t			ddma_flag = DDI_DMA_CONSISTENT;
+	uint_t			ddabh_flag = DDI_DMA_CONSISTENT | DDI_DMA_RDWR;
+	uint_t			cookie_cnt;
+	ddi_device_acc_attr_t	mattr = {
+		DDI_DEVICE_ATTR_V0,
+		DDI_NEVERSWAP_ACC,
+		DDI_STRICTORDER_ACC,
+		DDI_DEFAULT_ACC
+	};
+
+	*acch = NULL;
+	*dmah = NULL;
+
+	if (ddi_dma_alloc_handle(dip, dma_attr, DDI_DMA_SLEEP, NULL, dmah) !=
+	    DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Failed to allocate DMA handle");
+		return (B_FALSE);
+	}
+
+	if (ddi_dma_mem_alloc(*dmah, length, &mattr, ddma_flag, DDI_DMA_SLEEP,
+	    NULL, kvp, &real_length, acch) != DDI_SUCCESS) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Failed to allocate DMA mem");
+		ddi_dma_free_handle(dmah);
+		*dmah = NULL;
+		return (B_FALSE);
+	}
+
+	if (ddi_dma_addr_bind_handle(*dmah, NULL, *kvp, real_length,
+	    ddabh_flag, DDI_DMA_SLEEP, NULL, &cookie, &cookie_cnt)
+	    != DDI_DMA_MAPPED) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Failed to bind DMA");
+		ddi_dma_free_handle(dmah);
+		ddi_dma_mem_free(acch);
+		*dmah = NULL;
+		*acch = NULL;
+		return (B_FALSE);
+	}
+
+	if (cookie_cnt != 1) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "Multiple cookies");
+		if (ddi_dma_unbind_handle(*dmah) != DDI_SUCCESS) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition failed at "
+			    "%s():%d", __func__, __LINE__);
+		}
+		ddi_dma_free_handle(dmah);
+		ddi_dma_mem_free(acch);
+		*dmah = NULL;
+		*acch = NULL;
+		return (B_FALSE);
+	}
+
+	*dma_addr = cookie.dmac_laddress;
+
+	return (B_TRUE);
+}
+
+/*
+ * Flush requested queues for a particular target.  Called with statlock held
+ */
+void
+pmcs_flush_target_queues(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt, uint8_t queues)
+{
+	pmcs_cmd_t	*sp;
+	pmcwork_t	*pwrk;
+
+	ASSERT(pwp != NULL);
+	ASSERT(tgt != NULL);
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG,
+	    "%s: Flushing queues (%d) for target 0x%p", __func__,
+	    queues, (void *)tgt);
+
+	/*
+	 * Commands on the wait queue (or the special queue below) don't have
+	 * work structures associated with them.
+	 */
+	if (queues & PMCS_TGT_WAIT_QUEUE) {
+		mutex_enter(&tgt->wqlock);
+		while ((sp = STAILQ_FIRST(&tgt->wq)) != NULL) {
+			STAILQ_REMOVE(&tgt->wq, sp, pmcs_cmd, cmd_next);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
+			    "%s: Removing cmd 0x%p from wq for target 0x%p",
+			    __func__, (void *)sp, (void *)tgt);
+			CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
+			CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
+			mutex_exit(&tgt->wqlock);
+			pmcs_dma_unload(pwp, sp);
+			mutex_enter(&pwp->cq_lock);
+			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+			mutex_exit(&pwp->cq_lock);
+			mutex_enter(&tgt->wqlock);
+		}
+		mutex_exit(&tgt->wqlock);
+	}
+
+	/*
+	 * Commands on the active queue will have work structures associated
+	 * with them.
+	 */
+	if (queues & PMCS_TGT_ACTIVE_QUEUE) {
+		mutex_enter(&tgt->aqlock);
+		while ((sp = STAILQ_FIRST(&tgt->aq)) != NULL) {
+			STAILQ_REMOVE(&tgt->aq, sp, pmcs_cmd, cmd_next);
+			pwrk = pmcs_tag2wp(pwp, sp->cmd_tag);
+			mutex_exit(&tgt->aqlock);
+			mutex_exit(&tgt->statlock);
+			/*
+			 * If we found a work structure, mark it as dead
+			 * and complete it
+			 */
+			if (pwrk != NULL) {
+				pwrk->dead = 1;
+				CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
+				CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
+				pmcs_complete_work_impl(pwp, pwrk, NULL, 0);
+			}
+			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
+			    "%s: Removing cmd 0x%p from aq for target 0x%p",
+			    __func__, (void *)sp, (void *)tgt);
+			pmcs_dma_unload(pwp, sp);
+			mutex_enter(&pwp->cq_lock);
+			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+			mutex_exit(&pwp->cq_lock);
+			mutex_enter(&tgt->aqlock);
+			mutex_enter(&tgt->statlock);
+		}
+		mutex_exit(&tgt->aqlock);
+	}
+
+	if (queues & PMCS_TGT_SPECIAL_QUEUE) {
+		while ((sp = STAILQ_FIRST(&tgt->sq)) != NULL) {
+			STAILQ_REMOVE(&tgt->sq, sp, pmcs_cmd, cmd_next);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
+			    "%s: Removing cmd 0x%p from sq for target 0x%p",
+			    __func__, (void *)sp, (void *)tgt);
+			CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE;
+			CMD2PKT(sp)->pkt_state = STATE_GOT_BUS;
+			pmcs_dma_unload(pwp, sp);
+			mutex_enter(&pwp->cq_lock);
+			STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
+			mutex_exit(&pwp->cq_lock);
+		}
+	}
+}
+
+void
+pmcs_complete_work_impl(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb,
+    size_t amt)
+{
+	switch (PMCS_TAG_TYPE(pwrk->htag)) {
+	case PMCS_TAG_TYPE_CBACK:
+	{
+		pmcs_cb_t callback = (pmcs_cb_t)pwrk->ptr;
+		(*callback)(pwp, pwrk, iomb);
+		break;
+	}
+	case PMCS_TAG_TYPE_WAIT:
+		if (pwrk->arg && iomb && amt) {
+			(void) memcpy(pwrk->arg, iomb, amt);
+		}
+		cv_signal(&pwrk->sleep_cv);
+		mutex_exit(&pwrk->lock);
+		break;
+	case PMCS_TAG_TYPE_NONE:
+#ifdef DEBUG
+		pmcs_check_iomb_status(pwp, iomb);
+#endif
+		pmcs_pwork(pwp, pwrk);
+		break;
+	default:
+		/*
+		 * We will leak a structure here if we don't know
+		 * what happened
+		 */
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Unknown PMCS_TAG_TYPE (%x)",
+		    __func__, PMCS_TAG_TYPE(pwrk->htag));
+		break;
+	}
+}
+
+/*
+ * Determine if iport still has targets. During detach(9E), if SCSA is
+ * successfull in its guarantee of tran_tgt_free(9E) before detach(9E),
+ * this should always return B_FALSE.
+ */
+boolean_t
+pmcs_iport_has_targets(pmcs_hw_t *pwp, pmcs_iport_t *iport)
+{
+	pmcs_xscsi_t *xp;
+	int i;
+
+	mutex_enter(&pwp->lock);
+
+	if (!pwp->targets || !pwp->max_dev) {
+		mutex_exit(&pwp->lock);
+		return (B_FALSE);
+	}
+
+	for (i = 0; i < pwp->max_dev; i++) {
+		xp = pwp->targets[i];
+		if ((xp == NULL) || (xp->phy == NULL) ||
+		    (xp->phy->iport != iport)) {
+			continue;
+		}
+
+		mutex_exit(&pwp->lock);
+		return (B_TRUE);
+	}
+
+	mutex_exit(&pwp->lock);
+	return (B_FALSE);
+}
+
+/*
+ * Called with softstate lock held
+ */
+void
+pmcs_destroy_target(pmcs_xscsi_t *target)
+{
+	pmcs_hw_t *pwp = target->pwp;
+	pmcs_iport_t *iport;
+
+	ASSERT(pwp);
+	ASSERT(mutex_owned(&pwp->lock));
+
+	if (!target->ua) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: target %p iport addres is null",
+		    __func__, (void *)target);
+	}
+
+	iport = pmcs_get_iport_by_ua(pwp, target->ua);
+	if (iport == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: no iport associated with tgt(0x%p)",
+		    __func__, (void *)target);
+		return;
+	}
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+	    "%s: free target %p", __func__, (void *)target);
+	if (target->ua) {
+		strfree(target->ua);
+	}
+
+	mutex_destroy(&target->wqlock);
+	mutex_destroy(&target->aqlock);
+	mutex_destroy(&target->statlock);
+	cv_destroy(&target->reset_cv);
+	cv_destroy(&target->abort_cv);
+	ddi_soft_state_bystr_fini(&target->lun_sstate);
+	ddi_soft_state_bystr_free(iport->tgt_sstate, target->unit_address);
+	pmcs_rele_iport(iport);
+}
+
+/*
+ * Get device state.  Called with statlock and PHY lock held.
+ */
+int
+pmcs_get_dev_state(pmcs_hw_t *pwp, pmcs_xscsi_t *xp, uint8_t *ds)
+{
+	uint32_t htag, *ptr, msg[PMCS_MSG_SIZE];
+	int result;
+	struct pmcwork *pwrk;
+	pmcs_phy_t *phyp;
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG3, "%s: tgt(0x%p)", __func__, (void *)xp);
+	if (xp == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Target is NULL", __func__);
+		return (-1);
+	}
+
+	ASSERT(mutex_owned(&xp->statlock));
+	phyp = xp->phy;
+	ASSERT(mutex_owned(&phyp->phy_lock));
+
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, phyp);
+	if (pwrk == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
+		return (-1);
+	}
+	pwrk->arg = msg;
+	pwrk->dtype = phyp->dtype;
+
+	if (phyp->valid_device_id == 0) {
+		pmcs_pwork(pwp, pwrk);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Invalid DeviceID", __func__);
+		return (-1);
+	}
+	htag = pwrk->htag;
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
+	    PMCIN_GET_DEVICE_STATE));
+	msg[1] = LE_32(pwrk->htag);
+	msg[2] = LE_32(phyp->device_id);
+
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (ptr == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		pmcs_pwork(pwp, pwrk);
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
+		return (-1);
+	}
+	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	mutex_exit(&xp->statlock);
+	pmcs_unlock_phy(phyp);
+	WAIT_FOR(pwrk, 1000, result);
+	pmcs_lock_phy(phyp);
+	pmcs_pwork(pwp, pwrk);
+	mutex_enter(&xp->statlock);
+
+	if (result) {
+		pmcs_timed_out(pwp, htag, __func__);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: cmd timed out, returning ",
+		    __func__);
+		return (-1);
+	}
+	if (LE_32(msg[2]) == 0) {
+		*ds = (uint8_t)(LE_32(msg[4]));
+		if (*ds !=  xp->dev_state) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+			    "%s: retrieved_ds=0x%x, target_ds=0x%x", __func__,
+			    *ds, xp->dev_state);
+		}
+		return (0);
+	} else {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+		    "%s: cmd failed Status(0x%x), returning ", __func__,
+		    LE_32(msg[2]));
+		return (-1);
+	}
+}
+
+/*
+ * Set device state.  Called with target's statlock and PHY lock held.
+ */
+int
+pmcs_set_dev_state(pmcs_hw_t *pwp, pmcs_xscsi_t *xp, uint8_t ds)
+{
+	uint32_t htag, *ptr, msg[PMCS_MSG_SIZE];
+	int result;
+	uint8_t pds, nds;
+	struct pmcwork *pwrk;
+	pmcs_phy_t *phyp;
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: ds(0x%x), tgt(0x%p)",
+	    __func__, ds, (void *)xp);
+	if (xp == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Target is Null", __func__);
+		return (-1);
+	}
+
+	phyp = xp->phy;
+	pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, phyp);
+	if (pwrk == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nowrk, __func__);
+		return (-1);
+	}
+	if (phyp == NULL) {
+		pmcs_pwork(pwp, pwrk);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: PHY is Null",
+		    __func__);
+		return (-1);
+	}
+	if (phyp->valid_device_id == 0) {
+		pmcs_pwork(pwp, pwrk);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+		    "%s: Invalid DeviceID", __func__);
+		return (-1);
+	}
+	pwrk->arg = msg;
+	pwrk->dtype = phyp->dtype;
+	htag = pwrk->htag;
+	msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL,
+	    PMCIN_SET_DEVICE_STATE));
+	msg[1] = LE_32(pwrk->htag);
+	msg[2] = LE_32(phyp->device_id);
+	msg[3] = LE_32(ds);
+
+	mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+	ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+	if (ptr == NULL) {
+		mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
+		pmcs_pwork(pwp, pwrk);
+		pmcs_prt(pwp, PMCS_PRT_ERR, pmcs_nomsg, __func__);
+		return (-1);
+	}
+	COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
+	pwrk->state = PMCS_WORK_STATE_ONCHIP;
+	INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
+
+	mutex_exit(&xp->statlock);
+	pmcs_unlock_phy(phyp);
+	WAIT_FOR(pwrk, 1000, result);
+	pmcs_lock_phy(phyp);
+	pmcs_pwork(pwp, pwrk);
+	mutex_enter(&xp->statlock);
+
+	if (result) {
+		pmcs_timed_out(pwp, htag, __func__);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+		    "%s: cmd timed out, returning", __func__);
+		return (-1);
+	}
+	if (LE_32(msg[2]) == 0) {
+		pds = (uint8_t)(LE_32(msg[4]) >> 4);
+		nds = (uint8_t)(LE_32(msg[4]) & 0x0000000f);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: previous_ds=0x%x, "
+		    "new_ds=0x%x", __func__, pds, nds);
+		xp->dev_state = nds;
+		return (0);
+	} else {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+		    "%s: cmd failed Status(0x%x), returning ", __func__,
+		    LE_32(msg[2]));
+		return (-1);
+	}
+}
+
+void
+pmcs_dev_state_recovery(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
+{
+	uint8_t	ds;
+	int rc;
+	pmcs_xscsi_t *tgt;
+	pmcs_phy_t *pptr, *pnext, *pchild;
+
+	/*
+	 * First time, check to see if we're already performing recovery
+	 */
+	if (phyp == NULL) {
+		mutex_enter(&pwp->lock);
+		if (pwp->ds_err_recovering) {
+			mutex_exit(&pwp->lock);
+			SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY);
+			return;
+		}
+
+		pwp->ds_err_recovering = 1;
+		pptr = pwp->root_phys;
+		mutex_exit(&pwp->lock);
+	} else {
+		pptr = phyp;
+	}
+
+	while (pptr) {
+		/*
+		 * Since ds_err_recovering is set, we can be assured these
+		 * PHYs won't disappear on us while we do this.
+		 */
+		pmcs_lock_phy(pptr);
+		pchild = pptr->children;
+		pnext = pptr->sibling;
+		pmcs_unlock_phy(pptr);
+
+		if (pchild) {
+			pmcs_dev_state_recovery(pwp, pchild);
+		}
+
+		tgt = NULL;
+		pmcs_lock_phy(pptr);
+
+		if (pptr->dead) {
+			goto next_phy;
+		}
+
+		tgt = pptr->target;
+		if (tgt == NULL) {
+			if (pptr->dtype != NOTHING) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG2,
+				    "%s: no target for DS error recovery for "
+				    "PHY 0x%p", __func__, (void *)pptr);
+			}
+			goto next_phy;
+		}
+
+		mutex_enter(&tgt->statlock);
+
+		if (tgt->recover_wait == 0) {
+			goto next_phy;
+		}
+
+		if (tgt->dying) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+			    "%s: Not doing DS recovery on dying target %p",
+			    __func__, (void *)tgt);
+			goto next_phy;
+		}
+
+		/*
+		 * Step 1: Put the device into the IN_RECOVERY state
+		 */
+		rc = pmcs_get_dev_state(pwp, tgt, &ds);
+		if (rc != 0) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: pmcs_get_dev_state on PHY %s "
+			    "failed (rc=%d)",
+			    __func__, pptr->path, rc);
+
+			pmcs_handle_ds_recovery_error(pptr, tgt, pwp,
+			    __func__, __LINE__, "pmcs_get_dev_state");
+
+			goto next_phy;
+		}
+
+		if (tgt->dev_state == ds) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+			    "%s: Target 0x%p already IN_RECOVERY", __func__,
+			    (void *)tgt);
+		} else {
+			tgt->dev_state = ds;
+			ds = PMCS_DEVICE_STATE_IN_RECOVERY;
+			rc = pmcs_send_err_recovery_cmd(pwp, ds, tgt);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+			    "%s: pmcs_send_err_recovery_cmd "
+			    "result(%d) tgt(0x%p) ds(0x%x) tgt->ds(0x%x)",
+			    __func__, rc, (void *)tgt, ds, tgt->dev_state);
+
+			if (rc) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "%s: pmcs_send_err_recovery_cmd to PHY %s "
+				    "failed (rc=%d)",
+				    __func__, pptr->path, rc);
+
+				pmcs_handle_ds_recovery_error(pptr, tgt, pwp,
+				    __func__, __LINE__,
+				    "pmcs_send_err_recovery_cmd");
+
+				goto next_phy;
+			}
+		}
+
+		/*
+		 * Step 2: Perform a hard reset on the PHY
+		 */
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+		    "%s: Issue HARD_RESET to PHY %s", __func__, pptr->path);
+		/*
+		 * Must release statlock here because pmcs_reset_phy will
+		 * drop and reacquire the PHY lock.
+		 */
+		mutex_exit(&tgt->statlock);
+		rc = pmcs_reset_phy(pwp, pptr, PMCS_PHYOP_HARD_RESET);
+		mutex_enter(&tgt->statlock);
+		if (rc) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: HARD_RESET to PHY %s failed (rc=%d)",
+			    __func__, pptr->path, rc);
+
+			pmcs_handle_ds_recovery_error(pptr, tgt, pwp,
+			    __func__, __LINE__, "HARD_RESET");
+
+			goto next_phy;
+		}
+
+		/*
+		 * Step 3: Abort all I/Os to the device
+		 */
+		if (pptr->abort_all_start) {
+			while (pptr->abort_all_start) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "%s: Waiting for outstanding ABORT_ALL on "
+				    "PHY 0x%p", __func__, (void *)pptr);
+				cv_wait(&pptr->abort_all_cv, &pptr->phy_lock);
+			}
+		} else {
+			mutex_exit(&tgt->statlock);
+			rc = pmcs_abort(pwp, pptr, pptr->device_id, 1, 1);
+			mutex_enter(&tgt->statlock);
+			if (rc != 0) {
+				pptr->abort_pending = 1;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "%s: pmcs_abort to PHY %s failed (rc=%d)",
+				    __func__, pptr->path, rc);
+
+				pmcs_handle_ds_recovery_error(pptr, tgt,
+				    pwp, __func__, __LINE__, "pmcs_abort");
+
+				goto next_phy;
+			}
+		}
+
+		/*
+		 * Step 4: Set the device back to OPERATIONAL state
+		 */
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+		    "%s: Set PHY/tgt 0x%p/0x%p to OPERATIONAL state",
+		    __func__, (void *)pptr, (void *)tgt);
+		rc = pmcs_set_dev_state(pwp, tgt,
+		    PMCS_DEVICE_STATE_OPERATIONAL);
+		if (rc == 0) {
+			tgt->recover_wait = 0;
+			pptr->ds_recovery_retries = 0;
+			/*
+			 * Don't bother to run the work queues if the PHY
+			 * is dead.
+			 */
+			if (tgt->phy && !tgt->phy->dead) {
+				SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
+				(void) ddi_taskq_dispatch(pwp->tq, pmcs_worker,
+				    pwp, DDI_NOSLEEP);
+			}
+		} else {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+			    "%s: Failed to SET tgt 0x%p to OPERATIONAL state",
+			    __func__, (void *)tgt);
+
+			pmcs_handle_ds_recovery_error(pptr, tgt, pwp,
+			    __func__, __LINE__, "SET tgt to OPERATIONAL state");
+
+			goto next_phy;
+		}
+
+next_phy:
+		if (tgt) {
+			mutex_exit(&tgt->statlock);
+		}
+		pmcs_unlock_phy(pptr);
+		pptr = pnext;
+	}
+
+	/*
+	 * Only clear ds_err_recovering if we're exiting for good and not
+	 * just unwinding from recursion
+	 */
+	if (phyp == NULL) {
+		mutex_enter(&pwp->lock);
+		pwp->ds_err_recovering = 0;
+		mutex_exit(&pwp->lock);
+	}
+}
+
+/*
+ * Called with target's statlock and PHY lock held.
+ */
+int
+pmcs_send_err_recovery_cmd(pmcs_hw_t *pwp, uint8_t dev_state, pmcs_xscsi_t *tgt)
+{
+	pmcs_phy_t *pptr;
+	int rc = -1;
+
+	ASSERT(tgt != NULL);
+	ASSERT(mutex_owned(&tgt->statlock));
+
+	if (tgt->recovering) {
+		return (0);
+	}
+
+	tgt->recovering = 1;
+	pptr = tgt->phy;
+
+	if (pptr == NULL) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: PHY is Null",
+		    __func__);
+		return (-1);
+	}
+
+	ASSERT(mutex_owned(&pptr->phy_lock));
+
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, "%s: ds: 0x%x, tgt ds(0x%x)",
+	    __func__, dev_state, tgt->dev_state);
+
+	switch (dev_state) {
+	case PMCS_DEVICE_STATE_IN_RECOVERY:
+		if (tgt->dev_state == PMCS_DEVICE_STATE_IN_RECOVERY) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+			    "%s: Target 0x%p already IN_RECOVERY", __func__,
+			    (void *)tgt);
+			rc = 0;	/* This is not an error */
+			goto no_action;
+		}
+
+		rc = pmcs_set_dev_state(pwp, tgt,
+		    PMCS_DEVICE_STATE_IN_RECOVERY);
+		if (rc != 0) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+			    "%s(1): Failed to SET tgt(0x%p) to _IN_RECOVERY",
+			    __func__, (void *)tgt);
+		}
+
+		break;
+
+	case PMCS_DEVICE_STATE_OPERATIONAL:
+		if (tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+			    "%s: Target 0x%p not ready to go OPERATIONAL",
+			    __func__, (void *)tgt);
+			goto no_action;
+		}
+
+		rc = pmcs_set_dev_state(pwp, tgt,
+		    PMCS_DEVICE_STATE_OPERATIONAL);
+		tgt->reset_success = 1;
+		if (rc != 0) {
+			pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+			    "%s(2): Failed to SET tgt(0x%p) to OPERATIONAL",
+			    __func__, (void *)tgt);
+			tgt->reset_success = 0;
+		}
+
+		break;
+
+	case PMCS_DEVICE_STATE_NON_OPERATIONAL:
+		PHY_CHANGED(pwp, pptr);
+		RESTART_DISCOVERY(pwp);
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+		    "%s: Device at %s is non-operational",
+		    __func__, pptr->path);
+		tgt->dev_state = PMCS_DEVICE_STATE_NON_OPERATIONAL;
+		rc = 0;
+
+		break;
+
+	default:
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE,
+		    "%s: Invalid state requested (%d)", __func__,
+		    dev_state);
+		break;
+
+	}
+
+no_action:
+	tgt->recovering = 0;
+	return (rc);
+}
+
+/*
+ * pmcs_lock_phy_impl
+ *
+ * This function is what does the actual work for pmcs_lock_phy.  It will
+ * lock all PHYs from phyp down in a top-down fashion.
+ *
+ * Locking notes:
+ * 1. level starts from 0 for the PHY ("parent") that's passed in.  It is
+ * not a reflection of the actual level of the PHY in the SAS topology.
+ * 2. If parent is an expander, then parent is locked along with all its
+ * descendents.
+ * 3. Expander subsidiary PHYs at level 0 are not locked.  It is the
+ * responsibility of the caller to individually lock expander subsidiary PHYs
+ * at level 0 if necessary.
+ * 4. Siblings at level 0 are not traversed due to the possibility that we're
+ * locking a PHY on the dead list.  The siblings could be pointing to invalid
+ * PHYs.  We don't lock siblings at level 0 anyway.
+ */
+static void
+pmcs_lock_phy_impl(pmcs_phy_t *phyp, int level)
+{
+	pmcs_phy_t *tphyp;
+
+	ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) ||
+	    (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING));
+
+	/*
+	 * Start walking the PHYs.
+	 */
+	tphyp = phyp;
+	while (tphyp) {
+		/*
+		 * If we're at the top level, only lock ourselves.  For anything
+		 * at level > 0, traverse children while locking everything.
+		 */
+		if ((level > 0) || (tphyp == phyp)) {
+			pmcs_prt(tphyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
+			    "%s: PHY 0x%p parent 0x%p path %s lvl %d",
+			    __func__, (void *)tphyp, (void *)tphyp->parent,
+			    tphyp->path, level);
+			mutex_enter(&tphyp->phy_lock);
+
+			if (tphyp->children) {
+				pmcs_lock_phy_impl(tphyp->children, level + 1);
+			}
+		}
+
+		if (level == 0) {
+			return;
+		}
+
+		tphyp = tphyp->sibling;
+	}
+}
+
+/*
+ * pmcs_lock_phy
+ *
+ * This function is responsible for locking a PHY and all its descendents
+ */
+void
+pmcs_lock_phy(pmcs_phy_t *phyp)
+{
+#ifdef DEBUG
+	char *callername = NULL;
+	ulong_t off;
+
+	ASSERT(phyp != NULL);
+
+	callername = modgetsymname((uintptr_t)caller(), &off);
+
+	if (callername == NULL) {
+		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
+		    "%s: PHY 0x%p path %s caller: unknown", __func__,
+		    (void *)phyp, phyp->path);
+	} else {
+		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
+		    "%s: PHY 0x%p path %s caller: %s+%lx", __func__,
+		    (void *)phyp, phyp->path, callername, off);
+	}
+#else
+	pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
+	    "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path);
+#endif
+	pmcs_lock_phy_impl(phyp, 0);
+}
+
+/*
+ * pmcs_unlock_phy_impl
+ *
+ * Unlock all PHYs from phyp down in a bottom-up fashion.
+ */
+static void
+pmcs_unlock_phy_impl(pmcs_phy_t *phyp, int level)
+{
+	pmcs_phy_t *phy_next;
+
+	ASSERT((phyp->dtype == SAS) || (phyp->dtype == SATA) ||
+	    (phyp->dtype == EXPANDER) || (phyp->dtype == NOTHING));
+
+	/*
+	 * Recurse down to the bottom PHYs
+	 */
+	if (level == 0) {
+		if (phyp->children) {
+			pmcs_unlock_phy_impl(phyp->children, level + 1);
+		}
+	} else {
+		phy_next = phyp;
+		while (phy_next) {
+			if (phy_next->children) {
+				pmcs_unlock_phy_impl(phy_next->children,
+				    level + 1);
+			}
+			phy_next = phy_next->sibling;
+		}
+	}
+
+	/*
+	 * Iterate through PHYs unlocking all at level > 0 as well the top PHY
+	 */
+	phy_next = phyp;
+	while (phy_next) {
+		if ((level > 0) || (phy_next == phyp)) {
+			pmcs_prt(phy_next->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
+			    "%s: PHY 0x%p parent 0x%p path %s lvl %d",
+			    __func__, (void *)phy_next,
+			    (void *)phy_next->parent, phy_next->path, level);
+			mutex_exit(&phy_next->phy_lock);
+		}
+
+		if (level == 0) {
+			return;
+		}
+
+		phy_next = phy_next->sibling;
+	}
+}
+
+/*
+ * pmcs_unlock_phy
+ *
+ * Unlock a PHY and all its descendents
+ */
+void
+pmcs_unlock_phy(pmcs_phy_t *phyp)
+{
+#ifdef DEBUG
+	char *callername = NULL;
+	ulong_t off;
+
+	ASSERT(phyp != NULL);
+
+	callername = modgetsymname((uintptr_t)caller(), &off);
+
+	if (callername == NULL) {
+		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
+		    "%s: PHY 0x%p path %s caller: unknown", __func__,
+		    (void *)phyp, phyp->path);
+	} else {
+		pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
+		    "%s: PHY 0x%p path %s caller: %s+%lx", __func__,
+		    (void *)phyp, phyp->path, callername, off);
+	}
+#else
+	pmcs_prt(phyp->pwp, PMCS_PRT_DEBUG_PHY_LOCKING,
+	    "%s: PHY 0x%p path %s", __func__, (void *)phyp, phyp->path);
+#endif
+	pmcs_unlock_phy_impl(phyp, 0);
+}
+
+/*
+ * pmcs_get_root_phy
+ *
+ * For a given phy pointer return its root phy.
+ * The caller must be holding the lock on every PHY from phyp up to the root.
+ */
+pmcs_phy_t *
+pmcs_get_root_phy(pmcs_phy_t *phyp)
+{
+	ASSERT(phyp);
+
+	while (phyp) {
+		if (IS_ROOT_PHY(phyp)) {
+			break;
+		}
+		phyp = phyp->parent;
+	}
+
+	return (phyp);
+}
+
+/*
+ * pmcs_free_dma_chunklist
+ *
+ * Free DMA S/G chunk list
+ */
+void
+pmcs_free_dma_chunklist(pmcs_hw_t *pwp)
+{
+	pmcs_chunk_t	*pchunk;
+
+	while (pwp->dma_chunklist) {
+		pchunk = pwp->dma_chunklist;
+		pwp->dma_chunklist = pwp->dma_chunklist->next;
+		if (pchunk->dma_handle) {
+			if (ddi_dma_unbind_handle(pchunk->dma_handle) !=
+			    DDI_SUCCESS) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "Condition failed"
+				    " at %s():%d", __func__, __LINE__);
+			}
+			ddi_dma_free_handle(&pchunk->dma_handle);
+			ddi_dma_mem_free(&pchunk->acc_handle);
+		}
+		kmem_free(pchunk, sizeof (pmcs_chunk_t));
+	}
+}
+
+
+/*
+ * Start ssp event recovery. We have to schedule recovery operation because
+ * it involves sending multiple commands to device and we should not do it
+ * in the interrupt context.
+ * If it is failure of a recovery command, let the recovery thread deal with it.
+ * Called with pmcwork lock held.
+ */
+
+void
+pmcs_start_ssp_event_recovery(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb,
+    size_t amt)
+{
+	pmcs_xscsi_t *tgt = pwrk->xp;
+	uint32_t event = LE_32(iomb[2]);
+	pmcs_phy_t *pptr = pwrk->phy;
+	uint32_t tag;
+
+	if (tgt != NULL) {
+		mutex_enter(&tgt->statlock);
+		if (tgt->dying || !tgt->assigned) {
+			if (pptr) {
+				pmcs_dec_phy_ref_count(pptr);
+			}
+			pptr = NULL;
+			pwrk->phy = NULL;
+		}
+		mutex_exit(&tgt->statlock);
+	}
+	if (pptr == NULL) {
+		/*
+		 * No target or dying target.Need to run RE-DISCOVERY here.
+		 */
+		if (pwrk->state != PMCS_WORK_STATE_TIMED_OUT) {
+			pwrk->state = PMCS_WORK_STATE_INTR;
+		}
+		/*
+		 * Although we cannot mark phy to force abort nor mark phy
+		 * as changed, killing of a target would take care of aborting
+		 * commands for the device.
+		 */
+		pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: No valid target for event "
+		    "processing found. Scheduling RECONFIGURE",  __func__);
+		pmcs_pwork(pwp, pwrk);
+		RESTART_DISCOVERY(pwp);
+		return;
+	} else {
+		pmcs_lock_phy(pptr);
+		mutex_enter(&tgt->statlock);
+		if (event == PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS) {
+			if (tgt->dev_state !=
+			    PMCS_DEVICE_STATE_NON_OPERATIONAL) {
+				pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: Device at "
+				    "%s is non-operational", __func__,
+				    pptr->path);
+				tgt->dev_state =
+				    PMCS_DEVICE_STATE_NON_OPERATIONAL;
+			}
+			pptr->abort_pending = 1;
+			mutex_exit(&tgt->statlock);
+			pmcs_unlock_phy(pptr);
+			mutex_exit(&pwrk->lock);
+			SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
+			RESTART_DISCOVERY(pwp);
+			return;
+		}
+
+		/*
+		 * If this command is run in WAIT mode, it is a failing recovery
+		 * command. If so, just wake up recovery thread waiting for
+		 * command completion.
+		 */
+		tag = PMCS_TAG_TYPE(pwrk->htag);
+		if (tag == PMCS_TAG_TYPE_WAIT) {
+			pwrk->htag |= PMCS_TAG_DONE;
+			if (pwrk->arg && amt) {
+				(void) memcpy(pwrk->arg, iomb, amt);
+			}
+			cv_signal(&pwrk->sleep_cv);
+			mutex_exit(&tgt->statlock);
+			pmcs_unlock_phy(pptr);
+			mutex_exit(&pwrk->lock);
+			return;
+		}
+
+		/*
+		 * To recover from primary failures,
+		 * we need to schedule handling events recovery.
+		 */
+		tgt->event_recovery = 1;
+		mutex_exit(&tgt->statlock);
+		pmcs_unlock_phy(pptr);
+		pwrk->ssp_event = event;
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Scheduling SSP event recovery for tgt(0x%p) "
+		    "pwrk(%p) tag(0x%x)", __func__, (void *)tgt, (void *)pwrk,
+		    pwrk->htag);
+		mutex_exit(&pwrk->lock);
+		SCHEDULE_WORK(pwp, PMCS_WORK_SSP_EVT_RECOVERY);
+	}
+
+	/* Work cannot be completed until event recovery is completed. */
+}
+
+/*
+ * SSP target event recovery
+ * Entered with a phy lock held
+ * Pwrk lock is not needed - pwrk is on the target aq and no other thread
+ * will do anything with it until this thread starts the chain of recovery.
+ * Statlock may be acquired and released.
+ */
+
+void
+pmcs_tgt_event_recovery(pmcs_hw_t *pwp, pmcwork_t *pwrk)
+{
+	pmcs_phy_t *pptr = pwrk->phy;
+	pmcs_cmd_t *sp = pwrk->arg;
+	pmcs_lun_t *lun = sp->cmd_lun;
+	pmcs_xscsi_t *tgt = pwrk->xp;
+	uint32_t event;
+	uint32_t htag;
+	uint32_t status;
+	uint8_t dstate;
+	int rv;
+
+	ASSERT(pwrk->arg != NULL);
+	ASSERT(pwrk->xp != NULL);
+	pmcs_prt(pwp, PMCS_PRT_DEBUG, "%s: event recovery for "
+	    "target 0x%p", __func__, (void *)pwrk->xp);
+	htag = pwrk->htag;
+	event = pwrk->ssp_event;
+	pwrk->ssp_event = 0xffffffff;
+	if (event == PMCOUT_STATUS_XFER_ERR_BREAK ||
+	    event == PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY ||
+	    event == PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT) {
+		/* Command may be still pending on device */
+		rv = pmcs_ssp_tmf(pwp, pptr, SAS_QUERY_TASK, htag,
+		    lun->lun_num, &status);
+		if (rv != 0) {
+			goto out;
+		}
+		if (status == SAS_RSP_TMF_COMPLETE) {
+			/* Command NOT pending on a device */
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: No pending command for tgt 0x%p",
+			    __func__, (void *)tgt);
+			/* Nothing more to do, just abort it on chip */
+			htag = 0;
+		}
+	}
+	/*
+	 * All other events left the command pending in the host
+	 * Send abort task and abort it on the chip
+	 */
+	if (htag != 0) {
+		if (pmcs_ssp_tmf(pwp, pptr, SAS_ABORT_TASK, htag,
+		    lun->lun_num, &status))
+			goto out;
+	}
+	(void) pmcs_abort(pwp, pptr, pwrk->htag, 0, 1);
+	/*
+	 * Abort either took care of work completion, or put device in
+	 * a recovery state
+	 */
+	return;
+out:
+	/* Abort failed, do full device recovery */
+	mutex_enter(&tgt->statlock);
+	if (!pmcs_get_dev_state(pwp, tgt, &dstate))
+		tgt->dev_state = dstate;
+
+	if ((tgt->dev_state != PMCS_DEVICE_STATE_IN_RECOVERY) &&
+	    (tgt->dev_state != PMCS_DEVICE_STATE_NON_OPERATIONAL)) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: Setting IN_RECOVERY for tgt 0x%p",
+		    __func__, (void *)tgt);
+		(void) pmcs_send_err_recovery_cmd(pwp,
+		    PMCS_DEVICE_STATE_IN_RECOVERY, tgt);
+	}
+	mutex_exit(&tgt->statlock);
+}
+
+/*
+ * SSP event recovery task.
+ */
+void
+pmcs_ssp_event_recovery(pmcs_hw_t *pwp)
+{
+	int idx;
+	pmcs_xscsi_t *tgt;
+	pmcs_cmd_t *cp;
+	pmcwork_t *pwrk;
+	pmcs_phy_t *pphy;
+	int er_flag;
+	uint32_t idxpwrk;
+
+restart:
+	for (idx = 0; idx < pwp->max_dev; idx++) {
+		mutex_enter(&pwp->lock);
+		tgt = pwp->targets[idx];
+		mutex_exit(&pwp->lock);
+		if (tgt != NULL) {
+			mutex_enter(&tgt->statlock);
+			if (tgt->dying || !tgt->assigned) {
+				mutex_exit(&tgt->statlock);
+				continue;
+			}
+			pphy = tgt->phy;
+			er_flag = tgt->event_recovery;
+			mutex_exit(&tgt->statlock);
+			if (pphy != NULL && er_flag != 0) {
+				pmcs_lock_phy(pphy);
+				mutex_enter(&tgt->statlock);
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "%s: found target(0x%p)", __func__,
+				    (void *) tgt);
+
+				/* Check what cmd expects recovery */
+				mutex_enter(&tgt->aqlock);
+				STAILQ_FOREACH(cp, &tgt->aq, cmd_next) {
+					/*
+					 * Since work structure is on this
+					 * target aq, and only this thread
+					 * is accessing it now, we do not need
+					 * to lock it
+					 */
+					idxpwrk = PMCS_TAG_INDEX(cp->cmd_tag);
+					pwrk = &pwp->work[idxpwrk];
+					if (pwrk->htag != cp->cmd_tag) {
+						/*
+						 * aq may contain TMF commands,
+						 * so we may not find work
+						 * structure with htag
+						 */
+						break;
+					}
+					if (pwrk->ssp_event != 0 &&
+					    pwrk->ssp_event !=
+					    PMCS_REC_EVENT) {
+						pmcs_prt(pwp,
+						    PMCS_PRT_DEBUG,
+						    "%s: pwrk(%p) ctag(0x%x)",
+						    __func__, (void *) pwrk,
+						    cp->cmd_tag);
+						mutex_exit(&tgt->aqlock);
+						mutex_exit(&tgt->statlock);
+						pmcs_tgt_event_recovery(
+						    pwp, pwrk);
+						/*
+						 * We dropped statlock, so
+						 * restart scanning from scratch
+						 */
+						pmcs_unlock_phy(pphy);
+						goto restart;
+					}
+				}
+				mutex_exit(&tgt->aqlock);
+				tgt->event_recovery = 0;
+				pmcs_prt(pwp, PMCS_PRT_DEBUG,
+				    "%s: end of SSP event recovery for "
+				    "target(0x%p)", __func__, (void *) tgt);
+				mutex_exit(&tgt->statlock);
+				pmcs_unlock_phy(pphy);
+			}
+		}
+	}
+	pmcs_prt(pwp, PMCS_PRT_DEBUG,
+	    "%s: end of SSP event recovery for pwp(0x%p)", __func__,
+	    (void *) pwp);
+}
+
+/*ARGSUSED2*/
+int
+pmcs_phy_constructor(void *buf, void *arg, int kmflags)
+{
+	pmcs_hw_t *pwp = (pmcs_hw_t *)arg;
+	pmcs_phy_t *phyp = (pmcs_phy_t *)buf;
+
+	mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER,
+	    DDI_INTR_PRI(pwp->intr_pri));
+	cv_init(&phyp->abort_all_cv, NULL, CV_DRIVER, NULL);
+	return (0);
+}
+
+/*ARGSUSED1*/
+void
+pmcs_phy_destructor(void *buf, void *arg)
+{
+	pmcs_phy_t *phyp = (pmcs_phy_t *)buf;
+
+	cv_destroy(&phyp->abort_all_cv);
+	mutex_destroy(&phyp->phy_lock);
+}
+
+/*
+ * Free all PHYs from the kmem_cache starting at phyp as well as everything
+ * on the dead_phys list.
+ *
+ * NOTE: This function does not free root PHYs as they are not allocated
+ * from the kmem_cache.
+ *
+ * No PHY locks are acquired as this should only be called during DDI_DETACH
+ * or soft reset (while pmcs interrupts are disabled).
+ */
+void
+pmcs_free_all_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
+{
+	pmcs_phy_t *tphyp, *nphyp;
+
+	if (phyp == NULL) {
+		return;
+	}
+
+	tphyp = phyp;
+	while (tphyp) {
+		nphyp = tphyp->sibling;
+
+		if (tphyp->children) {
+			pmcs_free_all_phys(pwp, tphyp->children);
+			tphyp->children = NULL;
+		}
+		if (!IS_ROOT_PHY(tphyp)) {
+			kmem_cache_free(pwp->phy_cache, tphyp);
+		}
+
+		tphyp = nphyp;
+	}
+
+	tphyp = pwp->dead_phys;
+	while (tphyp) {
+		nphyp = tphyp->sibling;
+		kmem_cache_free(pwp->phy_cache, tphyp);
+		tphyp = nphyp;
+	}
+	pwp->dead_phys = NULL;
+}
+
+/*
+ * Free a list of PHYs linked together by the sibling pointer back to the
+ * kmem cache from whence they came.  This function does not recurse, so the
+ * caller must ensure there are no children.
+ */
+void
+pmcs_free_phys(pmcs_hw_t *pwp, pmcs_phy_t *phyp)
+{
+	pmcs_phy_t *next_phy;
+
+	while (phyp) {
+		next_phy = phyp->sibling;
+		ASSERT(!mutex_owned(&phyp->phy_lock));
+		kmem_cache_free(pwp->phy_cache, phyp);
+		phyp = next_phy;
+	}
+}
+
+/*
+ * Make a copy of an existing PHY structure.  This is used primarily in
+ * discovery to compare the contents of an existing PHY with what gets
+ * reported back by an expander.
+ *
+ * This function must not be called from any context where sleeping is
+ * not possible.
+ *
+ * The new PHY is returned unlocked.
+ */
+static pmcs_phy_t *
+pmcs_clone_phy(pmcs_phy_t *orig_phy)
+{
+	pmcs_phy_t *local;
+
+	local = kmem_cache_alloc(orig_phy->pwp->phy_cache, KM_SLEEP);
+
+	/*
+	 * Go ahead and just copy everything...
+	 */
+	*local = *orig_phy;
+
+	/*
+	 * But the following must be set appropriately for this copy
+	 */
+	local->sibling = NULL;
+	local->children = NULL;
+	mutex_init(&local->phy_lock, NULL, MUTEX_DRIVER,
+	    DDI_INTR_PRI(orig_phy->pwp->intr_pri));
+
+	return (local);
+}
+
+int
+pmcs_check_acc_handle(ddi_acc_handle_t handle)
+{
+	ddi_fm_error_t de;
+
+	if (handle == NULL) {
+		return (DDI_FAILURE);
+	}
+	ddi_fm_acc_err_get(handle, &de, DDI_FME_VER0);
+	return (de.fme_status);
+}
+
+int
+pmcs_check_dma_handle(ddi_dma_handle_t handle)
+{
+	ddi_fm_error_t de;
+
+	if (handle == NULL) {
+		return (DDI_FAILURE);
+	}
+	ddi_fm_dma_err_get(handle, &de, DDI_FME_VER0);
+	return (de.fme_status);
+}
+
+
+void
+pmcs_fm_ereport(pmcs_hw_t *pwp, char *detail)
+{
+	uint64_t ena;
+	char buf[FM_MAX_CLASS];
+
+	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, detail);
+	ena = fm_ena_generate(0, FM_ENA_FMT1);
+	if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities)) {
+		ddi_fm_ereport_post(pwp->dip, buf, ena, DDI_NOSLEEP,
+		    FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, NULL);
+	}
+}
+
+int
+pmcs_check_acc_dma_handle(pmcs_hw_t *pwp)
+{
+	pmcs_chunk_t *pchunk;
+	int i;
+
+	/* check all acc & dma handles allocated in attach */
+	if ((pmcs_check_acc_handle(pwp->pci_acc_handle) != DDI_SUCCESS) ||
+	    (pmcs_check_acc_handle(pwp->msg_acc_handle) != DDI_SUCCESS) ||
+	    (pmcs_check_acc_handle(pwp->top_acc_handle) != DDI_SUCCESS) ||
+	    (pmcs_check_acc_handle(pwp->mpi_acc_handle) != DDI_SUCCESS) ||
+	    (pmcs_check_acc_handle(pwp->gsm_acc_handle) != DDI_SUCCESS)) {
+		goto check_failed;
+	}
+
+	for (i = 0; i < PMCS_NIQ; i++) {
+		if ((pmcs_check_dma_handle(
+		    pwp->iqp_handles[i]) != DDI_SUCCESS) ||
+		    (pmcs_check_acc_handle(
+		    pwp->iqp_acchdls[i]) != DDI_SUCCESS)) {
+			goto check_failed;
+		}
+	}
+
+	for (i = 0; i < PMCS_NOQ; i++) {
+		if ((pmcs_check_dma_handle(
+		    pwp->oqp_handles[i]) != DDI_SUCCESS) ||
+		    (pmcs_check_acc_handle(
+		    pwp->oqp_acchdls[i]) != DDI_SUCCESS)) {
+			goto check_failed;
+		}
+	}
+
+	if ((pmcs_check_dma_handle(pwp->cip_handles) != DDI_SUCCESS) ||
+	    (pmcs_check_acc_handle(pwp->cip_acchdls) != DDI_SUCCESS)) {
+		goto check_failed;
+	}
+
+	if (pwp->fwlog &&
+	    ((pmcs_check_dma_handle(pwp->fwlog_hndl) != DDI_SUCCESS) ||
+	    (pmcs_check_acc_handle(pwp->fwlog_acchdl) != DDI_SUCCESS))) {
+		goto check_failed;
+	}
+
+	if (pwp->regdump_hndl && pwp->regdump_acchdl &&
+	    ((pmcs_check_dma_handle(pwp->regdump_hndl) != DDI_SUCCESS) ||
+	    (pmcs_check_acc_handle(pwp->regdump_acchdl)
+	    != DDI_SUCCESS))) {
+		goto check_failed;
+	}
+
+
+	pchunk = pwp->dma_chunklist;
+	while (pchunk) {
+		if ((pmcs_check_acc_handle(pchunk->acc_handle)
+		    != DDI_SUCCESS) ||
+		    (pmcs_check_dma_handle(pchunk->dma_handle)
+		    != DDI_SUCCESS)) {
+			goto check_failed;
+		}
+		pchunk = pchunk->next;
+	}
+
+	return (0);
+
+check_failed:
+
+	return (1);
+}
+
+/*
+ * pmcs_handle_dead_phys
+ *
+ * If the PHY has no outstanding work associated with it, remove it from
+ * the dead PHY list and free it.
+ *
+ * If pwp->ds_err_recovering or pwp->configuring is set, don't run.
+ * This keeps routines that need to submit work to the chip from having to
+ * hold PHY locks to ensure that PHYs don't disappear while they do their work.
+ */
+void
+pmcs_handle_dead_phys(pmcs_hw_t *pwp)
+{
+	pmcs_phy_t *phyp, *nphyp, *pphyp;
+
+	mutex_enter(&pwp->lock);
+	mutex_enter(&pwp->config_lock);
+
+	if (pwp->configuring | pwp->ds_err_recovering) {
+		mutex_exit(&pwp->config_lock);
+		mutex_exit(&pwp->lock);
+		return;
+	}
+
+	/*
+	 * Check every PHY in the dead PHY list
+	 */
+	mutex_enter(&pwp->dead_phylist_lock);
+	phyp = pwp->dead_phys;
+	pphyp = NULL;	/* Set previous PHY to NULL */
+
+	while (phyp != NULL) {
+		pmcs_lock_phy(phyp);
+		ASSERT(phyp->dead);
+
+		nphyp = phyp->dead_next;
+
+		/*
+		 * Check for outstanding work
+		 */
+		if (phyp->ref_count > 0) {
+			pmcs_unlock_phy(phyp);
+			pphyp = phyp;	/* This PHY becomes "previous" */
+		} else if (phyp->target) {
+			pmcs_unlock_phy(phyp);
+			pmcs_prt(pwp, PMCS_PRT_DEBUG1,
+			    "%s: Not freeing PHY 0x%p: target 0x%p is not free",
+			    __func__, (void *)phyp, (void *)phyp->target);
+			pphyp = phyp;
+		} else {
+			/*
+			 * No outstanding work or target references. Remove it
+			 * from the list and free it
+			 */
+			pmcs_prt(pwp, PMCS_PRT_DEBUG,
+			    "%s: Freeing inactive dead PHY 0x%p @ %s "
+			    "target = 0x%p", __func__, (void *)phyp,
+			    phyp->path, (void *)phyp->target);
+			/*
+			 * If pphyp is NULL, then phyp was the head of the list,
+			 * so just reset the head to nphyp. Otherwise, the
+			 * previous PHY will now point to nphyp (the next PHY)
+			 */
+			if (pphyp == NULL) {
+				pwp->dead_phys = nphyp;
+			} else {
+				pphyp->dead_next = nphyp;
+			}
+			/*
+			 * If the target still points to this PHY, remove
+			 * that linkage now.
+			 */
+			if (phyp->target) {
+				mutex_enter(&phyp->target->statlock);
+				if (phyp->target->phy == phyp) {
+					phyp->target->phy = NULL;
+				}
+				mutex_exit(&phyp->target->statlock);
+			}
+			kmem_cache_free(pwp->phy_cache, phyp);
+		}
+
+		phyp = nphyp;
+	}
+
+	mutex_exit(&pwp->dead_phylist_lock);
+	mutex_exit(&pwp->config_lock);
+	mutex_exit(&pwp->lock);
+}
+
+void
+pmcs_inc_phy_ref_count(pmcs_phy_t *phyp)
+{
+	atomic_inc_32(&phyp->ref_count);
+}
+
+void
+pmcs_dec_phy_ref_count(pmcs_phy_t *phyp)
+{
+	ASSERT(phyp->ref_count != 0);
+	atomic_dec_32(&phyp->ref_count);
+}
+
+/*
+ * pmcs_reap_dead_phy
+ *
+ * This function is called from pmcs_new_tport when we have a PHY
+ * without a target pointer.  It's possible in that case that this PHY
+ * may have a "brother" on the dead_phys list.  That is, it may be the same as
+ * this one but with a different root PHY number (e.g. pp05 vs. pp04).  If
+ * that's the case, update the dead PHY and this new PHY.  If that's not the
+ * case, we should get a tran_tgt_init on this after it's reported to SCSA.
+ *
+ * Called with PHY locked.
+ */
+static void
+pmcs_reap_dead_phy(pmcs_phy_t *phyp)
+{
+	pmcs_hw_t *pwp = phyp->pwp;
+	pmcs_phy_t *ctmp;
+
+	ASSERT(mutex_owned(&phyp->phy_lock));
+
+	/*
+	 * Check the dead PHYs list
+	 */
+	mutex_enter(&pwp->dead_phylist_lock);
+	ctmp = pwp->dead_phys;
+	while (ctmp) {
+		if ((ctmp->iport != phyp->iport) ||
+		    (memcmp((void *)&ctmp->sas_address[0],
+		    (void *)&phyp->sas_address[0], 8))) {
+			ctmp = ctmp->dead_next;
+			continue;
+		}
+
+		/*
+		 * Same SAS address on same iport.  Now check to see if
+		 * the PHY path is the same with the possible exception
+		 * of the root PHY number.
+		 * The "5" is the string length of "pp00."
+		 */
+		if ((strnlen(phyp->path, 5) >= 5) &&
+		    (strnlen(ctmp->path, 5) >= 5)) {
+			if (memcmp((void *)&phyp->path[5],
+			    (void *)&ctmp->path[5],
+			    strnlen(phyp->path, 32) - 5) == 0) {
+				break;
+			}
+		}
+
+		ctmp = ctmp->dead_next;
+	}
+	mutex_exit(&pwp->dead_phylist_lock);
+
+	/*
+	 * Found a match.  Remove the target linkage and drop the
+	 * ref count on the old PHY.  Then, increment the ref count
+	 * on the new PHY to compensate.
+	 */
+	if (ctmp) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG,
+		    "%s: Found match in dead PHY list for new PHY %s",
+		    __func__, phyp->path);
+		if (ctmp->target) {
+			/*
+			 * If there is a pointer to the target in the dead
+			 * PHY, and that PHY's ref_count drops to 0, we can
+			 * clear the target linkage now.  If the PHY's
+			 * ref_count is > 1, then there may be multiple
+			 * LUNs still remaining, so leave the linkage.
+			 */
+			pmcs_inc_phy_ref_count(phyp);
+			pmcs_dec_phy_ref_count(ctmp);
+			phyp->target = ctmp->target;
+			/*
+			 * Update the target's linkage as well
+			 */
+			mutex_enter(&phyp->target->statlock);
+			phyp->target->phy = phyp;
+			phyp->target->dtype = phyp->dtype;
+			mutex_exit(&phyp->target->statlock);
+
+			if (ctmp->ref_count == 0) {
+				ctmp->target = NULL;
+			}
+		}
+	}
+}
+
+/*
+ * Called with iport lock held
+ */
+void
+pmcs_add_phy_to_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp)
+{
+	ASSERT(mutex_owned(&iport->lock));
+	ASSERT(phyp);
+	ASSERT(!list_link_active(&phyp->list_node));
+	iport->nphy++;
+	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
+	    &iport->nphy);
+	list_insert_tail(&iport->phys, phyp);
+	mutex_enter(&iport->refcnt_lock);
+	iport->refcnt++;
+	mutex_exit(&iport->refcnt_lock);
+}
+
+/*
+ * Called with the iport lock held
+ */
+void
+pmcs_remove_phy_from_iport(pmcs_iport_t *iport, pmcs_phy_t *phyp)
+{
+	pmcs_phy_t *pptr, *next_pptr;
+
+	ASSERT(mutex_owned(&iport->lock));
+
+	/*
+	 * If phyp is NULL, remove all PHYs from the iport
+	 */
+	if (phyp == NULL) {
+		for (pptr = list_head(&iport->phys); pptr != NULL;
+		    pptr = next_pptr) {
+			next_pptr = list_next(&iport->phys, pptr);
+			mutex_enter(&pptr->phy_lock);
+			pptr->iport = NULL;
+			mutex_exit(&pptr->phy_lock);
+			pmcs_rele_iport(iport);
+			list_remove(&iport->phys, pptr);
+		}
+		iport->nphy = 0;
+		return;
+	}
+
+	ASSERT(phyp);
+	ASSERT(iport->nphy > 0);
+	ASSERT(list_link_active(&phyp->list_node));
+	iport->nphy--;
+	pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS,
+	    &iport->nphy);
+	list_remove(&iport->phys, phyp);
+	pmcs_rele_iport(iport);
+}
+
+/*
+ * This function checks to see if the target pointed to by phyp is still
+ * correct.  This is done by comparing the target's unit address with the
+ * SAS address in phyp.
+ *
+ * Called with PHY locked and target statlock held
+ */
+static boolean_t
+pmcs_phy_target_match(pmcs_phy_t *phyp)
+{
+	uint64_t wwn;
+	char unit_address[PMCS_MAX_UA_SIZE];
+	boolean_t rval = B_FALSE;
+
+	ASSERT(phyp);
+	ASSERT(phyp->target);
+	ASSERT(mutex_owned(&phyp->phy_lock));
+	ASSERT(mutex_owned(&phyp->target->statlock));
+
+	wwn = pmcs_barray2wwn(phyp->sas_address);
+	(void) scsi_wwn_to_wwnstr(wwn, 1, unit_address);
+
+	if (memcmp((void *)unit_address, (void *)phyp->target->unit_address,
+	    strnlen(phyp->target->unit_address, PMCS_MAX_UA_SIZE)) == 0) {
+		rval = B_TRUE;
+	}
+
+	return (rval);
+}
+
+void
+pmcs_start_dev_state_recovery(pmcs_xscsi_t *xp, pmcs_phy_t *phyp)
+{
+	ASSERT(mutex_owned(&xp->statlock));
+	ASSERT(xp->pwp != NULL);
+
+	if (xp->recover_wait == 0) {
+		pmcs_prt(xp->pwp, PMCS_PRT_DEBUG_DEV_STATE,
+		    "%s: Start ds_recovery for tgt 0x%p/PHY 0x%p (%s)",
+		    __func__, (void *)xp, (void *)phyp, phyp->path);
+		xp->recover_wait = 1;
+
+		/*
+		 * Rather than waiting for the watchdog timer, we'll
+		 * kick it right now.
+		 */
+		SCHEDULE_WORK(xp->pwp, PMCS_WORK_DS_ERR_RECOVERY);
+		(void) ddi_taskq_dispatch(xp->pwp->tq, pmcs_worker, xp->pwp,
+		    DDI_NOSLEEP);
+	}
+}
+
+/*
+ * Increment the phy ds error retry count.
+ * If too many retries, mark phy dead and restart discovery;
+ * otherwise schedule ds recovery.
+ */
+static void
+pmcs_handle_ds_recovery_error(pmcs_phy_t *phyp, pmcs_xscsi_t *tgt,
+    pmcs_hw_t *pwp, const char *func_name, int line, char *reason_string)
+{
+	ASSERT(mutex_owned(&phyp->phy_lock));
+
+	phyp->ds_recovery_retries++;
+
+	if (phyp->ds_recovery_retries > PMCS_MAX_DS_RECOVERY_RETRIES) {
+		pmcs_prt(pwp, PMCS_PRT_DEBUG,
+		    "%s: retry limit reached after %s to PHY %s failed",
+		    func_name, reason_string, phyp->path);
+		tgt->recover_wait = 0;
+		phyp->dead = 1;
+		PHY_CHANGED_AT_LOCATION(pwp, phyp, func_name, line);
+		RESTART_DISCOVERY(pwp);
+	} else {
+		SCHEDULE_WORK(pwp, PMCS_WORK_DS_ERR_RECOVERY);
+	}
+}
--- a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/mpapi_impl.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/mpapi_impl.c	Mon Oct 05 09:17:26 2009 -0400
@@ -908,6 +908,8 @@
 	uint64_t		*oid = (uint64_t *)(input_data);
 	mpapi_item_list_t	*ilist, *mplu_path_list = NULL;
 	mpapi_lu_data_t		*mplup;
+	mpapi_path_data_t	*mppathp;
+	mdi_pathinfo_t		*pip;
 
 	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
 
@@ -934,14 +936,33 @@
 	}
 
 	while (mplu_path_list != NULL) {
-		if (count < list_len) {
-			oid_list[count] = (uint64_t)mplu_path_list->
-			    item->oid.raw_oid;
-		} else {
-			rval = MP_MORE_DATA;
+		mppathp  = (mpapi_path_data_t *)(mplu_path_list->item->idata);
+		/* skip a path that should be hidden. */
+		if (!(mppathp->hide)) {
+			pip = (mdi_pathinfo_t *)mppathp->resp;
+			mdi_hold_path(pip);
+			/*
+			 * check if the pip is marked as device removed.
+			 * When pi_flag MDI_PATHINFO_FLAGS_DEVICE_REMOVED is set
+			 * the node should have been destroyed but did not
+			 * due to open on the client node.
+			 * The driver tracks such a node through the hide flag
+			 * and doesn't report it throuth ioctl response.
+			 * The devinfo driver doesn't report such a path.
+			 */
+			if (!(MDI_PI_FLAGS_IS_DEVICE_REMOVED(pip))) {
+				if (count < list_len) {
+					oid_list[count] =
+					    (uint64_t)mplu_path_list->
+					    item->oid.raw_oid;
+				} else {
+					rval = MP_MORE_DATA;
+				}
+				count++;
+			}
+			mdi_rele_path(pip);
 		}
 		mplu_path_list = mplu_path_list->next;
-		count++;
 	}
 
 	mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
@@ -972,6 +993,8 @@
 	uint64_t		*oid = (uint64_t *)(input_data);
 	mpapi_item_list_t	*ilist, *mpinit_path_list = NULL;
 	mpapi_initiator_data_t	*mpinitp;
+	mpapi_path_data_t	*mppathp;
+	mdi_pathinfo_t		*pip;
 
 	ilist = vhci->mp_priv->
 	    obj_hdr_list[MP_OBJECT_TYPE_INITIATOR_PORT]->head;
@@ -1016,14 +1039,33 @@
 	}
 
 	while (mpinit_path_list != NULL) {
-		if (count < list_len) {
-			oid_list[count] = (uint64_t)mpinit_path_list->
-			    item->oid.raw_oid;
-		} else {
-			rval = MP_MORE_DATA;
+		mppathp  = (mpapi_path_data_t *)(mpinit_path_list->item->idata);
+		/* skip a path that should be hidden. */
+		if (!(mppathp->hide)) {
+			pip = (mdi_pathinfo_t *)mppathp->resp;
+			mdi_hold_path(pip);
+			/*
+			 * check if the pip is marked as device removed.
+			 * When pi_flag MDI_PATHINFO_FLAGS_DEVICE_REMOVED is set
+			 * the node should have been destroyed but did not
+			 * due to open on the client node.
+			 * The driver tracks such a node through the hide flag
+			 * and doesn't report it throuth ioctl response.
+			 * The devinfo driver doesn't report such a path.
+			 */
+			if (!(MDI_PI_FLAGS_IS_DEVICE_REMOVED(pip))) {
+				if (count < list_len) {
+					oid_list[count] =
+					    (uint64_t)mpinit_path_list->
+					    item->oid.raw_oid;
+				} else {
+					rval = MP_MORE_DATA;
+				}
+				count++;
+			}
+			mdi_rele_path(pip);
 		}
 		mpinit_path_list = mpinit_path_list->next;
-		count++;
 	}
 
 	mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
@@ -1054,6 +1096,8 @@
 	uint64_t		*oid = (uint64_t *)(input_data);
 	mpapi_item_list_t	*ilist, *mptp_path_list = NULL;
 	mpapi_tport_data_t	*mptpp;
+	mpapi_path_data_t	*mppathp;
+	mdi_pathinfo_t		*pip;
 
 	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_TARGET_PORT]->head;
 
@@ -1080,14 +1124,33 @@
 	}
 
 	while (mptp_path_list != NULL) {
-		if (count < list_len) {
-			oid_list[count] =
-			    (uint64_t)mptp_path_list->item->oid.raw_oid;
-		} else {
-			rval = MP_MORE_DATA;
+		mppathp  = (mpapi_path_data_t *)(mptp_path_list->item->idata);
+		/* skip a path that should be hidden. */
+		if (!(mppathp->hide)) {
+			pip = (mdi_pathinfo_t *)mppathp->resp;
+			mdi_hold_path(pip);
+			/*
+			 * check if the pip is marked as device removed.
+			 * When pi_flag MDI_PATHINFO_FLAGS_DEVICE_REMOVED is set
+			 * the node should have been destroyed but did not
+			 * due to open on the client node.
+			 * The driver tracks such a node through the hide flag
+			 * and doesn't report it throuth ioctl response.
+			 * The devinfo driver doesn't report such a path.
+			 */
+			if (!(MDI_PI_FLAGS_IS_DEVICE_REMOVED(pip))) {
+				if (count < list_len) {
+					oid_list[count] =
+					    (uint64_t)mptp_path_list->
+					    item->oid.raw_oid;
+				} else {
+					rval = MP_MORE_DATA;
+				}
+				count++;
+			}
+			mdi_rele_path(pip);
 		}
 		mptp_path_list = mptp_path_list->next;
-		count++;
 	}
 
 	mpioc->mp_alen = (uint32_t)(count * sizeof (uint64_t));
@@ -2559,21 +2622,26 @@
 			if (strncmp(interconnect,
 			    INTERCONNECT_FABRIC_STR,
 			    strlen(interconnect)) == 0) {
-				mp_interconnect_type = 2;
+				mp_interconnect_type =
+				    MP_DRVR_TRANSPORT_TYPE_FC;
 			} else if (strncmp(interconnect,
 			    INTERCONNECT_PARALLEL_STR,
 			    strlen(interconnect)) == 0) {
-				mp_interconnect_type = 3;
+				mp_interconnect_type =
+				    MP_DRVR_TRANSPORT_TYPE_SPI;
 			} else if (strncmp(interconnect,
 			    INTERCONNECT_ISCSI_STR,
 			    strlen(interconnect)) == 0) {
-				mp_interconnect_type = 4;
+				mp_interconnect_type =
+				    MP_DRVR_TRANSPORT_TYPE_ISCSI;
 			} else if (strncmp(interconnect,
 			    INTERCONNECT_IBSRP_STR,
 			    strlen(interconnect)) == 0) {
-				mp_interconnect_type = 5;
+				mp_interconnect_type =
+				    MP_DRVR_TRANSPORT_TYPE_IFB;
 			} else {
-				mp_interconnect_type = 0;
+				mp_interconnect_type =
+				    MP_DRVR_TRANSPORT_TYPE_UNKNOWN;
 			}
 
 			init = kmem_zalloc(
@@ -2762,6 +2830,7 @@
 			path->resp = res;
 			path->path_name = pname;
 			path->valid = 1;
+			path->hide = 0;
 			path->prop.id = item->oid.raw_oid;
 			item->idata = (void *)path;
 			vhci_mpapi_log_sysevent(vhci->vhci_dip,
@@ -2886,6 +2955,7 @@
 		 */
 		pd = path_list->item->idata;
 		pd->valid = 1;
+		pd->hide = 0;
 		pd->resp = pip;
 	}
 
@@ -3907,14 +3977,27 @@
 	}
 
 	/*
+	 * Check if the pathinfo is uninitialized(destroyed).
+	 */
+	if (state == MP_DRVR_PATH_STATE_UNINIT) {
+		pp->hide = 1;
+		VHCI_DEBUG(6, (CE_NOTE, NULL, "vhci_mpapi_set_path_state: "
+		    "path(pip: %p) is uninited(destroyed).",
+		    (void *)pip));
+	} else {
+		pp->hide = 0;
+	}
+	/*
 	 * Find if there are any paths at all to the lun
 	 */
 	if ((state == MP_DRVR_PATH_STATE_REMOVED) || (state ==
 	    MP_DRVR_PATH_STATE_PATH_ERR) || (state ==
 	    MP_DRVR_PATH_STATE_LU_ERR) || (state ==
-	    MP_DRVR_PATH_STATE_UNKNOWN)) {
+	    MP_DRVR_PATH_STATE_UNKNOWN) || pp->hide) {
 		pp->valid = 0;
-
+		VHCI_DEBUG(6, (CE_NOTE, NULL, "vhci_mpapi_set_path_state: "
+		    "path(pip: %p) is not okay state.  Set to invalid.",
+		    (void *)pip));
 		svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
 		svl = svp->svp_svl;
 		/*
@@ -3937,6 +4020,9 @@
 			if (lu_list != NULL) {
 				ld = lu_list->item->idata;
 				ld->valid = 0;
+				VHCI_DEBUG(6, (CE_NOTE, NULL,
+				    "vhci_mpapi_set_path_state: "
+				    " Invalidated LU(%s)", svl->svl_lun_wwn));
 			}
 		}
 	}
@@ -4198,7 +4284,8 @@
 		    MDI_PI_IS_STANDBY(ret_pip) ||
 		    MDI_PI_IS_INIT(ret_pip)) &&
 		    !(MDI_PI_IS_DISABLE(ret_pip) ||
-		    MDI_PI_IS_TRANSIENT(ret_pip))) {
+		    MDI_PI_IS_TRANSIENT(ret_pip) ||
+		    MDI_PI_FLAGS_IS_DEVICE_REMOVED(ret_pip))) {
 			count++;
 		}
 		mdi_pi_unlock(ret_pip);
--- a/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/adapters/scsi_vhci/scsi_vhci.c	Mon Oct 05 09:17:26 2009 -0400
@@ -221,6 +221,7 @@
 #endif
 static void vhci_print_prout_keys(scsi_vhci_lun_t *, char *);
 static void vhci_uscsi_iodone(struct scsi_pkt *pkt);
+static void vhci_invalidate_mpapi_lu(struct scsi_vhci *, scsi_vhci_lun_t *);
 
 /*
  * MP-API related functions
@@ -1514,7 +1515,7 @@
 static int
 vhci_scsi_reset_target(struct scsi_address *ap, int level, uint8_t select_path)
 {
-	dev_info_t		*vdip, *pdip, *cdip;
+	dev_info_t		*vdip, *cdip;
 	mdi_pathinfo_t		*pip = NULL;
 	mdi_pathinfo_t		*npip = NULL;
 	int			rval = -1;
@@ -1572,12 +1573,10 @@
 
 	if (hba->tran_reset != NULL) {
 		if (hba->tran_reset(pap, level) == 0) {
-			pdip = mdi_pi_get_phci(pip);
-			vhci_log(CE_WARN, vdip, "!(%s%d):"
-			    " path (%s%d), reset %d failed",
+			vhci_log(CE_WARN, vdip, "!%s%d: "
+			    "path %s, reset %d failed",
 			    ddi_driver_name(cdip), ddi_get_instance(cdip),
-			    ddi_driver_name(pdip), ddi_get_instance(pdip),
-			    level);
+			    mdi_pi_spathname(pip), level);
 
 			/*
 			 * Select next path and issue the reset, repeat
@@ -2052,28 +2051,32 @@
 	ASSERT(sd != NULL);
 	ASSERT(name != NULL);
 
+	*name = 0;
 	cdip = sd->sd_dev;
 
 	ASSERT(cdip != NULL);
 
-	if (mdi_component_is_client(cdip, NULL) != MDI_SUCCESS) {
-		name[0] = '\0';
+	if (mdi_component_is_client(cdip, NULL) != MDI_SUCCESS)
 		return (1);
-	}
 
 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, cdip, PROPFLAGS,
-	    MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS) {
-		name[0] = '\0';
+	    MDI_CLIENT_GUID_PROP, &guid) != DDI_SUCCESS)
 		return (1);
-	}
-
+
+	/*
+	 * Message is "sd# at scsi_vhci0: unit-address <guid>: <bus_addr>".
+	 *	<guid>		bus_addr argument == 0
+	 *	<bus_addr>	bus_addr argument != 0
+	 * Since the <guid> is already provided with unit-address, we just
+	 * provide failover module in <bus_addr> to keep output shorter.
+	 */
 	vlun = ADDR2VLUN(&sd->sd_address);
-	if (bus_addr && vlun && vlun->svl_fops_name) {
-		/* report the guid and the name of the failover module */
-		(void) snprintf(name, len, "g%s %s", guid, vlun->svl_fops_name);
-	} else {
-		/* report the guid */
+	if (bus_addr == 0) {
+		/* report the guid:  */
 		(void) snprintf(name, len, "g%s", guid);
+	} else if (vlun && vlun->svl_fops_name) {
+		/* report the name of the failover module */
+		(void) snprintf(name, len, "%s", vlun->svl_fops_name);
 	}
 
 	ddi_prop_free(guid);
@@ -2431,12 +2434,15 @@
 				sema_v(&vlun->svl_pgr_sema);
 			}
 			/*
-			 * Looks like a fatal error.
-			 * May be device disappeared underneath.
-			 * Give another chance to target driver for a retry to
-			 * get another path.
+			 * Consider it a fatal error if b_error is
+			 * set as a result of DMA binding failure
+			 * vs. a condition of being temporarily out of
+			 * some resource
 			 */
-			return (TRAN_BUSY);
+			if (geterror(vpkt->vpkt_tgt_init_bp))
+				return (TRAN_FATAL_ERROR);
+			else
+				return (TRAN_BUSY);
 		}
 	}
 
@@ -3054,8 +3060,8 @@
 	static char		*timeout_err = "Command Timeout";
 	static char		*parity_err = "Parity Error";
 	char			*err_str = NULL;
-	dev_info_t		*vdip, *cdip, *pdip;
-	char			*cpath, *dpath;
+	dev_info_t		*vdip, *cdip;
+	char			*cpath;
 
 	ASSERT(vpkt != NULL);
 	tpkt = vpkt->vpkt_tgt_pkt;
@@ -3359,6 +3365,21 @@
 		break;
 
 	case CMD_DEV_GONE:
+		/*
+		 * If this is the last path then report CMD_DEV_GONE to the
+		 * target driver, otherwise report BUSY to triggger retry.
+		 */
+		if (vlun->svl_dip &&
+		    (mdi_client_get_path_count(vlun->svl_dip) <= 1)) {
+			struct scsi_vhci	*vhci;
+			vhci = ADDR2VHCI(&tpkt->pkt_address);
+			VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_intr received "
+			    "cmd_dev_gone on last path\n"));
+			(void) vhci_invalidate_mpapi_lu(vhci, vlun);
+			break;
+		}
+
+		/* Report CMD_CMPLT-with-BUSY to cause retry. */
 		VHCI_DEBUG(1, (CE_NOTE, NULL, "vhci_intr received "
 		    "cmd_dev_gone\n"));
 		tpkt->pkt_reason = CMD_CMPLT;
@@ -3414,17 +3435,13 @@
 	if ((err_str != NULL) && (pkt->pkt_reason !=
 	    svp->svp_last_pkt_reason)) {
 		cdip = vlun->svl_dip;
-		pdip = mdi_pi_get_phci(vpkt->vpkt_path);
 		vdip = ddi_get_parent(cdip);
 		cpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
-		dpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
-		vhci_log(CE_WARN, vdip, "!%s (%s%d): %s on path %s (%s%d)",
+		vhci_log(CE_WARN, vdip, "!%s (%s%d): %s on path %s",
 		    ddi_pathname(cdip, cpath), ddi_driver_name(cdip),
 		    ddi_get_instance(cdip), err_str,
-		    ddi_pathname(pdip, dpath), ddi_driver_name(pdip),
-		    ddi_get_instance(pdip));
+		    mdi_pi_spathname(vpkt->vpkt_path));
 		kmem_free(cpath, MAXPATHLEN);
-		kmem_free(dpath, MAXPATHLEN);
 	}
 	svp->svp_last_pkt_reason = pkt->pkt_reason;
 	VHCI_DECR_PATH_CMDCOUNT(svp);
@@ -3672,7 +3689,7 @@
 vhci_update_pathstates(void *arg)
 {
 	mdi_pathinfo_t			*pip, *npip;
-	dev_info_t			*dip, *pdip;
+	dev_info_t			*dip;
 	struct scsi_failover_ops	*fo;
 	struct scsi_vhci_priv		*svp;
 	struct scsi_device		*psd;
@@ -3680,7 +3697,7 @@
 	char				*pclass, *tptr;
 	struct scsi_vhci_lun		*vlun = (struct scsi_vhci_lun *)arg;
 	int				sps; /* mdi_select_path() status */
-	char				*cpath, *dpath;
+	char				*cpath;
 	struct scsi_vhci		*vhci;
 	struct scsi_pkt			*pkt;
 	struct buf			*bp;
@@ -3747,22 +3764,16 @@
 				VHCI_DEBUG(1, (CE_NOTE, NULL,
 				    "!vhci_update_pathstates: marking path"
 				    " 0x%p as ONLINE\n", (void *)pip));
-				pdip = mdi_pi_get_phci(pip);
 				cpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
-				dpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
-				vhci_log(CE_NOTE, ddi_get_parent(dip), "!%s"
-				    " (%s%d): path %s (%s%d) target address %s"
-				    " is now ONLINE because of"
-				    " an externally initiated failover",
+				vhci_log(CE_NOTE, ddi_get_parent(dip), "!%s "
+				    "(%s%d): path %s "
+				    "is now ONLINE because of "
+				    "an externally initiated failover",
 				    ddi_pathname(dip, cpath),
 				    ddi_driver_name(dip),
 				    ddi_get_instance(dip),
-				    ddi_pathname(pdip, dpath),
-				    ddi_driver_name(pdip),
-				    ddi_get_instance(pdip),
-				    mdi_pi_get_addr(pip));
+				    mdi_pi_spathname(pip));
 				kmem_free(cpath, MAXPATHLEN);
-				kmem_free(dpath, MAXPATHLEN);
 				mdi_pi_set_state(pip,
 				    MDI_PATHINFO_STATE_ONLINE);
 				mdi_pi_set_preferred(pip,
@@ -3861,22 +3872,16 @@
 			VHCI_DEBUG(1, (CE_NOTE, NULL,
 			    "!vhci_update_pathstates: marking path"
 			    " 0x%p as STANDBY\n", (void *)pip));
-			pdip = mdi_pi_get_phci(pip);
 			cpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
-			dpath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
-			vhci_log(CE_NOTE, ddi_get_parent(dip), "!%s"
-			    " (%s%d): path %s (%s%d) target address %s"
-			    " is now STANDBY because of"
-			    " an externally initiated failover",
+			vhci_log(CE_NOTE, ddi_get_parent(dip), "!%s "
+			    "(%s%d): path %s "
+			    "is now STANDBY because of "
+			    "an externally initiated failover",
 			    ddi_pathname(dip, cpath),
 			    ddi_driver_name(dip),
 			    ddi_get_instance(dip),
-			    ddi_pathname(pdip, dpath),
-			    ddi_driver_name(pdip),
-			    ddi_get_instance(pdip),
-			    mdi_pi_get_addr(pip));
+			    mdi_pi_spathname(pip));
 			kmem_free(cpath, MAXPATHLEN);
-			kmem_free(dpath, MAXPATHLEN);
 			mdi_pi_set_state(pip,
 			    MDI_PATHINFO_STATE_STANDBY);
 			mdi_pi_set_preferred(pip,
@@ -4103,7 +4108,7 @@
 	hba = ddi_get_driver_private(pdip);
 	ASSERT(hba != NULL);
 
-	vhci_mpapi_set_path_state(vdip, pip, MP_DRVR_PATH_STATE_REMOVED);
+	vhci_mpapi_set_path_state(vdip, pip, MP_DRVR_PATH_STATE_UNINIT);
 	svp = (scsi_vhci_priv_t *)mdi_pi_get_vhci_private(pip);
 	if (svp == NULL) {
 		/* path already freed. Nothing to do. */
@@ -8647,3 +8652,33 @@
 	}
 }
 #endif
+static void
+vhci_invalidate_mpapi_lu(struct scsi_vhci *vhci, scsi_vhci_lun_t *vlun)
+{
+	char			*svl_wwn;
+	mpapi_item_list_t	*ilist;
+	mpapi_lu_data_t		*ld;
+
+	if (vlun == NULL) {
+		return;
+	} else {
+		svl_wwn = vlun->svl_lun_wwn;
+	}
+
+	ilist = vhci->mp_priv->obj_hdr_list[MP_OBJECT_TYPE_MULTIPATH_LU]->head;
+
+	while (ilist != NULL) {
+		ld = (mpapi_lu_data_t *)(ilist->item->idata);
+		if ((ld != NULL) && (strncmp(ld->prop.name, svl_wwn,
+		    strlen(svl_wwn)) == 0)) {
+			ld->valid = 0;
+			VHCI_DEBUG(6, (CE_WARN, NULL,
+			    "vhci_invalidate_mpapi_lu: "
+			    "Invalidated LU(%s)", svl_wwn));
+			return;
+		}
+		ilist = ilist->next;
+	}
+	VHCI_DEBUG(6, (CE_WARN, NULL, "vhci_invalidate_mpapi_lu: "
+	    "Could not find LU(%s) to invalidate.", svl_wwn));
+}
--- a/usr/src/uts/common/io/scsi/conf/scsi_confdata.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/conf/scsi_confdata.c	Mon Oct 05 09:17:26 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -98,4 +98,22 @@
  */
 int	scsi_fm_capable = DDI_FM_EREPORT_CAPABLE;
 
+/*
+ * SCSI enumeration options defines are kept in <scsi/conf/autoconf.h>.
+ * When scsi_enumeration is enabled, driver.conf enumeration is unnecessary.
+ *
+ * The global variable "scsi_enumeration" is used as the default value of the
+ * "scsi-enumeration" property. In addition to enabline/disabling enumeration
+ * (bit 0), target and lun threading can be specified.
+ *
+ *	0	driver.conf enumeration
+ *	1	dynamic enumeration with target/lun multi-threading.
+ *	3	dynamic enumeration with lun multi-threading disabled.
+ *	5	dynamic enumeration with target multi-threading disabled;
+ *	7	dynamic enumeration with target/lun multi-threading disabled.
+ *
+ * Default is currently driver.conf enumeration (0).
+ */
+int	scsi_enumeration = 0;
+
 #endif	/* _KERNEL */
--- a/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/conf/scsi_confsubr.c	Mon Oct 05 09:17:26 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -39,11 +39,11 @@
  * macro for filling in lun value for scsi-1 support
  */
 
-#define	FILL_SCSI1_LUN(devp, pkt) \
-	if ((devp->sd_address.a_lun > 0) && \
-	    (devp->sd_inq->inq_ansi == 0x1)) { \
+#define	FILL_SCSI1_LUN(sd, pkt) \
+	if ((sd->sd_address.a_lun > 0) && \
+	    (sd->sd_inq->inq_ansi == 0x1)) { \
 		((union scsi_cdb *)(pkt)->pkt_cdbp)->scc_lun = \
-		    devp->sd_address.a_lun; \
+		    sd->sd_address.a_lun; \
 	}
 
 extern struct mod_ops mod_miscops;
@@ -64,6 +64,12 @@
 static void scsi_establish_LUN_limit(struct scsi_device *);
 static void scsi_update_parent_ss2_prop(dev_info_t *, int, int);
 
+static int check_vpd_page_support8083(struct scsi_device *sd,
+		int (*callback)(), int *, int *);
+static int send_scsi_INQUIRY(struct scsi_device *sd,
+		int (*callback)(), uchar_t *bufaddr, size_t buflen,
+		uchar_t evpd, uchar_t page_code, size_t *lenp);
+
 /*
  * this int-array HBA-node property keeps track of strictly SCSI-2
  * target IDs
@@ -209,10 +215,10 @@
 	return (mod_info(&modlinkage, modinfop));
 }
 
-#define	ROUTE	(&devp->sd_address)
+#define	ROUTE	(&sd->sd_address)
 
 static int
-scsi_slave_do_rqsense(struct scsi_device *devp, int (*callback)())
+scsi_slave_do_rqsense(struct scsi_device *sd, int (*callback)())
 {
 	struct scsi_pkt *rq_pkt = NULL;
 	struct buf *rq_bp = NULL;
@@ -243,7 +249,7 @@
 
 	(void) scsi_setup_cdb((union scsi_cdb *)rq_pkt->
 	    pkt_cdbp, SCMD_REQUEST_SENSE, 0, SENSE_LENGTH, 0);
-	FILL_SCSI1_LUN(devp, rq_pkt);
+	FILL_SCSI1_LUN(sd, rq_pkt);
 	rq_pkt->pkt_flags = FLAG_NOINTR|FLAG_NOPARITY|FLAG_SENSING;
 
 	/*
@@ -275,11 +281,11 @@
  *
  * SCSI slave probe routine - provided as a service to target drivers
  *
- * Mostly attempts to allocate and fill devp inquiry data..
+ * Mostly attempts to allocate and fill sd inquiry data..
  */
 
 int
-scsi_slave(struct scsi_device *devp, int (*callback)())
+scsi_slave(struct scsi_device *sd, int (*callback)())
 {
 	struct scsi_pkt	*pkt;
 	int		rval = SCSIPROBE_EXISTS;
@@ -298,7 +304,7 @@
 
 	(void) scsi_setup_cdb((union scsi_cdb *)pkt->pkt_cdbp,
 	    SCMD_TEST_UNIT_READY, 0, 0, 0);
-	FILL_SCSI1_LUN(devp, pkt);
+	FILL_SCSI1_LUN(sd, pkt);
 	pkt->pkt_flags = FLAG_NOINTR|FLAG_NOPARITY;
 
 	if (scsi_poll(pkt) < 0) {
@@ -313,7 +319,7 @@
 				 * scanner and processor devices can return a
 				 * check condition here
 				 */
-				rval = scsi_slave_do_rqsense(devp, callback);
+				rval = scsi_slave_do_rqsense(sd, callback);
 		}
 
 		if (rval != SCSIPROBE_EXISTS) {
@@ -338,18 +344,19 @@
 	 */
 	if ((pkt->pkt_state & STATE_ARQ_DONE) == 0) {
 		if (((struct scsi_status *)pkt->pkt_scbp)->sts_chk) {
-			rval = scsi_slave_do_rqsense(devp, callback);
+			rval = scsi_slave_do_rqsense(sd, callback);
 		}
 	}
 
 	/*
 	 * call scsi_probe to do the inquiry
-	 * XXX there is minor difference with the old scsi_slave implementation:
-	 * busy conditions are not handled in scsi_probe.
+	 *
+	 * NOTE: there is minor difference with the old scsi_slave
+	 * implementation: busy conditions are not handled in scsi_probe.
 	 */
 	scsi_destroy_pkt(pkt);
 	if (rval == SCSIPROBE_EXISTS) {
-		return (scsi_probe(devp, callback));
+		return (scsi_probe(sd, callback));
 	} else {
 		return (rval);
 	}
@@ -363,7 +370,7 @@
  */
 /*ARGSUSED*/
 void
-scsi_unslave(struct scsi_device *devp)
+scsi_unslave(struct scsi_device *sd)
 {
 }
 
@@ -375,7 +382,7 @@
  */
 /*ARGSUSED*/
 void
-scsi_unprobe(struct scsi_device *devp)
+scsi_unprobe(struct scsi_device *sd)
 {
 }
 
@@ -445,12 +452,12 @@
  * intact.
  */
 int
-scsi_probe(struct scsi_device *devp, int (*callback)())
+scsi_probe(struct scsi_device *sd, int (*callback)())
 {
-	int ret;
-	scsi_hba_tran_t		*tran = devp->sd_address.a_hba_tran;
+	int			ret;
+	scsi_hba_tran_t		*tran = sd->sd_address.a_hba_tran;
 
-	if (scsi_check_ss2_LUN_limit(devp) != 0) {
+	if (scsi_check_ss2_LUN_limit(sd) != 0) {
 		/*
 		 * caller is trying to probe a strictly-SCSI-2 device
 		 * with a LUN that is too large, so do not allow it
@@ -459,32 +466,43 @@
 	}
 
 	if (tran->tran_tgt_probe != NULL) {
-		ret = (*tran->tran_tgt_probe)(devp, callback);
+		ret = (*tran->tran_tgt_probe)(sd, callback);
 	} else {
-		ret = scsi_hba_probe(devp, callback);
+		ret = scsi_hba_probe(sd, callback);
 	}
 
 	if (ret == SCSIPROBE_EXISTS) {
-		create_inquiry_props(devp);
+		create_inquiry_props(sd);
 		/* is this a strictly-SCSI-2 node ?? */
-		scsi_establish_LUN_limit(devp);
+		scsi_establish_LUN_limit(sd);
 	}
 
 	return (ret);
 }
+/*
+ * probe scsi device using any available path
+ *
+ */
+int
+scsi_hba_probe(struct scsi_device *sd, int (*callback)())
+{
+	return (scsi_hba_probe_pi(sd, callback, 0));
+}
 
 /*
- * scsi_hba_probe does not do any test unit ready's which access the medium
+ * probe scsi device using specific path
+ *
+ * scsi_hba_probe_pi does not do any test unit ready's which access the medium
  * and could cause busy or not ready conditions.
- * scsi_hba_probe does 2 inquiries and a rqsense to clear unit attention
+ * scsi_hba_probe_pi does 2 inquiries and a rqsense to clear unit attention
  * and to allow sync negotiation to take place
- * finally, scsi_hba_probe does one more inquiry which should
+ * finally, scsi_hba_probe_pi does one more inquiry which should
  * reliably tell us what kind of target we have.
  * A scsi-2 compliant target should be able to	return inquiry with 250ms
  * and we actually wait more than a second after reset.
  */
 int
-scsi_hba_probe(struct scsi_device *devp, int (*callback)())
+scsi_hba_probe_pi(struct scsi_device *sd, int (*callback)(), int pi)
 {
 	struct scsi_pkt		*inq_pkt = NULL;
 	struct scsi_pkt		*rq_pkt = NULL;
@@ -494,11 +512,11 @@
 	int			(*cb_flag)();
 	int			pass = 1;
 
-	if (devp->sd_inq == NULL) {
-		devp->sd_inq = (struct scsi_inquiry *)
+	if (sd->sd_inq == NULL) {
+		sd->sd_inq = (struct scsi_inquiry *)
 		    kmem_alloc(SUN_INQSIZE, ((callback == SLEEP_FUNC) ?
 		    KM_SLEEP : KM_NOSLEEP));
-		if (devp->sd_inq == NULL) {
+		if (sd->sd_inq == NULL) {
 			goto out;
 		}
 	}
@@ -529,6 +547,14 @@
 	inq_pkt->pkt_flags = FLAG_NOINTR|FLAG_NOPARITY;
 
 	/*
+	 * set transport path
+	 */
+	if (pi && scsi_pkt_allocated_correctly(inq_pkt)) {
+		inq_pkt->pkt_path_instance = pi;
+		inq_pkt->pkt_flags |= FLAG_PKT_PATH_INSTANCE;
+	}
+
+	/*
 	 * the first inquiry will tell us whether a target
 	 * responded
 	 *
@@ -537,8 +563,8 @@
 	 * incorrect after we have real sd_inq data (for lun0) we will do a
 	 * second pass during which FILL_SCSI1_LUN will place lun in CDB.
 	 */
-	bzero((caddr_t)devp->sd_inq, SUN_INQSIZE);
-again:	FILL_SCSI1_LUN(devp, inq_pkt);
+	bzero((caddr_t)sd->sd_inq, SUN_INQSIZE);
+again:	FILL_SCSI1_LUN(sd, inq_pkt);
 
 	if (scsi_test(inq_pkt) < 0) {
 		if (inq_pkt->pkt_reason == CMD_INCOMPLETE) {
@@ -614,10 +640,18 @@
 
 			(void) scsi_setup_cdb((union scsi_cdb *)rq_pkt->
 			    pkt_cdbp, SCMD_REQUEST_SENSE, 0, SENSE_LENGTH, 0);
-			FILL_SCSI1_LUN(devp, rq_pkt);
+			FILL_SCSI1_LUN(sd, rq_pkt);
 			rq_pkt->pkt_flags = FLAG_NOINTR|FLAG_NOPARITY;
 
 			/*
+			 * set transport path
+			 */
+			if (pi && scsi_pkt_allocated_correctly(rq_pkt)) {
+				rq_pkt->pkt_path_instance = pi;
+				rq_pkt->pkt_flags |= FLAG_PKT_PATH_INSTANCE;
+			}
+
+			/*
 			 * The FILL_SCSI1_LUN above will find "inq_ansi != 1"
 			 * on first pass, see "again" comment above.
 			 *
@@ -698,7 +732,7 @@
 	} else {
 		ASSERT(inq_pkt->pkt_resid >= 0);
 		bcopy((caddr_t)inq_bp->b_un.b_addr,
-		    (caddr_t)devp->sd_inq, (SUN_INQSIZE - inq_pkt->pkt_resid));
+		    (caddr_t)sd->sd_inq, (SUN_INQSIZE - inq_pkt->pkt_resid));
 		rval = SCSIPROBE_EXISTS;
 	}
 
@@ -708,9 +742,9 @@
 	 * the "real" lun needs to be embedded into the cdb.
 	 */
 	if ((rval == SCSIPROBE_EXISTS) && (pass == 1) &&
-	    (devp->sd_address.a_lun > 0) && (devp->sd_inq->inq_ansi == 0x1)) {
+	    (sd->sd_address.a_lun > 0) && (sd->sd_inq->inq_ansi == 0x1)) {
 		pass++;
-		if (devp->sd_address.a_lun <= 7)
+		if (sd->sd_address.a_lun <= 7)
 			goto again;
 
 		/*
@@ -739,70 +773,171 @@
  * Convert from a scsi_device structure pointer to a scsi_hba_tran structure
  * pointer. The correct way to do this is
  *
- *	#define	DEVP_TO_TRAN(devp)	((devp)->sd_address.a_hba_tran)
+ *	#define	DEVP_TO_TRAN(sd)	((sd)->sd_address.a_hba_tran)
  *
  * however we have some consumers that place their own vector in a_hba_tran. To
  * avoid problems, we implement this using the sd_tran_safe. See
  * scsi_hba_initchild for more details.
  */
-#define	DEVP_TO_TRAN(devp)	((devp)->sd_tran_safe)
+#define	DEVP_TO_TRAN(sd)	((sd)->sd_tran_safe)
 
 /*
- * Function to get human readable REPORTDEV addressing information from
- * scsi address structure for SPI when tran_get_bus_addr is not implemented.
+ * Function, callable from SCSA framework, to get 'human' readable REPORTDEV
+ * addressing information from scsi_device properties.
+ */
+int
+scsi_ua_get_reportdev(struct scsi_device *sd, char *ra, int len)
+{
+	/* use deprecated tran_get_bus_addr interface if it is defined */
+	/* NOTE: tran_get_bus_addr is a poor name choice for interface */
+	if (DEVP_TO_TRAN(sd)->tran_get_bus_addr)
+		return ((*DEVP_TO_TRAN(sd)->tran_get_bus_addr)(sd, ra, len));
+	return (scsi_hba_ua_get_reportdev(sd, ra, len));
+}
+
+/*
+ * Function, callable from HBA driver's tran_get_bus_addr(9E) implementation,
+ * to get standard form of human readable REPORTDEV addressing information
+ * from scsi_device properties.
  */
 int
-scsi_get_bus_addr(struct scsi_device *devp, char *ba, int len)
+scsi_hba_ua_get_reportdev(struct scsi_device *sd, char *ra, int len)
 {
-	struct scsi_address	*ap;
+	int		tgt, lun, sfunc;
+	char		*tgt_port;
+	scsi_lun64_t	lun64;
+
+	/* get device unit-address properties */
+	tgt = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_TARGET, -1);
+	if (scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_PROP_SUCCESS)
+		tgt_port = NULL;
+	if ((tgt == -1) && (tgt_port == NULL))
+		return (0);		/* no target */
+
+	lun = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_LUN, 0);
+	lun64 = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_LUN64, lun);
+	sfunc = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_SFUNC, -1);
 
-	/* use tran_get_bus_addr interface if it is defined */
-	if (DEVP_TO_TRAN(devp)->tran_get_bus_addr)
-		return ((*DEVP_TO_TRAN(devp)->tran_get_bus_addr)
-		    (devp, ba, len));
+	/*
+	 * XXX should the default be to print this in decimal for
+	 * "human readable" form, so it matches conf files?
+	 */
+	if (tgt_port) {
+		if (sfunc == -1)
+			(void) snprintf(ra, len,
+			    "%s %s lun %" PRIx64,
+			    SCSI_ADDR_PROP_TARGET_PORT, tgt_port, lun64);
+		else
+			(void) snprintf(ra, len,
+			    "%s %s lun %" PRIx64 " sfunc %x",
+			    SCSI_ADDR_PROP_TARGET_PORT, tgt_port, lun64, sfunc);
+		scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
+	} else {
+		if (sfunc == -1)
+			(void) snprintf(ra, len,
+			    "%s %x lun %" PRIx64,
+			    SCSI_ADDR_PROP_TARGET, tgt, lun64);
+		else
+			(void) snprintf(ra, len,
+			    "%s %x lun %" PRIx64 " sfunc %x",
+			    SCSI_ADDR_PROP_TARGET, tgt, lun64, sfunc);
+	}
 
-	ap = &devp->sd_address;
-	(void) snprintf(ba, len, "target %x lun %x", ap->a_target, ap->a_lun);
 	return (1);
 }
 
 /*
- * scsi_set_name: using properties, return "unit-address" string.
+ * scsi_ua_get: using properties, return "unit-address" string.
+ * Called by SCSA framework, may call HBAs tran function.
+ */
+int
+scsi_ua_get(struct scsi_device *sd, char *ua, int len)
+{
+	char		*eua;
+
+	/* See if we already have established the unit-address. */
+	if ((eua = scsi_device_unit_address(sd)) != NULL) {
+		(void) strlcpy(ua, eua, len);
+		return (1);
+	}
+
+	/* Use deprecated tran_get_name interface if it is defined. */
+	/* NOTE: tran_get_name is a poor name choice for interface */
+	if (DEVP_TO_TRAN(sd)->tran_get_name)
+		return ((*DEVP_TO_TRAN(sd)->tran_get_name)(sd, ua, len));
+
+	/* Use generic property implementation */
+	return (scsi_hba_ua_get(sd, ua, len));
+}
+
+/*
+ * scsi_hba_ua_get: using properties, return "unit-address" string.
+ * This function may be called from an HBAs tran function.
  *
  * Function to get "unit-address" in "name@unit-address" /devices path
- * 'name' form from the unit-address properties on a node.
+ * component form from the scsi_device unit-address properties on a node.
  *
- * NOTE: a better name for this function would be scsi_get_unit_address.
+ * NOTE: This function works in conjunction with scsi_hba_ua_set().
  */
 int
-scsi_get_name(struct scsi_device *devp, char *ua, int len)
+scsi_hba_ua_get(struct scsi_device *sd, char *ua, int len)
 {
-	struct scsi_address	*ap;
+	int		tgt, lun, sfunc;
+	char		*tgt_port;
+	scsi_lun64_t	lun64;
+
+	/* get device unit-address properties */
+	tgt = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_TARGET, -1);
+	if (scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_PROP_SUCCESS)
+		tgt_port = NULL;
+	if ((tgt == -1) && (tgt_port == NULL))
+		return (0);		/* no target */
 
-	/* use tran_get_name interface if it is defined */
-	if (DEVP_TO_TRAN(devp)->tran_get_name)
-		return ((*DEVP_TO_TRAN(devp)->tran_get_name)
-		    (devp, ua, len));
-
-	ap = &devp->sd_address;
-	(void) snprintf(ua, len, "%x,%x", ap->a_target, ap->a_lun);
+	lun = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_LUN, 0);
+	lun64 = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_LUN64, lun);
+	sfunc = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
+	    SCSI_ADDR_PROP_SFUNC, -1);
+	if (tgt_port) {
+		if (sfunc == -1)
+			(void) snprintf(ua, len, "%s,%" PRIx64,
+			    tgt_port, lun64);
+		else
+			(void) snprintf(ua, len, "%s,%" PRIx64 ",%x",
+			    tgt_port, lun64, sfunc);
+		scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
+	} else {
+		if (sfunc == -1)
+			(void) snprintf(ua, len, "%x,%" PRIx64, tgt, lun64);
+		else
+			(void) snprintf(ua, len, "%x,%" PRIx64 ",%x",
+			    tgt, lun64, sfunc);
+	}
 	return (1);
 }
 
 void
-create_inquiry_props(struct scsi_device *devp)
+create_inquiry_props(struct scsi_device *sd)
 {
-	struct scsi_inquiry *inq = devp->sd_inq;
+	struct scsi_inquiry *inq = sd->sd_inq;
 
-	(void) ndi_prop_update_int(DDI_DEV_T_NONE, devp->sd_dev,
+	(void) ndi_prop_update_int(DDI_DEV_T_NONE, sd->sd_dev,
 	    INQUIRY_DEVICE_TYPE, (int)inq->inq_dtype);
 
 	/*
 	 * Create the following properties:
 	 *
-	 * inquiry-vendor-id 	Vendor id (INQUIRY data bytes 8-15)
-	 * inquiry-product-id 	Product id (INQUIRY data bytes 16-31)
-	 * inquiry-revision-id 	Product Rev level (INQUIRY data bytes 32-35)
+	 * inquiry-vendor-id	Vendor id (INQUIRY data bytes 8-15)
+	 * inquiry-product-id	Product id (INQUIRY data bytes 16-31)
+	 * inquiry-revision-id	Product Rev level (INQUIRY data bytes 32-35)
 	 *
 	 * Note we don't support creation of these properties for scsi-1
 	 * devices (as the vid, pid and revision were not defined) and we
@@ -810,21 +945,21 @@
 	 * stripped of Nulls and spaces.
 	 */
 	if (inq->inq_ansi != 1) {
-		if (ddi_prop_exists(DDI_DEV_T_NONE, devp->sd_dev,
+		if (ddi_prop_exists(DDI_DEV_T_NONE, sd->sd_dev,
 		    DDI_PROP_TYPE_STRING, INQUIRY_VENDOR_ID) == 0)
-			(void) scsi_hba_prop_update_inqstring(devp,
+			(void) scsi_device_prop_update_inqstring(sd,
 			    INQUIRY_VENDOR_ID,
 			    inq->inq_vid, sizeof (inq->inq_vid));
 
-		if (ddi_prop_exists(DDI_DEV_T_NONE, devp->sd_dev,
+		if (ddi_prop_exists(DDI_DEV_T_NONE, sd->sd_dev,
 		    DDI_PROP_TYPE_STRING, INQUIRY_PRODUCT_ID) == 0)
-			(void) scsi_hba_prop_update_inqstring(devp,
+			(void) scsi_device_prop_update_inqstring(sd,
 			    INQUIRY_PRODUCT_ID,
 			    inq->inq_pid, sizeof (inq->inq_pid));
 
-		if (ddi_prop_exists(DDI_DEV_T_NONE, devp->sd_dev,
+		if (ddi_prop_exists(DDI_DEV_T_NONE, sd->sd_dev,
 		    DDI_PROP_TYPE_STRING, INQUIRY_REVISION_ID) == 0)
-			(void) scsi_hba_prop_update_inqstring(devp,
+			(void) scsi_device_prop_update_inqstring(sd,
 			    INQUIRY_REVISION_ID,
 			    inq->inq_revision, sizeof (inq->inq_revision));
 	}
@@ -835,7 +970,7 @@
  * treatment to trim trailing blanks (etc) and ensure null termination.
  */
 int
-scsi_hba_prop_update_inqstring(struct scsi_device *devp,
+scsi_device_prop_update_inqstring(struct scsi_device *sd,
     char *name, char *data, size_t len)
 {
 	int	ilen;
@@ -851,7 +986,7 @@
 	data_string = kmem_zalloc(ilen + 1, KM_SLEEP);
 	bcopy(data, data_string, ilen);
 	rv = ndi_prop_update_string(DDI_DEV_T_NONE,
-	    devp->sd_dev, name, data_string);
+	    sd->sd_dev, name, data_string);
 	kmem_free(data_string, ilen + 1);
 	return (rv);
 }
@@ -944,11 +1079,11 @@
  * else return 1, meaning do *NOT* probe this target/LUN
  */
 static int
-scsi_check_ss2_LUN_limit(struct scsi_device *devp)
+scsi_check_ss2_LUN_limit(struct scsi_device *sd)
 {
-	struct scsi_address	*ap = &(devp->sd_address);
+	struct scsi_address	*ap = &(sd->sd_address);
 	dev_info_t		*pdevi =
-	    (dev_info_t *)DEVI(devp->sd_dev)->devi_parent;
+	    (dev_info_t *)DEVI(sd->sd_dev)->devi_parent;
 	int			ret_val = 0;	/* default return value */
 	uchar_t			*tgt_list;
 	uint_t			tgt_nelements;
@@ -1023,11 +1158,11 @@
  * and if it is we mark our parent node with this information
  */
 static void
-scsi_establish_LUN_limit(struct scsi_device *devp)
+scsi_establish_LUN_limit(struct scsi_device *sd)
 {
-	struct scsi_address	*ap = &(devp->sd_address);
-	struct scsi_inquiry	*inq = devp->sd_inq;
-	dev_info_t		*devi = devp->sd_dev;
+	struct scsi_address	*ap = &(sd->sd_address);
+	struct scsi_inquiry	*inq = sd->sd_inq;
+	dev_info_t		*devi = sd->sd_dev;
 	char			*vid = NULL;
 	char			*pid = NULL;
 	char			*rev = NULL;
@@ -1286,3 +1421,246 @@
 	}
 #endif
 }
+
+
+/* XXX BEGIN: find a better place for this: inquiry.h? */
+/*
+ * Definitions used by device id registration routines
+ */
+#define	VPD_HEAD_OFFSET		3	/* size of head for vpd page */
+#define	VPD_PAGE_LENGTH		3	/* offset for pge length data */
+#define	VPD_MODE_PAGE		1	/* offset into vpd pg for "page code" */
+
+/* size for devid inquiries */
+#define	MAX_INQUIRY_SIZE	0xF0
+#define	MAX_INQUIRY_SIZE_EVPD	0xFF	/* XXX why is this longer */
+/* XXX END: find a better place for these */
+
+
+/*
+ * Decorate devinfo node with identity properties using information obtained
+ * from device. These properties are used by device enumeration code to derive
+ * the devid, and guid for the device. These properties are also used to
+ * determine if a device should be enumerated under the physical HBA (PHCI) or
+ * the virtual HBA (VHCI, for mpxio support).
+ *
+ * Return zero on success. If commands that should succeed fail or allocations
+ * fail then return failure (non-zero). It is possible for this function to
+ * return success and not have decorated the node with any additional identity
+ * information if the device correctly responds indicating that they are not
+ * supported.  When failure occurs the caller should consider not making the
+ * device accessible.
+ */
+int
+scsi_device_identity(struct scsi_device *sd, int (*callback)())
+{
+	dev_info_t	*devi		= sd->sd_dev;
+	uchar_t		*inq80		= NULL;
+	uchar_t		*inq83		= NULL;
+	int		rval;
+	size_t		len;
+	int		pg80, pg83;
+
+	/* find out what pages are supported by device */
+	if (check_vpd_page_support8083(sd, callback, &pg80, &pg83) == -1)
+		return (-1);
+
+	/* if available, collect page 80 data and add as property */
+	if (pg80) {
+		inq80 = kmem_zalloc(MAX_INQUIRY_SIZE,
+		    ((callback == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP));
+		if (inq80 == NULL) {
+			rval = -1;
+			goto out;
+		}
+
+		rval = send_scsi_INQUIRY(sd, callback, inq80,
+		    MAX_INQUIRY_SIZE, 0x01, 0x80, &len);
+		if (rval)
+			goto out;		/* should have worked */
+
+		if (len && (ndi_prop_update_byte_array(DDI_DEV_T_NONE, devi,
+		    "inquiry-page-80", inq80, len) != DDI_PROP_SUCCESS)) {
+			cmn_err(CE_WARN, "scsi_device_identity: "
+			    "failed to add page80 prop");
+			rval = -1;
+			goto out;
+		}
+	}
+
+	/* if available, collect page 83 data and add as property */
+	if (pg83) {
+		inq83 = kmem_zalloc(MAX_INQUIRY_SIZE,
+		    ((callback == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP));
+		if (inq83 == NULL) {
+			rval = -1;
+			goto out;
+		}
+
+		rval = send_scsi_INQUIRY(sd, callback, inq83,
+		    MAX_INQUIRY_SIZE, 0x01, 0x83, &len);
+		if (rval)
+			goto out;		/* should have worked */
+
+		if (len && (ndi_prop_update_byte_array(DDI_DEV_T_NONE, devi,
+		    "inquiry-page-83", inq83, len) != DDI_PROP_SUCCESS)) {
+			cmn_err(CE_WARN, "scsi_device_identity: "
+			    "failed to add page83 prop");
+			rval = -1;
+			goto out;
+		}
+	}
+
+	/* Commands worked, identity information that exists has been added. */
+	rval = 0;
+
+	/* clean up resources */
+out:	if (inq80 != NULL)
+		kmem_free(inq80, MAX_INQUIRY_SIZE);
+	if (inq83 != NULL)
+		kmem_free(inq83, MAX_INQUIRY_SIZE);
+
+	return (rval);
+}
+
+/*
+ * Send an INQUIRY command with the EVPD bit set and a page code of 0x00 to
+ * the device, returning zero on success. Returned INQUIRY data is used to
+ * determine which vital product pages are supported. The device idenity
+ * information we are looking for is in pages 0x83 and/or 0x80. If the device
+ * fails the EVPD inquiry then no pages are supported but the call succeeds.
+ * Return -1 (failure) if there were memory allocation failures or if a
+ * command faild that should have worked.
+ */
+static int
+check_vpd_page_support8083(struct scsi_device *sd, int (*callback)(),
+	int *ppg80, int *ppg83)
+{
+	uchar_t *page_list;
+	int	counter;
+	int	rval;
+
+	/* pages are not supported */
+	*ppg80 = 0;
+	*ppg83 = 0;
+
+	/*
+	 * We'll set the page length to the maximum to save figuring it out
+	 * with an additional call.
+	 */
+	page_list =  kmem_zalloc(MAX_INQUIRY_SIZE_EVPD,
+	    ((callback == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP));
+	if (page_list == NULL)
+		return (-1);		/* memory allocation problem */
+
+	/* issue page 0 (Supported VPD Pages) INQUIRY with evpd set */
+	rval = send_scsi_INQUIRY(sd, callback,
+	    page_list, MAX_INQUIRY_SIZE_EVPD, 1, 0, NULL);
+
+	/*
+	 * Now we must validate that the device accepted the command (some
+	 * devices do not support it) and if the idenity pages we are
+	 * interested in are supported.
+	 */
+	if ((rval == 0) &&
+	    (page_list[VPD_MODE_PAGE] == 0x00)) {
+		/* Loop to find one of the 2 pages we need */
+		counter = 4;  /* Supported pages start at byte 4, with 0x00 */
+
+		/*
+		 * Pages are returned in ascending order, and 0x83 is the
+		 * last page we are hoping to find.
+		 */
+		while ((page_list[counter] <= 0x83) &&
+		    (counter <= (page_list[VPD_PAGE_LENGTH] +
+		    VPD_HEAD_OFFSET))) {
+			/*
+			 * Add 3 because page_list[3] is the number of
+			 * pages minus 3
+			 */
+
+			switch (page_list[counter]) {
+			case 0x80:
+				*ppg80 = 1;
+				break;
+			case 0x83:
+				*ppg83 = 1;
+				break;
+			}
+			counter++;
+		}
+	}
+
+	kmem_free(page_list, MAX_INQUIRY_SIZE_EVPD);
+	return (0);
+}
+
+/*
+ * Send INQUIRY command with specified EVPD and page code.  Return
+ * zero on success.  On success, the amount of data transferred
+ * is returned in *lenp.
+ */
+static int
+send_scsi_INQUIRY(struct scsi_device *sd, int (*callback)(),
+    uchar_t *bufaddr, size_t buflen,
+    uchar_t evpd, uchar_t page_code, size_t *lenp)
+{
+	int		(*cb_flag)();
+	struct buf	*inq_bp;
+	struct scsi_pkt *inq_pkt = NULL;
+	int		rval = -1;
+
+	if (lenp)
+		*lenp = 0;
+	if (callback != SLEEP_FUNC && callback != NULL_FUNC)
+		cb_flag = NULL_FUNC;
+	else
+		cb_flag = callback;
+	inq_bp = scsi_alloc_consistent_buf(ROUTE,
+	    (struct buf *)NULL, buflen, B_READ, cb_flag, NULL);
+	if (inq_bp == NULL)
+		goto out;		/* memory allocation problem */
+
+	inq_pkt = scsi_init_pkt(ROUTE, (struct scsi_pkt *)NULL,
+	    inq_bp, CDB_GROUP0, sizeof (struct scsi_arq_status),
+	    0, PKT_CONSISTENT, callback, NULL);
+	if (inq_pkt == NULL)
+		goto out;		/* memory allocation problem */
+
+	ASSERT(inq_bp->b_error == 0);
+
+	/* form INQUIRY cdb with specified EVPD and page code */
+	(void) scsi_setup_cdb((union scsi_cdb *)inq_pkt->pkt_cdbp,
+	    SCMD_INQUIRY, 0, buflen, 0);
+	inq_pkt->pkt_cdbp[1] = evpd;
+	inq_pkt->pkt_cdbp[2] = page_code;
+
+	inq_pkt->pkt_time = SCSI_POLL_TIMEOUT;	/* in seconds */
+	inq_pkt->pkt_flags = FLAG_NOINTR|FLAG_NOPARITY;
+
+	/*
+	 * Issue inquiry command thru scsi_test
+	 *
+	 * NOTE: This is important data about device identity, not sure why
+	 * NOPARITY is used. Also seems like we should check pkt_stat for
+	 * STATE_XFERRED_DATA.
+	 */
+	if ((scsi_test(inq_pkt) == 0) &&
+	    (inq_pkt->pkt_reason == CMD_CMPLT) &&
+	    (((*inq_pkt->pkt_scbp) & STATUS_MASK) == 0)) {
+		ASSERT(inq_pkt->pkt_resid >= 0);
+		ASSERT(inq_pkt->pkt_resid <= buflen);
+
+		bcopy(inq_bp->b_un.b_addr,
+		    bufaddr, buflen - inq_pkt->pkt_resid);
+		if (lenp)
+			*lenp = (buflen - inq_pkt->pkt_resid);
+		rval = 0;
+	}
+
+out:	if (inq_pkt)
+		scsi_destroy_pkt(inq_pkt);
+	if (inq_bp)
+		scsi_free_consistent_buf(inq_bp);
+	return (rval);
+}
--- a/usr/src/uts/common/io/scsi/impl/sas_transport.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/impl/sas_transport.c	Mon Oct 05 09:17:26 2009 -0400
@@ -55,23 +55,24 @@
 sas_hba_probe_smp(struct smp_device *smp_devp)
 {
 	smp_pkt_t smp_pkt_data, *smp_pkt = &smp_pkt_data;
-	struct smp_report_general_req smp_req;
-	struct smp_report_general_rsp smp_rsp;
+	uint8_t reqbuf[SMP_REQ_MINLEN];
+	uint8_t respbuf[SMP_RESP_MINLEN + sizeof (smp_report_general_resp_t)];
+	smp_request_frame_t *qfp = (smp_request_frame_t *)reqbuf;
+	smp_report_general_resp_t *sfp = (smp_report_general_resp_t *)respbuf;
 	int rval = DDI_PROBE_SUCCESS;
 
-	bzero(&smp_req, sizeof (struct smp_report_general_req));
-	bzero(&smp_rsp, sizeof (struct smp_report_general_rsp));
-	smp_req.frametype = (uint8_t)SMP_FRAME_TYPE_REQUEST;
-	smp_req.function = (uint8_t)SMP_REPORT_GENERAL;
-	smp_req.reqsize = 0x00;
+	bzero(reqbuf, sizeof (reqbuf));
+	bzero(respbuf, sizeof (respbuf));
+	qfp->srf_frame_type = SMP_FRAME_TYPE_REQUEST;
+	qfp->srf_function = SMP_FUNC_REPORT_GENERAL;
 
 	bzero(smp_pkt, sizeof (struct smp_pkt));
 	smp_pkt->pkt_address = &smp_devp->smp_addr;
 	smp_pkt->pkt_reason = 0;
-	smp_pkt->pkt_req = (caddr_t)&smp_req;
-	smp_pkt->pkt_reqsize = sizeof (struct smp_report_general_req);
-	smp_pkt->pkt_rsp = (caddr_t)&smp_rsp;
-	smp_pkt->pkt_rspsize = sizeof (struct smp_report_general_rsp);
+	smp_pkt->pkt_req = (caddr_t)qfp;
+	smp_pkt->pkt_reqsize = sizeof (reqbuf);
+	smp_pkt->pkt_rsp = (caddr_t)sfp;
+	smp_pkt->pkt_rspsize = sizeof (respbuf);
 	smp_pkt->pkt_timeout = SMP_DEFAULT_TIMEOUT;
 
 	if (sas_smp_transport(smp_pkt) != DDI_SUCCESS) {
--- a/usr/src/uts/common/io/scsi/impl/scsi_control.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/impl/scsi_control.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 1996-2002 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Generic Abort, Reset and Misc Routines
  */
@@ -50,6 +47,9 @@
 	    ((*A_TO_TRAN(ap)->tran_getcap)(ap, "lun-reset", 1) != 1)) {
 		return (0);
 	}
+	if ((A_TO_TRAN(ap)->tran_reset) == NULL) {
+		return (0);
+	}
 	return (*A_TO_TRAN(ap)->tran_reset)(ap, level);
 }
 
--- a/usr/src/uts/common/io/scsi/impl/scsi_hba.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/impl/scsi_hba.c	Mon Oct 05 09:17:26 2009 -0400
@@ -29,19 +29,28 @@
  * Generic SCSI Host Bus Adapter interface implementation
  */
 #include <sys/scsi/scsi.h>
+#include <sys/scsi/generic/sas.h>
 #include <sys/file.h>
+#include <sys/disp.h>			/* for minclsyspri */
 #include <sys/ddi_impldefs.h>
 #include <sys/ndi_impldefs.h>
+#include <sys/sunndi.h>
 #include <sys/ddi.h>
 #include <sys/sunmdi.h>
+#include <sys/mdi_impldefs.h>
+#include <sys/callb.h>
 #include <sys/epm.h>
+#include <sys/damap.h>
+#include <sys/time.h>
+#include <sys/sunldi.h>
 
 extern struct scsi_pkt *scsi_init_cache_pkt(struct scsi_address *,
 		    struct scsi_pkt *, struct buf *, int, int, int, int,
 		    int (*)(caddr_t), caddr_t);
-extern void scsi_free_cache_pkt(struct scsi_address *, struct scsi_pkt *);
-extern void scsi_cache_dmafree(struct scsi_address *, struct scsi_pkt *);
-extern void scsi_sync_cache_pkt(struct scsi_address *, struct scsi_pkt *);
+extern void	scsi_free_cache_pkt(struct scsi_address *, struct scsi_pkt *);
+extern void	scsi_cache_dmafree(struct scsi_address *, struct scsi_pkt *);
+extern void	scsi_sync_cache_pkt(struct scsi_address *, struct scsi_pkt *);
+extern int	modrootloaded;
 
 /*
  * Round up all allocations so that we can guarantee
@@ -57,9 +66,112 @@
 kcondvar_t	scsi_flag_nointr_cv;
 kmutex_t	scsi_log_mutex;
 
+/* asynchronous probe barrier deletion data structures */
+static kmutex_t	scsi_hba_barrier_mutex;
+static kcondvar_t	scsi_hba_barrier_cv;
+static struct scsi_hba_barrier {
+	struct scsi_hba_barrier	*barrier_next;
+	clock_t			barrier_endtime;
+	dev_info_t		*barrier_probe;
+}		*scsi_hba_barrier_list;
+static int	scsi_hba_devi_is_barrier(dev_info_t *probe);
+static void	scsi_hba_barrier_tran_tgt_free(dev_info_t *probe);
+static void	scsi_hba_barrier_add(dev_info_t *probe, int seconds);
+static int	scsi_hba_remove_node(dev_info_t *child);
+static void	scsi_hba_barrier_daemon(void *arg);
+
+/* LUN-change ASC/ASCQ processing data structures (stage1 and stage2) */
+static kmutex_t		scsi_lunchg1_mutex;
+static kcondvar_t	scsi_lunchg1_cv;
+static struct scsi_pkt	*scsi_lunchg1_list;
+static void		scsi_lunchg1_daemon(void *arg);
+static kmutex_t		scsi_lunchg2_mutex;
+static kcondvar_t	scsi_lunchg2_cv;
+static struct scsi_lunchg2 {
+	struct scsi_lunchg2	*lunchg2_next;
+	char			*lunchg2_path;
+}			*scsi_lunchg2_list;
+static void		scsi_lunchg2_daemon(void *arg);
+
+static int	scsi_hba_find_child(dev_info_t *self, char *name, char *addr,
+    int init, dev_info_t **dchildp, mdi_pathinfo_t **pchildp, int *ppi);
+
+/* return value defines for scsi_hba_find_child */
+#define	CHILD_TYPE_NONE		0
+#define	CHILD_TYPE_DEVINFO	1
+#define	CHILD_TYPE_PATHINFO	2
+
 /*
- * Prototypes for static functions
+ * Enumeration code path currently being followed. SE_BUSCONFIG results in
+ * DEVI_SID_NODEID, and SE_HP (hotplug) results in DEVI_SID_HP_NODEID.
+ *
+ * Since hotplug enumeration is based on information obtained from hardware
+ * (tgtmap/report_lun) the type/severity of enumeration error messages is
+ * sometimes based SE_HP (indirectly via ndi_dev_is_hotplug_node()). By
+ * convention, these messages all contain "enumeration failed during ...".
+ */
+typedef enum { SE_BUSCONFIG = 0, SE_HP = 1 } scsi_enum_t;
+
+/* compatible properties of driver to use during probe/enumeration operations */
+static char	*compatible_probe = "scsa,probe";
+static char	*compatible_nodev = "scsa,nodev";
+static char	*scsi_probe_ascii[] = SCSIPROBE_ASCII;
+
+/* number of LUNs we attempt to get on the first SCMD_REPORT_LUNS command */
+int	scsi_lunrpt_default_max = 256;
+int	scsi_lunrpt_timeout = 3;	/* seconds */
+
+/* 'scsi-binding-set' value for legacy enumerated 'spi' transports */
+char	*scsi_binding_set_spi = "spi";
+
+/* enable NDI_DEVI_DEBUG for bus_[un]config operations */
+int	scsi_hba_busconfig_debug = 0;
+
+/* number of probe serilization messages */
+int	scsi_hba_wait_msg = 5;
+
+/*
+ * Establish the timeout used to cache (in the probe node) the fact that the
+ * device does not exist. This replaces the target specific probe cache.
  */
+int	scsi_hba_barrier_timeout = (60);		/* seconds */
+
+/*
+ * Structure for scsi_hba_tgtmap_* implementation.
+ *
+ * Every call to scsi_hba_tgtmap_set_begin will increment tgtmap_reports,
+ * and a call to scsi_hba_tgtmap_set_end will reset tgtmap_reports to zero.
+ * If, in scsi_hba_tgtmap_set_begin, we detect a tgtmap_reports value of
+ * scsi_hba_tgtmap_reports_max we produce a message to indicate that
+ * the caller is never completing an observation (i.e. we are not making
+ * any forward progress). If this message occurs, it indicates that the
+ * solaris hotplug ramifications at the target and lun level are no longer
+ * tracking.
+ *
+ * NOTE: LUNMAPSIZE OK for now, but should be dynamic in reportlun code.
+ */
+typedef struct impl_scsi_tgtmap {
+	scsi_hba_tran_t *tgtmap_tran;
+	int		tgtmap_reports;		/* _begin without _end */
+	int		tgtmap_noisy;
+	scsi_tgt_activate_cb_t tgtmap_activate_cb;
+	scsi_tgt_deactivate_cb_t tgtmap_deactivate_cb;
+	void		*tgtmap_mappriv;
+	damap_t		*tgtmap_dam[SCSI_TGT_NTYPES];
+} impl_scsi_tgtmap_t;
+#define	LUNMAPSIZE 256		/* 256 LUNs/target */
+
+/* Produce warning if number of begins without an end exceed this value */
+int	scsi_hba_tgtmap_reports_max = 256;
+
+/* Prototype for static dev_ops devo_*() functions */
+static int	scsi_hba_info(
+			dev_info_t		*self,
+			ddi_info_cmd_t		infocmd,
+			void			*arg,
+			void			**result);
+
+/* Prototypes for static bus_ops bus_*() functions */
 static int	scsi_hba_bus_ctl(
 			dev_info_t		*self,
 			dev_info_t		*child,
@@ -106,12 +218,6 @@
 			ddi_eventcookie_t	event,
 			void			*bus_impldata);
 
-static int	scsi_hba_info(
-			dev_info_t		*self,
-			ddi_info_cmd_t		infocmd,
-			void			*arg,
-			void			**result);
-
 static int	scsi_hba_bus_config(
 			dev_info_t		*self,
 			uint_t			flags,
@@ -138,7 +244,7 @@
 			void			*arg,
 			void			*result);
 
-/* busops vector for SCSI HBA's. */
+/* bus_ops vector for SCSI HBA's. */
 static struct bus_ops scsi_hba_busops = {
 	BUSO_REV,
 	nullbusmap,			/* bus_map */
@@ -192,10 +298,47 @@
 	nodev			/* int (*cb_awrite)() */
 };
 
+/* Prototypes for static scsi_hba.c/SCSA private lunmap interfaces */
+static int	scsi_lunmap_create(
+			dev_info_t		*self,
+			impl_scsi_tgtmap_t	*tgtmap,
+			char			*tgt_addr);
+static void	scsi_lunmap_destroy(
+			dev_info_t		*self,
+			impl_scsi_tgtmap_t	*tgtmap,
+			char			*tgt_addr);
+static void	scsi_lunmap_set_begin(
+			dev_info_t		*self,
+			damap_t			*lundam);
+static int	scsi_lunmap_set_add(
+			dev_info_t		*self,
+			damap_t			*lundam,
+			char			*taddr,
+			scsi_lun64_t		lun_num,
+			int			lun_sfunc);
+static void	scsi_lunmap_set_end(
+			dev_info_t		*self,
+			damap_t			*lundam);
+
+/* Prototypes for static misc. scsi_hba.c private bus_config interfaces */
+static int scsi_hba_bus_config_iports(dev_info_t *self, uint_t flags,
+    ddi_bus_config_op_t op, void *arg, dev_info_t **childp);
+static int scsi_hba_bus_config_spi(dev_info_t *self, uint_t flags,
+    ddi_bus_config_op_t op, void *arg, dev_info_t **childp);
+static dev_info_t *scsi_hba_bus_config_port(dev_info_t *self,
+    char *nameaddr, scsi_enum_t se);
+
+#ifdef	sparc
+static int scsi_hba_bus_config_prom_node(dev_info_t *self, uint_t flags,
+    void *arg, dev_info_t **childp);
+#endif	/* sparc */
+
+
 /*
  * SCSI_HBA_LOG is used for all messages. A logging level is specified when
  * generating a message. Some levels correspond directly to cmn_err levels,
- * the others are associated with increasing levels diagnostic/debug output.
+ * some are associated with increasing levels diagnostic/debug output (LOG1-4),
+ * and others are associated with specific levels of interface (LOGMAP).
  * For _LOG() messages, a __func__ prefix will identify the function origin
  * of the message. For _LOG_NF messages, there is no function prefix or
  * self/child context. Filtering of messages is provided based on logging
@@ -216,7 +359,13 @@
 #define	SCSI_HBA_LOG2		0x00000020	/* DIAG2 level enable */
 #define	SCSI_HBA_LOG3		0x00000040	/* DIAG3 level enable */
 #define	SCSI_HBA_LOG4		0x00000080	/* DIAG4 level enable */
-#define	SCSI_HBA_LOGTRACE	0x00000100	/* TRACE enable */
+#define	SCSI_HBA_LOGMAPPHY	0x00000100	/* MAPPHY level enable */
+#define	SCSI_HBA_LOGMAPIPT	0x00000200	/* MAPIPT level enable */
+#define	SCSI_HBA_LOGMAPTGT	0x00000400	/* MAPTGT level enable */
+#define	SCSI_HBA_LOGMAPLUN	0x00000800	/* MAPLUN level enable */
+#define	SCSI_HBA_LOGMAPCFG	0x00001000	/* MAPCFG level enable */
+#define	SCSI_HBA_LOGMAPUNCFG	0x00002000	/* MAPUNCFG level enable */
+#define	SCSI_HBA_LOGTRACE	0x00010000	/* TRACE enable */
 #if (CE_CONT | CE_NOTE | CE_WARN | CE_PANIC | CE_IGNORE) > SCSI_HBA_LOG_CE_MASK
 Error, problem with CE_ definitions
 #endif
@@ -226,16 +375,23 @@
  * SCSI_HBA_LOG_CE_MASK level messages or LOG_NF() messages.
  *
  * An example set of /etc/system tunings to simplify debug a SCSA pHCI HBA
- * driver called "pmcs", including "scsi_vhci" operation, might be:
+ * driver called "pmcs", including "scsi_vhci" operation, by capturing
+ * log information in the system log might be:
  *
- * echo "set scsi:scsi_hba_log_filter_level=0xf0"		>> /etc/system
+ * echo "set scsi:scsi_hba_log_filter_level=0x3ff0"		>> /etc/system
  * echo "set scsi:scsi_hba_log_filter_phci=\"pmcs\""		>> /etc/system
  * echo "set scsi:scsi_hba_log_filter_vhci=\"scsi_vhci\""	>> /etc/system
+ *
+ * To capture information on just HBA-SCSAv3 *map operation, use
+ * echo "set scsi:scsi_hba_log_filter_level=0x3f10"		>> /etc/system
+ *
+ * For debugging an HBA driver, you may also want to set:
+ *
  * echo "set scsi:scsi_hba_log_align=1"				>> /etc/system
- * echo "set scsi:scsi_hba_log_fcif=0x21"			>> /etc/system
  * echo "set scsi:scsi_hba_log_mt_disable=0x6"			>> /etc/system
  * echo "set mtc_off=1"						>> /etc/system
  * echo "set mdi_mtc_off=1"					>> /etc/system
+ * echo "set scsi:scsi_hba_log_fcif=0"				>> /etc/system
  */
 int		scsi_hba_log_filter_level =
 			SCSI_HBA_LOG1 |
@@ -243,9 +399,12 @@
 char		*scsi_hba_log_filter_phci = "\0\0\0\0\0\0\0\0\0\0\0\0";
 char		*scsi_hba_log_filter_vhci = "\0\0\0\0\0\0\0\0\0\0\0\0";
 int		scsi_hba_log_align = 0;	/* NOTE: will not cause truncation */
-int		scsi_hba_log_fcif = '\0';	/* "^!?" first char in format */
-						/* ^==0x5e, !==0x21, ?==0x3F */
-						/* See cmn_err(9F) */
+int		scsi_hba_log_fcif = '!'; /* "^!?" first char in format */
+					/* NOTE: iff level > SCSI_HBA_LOG1 */
+					/* '\0'0x00 -> console and system log */
+					/* '^' 0x5e -> console_only */
+					/* '!' 0x21 -> system log only */
+					/* '?' 0x2F -> See cmn_err(9F) */
 int		scsi_hba_log_info =	/* augmentation: extra info output */
 			(0 << 0) |	/* 0x0001: process information */
 			(0 << 1) |	/* 0x0002: full /devices path */
@@ -265,11 +424,18 @@
 /* Macros to use in scsi_hba.c source code below */
 #define	SCSI_HBA_LOG(x)	scsi_hba_log x
 #define	_LOG(level)	SCSI_HBA_LOG##level, __func__
+#define	_MAP(map)	SCSI_HBA_LOGMAP##map, __func__
 #define	_LOG_NF(level)	SCSI_HBA_LOG##level, NULL, NULL, NULL
 #define	_LOG_TRACE	_LOG(TRACE)
+#define	_LOGLUN		_MAP(LUN)
+#define	_LOGTGT		_MAP(TGT)
+#define	_LOGIPT		_MAP(IPT)
+#define	_LOGPHY		_MAP(PHY)
+#define	_LOGCFG		_MAP(CFG)
+#define	_LOGUNCFG	_MAP(UNCFG)
 
 /*PRINTFLIKE5*/
-void
+static void
 scsi_hba_log(int level, const char *func, dev_info_t *self, dev_info_t *child,
     const char *fmt, ...)
 {
@@ -383,7 +549,7 @@
 	f = scsi_hba_fmt;
 	if (fmt[0] && strchr("^!?", fmt[0]))
 		*f++ = fmt[0];
-	else if (scsi_hba_log_fcif)
+	else if (scsi_hba_log_fcif && (level > SCSI_HBA_LOG1))
 		*f++ = (char)scsi_hba_log_fcif;		/* add global fcif */
 	if (align)
 		(void) sprintf(f, "%s", "%-18.18s: %36.36s: %s%s");
@@ -400,6 +566,61 @@
 }
 
 /*
+ * scsi_hba version of [nm]di_devi_enter/[nm]di_devi_exit that detects if HBA
+ * is a PHCI, and chooses mdi/ndi locking implementation.
+ */
+static void
+scsi_hba_devi_enter(dev_info_t *self, int *circp)
+{
+	if (MDI_PHCI(self))
+		mdi_devi_enter(self, circp);
+	else
+		ndi_devi_enter(self, circp);
+}
+
+static int
+scsi_hba_devi_tryenter(dev_info_t *self, int *circp)
+{
+	if (MDI_PHCI(self))
+		return (mdi_devi_tryenter(self, circp));
+	else
+		return (ndi_devi_tryenter(self, circp));
+}
+
+static void
+scsi_hba_devi_exit(dev_info_t *self, int circ)
+{
+	if (MDI_PHCI(self))
+		mdi_devi_exit(self, circ);
+	else
+		ndi_devi_exit(self, circ);
+}
+
+static void
+scsi_hba_devi_enter_phci(dev_info_t *self, int *circp)
+{
+	if (MDI_PHCI(self))
+		mdi_devi_enter_phci(self, circp);
+}
+
+static void
+scsi_hba_devi_exit_phci(dev_info_t *self, int circ)
+{
+	if (MDI_PHCI(self))
+		mdi_devi_exit_phci(self, circ);
+}
+
+static int
+scsi_hba_dev_is_sid(dev_info_t *child)
+{
+	/*
+	 * Use ndi_dev_is_persistent_node instead of ddi_dev_is_sid to avoid
+	 * any possible locking issues in mixed nexus devctl code (like usb).
+	 */
+	return (ndi_dev_is_persistent_node(child));
+}
+
+/*
  * Called from _init() when loading "scsi" module
  */
 void
@@ -407,10 +628,61 @@
 {
 	SCSI_HBA_LOG((_LOG_TRACE, NULL, NULL, __func__));
 
+	/* We need "scsiprobe" and "scsinodev" as an alias or a driver. */
+	if (ddi_name_to_major(compatible_probe) == DDI_MAJOR_T_NONE) {
+		SCSI_HBA_LOG((_LOG_NF(WARN), "failed to resolve '%s' "
+		    "driver alias, defaulting to 'nulldriver'",
+		    compatible_probe));
+
+		/* If no "nulldriver" driver nothing will work... */
+		compatible_probe = "nulldriver";
+		if (ddi_name_to_major(compatible_probe) == DDI_MAJOR_T_NONE)
+			SCSI_HBA_LOG((_LOG_NF(WARN), "no probe '%s' driver, "
+			    "system misconfigured", compatible_probe));
+	}
+	if (ddi_name_to_major(compatible_nodev) == DDI_MAJOR_T_NONE) {
+		SCSI_HBA_LOG((_LOG_NF(WARN), "failed to resolve '%s' "
+		    "driver alias, defaulting to 'nulldriver'",
+		    compatible_nodev));
+
+		/* If no "nulldriver" driver nothing will work... */
+		compatible_nodev = "nulldriver";
+		if (ddi_name_to_major(compatible_nodev) == DDI_MAJOR_T_NONE)
+			SCSI_HBA_LOG((_LOG_NF(WARN), "no nodev '%s' driver, "
+			    "system misconfigured", compatible_nodev));
+	}
+
+	/*
+	 * Verify our special node name "probe" will not be used in other ways.
+	 * Don't expect things to work if they are.
+	 */
+	if (ddi_major_to_name(ddi_name_to_major("probe")))
+		SCSI_HBA_LOG((_LOG_NF(WARN),
+		    "driver already using special node name 'probe'"));
+
 	mutex_init(&scsi_log_mutex, NULL, MUTEX_DRIVER, NULL);
 	mutex_init(&scsi_flag_nointr_mutex, NULL, MUTEX_DRIVER, NULL);
 	cv_init(&scsi_flag_nointr_cv, NULL, CV_DRIVER, NULL);
 	mutex_init(&scsi_hba_log_mutex, NULL, MUTEX_DRIVER, NULL);
+
+	/* initialize the asynchronous barrier deletion daemon */
+	mutex_init(&scsi_hba_barrier_mutex, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&scsi_hba_barrier_cv, NULL, CV_DRIVER, NULL);
+	(void) thread_create(NULL, 0,
+	    (void (*)())scsi_hba_barrier_daemon, NULL,
+	    0, &p0, TS_RUN, minclsyspri);
+
+	/* initialize lun change ASC/ASCQ processing daemon (stage1 & stage2) */
+	mutex_init(&scsi_lunchg1_mutex, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&scsi_lunchg1_cv, NULL, CV_DRIVER, NULL);
+	(void) thread_create(NULL, 0,
+	    (void (*)())scsi_lunchg1_daemon, NULL,
+	    0, &p0, TS_RUN, minclsyspri);
+	mutex_init(&scsi_lunchg2_mutex, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&scsi_lunchg2_cv, NULL, CV_DRIVER, NULL);
+	(void) thread_create(NULL, 0,
+	    (void (*)())scsi_lunchg2_daemon, NULL,
+	    0, &p0, TS_RUN, minclsyspri);
 }
 
 int
@@ -451,8 +723,7 @@
 	 */
 	pktw->pcw_granular = tran->tran_dma_attr.dma_attr_granular;
 
-	if (ddi_dma_alloc_handle(tran->tran_hba_dip,
-	    &tran->tran_dma_attr,
+	if (ddi_dma_alloc_handle(tran->tran_hba_dip, &tran->tran_dma_attr,
 	    kmflag == KM_SLEEP ? SLEEP_FUNC: NULL_FUNC, NULL,
 	    &pkt->pkt_handle) != DDI_SUCCESS) {
 
@@ -543,9 +814,9 @@
 }
 
 /*
- * Called by an HBA attach(9E) to allocate a scsi_hba_tran structure. An HBA
- * driver will then initialize the structure and then call
- * scsi_hba_attach_setup.
+ * Called by an HBA attach(9E) to allocate a scsi_hba_tran(9S) structure. An
+ * HBA driver will then initialize the structure and then call
+ * scsi_hba_attach_setup(9F).
  */
 /*ARGSUSED*/
 scsi_hba_tran_t *
@@ -565,6 +836,16 @@
 
 	if (tran) {
 		tran->tran_interconnect_type = INTERCONNECT_PARALLEL;
+
+		/*
+		 * HBA driver called scsi_hba_tran_alloc(), so tran structure
+		 * is proper size and unused/newer fields are zero.
+		 *
+		 * NOTE: We use SCSA_HBA_SCSA_TA as an obtuse form of
+		 * versioning to detect old HBA drivers that do not use
+		 * scsi_hba_tran_alloc, and would present garbage data
+		 * (instead of valid/zero data) for newer tran fields.
+		 */
 		tran->tran_hba_flags |= SCSI_HBA_SCSA_TA;
 	}
 
@@ -613,222 +894,50 @@
 }
 
 /*
- * Return the unit-address of an 'iport' node, or NULL for non-iport node.
+ * Obsolete: Called by an HBA to attach an instance of the driver
+ * Implement this older interface in terms of the new.
  */
-char *
-scsi_hba_iport_unit_address(dev_info_t *self)
-{
-	/*
-	 * NOTE: Since 'self' could be a SCSA iport node or a SCSA HBA node,
-	 * we can't use SCSA flavors: the flavor of a SCSA HBA node is not
-	 * established/owned by SCSA, it is established by the nexus that
-	 * created the SCSA HBA node (PCI) as a child.
-	 *
-	 * NOTE: If we want to support a node_name other than "iport" for
-	 * an iport node then we can add support for a "scsa-iport-node-name"
-	 * property on the SCSA HBA node.  A SCSA HBA driver would set this
-	 * property on the SCSA HBA node prior to using the iport API.
-	 */
-	if (strcmp(ddi_node_name(self), "iport") == 0)
-		return (ddi_get_name_addr(self));
-	else
-		return (NULL);
-}
-
-/*
- * Define a SCSI initiator port (bus/channel) for an HBA card that needs to
- * support multiple SCSI ports, but only has a single HBA devinfo node. This
- * function should be called from the HBA's attach(9E) implementation (when
- * processing the HBA devinfo node attach) after the number of SCSI ports on
- * the card is known or DR handler once DR handler detects a new port added.
- * The function returns 0 on failure and 1 on success.
- *
- * The implementation will add the port value into the "scsi-iports" property
- * value maintained on the HBA node as. These properties are used by the generic
- * scsi bus_config implementation to dynamicaly enumerate the specified iport
- * children. The enumeration code will, on demand, create the appropriate
- * iport children with a "scsi-iport" unit address. This node will bind to the
- * same driver as the HBA node itself. This means that an HBA driver that
- * uses iports should expect probe(9E), attach(9E), and detach(9E) calls on
- * the iport children of the HBA.  If configuration for all ports was already
- * done during HBA node attach, the driver should just return DDI_SUCCESS when
- * confronted with an iport node.
- *
- * A maximum of 32 iport ports are supported per HBA devinfo node.
- *
- * A NULL "port" can be used to indicate that the framework should enumerate
- * target children on the HBA node itself, in addition to enumerating target
- * children on any iport nodes declared. There are two reasons that an HBA may
- * wish to have target children enumerated on both the HBA node and iport
- * node(s):
- *
- *   o  If, in the past, HBA hardware had only a single physical port but now
- *      supports multiple physical ports, the updated driver that supports
- *      multiple physical ports may want to avoid /devices path upgrade issues
- *      by enumerating the first physical port under the HBA instead of as a
- *      iport.
- *
- *   o  Some hardware RAID HBA controllers (mlx, chs, etc) support multiple
- *      SCSI physical ports configured so that various physical devices on
- *      the physical ports are amalgamated into virtual devices on a virtual
- *      port.  Amalgamated physical devices no longer appear to the host OS
- *      on the physical ports, but other non-amalgamated devices may still be
- *      visible on the physical ports.  These drivers use a model where the
- *      physical ports are iport nodes and the HBA node is the virtual port to
- *      the configured virtual devices.
- *
- */
-
+/*ARGSUSED*/
 int
-scsi_hba_iport_register(dev_info_t *self, char *port)
-{
-	unsigned int ports = 0;
-	int rval, i;
-	char **iports, **newiports;
-
-	ASSERT(self);
-	if (self == NULL)
-		return (DDI_FAILURE);
-
-	rval = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, self,
-	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "scsi-iports", &iports,
-	    &ports);
-
-	if (ports == SCSI_HBA_MAX_IPORTS) {
-		ddi_prop_free(iports);
-		return (DDI_FAILURE);
-	}
-
-	if (rval == DDI_PROP_SUCCESS) {
-		for (i = 0; i < ports; i++) {
-			if (strcmp(port, iports[i]) == 0) {
-				ddi_prop_free(iports);
-				return (DDI_SUCCESS);
-			}
-		}
-	}
-
-	newiports = kmem_alloc((sizeof (char *) * (ports + 1)), KM_SLEEP);
-
-	for (i = 0; i < ports; i++) {
-		newiports[i] = strdup(iports[i]);
-	}
-	newiports[ports] = strdup(port);
-	ports++;
-
-	rval = 1;
-
-	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, self,
-	    "scsi-iports", newiports, ports) != DDI_PROP_SUCCESS) {
-		SCSI_HBA_LOG((_LOG(WARN), self, NULL,
-		    "Failed to establish scsi-iport %s", port));
-		rval = DDI_FAILURE;
-	} else {
-		rval = DDI_SUCCESS;
-	}
-
-	/* If there is iport exist, free property */
-	if (ports > 1)
-		ddi_prop_free(iports);
-	for (i = 0; i < ports; i++) {
-		strfree(newiports[i]);
-	}
-	kmem_free(newiports, (sizeof (char *)) * ports);
-
-	return (rval);
-}
-
-/*
- * Check if the HBA is with scsi-iport under it
- */
-int
-scsi_hba_iport_exist(dev_info_t *self)
-{
-	unsigned int ports = 0;
-	char **iports;
-	int rval;
-
-	rval = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, self,
-	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "scsi-iports", &iports,
-	    &ports);
-
-	if (rval != DDI_PROP_SUCCESS)
-		return (0);
-
-	/* If there is now at least 1 iport, then iports is valid */
-	if (ports > 0) {
-		rval = 1;
-	} else
-		rval = 0;
-	ddi_prop_free(iports);
-
-	return (rval);
-}
-
-dev_info_t *
-scsi_hba_iport_find(dev_info_t *self, char *portnm)
-{
-	char		*addr = NULL;
-	char		**iports;
-	unsigned int	num_iports = 0;
-	int		rval = DDI_FAILURE;
-	int		i = 0;
-	dev_info_t	*child = NULL;
-
-	/* check to see if this is an HBA that defined scsi iports */
-	rval = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, self,
-	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "scsi-iports", &iports,
-	    &num_iports);
-
-	if (rval != DDI_SUCCESS) {
-		return (NULL);
-	}
-	ASSERT(num_iports > 0);
-
-	/* check to see if this port was registered */
-	for (i = 0; i < num_iports; i++) {
-		if (strcmp(iports[i], portnm) == 0)
-			break;
-	}
-
-	if (i == num_iports) {
-		child = NULL;
-		goto out;
-	}
-
-	addr = kmem_zalloc(SCSI_MAXNAMELEN, KM_SLEEP);
-	(void) sprintf(addr, "iport@%s", portnm);
-	rval = ndi_devi_config_one(self, addr, &child, NDI_NO_EVENT);
-	kmem_free(addr, SCSI_MAXNAMELEN);
-
-	if (rval != DDI_SUCCESS) {
-		child = NULL;
-	}
-out:
-	ddi_prop_free(iports);
-	return (child);
+scsi_hba_attach(
+	dev_info_t		*self,
+	ddi_dma_lim_t		*hba_lim,
+	scsi_hba_tran_t		*tran,
+	int			flags,
+	void			*hba_options)
+{
+	ddi_dma_attr_t		hba_dma_attr;
+
+	bzero(&hba_dma_attr, sizeof (ddi_dma_attr_t));
+	hba_dma_attr.dma_attr_burstsizes = hba_lim->dlim_burstsizes;
+	hba_dma_attr.dma_attr_minxfer = hba_lim->dlim_minxfer;
+
+	return (scsi_hba_attach_setup(self, &hba_dma_attr, tran, flags));
 }
 
 /*
  * Common nexus teardown code: used by both scsi_hba_detach() on SCSA HBA node
- * and iport_devctl_uninitchild() on a SCSA HBA iport node (and for failure
- * cleanup). Undo scsa_nexus_setup in reverse order.
+ * and iport_postdetach_tran_scsi_device() on a SCSA HBA iport node (and for
+ * failure cleanup). Undo scsa_nexus_setup in reverse order.
+ *
+ * NOTE: Since we are in the Solaris IO framework, we can depend on
+ * undocumented cleanup operations performed by other parts of the framework:
+ * like detach_node() calling ddi_prop_remove_all() and
+ * ddi_remove_minor_node(,NULL).
  */
 static void
 scsa_nexus_teardown(dev_info_t *self, scsi_hba_tran_t	*tran)
 {
-	if ((self == NULL) || (tran == NULL))
-		return;
 	/* Teardown FMA. */
 	if (tran->tran_hba_flags & SCSI_HBA_SCSA_FM) {
 		ddi_fm_fini(self);
-		tran->tran_hba_flags &= SCSI_HBA_SCSA_FM;
+		tran->tran_hba_flags &= ~SCSI_HBA_SCSA_FM;
 	}
 }
 
 /*
  * Common nexus setup code: used by both scsi_hba_attach_setup() on SCSA HBA
- * node and iport_devctl_initchild() on a SCSA HBA iport node.
+ * node and iport_preattach_tran_scsi_device() on a SCSA HBA iport node.
  *
  * This code makes no assumptions about tran use by scsi_device children.
  */
@@ -853,34 +962,35 @@
 		tran->tran_fm_capable = ddi_prop_get_int(DDI_DEV_T_ANY, self,
 		    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
 		    "fm-capable", scsi_fm_capable);
+
 	/*
 	 * If an HBA is *not* doing its own fma support by calling
-	 * ddi_fm_init() prior to scsi_hba_attach_setup(), we provide a
-	 * minimal common SCSA implementation so that scsi_device children
-	 * can generate ereports via scsi_fm_ereport_post().  We use
-	 * ddi_fm_capable() to detect an HBA calling ddi_fm_init() prior to
-	 * scsi_hba_attach_setup().
+	 * ddi_fm_init() prior to scsi_hba_attach_setup(), we provide a minimal
+	 * common SCSA implementation so that scsi_device children can generate
+	 * ereports via scsi_fm_ereport_post().  We use ddi_fm_capable() to
+	 * detect an HBA calling ddi_fm_init() prior to scsi_hba_attach_setup().
 	 */
 	if (tran->tran_fm_capable &&
 	    (ddi_fm_capable(self) == DDI_FM_NOT_CAPABLE)) {
 		/*
-		 * We are capable of something, pass our capabilities up
-		 * the tree, but use a local variable so our parent can't
-		 * limit our capabilities (we don't want our parent to
-		 * clear DDI_FM_EREPORT_CAPABLE).
+		 * We are capable of something, pass our capabilities up the
+		 * tree, but use a local variable so our parent can't limit
+		 * our capabilities (we don't want our parent to clear
+		 * DDI_FM_EREPORT_CAPABLE).
 		 *
-		 * NOTE: iblock cookies are not important because scsi
-		 * HBAs always interrupt below LOCK_LEVEL.
+		 * NOTE: iblock cookies are not important because scsi HBAs
+		 * always interrupt below LOCK_LEVEL.
 		 */
 		capable = tran->tran_fm_capable;
 		ddi_fm_init(self, &capable, NULL);
 
 		/*
-		 * Set SCSI_HBA_SCSA_FM bit to mark us as usiung the
-		 * common minimal SCSA fm implementation -  we called
-		 * ddi_fm_init(), so we are responsible for calling
-		 * ddi_fm_fini() in scsi_hba_detach().
-		 * NOTE: if ddi_fm_init fails in any reason, SKIP.
+		 * Set SCSI_HBA_SCSA_FM bit to mark us as using the common
+		 * minimal SCSA fm implementation -  we called ddi_fm_init(),
+		 * so we are responsible for calling ddi_fm_fini() in
+		 * scsi_hba_detach().
+		 *
+		 * NOTE: if ddi_fm_init fails to establish handle, SKIP cleanup.
 		 */
 		if (DEVI(self)->devi_fmhdl)
 			tran->tran_hba_flags |= SCSI_HBA_SCSA_FM;
@@ -893,40 +1003,53 @@
 	    INST2DEVCTL(ddi_get_instance(self)), DDI_NT_SCSI_NEXUS, 0) !=
 	    DDI_SUCCESS))) {
 		SCSI_HBA_LOG((_LOG(WARN), self, NULL,
-		    "can't create devctl minor nodes"));
-		scsa_nexus_teardown(self, tran);
-		return (DDI_FAILURE);
+		    "can't create :devctl minor node"));
+		goto fail;
 	}
 
 	return (DDI_SUCCESS);
+
+fail:	scsa_nexus_teardown(self, tran);
+	return (DDI_FAILURE);
 }
 
 /*
- * Common tran teardown code: used by iport_devctl_uninitchild() on a SCSA HBA
- * iport node and (possibly) by scsi_hba_detach() on SCSA HBA node (and for
- * failure cleanup). Undo scsa_tran_setup in reverse order.
+ * Common tran teardown code: used by iport_postdetach_tran_scsi_device() on a
+ * SCSA HBA iport node and (possibly) by scsi_hba_detach() on SCSA HBA node
+ * (and for failure cleanup). Undo scsa_tran_setup in reverse order.
+ *
+ * NOTE: Since we are in the Solaris IO framework, we can depend on
+ * undocumented cleanup operations performed by other parts of the framework:
+ * like detach_node() calling ddi_prop_remove_all() and
+ * ddi_remove_minor_node(,NULL).
  */
-/*ARGSUSED*/
 static void
 scsa_tran_teardown(dev_info_t *self, scsi_hba_tran_t *tran)
 {
-	if (tran == NULL)
-		return;
 	tran->tran_iport_dip = NULL;
 
-	/*
-	 * In the future, if PHCI was registered in the SCSA
-	 * scsa_tran_teardown is able to unregiter PHCI
-	 */
-}
-
+	/* Teardown pHCI registration */
+	if (tran->tran_hba_flags & SCSI_HBA_SCSA_PHCI) {
+		(void) mdi_phci_unregister(self, 0);
+		tran->tran_hba_flags &= ~SCSI_HBA_SCSA_PHCI;
+	}
+}
+
+/*
+ * Common tran setup code: used by iport_preattach_tran_scsi_device() on a
+ * SCSA HBA iport node and (possibly) by scsi_hba_attach_setup() on SCSA HBA
+ * node.
+ */
 static int
 scsa_tran_setup(dev_info_t *self, scsi_hba_tran_t *tran)
 {
 	int			scsa_minor;
 	int			id;
+	char			*scsi_binding_set;
 	static const char	*interconnect[] = INTERCONNECT_TYPE_ASCII;
 
+	SCSI_HBA_LOG((_LOG_TRACE, self, NULL, __func__));
+
 	/* If SCSA responsible for for minor nodes, create ":scsi" */
 	scsa_minor = (ddi_get_driver(self)->devo_cb_ops->cb_open ==
 	    scsi_hba_open) ? 1 : 0;
@@ -934,8 +1057,7 @@
 	    INST2SCSI(ddi_get_instance(self)),
 	    DDI_NT_SCSI_ATTACHMENT_POINT, 0) != DDI_SUCCESS)) {
 		SCSI_HBA_LOG((_LOG(WARN), self, NULL,
-		    "can't create scsi minor nodes"));
-		scsa_nexus_teardown(self, tran);
+		    "can't create :scsi minor node"));
 		goto fail;
 	}
 
@@ -943,36 +1065,34 @@
 	 * If the property does not already exist on self then see if we can
 	 * pull it from further up the tree and define it on self. If the
 	 * property does not exist above (including options.conf) then use the
-	 * default value specified (global variable).
-	 */
-#define	CONFIG_INT_PROP(s, p, dv)	{				\
-	if ((ddi_prop_exists(DDI_DEV_T_ANY, s,				\
-	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, p) == 0) &&		\
-	    (ndi_prop_update_int(DDI_DEV_T_NONE, s, p,			\
-	    ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(s),		\
-	    DDI_PROP_NOTPROM, p, dv)) != DDI_PROP_SUCCESS))		\
-		SCSI_HBA_LOG((_LOG(WARN), NULL, s,	\
-		    "can't create property '%s'", p));			\
-	}
-
-	/*
-	 * Attach scsi configuration property parameters not already defined
-	 * via driver.conf to this instance of the HBA using global variable
-	 * value.  Pulling things down from above us to use
-	 * "DDI_PROP_NOTPROM | DDI_PROP_DONTPASS" for faster access.
-	 */
+	 * default value specified (global variable). We pull things down from
+	 * above for faster "DDI_PROP_NOTPROM | DDI_PROP_DONTPASS" runtime
+	 * access.
+	 *
+	 * Future: Should we avoid creating properties when value == global?
+	 */
+#define	CONFIG_INT_PROP(s, p, dv)	{			\
+	if ((ddi_prop_exists(DDI_DEV_T_ANY, s,			\
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, p) == 0) &&	\
+	    (ndi_prop_update_int(DDI_DEV_T_NONE, s, p,		\
+	    ddi_prop_get_int(DDI_DEV_T_ANY, ddi_get_parent(s),	\
+	    DDI_PROP_NOTPROM, p, dv)) != DDI_PROP_SUCCESS))	\
+		SCSI_HBA_LOG((_LOG(WARN), NULL, s,		\
+		    "can't create property '%s'", p));		\
+	}
+
+	/* Decorate with scsi configuration properties */
+	CONFIG_INT_PROP(self, "scsi-enumeration", scsi_enumeration);
 	CONFIG_INT_PROP(self, "scsi-options", scsi_options);
 	CONFIG_INT_PROP(self, "scsi-reset-delay", scsi_reset_delay);
-	CONFIG_INT_PROP(self, "scsi-tag-age-limit", scsi_tag_age_limit);
 	CONFIG_INT_PROP(self, "scsi-watchdog-tick", scsi_watchdog_tick);
 	CONFIG_INT_PROP(self, "scsi-selection-timeout", scsi_selection_timeout);
-
-	/*
-	 * cache the scsi-initiator-id as an property defined further up
-	 * the tree or defined by OBP on the HBA node so can use
-	 * "DDI_PROP_NOTPROM | DDI_PROP_DONTPASS" during enumeration.
-	 * We perform the same type of operation that an HBA driver would
-	 * use to obtain the 'initiator-id' capability.
+	CONFIG_INT_PROP(self, "scsi-tag-age-limit", scsi_tag_age_limit);
+
+	/*
+	 * Pull down the scsi-initiator-id from further up the tree, or as
+	 * defined by OBP. Place on node for faster access. NOTE: there is
+	 * some confusion about what the name of the property should be.
 	 */
 	id = ddi_prop_get_int(DDI_DEV_T_ANY, self, 0, "initiator-id", -1);
 	if (id == -1)
@@ -981,7 +1101,10 @@
 	if (id != -1)
 		CONFIG_INT_PROP(self, "scsi-initiator-id", id);
 
-	/* Establish 'initiator-interconnect-type' */
+	/*
+	 * If we are responsible for tran allocation, establish
+	 * 'initiator-interconnect-type'.
+	 */
 	if ((tran->tran_hba_flags & SCSI_HBA_SCSA_TA) &&
 	    (tran->tran_interconnect_type > 0) &&
 	    (tran->tran_interconnect_type < INTERCONNECT_MAX)) {
@@ -992,46 +1115,66 @@
 			SCSI_HBA_LOG((_LOG(WARN), self, NULL,
 			    "failed to establish "
 			    "'initiator-interconnect-type'"));
-			return (DDI_FAILURE);
-		}
-	}
-
-	tran->tran_iport_dip = self;
-	/*
-	 * In the future SCSA v3, PHCI could be registered in the SCSA
-	 * here.
-	 */
+			goto fail;
+		}
+	}
+
+	/*
+	 * The 'scsi-binding-set' property can be defined in driver.conf
+	 * files of legacy drivers on an as-needed basis. If 'scsi-binding-set'
+	 * is not driver.conf defined, and the HBA is not implementing its own
+	 * private bus_config, we define scsi-binding-set to the default
+	 * 'spi' legacy value.
+	 *
+	 * NOTE: This default 'spi' value will be deleted if an HBA driver
+	 * ends up using the scsi_hba_tgtmap_create() enumeration services.
+	 *
+	 * NOTE: If we were ever to decide to derive 'scsi-binding-set' from
+	 * the IEEE-1275 'device_type' property then this is where that code
+	 * should go - there is not enough consistency in 'device_type' to do
+	 * this correctly at this point in time.
+	 */
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, self,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "scsi-binding-set",
+	    &scsi_binding_set) == DDI_PROP_SUCCESS) {
+		SCSI_HBA_LOG((_LOG(2), NULL, self,
+		    "external 'scsi-binding-set' \"%s\"", scsi_binding_set));
+		ddi_prop_free(scsi_binding_set);
+	} else if (scsi_binding_set_spi &&
+	    ((tran->tran_bus_config == NULL) ||
+	    (tran->tran_bus_config == scsi_hba_bus_config_spi))) {
+		if (ndi_prop_update_string(DDI_DEV_T_NONE, self,
+		    "scsi-binding-set", scsi_binding_set_spi) !=
+		    DDI_PROP_SUCCESS) {
+			SCSI_HBA_LOG((_LOG(WARN), self, NULL,
+			    "failed to establish 'scsi_binding_set' default"));
+			goto fail;
+		}
+		SCSI_HBA_LOG((_LOG(2), NULL, self,
+		    "default 'scsi-binding-set' \"%s\"", scsi_binding_set_spi));
+	} else
+		SCSI_HBA_LOG((_LOG(2), NULL, self,
+		    "no 'scsi-binding-set'"));
+
+	/*
+	 * If SCSI_HBA_TRAN_PHCI is set, take care of pHCI registration of the
+	 * initiator.
+	 */
+	if ((tran->tran_hba_flags & SCSI_HBA_TRAN_PHCI) &&
+	    (mdi_phci_register(MDI_HCI_CLASS_SCSI, self, 0) == MDI_SUCCESS))
+		tran->tran_hba_flags |= SCSI_HBA_SCSA_PHCI;
+
+	/* NOTE: tran_hba_dip is for DMA operation at the HBA node level */
+	tran->tran_iport_dip = self;		/* for iport association */
 	return (DDI_SUCCESS);
-fail:
-	scsa_tran_teardown(self, tran);
+
+fail:	scsa_tran_teardown(self, tran);
 	return (DDI_FAILURE);
 }
 
 /*
- * Obsolete: Called by an HBA to attach an instance of the driver
- * Implement this older interface in terms of the new.
- */
-/*ARGSUSED*/
-int
-scsi_hba_attach(
-	dev_info_t		*self,
-	ddi_dma_lim_t		*hba_lim,
-	scsi_hba_tran_t		*tran,
-	int			flags,
-	void			*hba_options)
-{
-	ddi_dma_attr_t		hba_dma_attr;
-
-	bzero(&hba_dma_attr, sizeof (ddi_dma_attr_t));
-
-	hba_dma_attr.dma_attr_burstsizes = hba_lim->dlim_burstsizes;
-	hba_dma_attr.dma_attr_minxfer = hba_lim->dlim_minxfer;
-
-	return (scsi_hba_attach_setup(self, &hba_dma_attr, tran, flags));
-}
-
-/*
- * Called by an HBA to attach an instance of the driver.
+ * Called by a SCSA HBA driver to attach an instance of the driver to
+ * SCSA HBA node  enumerated by PCI.
  */
 int
 scsi_hba_attach_setup(
@@ -1040,78 +1183,100 @@
 	scsi_hba_tran_t		*tran,
 	int			flags)
 {
+	int			len;
+	char			cache_name[96];
+
 	SCSI_HBA_LOG((_LOG_TRACE, self, NULL, __func__));
 
-	/* Verify that we are a driver */
+	/*
+	 * Verify that we are a driver so other code does not need to
+	 * check for NULL ddi_get_driver() result.
+	 */
 	if (ddi_get_driver(self) == NULL)
 		return (DDI_FAILURE);
 
+	/*
+	 * Verify that we are called on a SCSA HBA node (function enumerated
+	 * by PCI), not on an iport node.
+	 */
 	ASSERT(scsi_hba_iport_unit_address(self) == NULL);
 	if (scsi_hba_iport_unit_address(self))
-		return (DDI_FAILURE);
-
+		return (DDI_FAILURE);		/* self can't be an iport */
+
+	/* Caller must provide the tran. */
 	ASSERT(tran);
 	if (tran == NULL)
 		return (DDI_FAILURE);
 
 	/*
 	 * Verify correct scsi_hba_tran_t form:
-	 *   o	both or none of tran_get_name/tran_get_addr.
-	 */
-	if ((tran->tran_get_name == NULL) ^
-	    (tran->tran_get_bus_addr == NULL)) {
+	 *
+	 * o Both or none of tran_get_name/tran_get_addr.
+	 *   NOTE: Older  SCSA HBA drivers for SCSI transports with addressing
+	 *   that did not fit the SPI "struct scsi_address" model were required
+	 *   to implement tran_get_name and tran_get_addr. This is no longer
+	 *   true - modern transport drivers should now use common SCSA
+	 *   enumeration services.  The SCSA enumeration code will represent
+	 *   the unit-address using well-known address properties
+	 *   (SCSI_ADDR_PROP_TARGET_PORT, SCSI_ADDR_PROP_LUN64) during
+	 *   devinfo/pathinfo node creation. The HBA driver can obtain values
+	 *   using scsi_device_prop_lookup_*() from its tran_tgt_init(9E).
+	 *
+	 */
+	if ((tran->tran_get_name == NULL) ^ (tran->tran_get_bus_addr == NULL)) {
+		SCSI_HBA_LOG((_LOG(WARN), self, NULL,
+		    "should support both or neither: "
+		    "tran_get_name, tran_get_bus_addr"));
 		return (DDI_FAILURE);
 	}
 
 	/*
-	 * Save all the important HBA information that must be accessed
-	 * later by scsi_hba_bus_ctl(), and scsi_hba_map().
-	 */
-	tran->tran_hba_dip = self;
-	tran->tran_hba_flags &= SCSI_HBA_SCSA_TA;
-	tran->tran_hba_flags |= (flags & ~SCSI_HBA_SCSA_TA);
+	 * Establish the devinfo context of this tran structure, preserving
+	 * knowledge of how the tran was allocated.
+	 */
+	tran->tran_hba_dip = self;		/* for DMA */
+	tran->tran_hba_flags = (flags & ~SCSI_HBA_SCSA_TA) |
+	    (tran->tran_hba_flags & SCSI_HBA_SCSA_TA);
 
 	/* Establish flavor of transport (and ddi_get_driver_private()) */
 	ndi_flavorv_set(self, SCSA_FLAVOR_SCSI_DEVICE, tran);
 
 	/*
-	 * Note: we only need dma_attr_minxfer and dma_attr_burstsizes
-	 * from the DMA attributes. scsi_hba_attach(9f) only
-	 * guarantees that these two fields are initialized properly.
-	 * If this changes, be sure to revisit the implementation
-	 * of scsi_hba_attach(9F).
+	 * Note: We only need dma_attr_minxfer and dma_attr_burstsizes
+	 * from the DMA attributes. scsi_hba_attach(9f) only guarantees
+	 * that these two fields are initialized properly. If this
+	 * changes, be sure to revisit the implementation of
+	 * scsi_hba_attach(9F).
 	 */
 	(void) memcpy(&tran->tran_dma_attr, hba_dma_attr,
 	    sizeof (ddi_dma_attr_t));
 
-	/* create kmem_cache, if needed */
+	/* Create tran_setup_pkt(9E) kmem_cache. */
 	if (tran->tran_setup_pkt) {
-		char tmp[96];
-		int hbalen;
-		int cmdlen = 0;
-		int statuslen = 0;
-
 		ASSERT(tran->tran_init_pkt == NULL);
 		ASSERT(tran->tran_destroy_pkt == NULL);
+		if (tran->tran_init_pkt || tran->tran_destroy_pkt)
+			goto fail;
 
 		tran->tran_init_pkt = scsi_init_cache_pkt;
 		tran->tran_destroy_pkt = scsi_free_cache_pkt;
 		tran->tran_sync_pkt = scsi_sync_cache_pkt;
 		tran->tran_dmafree = scsi_cache_dmafree;
 
-		hbalen = ROUNDUP(tran->tran_hba_len);
-		if (flags & SCSI_HBA_TRAN_CDB)
-			cmdlen = ROUNDUP(DEFAULT_CDBLEN);
-		if (flags & SCSI_HBA_TRAN_SCB)
-			statuslen = ROUNDUP(DEFAULT_SCBLEN);
-
-		(void) snprintf(tmp, sizeof (tmp), "pkt_cache_%s_%d",
-		    ddi_driver_name(self), ddi_get_instance(self));
-		tran->tran_pkt_cache_ptr = kmem_cache_create(tmp,
-		    sizeof (struct scsi_pkt_cache_wrapper) +
-		    hbalen + cmdlen + statuslen, 8,
-		    scsi_hba_pkt_constructor, scsi_hba_pkt_destructor,
-		    NULL, tran, NULL, 0);
+		len = sizeof (struct scsi_pkt_cache_wrapper);
+		len += ROUNDUP(tran->tran_hba_len);
+		if (tran->tran_hba_flags & SCSI_HBA_TRAN_CDB)
+			len += ROUNDUP(DEFAULT_CDBLEN);
+		if (tran->tran_hba_flags & SCSI_HBA_TRAN_SCB)
+			len += ROUNDUP(DEFAULT_SCBLEN);
+
+		(void) snprintf(cache_name, sizeof (cache_name),
+		    "pkt_cache_%s_%d", ddi_driver_name(self),
+		    ddi_get_instance(self));
+
+		tran->tran_pkt_cache_ptr = kmem_cache_create(
+		    cache_name, len, 8, scsi_hba_pkt_constructor,
+		    scsi_hba_pkt_destructor, NULL, tran, NULL, 0);
 	}
 
 	/* Perform node setup independent of initiator role */
@@ -1144,25 +1309,24 @@
 
 	return (DDI_SUCCESS);
 
-fail:
-	(void) scsi_hba_detach(self);
+fail:	(void) scsi_hba_detach(self);
 	return (DDI_FAILURE);
 }
 
 /*
- * Called by an HBA to detach an instance of the driver
+ * Called by an HBA to detach an instance of the driver. This may be called
+ * for SCSA HBA nodes and for SCSA iport nodes.
  */
 int
 scsi_hba_detach(dev_info_t *self)
 {
-	scsi_hba_tran_t	*tran;
-
-	SCSI_HBA_LOG((_LOG_TRACE, self, NULL, __func__));
+	scsi_hba_tran_t		*tran;
 
 	ASSERT(scsi_hba_iport_unit_address(self) == NULL);
 	if (scsi_hba_iport_unit_address(self))
-		return (DDI_FAILURE);
-
+		return (DDI_FAILURE);		/* self can't be an iport */
+
+	/* Check all error return conditions upfront */
 	tran = ndi_flavorv_get(self, SCSA_FLAVOR_SCSI_DEVICE);
 	ASSERT(tran);
 	if (tran == NULL)
@@ -1176,26 +1340,23 @@
 		scsa_tran_teardown(self, tran);
 	scsa_nexus_teardown(self, tran);
 
-	/*
-	 * XXX - scsi_transport.h states that these data fields should not be
-	 * referenced by the HBA. However, to be consistent with
-	 * scsi_hba_attach(), they are being reset.
-	 */
-	tran->tran_hba_dip = (dev_info_t *)NULL;
-	tran->tran_hba_flags = 0;
-	(void) memset(&tran->tran_dma_attr, 0, sizeof (ddi_dma_attr_t));
-
-	if (tran->tran_pkt_cache_ptr != NULL) {
+	/* Teardown tran_setup_pkt(9E) kmem_cache. */
+	if (tran->tran_pkt_cache_ptr) {
 		kmem_cache_destroy(tran->tran_pkt_cache_ptr);
 		tran->tran_pkt_cache_ptr = NULL;
 	}
 
+	(void) memset(&tran->tran_dma_attr, 0, sizeof (ddi_dma_attr_t));
+
 	/* Teardown flavor of transport (and ddi_get_driver_private()) */
 	ndi_flavorv_set(self, SCSA_FLAVOR_SCSI_DEVICE, NULL);
 
+	tran->tran_hba_dip = NULL;
+
 	return (DDI_SUCCESS);
 }
 
+
 /*
  * Called by an HBA from _fini()
  */
@@ -1221,11 +1382,8 @@
 /*
  * SAS specific functions
  */
-/*ARGSUSED*/
 sas_hba_tran_t *
-sas_hba_tran_alloc(
-	dev_info_t		*self,
-	int			flags)
+sas_hba_tran_alloc(dev_info_t *self)
 {
 	/* allocate SCSA flavors for self */
 	ndi_flavorv_alloc(self, SCSA_NFLAVORS);
@@ -1233,8 +1391,7 @@
 }
 
 void
-sas_hba_tran_free(
-	sas_hba_tran_t		*tran)
+sas_hba_tran_free(sas_hba_tran_t *tran)
 {
 	kmem_free(tran, sizeof (sas_hba_tran_t));
 }
@@ -1246,7 +1403,8 @@
 {
 	ASSERT(scsi_hba_iport_unit_address(self) == NULL);
 	if (scsi_hba_iport_unit_address(self))
-		return (DDI_FAILURE);
+		return (DDI_FAILURE);		/* self can't be an iport */
+
 	/*
 	 * The owner of the this devinfo_t was responsible
 	 * for informing the framework already about
@@ -1261,7 +1419,7 @@
 {
 	ASSERT(scsi_hba_iport_unit_address(self) == NULL);
 	if (scsi_hba_iport_unit_address(self))
-		return (DDI_FAILURE);
+		return (DDI_FAILURE);		/* self can't be an iport */
 
 	ndi_flavorv_set(self, SCSA_FLAVOR_SMP, NULL);
 	return (DDI_SUCCESS);
@@ -1270,102 +1428,283 @@
 /*
  * SMP child flavored functions
  */
-
 static int
-smp_busctl_reportdev(dev_info_t *child)
-{
-	dev_info_t		*self = ddi_get_parent(child);
-	char			*smp_wwn;
+smp_busctl_ua(dev_info_t *child, char *addr, int maxlen)
+{
+	char		*tport;
+	char		*wwn;
+
+	/* limit ndi_devi_findchild_by_callback to expected flavor */
+	if (ndi_flavor_get(child) != SCSA_FLAVOR_SMP)
+		return (DDI_FAILURE);
 
 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
-	    SMP_WWN, &smp_wwn) != DDI_SUCCESS) {
-		return (DDI_FAILURE);
-	}
-	SCSI_HBA_LOG((_LOG_NF(CONT), "?%s%d at %s%d: wwn %s\n",
-	    ddi_driver_name(child), ddi_get_instance(child),
-	    ddi_driver_name(self), ddi_get_instance(self), smp_wwn));
-	ddi_prop_free(smp_wwn);
-	return (DDI_SUCCESS);
+	    SCSI_ADDR_PROP_TARGET_PORT, &tport) == DDI_SUCCESS) {
+		(void) snprintf(addr, maxlen, "%s", tport);
+		ddi_prop_free(tport);
+		return (DDI_SUCCESS);
+	}
+
+	/*
+	 * NOTE: the following code should be deleted when mpt is changed to
+	 * use SCSI_ADDR_PROP_TARGET_PORT instead of SMP_WWN.
+	 */
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+	    SMP_WWN, &wwn) == DDI_SUCCESS) {
+		(void) snprintf(addr, maxlen, "w%s", wwn);
+		ddi_prop_free(wwn);
+		return (DDI_SUCCESS);
+	}
+	return (DDI_FAILURE);
+}
+
+static int
+smp_busctl_reportdev(dev_info_t *child)
+{
+	dev_info_t	*self = ddi_get_parent(child);
+	char		*tport;
+	char		*wwn;
+
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+	    SCSI_ADDR_PROP_TARGET_PORT, &tport) == DDI_SUCCESS) {
+		SCSI_HBA_LOG((_LOG_NF(CONT), "?%s%d at %s%d: target-port %s",
+		    ddi_driver_name(child), ddi_get_instance(child),
+		    ddi_driver_name(self), ddi_get_instance(self), tport));
+		ddi_prop_free(tport);
+		return (DDI_SUCCESS);
+	}
+
+	/*
+	 * NOTE: the following code should be deleted when mpt is changed to
+	 * use SCSI_ADDR_PROP_TARGET_PORT instead of SMP_WWN.
+	 */
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+	    SMP_WWN, &wwn) == DDI_SUCCESS) {
+		SCSI_HBA_LOG((_LOG_NF(CONT), "?%s%d at %s%d: wwn %s",
+		    ddi_driver_name(child), ddi_get_instance(child),
+		    ddi_driver_name(self), ddi_get_instance(self), wwn));
+		ddi_prop_free(wwn);
+		return (DDI_SUCCESS);
+	}
+	return (DDI_FAILURE);
 }
 
 static int
 smp_busctl_initchild(dev_info_t *child)
 {
 	dev_info_t		*self = ddi_get_parent(child);
-	sas_hba_tran_t		*tran = ndi_flavorv_get(self, SCSA_FLAVOR_SMP);
-	struct smp_device	*smp;
+	sas_hba_tran_t		*tran;
+	dev_info_t		*dup;
 	char			addr[SCSI_MAXNAMELEN];
-	dev_info_t		*ndip;
-	char			*smp_wwn = NULL;
+	struct smp_device	*smp;
 	uint64_t		wwn;
 
+	tran = ndi_flavorv_get(self, SCSA_FLAVOR_SMP);
 	ASSERT(tran);
 	if (tran == NULL)
 		return (DDI_FAILURE);
 
+	if (smp_busctl_ua(child, addr, sizeof (addr)) != DDI_SUCCESS)
+		return (DDI_NOT_WELL_FORMED);
+	if (scsi_wwnstr_to_wwn(addr, &wwn))
+		return (DDI_NOT_WELL_FORMED);
+
+	/* Prevent duplicate nodes.  */
+	dup = ndi_devi_findchild_by_callback(self, ddi_node_name(child), addr,
+	    smp_busctl_ua);
+	if (dup) {
+		ASSERT(ndi_flavor_get(dup) == SCSA_FLAVOR_SMP);
+		if (ndi_flavor_get(dup) != SCSA_FLAVOR_SMP) {
+			SCSI_HBA_LOG((_LOG(1), NULL, child,
+			    "init failed: %s@%s: not SMP flavored",
+			    ddi_node_name(child), addr));
+			return (DDI_FAILURE);
+		}
+		if (dup != child) {
+			SCSI_HBA_LOG((_LOG(4), NULL, child,
+			    "init failed: %s@%s: detected duplicate %p",
+			    ddi_node_name(child), addr, (void *)dup));
+			return (DDI_FAILURE);
+		}
+	}
+
+
+	/* set the node @addr string */
+	ddi_set_name_addr(child, addr);
+
+	/* Allocate and initialize smp_device. */
 	smp = kmem_zalloc(sizeof (struct smp_device), KM_SLEEP);
 	smp->dip = child;
 	smp->smp_addr.a_hba_tran = tran;
-
-	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
-	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
-	    SMP_WWN, &smp_wwn) != DDI_SUCCESS) {
-		goto failure;
-	}
-
-	if (scsi_wwnstr_to_wwn(smp_wwn, &wwn)) {
-		goto failure;
-	}
-
 	bcopy(&wwn, smp->smp_addr.a_wwn, SAS_WWN_BYTE_SIZE);
-	(void) snprintf(addr, SCSI_MAXNAMELEN, "w%s", smp_wwn);
-
-	/* Prevent duplicate nodes.  */
-	ndip = ndi_devi_find(self, ddi_node_name(child), addr);
-	if (ndip && (ndip != child)) {
-		goto failure;
-	}
-
-	ddi_set_name_addr(child, addr);
+
 	ddi_set_driver_private(child, smp);
-	ddi_prop_free(smp_wwn);
+
+	if (tran->tran_smp_init &&
+	    ((*tran->tran_smp_init)(self, child, tran, smp) != DDI_SUCCESS)) {
+		kmem_free(smp, sizeof (struct smp_device));
+		if (ndi_dev_is_hotplug_node(child))
+			SCSI_HBA_LOG((_LOG(WARN), NULL, child,
+			    "enumeration failed during tran_smp_init"));
+		else
+			SCSI_HBA_LOG((_LOG(2), NULL, child,
+			    "enumeration failed during tran_smp_init"));
+		ddi_set_driver_private(child, NULL);
+		ddi_set_name_addr(child, NULL);
+		return (DDI_FAILURE);
+	}
+
 	return (DDI_SUCCESS);
-
-failure:
-	kmem_free(smp, sizeof (struct smp_device));
-	if (smp_wwn) {
-		ddi_prop_free(smp_wwn);
-	}
-	return (DDI_FAILURE);
 }
 
 /*ARGSUSED*/
 static int
 smp_busctl_uninitchild(dev_info_t *child)
 {
+	dev_info_t		*self = ddi_get_parent(child);
 	struct smp_device	*smp = ddi_get_driver_private(child);
-
-	ASSERT(smp);
-	if (smp == NULL)
+	sas_hba_tran_t		*tran;
+
+	tran = ndi_flavorv_get(self, SCSA_FLAVOR_SMP);
+	ASSERT(smp && tran);
+	if ((smp == NULL) || (tran == NULL))
 		return (DDI_FAILURE);
+
+	if (tran->tran_smp_free) {
+		(*tran->tran_smp_free) (self, child, tran, smp);
+	}
 	kmem_free(smp, sizeof (*smp));
 	ddi_set_driver_private(child, NULL);
 	ddi_set_name_addr(child, NULL);
 	return (DDI_SUCCESS);
 }
 
+/* Find an "smp" child at the specified address. */
+static dev_info_t *
+smp_hba_find_child(dev_info_t *self, char *addr)
+{
+	dev_info_t	*child;
+
+	/* Search "smp" devinfo child at specified address. */
+	ASSERT(self && DEVI_BUSY_OWNED(self) && addr);
+	for (child = ddi_get_child(self); child;
+	    child = ddi_get_next_sibling(child)) {
+		/* skip non-"smp" nodes */
+		if (ndi_flavor_get(child) != SCSA_FLAVOR_SMP)
+			continue;
+
+		/* Attempt initchild to establish unit-address */
+		if (i_ddi_node_state(child) < DS_INITIALIZED)
+			(void) ddi_initchild(self, child);
+
+		/* Verify state and non-NULL unit-address. */
+		if ((i_ddi_node_state(child) < DS_INITIALIZED) ||
+		    (ddi_get_name_addr(child) == NULL))
+			continue;
+
+		/* Return "smp" child if unit-address matches. */
+		if (strcmp(ddi_get_name_addr(child), addr) == 0)
+			return (child);
+	}
+	return (NULL);
+}
+
 /*
- * Wrapper to scsi_get_name which takes a devinfo argument instead of a
+ * Search for "smp" child of self at the specified address. If found, online
+ * and return with a hold.  Unlike general SCSI configuration, we can assume
+ * the the device is actually there when we are called (i.e., device is
+ * created by hotplug, not by bus_config).
+ */
+int
+smp_hba_bus_config(dev_info_t *self, char *addr, dev_info_t **childp)
+{
+	dev_info_t	*child;
+	int		circ;
+
+	ASSERT(self && addr && childp);
+	*childp = NULL;
+
+	/* Search for "smp" child. */
+	scsi_hba_devi_enter(self, &circ);
+	if ((child = smp_hba_find_child(self, addr)) == NULL) {
+		scsi_hba_devi_exit(self, circ);
+		return (NDI_FAILURE);
+	}
+
+	/* Attempt online. */
+	if (ndi_devi_online(child, 0) != NDI_SUCCESS) {
+		scsi_hba_devi_exit(self, circ);
+		return (NDI_FAILURE);
+	}
+
+	/* On success, return with active hold. */
+	ndi_hold_devi(child);
+	scsi_hba_devi_exit(self, circ);
+	*childp = child;
+	return (NDI_SUCCESS);
+}
+
+
+
+/* Create "smp" child devinfo node at specified unit-address. */
+int
+smp_hba_bus_config_taddr(dev_info_t *self, char *addr)
+{
+	dev_info_t	*child;
+	int		circ;
+
+	/* Search for "smp" child. */
+	scsi_hba_devi_enter(self, &circ);
+	child = smp_hba_find_child(self, addr);
+	if (child) {
+		/* Child exists, note if this was a new reinsert. */
+		if (ndi_devi_device_insert(child))
+			SCSI_HBA_LOG((_LOGCFG, self, NULL,
+			    "devinfo smp@%s device_reinsert", addr));
+
+		scsi_hba_devi_exit(self, circ);
+		return (NDI_SUCCESS);
+	}
+
+	/* Allocate "smp" child devinfo node and establish flavor of child. */
+	ndi_devi_alloc_sleep(self, "smp", DEVI_SID_HP_NODEID, &child);
+	ndi_flavor_set(child, SCSA_FLAVOR_SMP);
+
+	/* Add unit-address property to child. */
+	if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
+	    SCSI_ADDR_PROP_TARGET_PORT, addr) != DDI_PROP_SUCCESS) {
+		(void) ndi_devi_free(child);
+		scsi_hba_devi_exit(self, circ);
+		return (NDI_FAILURE);
+	}
+
+	/* Attempt to online the new "smp" node. */
+	(void) ndi_devi_online(child, 0);
+
+	scsi_hba_devi_exit(self, circ);
+	return (NDI_SUCCESS);
+}
+
+/*
+ * Wrapper to scsi_ua_get which takes a devinfo argument instead of a
  * scsi_device structure.
  */
 static int
-scsi_hba_name_child(dev_info_t *child, char *addr, int maxlen)
-{
-	struct scsi_device	*sd = ddi_get_driver_private(child);
+scsi_busctl_ua(dev_info_t *child, char *addr, int maxlen)
+{
+	struct scsi_device	*sd;
+
+	/* limit ndi_devi_findchild_by_callback to expected flavor */
+	if (ndi_flavor_get(child) != SCSA_FLAVOR_SCSI_DEVICE)
+		return (DDI_FAILURE);
 
 	/* nodes are named by tran_get_name or default "tgt,lun" */
-	if (sd && (scsi_get_name(sd, addr, maxlen) == 1))
+	sd = ddi_get_driver_private(child);
+	if (sd && (scsi_ua_get(sd, addr, maxlen) == 1))
 		return (DDI_SUCCESS);
 
 	return (DDI_FAILURE);
@@ -1375,20 +1714,21 @@
 scsi_busctl_reportdev(dev_info_t *child)
 {
 	dev_info_t		*self = ddi_get_parent(child);
-	scsi_hba_tran_t		*tran = ddi_get_driver_private(self);
 	struct scsi_device	*sd = ddi_get_driver_private(child);
+	scsi_hba_tran_t		*tran;
 	char			ua[SCSI_MAXNAMELEN];
-	char			ba[SCSI_MAXNAMELEN];
+	char			ra[SCSI_MAXNAMELEN];
 
 	SCSI_HBA_LOG((_LOG_TRACE, NULL, child, __func__));
 
+	tran = ndi_flavorv_get(self, SCSA_FLAVOR_SCSI_DEVICE);
 	ASSERT(tran && sd);
 	if ((tran == NULL) || (sd == NULL))
 		return (DDI_FAILURE);
 
 	/* get the unit_address and bus_addr information */
-	if ((scsi_get_name(sd, ua, sizeof (ua)) == 0) ||
-	    (scsi_get_bus_addr(sd, ba, sizeof (ba)) == 0)) {
+	if ((scsi_ua_get(sd, ua, sizeof (ua)) == 0) ||
+	    (scsi_ua_get_reportdev(sd, ra, sizeof (ra)) == 0)) {
 		SCSI_HBA_LOG((_LOG(WARN), NULL, child, "REPORTDEV failure"));
 		return (DDI_FAILURE);
 	}
@@ -1396,27 +1736,35 @@
 	if (tran->tran_get_name == NULL)
 		SCSI_HBA_LOG((_LOG_NF(CONT), "?%s%d at %s%d: %s",
 		    ddi_driver_name(child), ddi_get_instance(child),
-		    ddi_driver_name(self), ddi_get_instance(self), ba));
-	else
+		    ddi_driver_name(self), ddi_get_instance(self), ra));
+	else if (*ra)
 		SCSI_HBA_LOG((_LOG_NF(CONT),
 		    "?%s%d at %s%d: unit-address %s: %s",
 		    ddi_driver_name(child), ddi_get_instance(child),
-		    ddi_driver_name(self), ddi_get_instance(self), ua, ba));
+		    ddi_driver_name(self), ddi_get_instance(self), ua, ra));
+	else
+		SCSI_HBA_LOG((_LOG_NF(CONT),
+		    "?%s%d at %s%d: unit-address %s",
+		    ddi_driver_name(child), ddi_get_instance(child),
+		    ddi_driver_name(self), ddi_get_instance(self), ua));
+
 	return (DDI_SUCCESS);
 }
 
+
 /*
  * scsi_busctl_initchild is called to initialize the SCSA transport for
  * communication with a particular child scsi target device. Successful
  * initialization requires properties on the node which describe the address
  * of the target device. If the address of the target device can't be
  * determined from properties then DDI_NOT_WELL_FORMED is returned. Nodes that
- * are DDI_NOT_WELL_FORMED are considered an implementation artifact.
+ * are DDI_NOT_WELL_FORMED are considered an implementation artifact and
+ * are hidden from devinfo snapshots by calling ndi_devi_set_hidden().
  * The child may be one of the following types of devinfo nodes:
  *
  * OBP node:
  *	OBP does not enumerate target devices attached a SCSI bus. These
- *	template/stub/wildcard nodes are a legacy artifact for support of old
+ *	template/stub/wild-card nodes are a legacy artifact for support of old
  *	driver loading methods. Since they have no properties,
  *	DDI_NOT_WELL_FORMED will be returned.
  *
@@ -1437,8 +1785,16 @@
  *
  * For HBA drivers that implement the deprecated tran_get_name interface,
  * "@addr" construction involves having that driver interpret properties via
- * scsi_hba_name_child -> scsi_get_name -> tran_get_name: there is no
- * requiremnt for the property names to be well-known.
+ * scsi_busctl_ua -> scsi_ua_get -> tran_get_name: there is no
+ * requirement for the property names to be well-known.
+ *
+ * NOTE: We don't currently support "merge".  When this support is added a
+ * specific property, like "unit-address", should *always* identify a
+ * driver.conf node that needs to be merged into a specific SID node. When
+ * enumeration is enabled, a .conf node without the "unit-address" property
+ * should be ignored.  The best way to establish the "unit-address" property
+ * would be to have the system assign parent= and unit-address= from an
+ * instance=# driver.conf entry (by using the instance tree).
  */
 static int
 scsi_busctl_initchild(dev_info_t *child)
@@ -1448,9 +1804,10 @@
 	scsi_hba_tran_t		*tran;
 	struct scsi_device	*sd;
 	scsi_hba_tran_t		*tran_clone;
-	int			tgt = 0;
-	int			lun = 0;
-	int			sfunc = 0;
+	char			*class;
+	int			tgt;
+	int			lun;
+	int			sfunc;
 	int			err = DDI_FAILURE;
 	char			addr[SCSI_MAXNAMELEN];
 
@@ -1463,19 +1820,19 @@
 	 * and have the load of fcp (which does scsi_hba_attach_setup)
 	 * to fail.  In this case we may get here with a NULL hba.
 	 */
-	tran = ddi_get_driver_private(self);
+	tran = ndi_flavorv_get(self, SCSA_FLAVOR_SCSI_DEVICE);
 	if (tran == NULL)
 		return (DDI_NOT_WELL_FORMED);
 
 	/*
-	 * OBP may create template/stub/wildcard nodes for legacy driver
+	 * OBP may create template/stub/wild-card nodes for legacy driver
 	 * loading methods. These nodes have no properties, so we lack the
 	 * addressing properties to initchild them. Hide the node and return
 	 * DDI_NOT_WELL_FORMED.
 	 *
-	 * XXX need ndi_devi_has_properties(dip) type interface?
+	 * Future: define/use a ndi_devi_has_properties(dip) type interface.
 	 *
-	 * XXX It would be nice if we could delete these ill formed nodes by
+	 * NOTE: It would be nice if we could delete these ill formed nodes by
 	 * implementing a DDI_NOT_WELL_FORMED_DELETE return code. This can't
 	 * be done until leadville debug code removes its dependencies
 	 * on the devinfo still being present after a failed ndi_devi_online.
@@ -1485,14 +1842,11 @@
 	    (DEVI(child)->devi_sys_prop_ptr == NULL)) {
 		SCSI_HBA_LOG((_LOG(4), NULL, child,
 		    "init failed: no properties"));
+		ndi_devi_set_hidden(child);
 		return (DDI_NOT_WELL_FORMED);
 	}
 
 	/* get legacy SPI addressing properties */
-	sfunc = ddi_prop_get_int(DDI_DEV_T_ANY, child,
-	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, SCSI_ADDR_PROP_SFUNC, -1);
-	lun = ddi_prop_get_int(DDI_DEV_T_ANY, child,
-	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, SCSI_ADDR_PROP_LUN, 0);
 	if ((tgt = ddi_prop_get_int(DDI_DEV_T_ANY, child,
 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
 	    SCSI_ADDR_PROP_TARGET, -1)) == -1) {
@@ -1502,14 +1856,20 @@
 		 * even if it is just a dummy that does not contain the real
 		 * target address. However drivers that register devids may
 		 * create stub driver.conf nodes without a target= property so
-		 * that pathological devid resolution works.
+		 * that pathological devid resolution works. Hide the stub
+		 * node and return DDI_NOT_WELL_FORMED.
 		 */
-		if (ndi_dev_is_persistent_node(child) == 0) {
+		if (!scsi_hba_dev_is_sid(child)) {
 			SCSI_HBA_LOG((_LOG(4), NULL, child,
 			    "init failed: stub .conf node"));
+			ndi_devi_set_hidden(child);
 			return (DDI_NOT_WELL_FORMED);
 		}
 	}
+	lun = ddi_prop_get_int(DDI_DEV_T_ANY, child,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, SCSI_ADDR_PROP_LUN, 0);
+	sfunc = ddi_prop_get_int(DDI_DEV_T_ANY, child,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, SCSI_ADDR_PROP_SFUNC, -1);
 
 	/*
 	 * The scsi_address structure may not specify all the addressing
@@ -1521,12 +1881,13 @@
 	    ((tgt >= USHRT_MAX) || (lun >= 256))) {
 		SCSI_HBA_LOG((_LOG(1), NULL, child,
 		    "init failed: illegal/missing properties"));
+		ndi_devi_set_hidden(child);
 		return (DDI_NOT_WELL_FORMED);
 	}
 
 	/*
 	 * We need to initialize a fair amount of our environment to invoke
-	 * tran_get_name (via scsi_hba_name_child and scsi_get_name) to
+	 * tran_get_name (via scsi_busctl_ua and scsi_ua_get) to
 	 * produce the "@addr" name from addressing properties. Allocate and
 	 * initialize scsi device structure.
 	 */
@@ -1534,6 +1895,7 @@
 	mutex_init(&sd->sd_mutex, NULL, MUTEX_DRIVER, NULL);
 	sd->sd_dev = child;
 	sd->sd_pathinfo = NULL;
+	sd->sd_uninit_prevent = 0;
 	ddi_set_driver_private(child, sd);
 
 	if (tran->tran_hba_flags & SCSI_HBA_ADDR_COMPLEX) {
@@ -1541,7 +1903,7 @@
 		 * For a SCSI_HBA_ADDR_COMPLEX transport we store a pointer to
 		 * scsi_device in the scsi_address structure.  This allows an
 		 * HBA driver to find its per-scsi_device private data
-		 * (accessable to the HBA given just the scsi_address by using
+		 * (accessible to the HBA given just the scsi_address by using
 		 *  scsi_address_device(9F)/scsi_device_hba_private_get(9F)).
 		 */
 		sd->sd_address.a.a_sd = sd;
@@ -1562,10 +1924,9 @@
 			sd->sd_address.a_sublun = (uchar_t)sfunc + 1;
 
 		/*
-		 * XXX TODO: apply target/lun limitation logic for SPI
-		 * binding_set. If spi this is based on scsi_options WIDE
-		 * NLUNS some forms of lun limitation are based on the
-		 * device @lun 0
+		 * NOTE: Don't limit LUNs to scsi_options value because a
+		 * scsi_device discovered via SPI dynamic enumeration might
+		 * still support SCMD_REPORT_LUNS.
 		 */
 
 		/*
@@ -1602,9 +1963,25 @@
 	 */
 	sd->sd_tran_safe = tran;
 
+	/*
+	 * If the class property is not already established, set it to "scsi".
+	 * This is done so that parent= driver.conf nodes have class.
+	 */
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "class",
+	    &class) == DDI_PROP_SUCCESS) {
+		ddi_prop_free(class);
+	} else if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
+	    "class", "scsi") != DDI_PROP_SUCCESS) {
+		SCSI_HBA_LOG((_LOG(2), NULL, child, "init failed: class"));
+		ndi_devi_set_hidden(child);
+		err = DDI_NOT_WELL_FORMED;
+		goto failure;
+	}
+
 	/* Establish the @addr name of the child. */
 	*addr = '\0';
-	if (scsi_hba_name_child(child, addr, SCSI_MAXNAMELEN) != DDI_SUCCESS) {
+	if (scsi_busctl_ua(child, addr, sizeof (addr)) != DDI_SUCCESS) {
 		/*
 		 * Some driver.conf files add bogus target properties (relative
 		 * to their nexus representation of target) to their stub
@@ -1612,6 +1989,7 @@
 		 */
 		SCSI_HBA_LOG((_LOG(3), NULL, child,
 		    "init failed: scsi_busctl_ua call"));
+		ndi_devi_set_hidden(child);
 		err = DDI_NOT_WELL_FORMED;
 		goto failure;
 	}
@@ -1622,24 +2000,40 @@
 		goto failure;
 	}
 
+	/* Prevent duplicate nodes.  */
+	dup = ndi_devi_findchild_by_callback(self, ddi_node_name(child), addr,
+	    scsi_busctl_ua);
+	if (dup) {
+		ASSERT(ndi_flavor_get(dup) == SCSA_FLAVOR_SCSI_DEVICE);
+		if (ndi_flavor_get(dup) != SCSA_FLAVOR_SCSI_DEVICE) {
+			SCSI_HBA_LOG((_LOG(1), NULL, child,
+			    "init failed: %s@%s: not SCSI_DEVICE flavored",
+			    ddi_node_name(child), addr));
+			goto failure;
+		}
+		if (dup != child) {
+			SCSI_HBA_LOG((_LOG(4), NULL, child,
+			    "init failed: %s@%s: detected duplicate %p",
+			    ddi_node_name(child), addr, (void *)dup));
+			goto failure;
+		}
+	}
+
 	/* set the node @addr string */
 	ddi_set_name_addr(child, addr);
 
-	/* prevent sibling duplicates */
-	dup = ndi_devi_find(self, ddi_node_name(child), addr);
-	if (dup && (dup != child)) {
-		SCSI_HBA_LOG((_LOG(4), NULL, child,
-		    "init failed: detected duplicate %p", (void *)dup));
-		goto failure;
-	}
-
 	/* call HBA's target init entry point if it exists */
 	if (tran->tran_tgt_init != NULL) {
 		SCSI_HBA_LOG((_LOG(4), NULL, child, "init tran_tgt_init"));
+
 		if ((*tran->tran_tgt_init)
 		    (self, child, tran, sd) != DDI_SUCCESS) {
-			SCSI_HBA_LOG((_LOG(2), NULL, child,
-			    "init failed: tran_tgt_init failed"));
+			if (ndi_dev_is_hotplug_node(child))
+				SCSI_HBA_LOG((_LOG(WARN), NULL, child,
+				    "enumeration failed during tran_tgt_init"));
+			else
+				SCSI_HBA_LOG((_LOG(2), NULL, child,
+				    "enumeration failed during tran_tgt_init"));
 			goto failure;
 		}
 	}
@@ -1662,16 +2056,35 @@
 scsi_busctl_uninitchild(dev_info_t *child)
 {
 	dev_info_t		*self = ddi_get_parent(child);
-	scsi_hba_tran_t		*tran = ddi_get_driver_private(self);
 	struct scsi_device	*sd = ddi_get_driver_private(child);
+	scsi_hba_tran_t		*tran;
 	scsi_hba_tran_t		*tran_clone;
 
 	ASSERT(DEVI_BUSY_OWNED(self));
 
+	tran = ndi_flavorv_get(self, SCSA_FLAVOR_SCSI_DEVICE);
 	ASSERT(tran && sd);
 	if ((tran == NULL) || (sd == NULL))
 		return (DDI_FAILURE);
 
+	/*
+	 * We use sd_uninit_prevent to avoid uninitializing barrier/probe
+	 * nodes that are still in use. Since barrier/probe nodes are not
+	 * attached we can't prevent their state demotion via ndi_hold_devi.
+	 */
+	if (sd->sd_uninit_prevent) {
+		SCSI_HBA_LOG((_LOG(2), NULL, child, "uninit prevented"));
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Don't uninitialize a client node if it still has paths.
+	 */
+	if (MDI_CLIENT(child) && mdi_client_get_path_count(child)) {
+		SCSI_HBA_LOG((_LOG(2), NULL, child,
+		    "uninit prevented, client has paths"));
+		return (DDI_FAILURE);
+	}
 
 	SCSI_HBA_LOG((_LOG(3), NULL, child, "uninit begin"));
 
@@ -1703,19 +2116,36 @@
 	/*
 	 * To simplify host adapter drivers we guarantee that multiple
 	 * tran_tgt_init(9E) calls of the same unit address are never
-	 * active at the same time.
-	 */
-	if (tran->tran_tgt_free)
-		(*tran->tran_tgt_free) (self, child, tran, sd);
+	 * active at the same time.  This requires that we call
+	 * tran_tgt_free on probe/barrier nodes directly prior to
+	 * uninitchild.
+	 *
+	 * NOTE: To correctly support SCSI_HBA_TRAN_CLONE, we must use
+	 * the (possibly cloned) hba_tran pointer from the scsi_device
+	 * instead of hba_tran.
+	 */
+	if (tran->tran_tgt_free) {
+		if (!scsi_hba_devi_is_barrier(child)) {
+			SCSI_HBA_LOG((_LOG(4), NULL, child,
+			    "uninit tran_tgt_free"));
+
+			(*tran->tran_tgt_free) (self, child, tran, sd);
+		} else {
+			SCSI_HBA_LOG((_LOG(4), NULL, child,
+			    "uninit tran_tgt_free already done"));
+		}
+	}
 
 	/*
 	 * If a inquiry data is still allocated (by scsi_probe()) we
 	 * free the allocation here. This keeps scsi_inq valid for the
 	 * same duration as the corresponding inquiry properties. It
 	 * also allows a tran_tgt_init() implementation that establishes
-	 * sd_inq (common/io/dktp/controller/ata/ata_disk.c) to deal
-	 * with deallocation in its tran_tgt_free (setting sd_inq back
-	 * to NULL) without upsetting the framework.
+	 * sd_inq to deal with deallocation in its tran_tgt_free
+	 * (setting sd_inq back to NULL) without upsetting the
+	 * framework. Moving the inquiry free here also allows setting
+	 * of sd_uninit_prevent to preserve the data for lun0 based
+	 * scsi_get_device_type_scsi_options() calls.
 	 */
 	if (sd->sd_inq) {
 		kmem_free(sd->sd_inq, SUN_INQSIZE);
@@ -1734,31 +2164,56 @@
 }
 
 static int
-iport_busctl_reportdev(dev_info_t *child)
-{
-	dev_info_t	*self = ddi_get_parent(child);
-	char		*iport;
-
+iport_busctl_ua(dev_info_t *child, char *addr, int maxlen)
+{
+	char	*iport_ua;
+
+	/* limit ndi_devi_findchild_by_callback to expected flavor */
+	if (ndi_flavor_get(child) != SCSA_FLAVOR_IPORT)
+		return (DDI_FAILURE);
 
 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
-	    "scsi-iport", &iport) != DDI_SUCCESS)
+	    SCSI_ADDR_PROP_IPORTUA, &iport_ua) != DDI_SUCCESS) {
+		return (DDI_FAILURE);
+	}
+
+	(void) snprintf(addr, maxlen, "%s", iport_ua);
+	ddi_prop_free(iport_ua);
+	return (DDI_SUCCESS);
+}
+
+static int
+iport_busctl_reportdev(dev_info_t *child)
+{
+	dev_info_t	*self = ddi_get_parent(child);
+	char		*iport_ua;
+	char		*initiator_port = NULL;
+
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+	    SCSI_ADDR_PROP_IPORTUA, &iport_ua) != DDI_SUCCESS)
 		return (DDI_FAILURE);
 
-	SCSI_HBA_LOG((_LOG_NF(CONT), "?%s%d at %s%d: iport %s\n",
-	    ddi_driver_name(child), ddi_get_instance(child),
-	    ddi_driver_name(self), ddi_get_instance(self),
-	    iport));
-
-	ddi_prop_free(iport);
-	return (DDI_SUCCESS);
-}
-
-/* uninitchild SCSA iport 'child' node */
-static int
-iport_busctl_uninitchild(dev_info_t *child)
-{
-	ddi_set_name_addr(child, NULL);
+	(void) ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
+	    SCSI_ADDR_PROP_INITIATOR_PORT, &initiator_port);
+
+	if (initiator_port) {
+		SCSI_HBA_LOG((_LOG_NF(CONT),
+		    "?%s%d at %s%d: %s %s %s %s",
+		    ddi_driver_name(child), ddi_get_instance(child),
+		    ddi_driver_name(self), ddi_get_instance(self),
+		    SCSI_ADDR_PROP_INITIATOR_PORT, initiator_port,
+		    SCSI_ADDR_PROP_IPORTUA, iport_ua));
+		ddi_prop_free(initiator_port);
+	} else {
+		SCSI_HBA_LOG((_LOG_NF(CONT), "?%s%d at %s%d: %s %s",
+		    ddi_driver_name(child), ddi_get_instance(child),
+		    ddi_driver_name(self), ddi_get_instance(self),
+		    SCSI_ADDR_PROP_IPORTUA, iport_ua));
+	}
+	ddi_prop_free(iport_ua);
 	return (DDI_SUCCESS);
 }
 
@@ -1769,29 +2224,40 @@
 	dev_info_t	*self = ddi_get_parent(child);
 	dev_info_t	*dup = NULL;
 	char		addr[SCSI_MAXNAMELEN];
-	char		*iport;
-
-
-	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
-	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
-	    "scsi-iport", &iport) != DDI_SUCCESS) {
+
+	if (iport_busctl_ua(child, addr, sizeof (addr)) != DDI_SUCCESS)
 		return (DDI_NOT_WELL_FORMED);
-	}
-
-	(void) snprintf(addr, SCSI_MAXNAMELEN, "%s", iport);
-	ddi_prop_free(iport);
-
+
+	/* Prevent duplicate nodes.  */
+	dup = ndi_devi_findchild_by_callback(self, ddi_node_name(child), addr,
+	    iport_busctl_ua);
+	if (dup) {
+		ASSERT(ndi_flavor_get(dup) == SCSA_FLAVOR_IPORT);
+		if (ndi_flavor_get(dup) != SCSA_FLAVOR_IPORT) {
+			SCSI_HBA_LOG((_LOG(1), NULL, child,
+			    "init failed: %s@%s: not IPORT flavored",
+			    ddi_node_name(child), addr));
+			return (DDI_FAILURE);
+		}
+		if (dup != child) {
+			SCSI_HBA_LOG((_LOG(4), NULL, child,
+			    "init failed: %s@%s: detected duplicate %p",
+			    ddi_node_name(child), addr, (void *)dup));
+			return (DDI_FAILURE);
+		}
+	}
 
 	/* set the node @addr string */
 	ddi_set_name_addr(child, addr);
 
-	/* Prevent duplicate nodes.  */
-	dup = ndi_devi_find(self, ddi_node_name(child), addr);
-	if (dup && (dup != child)) {
-		ddi_set_name_addr(child, NULL);
-		return (DDI_FAILURE);
-	}
-
+	return (DDI_SUCCESS);
+}
+
+/* uninitchild SCSA iport 'child' node */
+static int
+iport_busctl_uninitchild(dev_info_t *child)
+{
+	ddi_set_name_addr(child, NULL);
 	return (DDI_SUCCESS);
 }
 
@@ -1812,13 +2278,7 @@
 	scsi_hba_tran_free(tran);
 }
 
-/*
- * Initialize scsi_device flavor of transport on SCSA iport 'child' node.
- *
- * NOTE: Given our past history with SCSI_HBA_TRAN_CLONE (structure-copy tran
- * per scsi_device), using structure-copy of tran at the iport level should
- * not be a problem (the most risky thing is likely tran_hba_dip).
- */
+/* Initialize scsi_device flavor of transport on SCSA iport 'child' node. */
 static void
 iport_preattach_tran_scsi_device(dev_info_t *child)
 {
@@ -1828,14 +2288,11 @@
 
 	/* parent HBA node scsi_device tran is required */
 	htran = ndi_flavorv_get(hba, SCSA_FLAVOR_SCSI_DEVICE);
-	ASSERT(htran != NULL);
-	if (htran == NULL)
-		return;
+	ASSERT(htran);
 
 	/* Allocate iport child's scsi_device transport vector */
 	tran = scsi_hba_tran_alloc(child, SCSI_HBA_CANSLEEP);
-	if (tran == NULL)
-		return;
+	ASSERT(tran);
 
 	/* Structure-copy scsi_device transport of HBA to iport. */
 	*tran = *htran;
@@ -1846,11 +2303,14 @@
 	 */
 	tran->tran_open_flag = 0;
 	tran->tran_hba_private = NULL;
+
+	/* Establish the devinfo context of this tran structure. */
 	tran->tran_iport_dip = child;
 
 	/* Clear SCSI_HBA_SCSA flags (except TA) */
-	tran->tran_hba_flags &= ~SCSI_HBA_SCSA_FM;	/* clear parent state */
-	tran->tran_hba_flags |= SCSI_HBA_SCSA_TA;
+	tran->tran_hba_flags &=
+	    ~(SCSI_HBA_SCSA_FM | SCSI_HBA_SCSA_PHCI);	/* clear parent state */
+	tran->tran_hba_flags |= SCSI_HBA_SCSA_TA;	/* always TA */
 	tran->tran_hba_flags &= ~SCSI_HBA_HBA;		/* never HBA */
 
 	/* Establish flavor of transport (and ddi_get_driver_private()) */
@@ -1858,9 +2318,8 @@
 
 	/* Setup iport node */
 	if ((scsa_nexus_setup(child, tran) != DDI_SUCCESS) ||
-	    (scsa_tran_setup(child, tran) != DDI_SUCCESS)) {
+	    (scsa_tran_setup(child, tran) != DDI_SUCCESS))
 		iport_postdetach_tran_scsi_device(child);
-	}
 }
 
 /* Uninitialize smp_device flavor of transport on SCSA iport 'child' node. */
@@ -1893,7 +2352,7 @@
 	}
 
 	/* Allocate iport child's smp_device transport vector */
-	tran = sas_hba_tran_alloc(child, 0);
+	tran = sas_hba_tran_alloc(child);
 
 	/* Structure-copy smp_device transport of HBA to iport. */
 	*tran = *htran;
@@ -1902,7 +2361,6 @@
 	ndi_flavorv_set(child, SCSA_FLAVOR_SMP, tran);
 }
 
-
 /*
  * Generic bus_ctl operations for SCSI HBA's,
  * hiding the busctl interface from the HBA.
@@ -1927,37 +2385,49 @@
 	if ((op == DDI_CTLOPS_INITCHILD) || (op == DDI_CTLOPS_UNINITCHILD))
 		child = (dev_info_t *)arg;
 
-	/* Determine the flavor of the child: smp .vs. scsi */
+	/* Determine the flavor of the child: scsi, smp, iport */
 	child_flavor = ndi_flavor_get(child);
 
 	switch (op) {
 	case DDI_CTLOPS_INITCHILD:
 		switch (child_flavor) {
-		case SCSA_FLAVOR_IPORT:
-			return (iport_busctl_initchild(child));
+		case SCSA_FLAVOR_SCSI_DEVICE:
+			return (scsi_busctl_initchild(child));
 		case SCSA_FLAVOR_SMP:
 			return (smp_busctl_initchild(child));
+		case SCSA_FLAVOR_IPORT:
+			return (iport_busctl_initchild(child));
 		default:
-			return (scsi_busctl_initchild(child));
-		}
+			return (DDI_FAILURE);
+		}
+		/* NOTREACHED */
+
 	case DDI_CTLOPS_UNINITCHILD:
 		switch (child_flavor) {
-		case SCSA_FLAVOR_IPORT:
-			return (iport_busctl_uninitchild(child));
+		case SCSA_FLAVOR_SCSI_DEVICE:
+			return (scsi_busctl_uninitchild(child));
 		case SCSA_FLAVOR_SMP:
 			return (smp_busctl_uninitchild(child));
+		case SCSA_FLAVOR_IPORT:
+			return (iport_busctl_uninitchild(child));
 		default:
-			return (scsi_busctl_uninitchild(child));
-		}
+			return (DDI_FAILURE);
+		}
+		/* NOTREACHED */
+
 	case DDI_CTLOPS_REPORTDEV:
 		switch (child_flavor) {
-		case SCSA_FLAVOR_IPORT:
-			return (iport_busctl_reportdev(child));
+		case SCSA_FLAVOR_SCSI_DEVICE:
+			return (scsi_busctl_reportdev(child));
 		case SCSA_FLAVOR_SMP:
 			return (smp_busctl_reportdev(child));
+		case SCSA_FLAVOR_IPORT:
+			return (iport_busctl_reportdev(child));
 		default:
-			return (scsi_busctl_reportdev(child));
-		}
+			return (DDI_FAILURE);
+		}
+		/* NOTREACHED */
+
 	case DDI_CTLOPS_ATTACH:
 		as = (struct attachspec *)arg;
 
@@ -1976,6 +2446,7 @@
 			iport_postdetach_tran_smp_device(child);
 		}
 		return (DDI_SUCCESS);
+
 	case DDI_CTLOPS_DETACH:
 		ds = (struct detachspec *)arg;
 
@@ -1990,6 +2461,7 @@
 			iport_postdetach_tran_smp_device(child);
 		}
 		return (DDI_SUCCESS);
+
 	case DDI_CTLOPS_IOMIN:
 		tran = ddi_get_driver_private(self);
 		ASSERT(tran);
@@ -2015,7 +2487,6 @@
 		return (ndi_dev_is_persistent_node(child) ?
 		    DDI_SUCCESS : DDI_FAILURE);
 
-	/* XXX these should be handled */
 	case DDI_CTLOPS_POWER:
 		return (DDI_SUCCESS);
 
@@ -2042,6 +2513,7 @@
 	default:
 		return (ddi_ctlops(self, child, op, arg, result));
 	}
+	/* NOTREACHED */
 }
 
 /*
@@ -2064,7 +2536,7 @@
 /*ARGSUSED*/
 struct scsi_pkt *
 scsi_hba_pkt_alloc(
-	dev_info_t		*dip,
+	dev_info_t		*self,
 	struct scsi_address	*ap,
 	int			cmdlen,
 	int			statuslen,
@@ -2081,7 +2553,7 @@
 
 	/* Sanity check */
 	if (callback != SLEEP_FUNC && callback != NULL_FUNC)
-		SCSI_HBA_LOG((_LOG(WARN), dip, NULL,
+		SCSI_HBA_LOG((_LOG(WARN), self, NULL,
 		    "callback must be SLEEP_FUNC or NULL_FUNC"));
 
 	/*
@@ -2276,16 +2748,16 @@
  * SCSI_SIZE_CLEAN_VERIFY to ensure compliance (see scsi_pkt.h).
  */
 void
-scsi_size_clean(dev_info_t *dip)
+scsi_size_clean(dev_info_t *self)
 {
 	major_t		major;
 	struct devnames	*dnp;
 
-	ASSERT(dip);
-	major = ddi_driver_major(dip);
+	ASSERT(self);
+	major = ddi_driver_major(self);
 	ASSERT(major < devcnt);
 	if (major >= devcnt) {
-		SCSI_HBA_LOG((_LOG(WARN), dip, NULL,
+		SCSI_HBA_LOG((_LOG(WARN), self, NULL,
 		    "scsi_pkt_size: bogus major: %d", major));
 		return;
 	}
@@ -2353,7 +2825,7 @@
 /*ARGSUSED*/
 static int
 scsi_hba_map_fault(
-	dev_info_t		*dip,
+	dev_info_t		*self,
 	dev_info_t		*child,
 	struct hat		*hat,
 	struct seg		*seg,
@@ -2559,33 +3031,34 @@
 /* ARGSUSED */
 int
 scsi_hba_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
-	int *rvalp)
+    int *rvalp)
 {
 	dev_info_t		*self;
 	struct devctl_iocdata	*dcp = NULL;
 	dev_info_t		*child = NULL;
+	mdi_pathinfo_t		*path = NULL;
 	struct scsi_device	*sd;
 	scsi_hba_tran_t		*tran;
 	uint_t			bus_state;
 	int			rv = 0;
 	int			circ;
-
-	if ((self = e_ddi_hold_devi_by_dev(dev, 0)) == NULL) {
+	char			*name;
+	char			*addr;
+
+	self = e_ddi_hold_devi_by_dev(dev, 0);
+	if (self == NULL) {
 		rv = ENXIO;
 		goto out;
 	}
 
-	if ((tran = ddi_get_driver_private(self)) == NULL) {
+	tran = ddi_get_driver_private(self);
+	if (tran == NULL) {
 		rv = ENXIO;
 		goto out;
 	}
 
 	/* Ioctls for which the generic implementation suffices. */
 	switch (cmd) {
-	case DEVCTL_DEVICE_GETSTATE:
-	case DEVCTL_DEVICE_ONLINE:
-	case DEVCTL_DEVICE_OFFLINE:
-	case DEVCTL_DEVICE_REMOVE:
 	case DEVCTL_BUS_GETSTATE:
 		rv = ndi_devctl_ioctl(self, cmd, arg, mode, 0);
 		goto out;
@@ -2599,65 +3072,106 @@
 
 	/* Ioctls that require child identification */
 	switch (cmd) {
+	case DEVCTL_DEVICE_GETSTATE:
+	case DEVCTL_DEVICE_ONLINE:
+	case DEVCTL_DEVICE_OFFLINE:
+	case DEVCTL_DEVICE_REMOVE:
 	case DEVCTL_DEVICE_RESET:
-		/* child identification from unit-address */
-		if (ndi_dc_getname(dcp) == NULL ||
-		    ndi_dc_getaddr(dcp) == NULL) {
+		name = ndi_dc_getname(dcp);
+		addr = ndi_dc_getaddr(dcp);
+		if ((name == NULL) || (addr == NULL)) {
 			rv = EINVAL;
 			goto out;
 		}
 
-		ndi_devi_enter(self, &circ);
-		child = ndi_devi_find(self,
-		    ndi_dc_getname(dcp), ndi_dc_getaddr(dcp));
-		if (child == NULL) {
-			ndi_devi_exit(self, circ);
-			rv = ENXIO;
+		/*
+		 * Find child with name@addr - might find a devinfo
+		 * child (child), a pathinfo child (path), or nothing.
+		 */
+		scsi_hba_devi_enter(self, &circ);
+
+		(void) scsi_hba_find_child(self, name, addr,
+		    1, &child, &path, NULL);
+		if (path) {
+			/* Found a pathinfo */
+			ASSERT(path && (child == NULL));
+			mdi_hold_path(path);
+			scsi_hba_devi_exit_phci(self, circ);
+			sd = NULL;
+		} else if (child) {
+			/* Found a devinfo */
+			ASSERT(child && (path == NULL));
+
+			/* verify scsi_device of child */
+			if (ndi_flavor_get(child) == SCSA_FLAVOR_SCSI_DEVICE)
+				sd = ddi_get_driver_private(child);
+			else
+				sd = NULL;
+		} else {
+			ASSERT((path == NULL) && (child == NULL));
+			scsi_hba_devi_exit(self, circ);
+			rv = ENXIO;			/* found nothing */
 			goto out;
 		}
-		ndi_hold_devi(child);
-		ndi_devi_exit(self, circ);
 		break;
 
-	case DEVCTL_BUS_RESETALL:
+	case DEVCTL_BUS_RESETALL:	/* ioctl that operate on any child */
 		/*
-		 * Find a child's scsi_address so we can invoke tran_reset
-		 * below.
+		 * Find a child's scsi_address so we can invoke tran_reset.
 		 *
-		 * XXX If no child exists, one may to able to fake a child.
-		 *	This will be a enhancement for the future.
-		 *	For now, we fall back to BUS_RESET.
-		 * XXX We sould be looking at node state to get one
-		 *	that is initialized...
+		 * Future: If no child exists, we could fake a child. This will
+		 * be a enhancement for the future - for now, we fall back to
+		 * BUS_RESET.
 		 */
-		ndi_devi_enter(self, &circ);
+		scsi_hba_devi_enter(self, &circ);
 		child = ddi_get_child(self);
 		sd = NULL;
 		while (child) {
-			/* XXX verify scsi_device 'flavor' of child */
-			if ((sd = ddi_get_driver_private(child)) != NULL) {
+			/* verify scsi_device of child */
+			if (ndi_flavor_get(child) == SCSA_FLAVOR_SCSI_DEVICE)
+				sd = ddi_get_driver_private(child);
+			if (sd != NULL) {
+				/*
+				 * NOTE: node has a scsi_device structure, so
+				 * it must be initialized.
+				 */
 				ndi_hold_devi(child);
 				break;
 			}
 			child = ddi_get_next_sibling(child);
 		}
-		ndi_devi_exit(self, circ);
+		scsi_hba_devi_exit(self, circ);
 		break;
 	}
 
 	switch (cmd) {
+	case DEVCTL_DEVICE_GETSTATE:
+		if (path) {
+			if (mdi_dc_return_dev_state(path, dcp) != MDI_SUCCESS)
+				rv = EFAULT;
+		} else if (child) {
+			if (ndi_dc_return_dev_state(child, dcp) != NDI_SUCCESS)
+				rv = EFAULT;
+		} else {
+			rv = ENXIO;
+		}
+		break;
+
 	case DEVCTL_DEVICE_RESET:
-		ASSERT(child);
-		if (tran->tran_reset == NULL)
+		if (sd == NULL) {
+			rv = ENOTTY;
+			break;
+		}
+		if (tran->tran_reset == NULL) {
 			rv = ENOTSUP;
-		else {
-			sd = ddi_get_driver_private(child);
-			/* XXX verify scsi_device 'flavor' of child */
-			if ((sd == NULL) ||
-			    (tran->tran_reset(&sd->sd_address,
-			    RESET_TARGET) != 1))
-				rv = EIO;
-		}
+			break;
+		}
+
+		/* Start with the small stick */
+		if (scsi_reset(&sd->sd_address, RESET_LUN) == 1)
+			break;		/* LUN reset worked */
+		if (scsi_reset(&sd->sd_address, RESET_TARGET) != 1)
+			rv = EIO;	/* Target reset failed */
 		break;
 
 	case DEVCTL_BUS_QUIESCE:
@@ -2665,11 +3179,11 @@
 		    (bus_state == BUS_QUIESCED))
 			rv = EALREADY;
 		else if (tran->tran_quiesce == NULL)
-			rv = ENOTSUP;
-		else if ((*tran->tran_quiesce)(self) != 0)
+			rv = ENOTSUP; /* man ioctl(7I) says ENOTTY */
+		else if (tran->tran_quiesce(self) != 0)
 			rv = EIO;
-		else
-			(void) ndi_set_bus_state(self, BUS_QUIESCED);
+		else if (ndi_set_bus_state(self, BUS_QUIESCED) != NDI_SUCCESS)
+			rv = EIO;
 		break;
 
 	case DEVCTL_BUS_UNQUIESCE:
@@ -2677,61 +3191,119 @@
 		    (bus_state == BUS_ACTIVE))
 			rv = EALREADY;
 		else if (tran->tran_unquiesce == NULL)
-			rv = ENOTSUP;
-		else if ((*tran->tran_unquiesce)(self) != 0)
+			rv = ENOTSUP; /* man ioctl(7I) says ENOTTY */
+		else if (tran->tran_unquiesce(self) != 0)
 			rv = EIO;
-		else
-			(void) ndi_set_bus_state(self, BUS_ACTIVE);
+		else if (ndi_set_bus_state(self, BUS_ACTIVE) != NDI_SUCCESS)
+			rv = EIO;
 		break;
 
 	case DEVCTL_BUS_RESET:
 		if (tran->tran_bus_reset == NULL)
-			rv = ENOTSUP;
-		else if ((*tran->tran_bus_reset)(self, RESET_BUS) != 1)
+			rv = ENOTSUP; /* man ioctl(7I) says ENOTTY */
+		else if (tran->tran_bus_reset(self, RESET_BUS) != 1)
 			rv = EIO;
 		break;
 
 	case DEVCTL_BUS_RESETALL:
-		if (tran->tran_reset == NULL) {
-			rv = ENOTSUP;
-		} else {
-			if (sd) {
-				if ((*tran->tran_reset)
-				    (&sd->sd_address, RESET_ALL) != 1)
-					rv = EIO;
-			} else {
-				if ((tran->tran_bus_reset == NULL) ||
-				    ((*tran->tran_bus_reset)
-				    (self, RESET_BUS) != 1))
-					rv = EIO;
-			}
-		}
+		if ((sd != NULL) &&
+		    (scsi_reset(&sd->sd_address, RESET_ALL) == 1)) {
+			break;		/* reset all worked */
+		}
+		if (tran->tran_bus_reset == NULL) {
+			rv = ENOTSUP; /* man ioctl(7I) says ENOTTY */
+			break;
+		}
+		if (tran->tran_bus_reset(self, RESET_BUS) != 1)
+			rv = EIO;	/* bus reset failed */
 		break;
 
 	case DEVCTL_BUS_CONFIGURE:
-		if (ndi_devi_config(self, NDI_DEVFS_CLEAN|
-		    NDI_DEVI_PERSIST|NDI_CONFIG_REPROBE) != NDI_SUCCESS) {
+		if (ndi_devi_config(self, NDI_DEVFS_CLEAN | NDI_DEVI_PERSIST |
+		    NDI_CONFIG_REPROBE) != NDI_SUCCESS) {
 			rv = EIO;
 		}
 		break;
 
 	case DEVCTL_BUS_UNCONFIGURE:
 		if (ndi_devi_unconfig(self,
-		    NDI_DEVI_REMOVE|NDI_DEVFS_CLEAN) != NDI_SUCCESS) {
+		    NDI_DEVFS_CLEAN | NDI_DEVI_REMOVE) != NDI_SUCCESS) {
 			rv = EBUSY;
 		}
 		break;
 
+	case DEVCTL_DEVICE_ONLINE:
+		ASSERT(child || path);
+		if (path) {
+			if (mdi_pi_online(path, NDI_USER_REQ) != MDI_SUCCESS)
+				rv = EIO;
+		} else {
+			if (ndi_devi_online(child, 0) != NDI_SUCCESS)
+				rv = EIO;
+		}
+		break;
+
+	case DEVCTL_DEVICE_OFFLINE:
+		ASSERT(child || path);
+		if (sd != NULL)
+			(void) scsi_clear_task_set(&sd->sd_address);
+		if (path) {
+			if (mdi_pi_offline(path, NDI_USER_REQ) != MDI_SUCCESS)
+				rv = EIO;
+		} else {
+			if (ndi_devi_offline(child,
+			    NDI_DEVFS_CLEAN) != NDI_SUCCESS)
+				rv = EIO;
+		}
+		break;
+
+	case DEVCTL_DEVICE_REMOVE:
+		ASSERT(child || path);
+		if (sd != NULL)
+			(void) scsi_clear_task_set(&sd->sd_address);
+		if (path) {
+			/* NOTE: don't pass NDI_DEVI_REMOVE to mdi_pi_offline */
+			if (mdi_pi_offline(path, NDI_USER_REQ) == MDI_SUCCESS) {
+				scsi_hba_devi_enter_phci(self, &circ);
+				mdi_rele_path(path);
+
+				/* ... here is the DEVICE_REMOVE part. */
+				(void) mdi_pi_free(path, 0);
+				path = NULL;
+			} else {
+				rv = EIO;
+			}
+		} else {
+			if (ndi_devi_offline(child,
+			    NDI_DEVFS_CLEAN | NDI_DEVI_REMOVE) != NDI_SUCCESS)
+				rv = EIO;
+		}
+		break;
+
 	default:
+		ASSERT(dcp != NULL);
 		rv = ENOTTY;
-	}
-
-out:	if (child)
-		ndi_rele_devi(child);
+		break;
+	}
+
+	/* all done -- clean up and return */
+out:
+	/* release hold on what we found */
+	if (path) {
+		scsi_hba_devi_enter_phci(self, &circ);
+		mdi_rele_path(path);
+	}
+	if (path || child)
+		scsi_hba_devi_exit(self, circ);
+
 	if (dcp)
 		ndi_dc_freehdl(dcp);
+
 	if (self)
 		ddi_release_devi(self);
+
+	*rvalp = rv;
+
 	return (rv);
 }
 
@@ -2761,14 +3333,15 @@
 }
 
 /*
- * Return the lun from an address string. Either the lun is after the
- * first ',' or the entire addr is the lun. Return SCSI_LUN64_ILLEGAL
- * if the format is incorrect.
+ * Return the lun64 value from a address string: "addr,lun[,sfunc]". Either
+ * the lun is after the first ',' or the entire address string is the lun.
+ * Return SCSI_LUN64_ILLEGAL if the format is incorrect. A lun64 is at most
+ * 16 hex digits long.
  *
- * If the addr specified has incorrect syntax (busconfig one of
+ * If the address string specified has incorrect syntax (busconfig one of
  * bogus /devices path) then scsi_addr_to_lun64 can return SCSI_LUN64_ILLEGAL.
  */
-scsi_lun64_t
+static scsi_lun64_t
 scsi_addr_to_lun64(char *addr)
 {
 	scsi_lun64_t	lun64;
@@ -2776,9 +3349,9 @@
 	int		i;
 
 	if (addr) {
-		s = strchr(addr, ',');			/* "addr,lun[,sfunc]" */
+		s = strchr(addr, ',');			/* "addr,lun" */
 		if (s)
-			s++;				/* skip ',' */
+			s++;				/* skip ',', at lun */
 		else
 			s = addr;			/* "lun" */
 
@@ -2792,7 +3365,7 @@
 			else
 				break;
 		}
-		if (*s && (*s != ','))		/* addr,lun[,sfunc] is OK */
+		if (*s && (*s != ','))		/* [,sfunc] is OK */
 			lun64 = SCSI_LUN64_ILLEGAL;
 	} else
 		lun64 = SCSI_LUN64_ILLEGAL;
@@ -2805,6 +3378,47 @@
 }
 
 /*
+ * Return the sfunc value from a address string: "addr,lun[,sfunc]". Either the
+ * sfunc is after the second ',' or the entire address string is the sfunc.
+ * Return -1 if there is only one ',' in the address string or the string is
+ * invalid. An sfunc is at most two hex digits long.
+ */
+static int
+scsi_addr_to_sfunc(char *addr)
+{
+	int		sfunc;
+	char		*s;
+	int		i;
+
+	if (addr) {
+		s = strchr(addr, ',');			/* "addr,lun" */
+		if (s) {
+			s++;				/* skip ',', at lun */
+			s = strchr(s, ',');		/* "lun,sfunc]" */
+			if (s == NULL)
+				return (-1);		/* no ",sfunc" */
+			s++;				/* skip ',', at sfunc */
+		} else
+			s = addr;			/* "sfunc" */
+
+		for (sfunc = 0, i = 0; *s && (i < 2); s++, i++) {
+			if (*s >= '0' && *s <= '9')
+				sfunc = (sfunc << 4) + (*s - '0');
+			else if (*s >= 'A' && *s <= 'F')
+				sfunc = (sfunc << 4) + 10 + (*s - 'A');
+			else if (*s >= 'a' && *s <= 'f')
+				sfunc = (sfunc << 4) + 10 + (*s - 'a');
+			else
+				break;
+		}
+		if (*s)
+			sfunc = -1;			/* illegal */
+	} else
+		sfunc = -1;
+	return (sfunc);
+}
+
+/*
  * Convert scsi ascii string data to NULL terminated (semi) legal IEEE 1275
  * "compatible" (name) property form.
  *
@@ -2908,37 +3522,37 @@
  *
  *	where:
  *
- *	v                       is the letter 'v'. Denotest the
+ *	v			is the letter 'v'. Denotest the
  *				beginning of VVVVVVVV.
  *
- *	VVVVVVVV                Translated scsi_vendor.
+ *	VVVVVVVV		Translated scsi_vendor.
  *
- *	p                       is the letter 'p'. Denotes the
+ *	p			is the letter 'p'. Denotes the
  *				beginning of PPPPPPPPPPPPPPPP.
  *
  *	PPPPPPPPPPPPPPPP	Translated scsi_product.
  *
- *	r                       is the letter 'r'. Denotes the
+ *	r			is the letter 'r'. Denotes the
  *				beginning of RRRR.
  *
- *	RRRR                    Translated scsi_revision.
+ *	RRRR			Translated scsi_revision.
  *
- *	DD                      is a two digit ASCII hexadecimal
+ *	DD			is a two digit ASCII hexadecimal
  *				number. The value of the two digits is
  *				based one the SCSI "Peripheral device
  *				type" command set associated with the
  *				node. On a primary node this is the
  *				scsi_dtype of the primary command set,
  *				on a secondary node this is the
- *				scsi_dtype associated with the embedded
- *				function command set.
+ *				scsi_dtype associated with the secondary
+ *				function embedded command set.
  *
- *	EE                      Same encoding used for DD. This form is
+ *	EE			Same encoding used for DD. This form is
  *				only generated on secondary function
- *				nodes. The DD function is embedded in
- *				an EE device.
+ *				nodes. The DD secondary function is embedded
+ *				in an EE device.
  *
- *	FFF                     Concatenation, in alphabetical order,
+ *	FFF			Concatenation, in alphabetical order,
  *				of the flag characters within a form-group.
  *				For a given form-group, the following
  *				flags are defined.
@@ -2961,10 +3575,10 @@
  *				if there are applicable flag
  *				characters.
  *
- *	b                       is the letter 'b'. Denotes the
+ *	b			is the letter 'b'. Denotes the
  *				beginning of BBBBBBBB.
  *
- *	BBBBBBBB                Binding-set. Operating System Specific:
+ *	BBBBBBBB		Binding-set. Operating System Specific:
  *				scsi-binding-set property of HBA.
  */
 #define	NCOMPAT		(1 + (13 + 2) + 1)
@@ -2976,10 +3590,11 @@
  * to determine GUID FFF support.
  */
 static void
-scsi_hba_identity_nodename_compatible_get(struct scsi_inquiry *inq,
+scsi_hba_ident_nodename_compatible_get(struct scsi_inquiry *inq,
     uchar_t *inq80, size_t inq80len, uchar_t *inq83, size_t inq83len,
     char *binding_set, int dtype_node, char *compat0,
-    char **nodenamep, char ***compatiblep, int *ncompatiblep)
+    char **nodenamep, char **drivernamep,
+    char ***compatiblep, int *ncompatiblep)
 {
 	char		vid[sizeof (inq->inq_vid) + 1 ];
 	char		pid[sizeof (inq->inq_pid) + 1];
@@ -3106,13 +3721,6 @@
 	/* # for x86 fcp and vhci use generic nodenames */
 #endif	/* sparc */
 
-#ifdef  notdef
-	/*
-	 * The following binding-set specific mappings are not being
-	 * delivered at this time, but are listed here as an examples of
-	 * the type of mappings needed.
-	 */
-
 	/* # legacy mapping to driver nodenames for spi binding-set */
 		{"sd",			"scsa,00.bspi"},
 		{"sd",			"scsa,05.bspi"},
@@ -3138,7 +3746,6 @@
 		{"comm",		"scsa,09.busb"},
 		{"array_ctlr",		"scsa,0c.busb"},
 		{"esi",			"scsa,0d.busb"},
-#endif  /* notdef */
 
 	/*
 	 * mapping nodenames for mpt based on scsi dtype
@@ -3155,6 +3762,7 @@
 	};
 	struct nodename_aliases *nap;
 
+	/* NOTE: drivernamep can be NULL */
 	ASSERT(nodenamep && compatiblep && ncompatiblep &&
 	    (binding_set == NULL || (strlen(binding_set) <= 8)));
 	if ((nodenamep == NULL) || (compatiblep == NULL) ||
@@ -3186,7 +3794,7 @@
 	/*
 	 * Form flags in ***ALPHABETICAL*** order within form-group:
 	 *
-	 * NOTE: When adding a new flag to an existing form-group, carefull
+	 * NOTE: When adding a new flag to an existing form-group, careful
 	 * consideration must be given to not breaking existing bindings
 	 * based on that form-group.
 	 */
@@ -3203,7 +3811,7 @@
 	 */
 	i = 0;
 	dtype_device = inq->inq_dtype & DTYPE_MASK;
-	if (rootvp && (inq->inq_rmb ||
+	if (modrootloaded && (inq->inq_rmb ||
 	    (dtype_device == DTYPE_WORM) ||
 	    (dtype_device == DTYPE_RODIRECT) ||
 	    (dtype_device == DTYPE_OPTICAL)))
@@ -3433,31 +4041,33 @@
 	}
 
 	/*
-	 * If no nodename_aliases mapping exists then use the
-	 * driver_aliases specified driver binding as a nodename.
-	 * Determine the driver based on compatible (which may
-	 * have the passed in compat0 as the first item). The
-	 * driver_aliases file has entries like
+	 * Determine the driver name based on compatible (which may
+	 * have the passed in compat0 as the first item). The driver_aliases
+	 * file has entries like
 	 *
 	 *	sd	"scsiclass,00"
 	 *
-	 * that map compatible forms to specific drivers. These
-	 * entries are established by add_drv. We use the most specific
+	 * that map compatible forms to specific drivers. These entries are
+	 * established by add_drv/update_drv. We use the most specific
 	 * driver binding as the nodename. This matches the eventual
 	 * ddi_driver_compatible_major() binding that will be
 	 * established by bind_node()
 	 */
-	if (nname == NULL) {
-		for (dname = NULL, csp = compatp; *csp; csp++) {
-			major = ddi_name_to_major(*csp);
-			if ((major == (major_t)-1) ||
-			    (devnamesp[major].dn_flags & DN_DRIVER_REMOVED))
-				continue;
-			if (dname = ddi_major_to_name(major))
-				break;
-		}
+	for (dname = NULL, csp = compatp; *csp; csp++) {
+		major = ddi_name_to_major(*csp);
+		if ((major == DDI_MAJOR_T_NONE) ||
+		    (devnamesp[major].dn_flags & DN_DRIVER_REMOVED))
+			continue;
+		if (dname = ddi_major_to_name(major))
+			break;
+	}
+
+	/*
+	 * If no nodename_aliases mapping exists then use the
+	 * driver_aliases specified driver binding as a nodename.
+	 */
+	if (nname == NULL)
 		nname = dname;
-	}
 
 	/* return results */
 	if (nname) {
@@ -3472,41 +4082,70 @@
 		 * compatible contains all compatible forms concatenated
 		 * into a single string pointed to by the first element.
 		 */
-		if (nname == NULL) {
-			for (csp = compatp; *(csp + 1); csp++)
-				*((*csp) + strlen(*csp)) = ' ';
-			*(compatp + 1) = NULL;
-			ncompat = 1;
-		}
-
+		for (csp = compatp; *(csp + 1); csp++)
+			*((*csp) + strlen(*csp)) = ' ';
+		*(compatp + 1) = NULL;
+		ncompat = 1;
+
+	}
+	if (drivernamep) {
+		if (dname) {
+			*drivernamep = kmem_alloc(strlen(dname) + 1, KM_SLEEP);
+			(void) strcpy(*drivernamep, dname);
+		} else
+			*drivernamep = NULL;
 	}
 	*compatiblep = compatp;
 	*ncompatiblep = ncompat;
 }
 
+/*
+ * Free allocations associated with scsi_hba_ident_nodename_compatible_get.
+ */
+static void
+scsi_hba_ident_nodename_compatible_free(char *nodename, char *drivername,
+    char **compatible)
+{
+	if (nodename)
+		kmem_free(nodename, strlen(nodename) + 1);
+	if (drivername)
+		kmem_free(drivername, strlen(drivername) + 1);
+	if (compatible)
+		kmem_free(compatible, (NCOMPAT * sizeof (char *)) +
+		    (NCOMPAT * COMPAT_LONGEST));
+}
+
 void
 scsi_hba_nodename_compatible_get(struct scsi_inquiry *inq,
     char *binding_set, int dtype_node, char *compat0,
     char **nodenamep, char ***compatiblep, int *ncompatiblep)
 {
-	scsi_hba_identity_nodename_compatible_get(inq,
+	scsi_hba_ident_nodename_compatible_get(inq,
 	    NULL, 0, NULL, 0, binding_set, dtype_node, compat0, nodenamep,
-	    compatiblep, ncompatiblep);
-}
-
-/*
- * Free allocations associated with scsi_hba_nodename_compatible_get or
- * scsi_hba_identity_nodename_compatible_get use.
- */
+	    NULL, compatiblep, ncompatiblep);
+}
+
 void
 scsi_hba_nodename_compatible_free(char *nodename, char **compatible)
 {
-	if (nodename)
-		kmem_free(nodename, strlen(nodename) + 1);
-
-	if (compatible)
-		kmem_free(compatible, (NCOMPAT * sizeof (char *)) +
-		    (NCOMPAT * COMPAT_LONGEST));
+	scsi_hba_ident_nodename_compatible_free(nodename, NULL, compatible);
+}
+
+/* return the unit_address associated with a scsi_device */
+char *
+scsi_device_unit_address(struct scsi_device *sd)
+{
+	mdi_pathinfo_t	*pip;
+
+	ASSERT(sd && sd->sd_dev);
+	if ((sd == NULL) || (sd->sd_dev == NULL))
+		return (NULL);
+
+	pip = (mdi_pathinfo_t *)sd->sd_pathinfo;
+	if (pip)
+		return (mdi_pi_get_addr(pip));
+	else
+		return (ddi_get_name_addr(sd->sd_dev));
 }
 
 /* scsi_device property interfaces */
@@ -3518,23 +4157,6 @@
 	((((flags & SCSI_DEVICE_PROP_TYPE_MSK) == SCSI_DEVICE_PROP_PATH) && \
 	sd->sd_pathinfo) ? (mdi_pathinfo_t *)sd->sd_pathinfo : NULL)
 
-/* return the unit_address associated with a scsi_device */
-char *
-scsi_device_unit_address(struct scsi_device *sd)
-{
-	mdi_pathinfo_t	*pip;
-
-	ASSERT(sd && sd->sd_dev);
-	if ((sd == NULL) || (sd->sd_dev == NULL))
-		return (NULL);
-
-	pip = _DEVICE_PIP(sd, SCSI_DEVICE_PROP_PATH);
-	if (pip)
-		return (mdi_pi_get_addr(pip));
-	else
-		return (ddi_get_name_addr(sd->sd_dev));
-}
-
 int
 scsi_device_prop_get_int(struct scsi_device *sd, uint_t flags,
     char *name, int defval)
@@ -3837,26 +4459,4086 @@
 		ddi_prop_free(data);
 }
 
+/*
+ * scsi_hba_ua_set: given "unit-address" string, set properties.
+ *
+ * Function to set the properties on a devinfo or pathinfo node from
+ * the "unit-address" part of a "name@unit-address" /devices path 'name'
+ * string.
+ *
+ * This function works in conjunction with scsi_ua_get()/scsi_hba_ua_get()
+ * (and possibly with an HBA driver's tran_tgt_init() implementation).
+ */
+static int
+scsi_hba_ua_set(char *ua, dev_info_t *dchild, mdi_pathinfo_t *pchild)
+{
+	char		*p;
+	int		tgt;
+	char		*tgt_port_end;
+	char		*tgt_port;
+	int		tgt_port_len;
+	int		sfunc;
+	scsi_lun64_t	lun64;
+
+	/* Caller must choose to decorate devinfo *or* pathinfo */
+	ASSERT((dchild != NULL) ^ (pchild != NULL));
+	if (dchild && pchild)
+		return (0);
+
+	/*
+	 * generic implementation based on "tgt,lun[,sfunc]" address form.
+	 * parse hex "tgt" part of "tgt,lun[,sfunc]"
+	 */
+	p = ua;
+	tgt_port_end = NULL;
+	for (tgt = 0; *p && *p != ','; p++) {
+		if (*p >= '0' && *p <= '9')
+			tgt = (tgt << 4) + (*p - '0');
+		else if (*p >= 'a' && *p <= 'f')
+			tgt = (tgt << 4) + 10 + (*p - 'a');
+		else
+			tgt = -1;		/* non-numeric */
+
+		/*
+		 * if non-numeric or our of range set tgt to -1 and
+		 * skip forward
+		 */
+		if (tgt < 0) {
+			tgt = -1;
+			for (; *p && *p != ','; p++)
+				;
+			break;
+		}
+	}
+	tgt_port_end = p;
+
+	/* parse hex ",lun" part of "tgt,lun[,sfunc]" */
+	if (*p)
+		p++;
+	for (lun64 = 0; *p && *p != ','; p++) {
+		if (*p >= '0' && *p <= '9')
+			lun64 = (lun64 << 4) + (*p - '0');
+		else if (*p >= 'a' && *p <= 'f')
+			lun64 = (lun64 << 4) + 10 + (*p - 'a');
+		else
+			return (0);
+	}
+
+	/* parse hex ",sfunc" part of "tgt,lun[,sfunc]" */
+	if (*p) {
+		p++;
+		for (sfunc = 0; *p; p++) {
+			if (*p >= '0' && *p <= '9')
+				sfunc = (sfunc << 4) + (*p - '0');
+			else if (*p >= 'a' && *p <= 'f')
+				sfunc = (sfunc << 4) + 10 + (*p - 'a');
+			else
+				return (0);
+		}
+	} else
+		sfunc = -1;
+
+	if (dchild) {
+		/*
+		 * Decorate a devinfo node with unit address properties.
+		 * This adds the the addressing properties needed to
+		 * DDI_CTLOPS_UNINITCHILD the devinfo node (i.e. perform
+		 * the reverse operation - form unit address from properties).
+		 */
+		if ((tgt != -1) && (ndi_prop_update_int(DDI_DEV_T_NONE, dchild,
+		    SCSI_ADDR_PROP_TARGET, tgt) != DDI_PROP_SUCCESS))
+			return (0);
+
+		if (tgt_port_end) {
+			tgt_port_len = tgt_port_end - ua + 1;
+			tgt_port = kmem_alloc(tgt_port_len, KM_SLEEP);
+			(void) strlcpy(tgt_port, ua, tgt_port_len);
+			if (ndi_prop_update_string(DDI_DEV_T_NONE, dchild,
+			    SCSI_ADDR_PROP_TARGET_PORT, tgt_port) !=
+			    DDI_PROP_SUCCESS) {
+				kmem_free(tgt_port, tgt_port_len);
+				return (0);
+			}
+			kmem_free(tgt_port, tgt_port_len);
+		}
+
+		/* Set the appropriate lun properties. */
+		if (lun64 < SCSI_32LUNS_PER_TARGET) {
+			if (ndi_prop_update_int(DDI_DEV_T_NONE, dchild,
+			    SCSI_ADDR_PROP_LUN, (int)lun64) != DDI_PROP_SUCCESS)
+				return (0);
+		}
+		if (ndi_prop_update_int64(DDI_DEV_T_NONE, dchild,
+		    SCSI_ADDR_PROP_LUN64, lun64) != DDI_PROP_SUCCESS)
+			return (0);
+
+		/* Set the sfunc property */
+		if ((sfunc != -1) &&
+		    (ndi_prop_update_int(DDI_DEV_T_NONE, dchild,
+		    SCSI_ADDR_PROP_SFUNC, (int)sfunc) != DDI_PROP_SUCCESS))
+			return (0);
+	} else if (pchild) {
+		/*
+		 * Decorate a pathinfo node with unit address properties.
+		 */
+		if ((tgt != -1) && (mdi_prop_update_int(pchild,
+		    SCSI_ADDR_PROP_TARGET, tgt) != DDI_PROP_SUCCESS))
+			return (0);
+
+		if (tgt_port_end) {
+			tgt_port_len = tgt_port_end - ua + 1;
+			tgt_port = kmem_alloc(tgt_port_len, KM_SLEEP);
+			(void) strlcpy(tgt_port, ua, tgt_port_len);
+			if (mdi_prop_update_string(pchild,
+			    SCSI_ADDR_PROP_TARGET_PORT, tgt_port) !=
+			    DDI_PROP_SUCCESS) {
+				kmem_free(tgt_port, tgt_port_len);
+				return (0);
+			}
+			kmem_free(tgt_port, tgt_port_len);
+		}
+
+		/* Set the appropriate lun properties */
+		if (lun64 < SCSI_32LUNS_PER_TARGET) {
+			if (mdi_prop_update_int(pchild, SCSI_ADDR_PROP_LUN,
+			    (int)lun64) != DDI_PROP_SUCCESS)
+				return (0);
+		}
+
+		if (mdi_prop_update_int64(pchild, SCSI_ADDR_PROP_LUN64,
+		    lun64) != DDI_PROP_SUCCESS)
+			return (0);
+
+		/* Set the sfunc property */
+		if ((sfunc != -1) &&
+		    (mdi_prop_update_int(pchild,
+		    SCSI_ADDR_PROP_SFUNC, (int)sfunc) != DDI_PROP_SUCCESS))
+			return (0);
+	}
+	return (1);
+}
+
+/*
+ * Private ndi_devi_find/mdi_pi_find implementation - find the child
+ * dev_info/path_info of self whose phci name matches "name@caddr".
+ * We have our own implementation because we need to search with both
+ * forms of sibling lists (dev_info and path_info) and we need to be able
+ * to search with a NULL name in order to find siblings already associated
+ * with a given unit-address (same @addr). NOTE: NULL name search will never
+ * return probe node.
+ *
+ * If pchildp is NULL and we find a pathinfo child, we return the client
+ * devinfo node in *dchildp.
+ *
+ * The init flag argument should be clear when called from places where
+ * recursion could occur (like scsi_busctl_initchild) and when the caller
+ * has already performed a search for name@addr with init set (performance).
+ *
+ * Future: Integrate ndi_devi_findchild_by_callback into scsi_hba_find_child.
+ */
+static int
+scsi_hba_find_child(dev_info_t *self, char *name, char *addr, int init,
+    dev_info_t **dchildp, mdi_pathinfo_t **pchildp, int *ppi)
+{
+	dev_info_t	*dchild;	/* devinfo child */
+	mdi_pathinfo_t	*pchild;	/* pathinfo child */
+	int		found = CHILD_TYPE_NONE;
+	char		*daddr;
+
+	ASSERT(self && DEVI_BUSY_OWNED(self));
+	ASSERT(addr && dchildp);
+	if ((self == NULL) || (addr == NULL) || (dchildp == NULL))
+		return (CHILD_TYPE_NONE);
+
+	*dchildp = NULL;
+	if (pchildp)
+		*pchildp = NULL;
+	if (ppi)
+		*ppi = 0;
+
+	/* Walk devinfo child list to find a match */
+	for (dchild = ddi_get_child(self); dchild;
+	    dchild = ddi_get_next_sibling(dchild)) {
+		if (i_ddi_node_state(dchild) < DS_INITIALIZED)
+			continue;
+
+		daddr = ddi_get_name_addr(dchild);
+		if (daddr && (strcmp(addr, daddr) == 0) &&
+		    ((name == NULL) ||
+		    (strcmp(name, DEVI(dchild)->devi_node_name) == 0))) {
+			/*
+			 * If we are asked to find "anything" at a given
+			 * unit-address (name == NULL), we don't realy want
+			 * to find the 'probe' node. The existance of
+			 * a probe node on a 'name == NULL' search should
+			 * fail.  This will trigger slow-path code where
+			 * we explicity look for, and synchronize against,
+			 * a node named "probe" at the unit-address.
+			 */
+			if ((name == NULL) &&
+			    scsi_hba_devi_is_barrier(dchild)) {
+				SCSI_HBA_LOG((_LOG(4), NULL, dchild,
+				    "%s@%s 'probe' devinfo found, skip",
+				    name ? name : "", addr));
+				continue;
+			}
+
+			/* We have found a match. */
+			found |= CHILD_TYPE_DEVINFO;
+			SCSI_HBA_LOG((_LOG(4), NULL, dchild,
+			    "%s@%s devinfo found", name ? name : "", addr));
+			*dchildp = dchild;		/* devinfo found */
+			break;
+		}
+	}
+
+	/*
+	 * Walk pathinfo child list to find a match.
+	 *
+	 * NOTE: Unlike devinfo nodes, pathinfo nodes have a string searchable
+	 * unit-address from creation - so there is no need for an 'init'
+	 * search block of code for pathinfo nodes below.
+	 */
+	pchild = mdi_pi_find(self, NULL, addr);
+	if (pchild) {
+		/*
+		 * NOTE: If name specified and we match a pathinfo unit
+		 * address, we don't check the client node name.
+		 */
+		if (ppi)
+			*ppi = mdi_pi_get_path_instance(pchild);
+		found |= CHILD_TYPE_PATHINFO;
+
+		if (pchildp) {
+			SCSI_HBA_LOG((_LOG(4), self, NULL,
+			    "%s pathinfo found", mdi_pi_spathname(pchild)));
+			*pchildp = pchild;		/* pathinfo found */
+		} else if (*dchildp == NULL) {
+			/*
+			 * Did not find a devinfo node, found a pathinfo node,
+			 * but caller did not ask us to return a pathinfo node:
+			 * we return the 'client' devinfo node instead (but
+			 * with CHILD_TYPE_PATHINFO 'found' return value).
+			 */
+			dchild = mdi_pi_get_client(pchild);
+			SCSI_HBA_LOG((_LOG(4), NULL, dchild,
+			    "%s pathinfo found, client switch",
+			    mdi_pi_spathname(pchild)));
+
+			/*
+			 * A pathinfo node always has a 'client' devinfo node,
+			 * but we need to ensure that the 'client' is
+			 * initialized and has a scsi_device structure too.
+			 */
+			ASSERT(dchild);
+			if (i_ddi_node_state(dchild) < DS_INITIALIZED) {
+				SCSI_HBA_LOG((_LOG(4), NULL, dchild,
+				    "%s found client, initchild",
+				    mdi_pi_spathname(pchild)));
+				(void) ddi_initchild(ddi_get_parent(dchild),
+				    dchild);
+			}
+			if (i_ddi_node_state(dchild) >= DS_INITIALIZED) {
+				/* client found and initialized */
+				*dchildp = dchild;
+			} else {
+				SCSI_HBA_LOG((_LOG(4), NULL, dchild,
+				    "%s found client, but failed initchild",
+				    mdi_pi_spathname(pchild)));
+			}
+		}
+	}
+
+	/* Try devinfo again with initchild of uninitialized nodes */
+	if ((found == CHILD_TYPE_NONE) && init) {
+		for (dchild = ddi_get_child(self); dchild;
+		    dchild = ddi_get_next_sibling(dchild)) {
+			/* skip if checked above */
+			if (i_ddi_node_state(dchild) >= DS_INITIALIZED)
+				continue;
+			/* attempt initchild to establish unit-address */
+			(void) ddi_initchild(self, dchild);
+			if (i_ddi_node_state(dchild) < DS_INITIALIZED)
+				continue;
+			daddr = ddi_get_name_addr(dchild);
+			if (daddr &&
+			    ((name == NULL) || (strcmp(name,
+			    DEVI(dchild)->devi_node_name) == 0)) &&
+			    (strcmp(addr, daddr) == 0)) {
+				found |= CHILD_TYPE_DEVINFO;
+				SCSI_HBA_LOG((_LOG(4), NULL, dchild,
+				    "%s@%s devinfo found post initchild",
+				    name ? name : "", addr));
+				*dchildp = dchild;	/* devinfo found */
+				break;	/* node found */
+			}
+		}
+	}
+
+	/*
+	 * We should never find devinfo and pathinfo at the same
+	 * unit-address.
+	 */
+	ASSERT(found != (CHILD_TYPE_DEVINFO | CHILD_TYPE_PATHINFO));
+	if (found == (CHILD_TYPE_DEVINFO | CHILD_TYPE_PATHINFO)) {
+		found = CHILD_TYPE_NONE;
+		*dchildp = NULL;
+		*pchildp = NULL;
+	}
+	return (found);
+}
+
+/*
+ * Given information about a child device (contained on probe node) construct
+ * and return a pointer to the dynamic SID devinfo node associated with the
+ * device. In the creation of this SID node a compatible property for the
+ * device is formed and used to establish a nodename (via
+ * /etc/nodename_aliases) and to bind a driver (via /etc/driver_aliases).
+ *
+ * If this routine is called then we got a response from a device and
+ * obtained the inquiry data from the device. Some inquiry results indicate
+ * that the specific LUN we addressed does not exist, and we don't want to
+ * bind a standard target driver to the node we create. Even though the
+ * specific LUN is not usable, the framework may still want to bind a
+ * target driver to the device for internal communication with the device -
+ * an example would be issuing a report_lun to enumerate other LUNs under a
+ * DPQ_NEVER LUN0. Another example would be wanting to known that the
+ * DPQ_NEVER LUN0 device exists in BUS_CONFIG_ONE for non-existent LUN
+ * caching optimizations. To support this we let the caller specify a
+ * compatible property (or driver). If LUN0 inquiry data indicates that the
+ * LUN does not exist then we establish compat0 as the highest precedence(0)
+ * compatible form. If used, this compat0 driver will never be called on to
+ * issue external commands to the device.
+ *
+ * If no driver binds to the device using driver_alias we establish the driver
+ * passed in as the node name.
+ */
+static int
+scsi_device_createchild(dev_info_t *self, char *addr, scsi_enum_t se,
+    struct scsi_device *sdprobe, dev_info_t **dchildp, mdi_pathinfo_t **pchildp)
+{
+	scsi_lun64_t		lun64;
+	int			dtype;
+	int			dpq;
+	int			dpq_vu;
+	int			dtype_node;
+	int			lunexists;
+	char			*compat0;
+	char			*nname;
+	char			**compat = NULL;
+	int			ncompat;
+	dev_info_t		*dchild = NULL;
+	mdi_pathinfo_t		*pchild = NULL;
+	dev_info_t		*probe = sdprobe->sd_dev;
+	struct scsi_inquiry	*inq = sdprobe->sd_inq;
+	uchar_t			*inq80 = NULL;
+	uchar_t			*inq83 = NULL;
+	uint_t			inq80len, inq83len;
+	char			*binding_set = NULL;
+	char			*dname = NULL;
+	ddi_devid_t		devid;
+	int			have_devid = 0;
+	char			*devid_str;
+	char			*guid = NULL;
+
+	ASSERT(self && addr && *addr && DEVI_BUSY_OWNED(self));
+	ASSERT(dchildp && pchildp);
+
+	/*
+	 * Determine the lun and whether the lun exists. We may need to create
+	 * a node for LUN0 (with compat0 driver binding) even if the lun does
+	 * not exist - so we can run report_lun to find additional LUNs.
+	 */
+	lun64 = scsi_addr_to_lun64(addr);
+	dtype = inq->inq_dtype & DTYPE_MASK;		/* device */
+	dpq = inq->inq_dtype & DPQ_MASK;
+	dpq_vu = inq->inq_dtype & DPQ_VUNIQ ? 1 : 0;
+
+	dtype_node = scsi_addr_to_sfunc(addr);		/* secondary function */
+	if (dtype_node == -1)
+		dtype_node = dtype;			/* node for device */
+
+	lunexists = (dtype != dtype_node) ||		/* override */
+	    ((dpq_vu == 0) && (dpq == DPQ_POSSIBLE)) ||	/* ANSII */
+	    (dpq_vu && (lun64 == 0));			/* VU LUN0 */
+	if (dtype == DTYPE_UNKNOWN)
+		lunexists = 0;
+
+	SCSI_HBA_LOG((_LOG(4), self, NULL,
+	    "@%s dtype %x %x dpq_vu %d dpq %x: %d",
+	    addr, dtype, dtype_node, dpq_vu, dpq, lunexists));
+
+	/* A non-existent LUN0 uses compatible_nodev. */
+	if (lunexists) {
+		compat0 = NULL;				/* compat0 not needed */
+	} else if (lun64 == 0) {
+		compat0 = compatible_nodev;
+		SCSI_HBA_LOG((_LOG(2), self, NULL,
+		    "@%s lun 0 with compat0 %s", addr, compat0));
+	} else
+		goto out;				/* no node created */
+
+	/* Obtain identity information from probe node. */
+	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, probe,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "inquiry-page-80",
+	    &inq80, &inq80len) != DDI_PROP_SUCCESS)
+		inq80 = NULL;
+	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, probe,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "inquiry-page-83",
+	    &inq83, &inq83len) != DDI_PROP_SUCCESS)
+		inq83 = NULL;
+
+	/* Get "scsi-binding-set" property (if there is one). */
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, self,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
+	    "scsi-binding-set", &binding_set) == DDI_PROP_SUCCESS)
+		SCSI_HBA_LOG((_LOG(2), NULL, probe,
+		    "binding_set '%s'", binding_set));
+
+	/* determine the node name and compatible information */
+	scsi_hba_ident_nodename_compatible_get(inq,
+	    inq80, inq80len, inq83, inq83len, binding_set, dtype_node,
+	    compat0, &nname, &dname, &compat, &ncompat);
+
+	if (nname == NULL) {
+		/*
+		 * We will not be able to create a node because we could not
+		 * determine a node name. Print out a NODRIVER level warning
+		 * message with the compatible forms for the device. Note that
+		 * there may be a driver.conf node that attaches to the device,
+		 * which is why we only produce this warning message for debug
+		 * kernels.
+		 */
+		SCSI_HBA_LOG((_LOG(1), NULL, self,
+		    "no node_name for device @%s:\n         compatible: %s",
+		    addr, *compat));
+		goto out;
+	}
+
+	/*
+	 * FUTURE: some day we may want an accurate "compatible" on the probe
+	 * node so that vhci_is_dev_supported() in scsi_vhci could, at
+	 * least in part, determine/configure based on "compatible".
+	 *
+	 *	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, probe,
+	 *	    "compatible", compat, ncompat) != DDI_PROP_SUCCESS) {
+	 *		SCSI_HBA_LOG((_LOG(3), self, NULL,
+	 *		    "%s@%s failed probe compatible decoration",
+	 *		    nname, addr));
+	 *		goto out;
+	 *	}
+	 */
+
+	/* Encode devid from identity information. */
+	if (ddi_devid_scsi_encode(DEVID_SCSI_ENCODE_VERSION_LATEST, dname,
+	    (uchar_t *)inq, sizeof (*inq), inq80, inq80len, inq83, inq83len,
+	    &devid) == DDI_SUCCESS) {
+		have_devid = 1;
+
+		/* Attempt to form guid from devid. */
+		guid = ddi_devid_to_guid(devid);
+
+		/* Produce string devid for debug. */
+		devid_str = ddi_devid_str_encode(devid, NULL);
+		SCSI_HBA_LOG((_LOG(3), self, probe, "devid '%s' guid '%s'",
+		    devid_str ? devid_str : "NULL", guid ? guid : "NULL"));
+		ddi_devid_str_free(devid_str);
+	}
+
+
+	/*
+	 * Determine if the device should be enumerated as under the vHCI
+	 * (client node) or under the pHCI. By convention scsi_vhci expects
+	 * the "cinfo" argument identity information to be represented as a
+	 * devinfo node with the needed information (i.e. the pHCI probe node).
+	 */
+	if ((guid == NULL) ||
+	    (mdi_is_dev_supported(MDI_HCI_CLASS_SCSI, self, sdprobe) !=
+	    MDI_SUCCESS)) {
+		SCSI_HBA_LOG((_LOG(3), self, probe, "==> devinfo"));
+
+		/*
+		 * Enumerate under pHCI:
+		 *
+		 * Create dynamic SID dchild node. No attempt is made to
+		 * transfer information (except the addressing and identity
+		 * information) from the probe node to the dynamic node since
+		 * there may be HBA specific side effects that the framework
+		 * does not known how to transfer.
+		 */
+		ndi_devi_alloc_sleep(self, nname,
+		    (se == SE_HP) ? DEVI_SID_HP_NODEID : DEVI_SID_NODEID,
+		    &dchild);
+		ASSERT(dchild);
+
+		/*
+		 * Decorate new node with addressing properties (via
+		 * scsi_hba_ua_set()), compatible, identity information, and
+		 * class.
+		 */
+		if ((scsi_hba_ua_set(addr, dchild, NULL) == 0) ||
+		    (ndi_prop_update_string_array(DDI_DEV_T_NONE, dchild,
+		    "compatible", compat, ncompat) != DDI_PROP_SUCCESS) ||
+		    (inq80 && (ndi_prop_update_byte_array(DDI_DEV_T_NONE,
+		    dchild, "inquiry-page-80", inq80, inq80len) !=
+		    DDI_PROP_SUCCESS)) ||
+		    (inq83 && (ndi_prop_update_byte_array(DDI_DEV_T_NONE,
+		    dchild, "inquiry-page-83", inq83, inq83len) !=
+		    DDI_PROP_SUCCESS)) ||
+		    (ndi_prop_update_string(DDI_DEV_T_NONE, dchild,
+		    "class", "scsi") != DDI_PROP_SUCCESS)) {
+			SCSI_HBA_LOG((_LOG(2), self, NULL,
+			    "@%s failed devinfo decoration", addr));
+			(void) scsi_hba_remove_node(dchild);
+			dchild = NULL;
+			goto out;
+		}
+
+		/* Bind the driver */
+		if (ndi_devi_bind_driver(dchild, 0) != NDI_SUCCESS) {
+			/* need to bind in order to register a devid */
+			SCSI_HBA_LOG((_LOGCFG, NULL, dchild,
+			    "devinfo @%s created, no driver-> "
+			    "no devid_register", addr));
+			goto out;
+		}
+
+		/* Register devid */
+		if (have_devid) {
+			if (ddi_devid_register(dchild, devid) == DDI_FAILURE)
+				SCSI_HBA_LOG((_LOG(1), NULL, dchild,
+				    "devinfo @%s created, devid failed", addr));
+			else
+				SCSI_HBA_LOG((_LOG(2), NULL, dchild,
+				    "devinfo @%s created devid", addr));
+		} else
+			SCSI_HBA_LOG((_LOG(2), NULL, dchild,
+			    "devinfo @%s created, no devid", addr));
+	} else {
+		/*
+		 * Enumerate under vHCI:
+		 *
+		 * Create a pathinfo pchild node.
+		 */
+		SCSI_HBA_LOG((_LOG(3), self, probe, "==>pathinfo"));
+
+		if (mdi_pi_alloc_compatible(self, nname, guid, addr, compat,
+		    ncompat, 0, &pchild) != MDI_SUCCESS) {
+			SCSI_HBA_LOG((_LOG(2), self, probe,
+			    "pathinfo alloc failed"));
+			goto out;
+		}
+		ASSERT(pchild);
+
+		/*
+		 * Decorate new node with addressing properties via
+		 * scsi_hba_ua_set().
+		 */
+		if (scsi_hba_ua_set(addr, NULL, pchild) == 0) {
+			SCSI_HBA_LOG((_LOG(1), self, NULL,
+			    "%s pathinfo decoration failed",
+			    mdi_pi_spathname(pchild)));
+
+			/* For hotplug, path exists even on failure. */
+			if (se != SE_HP)
+				(void) mdi_pi_free(pchild, 0);
+			goto out;
+		}
+	}
+
+	/* free the node name and compatible information */
+out:	if (have_devid)
+		ddi_devid_free(devid);
+	if (guid)
+		ddi_devid_free_guid(guid);
+	if (compat)
+		scsi_hba_ident_nodename_compatible_free(nname, dname, compat);
+	if (inq80)
+		ddi_prop_free(inq80);
+	if (inq83)
+		ddi_prop_free(inq83);
+	if (binding_set)
+		ddi_prop_free(binding_set);
+
+	/* return child_type results */
+	ASSERT(!(dchild && pchild));
+	if (dchild) {
+		*dchildp = dchild;
+		*pchildp = NULL;
+		return (CHILD_TYPE_DEVINFO);
+	}
+	if (pchild) {
+		*dchildp = NULL;
+		*pchildp = pchild;
+		return (CHILD_TYPE_PATHINFO);
+	}
+	return (CHILD_TYPE_NONE);
+}
+
+/*
+ * Call scsi_device_createchild and then initchild the new node.
+ */
+static dev_info_t *
+scsi_device_configchild(dev_info_t *self, char *addr, scsi_enum_t se,
+    struct scsi_device *sdprobe, int *circp, int *ppi)
+{
+	int		child_type;
+	dev_info_t	*dchild;
+	mdi_pathinfo_t	*pchild;
+	dev_info_t	*child;
+	int		rval;
+
+	ASSERT(self && addr && *addr && DEVI_BUSY_OWNED(self));
+	if (ppi)
+		*ppi = 0;
+
+	child_type = scsi_device_createchild(self, addr, se, sdprobe,
+	    &dchild, &pchild);
+
+	/*
+	 * Prevent multiple initialized (tran_tgt_init) nodes associated with
+	 * the same @addr at the same time by calling tran_tgt_free() on the
+	 * probe node prior to promotion of the 'real' node.  After the call
+	 * to scsi_hba_barrier_tran_tgt_free(), the HBA no longer has any
+	 * probe node context.
+	 */
+	scsi_hba_barrier_tran_tgt_free(sdprobe->sd_dev);
+
+	switch (child_type) {
+	case CHILD_TYPE_NONE:
+		child = NULL;
+		break;
+
+	case CHILD_TYPE_PATHINFO:
+		/*
+		 * Online pathinfo: Hold the path and exit the pHCI while
+		 * calling mdi_pi_online() to avoid deadlock with power
+		 * management of pHCI.
+		 */
+		ASSERT(MDI_PHCI(self));
+		mdi_hold_path(pchild);
+		scsi_hba_devi_exit_phci(self, *circp);
+
+		rval = mdi_pi_online(pchild, 0);
+
+		scsi_hba_devi_enter_phci(self, circp);
+		mdi_rele_path(pchild);
+
+		if (rval != MDI_SUCCESS) {
+			SCSI_HBA_LOG((_LOG(2), self, NULL,
+			    "%s pathinfo online failed",
+			    mdi_pi_spathname(pchild)));
+
+			/* For hotplug, path exists even when online fails */
+			if (se != SE_HP)
+				(void) mdi_pi_free(pchild, 0);
+			return (NULL);
+		}
+
+		/*
+		 * Return the path_instance of the pathinfo node.
+		 *
+		 * NOTE: We assume that sd_inq is not path-specific.
+		 */
+		if (ppi)
+			*ppi = mdi_pi_get_path_instance(pchild);
+
+
+		/*
+		 * Fallthrough into CHILD_TYPE_DEVINFO code to promote
+		 * the 'client' devinfo node as a dchild.
+		 */
+		dchild = mdi_pi_get_client(pchild);
+		SCSI_HBA_LOG((_LOG(4), NULL, dchild,
+		    "pathinfo online successful"));
+		/* FALLTHROUGH */
+
+	case CHILD_TYPE_DEVINFO:
+		/*
+		 * For now, we ndi_devi_online() the child because some other
+		 * parts of the IO framework, like degenerate devid code,
+		 * depend on bus_config driving nodes to DS_ATTACHED. At some
+		 * point in the future, to keep things light-weight, we would
+		 * like to change the ndi_devi_online call below to be
+		 *
+		 *	if (ddi_initchild(self, dchild) != DDI_SUCCESS)
+		 *
+		 * This would promote the node so that framework code could
+		 * find the child with an @addr search, but does not incur
+		 * attach(9E) overhead for BUS_CONFIG_ALL cases where the
+		 * framework is not interested in attach of the node.
+		 *
+		 * NOTE: If the addr specified has incorrect syntax (busconfig
+		 * one of bogus /devices path) then call below can fail.
+		 */
+		if (ndi_devi_online(dchild, 0) != NDI_SUCCESS) {
+			SCSI_HBA_LOG((_LOG(2), NULL, dchild,
+			    "devinfo online failed"));
+
+			/* failed online does not remove the node */
+			(void) scsi_hba_remove_node(dchild);
+			return (NULL);
+		}
+		SCSI_HBA_LOG((_LOG(4), NULL, dchild,
+		    "devinfo initchild successful"));
+		child = dchild;
+		break;
+	}
+	return (child);
+}
+
+void
+scsi_hba_pkt_comp(struct scsi_pkt *pkt)
+{
+	scsi_hba_tran_t	*tran;
+	uint8_t		*sensep;
+
+	ASSERT(pkt);
+	if (pkt->pkt_comp == NULL)
+		return;
+
+	/*
+	 * For HBA drivers that implement tran_setup_pkt(9E), if we are
+	 * completing a 'consistent' mode DMA operation then we must
+	 * perform dma_sync prior to calling pkt_comp to ensure that
+	 * the target driver sees the correct data in memory.
+	 */
+	ASSERT((pkt->pkt_flags & FLAG_NOINTR) == 0);
+	if (((pkt->pkt_dma_flags & DDI_DMA_CONSISTENT) &&
+	    (pkt->pkt_dma_flags & DDI_DMA_READ)) &&
+	    ((P_TO_TRAN(pkt)->tran_setup_pkt) != NULL)) {
+		scsi_sync_pkt(pkt);
+	}
+
+	/*
+	 * If the HBA driver is using SCSAv3 scsi_hba_tgtmap_create enumeration
+	 * then we detect the special ASC/ASCQ completion codes that indicate
+	 * that the lun configuration of a target has changed. Since we need to
+	 * be determine scsi_device given scsi_address enbedded in
+	 * scsi_pkt (via scsi_address_device(9F)), we also require use of
+	 * SCSI_HBA_ADDR_COMPLEX.
+	 */
+	tran = pkt->pkt_address.a_hba_tran;
+	ASSERT(tran);
+	if ((tran->tran_tgtmap == NULL) ||
+	    !(tran->tran_hba_flags & SCSI_HBA_ADDR_COMPLEX))
+		goto comp;		/* not using tgtmap */
+
+	/*
+	 * Check for lun-change notification and queue the scsi_pkt for
+	 * lunchg1 processing. The 'pkt_comp' call to the target driver
+	 * is part of lunchg1 processing.
+	 */
+	if ((pkt->pkt_reason == CMD_CMPLT) &&
+	    (((*pkt->pkt_scbp) & STATUS_MASK) == STATUS_CHECK) &&
+	    (pkt->pkt_state & STATE_ARQ_DONE)) {
+		sensep = (uint8_t *)&(((struct scsi_arq_status *)(uintptr_t)
+		    (pkt->pkt_scbp))->sts_sensedata);
+		if (((scsi_sense_key(sensep) == KEY_UNIT_ATTENTION) &&
+		    (scsi_sense_asc(sensep) == 0x3f) &&
+		    (scsi_sense_ascq(sensep) == 0x0e)) ||
+
+		    ((scsi_sense_key(sensep) == KEY_UNIT_ATTENTION) &&
+		    (scsi_sense_asc(sensep) == 0x25) &&
+		    (scsi_sense_ascq(sensep) == 0x00))) {
+			/*
+			 * The host adaptor is done with the packet, we use
+			 * pkt_stmp stage-temporary to link the packet for
+			 * lunchg1 processing.
+			 *
+			 * NOTE: pkt_ha_private is not available since its use
+			 * extends to tran_teardown_pkt.
+			 */
+			mutex_enter(&scsi_lunchg1_mutex);
+			pkt->pkt_stmp = scsi_lunchg1_list;
+			scsi_lunchg1_list = pkt;
+			if (pkt->pkt_stmp == NULL)
+				(void) cv_signal(&scsi_lunchg1_cv);
+			mutex_exit(&scsi_lunchg1_mutex);
+			return;
+		}
+	}
+
+comp:	(*pkt->pkt_comp)(pkt);
+}
+
+/*
+ * return 1 if the specified node is a barrier/probe node
+ */
+static int
+scsi_hba_devi_is_barrier(dev_info_t *probe)
+{
+	if (probe && (strcmp(ddi_node_name(probe), "probe") == 0))
+		return (1);
+	return (0);
+}
+
+/*
+ * A host adapter driver is easier to write if we prevent multiple initialized
+ * (tran_tgt_init) scsi_device structures to the same unit-address at the same
+ * time.  We prevent this from occurring all the time during the barrier/probe
+ * node to real child hand-off by calling scsi_hba_barrier_tran_tgt_free
+ * on the probe node prior to ddi_inichild of the 'real' node.  As part of
+ * this early tran_tgt_free implementation, we must also call this function
+ * as we put a probe node on the scsi_hba_barrier_list.
+ */
+static void
+scsi_hba_barrier_tran_tgt_free(dev_info_t *probe)
+{
+	struct scsi_device	*sdprobe;
+	dev_info_t		*self;
+	scsi_hba_tran_t		*tran;
+
+	ASSERT(probe && scsi_hba_devi_is_barrier(probe));
+
+	/* Return if we never called tran_tgt_init(9E). */
+	if (i_ddi_node_state(probe) < DS_INITIALIZED)
+		return;
+
+	sdprobe = ddi_get_driver_private(probe);
+	self = ddi_get_parent(probe);
+	ASSERT(sdprobe && self);
+	tran = ddi_get_driver_private(self);
+	ASSERT(tran);
+
+	if (tran->tran_tgt_free) {
+		/*
+		 * To correctly support TRAN_CLONE, we need to use the same
+		 * cloned scsi_hba_tran(9S) structure for both tran_tgt_init(9E)
+		 * and tran_tgt_free(9E).
+		 */
+		if (tran->tran_hba_flags & SCSI_HBA_TRAN_CLONE)
+			tran = sdprobe->sd_address.a_hba_tran;
+		SCSI_HBA_LOG((_LOG(4), NULL, probe, "tran_tgt_free EARLY"));
+
+		(*tran->tran_tgt_free) (self, probe, tran, sdprobe);
+	}
+}
+
+/*
+ * Add an entry to the list of barrier nodes to be asynchronously deleted by
+ * the scsi_hba_barrier_daemon after the specified timeout. Nodes on
+ * the barrier list are used to implement the bus_config probe cache
+ * of non-existent devices. The nodes are at DS_INITIALIZED, so their
+ * @addr is established for searching. Since devi_ref of a DS_INITIALIZED
+ * node will *not* prevent demotion, demotion is prevented by setting
+ * sd_uninit_prevent. Devinfo snapshots attempt to attach probe cache
+ * nodes, and on failure attempt to demote the node (without the participation
+ * of bus_unconfig) to DS_BOUND - this demotion is prevented via
+ * sd_uninit_prevent causing any attempted DDI_CTLOPS_UNINITCHILD to fail.
+ * Probe nodes are bound to nulldriver. The list is sorted by
+ * expiration time.
+ *
+ * NOTE: If we drove a probe node to DS_ATTACHED, we could use ndi_hold_devi()
+ * to prevent demotion (instead of sd_uninit_prevent).
+ */
+static void
+scsi_hba_barrier_add(dev_info_t *probe, int seconds)
+{
+	struct scsi_hba_barrier	*nb;
+	struct scsi_hba_barrier	*b;
+	struct scsi_hba_barrier	**bp;
+	clock_t			endtime;
+
+	ASSERT(scsi_hba_devi_is_barrier(probe));
+
+	/* HBA is no longer responsible for nodes on the barrier list. */
+	scsi_hba_barrier_tran_tgt_free(probe);
+
+	nb = kmem_alloc(sizeof (struct scsi_hba_barrier), KM_SLEEP);
+	mutex_enter(&scsi_hba_barrier_mutex);
+	endtime = ddi_get_lbolt() + drv_usectohz(seconds * MICROSEC);
+	for (bp = &scsi_hba_barrier_list; (b = *bp) != NULL;
+	    bp = &b->barrier_next)
+		if (b->barrier_endtime > endtime)
+			break;
+	nb->barrier_next = *bp;
+	nb->barrier_endtime = endtime;
+	nb->barrier_probe = probe;
+	*bp = nb;
+	if (bp == &scsi_hba_barrier_list)
+		(void) cv_signal(&scsi_hba_barrier_cv);
+	mutex_exit(&scsi_hba_barrier_mutex);
+}
+
+/*
+ * Attempt to remove devinfo node node, return 1 if removed. We don't
+ * don't try to remove barrier nodes that have sd_uninit_prevent set
+ * (even though they should fail device_uninitchild).
+ */
+static int
+scsi_hba_remove_node(dev_info_t *child)
+{
+	dev_info_t		*self = ddi_get_parent(child);
+	struct scsi_device	*sd;
+	int			circ;
+	int			remove = 1;
+	int			ret = 0;
+	char			na[SCSI_MAXNAMELEN];
+
+	scsi_hba_devi_enter(self, &circ);
+
+	/* Honor sd_uninit_prevent on barrier nodes */
+	if (scsi_hba_devi_is_barrier(child)) {
+		sd = ddi_get_driver_private(child);
+		if (sd && sd->sd_uninit_prevent)
+			remove = 0;
+	}
+
+	if (remove) {
+		(void) ddi_deviname(child, na);
+		if (ddi_remove_child(child, 0) != DDI_SUCCESS) {
+			SCSI_HBA_LOG((_LOG(2), NULL, child,
+			    "remove_node failed"));
+		} else {
+			child = NULL;		/* child is gone */
+			SCSI_HBA_LOG((_LOG(4), self, NULL,
+			    "remove_node removed %s", *na ? &na[1] : na));
+			ret = 1;
+		}
+	} else {
+		SCSI_HBA_LOG((_LOG(4), NULL, child, "remove_node prevented"));
+	}
+	scsi_hba_devi_exit(self, circ);
+	return (ret);
+}
+
+/*
+ * The asynchronous barrier deletion daemon. Waits for a barrier timeout
+ * to expire, then deletes the barrier (removes it as a child).
+ */
 /*ARGSUSED*/
+static void
+scsi_hba_barrier_daemon(void *arg)
+{
+	struct scsi_hba_barrier	*b;
+	dev_info_t		*probe;
+	callb_cpr_t		cprinfo;
+	int			circ;
+	dev_info_t		*self;
+
+	CALLB_CPR_INIT(&cprinfo, &scsi_hba_barrier_mutex,
+	    callb_generic_cpr, "scsi_hba_barrier_daemon");
+again:	mutex_enter(&scsi_hba_barrier_mutex);
+	for (;;) {
+		b = scsi_hba_barrier_list;
+		if (b == NULL) {
+			/* all barriers expired, wait for barrier_add */
+			CALLB_CPR_SAFE_BEGIN(&cprinfo);
+			(void) cv_wait(&scsi_hba_barrier_cv,
+			    &scsi_hba_barrier_mutex);
+			CALLB_CPR_SAFE_END(&cprinfo, &scsi_hba_barrier_mutex);
+		} else {
+			if (ddi_get_lbolt() >= b->barrier_endtime) {
+				/*
+				 * Drop and retry if ordering issue. Do this
+				 * before calling scsi_hba_remove_node() and
+				 * deadlocking.
+				 */
+				probe = b->barrier_probe;
+				self = ddi_get_parent(probe);
+				if (scsi_hba_devi_tryenter(self, &circ) == 0) {
+delay:					mutex_exit(&scsi_hba_barrier_mutex);
+					delay_random(5);
+					goto again;
+				}
+
+				/* process expired barrier */
+				if (!scsi_hba_remove_node(probe)) {
+					/* remove failed, delay and retry */
+					SCSI_HBA_LOG((_LOG(4), NULL, probe,
+					    "delay expire"));
+					scsi_hba_devi_exit(self, circ);
+					goto delay;
+				}
+				scsi_hba_barrier_list = b->barrier_next;
+				kmem_free(b, sizeof (struct scsi_hba_barrier));
+				scsi_hba_devi_exit(self, circ);
+			} else {
+				/* establish timeout for next barrier expire */
+				(void) cv_timedwait(&scsi_hba_barrier_cv,
+				    &scsi_hba_barrier_mutex,
+				    b->barrier_endtime);
+			}
+		}
+	}
+}
+
+/*
+ * Remove all barriers associated with the specified HBA. This is called
+ * from from the bus_unconfig implementation to remove probe nodes associated
+ * with the specified HBA (self) so that probe nodes that have not expired
+ * will not prevent DR of the HBA.
+ */
+static void
+scsi_hba_barrier_purge(dev_info_t *self)
+{
+	struct scsi_hba_barrier	**bp;
+	struct scsi_hba_barrier	*b;
+
+	mutex_enter(&scsi_hba_barrier_mutex);
+	for (bp = &scsi_hba_barrier_list; (b = *bp) != NULL; ) {
+		if (ddi_get_parent(b->barrier_probe) == self) {
+			if (scsi_hba_remove_node(b->barrier_probe)) {
+				*bp = b->barrier_next;
+				kmem_free(b, sizeof (struct scsi_hba_barrier));
+			} else {
+				SCSI_HBA_LOG((_LOG(4), NULL, b->barrier_probe,
+				    "skip purge"));
+			}
+		} else
+			bp = &b->barrier_next;
+	}
+
+	mutex_exit(&scsi_hba_barrier_mutex);
+}
+
+/*
+ * LUN-change processing daemons: processing occurs in two stages:
+ *
+ * Stage 1:	Daemon waits for a lunchg1 queued scsi_pkt, dequeues the pkt,
+ *		forms the path, completes the scsi_pkt (pkt_comp), and
+ *		queues the path for stage 2 processing. The use of stage 1
+ *		avoids issues related to memory allocation in interrupt context
+ *		(scsi_hba_pkt_comp()). We delay the pkt_comp completion until
+ *		after lunchg1 processing forms the path for stage 2 - this is
+ *		done to prevent the target driver from detaching until the
+ *		path formation is complete (driver with outstanding commands
+ *		should not detach).
+ *
+ * Stage 2:	Daemon waits for a lunchg2 queued request, dequeues the
+ *		request, and opens the path using ldi_open_by_name(). The
+ *		path opened uses a special "@taddr,*" unit address that will
+ *		trigger lun enumeration in scsi_hba_bus_configone(). We
+ *		trigger lun enumeration in stage 2 to avoid problems when
+ *		initial ASC/ASCQ trigger occurs during discovery.
+ */
+/*ARGSUSED*/
+static void
+scsi_lunchg1_daemon(void *arg)
+{
+	callb_cpr_t		cprinfo;
+	struct scsi_pkt		*pkt;
+	scsi_hba_tran_t		*tran;
+	dev_info_t		*self;
+	struct scsi_device	*sd;
+	char			*ua, *p;
+	char			taddr[SCSI_MAXNAMELEN];
+	char			path[MAXPATHLEN];
+	struct scsi_lunchg2	*lunchg2;
+
+	CALLB_CPR_INIT(&cprinfo, &scsi_lunchg1_mutex,
+	    callb_generic_cpr, "scsi_lunchg1_daemon");
+	mutex_enter(&scsi_lunchg1_mutex);
+	for (;;) {
+		pkt = scsi_lunchg1_list;
+		if (pkt == NULL) {
+			/* All lunchg1 processing requests serviced, wait. */
+			CALLB_CPR_SAFE_BEGIN(&cprinfo);
+			(void) cv_wait(&scsi_lunchg1_cv,
+			    &scsi_lunchg1_mutex);
+			CALLB_CPR_SAFE_END(&cprinfo, &scsi_lunchg1_mutex);
+			continue;
+		}
+
+		/* Unlink and perform lunchg1 processing on pkt. */
+		scsi_lunchg1_list = pkt->pkt_stmp;
+
+		/* Determine initiator port (self) from the pkt_address. */
+		tran = pkt->pkt_address.a_hba_tran;
+		ASSERT(tran && tran->tran_tgtmap && tran->tran_iport_dip);
+		self = tran->tran_iport_dip;
+
+		/*
+		 * Determine scsi_devie from pkt_address (depends on
+		 * SCSI_HBA_ADDR_COMPLEX).
+		 */
+		sd = scsi_address_device(&(pkt->pkt_address));
+		ASSERT(sd);
+		if (sd == NULL) {
+			(*pkt->pkt_comp)(pkt);
+			continue;
+		}
+
+		/* Determine unit-address from scsi_device. */
+		ua = scsi_device_unit_address(sd);
+
+		/* Extract taddr from the unit-address. */
+		for (p = taddr; (*ua != ',') && (*ua != '\0'); )
+			*p++ = *ua++;
+		*p = '\0';			/* NULL terminate taddr */
+
+		/*
+		 * Form path using special "@taddr,*" notation to trigger
+		 * lun enumeration.
+		 */
+		(void) ddi_pathname(self, path);
+		(void) strcat(path, "/luns@");
+		(void) strcat(path, taddr);
+		(void) strcat(path, ",*");
+
+		/*
+		 * Now that we have the path, complete the pkt that
+		 * triggered lunchg1 processing.
+		 */
+		(*pkt->pkt_comp)(pkt);
+
+		/* Allocate element for stage2 processing queue. */
+		lunchg2 = kmem_alloc(sizeof (*lunchg2), KM_SLEEP);
+		lunchg2->lunchg2_path = strdup(path);
+
+		/* Queue and dispatch to stage 2. */
+		SCSI_HBA_LOG((_LOG(2), self, NULL,
+		    "lunchg stage1: queue %s", lunchg2->lunchg2_path));
+		mutex_enter(&scsi_lunchg2_mutex);
+		lunchg2->lunchg2_next = scsi_lunchg2_list;
+		scsi_lunchg2_list = lunchg2;
+		if (lunchg2->lunchg2_next == NULL)
+			(void) cv_signal(&scsi_lunchg2_cv);
+		mutex_exit(&scsi_lunchg2_mutex);
+	}
+}
+
+/*ARGSUSED*/
+static void
+scsi_lunchg2_daemon(void *arg)
+{
+	callb_cpr_t		cprinfo;
+	struct scsi_lunchg2	*lunchg2;
+	ldi_ident_t		li;
+	ldi_handle_t		lh;
+
+	CALLB_CPR_INIT(&cprinfo, &scsi_lunchg2_mutex,
+	    callb_generic_cpr, "scsi_lunchg2_daemon");
+
+	li = ldi_ident_from_anon();
+	mutex_enter(&scsi_lunchg2_mutex);
+	for (;;) {
+		lunchg2 = scsi_lunchg2_list;
+		if (lunchg2 == NULL) {
+			/* All lunchg2 processing requests serviced, wait. */
+			CALLB_CPR_SAFE_BEGIN(&cprinfo);
+			(void) cv_wait(&scsi_lunchg2_cv,
+			    &scsi_lunchg2_mutex);
+			CALLB_CPR_SAFE_END(&cprinfo, &scsi_lunchg2_mutex);
+			continue;
+		}
+
+		/* Unlink and perform lunchg2 processing on pkt. */
+		scsi_lunchg2_list = lunchg2->lunchg2_next;
+
+		/*
+		 * Open and close the path to trigger lun enumeration.  We
+		 * don't expect the open to succeed, but we do expect code in
+		 * scsi_hba_bus_configone() to trigger lun enumeration.
+		 */
+		SCSI_HBA_LOG((_LOG(2), NULL, NULL,
+		    "lunchg stage2: open %s", lunchg2->lunchg2_path));
+		if (ldi_open_by_name(lunchg2->lunchg2_path,
+		    FREAD, kcred, &lh, li) == 0)
+			(void) ldi_close(lh, FREAD, kcred);
+
+		/* Free path and linked element. */
+		strfree(lunchg2->lunchg2_path);
+		kmem_free(lunchg2, sizeof (*lunchg2));
+	}
+}
+
+/*
+ * Enumerate a child at the specified @addr. If a device exists @addr then
+ * ensure that we have the appropriately named devinfo node for it. Name is
+ * NULL in the bus_config_all case. This routine has no knowledge of the
+ * format of an @addr string or associated addressing properties.
+ *
+ * The caller must guarantee that there is an open scsi_hba_devi_enter on the
+ * parent. We return the scsi_device structure for the child device. This
+ * scsi_device structure is valid until the caller scsi_hba_devi_exit the
+ * parent. The caller can add do ndi_hold_devi of the child prior to the
+ * scsi_hba_devi_exit to extend the validity of the child.
+ *
+ * In some cases the returned scsi_device structure may be used to drive
+ * additional SCMD_REPORT_LUNS operations by bus_config_all callers.
+ *
+ * The first operation performed is to see if there is a dynamic SID nodes
+ * already attached at the specified "name@addr". This is the fastpath
+ * case for resolving a reference to a node that has already been created.
+ * All other references are serialized for a given @addr prior to probing
+ * to determine the type of device, if any, at the specified @addr.
+ * If no device is present then NDI_FAILURE is returned. The fact that a
+ * device does not exist may be determined via the barrier/probe cache,
+ * minimizing the probes of non-existent devices.
+ *
+ * When there is a device present the dynamic SID node is created based on
+ * the device found. If a driver.conf node exists for the same @addr it
+ * will either merge into the dynamic SID node (if the SID node bound to
+ * that driver), or exist independently. To prevent the actions of one driver
+ * causing side effects in another, code prevents multiple SID nodes from
+ * binding to the same "@addr" at the same time. There is autodetach code
+ * to allow one device to be replaced with another at the same @addr for
+ * slot addressed SCSI bus implementations (SPI). For compatibility with
+ * legacy driver.conf behavior, the code does not prevent multiple driver.conf
+ * nodes from attaching to the same @addr at the same time.
+ *
+ * This routine may have the side effect of creating nodes for devices other
+ * than the one being sought. It is possible that there is a different type of
+ * target device at that target/lun address than we were asking for. In that
+ * It is the caller's responsibility to determine whether the device we found,
+ * if any, at the specified address, is the one it really wanted.
+ */
+static struct scsi_device *
+scsi_device_config(dev_info_t *self, char *name, char *addr, scsi_enum_t se,
+    int *circp, int *ppi)
+{
+	dev_info_t		*child = NULL;
+	dev_info_t		*probe = NULL;
+	struct scsi_device	*sdchild;
+	struct scsi_device	*sdprobe;
+	dev_info_t		*dsearch;
+	mdi_pathinfo_t		*psearch;
+	major_t			major;
+	int			sp;
+	int			pi = 0;
+	int			wait_msg = scsi_hba_wait_msg;
+	int			chg;
+
+	ASSERT(self && addr && DEVI_BUSY_OWNED(self));
+
+	SCSI_HBA_LOG((_LOG(4), self, NULL, "%s@%s wanted",
+	    name ? name : "", addr));
+
+	/* playing with "probe" node name is dangerous */
+	if (name && (strcmp(name, "probe") == 0))
+		return (NULL);
+
+	/*
+	 * NOTE: use 'goto done;' or 'goto fail;'. There should only be one
+	 * 'return' statement from here to the end of the function - the one
+	 * on the last line of the function.
+	 */
+
+	/*
+	 * Fastpath: search to see if we are requesting a named SID node that
+	 * already exists (we already created) - probe node does not count.
+	 * scsi_hba_find_child() does not hold the returned devinfo node, but
+	 * this is OK since the caller has a scsi_hba_devi_enter on the
+	 * attached parent HBA (self). The caller is responsible for attaching
+	 * and placing a hold on the child (directly via ndi_hold_devi or
+	 * indirectly via ndi_busop_bus_config) before doing an
+	 * scsi_hba_devi_exit on the parent.
+	 *
+	 * NOTE: This fastpath prevents detecting a driver binding change
+	 * (autodetach) if the same nodename is used for old and new binding.
+	 */
+	/* first call is with init set */
+	(void) scsi_hba_find_child(self, name, addr,
+	    1, &dsearch, NULL, &pi);
+	if (dsearch && scsi_hba_dev_is_sid(dsearch) &&
+	    !scsi_hba_devi_is_barrier(dsearch)) {
+		SCSI_HBA_LOG((_LOG(4), NULL, dsearch,
+		    "%s@%s devinfo fastpath", name ? name : "", addr));
+		child = dsearch;
+		goto done;
+	}
+
+	/*
+	 * Create a barrier devinfo node used to "probe" the device with. We
+	 * need to drive this node to DS_INITIALIZED so that the
+	 * DDI_CTLOPS_INITCHILD has occurred, bringing the SCSA transport to
+	 * a state useable state for issuing our "probe" commands. We establish
+	 * this barrier node with a node name of "probe" and compatible
+	 * property of "scsiprobe". The compatible property must be associated
+	 * in /etc/driver_aliases with a scsi target driver available in the
+	 * root file system (sd).
+	 *
+	 * The "probe" that we perform on the barrier node, after it is
+	 * DS_INITIALIZED, is used to find the information needed to create a
+	 * dynamic devinfo (SID) node. This "probe" is separate from the
+	 * probe(9E) call associated with the transition of a node from
+	 * DS_INITIALIZED to DS_PROBED. The probe(9E) call that eventually
+	 * occurs against the created SID node should find ddi_dev_is_sid and
+	 * just return DDI_PROBE_DONTCARE.
+	 *
+	 * Trying to avoid the use of a barrier node is not a good idea
+	 * because we may have an HBA driver that uses generic bus_config
+	 * (this code) but implements its own DDI_CTLOPS_INITCHILD with side
+	 * effects that we can't duplicate (such as the ATA nexus driver).
+	 *
+	 * The probe/barrier node plays an integral part of the locking scheme.
+	 * The objective is to single thread probes of the same device (same
+	 * @addr) while allowing parallelism for probes of different devices
+	 * with the same parent. At this point we are serialized on our self.
+	 * For parallelism we will need to release our self. Prior to release
+	 * we construct a barrier for probes of the same device to serialize
+	 * against. The "probe@addr" node acts as this barrier. An entering
+	 * thread must wait until the probe node does not exist - it can then
+	 * create and link the probe node - dropping the HBA (self) lock after
+	 * the node is linked and visible (after ddi_initchild). A side effect
+	 * of this is that transports should not "go over the wire" (i.e. do
+	 * things that incur significant delays) until after tran_target_init.
+	 * This means that the first "over the wire" operation should occur
+	 * at tran_target_probe time - when things are running in parallel
+	 * again.
+	 *
+	 * If the probe node exists then another probe with the same @addr is
+	 * in progress, we must wait until there is no probe in progress
+	 * before proceeding, and when we proceed we must continue to hold the
+	 * HBA (self) until we have linked a new probe node as a barrier.
+	 *
+	 * When a device is found to *not* exist, its probe/barrier node may be
+	 * marked with DEVICE_REMOVED with node deletion scheduled for some
+	 * future time (seconds). This asynchronous deletion allows the
+	 * framework to detect repeated requests to the same non-existent
+	 * device and avoid overhead associated with contacting a non-existent
+	 * device again and again.
+	 */
+	for (;;) {
+		/*
+		 * Search for probe node - they should only exist as devinfo
+		 * nodes.
+		 */
+		(void) scsi_hba_find_child(self, "probe", addr,
+		    0, &probe, &psearch, NULL);
+		if (probe == NULL) {
+			if (psearch)
+				SCSI_HBA_LOG((_LOG(2), self,
+				    mdi_pi_get_client(psearch),
+				    "???? @%s 'probe' search found "
+				    "pathinfo: %p", addr, (void *)psearch));
+			break;
+		}
+
+		/*
+		 * The barrier node may cache the non-existence of a device
+		 * by leaving the barrier node in place (with
+		 * DEVI_DEVICE_REMOVED flag set ) for some amount of time after
+		 * the failure of a probe. This flag is used to fail
+		 * additional probes until the barrier probe node is deleted,
+		 * which will occur from a timeout some time after a failed
+		 * probe. The failed probe will use DEVI_SET_DEVICE_REMOVED
+		 * and schedule probe node deletion from a timeout. The callers
+		 * scsi_hba_devi_exit on the way out of the first failure will
+		 * do the cv_broadcast associated with the cv_wait below - this
+		 * handles threads that wait prior to DEVI_DEVICE_REMOVED being
+		 * set.
+		 */
+		if (DEVI_IS_DEVICE_REMOVED(probe)) {
+			SCSI_HBA_LOG((_LOG(3), NULL, probe,
+			    "detected probe DEVICE_REMOVED"));
+			probe = NULL;	/* deletion already scheduled */
+			goto fail;
+		}
+
+		/*
+		 * Drop the lock on the HBA (self) and wait until the probe in
+		 * progress has completed. A changes in the sibling list from
+		 * removing the probe node will cause cv_wait to return
+		 * (scsi_hba_devi_exit does the cv_broadcast).
+		 */
+		if (wait_msg) {
+			wait_msg--;
+			SCSI_HBA_LOG((_LOG(2), NULL, probe,
+			    "exists, probe already in progress: %s", wait_msg ?
+			    "waiting..." : "last msg, but still waiting..."));
+		}
+
+		/*
+		 * NOTE: we could avoid rare case of one second delay by
+		 * implementing scsi_hba_devi_exit_and_wait based on
+		 * ndi/mdi_devi_exit_and_wait (and consider switching devcfg.c
+		 * code to use these ndi/mdi interfaces too).
+		 */
+		scsi_hba_devi_exit(self, *circp);
+		mutex_enter(&DEVI(self)->devi_lock);
+		(void) cv_timedwait(&DEVI(self)->devi_cv,
+		    &DEVI(self)->devi_lock,
+		    ddi_get_lbolt() + drv_usectohz(MICROSEC));
+		mutex_exit(&DEVI(self)->devi_lock);
+		scsi_hba_devi_enter(self, circp);
+	}
+	ASSERT(probe == NULL);
+
+	/*
+	 * Search to see if we are requesting a SID node that already exists.
+	 * We hold the HBA (self) and there is not another probe in progress at
+	 * the same @addr. scsi_hba_find_child() does not hold the returned
+	 * devinfo node but this is OK since we hold the HBA (self).
+	 */
+	if (name) {
+		(void) scsi_hba_find_child(self, name, addr,
+		    1, &dsearch, NULL, &pi);
+		if (dsearch && scsi_hba_dev_is_sid(dsearch)) {
+			SCSI_HBA_LOG((_LOG(4), NULL, dsearch,
+			    "%s@%s probe devinfo fastpath",
+			    name ? name : "", addr));
+			child = dsearch;
+			goto done;
+		}
+	}
+
+	/*
+	 * We are looking for a SID node that does not exist or a driver.conf
+	 * node.
+	 *
+	 * To avoid probe side effects, before we probe the device at the
+	 * specified address we need to check to see if there is already an
+	 * initialized child "@addr".
+	 *
+	 * o If we find an initialized SID child and name is NULL or matches
+	 *   the name or the name of the attached driver then we return the
+	 *   existing node.
+	 *
+	 * o If we find a non-matching SID node, we will attempt to autodetach
+	 *   and remove the node in preference to our new node.
+	 *
+	 * o If SID node found does not match and can't be autodetached, we
+	 *   fail: we only allow one SID node at an address.
+	 *
+	 * NOTE: This code depends on SID nodes showing up prior to
+	 * driver.conf nodes in the sibling list.
+	 */
+	for (;;) {
+		/* first NULL name call is with init set */
+		(void) scsi_hba_find_child(self, NULL, addr,
+		    1, &dsearch, NULL, &pi);
+		if (dsearch == NULL)
+			break;
+		ASSERT(!scsi_hba_devi_is_barrier(dsearch));
+
+		/*
+		 * To detect changes in driver binding that should attempt
+		 * autodetach we determine the major number of the driver
+		 * that should currently be associated with the device based
+		 * on the compatible property.
+		 */
+		major = DDI_MAJOR_T_NONE;
+		if (scsi_hba_dev_is_sid(dsearch))
+			major = ddi_compatible_driver_major(dsearch, NULL);
+		if ((major == DDI_MAJOR_T_NONE) && (name == NULL))
+			major = ddi_driver_major(dsearch);
+
+		if ((scsi_hba_dev_is_sid(dsearch) ||
+		    (i_ddi_node_state(dsearch) >= DS_INITIALIZED)) &&
+		    ((name == NULL) ||
+		    (strcmp(ddi_node_name(dsearch), name) == 0) ||
+		    (strcmp(ddi_driver_name(dsearch), name) == 0)) &&
+		    (major == ddi_driver_major(dsearch))) {
+			SCSI_HBA_LOG((_LOG(3), NULL, dsearch,
+			    "already attached @addr"));
+			child = dsearch;
+			goto done;
+		}
+
+		if (!scsi_hba_dev_is_sid(dsearch))
+			break;			/* driver.conf node */
+
+		/*
+		 * Implement autodetach of SID node for situations like a
+		 * previously "scsinodev" LUN0 coming into existence (or a
+		 * disk/tape on an SPI transport at same addr but never both
+		 * powered on at the same time). Try to autodetach the existing
+		 * SID node @addr. If that works, search again - otherwise fail.
+		 */
+		SCSI_HBA_LOG((_LOG(2), NULL, dsearch,
+		    "looking for %s@%s: SID @addr exists, autodetach",
+		    name ? name : "", addr));
+		if (!scsi_hba_remove_node(dsearch)) {
+			SCSI_HBA_LOG((_LOG(2), NULL, dsearch,
+			    "autodetach @%s failed: fail %s@%s",
+			    addr, name ? name : "", addr));
+			goto fail;
+		}
+		SCSI_HBA_LOG((_LOG(2), self, NULL, "autodetach @%s OK", addr));
+	}
+
+	/*
+	 * We will be creating a new SID node, allocate probe node
+	 * used to find out information about the device located @addr.
+	 * The probe node also acts as a barrier against additional
+	 * configuration at the same address, and in the case of non-existent
+	 * devices it will (for some amount of time) avoid re-learning that
+	 * the device does not exist on every reference. Once the probe
+	 * node is DS_LINKED we can drop the HBA (self).
+	 *
+	 * The probe node is allocated as a hidden node so that it does not
+	 * show up in devinfo snapshots.
+	 */
+	ndi_devi_alloc_sleep(self, "probe",
+	    (se == SE_HP) ? DEVI_SID_HP_HIDDEN_NODEID : DEVI_SID_HIDDEN_NODEID,
+	    &probe);
+	ASSERT(probe);
+
+	/*
+	 * Decorate the probe node with the property representation of @addr
+	 * unit-address string prior to initchild so that initchild can
+	 * construct the name of the node from properties and tran_tgt_init
+	 * implementation can determine what LUN is being referenced.
+	 *
+	 * If the addr specified has incorrect syntax (busconfig one of bogus
+	 * /devices path) then scsi_hba_ua_set can fail.  If the address
+	 * is not understood by the SCSA HBA driver then this operation will
+	 * work, but tran_tgt_init may still fail (for example the HBA
+	 * driver may not support secondary functions).
+	 */
+	if (scsi_hba_ua_set(addr, probe, NULL) == 0) {
+		SCSI_HBA_LOG((_LOG(2), NULL, probe,
+		    "@%s failed scsi_hba_ua_set", addr));
+		goto fail;
+	}
+
+	/*
+	 * Set the class property to "scsi". This is sufficient to distinguish
+	 * the node for HBAs that have multiple classes of children (like uata
+	 * - which has "dada" class for ATA children and "scsi" class for
+	 * ATAPI children) and may not use our scsi_busctl_initchild()
+	 * implementation. We also add a "compatible" property of "scsiprobe"
+	 * to select the probe driver.
+	 */
+	if ((ndi_prop_update_string(DDI_DEV_T_NONE, probe,
+	    "class", "scsi") != DDI_PROP_SUCCESS) ||
+	    (ndi_prop_update_string_array(DDI_DEV_T_NONE, probe,
+	    "compatible", &compatible_probe, 1) != DDI_PROP_SUCCESS)) {
+		SCSI_HBA_LOG((_LOG(1), NULL, probe,
+		    "@%s failed node decoration", addr));
+		goto fail;
+	}
+
+	/*
+	 * Promote probe node to DS_INITIALIZED so that transport can be used
+	 * for scsi_probe. After this the node is linked and visible as a
+	 * barrier for serialization of other @addr operations.
+	 *
+	 * NOTE: If we attached the probe node, we could get rid of
+	 * uninit_prevent.
+	 */
+	if (ddi_initchild(self, probe) != DDI_SUCCESS) {
+		SCSI_HBA_LOG((_LOG(2), NULL, probe,
+		    "@%s failed initchild", addr));
+
+		/* probe node will be removed in fail exit path */
+		goto fail;
+	}
+
+	/* get the scsi_device structure of the probe node */
+	sdprobe = ddi_get_driver_private(probe);
+	ASSERT(sdprobe);
+
+	/*
+	 * Do scsi_probe. The probe node is linked and visible as a barrier.
+	 * We prevent uninitialization of the probe node and drop our HBA (self)
+	 * while we run scsi_probe() of this "@addr". This allows the framework
+	 * to support multiple scsi_probes for different devices attached to
+	 * the same HBA (self) in parallel. We prevent node demotion of the
+	 * probe node from DS_INITIALIZED by setting sd_uninit_prevent. The
+	 * probe node can not be successfully demoted below DS_INITIALIZED
+	 * (scsi_busctl_uninitchild will fail) until we zero sd_uninit_prevent
+	 * as we are freeing the node via scsi_hba_remove_node(probe).
+	 */
+	sdprobe->sd_uninit_prevent++;
+	scsi_hba_devi_exit(self, *circp);
+	sp = scsi_probe(sdprobe, SLEEP_FUNC);
+
+	/* Introduce a small delay here to increase parallelism. */
+	delay_random(5);
+
+	if (sp == SCSIPROBE_EXISTS) {
+		/*
+		 * For a device that exists, while still running in parallel,
+		 * also get identity information from device. This is done
+		 * separate from scsi_probe/tran_tgt_probe/scsi_hba_probe
+		 * since the probe code path may still be used for HBAs
+		 * that don't use common bus_config services (we don't want
+		 * to expose that code path to a behavior change). This
+		 * operation is called 'identity' to avoid confusion with
+		 * deprecated identify(9E).
+		 *
+		 * Future: We may eventually want to allow HBA customization via
+		 * scsi_identity/tran_tgt_identity/scsi_device_identity, but for
+		 * now we just scsi_device_identity.
+		 *
+		 * The identity operation will establish additional properties
+		 * on the probe node related to device identity:
+		 *
+		 *	"inquiry-page-80"	byte array of SCSI page 80
+		 *	"inquiry-page-83"	byte array of SCSI page 83
+		 *
+		 * These properties will be used to generate a devid
+		 * (ddi_devid_scsi_encode) and guid - and to register
+		 * (ddi_devid_register) a devid for the device.
+		 *
+		 * If identify fails (non-zero return), the we had allocation
+		 * problems or the device returned inconsistent results then
+		 * we pretend that device does not exist.
+		 */
+		if (scsi_device_identity(sdprobe, SLEEP_FUNC)) {
+			if (ndi_dev_is_hotplug_node(probe))
+				SCSI_HBA_LOG((_LOG(WARN), NULL, probe,
+				    "enumeration failed during identity"));
+			else
+				SCSI_HBA_LOG((_LOG(2), NULL, probe,
+				    "enumeration failed during identity"));
+			sp = SCSIPROBE_FAILURE;
+		}
+
+		/*
+		 * Future: Is there anything more we can do here to help avoid
+		 * serialization on iport parent during scsi_device attach(9E)?
+		 */
+	}
+	scsi_hba_devi_enter(self, circp);
+	sdprobe->sd_uninit_prevent--;
+
+	if (sp != SCSIPROBE_EXISTS) {
+		if (ndi_dev_is_hotplug_node(probe))
+			SCSI_HBA_LOG((_LOG(WARN), NULL, probe,
+			    "enumeration failed during probe"));
+		else
+			SCSI_HBA_LOG((_LOG(2), NULL, probe,
+			    "enumeration failed durning probe"));
+
+		if (scsi_hba_barrier_timeout) {
+			/*
+			 * Target does not exist. Mark the barrier probe node
+			 * as DEVICE_REMOVED and schedule an asynchronous
+			 * deletion of the node in scsi_hba_barrier_timeout
+			 * seconds. We keep our hold on the probe node
+			 * until we are ready perform the asynchronous node
+			 * deletion.
+			 */
+			SCSI_HBA_LOG((_LOG(3), NULL, probe,
+			    "set probe DEVICE_REMOVED"));
+			mutex_enter(&DEVI(probe)->devi_lock);
+			DEVI_SET_DEVICE_REMOVED(probe);
+			mutex_exit(&DEVI(probe)->devi_lock);
+
+			scsi_hba_barrier_add(probe, scsi_hba_barrier_timeout);
+			probe = NULL;
+		}
+		goto fail;
+	}
+
+	/* Create the child node from the inquiry data in the probe node. */
+	if ((child = scsi_device_configchild(self, addr, se, sdprobe,
+	    circp, &pi)) == NULL) {
+		/*
+		 * This may fail because there was no driver binding identified
+		 * via driver_alias. We may still have a conf node.
+		 */
+		if (name) {
+			(void) scsi_hba_find_child(self, name, addr,
+			    0, &child, NULL, &pi);
+			if (child)
+				SCSI_HBA_LOG((_LOG(2), NULL, child,
+				    "using driver.conf driver binding"));
+		}
+		if (child == NULL) {
+			SCSI_HBA_LOG((_LOG(2), NULL, probe,
+			    "device not configured"));
+			goto fail;
+		}
+	}
+
+	/*
+	 * Transfer the inquiry data from the probe node to the child
+	 * SID node to avoid an extra scsi_probe. Callers depend on
+	 * established inquiry data for the returned scsi_device.
+	 */
+	sdchild = ddi_get_driver_private(child);
+	if (sdchild && (sdchild->sd_inq == NULL)) {
+		sdchild->sd_inq = sdprobe->sd_inq;
+		sdprobe->sd_inq = NULL;
+	}
+
+	/*
+	 * If we are doing a bus_configone and the node we created has the
+	 * wrong node and driver name then switch the return result to a
+	 * driver.conf node with the correct name - if such a node exists.
+	 */
+	if (name && (strcmp(ddi_node_name(child), name) != 0) &&
+	    (strcmp(ddi_driver_name(child), name) != 0)) {
+		(void) scsi_hba_find_child(self, name, addr,
+		    0, &dsearch, NULL, &pi);
+		if (dsearch == NULL) {
+			SCSI_HBA_LOG((_LOG(2), NULL, child,
+			    "wrong device configured %s@%s", name, addr));
+			/*
+			 * We can't remove when modrootloaded == 0 in case
+			 * boot-device a uses generic name and
+			 * scsi_hba_nodename_compatible_get() returned a
+			 * legacy binding-set driver oriented name.
+			 */
+			if (modrootloaded) {
+				(void) scsi_hba_remove_node(child);
+				child = NULL;
+				goto fail;
+			}
+		} else {
+			SCSI_HBA_LOG((_LOG(2), NULL, dsearch,
+			    "device configured, but switching to driver.conf"));
+			child = dsearch;
+		}
+	}
+
+	/* get the scsi_device structure from the node */
+	SCSI_HBA_LOG((_LOG(3), NULL, child, "device configured"));
+
+	if (child) {
+done:		ASSERT(child);
+		sdchild = ddi_get_driver_private(child);
+		ASSERT(sdchild);
+		/*
+		 * We may have ended up here after promotion of a previously
+		 * demoted node, where demotion deleted sd_inq data in
+		 * scsi_busctl_uninitchild.  We redo the scsi_probe() to
+		 * reestablish sd_inq.  We also want to redo the scsi_probe
+		 * for devices are currently device_isremove in order to
+		 * detect new device_insert.
+		 */
+		if ((sdchild->sd_inq == NULL) ||
+		    ndi_devi_device_isremoved(child)) {
+
+			/* hotplug_node can only be revived via hotplug. */
+			if ((se == SE_HP) || !ndi_dev_is_hotplug_node(child)) {
+				SCSI_HBA_LOG((_LOG(3), NULL, child,
+				    "scsi_probe() demoted devinfo"));
+
+				sp = scsi_probe(sdchild, SLEEP_FUNC);
+
+				if (sp == SCSIPROBE_EXISTS) {
+					ASSERT(sdchild->sd_inq);
+
+					/*
+					 * Devinfo child exists and we are
+					 * talking to the device, report
+					 * reinsert and note if this was a
+					 * new reinsert.
+					 */
+					chg = ndi_devi_device_insert(child);
+					SCSI_HBA_LOG((_LOGCFG, self, NULL,
+					    "devinfo %s@%s device_reinsert%s",
+					    name ? name : "", addr,
+					    chg ? "" : "ed already"));
+				} else {
+					if (se == SE_HP)
+						SCSI_HBA_LOG((_LOG(WARN),
+						    NULL, child,
+						    "enumeration failed during "
+						    "reprobe"));
+					else
+						SCSI_HBA_LOG((_LOG(2),
+						    NULL, child,
+						    "enumeration failed during "
+						    "reprobe"));
+
+					chg = ndi_devi_device_remove(child);
+					SCSI_HBA_LOG((_LOG(2), NULL, child,
+					    "%s device_remove%s",
+					    (sp > (sizeof (scsi_probe_ascii) /
+					    sizeof (scsi_probe_ascii[0]))) ?
+					    "UNKNOWN" : scsi_probe_ascii[sp],
+					    chg ? "" : "ed already"));
+
+					child = NULL;
+					sdchild = NULL;
+				}
+			} else {
+				SCSI_HBA_LOG((_LOG(2), NULL, child,
+				    "no reprobe"));
+
+				child = NULL;
+				sdchild = NULL;
+			}
+		}
+	} else {
+fail:		ASSERT(child == NULL);
+		sdchild = NULL;
+	}
+	if (probe) {
+		/*
+		 * Clean up probe node, destroying node if uninit_prevent
+		 * it is going to zero. Destroying the probe node (deleting
+		 * from the sibling list) will wake up any people waiting on
+		 * the probe node barrier.
+		 */
+		SCSI_HBA_LOG((_LOG(4), NULL, probe, "remove probe"));
+		if (!scsi_hba_remove_node(probe)) {
+			/*
+			 * Probe node removal should not fail, but if it
+			 * does we hand that responsibility over to the
+			 * async barrier deletion thread - other references
+			 * to the same unit-address can hang until the
+			 * probe node delete completes.
+			 */
+			SCSI_HBA_LOG((_LOG(4), NULL, probe,
+			    "remove probe failed, go async"));
+			scsi_hba_barrier_add(probe, 1);
+		}
+		probe = NULL;
+	}
+
+	/*
+	 * If we successfully resolved via a pathinfo node, we need to find
+	 * the pathinfo node and ensure that it is online (if possible). This
+	 * is done for the case where the device was open when
+	 * scsi_device_unconfig occurred, so mdi_pi_free did not occur. If the
+	 * device has now been reinserted, we want the path back online.
+	 * NOTE: This needs to occur after destruction of the probe node to
+	 * avoid ASSERT related to two nodes at the same unit-address.
+	 */
+	if (sdchild && pi && (probe == NULL)) {
+		ASSERT(MDI_PHCI(self));
+
+		(void) scsi_hba_find_child(self, NULL, addr,
+		    0, &dsearch, &psearch, NULL);
+		ASSERT((psearch == NULL) ||
+		    (mdi_pi_get_client(psearch) == child));
+
+		if (psearch && mdi_pi_device_isremoved(psearch)) {
+			/*
+			 * Verify that we can talk to the device, and if
+			 * so note if this is a new device_insert.
+			 *
+			 * NOTE: We depend on mdi_path_select(), when given
+			 * a specific path_instance, to select that path
+			 * even if the path is offline.
+			 *
+			 * NOTE: A Client node is not ndi_dev_is_hotplug_node().
+			 */
+			if (se == SE_HP) {
+				SCSI_HBA_LOG((_LOG(3), NULL, child,
+				    "%s scsi_probe() demoted pathinfo",
+				    mdi_pi_spathname(psearch)));
+
+				sp = scsi_hba_probe_pi(sdchild, SLEEP_FUNC, pi);
+
+				if (sp == SCSIPROBE_EXISTS) {
+					/*
+					 * Pathinfo child exists and we are
+					 * talking to the device, report
+					 * reinsert and note if this
+					 * was a new reinsert.
+					 */
+					chg = mdi_pi_device_insert(psearch);
+					SCSI_HBA_LOG((_LOGCFG, self, NULL,
+					    "pathinfo %s device_reinsert%s",
+					    mdi_pi_spathname(psearch),
+					    chg ? "" : "ed already"));
+
+					if (chg)
+						(void) mdi_pi_online(psearch,
+						    0);
+
+				} else {
+					SCSI_HBA_LOG((_LOG(WARN), NULL, child,
+					    "%s enumeration failed "
+					    "during reprobe",
+					    mdi_pi_spathname(psearch)));
+
+					child = NULL;
+					sdchild = NULL;
+				}
+
+			} else {
+				SCSI_HBA_LOG((_LOG(2), NULL, child,
+				    "%s no reprobe",
+				    mdi_pi_spathname(psearch)));
+
+				child = NULL;
+				sdchild = NULL;
+			}
+		}
+	}
+
+	/* If asked for path_instance, return it. */
+	if (ppi)
+		*ppi = pi;
+
+	return (sdchild);
+}
+
+static void
+scsi_device_unconfig(dev_info_t *self, char *name, char *addr, int *circp)
+{
+	dev_info_t		*child = NULL;
+	mdi_pathinfo_t		*path = NULL;
+	char			*spathname;
+	int			rval;
+
+	ASSERT(self && addr && DEVI_BUSY_OWNED(self));
+
+	/*
+	 * We have a catch-22. We may have a demoted node that we need to find
+	 * and offline/remove. To find the node it it isn't demoted, we
+	 * use scsi_hba_find_child. If it's demoted, we then use
+	 * ndi_devi_findchild_by_callback.
+	 */
+	(void) scsi_hba_find_child(self, name, addr, 0, &child, &path, NULL);
+
+	if ((child == NULL) && (path == NULL)) {
+		child = ndi_devi_findchild_by_callback(self, name, addr,
+		    scsi_busctl_ua);
+		if (child) {
+			SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+			    "devinfo %s@%s found by callback",
+			    name ? name : "", addr));
+			ASSERT(ndi_flavor_get(child) ==
+			    SCSA_FLAVOR_SCSI_DEVICE);
+			if (ndi_flavor_get(child) != SCSA_FLAVOR_SCSI_DEVICE) {
+				SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+				    "devinfo %s@%s not SCSI_DEVICE flavored",
+				    name ? name : "", addr));
+				child = NULL;
+			}
+		}
+	}
+
+	if (child) {
+		ASSERT(child && (path == NULL));
+
+		/* Don't unconfig probe nodes. */
+		if (scsi_hba_devi_is_barrier(child)) {
+			SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+			    "devinfo %s@%s is_barrier, skip",
+			    name ? name : "", addr));
+			return;
+		}
+
+		/* Attempt to offline/remove the devinfo node */
+		if (ndi_devi_offline(child,
+		    NDI_DEVFS_CLEAN | NDI_DEVI_REMOVE) == DDI_SUCCESS) {
+			SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+			    "devinfo %s@%s offlined and removed",
+			    name ? name : "", addr));
+		} else if (ndi_devi_device_remove(child)) {
+			/* Offline/remove failed, note new device_remove */
+			SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+			    "devinfo %s@%s offline failed, device_remove",
+			    name ? name : "", addr));
+		}
+	} else if (path) {
+		ASSERT(path && (child == NULL));
+
+		/*
+		 * Attempt to offline/remove the pathinfo node.
+		 *
+		 * NOTE: mdi_pi_offline of last path will fail if the
+		 * device is open (i.e. the client can't be offlined).
+		 *
+		 * NOTE: For mdi there is no REMOVE flag for mdi_pi_offline().
+		 * When mdi_pi_offline returns MDI_SUCCESS, we are responsible
+		 * for remove via mdi_pi_free().
+		 */
+		mdi_hold_path(path);
+		spathname = mdi_pi_spathname(path);	/* valid after free */
+		scsi_hba_devi_exit_phci(self, *circp);
+		rval = mdi_pi_offline(path, 0);
+		scsi_hba_devi_enter_phci(self, circp);
+		if (rval == MDI_SUCCESS) {
+			SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+			    "pathinfo %s offlined and removed", spathname));
+		} else if (mdi_pi_device_remove(path)) {
+			/* Offline/remove failed, note new device_remove */
+			SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+			    "pathinfo %s offline failed, "
+			    "device_remove", spathname));
+		}
+		mdi_rele_path(path);
+		if ((rval == MDI_SUCCESS) &&
+		    (mdi_pi_free(path, 0) != MDI_SUCCESS)) {	/* REMOVE */
+			SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+			    "pathinfo %s mdi_pi_free failed, "
+			    "device_remove", spathname));
+			(void) mdi_pi_device_remove(path);
+		}
+	} else {
+		ASSERT((path == NULL) && (child == NULL));
+
+		SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+		    "%s@%s not found", name ? name : "", addr));
+	}
+}
+
+/*
+ * configure the device at the specified "@addr" address.
+ */
+static struct scsi_device *
+scsi_hba_bus_configone_addr(dev_info_t *self, char *addr, scsi_enum_t se)
+{
+	int			circ;
+	struct scsi_device	*sd;
+
+	scsi_hba_devi_enter(self, &circ);
+	sd = scsi_device_config(self, NULL, addr, se, &circ, NULL);
+	scsi_hba_devi_exit(self, circ);
+	return (sd);
+}
+
+/*
+ * unconfigure the device at the specified "@addr" address.
+ */
+static void
+scsi_hba_bus_unconfigone_addr(dev_info_t *self, char *addr)
+{
+	int			circ;
+
+	scsi_hba_devi_enter(self, &circ);
+	(void) scsi_device_unconfig(self, NULL, addr, &circ);
+	scsi_hba_devi_exit(self, circ);
+}
+
+/*
+ * The bus_config_all operations are multi-threaded for performance. A
+ * separate thread per target and per LUN is used. The config handle is used
+ * to coordinate all the threads at a given level and the config thread data
+ * contains the required information for a specific thread to identify what it
+ * is processing and the handle under which this is being processed.
+ */
+
+/* multi-threaded config handle */
+struct	scsi_hba_mte_h {
+	dev_info_t		*h_self;	/* initiator port */
+	int			h_thr_count;
+	kmutex_t		h_lock;
+	kcondvar_t		h_cv;
+};
+
+/* target of 'self' config thread data */
+struct scsi_hba_mte_td {
+	struct scsi_hba_mte_h	*td_h;
+	char			*td_taddr;	/* target port */
+	int			td_mt;
+	scsi_enum_t		td_se;
+};
+
+/* Invoke callback on a vector of taddrs from multiple threads */
+static void
+scsi_hba_thread_taddrs(dev_info_t *self, char **taddrs, int mt,
+    scsi_enum_t se, void (*callback)(void *arg))
+{
+	struct scsi_hba_mte_h	*h;	/* HBA header */
+	struct scsi_hba_mte_td	*td;	/* target data */
+	char			**taddr;
+
+	/* allocate and initialize the handle */
+	h = kmem_zalloc(sizeof (*h), KM_SLEEP);
+	mutex_init(&h->h_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&h->h_cv, NULL, CV_DEFAULT, NULL);
+	h->h_self = self;
+
+	/* loop over all the targets */
+	for (taddr = taddrs; *taddr; taddr++) {
+		/* allocate a thread data structure for target */
+		td = kmem_alloc(sizeof (*td), KM_SLEEP);
+		td->td_h = h;
+		td->td_taddr = *taddr;
+		td->td_mt = mt;
+		td->td_se = se;
+
+		/* process the target */
+		mutex_enter(&h->h_lock);
+		h->h_thr_count++;
+		mutex_exit(&h->h_lock);
+
+		mt |= scsi_hba_log_mt_disable;
+		if (mt & SCSI_ENUMERATION_MT_LUN_DISABLE)
+			callback((void *)td);
+		else
+			(void) thread_create(NULL, 0, callback, (void *)td,
+			    0, &p0, TS_RUN, minclsyspri);
+	}
+
+	/* wait for all the target threads to complete */
+	mutex_enter(&h->h_lock);
+	while (h->h_thr_count > 0)
+		cv_wait(&h->h_cv, &h->h_lock);
+	mutex_exit(&h->h_lock);
+
+	/* free the handle */
+	cv_destroy(&h->h_cv);
+	mutex_destroy(&h->h_lock);
+	kmem_free(h, sizeof (*h));
+}
+
+
+/* lun/secondary function of lun0 config thread data */
+struct scsi_hba_mte_ld {
+	struct scsi_hba_mte_h	*ld_h;
+	char			*ld_taddr;	/* target port */
+	scsi_lun64_t		ld_lun64;	/* lun */
+	int			ld_sfunc;	/* secondary function */
+	scsi_enum_t		ld_se;
+};
+
+/*
+ * Enumerate the LUNs and secondary functions of the specified target. The
+ * target portion of the "@addr" is already represented as a string in the
+ * thread data, we add a ",lun" representation to this and perform a
+ * bus_configone byte of enumeration on that "@addr".
+ */
+static void
+scsi_hba_enum_lsf_of_tgt_thr(void *arg)
+{
+	struct scsi_hba_mte_ld	*ld = (struct scsi_hba_mte_ld *)arg;
+	struct scsi_hba_mte_h	*h = ld->ld_h;
+	dev_info_t		*self = h->h_self;
+	char			addr[SCSI_MAXNAMELEN];
+
+	/* make string form of "@taddr,lun[,sfunc]" and see if it exists */
+	if (ld->ld_sfunc == -1)
+		(void) snprintf(addr, sizeof (addr),
+		    "%s,%" PRIx64, ld->ld_taddr, ld->ld_lun64);
+	else
+		(void) snprintf(addr, sizeof (addr),
+		    "%s,%" PRIx64 ",%x",
+		    ld->ld_taddr, ld->ld_lun64, ld->ld_sfunc);
+
+	/* configure device at that unit-address address */
+	(void) scsi_hba_bus_configone_addr(self, addr, ld->ld_se);
+
+	/* signal completion of this LUN thread to the target */
+	mutex_enter(&h->h_lock);
+	if (--h->h_thr_count == 0)
+		cv_broadcast(&h->h_cv);
+	mutex_exit(&h->h_lock);
+
+	/* free config thread data */
+	kmem_free(ld, sizeof (*ld));
+}
+
+/* Format of SCSI REPORT_LUNS report */
+typedef struct scsi_lunrpt {
+	uchar_t		lunrpt_len_msb;		/* # LUNs being reported */
+	uchar_t		lunrpt_len_mmsb;
+	uchar_t		lunrpt_len_mlsb;
+	uchar_t		lunrpt_len_lsb;
+	uchar_t		lunrpt_reserved[4];
+	scsi_lun_t	lunrpt_luns[1];		/* LUNs, variable size */
+} scsi_lunrpt_t;
+
+/*
+ * scsi_device_reportluns()
+ *
+ * Callers of this routine should ensure that the 'sd0' scsi_device structure
+ * and 'pi' path_instance specified are associated with a responding LUN0.
+ * This should not be called for SCSI-1 devices.
+ *
+ * To get a LUN report, we must allocate a buffer. To know how big to make the
+ * buffer, we must know the number of LUNs. To know the number of LUNs, we must
+ * get a LUN report. We first issue a SCMD_REPORT_LUNS command using a
+ * reasonably sized buffer that's big enough to report all LUNs for most
+ * typical devices. If it turns out that we needed a bigger buffer, we attempt
+ * to allocate a buffer of sufficient size, and reissue the command. If the
+ * first command succeeds, but the second fails, we return whatever we were
+ * able to get the first time. We return enough information for the caller to
+ * tell whether he got all the LUNs or only a subset.
+ *
+ * If successful, we allocate an array of scsi_lun_t to hold the results. The
+ * caller must kmem_free(*lunarrayp, *sizep) when finished with it. Upon
+ * successful return return value is NDI_SUCCESS and:
+ *
+ *	*lunarrayp points to the allocated array,
+ *	*nlunsp is the number of valid LUN entries in the array,
+ *	*tlunsp is the total number of LUNs in the target,
+ *	*sizep is the size of the lunarrayp array, which must be freed.
+ *
+ * If the *nlunsp is less than *tlunsp, then we were only able to retrieve a
+ * subset of the total set of LUNs in the target.
+ */
+static int
+scsi_device_reportluns(struct scsi_device *sd0, char *taddr, int pi,
+    scsi_lun_t **lunarrayp, uint32_t *nlunsp, uint32_t *tlunsp, size_t *sizep)
+{
+	struct buf	*lunrpt_bp;
+	struct scsi_pkt *lunrpt_pkt;
+	scsi_lunrpt_t	*lunrpt;
+	uint32_t	bsize;
+	uint32_t	tluns, nluns;
+	int		default_maxluns = scsi_lunrpt_default_max;
+	dev_info_t	*child;
+
+	ASSERT(sd0 && lunarrayp && nlunsp && tlunsp && sizep);
+
+	/*
+	 * NOTE: child should only be used in SCSI_HBA_LOG context since with
+	 * vHCI enumeration it may be the vHCI 'client' devinfo child instead
+	 * of a child of the 'self' pHCI we are enumerating.
+	 */
+	child = sd0->sd_dev;
+
+	/* first try, look for up to scsi_lunrpt_default_max LUNs */
+	nluns = default_maxluns;
+
+again:	bsize = sizeof (struct scsi_lunrpt) +
+	    ((nluns - 1) * sizeof (struct scsi_lun));
+
+	lunrpt_bp = scsi_alloc_consistent_buf(&sd0->sd_address,
+	    (struct buf *)NULL, bsize, B_READ, SLEEP_FUNC, NULL);
+	if (lunrpt_bp == NULL) {
+		SCSI_HBA_LOG((_LOG(1), NULL, child, "failed alloc"));
+		return (NDI_NOMEM);
+	}
+
+	lunrpt_pkt = scsi_init_pkt(&sd0->sd_address,
+	    (struct scsi_pkt *)NULL, lunrpt_bp, CDB_GROUP5,
+	    sizeof (struct scsi_arq_status), 0, PKT_CONSISTENT,
+	    SLEEP_FUNC, NULL);
+	if (lunrpt_pkt == NULL) {
+		SCSI_HBA_LOG((_LOG(1), NULL, child, "failed init"));
+		scsi_free_consistent_buf(lunrpt_bp);
+		return (NDI_NOMEM);
+	}
+
+	(void) scsi_setup_cdb((union scsi_cdb *)lunrpt_pkt->pkt_cdbp,
+	    SCMD_REPORT_LUNS, 0, bsize, 0);
+
+	lunrpt_pkt->pkt_time = scsi_lunrpt_timeout;
+
+	/*
+	 * When sd0 is a vHCI scsi device, we need reportlun to be issued
+	 * against a specific LUN0 path_instance that we are enumerating.
+	 */
+	lunrpt_pkt->pkt_path_instance = pi;
+	lunrpt_pkt->pkt_flags |= FLAG_PKT_PATH_INSTANCE;
+
+	/*
+	 * NOTE: scsi_poll may not allow HBA specific recovery from TRAN_BUSY.
+	 */
+	if (scsi_poll(lunrpt_pkt) < 0) {
+		SCSI_HBA_LOG((_LOG(2), NULL, child, "reportlun not supported"));
+		scsi_destroy_pkt(lunrpt_pkt);
+		scsi_free_consistent_buf(lunrpt_bp);
+		return (NDI_FAILURE);
+	}
+
+	scsi_destroy_pkt(lunrpt_pkt);
+
+	lunrpt = (scsi_lunrpt_t *)lunrpt_bp->b_un.b_addr;
+
+	/* Compute the total number of LUNs in the target */
+	tluns = (((uint_t)lunrpt->lunrpt_len_msb << 24) |
+	    ((uint_t)lunrpt->lunrpt_len_mmsb << 16) |
+	    ((uint_t)lunrpt->lunrpt_len_mlsb << 8) |
+	    ((uint_t)lunrpt->lunrpt_len_lsb)) >> 3;
+
+	if (tluns == 0) {
+		/* Illegal response -- this target is broken */
+		SCSI_HBA_LOG((_LOG(1), NULL, child, "illegal tluns of zero"));
+		scsi_free_consistent_buf(lunrpt_bp);
+		return (DDI_NOT_WELL_FORMED);
+	}
+
+	if (tluns > nluns) {
+		/* have more than we allocated space for */
+		if (nluns == default_maxluns) {
+			/* first time around, reallocate larger */
+			scsi_free_consistent_buf(lunrpt_bp);
+			nluns = tluns;
+			goto again;
+		}
+
+		/* uh oh, we got a different tluns the second time! */
+		SCSI_HBA_LOG((_LOG(1), NULL, child,
+		    "tluns changed from %d to %d", nluns, tluns));
+	} else
+		nluns = tluns;
+
+	/*
+	 * Now we have:
+	 *	lunrpt_bp is the buffer we're using;
+	 *	tluns is the total number of LUNs the target says it has;
+	 *	nluns is the number of LUNs we were able to get into the buffer.
+	 *
+	 * Copy the data out of scarce iopb memory into regular kmem.
+	 * The caller must kmem_free(*lunarrayp, *sizep) when finished with it.
+	 */
+	*lunarrayp = (scsi_lun_t *)kmem_alloc(
+	    nluns * sizeof (scsi_lun_t), KM_SLEEP);
+	if (*lunarrayp == NULL) {
+		SCSI_HBA_LOG((_LOG(1), NULL, child, "NULL lunarray"));
+		scsi_free_consistent_buf(lunrpt_bp);
+		return (NDI_NOMEM);
+	}
+
+	*sizep = nluns * sizeof (scsi_lun_t);
+	*nlunsp = nluns;
+	*tlunsp = tluns;
+	bcopy((void *)&lunrpt->lunrpt_luns, (void *)*lunarrayp, *sizep);
+	scsi_free_consistent_buf(lunrpt_bp);
+	SCSI_HBA_LOG((_LOG(3), NULL, child,
+	    "@%s,0 path %d: %d/%d luns", taddr, pi, nluns, tluns));
+	return (NDI_SUCCESS);
+}
+
+/*
+ * Enumerate all the LUNs and secondary functions of the specified 'taddr'
+ * target port as accessed via 'self' pHCI.  Note that sd0 may be associated
+ * with a child of the vHCI instead of 'self' - in this case the 'pi'
+ * path_instance is used to ensure that the SCMD_REPORT_LUNS command is issued
+ * through the 'self' pHCI path.
+ *
+ * We multi-thread across all the LUNs and secondary functions and enumerate
+ * them. Which LUNs exist is based on SCMD_REPORT_LUNS data.
+ *
+ * The scsi_device we are called with should be for LUN0 and has been probed.
+ *
+ * This function is structured so that an HBA that has a different target
+ * addressing structure can still use this function to enumerate the its
+ * LUNs if it uses "taddr,lun" for its LUN space.
+ *
+ * We make assumptions about other LUNs associated with the target:
+ *
+ *	For SCSI-2 and SCSI-3 target we will issue the SCSI report_luns
+ *	command. If this fails or we have a SCSI-1 then the number of
+ *	LUNs is determined based on SCSI_OPTIONS_NLUNS. For a SCSI-1
+ *	target we never probe above LUN 8, even if SCSI_OPTIONS_NLUNS
+ *	indicates we should.
+ *
+ * HBA drivers wanting a different set of assumptions should implement their
+ * own LUN enumeration code.
+ */
+static int
+scsi_hba_enum_lsf_of_t(struct scsi_device *sd0,
+    dev_info_t *self, char *taddr, int pi, int mt, scsi_enum_t se)
+{
+	dev_info_t		*child;
+	scsi_hba_tran_t		*tran;
+	impl_scsi_tgtmap_t	*tgtmap;
+	damap_id_t		tgtid;
+	damap_t			*tgtdam;
+	damap_t			*lundam = NULL;
+	struct scsi_hba_mte_h	*h;
+	struct scsi_hba_mte_ld	*ld;
+	int			aver;
+	scsi_lun_t		*lunp = NULL;
+	int			lun;
+	uint32_t		nluns;
+	uint32_t		tluns;
+	size_t			size;
+	scsi_lun64_t		lun64;
+	int			maxluns;
+
+	/*
+	 * If LUN0 failed then we have no other LUNs.
+	 *
+	 * NOTE: We need sd_inq to be valid to check ansi version. Since
+	 * scsi_unprobe is now a noop (sd_inq freeded in
+	 * scsi_busctl_uninitchild) sd_inq remains valid even if a target
+	 * driver detach(9E) occurs, resulting in a scsi_unprobe call
+	 * (sd_uninit_prevent keeps sd_inq valid by failing any
+	 * device_uninitchild attempts).
+	 */
+	ASSERT(sd0 && sd0->sd_uninit_prevent && sd0->sd_dev && sd0->sd_inq);
+	if ((sd0 == NULL) || (sd0->sd_dev == NULL) || (sd0->sd_inq == NULL)) {
+		SCSI_HBA_LOG((_LOG(1), NULL, sd0 ? sd0->sd_dev : NULL,
+		    "not setup correctly:%s%s%s",
+		    (sd0 == NULL) ? " device" : "",
+		    (sd0 && (sd0->sd_dev == NULL)) ? " dip" : "",
+		    (sd0 && (sd0->sd_inq == NULL)) ? " inq" : ""));
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * NOTE: child should only be used in SCSI_HBA_LOG context since with
+	 * vHCI enumeration it may be the vHCI 'client' devinfo child instead
+	 * of a child of the 'self' pHCI we are enumerating.
+	 */
+	child = sd0->sd_dev;
+
+	/* Determine if we are reporting lun observations into lunmap. */
+	tran = ndi_flavorv_get(self, SCSA_FLAVOR_SCSI_DEVICE);
+	tgtmap = (impl_scsi_tgtmap_t *)tran->tran_tgtmap;
+	if (tgtmap) {
+		tgtdam = tgtmap->tgtmap_dam[SCSI_TGT_SCSI_DEVICE];
+		tgtid = damap_lookup(tgtdam, taddr);
+		if (tgtid != NODAM) {
+			lundam = damap_id_priv_get(tgtdam, tgtid);
+			damap_id_rele(tgtdam, tgtid);
+			ASSERT(lundam);
+		}
+	}
+
+	if (lundam) {
+		/* If using lunmap, start the observation */
+		scsi_lunmap_set_begin(self, lundam);
+	} else {
+		/* allocate and initialize the LUN handle */
+		h = kmem_zalloc(sizeof (*h), KM_SLEEP);
+		mutex_init(&h->h_lock, NULL, MUTEX_DEFAULT, NULL);
+		cv_init(&h->h_cv, NULL, CV_DEFAULT, NULL);
+		h->h_self = self;
+	}
+
+	/* See if SCMD_REPORT_LUNS works for SCSI-2 and beyond */
+	aver = sd0->sd_inq->inq_ansi;
+	if ((aver >= SCSI_VERSION_2) && (scsi_device_reportluns(sd0,
+	    taddr, pi, &lunp, &nluns, &tluns, &size) == NDI_SUCCESS)) {
+
+		ASSERT(lunp && (size > 0) && (nluns > 0) && (tluns > 0));
+
+		/* loop over the reported LUNs */
+		SCSI_HBA_LOG((_LOG(2), NULL, child,
+		    "@%s,0 path %d: enumerating %d reported lun%s", taddr, pi,
+		    nluns, nluns > 1 ? "s" : ""));
+
+		for (lun = 0; lun < nluns; lun++) {
+			lun64 = scsi_lun_to_lun64(lunp[lun]);
+
+			if (lundam) {
+				if (scsi_lunmap_set_add(self, lundam,
+				    taddr, lun64, -1) != DDI_SUCCESS) {
+					SCSI_HBA_LOG((_LOG_NF(WARN),
+					    "@%s,%" PRIx64 " failed to create",
+					    taddr, lun64));
+				}
+			} else {
+				if (lun64 == 0)
+					continue;
+
+				/* allocate a thread data structure for LUN */
+				ld = kmem_alloc(sizeof (*ld), KM_SLEEP);
+				ld->ld_h = h;
+				ld->ld_taddr = taddr;
+				ld->ld_lun64 = lun64;
+				ld->ld_sfunc = -1;
+				ld->ld_se = se;
+
+				/* process the LUN */
+				mutex_enter(&h->h_lock);
+				h->h_thr_count++;
+				mutex_exit(&h->h_lock);
+
+				mt |= scsi_hba_log_mt_disable;
+				if (mt & SCSI_ENUMERATION_MT_TARGET_DISABLE)
+					scsi_hba_enum_lsf_of_tgt_thr(
+					    (void *)ld);
+				else
+					(void) thread_create(NULL, 0,
+					    scsi_hba_enum_lsf_of_tgt_thr,
+					    (void *)ld, 0, &p0, TS_RUN,
+					    minclsyspri);
+			}
+		}
+
+		/* free the LUN array allocated by scsi_device_reportluns */
+		kmem_free(lunp, size);
+	} else {
+		/* Couldn't get SCMD_REPORT_LUNS data */
+		if (aver >= SCSI_VERSION_3) {
+			if (se == SE_HP)
+				SCSI_HBA_LOG((_LOG(WARN), NULL, child,
+				    "enumeration failed during report_lun"));
+			else
+				SCSI_HBA_LOG((_LOG(2), NULL, child,
+				    "enumeration failed during report_lun"));
+		}
+
+		/* Determine the number of LUNs to enumerate. */
+		maxluns = scsi_get_scsi_maxluns(sd0);
+
+		/* loop over possible LUNs, skipping LUN0 */
+		if (maxluns > 1)
+			SCSI_HBA_LOG((_LOG(2), NULL, child,
+			    "@%s,0 path %d: enumerating luns 1-%d", taddr, pi,
+			    maxluns - 1));
+		else
+			SCSI_HBA_LOG((_LOG(2), NULL, child,
+			    "@%s,0 path %d: enumerating just lun0", taddr, pi));
+
+		for (lun64 = 0; lun64 < maxluns; lun64++) {
+			if (lundam) {
+				if (scsi_lunmap_set_add(self, lundam,
+				    taddr, lun64, -1) != DDI_SUCCESS) {
+					SCSI_HBA_LOG((_LOG_NF(WARN),
+					    "@%s,%" PRIx64 " failed to create",
+					    taddr, lun64));
+				}
+			} else {
+				if (lun64 == 0)
+					continue;
+
+				/* allocate a thread data structure for LUN */
+				ld = kmem_alloc(sizeof (*ld), KM_SLEEP);
+				ld->ld_h = h;
+				ld->ld_taddr = taddr;
+				ld->ld_lun64 = lun64;
+				ld->ld_sfunc = -1;
+				ld->ld_se = se;
+
+				/* process the LUN */
+				mutex_enter(&h->h_lock);
+				h->h_thr_count++;
+				mutex_exit(&h->h_lock);
+
+				mt |= scsi_hba_log_mt_disable;
+				if (mt & SCSI_ENUMERATION_MT_TARGET_DISABLE)
+					scsi_hba_enum_lsf_of_tgt_thr(
+					    (void *)ld);
+				else
+					(void) thread_create(NULL, 0,
+					    scsi_hba_enum_lsf_of_tgt_thr,
+					    (void *)ld, 0, &p0, TS_RUN,
+					    minclsyspri);
+			}
+		}
+	}
+
+	/*
+	 * If we have an embedded service as a secondary function on LUN0 and
+	 * the primary LUN0 function is different than the secondary function
+	 * then enumerate the secondary function. The sfunc value is the dtype
+	 * associated with the embedded service.
+	 *
+	 * inq_encserv: enclosure service and our dtype is not DTYPE_ESI
+	 * or DTYPE_UNKNOWN then create a separate DTYPE_ESI node for
+	 * enclosure service access.
+	 */
+	ASSERT(sd0->sd_inq);
+	if (sd0->sd_inq->inq_encserv &&
+	    ((sd0->sd_inq->inq_dtype & DTYPE_MASK) != DTYPE_UNKNOWN) &&
+	    ((sd0->sd_inq->inq_dtype & DTYPE_MASK) != DTYPE_ESI) &&
+	    ((sd0->sd_inq->inq_ansi >= SCSI_VERSION_3))) {
+		if (lundam) {
+			if (scsi_lunmap_set_add(self, lundam,
+			    taddr, 0, DTYPE_ESI) != DDI_SUCCESS) {
+				SCSI_HBA_LOG((_LOG_NF(WARN),
+				    "@%s,0,%x failed to create",
+				    taddr, DTYPE_ESI));
+			}
+		} else {
+			/* allocate a thread data structure for sfunc */
+			ld = kmem_alloc(sizeof (*ld), KM_SLEEP);
+			ld->ld_h = h;
+			ld->ld_taddr = taddr;
+			ld->ld_lun64 = 0;
+			ld->ld_sfunc = DTYPE_ESI;
+			ld->ld_se = se;
+
+			/* process the LUN */
+			mutex_enter(&h->h_lock);
+			h->h_thr_count++;
+			mutex_exit(&h->h_lock);
+
+			mt |= scsi_hba_log_mt_disable;
+			if (mt & SCSI_ENUMERATION_MT_TARGET_DISABLE)
+				scsi_hba_enum_lsf_of_tgt_thr((void *)ld);
+			else
+				(void) thread_create(NULL, 0,
+				    scsi_hba_enum_lsf_of_tgt_thr, (void *)ld,
+				    0, &p0, TS_RUN, minclsyspri);
+		}
+	}
+
+	/*
+	 * Future: Add secondary function support for:
+	 *	inq_mchngr (DTYPE_CHANGER)
+	 *	inq_sccs (DTYPE_ARRAY_CTRL)
+	 */
+
+	if (lundam) {
+		/* If using lunmap, end the observation */
+		scsi_lunmap_set_end(self, lundam);
+	} else {
+		/* wait for all the LUN threads of this target to complete */
+		mutex_enter(&h->h_lock);
+		while (h->h_thr_count > 0)
+			cv_wait(&h->h_cv, &h->h_lock);
+		mutex_exit(&h->h_lock);
+
+		/* free the target handle */
+		cv_destroy(&h->h_cv);
+		mutex_destroy(&h->h_lock);
+		kmem_free(h, sizeof (*h));
+	}
+
+	return (DDI_SUCCESS);
+}
+
+/*
+ * Enumerate LUN0 and all other LUNs and secondary functions associated with
+ * the specified target address.
+ *
+ * Return NDI_SUCCESS if we might have created a new node.
+ * Return NDI_FAILURE if we definitely did not create a new node.
+ */
+static int
+scsi_hba_bus_config_taddr(dev_info_t *self, char *taddr, int mt, scsi_enum_t se)
+{
+	char			addr[SCSI_MAXNAMELEN];
+	struct scsi_device	*sd;
+	int			circ;
+	int			ret;
+	int			pi;
+
+	/* See if LUN0 of the specified target exists. */
+	(void) snprintf(addr, sizeof (addr), "%s,0", taddr);
+
+	scsi_hba_devi_enter(self, &circ);
+	sd = scsi_device_config(self, NULL, addr, se, &circ, &pi);
+
+	if (sd) {
+		/*
+		 * LUN0 exists, enumerate all the other LUNs.
+		 *
+		 * With vHCI enumeration, when 'self' is a pHCI the sd
+		 * scsi_device may be associated with the vHCI 'client'.
+		 * In this case 'pi' is the path_instance needed to
+		 * continue enumeration communication LUN0 via 'self'
+		 * pHCI and specific 'taddr' target address.
+		 *
+		 * We prevent the removal of LUN0 until we are done with
+		 * prevent/allow because we must exit the parent for
+		 * multi-threaded scsi_hba_enum_lsf_of_t().
+		 *
+		 * NOTE: scsi_unprobe is a noop, sd->sd_inq is valid until
+		 * device_uninitchild - so sd_uninit_prevent keeps sd_inq valid
+		 * by failing any device_uninitchild attempts.
+		 */
+		ret = NDI_SUCCESS;
+		sd->sd_uninit_prevent++;
+		scsi_hba_devi_exit(self, circ);
+
+		(void) scsi_hba_enum_lsf_of_t(sd, self, taddr, pi, mt, se);
+
+		scsi_hba_devi_enter(self, &circ);
+		sd->sd_uninit_prevent--;
+	} else
+		ret = NDI_FAILURE;
+	scsi_hba_devi_exit(self, circ);
+	return (ret);
+}
+
+/* Config callout from scsi_hba_thread_taddrs */
+static void
+scsi_hba_taddr_config_thr(void *arg)
+{
+	struct scsi_hba_mte_td	*td = (struct scsi_hba_mte_td *)arg;
+	struct scsi_hba_mte_h	*h = td->td_h;
+
+	(void) scsi_hba_bus_config_taddr(h->h_self, td->td_taddr,
+	    td->td_mt, td->td_se);
+
+	/* signal completion of this target thread to the HBA */
+	mutex_enter(&h->h_lock);
+	if (--h->h_thr_count == 0)
+		cv_broadcast(&h->h_cv);
+	mutex_exit(&h->h_lock);
+
+	/* free config thread data */
+	kmem_free(td, sizeof (*td));
+}
+
+/*
+ * Enumerate all the children of the specified SCSI parallel interface (spi).
+ * An HBA associated with a non-parallel scsi bus should be using another bus
+ * level enumeration implementation (possibly their own) and calling
+ * scsi_hba_bus_config_taddr to do enumeration of devices associated with a
+ * particular target address.
+ *
+ * On an spi bus the targets are sequentially enumerated based on the
+ * width of the bus. We also take care to try to skip the HBAs own initiator
+ * id. See scsi_hba_enum_lsf_of_t() for LUN and secondary function enumeration.
+ *
+ * Return NDI_SUCCESS if we might have created a new node.
+ * Return NDI_FAILURE if we definitely did not create a new node.
+ *
+ * Note: At some point we may want to expose this interface in transport.h
+ * if we find an hba that implements bus_config but still uses spi-like target
+ * addresses.
+ */
+static int
+scsi_hba_bus_configall_spi(dev_info_t *self, int mt)
+{
+	int	options;
+	int	ntargets;
+	int	id;
+	int	tgt;
+	char	**taddrs;
+	char	**taddr;
+	char	*tbuf;
+
+	/*
+	 * Find the number of targets supported on the bus. Look at the per
+	 * bus scsi-options property on the HBA node and check its
+	 * SCSI_OPTIONS_WIDE setting.
+	 */
+	options = ddi_prop_get_int(DDI_DEV_T_ANY, self,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "scsi-options", -1);
+	if ((options != -1) && ((options & SCSI_OPTIONS_WIDE) == 0))
+		ntargets = NTARGETS;			/* 8 */
+	else
+		ntargets = NTARGETS_WIDE;		/* 16 */
+
+	/*
+	 * Find the initiator-id for the HBA so we can skip that. We get the
+	 * cached value on the HBA node, established in scsi_hba_attach_setup.
+	 * If we were unable to determine the id then we rely on the HBA to
+	 * fail gracefully when asked to enumerate itself.
+	 */
+	id = ddi_prop_get_int(DDI_DEV_T_ANY, self,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "scsi-initiator-id", -1);
+	if (id > ntargets) {
+		SCSI_HBA_LOG((_LOG(1), self, NULL,
+		    "'scsi-initiator-id' bogus for %d target bus: %d",
+		    ntargets, id));
+		id = -1;
+	}
+	SCSI_HBA_LOG((_LOG(2), self, NULL,
+	    "enumerating targets 0-%d skip %d", ntargets, id));
+
+	/* form vector of target addresses */
+	taddrs = kmem_zalloc(sizeof (char *) * (ntargets + 1), KM_SLEEP);
+	for (tgt = 0, taddr = taddrs; tgt < ntargets; tgt++) {
+		/* skip initiator */
+		if (tgt == id)
+			continue;
+
+		/* convert to string and enumerate the target address */
+		tbuf = kmem_alloc(((tgt/16) + 1) + 1, KM_SLEEP);
+		(void) sprintf(tbuf, "%x", tgt);
+		ASSERT(strlen(tbuf) == ((tgt/16) + 1));
+		*taddr++ = tbuf;
+	}
+
+	/* null terminate vector of target addresses */
+	*taddr = NULL;
+
+	/* configure vector of target addresses */
+	scsi_hba_thread_taddrs(self, taddrs, mt, SE_BUSCONFIG,
+	    scsi_hba_taddr_config_thr);
+
+	/* free vector of target addresses */
+	for (taddr = taddrs; *taddr; taddr++)
+		kmem_free(*taddr, strlen(*taddr) + 1);
+	kmem_free(taddrs, sizeof (char *) * (ntargets + 1));
+	return (NDI_SUCCESS);
+}
+
+/*
+ * Transport independent bus_configone BUS_CONFIG_ONE implementation.  Takes
+ * same arguments, minus op, as scsi_hba_bus_config(), tran_bus_config(),
+ * and scsi_hba_bus_config_spi().
+ */
+int
+scsi_hba_bus_configone(dev_info_t *self, uint_t flags, char *arg,
+    dev_info_t **childp)
+{
+	int			ret;
+	int			circ;
+	char			*name, *addr;
+	char			*lcp;
+	char			sc1, sc2;
+	char			nameaddr[SCSI_MAXNAMELEN];
+	extern int		i_ndi_make_spec_children(dev_info_t *, uint_t);
+	struct scsi_device	*sd0, *sd;
+	scsi_lun64_t		lun64;
+	int			mt;
+
+	/* parse_name modifies arg1, we must duplicate "name@addr" */
+	(void) strcpy(nameaddr, arg);
+	i_ddi_parse_name(nameaddr, &name, &addr, NULL);
+
+	/* verify the form of the node - we need an @addr */
+	if ((name == NULL) || (addr == NULL) ||
+	    (*name == '\0') || (*addr == '\0')) {
+		/*
+		 * OBP may create ill formed template/stub/wild-card
+		 * nodes (no @addr) for legacy driver loading methods -
+		 * ignore them.
+		 */
+		SCSI_HBA_LOG((_LOG(2), self, NULL, "%s ill formed", arg));
+		return (NDI_FAILURE);
+	}
+
+	/*
+	 * Check to see if this is a non-scsi flavor configuration operation.
+	 */
+	if (strcmp(name, "smp") == 0) {
+		/*
+		 * Configure the child, and if we're successful return with
+		 * active hold.
+		 */
+		return (smp_hba_bus_config(self, addr, childp));
+	}
+
+	/*
+	 * The framework does not ensure the creation of driver.conf
+	 * nodes prior to calling a nexus bus_config. For legacy
+	 * support of driver.conf file nodes we want to create our
+	 * driver.conf file children now so that we can detect if we
+	 * are being asked to bus_configone one of these nodes.
+	 *
+	 * Needing driver.conf file nodes prior to bus config is unique
+	 * to scsi_enumeration mixed mode (legacy driver.conf and
+	 * dynamic SID node) support. There is no general need for the
+	 * framework to make driver.conf children prior to bus_config.
+	 *
+	 * We enter our HBA (self) prior to scsi_device_config, and
+	 * pass it our circ. The scsi_device_config may exit the
+	 * HBA around scsi_probe() operations to allow for parallelism.
+	 * This is done after the probe node "@addr" is available as a
+	 * barrier to prevent parallel probes of the same device. The
+	 * probe node is also configured in a way that it can't be
+	 * removed by the framework until we are done with it.
+	 *
+	 * NOTE: The framework is currently preventing many parallel
+	 * sibling operations (such as attaches), so the parallelism
+	 * we are providing is of marginal use until that is improved.
+	 * The most logical way to solve this would be to have separate
+	 * target and lun nodes. This would be a large change in the
+	 * format of /devices paths and is not being pursued at this
+	 * time. The need for parallelism will become more of an issue
+	 * with top-down attach for mpxio/vhci and for iSCSI support.
+	 * We may want to eventually want a dual mode implementation,
+	 * where the HBA determines if we should construct separate
+	 * target and lun devinfo nodes.
+	 */
+	scsi_hba_devi_enter(self, &circ);
+	SCSI_HBA_LOG((_LOG(4), self, NULL, "%s@%s config_one", name, addr));
+	(void) i_ndi_make_spec_children(self, flags);
+
+	/*
+	 * For bus_configone, we make sure that we can find LUN0
+	 * first. This allows the delayed probe/barrier deletion for a
+	 * non-existent LUN0 (if enabled in scsi_device_config) to
+	 * cover all LUNs on the target. This is done to minimize the
+	 * number of independent target selection timeouts that occur
+	 * when a target with many LUNs is no longer accessible
+	 * (powered off). This removes the need for target driver
+	 * probe cache implementations.
+	 *
+	 * This optimization may not be desirable in a pure bridge
+	 * environment where targets on the other side of the bridge
+	 * show up as LUNs to the host. If we ever need to support
+	 * such a configuration then we should consider implementing a
+	 * SCSI_OPTIONS_ILUN0 bit.
+	 *
+	 * NOTE: we are *not* applying any target limitation filtering
+	 * to bus_configone, which means that we are relying on the
+	 * HBA tran_tgt_init entry point invoked by scsi_busctl_initchild
+	 * to fail.
+	 */
+	sd0 = (struct scsi_device *)-1;
+	lcp = strchr(addr, ',');		/* "addr,lun[,sfunc]" */
+	if (lcp) {
+		/*
+		 * With "tgt,lun[,sfunc]" addressing, multiple addressing levels
+		 * have been compressed into single devinfo node unit-address.
+		 * This presents a mismatch - there is no bus_config to discover
+		 * LUNs below a specific target, the only choice is to
+		 * BUS_CONFIG_ALL the HBA. To support BUS_CONFIG_ALL_LUNS below
+		 * a specific target, a bus_configone with lun address of "*"
+		 * triggers lun discovery below a target.
+		 */
+		if (*(lcp + 1) == '*') {
+			mt = ddi_prop_get_int(DDI_DEV_T_ANY, self,
+			    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
+			    "scsi-enumeration", scsi_enumeration);
+			SCSI_HBA_LOG((_LOG(2), self, NULL,
+			    "%s@%s lun enumeration triggered", name, addr));
+			*lcp = '\0';		/* turn ',' into '\0' */
+			scsi_hba_devi_exit(self, circ);
+			(void) scsi_hba_bus_config_taddr(self, addr,
+			    mt, SE_BUSCONFIG);
+			return (NDI_FAILURE);
+		}
+
+		/* convert hex lun number from ascii */
+		lun64 = scsi_addr_to_lun64(lcp + 1);
+
+		if ((lun64 != 0) && (lun64 != SCSI_LUN64_ILLEGAL)) {
+			/*
+			 * configure ",0" lun first, saving off
+			 * original lun characters.
+			 */
+			sc1 = *(lcp + 1);
+			sc2 = *(lcp + 2);
+			*(lcp + 1) = '0';
+			*(lcp + 2) = '\0';
+			sd0 = scsi_device_config(self, NULL, addr,
+			    SE_BUSCONFIG, &circ, NULL);
+
+			/* restore original lun */
+			*(lcp + 1) = sc1;
+			*(lcp + 2) = sc2;
+
+			/*
+			 * Apply maxlun filtering.
+			 *
+			 * Future: We still have the kludged
+			 * scsi_check_ss2_LUN_limit() filtering off
+			 * scsi_probe() to catch bogus driver.conf
+			 * entries.
+			 */
+			if (sd0 && (lun64 < SCSI_32LUNS_PER_TARGET) &&
+			    (lun64 >= scsi_get_scsi_maxluns(sd0))) {
+				sd0 = NULL;
+				SCSI_HBA_LOG((_LOG(4), self, NULL,
+				    "%s@%s filtered", name, addr));
+			} else
+				SCSI_HBA_LOG((_LOG(4), self, NULL,
+				    "%s@%s lun 0 %s", name, addr,
+				    sd0 ? "worked" : "failed"));
+		}
+	}
+
+	/*
+	 * configure the requested device if LUN0 exists or we were
+	 * unable to determine the lun format to determine if LUN0
+	 * exists.
+	 */
+	if (sd0) {
+		sd = scsi_device_config(self, name, addr,
+		    SE_BUSCONFIG, &circ, NULL);
+	} else {
+		sd = NULL;
+		SCSI_HBA_LOG((_LOG(2), self, NULL,
+		    "%s@%s no lun 0 or filtered lun", name, addr));
+	}
+
+	/*
+	 * We know what we found, to reduce overhead we finish BUS_CONFIG_ONE
+	 * processing without calling back to the frameworks
+	 * ndi_busop_bus_config (unless we goto framework below).
+	 *
+	 * If the reference is to a driver name and we created a generic name
+	 * (bound to that driver) we will still succeed.  This is important
+	 * for correctly resolving old drivername references to device that now
+	 * uses a generic names across the transition to generic naming. This
+	 * is effectively an internal implementation of the NDI_DRIVERNAME flag.
+	 *
+	 * We also need to special case the resolve_pathname OBP boot-device
+	 * case (modrootloaded == 0) where reference is to a generic name but
+	 * we created a legacy driver name node by returning just returning
+	 * the node created.
+	 */
+	if (sd && sd->sd_dev &&
+	    ((strcmp(ddi_node_name(sd->sd_dev), name) == 0) ||
+	    (strcmp(ddi_driver_name(sd->sd_dev), name) == 0) ||
+	    (modrootloaded == 0)) &&
+	    (ndi_devi_online(sd->sd_dev,
+	    flags & NDI_NO_EVENT) == NDI_SUCCESS)) {
+
+		/* device attached, return devinfo node with hold */
+		ret = NDI_SUCCESS;
+		*childp = sd->sd_dev;
+		ndi_hold_devi(sd->sd_dev);
+	} else {
+		/*
+		 * In the process of failing we may have added nodes to the HBA
+		 * (self), clearing DEVI_MADE_CHILDREN. To reduce the overhead
+		 * associated with the frameworks reaction to this we clear the
+		 * flag here.
+		 */
+		mutex_enter(&DEVI(self)->devi_lock);
+		DEVI(self)->devi_flags &= ~DEVI_MADE_CHILDREN;
+		mutex_exit(&DEVI(self)->devi_lock);
+		ret = NDI_FAILURE;
+
+		/*
+		 * The framework may still be able to succeed with
+		 * with its GENERIC_PROP code.
+		 */
+		scsi_hba_devi_exit(self, circ);
+		if (flags & NDI_DRV_CONF_REPROBE)
+			flags |= NDI_CONFIG_REPROBE;
+		flags |= NDI_MDI_FALLBACK;	/* devinfo&pathinfo children */
+		return (ndi_busop_bus_config(self, flags, BUS_CONFIG_ONE,
+		    (void *)arg, childp, 0));
+	}
+
+	scsi_hba_devi_exit(self, circ);
+	return (ret);
+}
+
+/*
+ * Perform SCSI Parallel Interconnect bus_config
+ */
+static int
+scsi_hba_bus_config_spi(dev_info_t *self, uint_t flags,
+    ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
+{
+	int			ret;
+	int			mt;
+
+	/*
+	 * Enumerate scsi target devices: See if we are doing generic dynamic
+	 * enumeration: if driver.conf has not specified the 'scsi-enumeration'
+	 * knob then use the global scsi_enumeration knob.
+	 */
+	mt = ddi_prop_get_int(DDI_DEV_T_ANY, self,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
+	    "scsi-enumeration", scsi_enumeration);
+	if ((mt & SCSI_ENUMERATION_ENABLE) == 0) {
+		/*
+		 * Static driver.conf file enumeration:
+		 *
+		 * Force reprobe for BUS_CONFIG_ONE or when manually
+		 * reconfiguring via devfsadm(1m) to emulate deferred attach.
+		 * Reprobe only discovers driver.conf enumerated nodes, more
+		 * dynamic implementations probably require their own
+		 * bus_config.
+		 */
+		if ((op == BUS_CONFIG_ONE) || (flags & NDI_DRV_CONF_REPROBE))
+			flags |= NDI_CONFIG_REPROBE;
+		flags |= NDI_MDI_FALLBACK;	/* devinfo&pathinfo children */
+		return (ndi_busop_bus_config(self, flags, op, arg, childp, 0));
+	}
+
+	if (scsi_hba_busconfig_debug)
+		flags |= NDI_DEVI_DEBUG;
+
+	/*
+	 * Generic spi dynamic bus config enumeration to discover and enumerate
+	 * the target device nodes we are looking for.
+	 */
+	switch (op) {
+	case BUS_CONFIG_ONE:	/* enumerate the named child */
+		ret = scsi_hba_bus_configone(self, flags, (char *)arg, childp);
+		break;
+
+	case BUS_CONFIG_ALL:	/* enumerate all children on the bus */
+	case BUS_CONFIG_DRIVER: /* enumerate all children that bind to driver */
+		SCSI_HBA_LOG((_LOG(3), self, NULL,
+		    "BUS_CONFIG_%s mt %x",
+		    (op == BUS_CONFIG_ALL) ? "ALL" : "DRIVER", mt));
+
+		/*
+		 * Enumerate targets on SCSI parallel interconnect and let the
+		 * framework finish the operation (attach the nodes).
+		 */
+		if ((ret = scsi_hba_bus_configall_spi(self, mt)) == NDI_SUCCESS)
+			ret = ndi_busop_bus_config(self, flags, op,
+			    arg, childp, 0);
+		break;
+
+	default:
+		ret = NDI_FAILURE;
+		break;
+	}
+	return (ret);
+}
+
+/*
+ * Perform SCSI Parallel Interconnect bus_unconfig
+ */
+static int
+scsi_hba_bus_unconfig_spi(dev_info_t *self, uint_t flags,
+    ddi_bus_config_op_t op, void *arg)
+{
+	int	mt;
+	int	circ;
+	int	ret;
+
+	/*
+	 * See if we are doing generic dynamic enumeration: if driver.conf has
+	 * not specified the 'scsi-enumeration' knob then use the global
+	 * scsi_enumeration knob.
+	 */
+	mt = ddi_prop_get_int(DDI_DEV_T_ANY, self,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
+	    "scsi-enumeration", scsi_enumeration);
+	if ((mt & SCSI_ENUMERATION_ENABLE) == 0)
+		return (ndi_busop_bus_unconfig(self, flags, op, arg));
+
+	if (scsi_hba_busconfig_debug)
+		flags |= NDI_DEVI_DEBUG;
+
+	scsi_hba_devi_enter(self, &circ);
+	switch (op) {
+	case BUS_UNCONFIG_ONE:
+		SCSI_HBA_LOG((_LOG(3), self, NULL,
+		    "unconfig one: %s", (char *)arg));
+		ret = NDI_SUCCESS;
+		break;
+
+	case BUS_UNCONFIG_ALL:
+	case BUS_UNCONFIG_DRIVER:
+		ret = NDI_SUCCESS;
+		break;
+
+	default:
+		ret = NDI_FAILURE;
+		break;
+	}
+
+	/* Perform the generic default bus unconfig */
+	if (ret == NDI_SUCCESS)
+		ret = ndi_busop_bus_unconfig(self, flags, op, arg);
+
+	scsi_hba_devi_exit(self, circ);
+
+	return (ret);
+}
+
+static int
+scsi_hba_bus_config_tgtmap(dev_info_t *self, uint_t flags,
+    ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
+{
+	int ret = NDI_FAILURE;
+
+	switch (op) {
+	case BUS_CONFIG_ONE:
+		ret = scsi_hba_bus_configone(self, flags, arg, childp);
+		break;
+
+	case BUS_CONFIG_ALL:
+	case BUS_CONFIG_DRIVER:
+		ret = ndi_busop_bus_config(self, flags, op, arg, childp, 0);
+		break;
+
+	default:
+		break;
+	}
+
+	return (ret);
+}
+
+static int
+scsi_hba_bus_unconfig_tgtmap(dev_info_t *self, uint_t flags,
+    ddi_bus_config_op_t op, void *arg)
+{
+	int ret = NDI_FAILURE;
+
+	switch (op) {
+	case BUS_UNCONFIG_ONE:
+	case BUS_UNCONFIG_DRIVER:
+	case BUS_UNCONFIG_ALL:
+		ret = NDI_SUCCESS;
+		break;
+	default:
+		break;
+	}
+
+	if (ret == NDI_SUCCESS) {
+		flags &= ~NDI_DEVI_REMOVE;
+		ret = ndi_busop_bus_unconfig(self, flags, op, arg);
+	}
+	return (ret);
+}
+
+static int
+scsi_hba_bus_config_iportmap(dev_info_t *self, uint_t flags,
+    ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
+{
+	dev_info_t	*child;
+	int		circ;
+	int		ret = NDI_FAILURE;
+
+	/*
+	 * MPXIO is never a sure thing (and we have mixed children), so
+	 * set NDI_NDI_FALLBACK so that ndi_busop_bus_config will
+	 * search for both devinfo and pathinfo children.
+	 *
+	 * Future: Remove NDI_MDI_FALLBACK since devcfg.c now looks for
+	 * devinfo/pathinfo children in parallel (instead of old way of
+	 * looking for one form of child and then doing "fallback" to
+	 * look for other form of child).
+	 */
+	flags |= NDI_MDI_FALLBACK;	/* devinfo&pathinfo children */
+	switch (op) {
+	case BUS_CONFIG_ONE:
+		scsi_hba_devi_enter(self, &circ);
+		/* create the iport node child */
+		if ((child = scsi_hba_bus_config_port(self, (char *)arg,
+		    SE_BUSCONFIG)) != NULL) {
+			if (childp) {
+				ndi_hold_devi(child);
+				*childp = child;
+			}
+			ret = NDI_SUCCESS;
+		}
+		scsi_hba_devi_exit(self, circ);
+		break;
+
+	case BUS_CONFIG_ALL:
+	case BUS_CONFIG_DRIVER:
+		ret = ndi_busop_bus_config(self, flags, op, arg, childp, 0);
+		break;
+
+	default:
+		break;
+	}
+	return (ret);
+}
+
+static int
+scsi_hba_bus_unconfig_iportmap(dev_info_t *self, uint_t flags,
+    ddi_bus_config_op_t op, void *arg)
+{
+	flags &= ~NDI_DEVI_REMOVE;
+	return (ndi_busop_bus_unconfig(self, flags, op, arg));
+}
+
+/*
+ * SCSI HBA bus config enumeration entry point. Called via the bus_ops
+ * bus_config entry point for all SCSA HBA drivers.
+ *
+ *  o	If an HBA implements its own bus_config via tran_bus_config then we
+ *	invoke it. An HBA that implements its own tran_bus_config entry	point
+ *	may still call back into common SCSA code bus_config code for:
+ *
+ *	o SPI bus_config (scsi_hba_bus_spi)
+ *	o LUN and secondary function enumeration (scsi_hba_enum_lsf_of_t()).
+ *	o configuration of a specific device (scsi_device_config).
+ *	o determining 1275 SCSI nodename and compatible property
+ *	  (scsi_hba_nodename_compatible_get/_free).
+ *
+ *   o	Otherwise we implement a SCSI parallel interface (spi) bus config.
+ *
+ * Return NDI_SUCCESS if we might have created a new node.
+ * Return NDI_FAILURE if we definitely did not create a new node.
+ */
+static int
+scsi_hba_bus_config(dev_info_t *self, uint_t flags,
+    ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
+{
+	scsi_hba_tran_t	*tran;
+	int		ret;
+
+	/* make sure that we will not disappear */
+	ASSERT(DEVI(self)->devi_ref);
+
+	tran = ndi_flavorv_get(self, SCSA_FLAVOR_SCSI_DEVICE);
+	if (tran == NULL) {
+		/* NULL tran driver.conf config (used by cmdk). */
+		if ((op == BUS_CONFIG_ONE) || (flags & NDI_DRV_CONF_REPROBE))
+			flags |= NDI_CONFIG_REPROBE;
+		return (ndi_busop_bus_config(self, flags, op, arg, childp, 0));
+	}
+
+	/* Check if self is HBA-only node. */
+	if (tran->tran_hba_flags & SCSI_HBA_HBA) {
+		/* The bus_config request is to configure iports below HBA. */
+
+#ifdef	sparc
+		/*
+		 * Sparc's 'boot-device' OBP property value lacks an /iport@X/
+		 * component. Prior to the mount of root, we drive a disk@
+		 * BUS_CONFIG_ONE operatino down a level to resolve an
+		 * OBP 'boot-device' path.
+		 *
+		 * Future: Add (modrootloaded == 0) below, and insure that
+		 * all attempts bus_conf of 'bo_name' (in OBP form) occur
+		 * prior to 'modrootloaded = 1;' assignment in vfs_mountroot.
+		 */
+		if ((op == BUS_CONFIG_ONE) &&
+		    (strncmp((char *)arg, "disk@", strlen("disk@")) == 0)) {
+			return (scsi_hba_bus_config_prom_node(self,
+			    flags, arg, childp));
+		}
+#endif	/* sparc */
+
+		if (tran->tran_iportmap) {
+			/* config based on scsi_hba_iportmap API */
+			ret = scsi_hba_bus_config_iportmap(self,
+			    flags, op, arg, childp);
+		} else {
+			/* config based on 'iport_register' API */
+			ret = scsi_hba_bus_config_iports(self,
+			    flags, op, arg, childp);
+		}
+		return (ret);
+	}
+
+	/* Check to see how the iport/HBA does target/lun bus config. */
+	if (tran->tran_bus_config) {
+		/* HBA config based on Sun-private/legacy tran_bus_config */
+		ret = tran->tran_bus_config(self, flags, op, arg, childp);
+	} else if (tran->tran_tgtmap) {
+		/* SCSAv3 config based on scsi_hba_tgtmap_*() API */
+		ret =  scsi_hba_bus_config_tgtmap(self, flags, op, arg, childp);
+	} else {
+		/* SCSA config based on SCSI Parallel Interconnect */
+		ret = scsi_hba_bus_config_spi(self, flags, op, arg, childp);
+	}
+	return (ret);
+}
+
+/*
+ * Called via the bus_ops bus_unconfig entry point for SCSI HBA drivers.
+ */
+static int
+scsi_hba_bus_unconfig(dev_info_t *self, uint_t flags,
+    ddi_bus_config_op_t op, void *arg)
+{
+	int		circ;
+	scsi_hba_tran_t	*tran;
+	int		ret;
+
+	tran = ddi_get_driver_private(self);
+	if (tran == NULL) {
+		/* NULL tran driver.conf unconfig (used by cmdk). */
+		return (ndi_busop_bus_unconfig(self, flags, op, arg));
+	}
+
+	/*
+	 * Purge barrier/probe node children. We do this prior to
+	 * tran_bus_unconfig in case the unconfig implementation calls back
+	 * into the common code at a different enumeration level, such a
+	 * scsi_device_config, which still creates barrier/probe nodes.
+	 */
+	scsi_hba_devi_enter(self, &circ);
+	scsi_hba_barrier_purge(self);
+	scsi_hba_devi_exit(self, circ);
+
+	/* Check if self is HBA-only node. */
+	if (tran->tran_hba_flags & SCSI_HBA_HBA) {
+		/* The bus_config request is to unconfigure iports below HBA. */
+		if (tran->tran_iportmap) {
+			/* unconfig based on scsi_hba_iportmap API */
+			ret = scsi_hba_bus_unconfig_iportmap(self,
+			    flags, op, arg);
+		}
+		return (ret);
+	}
+
+	/* Check to see how the iport/HBA does target/lun bus unconfig. */
+	if (tran->tran_bus_unconfig) {
+		/* HBA unconfig based on Sun-private/legacy tran_bus_unconfig */
+		ret = tran->tran_bus_unconfig(self, flags, op, arg);
+	} else if (tran->tran_tgtmap) {
+		/* SCSAv3 unconfig based on scsi_hba_tgtmap_*() API */
+		ret = scsi_hba_bus_unconfig_tgtmap(self, flags, op, arg);
+	} else {
+		/* SCSA unconfig based on SCSI Parallel Interconnect */
+		ret = scsi_hba_bus_unconfig_spi(self, flags, op, arg);
+	}
+	return (ret);
+}
+
+static void
+scsi_tgtmap_scsi_config(void *arg, damap_t *mapp, damap_id_list_t id_list)
+{
+	scsi_hba_tran_t		*tran = (scsi_hba_tran_t *)arg;
+	dev_info_t		*self = tran->tran_iport_dip;
+	impl_scsi_tgtmap_t	*tgtmap;
+	int			mt;
+	damap_id_t		tgtid;
+	int			ntargets;
+	char			**tgt_addrv;
+	char			**tgt_addr;
+
+	tgtmap = (impl_scsi_tgtmap_t *)tran->tran_tgtmap;
+	mt = ddi_prop_get_int(DDI_DEV_T_ANY, self,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
+	    "scsi-enumeration", scsi_enumeration);
+
+	/* count the number of targets we need to config */
+	for (ntargets = 0,
+	    tgtid = damap_id_next(mapp, id_list, NODAM);
+	    tgtid != NODAM;
+	    tgtid = damap_id_next(mapp, id_list, tgtid))
+		ntargets++;
+
+	SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s %d target%s",
+	    damap_name(mapp), ntargets, (ntargets == 1) ? "" : "s"));
+	if (ntargets == 0)
+		return;
+
+	/* allocate and form vector of addresses */
+	tgt_addrv = kmem_zalloc(sizeof (char *) * (ntargets + 1), KM_SLEEP);
+	for (tgt_addr = tgt_addrv,
+	    tgtid = damap_id_next(mapp, id_list, NODAM);
+	    tgtid != NODAM;
+	    tgtid = damap_id_next(mapp, id_list, tgtid),
+	    tgt_addr++) {
+		*tgt_addr = damap_id2addr(mapp, tgtid);
+
+		if (scsi_lunmap_create(self, tgtmap, *tgt_addr) != DDI_SUCCESS)
+			SCSI_HBA_LOG((_LOG_NF(WARN),
+			    "failed to create lunmap for %s", *tgt_addr));
+		else
+			SCSI_HBA_LOG((_LOGTGT, self, NULL,
+			    "%s @%s", damap_name(mapp), *tgt_addr));
+	}
+
+	/* null terminate vector */
+	*tgt_addr = NULL;
+
+	/* configure vector of addresses (with multi-threading) */
+	scsi_hba_thread_taddrs(self, tgt_addrv, mt, SE_HP,
+	    scsi_hba_taddr_config_thr);
+
+	/* free vector */
+	kmem_free(tgt_addrv, sizeof (char *) * (ntargets + 1));
+}
+
+static void
+scsi_tgtmap_scsi_unconfig(void *arg, damap_t *mapp, damap_id_list_t id_list)
+{
+	scsi_hba_tran_t		*tran = (scsi_hba_tran_t *)arg;
+	dev_info_t		*self = tran->tran_iport_dip;
+	impl_scsi_tgtmap_t	*tgtmap;
+	damap_id_t		tgtid;
+	char			*tgt_addr;
+
+	tgtmap = (impl_scsi_tgtmap_t *)tran->tran_tgtmap;
+
+	for (tgtid = damap_id_next(mapp, id_list, NODAM);
+	    tgtid != NODAM;
+	    tgtid = damap_id_next(mapp, id_list, tgtid)) {
+		tgt_addr = damap_id2addr(mapp, tgtid);
+
+		SCSI_HBA_LOG((_LOGTGT, self, NULL,
+		    "%s @%s", damap_name(mapp), tgt_addr));
+		scsi_lunmap_destroy(self, tgtmap, tgt_addr);
+	}
+}
+
+static void
+scsi_tgtmap_smp_config(void *arg, damap_t *mapp, damap_id_list_t id_list)
+{
+	scsi_hba_tran_t	*tran = (scsi_hba_tran_t *)arg;
+	dev_info_t	*self = tran->tran_iport_dip;
+	damap_id_t	tgtid;
+	char		*addr;
+
+	for (tgtid = damap_id_next(mapp, id_list, NODAM);
+	    tgtid != NODAM;
+	    tgtid = damap_id_next(mapp, id_list, tgtid)) {
+		addr = damap_id2addr(mapp, tgtid);
+		SCSI_HBA_LOG((_LOGTGT, self, NULL,
+		    "%s @%s", damap_name(mapp), addr));
+
+		(void) smp_hba_bus_config_taddr(self, addr);
+	}
+}
+
+static void
+scsi_tgtmap_smp_unconfig(void *arg, damap_t *mapp, damap_id_list_t id_list)
+{
+	scsi_hba_tran_t	*tran = (scsi_hba_tran_t *)arg;
+	dev_info_t	*self = tran->tran_iport_dip;
+	damap_id_t	tgtid;
+	char		*addr;
+	dev_info_t	*child;
+	char		nameaddr[SCSI_MAXNAMELEN];
+	int		circ;
+
+	for (tgtid = damap_id_next(mapp, id_list, NODAM);
+	    tgtid != NODAM;
+	    tgtid = damap_id_next(mapp, id_list, tgtid)) {
+		addr = damap_id2addr(mapp, tgtid);
+		SCSI_HBA_LOG((_LOGTGT, self, NULL,
+		    "%s @%s", damap_name(mapp), addr));
+
+		(void) snprintf(nameaddr, sizeof (nameaddr), "smp@%s", addr);
+		scsi_hba_devi_enter(self, &circ);
+		if ((child = ndi_devi_findchild(self, nameaddr)) == NULL) {
+			scsi_hba_devi_exit(self, circ);
+			continue;
+		}
+
+		if (ndi_devi_offline(child,
+		    NDI_DEVFS_CLEAN | NDI_DEVI_REMOVE) == DDI_SUCCESS) {
+			SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+			    "devinfo smp@%s offlined and removed", addr));
+		} else if (ndi_devi_device_remove(child)) {
+			/* Offline/remove failed, note new device_remove */
+			SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+			    "devinfo smp@%s offline failed, device_remove",
+			    addr));
+		}
+		scsi_hba_devi_exit(self, circ);
+	}
+}
+
+/* ARGSUSED1 */
+static void
+scsi_tgtmap_smp_activate(void *map_priv, char *tgt_addr, int addrid,
+    void **tgt_privp)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)map_priv;
+	dev_info_t		*self = tgtmap->tgtmap_tran->tran_iport_dip;
+
+	if (tgtmap->tgtmap_activate_cb) {
+		SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s @%s activated",
+		    damap_name(tgtmap->tgtmap_dam[SCSI_TGT_SMP_DEVICE]),
+		    tgt_addr));
+
+		(*tgtmap->tgtmap_activate_cb)(tgtmap->tgtmap_mappriv,
+		    tgt_addr, SCSI_TGT_SMP_DEVICE, tgt_privp);
+	}
+}
+
+/* ARGSUSED1 */
+static void
+scsi_tgtmap_smp_deactivate(void *map_priv, char *tgt_addr, int addrid,
+    void *tgt_privp)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)map_priv;
+	dev_info_t		*self = tgtmap->tgtmap_tran->tran_iport_dip;
+
+	if (tgtmap->tgtmap_deactivate_cb) {
+		SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s @%s deactivated",
+		    damap_name(tgtmap->tgtmap_dam[SCSI_TGT_SMP_DEVICE]),
+		    tgt_addr));
+
+
+		(*tgtmap->tgtmap_deactivate_cb)(tgtmap->tgtmap_mappriv,
+		    tgt_addr, SCSI_TGT_SMP_DEVICE, tgt_privp);
+	}
+}
+
+/* ARGSUSED1 */
+static void
+scsi_tgtmap_scsi_activate(void *map_priv, char *tgt_addr, int addrid,
+    void **tgt_privp)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)map_priv;
+	dev_info_t		*self = tgtmap->tgtmap_tran->tran_iport_dip;
+
+	if (tgtmap->tgtmap_activate_cb) {
+		SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s @%s activated",
+		    damap_name(tgtmap->tgtmap_dam[SCSI_TGT_SCSI_DEVICE]),
+		    tgt_addr));
+
+		(*tgtmap->tgtmap_activate_cb)(tgtmap->tgtmap_mappriv,
+		    tgt_addr, SCSI_TGT_SCSI_DEVICE, tgt_privp);
+	}
+}
+
+/* ARGSUSED1 */
+static void
+scsi_tgtmap_scsi_deactivate(void *map_priv, char *tgt_addr, int addrid,
+    void *tgt_privp)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)map_priv;
+	dev_info_t		*self = tgtmap->tgtmap_tran->tran_iport_dip;
+
+	if (tgtmap->tgtmap_deactivate_cb) {
+		SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s @%s deactivated",
+		    damap_name(tgtmap->tgtmap_dam[SCSI_TGT_SCSI_DEVICE]),
+		    tgt_addr));
+
+		(*tgtmap->tgtmap_deactivate_cb)(tgtmap->tgtmap_mappriv,
+		    tgt_addr, SCSI_TGT_SCSI_DEVICE, tgt_privp);
+
+	}
+}
+
+
+int
+scsi_hba_tgtmap_create(dev_info_t *self, scsi_tgtmap_mode_t mode,
+    clock_t settle, int n_entries,
+    void *tgtmap_priv, scsi_tgt_activate_cb_t activate_cb,
+    scsi_tgt_deactivate_cb_t deactivate_cb,
+    scsi_hba_tgtmap_t **handle)
+{
+	scsi_hba_tran_t		*tran;
+	damap_t			*mapp;
+	char			context[64];
+	impl_scsi_tgtmap_t	*tgtmap;
+	damap_rptmode_t		rpt_style;
+	char			*scsi_binding_set;
+
+	if (self == NULL || settle == 0 || n_entries == 0 || handle == NULL)
+		return (DDI_FAILURE);
+
+	*handle = NULL;
+
+	if (scsi_hba_iport_unit_address(self) == NULL)
+		return (DDI_FAILURE);
+
+	switch (mode) {
+	case SCSI_TM_FULLSET:
+		rpt_style = DAMAP_REPORT_FULLSET;
+		break;
+	case SCSI_TM_PERADDR:
+		rpt_style = DAMAP_REPORT_PERADDR;
+		break;
+	default:
+		return (DDI_FAILURE);
+	}
+
+	tran = (scsi_hba_tran_t *)ddi_get_driver_private(self);
+	ASSERT(tran);
+	if (tran == NULL)
+		return (DDI_FAILURE);
+
+	tgtmap = kmem_zalloc(sizeof (*tgtmap), KM_SLEEP);
+	tgtmap->tgtmap_tran = tran;
+	tgtmap->tgtmap_activate_cb = activate_cb;
+	tgtmap->tgtmap_deactivate_cb = deactivate_cb;
+	tgtmap->tgtmap_mappriv = tgtmap_priv;
+
+	(void) snprintf(context, sizeof (context), "%s%d.tgtmap.scsi",
+	    ddi_driver_name(self), ddi_get_instance(self));
+	SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s", context));
+	if (damap_create(context, n_entries, rpt_style, settle,
+	    tgtmap, scsi_tgtmap_scsi_activate, scsi_tgtmap_scsi_deactivate,
+	    tran, scsi_tgtmap_scsi_config, scsi_tgtmap_scsi_unconfig,
+	    &mapp) != DAM_SUCCESS) {
+		kmem_free(tgtmap, sizeof (*tgtmap));
+		return (DDI_FAILURE);
+	}
+	tgtmap->tgtmap_dam[SCSI_TGT_SCSI_DEVICE] = mapp;
+
+	(void) snprintf(context, sizeof (context), "%s%d.tgtmap.smp",
+	    ddi_driver_name(self), ddi_get_instance(self));
+	SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s", context));
+	if (damap_create(context, n_entries, rpt_style, settle,
+	    tgtmap, scsi_tgtmap_smp_activate, scsi_tgtmap_smp_deactivate,
+	    tran, scsi_tgtmap_smp_config, scsi_tgtmap_smp_unconfig,
+	    &mapp) != DAM_SUCCESS) {
+		damap_destroy(tgtmap->tgtmap_dam[SCSI_TGT_SCSI_DEVICE]);
+		kmem_free(tgtmap, sizeof (*tgtmap));
+		return (DDI_FAILURE);
+	}
+	tgtmap->tgtmap_dam[SCSI_TGT_SMP_DEVICE] = mapp;
+
+	tran->tran_tgtmap = (scsi_hba_tgtmap_t *)tgtmap;
+	*handle = (scsi_hba_tgtmap_t *)tgtmap;
+
+	/*
+	 * We have now set tran_tgtmap, marking the tran as using tgtmap
+	 * enumeration services.  To prevent the generation of legacy spi
+	 * 'binding-set' compatible forms, remove the 'scsi-binding-set'
+	 * property.
+	 */
+	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, self,
+	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "scsi-binding-set",
+	    &scsi_binding_set) == DDI_PROP_SUCCESS) {
+		if (strcmp(scsi_binding_set, scsi_binding_set_spi) == 0)
+			(void) ndi_prop_remove(DDI_DEV_T_NONE, self,
+			    "scsi-binding-set");
+		ddi_prop_free(scsi_binding_set);
+	}
+	return (DDI_SUCCESS);
+}
+
+void
+scsi_hba_tgtmap_destroy(scsi_hba_tgtmap_t *handle)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)handle;
+	dev_info_t		*self = tgtmap->tgtmap_tran->tran_iport_dip;
+	int			i;
+
+	for (i = 0; i < SCSI_TGT_NTYPES; i++) {
+		if (tgtmap->tgtmap_dam[i]) {
+			SCSI_HBA_LOG((_LOGTGT, self, NULL,
+			    "%s", damap_name(tgtmap->tgtmap_dam[i])));
+
+			damap_destroy(tgtmap->tgtmap_dam[i]);
+		}
+	}
+	kmem_free(tgtmap, sizeof (*tgtmap));
+}
+
+static int
+scsi_tgtmap_sync(scsi_hba_tgtmap_t *handle)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)handle;
+	dev_info_t		*self = tgtmap->tgtmap_tran->tran_iport_dip;
+	int			empty = 1;
+	int			i;
+
+	for (i = 0; i < SCSI_TGT_NTYPES; i++) {
+		if (tgtmap->tgtmap_dam[i]) {
+			SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s sync begin",
+			    damap_name(tgtmap->tgtmap_dam[i])));
+
+			/* return 1 if all maps ended up empty */
+			empty &= damap_sync(tgtmap->tgtmap_dam[i]);
+
+			SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s sync end",
+			    damap_name(tgtmap->tgtmap_dam[i])));
+		}
+	}
+	return (empty);
+}
+
+int
+scsi_hba_tgtmap_set_begin(scsi_hba_tgtmap_t *handle)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)handle;
+	dev_info_t		*self = tgtmap->tgtmap_tran->tran_iport_dip;
+	char			*context;
+	int			rv = DDI_SUCCESS;
+	int			i;
+
+	for (i = 0; i < SCSI_TGT_NTYPES; i++) {
+		if (tgtmap->tgtmap_dam[i] == NULL)
+			continue;
+
+		context = damap_name(tgtmap->tgtmap_dam[i]);
+
+		if (i == SCSI_TGT_SCSI_DEVICE) {
+			/*
+			 * In scsi_device context, so we have the 'context'
+			 * string, diagnose the case where the tgtmap caller
+			 * is failing to make forward progress, i.e. the caller
+			 * is never completing an observation, and calling
+			 * scsi_hbg_tgtmap_set_end. If this occurs, the solaris
+			 * target/lun state may be out of sync with hardware.
+			 */
+			if (tgtmap->tgtmap_reports++ >=
+			    scsi_hba_tgtmap_reports_max) {
+				tgtmap->tgtmap_noisy++;
+				if (tgtmap->tgtmap_noisy == 1)
+					SCSI_HBA_LOG((_LOG(WARN), self, NULL,
+					    "%s: failing to complete a tgtmap "
+					    "observation", context));
+			}
+		}
+
+		if (damap_addrset_begin(
+		    tgtmap->tgtmap_dam[i]) != DAM_SUCCESS) {
+			SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s FAIL", context));
+			rv = DDI_FAILURE;
+			continue;
+		}
+
+		SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s", context));
+	}
+	return (rv);
+}
+
+
+int
+scsi_hba_tgtmap_set_add(scsi_hba_tgtmap_t *handle,
+    scsi_tgtmap_tgt_type_t tgt_type, char *tgt_addr, void *tgt_priv)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)handle;
+	dev_info_t		*self = tgtmap->tgtmap_tran->tran_iport_dip;
+
+	if (tgt_type >= SCSI_TGT_NTYPES || !tgtmap->tgtmap_dam[tgt_type])
+		return (DDI_FAILURE);
+
+	SCSI_HBA_LOG((_LOGTGT, self, NULL,
+	    "%s @%s", damap_name(tgtmap->tgtmap_dam[tgt_type]), tgt_addr));
+
+	return ((damap_addrset_add(tgtmap->tgtmap_dam[tgt_type], tgt_addr,
+	    NULL, NULL, tgt_priv) == DAM_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+/*ARGSUSED*/
+int
+scsi_hba_tgtmap_set_end(scsi_hba_tgtmap_t *handle, uint_t flags)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)handle;
+	dev_info_t		*self = tgtmap->tgtmap_tran->tran_iport_dip;
+	char			*context;
+	int			rv = DDI_SUCCESS;
+	int			i;
+
+	tgtmap->tgtmap_reports = tgtmap->tgtmap_noisy = 0;
+
+	for (i = 0; i < SCSI_TGT_NTYPES; i++) {
+		if (tgtmap->tgtmap_dam[i] == NULL)
+			continue;
+
+		context = damap_name(tgtmap->tgtmap_dam[i]);
+		if (damap_addrset_end(
+		    tgtmap->tgtmap_dam[i], 0) != DAM_SUCCESS) {
+			SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s FAIL", context));
+			rv = DDI_FAILURE;
+			continue;
+		}
+
+		SCSI_HBA_LOG((_LOGTGT, self, NULL, "%s", context));
+	}
+	return (rv);
+}
+
+int
+scsi_hba_tgtmap_tgt_add(scsi_hba_tgtmap_t *handle,
+    scsi_tgtmap_tgt_type_t tgt_type, char *tgt_addr, void *tgt_priv)
+
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)handle;
+	dev_info_t		*self = tgtmap->tgtmap_tran->tran_iport_dip;
+
+	if (tgt_type >= SCSI_TGT_NTYPES || !tgtmap->tgtmap_dam[tgt_type])
+		return (DDI_FAILURE);
+
+	SCSI_HBA_LOG((_LOGTGT, self, NULL,
+	    "%s @%s", damap_name(tgtmap->tgtmap_dam[tgt_type]), tgt_addr));
+
+	return ((damap_addr_add(tgtmap->tgtmap_dam[tgt_type], tgt_addr, NULL,
+	    NULL, tgt_priv) == DAM_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+int
+scsi_hba_tgtmap_tgt_remove(scsi_hba_tgtmap_t *handle,
+    scsi_tgtmap_tgt_type_t tgt_type, char *tgt_addr)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)handle;
+	dev_info_t		*self = tgtmap->tgtmap_tran->tran_iport_dip;
+
+	if (tgt_type >= SCSI_TGT_NTYPES || !tgtmap->tgtmap_dam[tgt_type])
+		return (DDI_FAILURE);
+
+	SCSI_HBA_LOG((_LOGTGT, self, NULL,
+	    "%s @%s", damap_name(tgtmap->tgtmap_dam[tgt_type]), tgt_addr));
+
+	return ((damap_addr_del(tgtmap->tgtmap_dam[tgt_type],
+	    tgt_addr) == DAM_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+int
+scsi_hba_tgtmap_lookup(scsi_hba_tgtmap_t *handle,
+    char *tgt_addr, scsi_tgtmap_tgt_type_t *r_type)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)handle;
+	dev_info_t		*self = tgtmap->tgtmap_tran->tran_iport_dip;
+	damap_id_t		tgtid;
+	int			i;
+
+	for (i = 0; i < SCSI_TGT_NTYPES; i++) {
+		tgtid = damap_lookup(tgtmap->tgtmap_dam[i], tgt_addr);
+		if (tgtid != NODAM) {
+			*r_type = i;
+			SCSI_HBA_LOG((_LOG(3), self, NULL,
+			    "%s @%s found: type %d",
+			    damap_name(tgtmap->tgtmap_dam[i]), tgt_addr, i));
+			damap_id_rele(tgtmap->tgtmap_dam[i], tgtid);
+			return (DDI_SUCCESS);
+		}
+	}
+
+	SCSI_HBA_LOG((_LOG(3), self, NULL,
+	    "%s%d.tgtmap @%s not found",
+	    ddi_driver_name(self), ddi_get_instance(self), tgt_addr));
+	return (DDI_FAILURE);
+}
+
+/*
+ * Return the unit-address of an 'iport' node, or NULL for non-iport node.
+ */
+char *
+scsi_hba_iport_unit_address(dev_info_t *self)
+{
+	/*
+	 * NOTE: Since 'self' could be a SCSA iport node or a SCSA HBA node,
+	 * we can't use SCSA flavors: the flavor of a SCSA HBA node is not
+	 * established/owned by SCSA, it is established by the nexus that
+	 * created the SCSA HBA node (PCI) as a child.
+	 *
+	 * NOTE: If we want to support a node_name other than "iport" for
+	 * an iport node then we can add support for a "scsa-iport-node-name"
+	 * property on the SCSA HBA node.  A SCSA HBA driver would set this
+	 * property on the SCSA HBA node prior to using the iport API.
+	 */
+	if (strcmp(ddi_node_name(self), "iport") == 0)
+		return (ddi_get_name_addr(self));
+	else
+		return (NULL);
+}
+
+/*
+ * Define a SCSI initiator port (bus/channel) for an HBA card that needs to
+ * support multiple SCSI ports, but only has a single HBA devinfo node. This
+ * function should be called from the HBA's attach(9E) implementation (when
+ * processing the HBA devinfo node attach) after the number of SCSI ports on
+ * the card is known or when the HBA driver DR handler detects a new port.
+ * The function returns 0 on failure and 1 on success.
+ *
+ * The implementation will add the port value into the "scsi-iports" property
+ * value maintained on the HBA node as. These properties are used by the generic
+ * scsi bus_config implementation to dynamicaly enumerate the specified iport
+ * children. The enumeration code will, on demand, create the appropriate
+ * iport children with a SCSI_ADDR_PROP_IPORTUA unit address. This node will
+ * bind to the same driver as the HBA node itself. This means that an HBA
+ * driver that uses iports should expect probe(9E), attach(9E), and detach(9E)
+ * calls on the iport children of the HBA.  If configuration for all ports was
+ * already done during HBA node attach, the driver should just return
+ * DDI_SUCCESS when confronted with an iport node.
+ *
+ * A maximum of 32 iport ports are supported per HBA devinfo node.
+ *
+ * A NULL "port" can be used to indicate that the framework should enumerate
+ * target children on the HBA node itself, in addition to enumerating target
+ * children on any iport nodes declared. There are two reasons that an HBA may
+ * wish to have target children enumerated on both the HBA node and iport
+ * node(s):
+ *
+ *   o  If, in the past, HBA hardware had only a single physical port but now
+ *      supports multiple physical ports, the updated driver that supports
+ *      multiple physical ports may want to avoid /devices path upgrade issues
+ *      by enumerating the first physical port under the HBA instead of as a
+ *      iport.
+ *
+ *   o  Some hardware RAID HBA controllers (mlx, chs, etc) support multiple
+ *      SCSI physical ports configured so that various physical devices on
+ *      the physical ports are amalgamated into virtual devices on a virtual
+ *      port.  Amalgamated physical devices no longer appear to the host OS
+ *      on the physical ports, but other non-amalgamated devices may still be
+ *      visible on the physical ports.  These drivers use a model where the
+ *      physical ports are iport nodes and the HBA node is the virtual port to
+ *      the configured virtual devices.
+ */
+int
+scsi_hba_iport_register(dev_info_t *self, char *port)
+{
+	unsigned int ports = 0;
+	int rval, i;
+	char **iports, **newiports;
+
+	ASSERT(self);
+	if (self == NULL)
+		return (DDI_FAILURE);
+
+	rval = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, self,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "scsi-iports", &iports,
+	    &ports);
+
+	if (ports >= SCSI_HBA_MAX_IPORTS) {
+		ddi_prop_free(iports);
+		return (DDI_FAILURE);
+	}
+
+	if (rval == DDI_PROP_SUCCESS) {
+		for (i = 0; i < ports; i++) {
+			if (strcmp(port, iports[i]) == 0) {
+				/* iport already registered */
+				ddi_prop_free(iports);
+				return (DDI_SUCCESS);
+			}
+		}
+	}
+
+	newiports = kmem_alloc((sizeof (char *) * (ports + 1)), KM_SLEEP);
+
+	for (i = 0; i < ports; i++) {
+		newiports[i] = strdup(iports[i]);
+	}
+	newiports[ports] = strdup(port);
+	ports++;
+
+	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, self,
+	    "scsi-iports", newiports, ports) != DDI_PROP_SUCCESS) {
+		SCSI_HBA_LOG((_LOG(WARN), self, NULL,
+		    "failed to establish %s %s",
+		    SCSI_ADDR_PROP_IPORTUA, port));
+		rval = DDI_FAILURE;
+	} else {
+		rval = DDI_SUCCESS;
+	}
+
+	/* If there is iport exist, free property */
+	if (ports > 1)
+		ddi_prop_free(iports);
+	for (i = 0; i < ports; i++) {
+		strfree(newiports[i]);
+	}
+	kmem_free(newiports, (sizeof (char *)) * ports);
+
+	return (rval);
+}
+
+/*
+ * Check if the HBA has any scsi_hba_iport_register()ed children.
+ */
+int
+scsi_hba_iport_exist(dev_info_t *self)
+{
+	unsigned int ports = 0;
+	char **iports;
+	int rval;
+
+	rval = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, self,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "scsi-iports", &iports,
+	    &ports);
+
+	if (rval != DDI_PROP_SUCCESS)
+		return (0);
+
+	/* If there is now at least 1 iport, then iports is valid */
+	if (ports > 0) {
+		rval = 1;
+	} else
+		rval = 0;
+	ddi_prop_free(iports);
+
+	return (rval);
+}
+
+dev_info_t *
+scsi_hba_iport_find(dev_info_t *self, char *portnm)
+{
+	char		*addr = NULL;
+	char		**iports;
+	unsigned int	num_iports = 0;
+	int		rval = DDI_FAILURE;
+	int		i = 0;
+	dev_info_t	*child = NULL;
+
+	/* check to see if this is an HBA that defined scsi iports */
+	rval = ddi_prop_lookup_string_array(DDI_DEV_T_ANY, self,
+	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "scsi-iports", &iports,
+	    &num_iports);
+
+	if (rval != DDI_SUCCESS) {
+		return (NULL);
+	}
+	ASSERT(num_iports > 0);
+
+	/* check to see if this port was registered */
+	for (i = 0; i < num_iports; i++) {
+		if (strcmp(iports[i], portnm) == 0)
+			break;
+	}
+
+	if (i == num_iports) {
+		child = NULL;
+		goto out;
+	}
+
+	addr = kmem_zalloc(SCSI_MAXNAMELEN, KM_SLEEP);
+	(void) snprintf(addr, SCSI_MAXNAMELEN, "iport@%s", portnm);
+	rval = ndi_devi_config_one(self, addr, &child, NDI_NO_EVENT);
+	kmem_free(addr, SCSI_MAXNAMELEN);
+
+	if (rval != DDI_SUCCESS) {
+		child = NULL;
+	}
+out:
+	ddi_prop_free(iports);
+	return (child);
+}
+
 /*
  * Search/create the specified iport node
  */
 static dev_info_t *
-scsi_hba_bus_config_port(dev_info_t *self, char *nameaddr)
-{
-	dev_info_t	*child;
-	char		*mcompatible, *addr;
+scsi_hba_bus_config_port(dev_info_t *self, char *nameaddr, scsi_enum_t se)
+{
+	dev_info_t	*child;		/* iport child of HBA node */
+	scsi_hba_tran_t	*tran;
+	char		*addr;
+	char		*compat;
 
 	/*
 	 * See if the iport node already exists.
 	 */
-
+	addr = nameaddr + strlen("iport@");
 	if (child = ndi_devi_findchild(self, nameaddr)) {
+		if (ndi_devi_device_isremoved(child)) {
+			if ((se == SE_HP) || !ndi_dev_is_hotplug_node(child)) {
+				if (ndi_devi_device_insert(child))
+					SCSI_HBA_LOG((_LOGCFG, self, NULL,
+					    "devinfo iport@%s device_reinsert",
+					    addr));
+			} else
+				return (NULL);
+		}
 		return (child);
 	}
 
+
+	/*
+	 * If config based on scsi_hba_iportmap API, only allow create
+	 * from hotplug.
+	 */
+	tran = ndi_flavorv_get(self, SCSA_FLAVOR_SCSI_DEVICE);
+	ASSERT(tran);
+	if (tran->tran_iportmap && (se != SE_HP))
+		return (NULL);
+
 	/* allocate and initialize a new "iport" node */
-	ndi_devi_alloc_sleep(self, "iport", DEVI_SID_NODEID, &child);
+	ndi_devi_alloc_sleep(self, "iport",
+	    (se == SE_HP) ? DEVI_SID_HP_NODEID : DEVI_SID_NODEID,
+	    &child);
 	ASSERT(child);
 	/*
 	 * Set the flavor of the child to be IPORT flavored
@@ -3864,12 +8546,13 @@
 	ndi_flavor_set(child, SCSA_FLAVOR_IPORT);
 
 	/*
-	 * Add the "scsi-iport" addressing property for this child. This
-	 * property is used to identify a iport node, and to represent the
+	 * Add the SCSI_ADDR_PROP_IPORTUA addressing property for this child.
+	 * This property is used to identify a iport node, and to represent the
 	 * nodes @addr form via node properties.
 	 *
 	 * Add "compatible" property to the "scsi-iport" node to cause it bind
-	 * to the same driver as the HBA  driver.
+	 * to the same driver as the HBA  driver. Use the "driver" name
+	 * instead of the "binding name" to distinguish from hw node.
 	 *
 	 * Give the HBA a chance, via tran_set_name_prop, to set additional
 	 * iport node properties or to change the "compatible" binding
@@ -3878,31 +8561,40 @@
 	 * NOTE: the order of these operations is important so that
 	 * scsi_hba_iport works when called.
 	 */
-	mcompatible = ddi_binding_name(self);
-	addr = nameaddr + strlen("iport@");
-
+	compat = (char *)ddi_driver_name(self);
 	if ((ndi_prop_update_string(DDI_DEV_T_NONE, child,
-	    "scsi-iport", addr) != DDI_PROP_SUCCESS) ||
+	    SCSI_ADDR_PROP_IPORTUA, addr) != DDI_PROP_SUCCESS) ||
 	    (ndi_prop_update_string_array(DDI_DEV_T_NONE, child,
-	    "compatible", &mcompatible, 1) != DDI_PROP_SUCCESS) ||
+	    "compatible", &compat, 1) != DDI_PROP_SUCCESS) ||
 	    ddi_pathname_obp_set(child, NULL) != DDI_SUCCESS) {
-		SCSI_HBA_LOG((_LOG(WARN), self, NULL,
-		    "scsi_hba_bus_config_port:%s failed dynamic decoration",
+		SCSI_HBA_LOG((_LOG_NF(WARN), "%s failed dynamic decoration",
 		    nameaddr));
 		(void) ddi_remove_child(child, 0);
 		child = NULL;
 	} else {
-		if (ddi_initchild(self, child) != DDI_SUCCESS) {
+		/*
+		 * Online/attach in order to get events so devfsadm will
+		 * create public names.
+		 */
+		ndi_hold_devi(child);
+		if (ndi_devi_online(child, 0) != NDI_SUCCESS) {
+			ndi_rele_devi(child);
 			ndi_prop_remove_all(child);
 			(void) ndi_devi_free(child);
 			child = NULL;
-		}
+		} else
+			ndi_rele_devi(child);
 	}
 
 	return (child);
 }
 
 #ifdef	sparc
+/*
+ * Future: When iportmap boot support is added, consider rewriting this to
+ * perform a scsi_hba_bus_config(BUS_CONFIG_ALL) on self (HBA) followed by
+ * a scsi_hba_bus_config(BUS_CONFIG_ONE) on each child of self (each iport).
+ */
 /* ARGSUSED */
 static int
 scsi_hba_bus_config_prom_node(dev_info_t *self, uint_t flags,
@@ -3930,7 +8622,7 @@
 
 	ret = NDI_FAILURE;
 
-	ndi_devi_enter(self, &circ);
+	scsi_hba_devi_enter(self, &circ);
 
 	/* create iport nodes for each scsi port/bus */
 	for (i = 0; i < num_iports; i++) {
@@ -3938,7 +8630,7 @@
 		/* Prepend the iport name */
 		(void) snprintf(addr, SCSI_MAXNAMELEN, "iport@%s",
 		    iports[i]);
-		if (pdip = scsi_hba_bus_config_port(self, addr)) {
+		if (pdip = scsi_hba_bus_config_port(self, addr, SE_HP)) {
 			if (ndi_busop_bus_config(self, NDI_NO_EVENT,
 			    BUS_CONFIG_ONE, addr, &pdip, 0) !=
 			    NDI_SUCCESS) {
@@ -3956,7 +8648,7 @@
 		}
 	}
 
-	ndi_devi_exit(self, circ);
+	scsi_hba_devi_exit(self, circ);
 
 	kmem_free(addr, SCSI_MAXNAMELEN);
 
@@ -3990,7 +8682,7 @@
 
 	ASSERT(num_iports > 0);
 
-	ndi_devi_enter(self, &circ);
+	scsi_hba_devi_enter(self, &circ);
 
 	switch (op) {
 	case BUS_CONFIG_ONE:
@@ -3999,15 +8691,12 @@
 		if ((nameaddr == NULL) ||
 		    (strncmp(nameaddr, "iport@", strlen("iport@")) != 0)) {
 			ret = NDI_FAILURE;
-			ndi_devi_exit(self, circ);
+			scsi_hba_devi_exit(self, circ);
 			ddi_prop_free(iports);
 			return (ret);
 		}
 
-		/*
-		 * parse the port number from "iport@%x"
-		 * XXX use atoi (hex)
-		 */
+		/* parse the port number from "iport@%s" */
 		addr = nameaddr + strlen("iport@");
 
 		/* check to see if this port was registered */
@@ -4021,11 +8710,12 @@
 			break;
 		}
 
-		/* create the iport node */
-		if (scsi_hba_bus_config_port(self, nameaddr)) {
+		/* create the iport node child */
+		if (scsi_hba_bus_config_port(self, nameaddr, SE_BUSCONFIG)) {
 			ret = NDI_SUCCESS;
 		}
 		break;
+
 	case BUS_CONFIG_ALL:
 	case BUS_CONFIG_DRIVER:
 		addr = kmem_zalloc(SCSI_MAXNAMELEN, KM_SLEEP);
@@ -4035,7 +8725,8 @@
 			/* Prepend the iport name */
 			(void) snprintf(addr, SCSI_MAXNAMELEN, "iport@%s",
 			    iports[i]);
-			(void) scsi_hba_bus_config_port(self, addr);
+			(void) scsi_hba_bus_config_port(self, addr,
+			    SE_BUSCONFIG);
 		}
 
 		kmem_free(addr, SCSI_MAXNAMELEN);
@@ -4050,115 +8741,1125 @@
 		 */
 		flags &= (~NDI_PROMNAME);
 #endif
+		flags |= NDI_MDI_FALLBACK;	/* devinfo&pathinfo children */
 		ret = ndi_busop_bus_config(self, flags, op,
 		    arg, childp, 0);
 	}
-	ndi_devi_exit(self, circ);
+	scsi_hba_devi_exit(self, circ);
 
 	ddi_prop_free(iports);
 
 	return (ret);
 }
 
-
-/*ARGSUSED*/
-static int
-scsi_hba_bus_config(dev_info_t *self, uint_t flag, ddi_bus_config_op_t op,
-    void *arg, dev_info_t **childp)
-{
-	scsi_hba_tran_t	*tran = NULL;
-
-	tran = ddi_get_driver_private(self);
-
-	if (tran && (tran->tran_hba_flags & SCSI_HBA_HBA)) {
-#ifdef	sparc
-		char *nameaddr = NULL;
-		nameaddr = (char *)arg;
-		switch (op) {
-		case BUS_CONFIG_ONE:
-			if (nameaddr == NULL)
-				return (NDI_FAILURE);
-			if (strncmp(nameaddr, "iport", strlen("iport")) == 0) {
-				break;
-			}
-			/*
-			 * If this operation is not against an iport node, it's
-			 * possible the operation is requested to configure
-			 * root disk by OBP. Unfortunately, prom path is without
-			 * iport string in the boot path.
-			 */
-			if (strncmp(nameaddr, "disk@", strlen("disk@")) == 0) {
-				return (scsi_hba_bus_config_prom_node(self,
-				    flag, arg, childp));
+typedef struct impl_scsi_iportmap {
+	dev_info_t	*iportmap_hba_dip;
+	damap_t		*iportmap_dam;
+} impl_scsi_iportmap_t;
+
+static void
+scsi_iportmap_config(void *arg, damap_t *mapp, damap_id_list_t id_list)
+{
+	dev_info_t	*self = (dev_info_t *)arg;
+	int		circ;
+	damap_id_t	tgtid;
+	char		nameaddr[SCSI_MAXNAMELEN];
+	char		*iport_addr;
+
+	scsi_hba_devi_enter(self, &circ);
+
+	for (tgtid = damap_id_next(mapp, id_list, NODAM);
+	    tgtid != NODAM;
+	    tgtid = damap_id_next(mapp, id_list, tgtid)) {
+		iport_addr = damap_id2addr(mapp, tgtid);
+		SCSI_HBA_LOG((_LOGIPT, self, NULL,
+		    "%s @%s", damap_name(mapp), iport_addr));
+
+		(void) snprintf(nameaddr, sizeof (nameaddr),
+		    "iport@%s", iport_addr);
+		(void) scsi_hba_bus_config_port(self, nameaddr, SE_HP);
+	}
+
+	scsi_hba_devi_exit(self, circ);
+}
+
+static void
+scsi_iportmap_unconfig(void *arg, damap_t *mapp, damap_id_list_t id_list)
+{
+	dev_info_t	*self = arg;
+	dev_info_t	*child;		/* iport child of HBA node */
+	int		circ;
+	damap_id_t	tgtid;
+	char		*addr;
+	char		nameaddr[SCSI_MAXNAMELEN];
+	scsi_hba_tran_t	*tran;
+	int		empty;
+
+	for (tgtid = damap_id_next(mapp, id_list, NODAM);
+	    tgtid != NODAM;
+	    tgtid = damap_id_next(mapp, id_list, tgtid)) {
+		addr = damap_id2addr(mapp, tgtid);
+		SCSI_HBA_LOG((_LOGIPT, self, NULL,
+		    "%s @%s", damap_name(mapp), addr));
+
+		(void) snprintf(nameaddr, sizeof (nameaddr), "iport@%s", addr);
+		scsi_hba_devi_enter(self, &circ);
+		if ((child = ndi_devi_findchild(self, nameaddr)) == NULL) {
+			scsi_hba_devi_exit(self, circ);
+			continue;
+		}
+
+		tran = ddi_get_driver_private(child);
+		ASSERT(tran);
+
+		ndi_hold_devi(child);
+		scsi_hba_devi_exit(self, circ);
+
+		/*
+		 * A begin/end (clear) against the iport's
+		 * tgtmap will trigger unconfigure of all
+		 * targets on the iport.
+		 *
+		 * Future: This bit of code only works if the
+		 * target map reporting style is are full
+		 * reports and not per-address. Maybe we
+		 * should plan on handling this by
+		 * auto-unconfiguration when destroying the
+		 * target map(s).
+		 */
+		(void) scsi_hba_tgtmap_set_begin(
+		    tran->tran_tgtmap);
+		(void) scsi_hba_tgtmap_set_end(
+		    tran->tran_tgtmap, 0);
+
+		/* wait for unconfigure */
+		empty = scsi_tgtmap_sync(tran->tran_tgtmap);
+
+		scsi_hba_devi_enter(self, &circ);
+		ndi_rele_devi(child);
+
+		/* If begin/end/sync ends in empty map, offline/remove. */
+		if (empty) {
+			if (ndi_devi_offline(child,
+			    NDI_DEVFS_CLEAN | NDI_DEVI_REMOVE) == DDI_SUCCESS) {
+				SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+				    "devinfo iport@%s offlined and removed",
+				    addr));
+			} else if (ndi_devi_device_remove(child)) {
+				/* Offline/rem failed, note new device_remove */
+				SCSI_HBA_LOG((_LOGUNCFG, self, NULL,
+				    "devinfo iport@%s offline failed, "
+				    "device_remove", addr));
 			}
-			break;
-		default:
-			break;
-		}
-#endif
-		/*
-		 * The request is to configure multi-port HBA.
-		 * Now start to configure iports, for the end
-		 * devices attached to iport, should be configured
-		 * by bus_configure routine of iport
-		 */
-		return (scsi_hba_bus_config_iports(self, flag, op, arg,
-		    childp));
-	}
-
-#ifdef sparc
-	if (scsi_hba_iport_unit_address(self)) {
-		flag &= (~NDI_PROMNAME);
-	}
-#endif
-	if (tran && tran->tran_bus_config) {
-		return (tran->tran_bus_config(self, flag, op, arg, childp));
-	}
-
-	/*
-	 * Force reprobe for BUS_CONFIG_ONE or when manually reconfiguring
-	 * via devfsadm(1m) to emulate deferred attach.
-	 * Reprobe only discovers driver.conf enumerated nodes, more
-	 * dynamic implementations probably require their own bus_config.
-	 */
-	if ((op == BUS_CONFIG_ONE) || (flag & NDI_DRV_CONF_REPROBE))
-		flag |= NDI_CONFIG_REPROBE;
-
-	return (ndi_busop_bus_config(self, flag, op, arg, childp, 0));
+		}
+		scsi_hba_devi_exit(self, circ);
+	}
+
+}
+
+int
+scsi_hba_iportmap_create(dev_info_t *self, clock_t settle, int n_entries,
+    scsi_hba_iportmap_t **handle)
+{
+	scsi_hba_tran_t		*tran;
+	damap_t			*mapp;
+	char			context[64];
+	impl_scsi_iportmap_t	*iportmap;
+
+	if (self == NULL || settle == 0 || n_entries == 0 || handle == NULL)
+		return (DDI_FAILURE);
+
+	*handle = NULL;
+
+	if (scsi_hba_iport_unit_address(self) != NULL)
+		return (DDI_FAILURE);
+
+	tran = (scsi_hba_tran_t *)ddi_get_driver_private(self);
+	ASSERT(tran);
+	if (tran == NULL)
+		return (DDI_FAILURE);
+
+	(void) snprintf(context, sizeof (context), "%s%d.iportmap",
+	    ddi_driver_name(self), ddi_get_instance(self));
+
+	if (damap_create(context, n_entries, DAMAP_REPORT_PERADDR, settle,
+	    NULL, NULL, NULL, self,
+	    scsi_iportmap_config, scsi_iportmap_unconfig, &mapp) !=
+	    DAM_SUCCESS) {
+		return (DDI_FAILURE);
+	}
+	iportmap = kmem_zalloc(sizeof (*iportmap), KM_SLEEP);
+	iportmap->iportmap_hba_dip = self;
+	iportmap->iportmap_dam = mapp;
+
+	tran->tran_iportmap = (scsi_hba_iportmap_t *)iportmap;
+	*handle = (scsi_hba_iportmap_t *)iportmap;
+
+	SCSI_HBA_LOG((_LOGIPT, self, NULL, "%s", damap_name(mapp)));
+	return (DDI_SUCCESS);
+}
+
+void
+scsi_hba_iportmap_destroy(scsi_hba_iportmap_t *handle)
+{
+	impl_scsi_iportmap_t	*iportmap = (impl_scsi_iportmap_t *)handle;
+	dev_info_t		*self = iportmap->iportmap_hba_dip;
+
+	SCSI_HBA_LOG((_LOGIPT, self, NULL,
+	    "%s", damap_name(iportmap->iportmap_dam)));
+
+	damap_destroy(iportmap->iportmap_dam);
+	kmem_free(iportmap, sizeof (*iportmap));
+}
+
+int
+scsi_hba_iportmap_iport_add(scsi_hba_iportmap_t *handle,
+    char *iport_addr, void *iport_priv)
+{
+	impl_scsi_iportmap_t	*iportmap = (impl_scsi_iportmap_t *)handle;
+	dev_info_t		*self = iportmap->iportmap_hba_dip;
+
+	SCSI_HBA_LOG((_LOGIPT, self, NULL,
+	    "%s @%s", damap_name(iportmap->iportmap_dam), iport_addr));
+
+	return ((damap_addr_add(iportmap->iportmap_dam, iport_addr, NULL,
+	    NULL, iport_priv) == DAM_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+int
+scsi_hba_iportmap_iport_remove(scsi_hba_iportmap_t *handle,
+    char *iport_addr)
+{
+	impl_scsi_iportmap_t	*iportmap = (impl_scsi_iportmap_t *)handle;
+	dev_info_t		*self = iportmap->iportmap_hba_dip;
+
+	SCSI_HBA_LOG((_LOGIPT, self, NULL,
+	    "%s @%s", damap_name(iportmap->iportmap_dam), iport_addr));
+
+	return ((damap_addr_del(iportmap->iportmap_dam,
+	    iport_addr) == DAM_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+int
+scsi_hba_iportmap_lookup(scsi_hba_iportmap_t *handle,
+    char *iport_addr)
+{
+	impl_scsi_iportmap_t	*iportmap = (impl_scsi_iportmap_t *)handle;
+	dev_info_t		*self = iportmap->iportmap_hba_dip;
+	damap_id_t		iportid;
+
+	iportid = damap_lookup(iportmap->iportmap_dam, iport_addr);
+	if (iportid != NODAM) {
+		SCSI_HBA_LOG((_LOG(3), self, NULL,
+		    "%s @%s found",
+		    damap_name(iportmap->iportmap_dam), iport_addr));
+		damap_id_rele(iportmap->iportmap_dam, iportid);
+		return (DDI_SUCCESS);
+	}
+
+	SCSI_HBA_LOG((_LOG(3), self, NULL,
+	    "%s @%s not found",
+	    damap_name(iportmap->iportmap_dam), iport_addr));
+	return (DDI_FAILURE);
+}
+
+static void
+scsi_lunmap_config(void *arg, damap_t *lundam, damap_id_list_t id_list)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)arg;
+	scsi_hba_tran_t		*tran = tgtmap->tgtmap_tran;
+	dev_info_t		*self = tran->tran_iport_dip;
+	damap_id_t		lunid;
+	char			*addr;
+
+	/* iterate over the LUNS we need to config */
+	for (lunid = damap_id_next(lundam, id_list, NODAM);
+	    lunid != NODAM;
+	    lunid = damap_id_next(lundam, id_list, lunid)) {
+		addr = damap_id2addr(lundam, lunid);
+		SCSI_HBA_LOG((_LOGLUN, self, NULL,
+		    "%s @%s", damap_name(lundam), addr));
+
+		(void) scsi_hba_bus_configone_addr(self, addr, SE_HP);
+	}
+}
+
+static void
+scsi_lunmap_unconfig(void *arg, damap_t *lundam, damap_id_list_t id_list)
+{
+	impl_scsi_tgtmap_t	*tgtmap = (impl_scsi_tgtmap_t *)arg;
+	scsi_hba_tran_t		*tran = tgtmap->tgtmap_tran;
+	dev_info_t		*self = tran->tran_iport_dip;
+	damap_id_t		lunid;
+	char			*addr;
+
+	for (lunid = damap_id_next(lundam, id_list, NODAM);
+	    lunid != NODAM;
+	    lunid = damap_id_next(lundam, id_list, lunid)) {
+		addr = damap_id2addr(lundam, lunid);
+		SCSI_HBA_LOG((_LOGLUN, self, NULL,
+		    "%s @%s", damap_name(lundam), addr));
+
+		scsi_hba_bus_unconfigone_addr(self, addr);
+	}
+}
+
+static int
+scsi_lunmap_create(dev_info_t *self, impl_scsi_tgtmap_t *tgtmap, char *taddr)
+{
+	char			context[64];
+	damap_t			*tgtdam;
+	damap_id_t		tgtid;
+	damap_t			*lundam;
+
+	(void) snprintf(context, sizeof (context), "%s%d.%s.lunmap",
+	    ddi_driver_name(self), ddi_get_instance(self), taddr);
+
+	tgtdam = tgtmap->tgtmap_dam[SCSI_TGT_SCSI_DEVICE];
+	tgtid = damap_lookup(tgtdam, taddr);
+	if (tgtid == NODAM) {
+		SCSI_HBA_LOG((_LOG(1), self, NULL,
+		    "target %s not found", context));
+		return (DDI_FAILURE);
+	}
+
+	lundam = damap_id_priv_get(tgtdam, tgtid);
+	if (lundam) {
+		SCSI_HBA_LOG((_LOG(1), self, NULL,
+		    "lunmap %s already created", context));
+		damap_id_rele(tgtdam, tgtid);
+		return (DDI_FAILURE);
+	}
+
+	/* NOTE: expected ref at tgtid/taddr: 2: caller + lookup. */
+	SCSI_HBA_LOG((_LOGLUN, self, NULL, "%s creat, id %d ref %d",
+	    context, tgtid, damap_id_ref(tgtdam, tgtid)));
+
+	/* create lundam */
+	if (damap_create(context, LUNMAPSIZE, DAMAP_REPORT_FULLSET, 1,
+	    NULL, NULL, NULL,
+	    tgtmap, scsi_lunmap_config, scsi_lunmap_unconfig,
+	    &lundam) != DAM_SUCCESS) {
+		SCSI_HBA_LOG((_LOG(1), self, NULL,
+		    "%s create failed, id %d ref %d",
+		    context, tgtid, damap_id_ref(tgtdam, tgtid)));
+		damap_id_rele(tgtdam, tgtid);
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Return with damap_id_hold at tgtid/taddr from damap_lookup to
+	 * account for damap_id_prv_set below.
+	 */
+	damap_id_priv_set(tgtdam, tgtid, lundam);
+	return (DDI_SUCCESS);
+}
+
+static void
+scsi_lunmap_destroy(dev_info_t *self, impl_scsi_tgtmap_t *tgtmap, char *taddr)
+{
+	char			context[64];
+	damap_t			*tgtdam;
+	damap_id_t		tgtid;
+	damap_t			*lundam;
+
+	(void) snprintf(context, sizeof (context), "%s%d.%s.lunmap",
+	    ddi_driver_name(self), ddi_get_instance(self), taddr);
+
+	tgtdam = tgtmap->tgtmap_dam[SCSI_TGT_SCSI_DEVICE];
+	tgtid = damap_lookup(tgtdam, taddr);
+	if (tgtid == NODAM) {
+		SCSI_HBA_LOG((_LOG(1), self, NULL,
+		    "target %s not found", context));
+		return;
+	}
+
+	lundam = (damap_t *)damap_id_priv_get(tgtdam, tgtid);
+	if (lundam == NULL) {
+		damap_id_rele(tgtdam, tgtid);		/* from damap_lookup */
+		SCSI_HBA_LOG((_LOG(1), self, NULL,
+		    "lunmap %s already destroyed", context));
+		return;
+	}
+
+	/* NOTE: expected ref at tgtid/taddr: 3: priv_set + caller + lookup. */
+	SCSI_HBA_LOG((_LOGLUN, self, NULL, "%s, id %d ref %d",
+	    damap_name(lundam), tgtid, damap_id_ref(tgtdam, tgtid)));
+
+	/*
+	 * A begin/end (clear) against a target's lunmap will trigger
+	 * unconfigure of all LUNs on the target.
+	 */
+	scsi_lunmap_set_begin(self, lundam);
+	scsi_lunmap_set_end(self, lundam);
+
+	SCSI_HBA_LOG((_LOGLUN, self, NULL,
+	    "%s sync begin", damap_name(lundam)));
+
+	(void) damap_sync(lundam);	/* wait for unconfigure */
+
+	SCSI_HBA_LOG((_LOGLUN, self, NULL,
+	    "%s sync end", damap_name(lundam)));
+
+	damap_id_priv_set(tgtdam, tgtid, NULL);
+
+	/* release hold established by damap_lookup above */
+	damap_id_rele(tgtdam, tgtid);
+
+	/* release hold established since scsi_lunmap_create() */
+	damap_id_rele(tgtdam, tgtid);
+
+	damap_destroy(lundam);
+}
+
+static void
+scsi_lunmap_set_begin(dev_info_t *self, damap_t *lundam)
+{
+	SCSI_HBA_LOG((_LOGLUN, self, NULL, "%s", damap_name(lundam)));
+
+	(void) damap_addrset_begin(lundam);
 }
 
 static int
-scsi_hba_bus_unconfig(dev_info_t *self, uint_t flag, ddi_bus_config_op_t op,
-    void *arg)
-{
-	scsi_hba_tran_t	*tran = NULL;
-
-	tran = ddi_get_driver_private(self);
-
-	if (tran && tran->tran_bus_unconfig) {
-		return (tran->tran_bus_unconfig(self, flag, op, arg));
-	}
-	return (ndi_busop_bus_unconfig(self, flag, op, arg));
+scsi_lunmap_set_add(dev_info_t *self, damap_t *lundam,
+    char *taddr, scsi_lun64_t lun64, int sfunc)
+{
+	char	ua[SCSI_MAXNAMELEN];
+
+	/* make unit address string form of "@taddr,lun[,sfunc]" */
+	if (sfunc == -1)
+		(void) snprintf(ua, sizeof (ua), "%s,%" PRIx64, taddr, lun64);
+	else
+		(void) snprintf(ua, sizeof (ua), "%s,%" PRIx64 ",%x",
+		    taddr, lun64, sfunc);
+
+	SCSI_HBA_LOG((_LOGLUN, self, NULL, "%s @%s", damap_name(lundam), ua));
+
+	return ((damap_addrset_add(lundam, ua, NULL, NULL,
+	    NULL) == DAM_SUCCESS) ? DDI_SUCCESS : DDI_FAILURE);
+}
+
+static void
+scsi_lunmap_set_end(dev_info_t *self, damap_t *lundam)
+{
+	SCSI_HBA_LOG((_LOGLUN, self, NULL, "%s", damap_name(lundam)));
+
+	(void) damap_addrset_end(lundam, 0);
+}
+
+int
+scsi_lunmap_lookup(dev_info_t *self, damap_t *lundam, char *addr)
+{
+	damap_id_t		lunid;
+
+	if ((lunid = damap_lookup(lundam, addr)) != NODAM) {
+		SCSI_HBA_LOG((_LOG(3), self, NULL,
+		    "%s @%s found", damap_name(lundam), addr));
+		damap_id_rele(lundam, lunid);
+		return (DDI_SUCCESS);
+	}
+
+	SCSI_HBA_LOG((_LOG(3), self, NULL,
+	    "%s @%s not found", damap_name(lundam), addr));
+	return (DDI_FAILURE);
+}
+
+/*
+ * phymap implementation
+ *
+ * We manage the timed aggregation of phys into a phy map * by creating a
+ * SAS port construct (based upon 'name' of "local,remote" SAS addresses)
+ * upon the first link up. As time goes on additional phys may join that port.
+ * After an appropriate amount of settle time, we trigger the activation
+ * callback which will then take the resultant bit mask of phys (phymask) in
+ * the SAS port and use that to call back to the callback function
+ * provided by the additional caller.
+ *
+ * We cross check to make sure that phys only exist in one SAS port at a
+ * time by having soft state for each phy point back to the created
+ * SAS port.
+ *
+ * NOTE: Make SAS_PHY_UA_LEN max(SAS_PHY_PHYMASK_LEN, SAS_PHY_NAME_LEN)
+ * so we have enough space if sas_phymap_bitset2phymaskua phymask address
+ * is already in use, and we end up using port name as unit address.
+ */
+#define	SAS_PHY_NAME_FMT	"%" PRIx64 ",%" PRIx64
+#define	SAS_PHY_NAME_LEN	(16 + 1 + 16 + 1)
+#define	SAS_PHY_NPHY		(SAS2_PHYNUM_MAX + 1)
+#define	SAS_PHY_PHYMASK_LEN	((roundup(SAS_PHY_NPHY, 4)) / 4)
+#if	(SAS_PHY_PHYMASK_LEN > SAS_PHY_NAME_LEN)
+#define	SAS_PHY_UA_LEN		SAS_PHY_PHYMASK_LEN
+#else
+#define	SAS_PHY_UA_LEN		SAS_PHY_NAME_LEN
+#endif
+typedef struct impl_sas_phymap {
+	dev_info_t			*phymap_self;
+
+	kmutex_t			phymap_lock;
+	damap_t				*phymap_dam;
+	void				*phymap_phy2name;
+	ddi_soft_state_bystr		*phymap_name2phys;	/* bitset */
+	ddi_soft_state_bystr		*phymap_name2ua;
+	ddi_soft_state_bystr		*phymap_ua2name;
+
+	/* Noisy phy information - ensure forward progress for noisy phys */
+	int				phymap_phy_max;		/* max phy# */
+	int				phymap_reports;		/* per period */
+	int				phymap_reports_max;	/* scales */
+	int				phymap_phys_noisy;	/* detected */
+
+	/* These are for callbacks to the consumer. */
+	sas_phymap_activate_cb_t	phymap_acp;
+	sas_phymap_deactivate_cb_t	phymap_dcp;
+	void				*phymap_private;
+} impl_sas_phymap_t;
+
+/* Detect noisy phy: max changes per stabilization period per phy. */
+static int sas_phymap_phy_max_factor = 16;
+
+/*
+ * Convert bitset into a unit-address string. The maximum string length would
+ * be the maximum number of phys, rounded up by 4 and divided by 4.
+ */
+static void
+sas_phymap_bitset2phymaskua(bitset_t *phys, char *buf)
+{
+	char			*ptr;
+	int			grp;
+	int			cur;
+	uint_t			bit;
+
+	bit = roundup(SAS_PHY_NPHY, 4);
+	grp = 4;
+	ptr = buf;
+	cur = 0;
+	do {
+		bit -= 1;
+		grp -= 1;
+		if (bitset_in_set(phys, bit)) {
+			cur |= (1 << grp);
+		}
+		if (grp == 0) {
+			grp = 4;
+			if (cur || ptr != buf) {
+				*ptr++ = "0123456789abcdef"[cur];
+				*ptr = 0;
+			}
+			cur = 0;
+		}
+	} while (bit != 0);
+	if (ptr == buf) {
+		*ptr++ = '0';
+		*ptr = 0;
+	}
+}
+
+static void
+sas_phymap_config(void *arg, damap_t *phydam, damap_id_list_t dl)
+{
+	impl_sas_phymap_t	*phymap = (impl_sas_phymap_t *)arg;
+	char			*context = damap_name(phymap->phymap_dam);
+	int			pairs;
+	damap_id_t		phyid;
+	char			*damn;
+	char			*name;
+	bitset_t		*phys;
+	char			*ua;
+	void			*ua_priv;
+
+	ASSERT(context);
+
+	mutex_enter(&phymap->phymap_lock);
+	phymap->phymap_reports = phymap->phymap_phys_noisy = 0;
+
+	/* count the number of local,remove pairs we need to config */
+	for (pairs = 0, phyid = damap_id_next(phydam, dl, NODAM);
+	    phyid != NODAM;
+	    phyid = damap_id_next(phydam, dl, phyid))
+		pairs++;
+	SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL,
+	    "%s: config %d local,remote pairs", context, pairs));
+
+	for (phyid = damap_id_next(phydam, dl, NODAM);
+	    phyid != NODAM;
+	    phyid = damap_id_next(phydam, dl, phyid)) {
+		/* Get the name ("local,remote" address string) from damap. */
+		damn = damap_id2addr(phydam, phyid);
+
+		/* Get the bitset of phys currently forming the port. */
+		phys = ddi_soft_state_bystr_get(phymap->phymap_name2phys, damn);
+		if (phys == NULL) {
+			SCSI_HBA_LOG((_LOG_NF(WARN),
+			    "%s: %s: no phys", context, damn));
+			continue;
+		}
+
+		/* allocate, get, and initialize name index of name2ua map */
+		if (ddi_soft_state_bystr_zalloc(
+		    phymap->phymap_name2ua, damn) != DDI_SUCCESS) {
+			SCSI_HBA_LOG((_LOG_NF(WARN),
+			    "%s: %s: failed name2ua alloc", context, damn));
+			continue;
+		}
+		ua = ddi_soft_state_bystr_get(phymap->phymap_name2ua, damn);
+		if (ua == NULL) {
+			SCSI_HBA_LOG((_LOG_NF(WARN),
+			    "%s: %s: no name2ua", context, damn));
+			continue;
+		}
+		sas_phymap_bitset2phymaskua(phys, ua);		/* set ua */
+
+		/* see if phymask ua index already allocated in ua2name map */
+		name = ddi_soft_state_bystr_get(phymap->phymap_ua2name, ua);
+		if (name) {
+			/*
+			 * The 'phymask' sas_phymap_bitset2phymaskua ua is
+			 * already in use. This means that original phys have
+			 * formed into a new port, and that the original port
+			 * still exists (it has migrated to some completely
+			 * different set of phys). In this corner-case we use
+			 * "local,remote" name as a 'temporary' unit address.
+			 * Reset ua in name2ua map.
+			 */
+			(void) strlcpy(ua, damn, SAS_PHY_NAME_LEN);
+
+			name = ddi_soft_state_bystr_get(
+			    phymap->phymap_ua2name, ua);
+			if (name) {
+				/* The "local,remote" ua should be new... */
+				SCSI_HBA_LOG((_LOG_NF(WARN),
+				    "%s: %s ua already configured",
+				    context, ua));
+				continue;
+			}
+		}
+
+		/* allocate, get, and init ua index of ua2name map */
+		if (ddi_soft_state_bystr_zalloc(
+		    phymap->phymap_ua2name, ua) != DDI_SUCCESS) {
+			ddi_soft_state_bystr_free(
+			    phymap->phymap_name2ua, damn);
+			SCSI_HBA_LOG((_LOG_NF(WARN),
+			    "%s: %s: failed ua2name alloc",
+			    context, damn));
+			continue;
+		}
+		name = ddi_soft_state_bystr_get(
+		    phymap->phymap_ua2name, ua);
+		if (name == NULL) {
+			ddi_soft_state_bystr_free(
+			    phymap->phymap_name2ua, damn);
+			SCSI_HBA_LOG((_LOG_NF(WARN),
+			    "%s: %s: no ua2name", context, ua));
+			continue;
+		}
+
+		/* set name in ua2name map */
+		(void) strlcpy(name, damn, SAS_PHY_NAME_LEN);
+
+		SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL,
+		    "%s: %s: ua %s: activate", context, damn, ua));
+		if (phymap->phymap_acp) {
+			mutex_exit(&phymap->phymap_lock);
+			ua_priv = NULL;
+			(phymap->phymap_acp)(phymap->phymap_private,
+			    ua, &ua_priv);
+			mutex_enter(&phymap->phymap_lock);
+
+			damap_id_priv_set(phydam, phyid, ua_priv);
+		}
+		SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL,
+		    "%s: %s: ua %s: activate complete", context, damn, ua));
+	}
+
+	SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL,
+	    "%s: config complete", context));
+	mutex_exit(&phymap->phymap_lock);
+}
+
+/*ARGSUSED*/
+static void
+sas_phymap_unconfig(void *arg, damap_t *phydam, damap_id_list_t dl)
+{
+	impl_sas_phymap_t	*phymap = (impl_sas_phymap_t *)arg;
+	char			*context = damap_name(phymap->phymap_dam);
+	int			pairs;
+	damap_id_t		phyid;
+	char			*damn;
+	char			*ua;
+	void			*ua_priv;
+
+	ASSERT(context);
+
+	mutex_enter(&phymap->phymap_lock);
+	phymap->phymap_reports = phymap->phymap_phys_noisy = 0;
+
+	/* count the number of local,remove pairs we need to unconfig */
+	for (pairs = 0, phyid = damap_id_next(phydam, dl, NODAM);
+	    phyid != NODAM;
+	    phyid = damap_id_next(phydam, dl, phyid))
+		pairs++;
+	SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL,
+	    "%s: unconfig %d local,remote pairs", context, pairs));
+
+	for (phyid = damap_id_next(phydam, dl, NODAM);
+	    phyid != NODAM;
+	    phyid = damap_id_next(phydam, dl, phyid)) {
+		/* Get the name ("local,remote" address string) from damap. */
+		damn = damap_id2addr(phydam, phyid);
+
+		ua = ddi_soft_state_bystr_get(phymap->phymap_name2ua, damn);
+		if (ua == NULL) {
+			SCSI_HBA_LOG((_LOG_NF(WARN),
+			    "%s: %s: no name2ua", context, damn));
+			continue;
+		}
+
+		SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL,
+		    "%s: %s: ua %s: deactivate", context, damn, ua));
+		if (phymap->phymap_dcp) {
+			ua_priv = damap_id_priv_get(phydam, phyid);
+			mutex_exit(&phymap->phymap_lock);
+			(phymap->phymap_dcp)(phymap->phymap_private,
+			    ua, ua_priv);
+			mutex_enter(&phymap->phymap_lock);
+		}
+		SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL,
+		    "%s: %s: ua %s: deactivate complete", context, damn, ua));
+
+		/* delete ua<->name mappings */
+		ddi_soft_state_bystr_free(phymap->phymap_ua2name, ua);
+		ddi_soft_state_bystr_free(phymap->phymap_name2ua, damn);
+	}
+
+	SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL,
+	    "%s: unconfig complete", context));
+	mutex_exit(&phymap->phymap_lock);
+}
+
+int
+sas_phymap_create(dev_info_t *self, clock_t settle,
+    sas_phymap_mode_t mode, void *mode_argument, void *phymap_priv,
+    sas_phymap_activate_cb_t  activate_cb,
+    sas_phymap_deactivate_cb_t deactivate_cb,
+    sas_phymap_t **handlep)
+{
+	_NOTE(ARGUNUSED(mode_argument));
+	char			context[64];
+	impl_sas_phymap_t	*phymap;
+
+	if (self == NULL || settle == 0 || handlep == NULL)
+		return (DDI_FAILURE);
+
+	if (mode != PHYMAP_MODE_SIMPLE)
+		return (DDI_FAILURE);
+
+	phymap = kmem_zalloc(sizeof (*phymap), KM_SLEEP);
+	phymap->phymap_self = self;
+	phymap->phymap_reports_max = 1 * sas_phymap_phy_max_factor;
+	phymap->phymap_acp = activate_cb;
+	phymap->phymap_dcp = deactivate_cb;
+	phymap->phymap_private = phymap_priv;
+	mutex_init(&phymap->phymap_lock, NULL, MUTEX_DRIVER, NULL);
+
+	(void) snprintf(context, sizeof (context), "%s%d.phymap",
+	    ddi_driver_name(self), ddi_get_instance(self));
+	SCSI_HBA_LOG((_LOGPHY, self, NULL, "%s", context));
+
+	if (damap_create(context, SAS_PHY_NPHY, DAMAP_REPORT_PERADDR, settle,
+	    NULL, NULL, NULL,
+	    phymap, sas_phymap_config, sas_phymap_unconfig,
+	    &phymap->phymap_dam) != DAM_SUCCESS)
+		goto fail;
+
+	if (ddi_soft_state_init(&phymap->phymap_phy2name,
+	    SAS_PHY_NAME_LEN, SAS_PHY_NPHY) != 0)
+		goto fail;
+
+	if (ddi_soft_state_bystr_init(&phymap->phymap_name2phys,
+	    sizeof (bitset_t), SAS_PHY_NPHY) != 0)
+		goto fail;
+
+	if (ddi_soft_state_bystr_init(&phymap->phymap_name2ua,
+	    SAS_PHY_UA_LEN, SAS_PHY_NPHY) != 0)
+		goto fail;
+	if (ddi_soft_state_bystr_init(&phymap->phymap_ua2name,
+	    SAS_PHY_NAME_LEN, SAS_PHY_NPHY) != 0)
+		goto fail;
+
+	*handlep = (sas_phymap_t *)phymap;
+	return (DDI_SUCCESS);
+
+fail:	sas_phymap_destroy((sas_phymap_t *)phymap);
+	*handlep = NULL;
+	return (DDI_FAILURE);
 }
 
 void
-scsi_hba_pkt_comp(struct scsi_pkt *pkt)
-{
-	ASSERT(pkt);
-	if (pkt->pkt_comp == NULL)
-		return;
-
-	/*
-	 * For HBA drivers that implement tran_setup_pkt(9E), if we are
-	 * completing a 'consistent' mode DMA operation then we must
-	 * perform dma_sync prior to calling pkt_comp to ensure that
-	 * the target driver sees the correct data in memory.
-	 */
-	ASSERT((pkt->pkt_flags & FLAG_NOINTR) == 0);
-	if (((pkt->pkt_dma_flags & DDI_DMA_CONSISTENT) &&
-	    (pkt->pkt_dma_flags & DDI_DMA_READ)) &&
-	    ((P_TO_TRAN(pkt)->tran_setup_pkt) != NULL)) {
-		scsi_sync_pkt(pkt);
-	}
-	(*pkt->pkt_comp)(pkt);
-}
+sas_phymap_destroy(sas_phymap_t *handle)
+{
+	impl_sas_phymap_t	*phymap = (impl_sas_phymap_t *)handle;
+	char			*context;
+
+	context = phymap->phymap_dam ?
+	    damap_name(phymap->phymap_dam) : "unknown";
+	SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL, "%s", context));
+
+	if (phymap->phymap_ua2name)
+		ddi_soft_state_bystr_fini(&phymap->phymap_ua2name);
+	if (phymap->phymap_name2ua)
+		ddi_soft_state_bystr_fini(&phymap->phymap_name2ua);
+
+	if (phymap->phymap_name2phys)
+		ddi_soft_state_bystr_fini(&phymap->phymap_name2phys);
+
+	if (phymap->phymap_phy2name)
+		ddi_soft_state_fini(&phymap->phymap_phy2name);
+
+	if (phymap->phymap_dam)
+		damap_destroy(phymap->phymap_dam);
+	mutex_destroy(&phymap->phymap_lock);
+	kmem_free(phymap, sizeof (*phymap));
+}
+
+
+int
+sas_phymap_phy_add(sas_phymap_t *handle,
+    int phy, uint64_t local, uint64_t remote)
+{
+	impl_sas_phymap_t	*phymap = (impl_sas_phymap_t *)handle;
+	char			*context = damap_name(phymap->phymap_dam);
+	char			port[SAS_PHY_NAME_LEN];
+	char			*name;
+	bitset_t		*phys;
+	int			phy2name_allocated = 0;
+	int			name2phys_allocated = 0;
+	int			rv;
+
+	/* Create the SAS port name from the local and remote addresses. */
+	(void) snprintf(port, SAS_PHY_NAME_LEN, SAS_PHY_NAME_FMT,
+	    local, remote);
+
+	mutex_enter(&phymap->phymap_lock);
+	SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL, "%s: %s: add phy %d",
+	    context, port, phy));
+
+	/* Check for conflict in phy2name map */
+	name = ddi_get_soft_state(phymap->phymap_phy2name, phy);
+	if (name) {
+		if (strcmp(name, port) != 0)
+			SCSI_HBA_LOG((_LOG_NF(WARN), "%s: %s: add phy %d: "
+			    "already in %s", context, port, phy, name));
+		else
+			SCSI_HBA_LOG((_LOG_NF(WARN), "%s: %s: add phy %d: "
+			    "duplicate add", context, port, phy));
+		mutex_exit(&phymap->phymap_lock);
+		return (DDI_FAILURE);
+	}
+
+	/* allocate, get, and initialize phy index of phy2name map */
+	if (ddi_soft_state_zalloc(
+	    phymap->phymap_phy2name, phy) != DDI_SUCCESS) {
+		SCSI_HBA_LOG((_LOG_NF(WARN),
+		    "%s: %s: failed phy2name alloc", context, port));
+		goto fail;
+	}
+	name = ddi_get_soft_state(phymap->phymap_phy2name, phy);
+	if (name == NULL) {
+		SCSI_HBA_LOG((_LOG_NF(WARN),
+		    "%s: %s: no phy2name", context, port));
+		goto fail;
+	}
+	phy2name_allocated = 1;
+	(void) strlcpy(name, port, SAS_PHY_NAME_LEN);	/* set name */
+
+	/* Find/alloc, initialize name index of name2phys map */
+	phys = ddi_soft_state_bystr_get(phymap->phymap_name2phys, name);
+	if (phys == NULL) {
+		if (ddi_soft_state_bystr_zalloc(phymap->phymap_name2phys,
+		    name) != DDI_SUCCESS) {
+			SCSI_HBA_LOG((_LOG_NF(WARN),
+			    "%s: %s: failed name2phys alloc", context, name));
+			goto fail;
+		}
+		phys = ddi_soft_state_bystr_get(phymap->phymap_name2phys, name);
+		if (phys == NULL) {
+			SCSI_HBA_LOG((_LOG_NF(WARN),
+			    "%s: %s: no name2phys", context, name));
+			goto fail;
+		}
+		name2phys_allocated = 1;
+
+		/* Initialize bitset of phys */
+		bitset_init(phys);
+		bitset_resize(phys, SAS_PHY_NPHY);
+
+		/* NOTE: no bitset_fini of phys needed */
+	}
+	ASSERT(phys);
+
+	/* Reflect 'add' in phys bitset. */
+	if (bitset_atomic_test_and_add(phys, phy) < 0) {
+		/* It is an error if the phy was already recorded. */
+		SCSI_HBA_LOG((_LOG_NF(WARN),
+		    "%s: %s: phy bit %d already in port", context, name, phy));
+		goto fail;
+	}
+
+	/*
+	 * Check to see if we have a new phy_max for this map, and if so
+	 * scale phymap_reports_max to the new number of phys.
+	 */
+	if (phy > phymap->phymap_phy_max) {
+		phymap->phymap_phy_max = phy + 1;
+		phymap->phymap_reports_max = phymap->phymap_phy_max *
+		    sas_phymap_phy_max_factor;
+	}
+
+	/*
+	 * If we have not reached phymap_reports_max, start/restart the
+	 * activate timer. Otherwise, if phymap->phymap_reports add/rem reports
+	 * ever exceeds phymap_reports_max due to noisy phys, then report the
+	 * noise and force stabilization by stopping reports into the damap.
+	 *
+	 * The first config/unconfig callout out of the damap will reset
+	 * phymap->phymap_reports.
+	 */
+	rv = DDI_SUCCESS;
+	if (phymap->phymap_reports++ < phymap->phymap_reports_max) {
+		if (damap_addr_add(phymap->phymap_dam, name,
+		    NULL, NULL, NULL) == DAM_SUCCESS) {
+			SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL,
+			    "%s: %s: damap_addr_add", context, name));
+		} else {
+			SCSI_HBA_LOG((_LOG_NF(WARN),
+			    "%s: %s: damap_addr_add failed", context, name));
+			rv = DDI_FAILURE;
+		}
+	} else {
+		phymap->phymap_phys_noisy++;
+		if (phymap->phymap_phys_noisy == 1)
+			SCSI_HBA_LOG((_LOG_NF(WARN),
+			    "%s: %s: noisy phys", context, name));
+	}
+	mutex_exit(&phymap->phymap_lock);
+	return (rv);
+
+fail:	if (phy2name_allocated)
+		ddi_soft_state_free(phymap->phymap_phy2name, phy);
+	if (name2phys_allocated)
+		ddi_soft_state_bystr_free(phymap->phymap_name2phys, name);
+	mutex_exit(&phymap->phymap_lock);
+	return (DDI_FAILURE);
+}
+
+int
+sas_phymap_phy_rem(sas_phymap_t *handle, int phy)
+{
+	impl_sas_phymap_t	*phymap = (impl_sas_phymap_t *)handle;
+	char			*context = damap_name(phymap->phymap_dam);
+	char			*name;
+	bitset_t		*phys;
+	int			rv = DDI_FAILURE;
+
+	ASSERT(context);
+
+	mutex_enter(&phymap->phymap_lock);
+	phymap->phymap_reports++;
+
+	/* Find and free phy index of phy2name map */
+	name = ddi_get_soft_state(phymap->phymap_phy2name, phy);
+	if (name == NULL) {
+		SCSI_HBA_LOG((_LOG_NF(WARN), "%s: rem phy %d: never added",
+		    context, phy));
+		goto fail;
+	}
+	/* NOTE: always free phy index of phy2name map before return... */
+
+	SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL, "%s: %s: rem phy %d",
+	    context, name, phy));
+
+	/* Get bitset of phys currently associated with named port. */
+	phys = ddi_soft_state_bystr_get(phymap->phymap_name2phys, name);
+	if (phys == NULL) {
+		SCSI_HBA_LOG((_LOG_NF(WARN), "%s: %s: name2phys failed",
+		    context, name));
+		goto fail;
+	}
+
+	/* Reflect 'rem' in phys bitset. */
+	if (bitset_atomic_test_and_del(phys, phy) < 0) {
+		/* It is an error if the phy wasn't one of the port's phys. */
+		SCSI_HBA_LOG((_LOG_NF(WARN),
+		    "%s: %s: phy bit %d not in port", context, name, phy));
+		goto fail;
+	}
+
+	/* If this was the last phy in the port, start the deactivate timer. */
+	if (bitset_is_null(phys) &&
+	    (phymap->phymap_reports++ < phymap->phymap_reports_max)) {
+		if (damap_addr_del(phymap->phymap_dam, name) == DAM_SUCCESS) {
+			SCSI_HBA_LOG((_LOGPHY, phymap->phymap_self, NULL,
+			    "%s: %s: damap_addr_del", context, name));
+		} else {
+			SCSI_HBA_LOG((_LOG_NF(WARN),
+			    "%s: %s: damap_addr_del failure", context, name));
+			goto fail;
+		}
+	}
+	rv = DDI_SUCCESS;
+
+	/* free phy index of phy2name map */
+fail:	if (name)
+		ddi_soft_state_free(phymap->phymap_phy2name, phy); /* free */
+	mutex_exit(&phymap->phymap_lock);
+	return (rv);
+}
+
+char *
+sas_phymap_lookup_ua(sas_phymap_t *handle, uint64_t local, uint64_t remote)
+{
+	impl_sas_phymap_t	*phymap = (impl_sas_phymap_t *)handle;
+	char			*context = damap_name(phymap->phymap_dam);
+	char			name[SAS_PHY_NAME_LEN];
+	char			*ua;
+
+	ASSERT(context);
+
+	(void) snprintf(name, SAS_PHY_NAME_LEN, SAS_PHY_NAME_FMT,
+	    local, remote);
+
+	mutex_enter(&phymap->phymap_lock);
+	ua = ddi_soft_state_bystr_get(phymap->phymap_name2ua, name);
+	SCSI_HBA_LOG((_LOG(3), phymap->phymap_self, NULL,
+	    "%s: %s: ua %s", context, name, ua ? ua : "NULL"));
+	mutex_exit(&phymap->phymap_lock);
+	return (ua);
+}
+
+void *
+sas_phymap_lookup_uapriv(sas_phymap_t *handle, char *ua)
+{
+	impl_sas_phymap_t	*phymap = (impl_sas_phymap_t *)handle;
+	char			*context = damap_name(phymap->phymap_dam);
+	char			*name;
+	damap_id_t		phyid;
+	void			*ua_priv = NULL;
+
+	ASSERT(context);
+
+	mutex_enter(&phymap->phymap_lock);
+	name = ddi_soft_state_bystr_get(phymap->phymap_ua2name, ua);
+	if (name) {
+		phyid = damap_lookup(phymap->phymap_dam, name);
+		if (phyid != NODAM) {
+			ua_priv = damap_id_priv_get(phymap->phymap_dam, phyid);
+			damap_id_rele(phymap->phymap_dam, phyid);
+		}
+	}
+
+	SCSI_HBA_LOG((_LOG(3), phymap->phymap_self, NULL,
+	    "%s: %s: ua %s ua_priv %p", context, name,
+	    ua ? ua : "NULL", ua_priv));
+	mutex_exit(&phymap->phymap_lock);
+	return (ua_priv);
+}
+
+int
+sas_phymap_uahasphys(sas_phymap_t *handle, char *ua)
+{
+	impl_sas_phymap_t	*phymap = (impl_sas_phymap_t *)handle;
+	char			*name;
+	bitset_t		*phys;
+	int			n = 0;
+
+	mutex_enter(&phymap->phymap_lock);
+	name = ddi_soft_state_bystr_get(phymap->phymap_ua2name, ua);
+	if (name) {
+		phys = ddi_soft_state_bystr_get(phymap->phymap_name2phys, name);
+		if (phys)
+			n = bitset_is_null(phys) ? 0 : 1;
+	}
+	mutex_exit(&phymap->phymap_lock);
+	return (n);
+}
+
+sas_phymap_phys_t *
+sas_phymap_ua2phys(sas_phymap_t *handle, char *ua)
+{
+	impl_sas_phymap_t	*phymap = (impl_sas_phymap_t *)handle;
+	char			*name;
+	bitset_t		*phys;
+	bitset_t		*cphys = NULL;
+
+	mutex_enter(&phymap->phymap_lock);
+	name = ddi_soft_state_bystr_get(phymap->phymap_ua2name, ua);
+	if (name == NULL)
+		goto fail;
+
+	phys = ddi_soft_state_bystr_get(phymap->phymap_name2phys, name);
+	if (phys == NULL)
+		goto fail;
+
+	/* dup the phys and return */
+	cphys = kmem_alloc(sizeof (*cphys), KM_SLEEP);
+	bitset_init(cphys);
+	bitset_resize(cphys, SAS_PHY_NPHY);
+	bitset_copy(phys, cphys);
+
+fail:	mutex_exit(&phymap->phymap_lock);
+	return ((sas_phymap_phys_t *)cphys);
+}
+
+int
+sas_phymap_phys_next(sas_phymap_phys_t *phys)
+{
+	bitset_t	*cphys = (bitset_t *)phys;
+	int		phy;
+
+	phy = bitset_find(cphys);
+	if (phy != -1)
+		bitset_del(cphys, phy);
+	return (phy);
+}
+
+void
+sas_phymap_phys_free(sas_phymap_phys_t *phys)
+{
+	bitset_t	*cphys = (bitset_t *)phys;
+
+	if (cphys) {
+		bitset_fini(cphys);
+		kmem_free(cphys, sizeof (*cphys));
+	}
+}
+
+char *
+sas_phymap_phy2ua(sas_phymap_t *handle, int phy)
+{
+	impl_sas_phymap_t	*phymap = (impl_sas_phymap_t *)handle;
+	char			*name;
+	char			*ua;
+	char			*rua = NULL;
+
+	mutex_enter(&phymap->phymap_lock);
+	name = ddi_get_soft_state(phymap->phymap_phy2name, phy);
+	if (name == NULL)
+		goto fail;
+	ua = ddi_soft_state_bystr_get(phymap->phymap_name2ua, name);
+	if (ua == NULL)
+		goto fail;
+
+	/* dup the ua and return */
+	rua = strdup(ua);
+
+fail:	mutex_exit(&phymap->phymap_lock);
+	return (rua);
+}
+
+void
+sas_phymap_ua_free(char *ua)
+{
+	if (ua)
+		strfree(ua);
+}
--- a/usr/src/uts/common/io/scsi/impl/scsi_resource.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/impl/scsi_resource.c	Mon Oct 05 09:17:26 2009 -0400
@@ -324,12 +324,12 @@
 		pktw->pcw_flags = 0;
 		in_pktp = &(pktw->pcw_pkt);
 		in_pktp->pkt_address = *ap;
+
 		/*
 		 * target drivers should initialize pkt_comp and
 		 * pkt_time, but sometimes they don't so initialize
 		 * them here to be safe.
 		 */
-		in_pktp->pkt_address = *ap;
 		in_pktp->pkt_flags = 0;
 		in_pktp->pkt_time = 0;
 		in_pktp->pkt_resid = 0;
--- a/usr/src/uts/common/io/scsi/impl/scsi_transport.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/impl/scsi_transport.c	Mon Oct 05 09:17:26 2009 -0400
@@ -188,7 +188,6 @@
 	} else {
 		uint_t	savef;
 		void	(*savec)();
-		int	status;
 
 #ifdef SCSI_POLL_STAT
 		mutex_enter(&scsi_flag_nointr_mutex);
@@ -202,7 +201,7 @@
 		pkt->pkt_flags &= ~FLAG_NOINTR;
 		pkt->pkt_flags |= FLAG_IMMEDIATE_CB;
 
-		if ((status = (*A_TO_TRAN(ap)->tran_start)(ap, pkt)) ==
+		if ((rval = (*A_TO_TRAN(ap)->tran_start)(ap, pkt)) ==
 		    TRAN_ACCEPT) {
 			mutex_enter(&scsi_flag_nointr_mutex);
 			while (pkt->pkt_comp != CALLBACK_DONE) {
@@ -214,6 +213,6 @@
 
 		pkt->pkt_flags = savef;
 		pkt->pkt_comp = savec;
-		return (status);
+		return (rval);
 	}
 }
--- a/usr/src/uts/common/io/scsi/targets/sd.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/targets/sd.c	Mon Oct 05 09:17:26 2009 -0400
@@ -16751,8 +16751,12 @@
 	 * state if needed.
 	 */
 	if (pktp->pkt_reason == CMD_DEV_GONE) {
-		scsi_log(SD_DEVINFO(un), sd_label, CE_WARN,
-		    "Command failed to complete...Device is gone\n");
+		/* Prevent multiple console messages for the same failure. */
+		if (un->un_last_pkt_reason != CMD_DEV_GONE) {
+			un->un_last_pkt_reason = CMD_DEV_GONE;
+			scsi_log(SD_DEVINFO(un), sd_label, CE_WARN,
+			    "Command failed to complete...Device is gone\n");
+		}
 		if (un->un_mediastate != DKIO_DEV_GONE) {
 			un->un_mediastate = DKIO_DEV_GONE;
 			cv_broadcast(&un->un_state_cv);
--- a/usr/src/uts/common/io/scsi/targets/ses.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/targets/ses.c	Mon Oct 05 09:17:26 2009 -0400
@@ -21,7 +21,7 @@
 /*
  * Enclosure Services Device target driver
  *
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -245,21 +245,27 @@
 	 * been able to get one to attach.
 	 *
 	 */
-
-
 	if (dip == NULL)
 		return (DDI_PROBE_FAILURE);
+	/* SES_LOG(NULL, SES_CE_DEBUG1, "ses_probe: OK"); */
+	if (ddi_dev_is_sid(dip) == DDI_SUCCESS) {
+		return (DDI_PROBE_DONTCARE);
+	}
+
+	devp = ddi_get_driver_private(dip);
+
+	/* Legacy: prevent driver.conf specified ses nodes on atapi. */
+	if (scsi_ifgetcap(&devp->sd_address, "interconnect-type", -1) ==
+	    INTERCONNECT_ATAPI)
+		return (DDI_PROBE_FAILURE);
+
 	/*
 	 * XXX: Breakage from the x86 folks.
 	 */
 	if (strcmp(ddi_get_name(ddi_get_parent(dip)), "ata") == 0) {
 		return (DDI_PROBE_FAILURE);
 	}
-	/* SES_LOG(NULL, SES_CE_DEBUG1, "ses_probe: OK"); */
-	if (ddi_dev_is_sid(dip) == DDI_SUCCESS) {
-		return (DDI_PROBE_DONTCARE);
-	}
-	devp = ddi_get_driver_private(dip);
+
 	switch (err = scsi_probe(devp, SLEEP_FUNC)) {
 	case SCSIPROBE_EXISTS:
 		if (is_enc_dev(NULL, devp->sd_inq, SUN_INQSIZE, &ep)) {
--- a/usr/src/uts/common/io/scsi/targets/sgen.conf	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/targets/sgen.conf	Mon Oct 05 09:17:26 2009 -0400
@@ -18,9 +18,7 @@
 #
 # CDDL HEADER END
 #
-
-#
-# Copyright 1999 by Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -28,17 +26,38 @@
 # Portions Copyright (c) Siemens 1999
 # All rights reserved.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
 
-#
 # WARNING: enabling this driver may impact the security and data integrity of
 # devices on your system.  Please refer to sgen(7d) for details.
+#
+# There are two ways of configuring sgen: by establishing an association
+# between a compatible alias for a device and the sgen driver via
+# "add_drv -i", or by using this file (sgen.conf).
 
+#--------------------------add_drv binding method-----------------------------
+# SCSI target devices are now self-identifying in Solaris. Add_drv is the
+# preferred method to control driver binding, it avoids issues associated
+# with multiple driver.conf files associating more than one driver with a
+# device. The compatible property forms for SCSI target devices used in the
+# add_drv command are described in scsi(4).
+#
+# USAGE EXAMPLE (add_drv)
 #
-# sgen may be configured to bind to SCSI devices exporting a particular device
-# type, using the device-type-config-list, which is a ',' delimited list of
-# strings.
+# In this example, sgen is configured to bind to all scanner and ocrw devices
+# in the system, as well as the UltraToast 4000 disk from ACME using the
+# add_drv configuration method.
+#
+# add_drv -i \
+#    '"scsiclass,06" "scsiclass,0f" "scsiclass,00.vACME,pUltraToast_4000"' sgen
+
+#-------------------------sgen.conf binding method----------------------------
+# NOTE: Support for sgen.conf configuration may be removed in a future release
+# of Solaris. 
+#
+# The the remainder of this file is concerned with the .conf file 
+# configuration method. Sgen may be configured to bind to SCSI devices
+# exporting a particular device type, using the device-type-config-list, which
+# is a ',' delimited list of strings.
 #
 #device-type-config-list=
 #	"direct"		(type 0x00)
@@ -60,7 +79,6 @@
 #	"bridge"		(type 0x10)
 #	"type_0x<typenum>"	(types 0x11-0x1e are undefined by SCSI-3)
 #	"type_unknown"		(type 0x1f)
-
 #
 # In addition to binding to device types, sgen can be configured to bind to one
 # or more particular devices.  The latter is accomplished by specifying the
@@ -69,9 +87,8 @@
 # strings in the inquiry-config-list property, below.  "*" may be substituted
 # for the vendor ID as a wildcard.  See sgen(7D) for details and extended usage
 # examples.
-
 #
-# USAGE EXAMPLE
+# USAGE EXAMPLE (sgen.conf)
 #
 # In this example, sgen is configured to bind to all scanner and ocrw devices in
 # the system, as well as the UltraToast 4000 from ACME, and the PowerToast
@@ -81,15 +98,14 @@
 #
 #inquiry-config-list=	"ACME",		"UltraToast 4000",
 #			"*",		"PowerToast";
-
-
 #
-# After configuring the device-type-config-list and/or the inquiry-config-list,
-# the administrator must uncomment those target/lun pairs at which there are
-# devices for sgen to control.  If it is expected that devices controlled by
-# sgen will be hotplugged or added into the system later, it is recommended
-# that all of the following lines be uncommented.
-
+# When using the sgen.conf method, after configuring the
+# device-type-config-list and/or the inquiry-config-list, the administrator
+# must uncomment those target/lun pairs at which there are devices for sgen to
+# control.  If it is expected that devices controlled by sgen will be hotplugged
+# or added into the system later, it is recommended that all of the following
+# lines be uncommented.
+#
 #name="sgen" class="scsi" target=0 lun=0;
 #name="sgen" class="scsi" target=1 lun=0;
 #name="sgen" class="scsi" target=2 lun=0;
@@ -106,3 +122,4 @@
 #name="sgen" class="scsi" target=13 lun=0;
 #name="sgen" class="scsi" target=14 lun=0;
 #name="sgen" class="scsi" target=15 lun=0;
+
--- a/usr/src/uts/common/io/scsi/targets/smp.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/scsi/targets/smp.c	Mon Oct 05 09:17:26 2009 -0400
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -65,7 +65,14 @@
  */
 static void smp_log(smp_state_t  *, int,  const char *, ...);
 
-int smp_retry_times = SMP_DEFAULT_RETRY_TIMES;
+int smp_retry_times	= SMP_DEFAULT_RETRY_TIMES;
+int smp_retry_delay	= 10000;	/* 10msec */
+int smp_delay_cmd	= 1;		/* 1usec */
+int smp_single_command	= 1;		/* one command at a time */
+
+static int smp_retry_recovered	= 0;	/* retry recovery counter */
+static int smp_retry_failed	= 0;	/* retry failed counter */
+static int smp_failed		= 0;
 
 static struct cb_ops smp_cb_ops = {
 	smp_open,			/* open */
@@ -147,7 +154,7 @@
 
 /*
  * smp_attach()
- * 	attach(9e) entrypoint.
+ *	attach(9e) entrypoint.
  */
 static int
 smp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
@@ -224,7 +231,7 @@
 
 /*
  * smp_detach()
- * 	detach(9E) entrypoint
+ *	detach(9E) entrypoint
  */
 static int
 smp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
@@ -255,7 +262,7 @@
 
 /*
  * smp_do_detach()
- * 	detach the driver, tearing down resources.
+ *	detach the driver, tearing down resources.
  */
 static int
 smp_do_detach(dev_info_t *dip)
@@ -459,7 +466,6 @@
 	DTRACE_PROBE1(smp__transport__start, caddr_t, smp_pkt->pkt_req);
 
 	smp_pkt->pkt_address = &smp_state->smp_dev->smp_addr;
-	smp_pkt->pkt_reason = 0;
 	if (usmp_cmd->usmp_timeout <= 0) {
 		smp_pkt->pkt_timeout = SMP_DEFAULT_TIMEOUT;
 	} else {
@@ -469,20 +475,56 @@
 	/* call sas_smp_transport entry and send smp_pkt to HBA driver */
 	cmd_flags |= SMP_FLAG_XFER;
 	for (retrycount = 0; retrycount <= smp_retry_times; retrycount++) {
-		if (sas_smp_transport(smp_pkt) == DDI_SUCCESS) {
-			rval = DDI_SUCCESS;
+
+		/*
+		 * To improve transport reliability, only allow one command
+		 * outstanding at a time in sas_smp_transport().
+		 *
+		 * NOTE: Some expanders have issues with heavy smp load.
+		 */
+		if (smp_single_command) {
+			mutex_enter(&smp_state->smp_mutex);
+			while (smp_state->smp_busy)
+				cv_wait(&smp_state->smp_cv,
+				    &smp_state->smp_mutex);
+			smp_state->smp_busy = 1;
+			mutex_exit(&smp_state->smp_mutex);
+		}
+
+		/* Let the transport know if more retries are possible. */
+		smp_pkt->pkt_will_retry =
+		    (retrycount < smp_retry_times) ? 1 : 0;
+
+		smp_pkt->pkt_reason = 0;
+		rval = sas_smp_transport(smp_pkt);	/* put on the wire */
+
+		if (smp_delay_cmd)
+			delay(drv_usectohz(smp_delay_cmd));
+
+		if (smp_single_command) {
+			mutex_enter(&smp_state->smp_mutex);
+			smp_state->smp_busy = 0;
+			cv_signal(&smp_state->smp_cv);
+			mutex_exit(&smp_state->smp_mutex);
+		}
+
+		if (rval == DDI_SUCCESS) {
+			if (retrycount)
+				smp_retry_recovered++;
+			rval = 0;
 			break;
 		}
 
 		switch (smp_pkt->pkt_reason) {
 		case EAGAIN:
 			if (retrycount < smp_retry_times) {
-				smp_pkt->pkt_reason = 0;
 				bzero(smp_pkt->pkt_rsp,
 				    (size_t)usmp_cmd->usmp_rspsize);
-				delay(drv_usectohz(10000)); /* 10 ms */
+				if (smp_retry_delay)
+					delay(drv_usectohz(smp_retry_delay));
 				continue;
 			} else {
+				smp_retry_failed++;
 				smp_log(smp_state, CE_NOTE,
 				    "!sas_smp_transport failed, pkt_reason %d",
 				    smp_pkt->pkt_reason);
@@ -516,6 +558,9 @@
 	if ((cmd_flags & SMP_FLAG_RSPBUF) != 0) {
 		kmem_free(smp_pkt->pkt_rsp, smp_pkt->pkt_rspsize);
 	}
+
+	if (rval)
+		smp_failed++;
 	return (rval);
 }
 
--- a/usr/src/uts/common/io/usb/scsa2usb/scsa2usb.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/usb/scsa2usb/scsa2usb.c	Mon Oct 05 09:17:26 2009 -0400
@@ -370,8 +370,11 @@
 
 	/* Alcor Micro Corp 6387 flash disk */
 	{MS_ALCOR_VID, MS_ALCOR_PID0, 0,
-	    SCSA2USB_ATTRS_GET_LUN | SCSA2USB_ATTRS_USE_CSW_RESIDUE}
-
+	    SCSA2USB_ATTRS_GET_LUN | SCSA2USB_ATTRS_USE_CSW_RESIDUE},
+
+	/* Western Digital External HDD */
+	{MS_WD_VID, MS_WD_PID, 0,
+	    SCSA2USB_ATTRS_INQUIRY_EVPD}
 };
 
 
@@ -3123,9 +3126,29 @@
 			cmd->cmd_pkt->pkt_state |= STATE_XFERRED_DATA;
 
 			return (SCSA2USB_JUST_ACCEPT);
+		} else if (!(scsa2usbp->scsa2usb_attrs &
+		    SCSA2USB_ATTRS_INQUIRY_EVPD)) {
+			/*
+			 * Some devices do not handle the inquiry cmd with
+			 * evpd bit set well, e.g. some devices return the
+			 * same page 0x83 data which will cause the generated
+			 * devid by sd is not unique, thus return CHECK
+			 * CONDITION directly to sd.
+			 */
+			uchar_t evpd = 0x01;
+
+			if (!(cmd->cmd_pkt->pkt_cdbp[1] & evpd))
+				break;
+
+			if (cmd->cmd_bp) {
+				cmd->cmd_pkt->pkt_resid = cmd->cmd_bp->
+				    b_bcount;
+			}
+			scsa2usb_force_invalid_request(scsa2usbp, cmd);
+
+			return (SCSA2USB_JUST_ACCEPT);
 		}
 		break;
-
 	/*
 	 * Fake accepting the following  Opcodes
 	 * (as most drives don't support these)
@@ -3171,6 +3194,7 @@
 scsa2usb_handle_scsi_cmd_sub_class(scsa2usb_state_t *scsa2usbp,
     scsa2usb_cmd_t *cmd, struct scsi_pkt *pkt)
 {
+	uchar_t evpd = 0x01;
 	USB_DPRINTF_L4(DPRINT_MASK_SCSA, scsa2usbp->scsa2usb_log_handle,
 	    "scsa2usb_handle_scsi_cmd_sub_class: cmd = 0x%p pkt = 0x%p",
 	    (void *)cmd, (void *)pkt);
@@ -3208,9 +3232,26 @@
 		cmd->cmd_dir = CBW_DIR_IN;
 		cmd->cmd_actual_len = CDB_GROUP0;
 		cmd->cmd_cdb[SCSA2USB_LBA_0] = pkt->pkt_cdbp[2];
-		cmd->cmd_cdb[SCSA2USB_LBA_2] = cmd->cmd_xfercount =
-		    min(SCSA2USB_MAX_INQ_LEN,
-		    cmd->cmd_bp ? cmd->cmd_bp->b_bcount : 0);
+
+		/*
+		 * If vpd pages data is limited to maximum SCSA2USB_MAX_INQ_LEN,
+		 * the page data may be truncated, which may cause some issues
+		 * such as making the unique page 0x83 or 0x80 data from
+		 * different devices become the same. So don't limit return
+		 * length for vpd page inquiry cmd.
+		 * Another, in order to maintain compatibility, the original
+		 * length limitation for standard inquiry retains here. It
+		 * can be removed in future if it is verified that enough
+		 * devices can work well.
+		 */
+		if (pkt->pkt_cdbp[1] & evpd) {
+			cmd->cmd_cdb[SCSA2USB_LBA_2] = cmd->cmd_xfercount =
+			    (cmd->cmd_bp ? cmd->cmd_bp->b_bcount : 0);
+		} else {
+			cmd->cmd_cdb[SCSA2USB_LBA_2] = cmd->cmd_xfercount =
+			    min(SCSA2USB_MAX_INQ_LEN,
+			    cmd->cmd_bp ? cmd->cmd_bp->b_bcount : 0);
+		}
 		break;
 
 	case SCMD_READ_CAPACITY:
--- a/usr/src/uts/common/io/zcons.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/io/zcons.c	Mon Oct 05 09:17:26 2009 -0400
@@ -172,6 +172,7 @@
 #include <sys/zcons.h>
 #include <sys/vnode.h>
 #include <sys/fs/snode.h>
+#include <sys/zone.h>
 
 static int zc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
 static int zc_attach(dev_info_t *, ddi_attach_cmd_t);
@@ -328,38 +329,11 @@
 	return (mod_info(&modlinkage, modinfop));
 }
 
-/*
- * This is a convenience function that clears a device's autopush configuration.
- * It is meant to be used on the slave side of the console.  Unlike calling
- * kstr_autopush() directly, this function outputs a warning via cmn_err() if
- * kstr_autopush() fails.  'dip' must be non-NULL in debug builds.  Both
- * 'major' and 'minor' must be valid.
- */
-static void
-zc_clearautopush(dev_info_t *dip, major_t major, minor_t minor)
-{
-	char *devicepathp;
-
-	if (kstr_autopush(CLR_AUTOPUSH, &major, &minor, NULL, NULL, NULL) !=
-	    0 && zcons_debug != 0) {
-		devicepathp = (char *)kmem_alloc(MAXPATHLEN * sizeof (char),
-		    KM_SLEEP);
-		(void) ddi_pathname(dip, devicepathp);
-		cmn_err(CE_NOTE, "zc_detach: could not clear sad configuration "
-		    "for device %s\n", devicepathp);
-		kmem_free(devicepathp, MAXPATHLEN * sizeof (char));
-	}
-}
-
 static int
 zc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 {
 	zc_state_t *zcs;
 	int instance;
-	major_t major;
-	minor_t minor;
-	minor_t lastminor;
-	uint_t anchorindex;
 
 	if (cmd != DDI_ATTACH)
 		return (DDI_FAILURE);
@@ -369,38 +343,20 @@
 		return (DDI_FAILURE);
 
 	/*
-	 * Set up sad(7D) so that the necessary STREAMS modules will be in place
-	 * when the slave is opened.  A wrinkle is that 'ptem' must be anchored
-	 * in place (see streamio(7i)) because we always want the console to
-	 * have terminal semantics.
-	 */
-	minor = instance << 1 | ZC_SLAVE_MINOR;
-	lastminor = 0;
-	major = ddi_driver_major(dip);
-	anchorindex = 1;
-	if (kstr_autopush(SET_AUTOPUSH, &major, &minor, &lastminor,
-	    &anchorindex, zcons_mods) != 0)
-		goto commonfail;
-
-	/*
 	 * Create the master and slave minor nodes.
 	 */
-	if ((ddi_create_minor_node(dip, ZCONS_SLAVE_NAME, S_IFCHR, minor,
-	    DDI_PSEUDO, 0) == DDI_FAILURE) ||
+	if ((ddi_create_minor_node(dip, ZCONS_SLAVE_NAME, S_IFCHR,
+	    instance << 1 | ZC_SLAVE_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE) ||
 	    (ddi_create_minor_node(dip, ZCONS_MASTER_NAME, S_IFCHR,
-	    instance << 1 | ZC_MASTER_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE))
-		goto failwithautopush;
+	    instance << 1 | ZC_MASTER_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE)) {
+		ddi_remove_minor_node(dip, NULL);
+		ddi_soft_state_free(zc_soft_state, instance);
+		return (DDI_FAILURE);
+	}
 
 	VERIFY((zcs = ddi_get_soft_state(zc_soft_state, instance)) != NULL);
 	zcs->zc_devinfo = dip;
 	return (DDI_SUCCESS);
-
-failwithautopush:
-	zc_clearautopush(dip, major, minor);
-	ddi_remove_minor_node(dip, NULL);
-commonfail:
-	ddi_soft_state_free(zc_soft_state, instance);
-	return (DDI_FAILURE);
 }
 
 static int
@@ -422,13 +378,6 @@
 		return (DDI_FAILURE);
 	}
 
-	/*
-	 * Clear the sad configuration so that reattaching doesn't fail to
-	 * set up sad configuration.
-	 */
-	zc_clearautopush(dip, ddi_driver_major(dip), instance << 1 |
-	    ZC_SLAVE_MINOR);
-
 	ddi_remove_minor_node(dip, NULL);
 	ddi_soft_state_free(zc_soft_state, instance);
 
@@ -570,6 +519,10 @@
 {
 	mblk_t *mop;
 	struct stroptions *sop;
+	major_t major;
+	minor_t minor;
+	minor_t lastminor;
+	uint_t anchorindex;
 
 	/*
 	 * The slave side can be opened as many times as needed.
@@ -579,6 +532,22 @@
 		return (0);
 	}
 
+	/*
+	 * Set up sad(7D) so that the necessary STREAMS modules will be in
+	 * place.  A wrinkle is that 'ptem' must be anchored
+	 * in place (see streamio(7i)) because we always want the console to
+	 * have terminal semantics.
+	 */
+	minor = ddi_get_instance(zcs->zc_devinfo) << 1 | ZC_SLAVE_MINOR;
+	major = ddi_driver_major(zcs->zc_devinfo);
+	lastminor = 0;
+	anchorindex = 1;
+	if (kstr_autopush(SET_AUTOPUSH, &major, &minor, &lastminor,
+	    &anchorindex, zcons_mods) != 0) {
+		DBG("zc_slave_open(): kstr_autopush() failed\n");
+		return (EIO);
+	}
+
 	if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
 		DBG("zc_slave_open(): mop allocation failed\n");
 		return (ENOMEM);
@@ -659,6 +628,8 @@
 	queue_t *wqp;
 	mblk_t	*bp;
 	zc_state_t *zcs;
+	major_t major;
+	minor_t minor;
 
 	zcs = (zc_state_t *)rqp->q_ptr;
 
@@ -704,6 +675,15 @@
 
 		qprocsoff(rqp);
 		WR(rqp)->q_ptr = rqp->q_ptr = NULL;
+
+		/*
+		 * Clear the sad configuration so that reopening doesn't fail
+		 * to set up sad configuration.
+		 */
+		major = ddi_driver_major(zcs->zc_devinfo);
+		minor = ddi_get_instance(zcs->zc_devinfo) << 1 | ZC_SLAVE_MINOR;
+		(void) kstr_autopush(CLR_AUTOPUSH, &major, &minor, NULL, NULL,
+		    NULL);
 	}
 
 	return (0);
@@ -799,6 +779,15 @@
 			}
 
 			/*
+			 * The process that passed the ioctl must be running in
+			 * the global zone.
+			 */
+			if (curzone != global_zone) {
+				miocack(qp, mp, 0, EINVAL);
+				return;
+			}
+
+			/*
 			 * The calling process must pass a file descriptor for
 			 * the slave device.
 			 */
@@ -848,6 +837,15 @@
 			}
 
 			/*
+			 * The process that passed the ioctl must be running in
+			 * the global zone.
+			 */
+			if (curzone != global_zone) {
+				miocack(qp, mp, 0, EINVAL);
+				return;
+			}
+
+			/*
 			 * The process that passed the ioctl must have provided
 			 * a file descriptor for the slave device.  Make sure
 			 * this is correct.
--- a/usr/src/uts/common/os/bitset.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/os/bitset.c	Mon Oct 05 09:17:26 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -260,3 +260,80 @@
 
 	return (elt);
 }
+
+/*
+ * AND, OR, and XOR bitset computations
+ * Returns 1 if resulting bitset has any set bits
+ */
+int
+bitset_and(bitset_t *bs1, bitset_t *bs2, bitset_t *res)
+{
+	int i, anyset;
+
+	for (anyset = 0, i = 0; i < bs1->bs_words; i++) {
+		if ((res->bs_set[i] = (bs1->bs_set[i] & bs2->bs_set[i])) != 0)
+			anyset = 1;
+	}
+	return (anyset);
+}
+
+int
+bitset_or(bitset_t *bs1, bitset_t *bs2, bitset_t *res)
+{
+	int i, anyset;
+
+	for (anyset = 0, i = 0; i < bs1->bs_words; i++) {
+		if ((res->bs_set[i] = (bs1->bs_set[i] | bs2->bs_set[i])) != 0)
+			anyset = 1;
+	}
+	return (anyset);
+}
+
+int
+bitset_xor(bitset_t *bs1, bitset_t *bs2, bitset_t *res)
+{
+	int i;
+	int anyset = 0;
+
+	for (i = 0; i < bs1->bs_words; i++) {
+		if ((res->bs_set[i] = (bs1->bs_set[i] ^ bs2->bs_set[i])) != 0)
+			anyset = 1;
+	}
+	return (anyset);
+}
+
+/*
+ * return 1 if bitmaps are identical
+ */
+int
+bitset_match(bitset_t *bs1, bitset_t *bs2)
+{
+	int i;
+
+	if (bs1->bs_words != bs2->bs_words)
+		return (0);
+
+	for (i = 0; i < bs1->bs_words; i++)
+		if (bs1->bs_set[i] != bs2->bs_set[i])
+			return (0);
+	return (1);
+}
+
+/*
+ * Zero a bitset_t
+ */
+void
+bitset_zero(bitset_t *b)
+{
+	bzero(b->bs_set, sizeof (ulong_t) * b->bs_words);
+}
+
+
+/*
+ * Copy a bitset_t
+ */
+void
+bitset_copy(bitset_t *src, bitset_t *dest)
+{
+	bcopy(src->bs_set, dest->bs_set, sizeof (ulong_t) * src->bs_words);
+}
--- a/usr/src/uts/common/os/callout.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/os/callout.c	Mon Oct 05 09:17:26 2009 -0400
@@ -55,10 +55,10 @@
 static callout_table_t *callout_table;		/* global callout table array */
 
 /*
- * We run normal callouts from PIL 10. This means that no other handler that
- * runs at PIL 10 is allowed to wait for normal callouts directly or indirectly
- * as it will cause a deadlock. This has always been an unwritten rule.
- * We are making it explicit here.
+ * We run 'realtime' callouts at PIL 1 (CY_LOW_LEVEL). For 'normal'
+ * callouts, from PIL 10 (CY_LOCK_LEVEL) we dispatch the callout,
+ * via taskq, to a thread that executes at PIL 0 - so we end up running
+ * 'normal' callouts at PIL 0.
  */
 static volatile int callout_realtime_level = CY_LOW_LEVEL;
 static volatile int callout_normal_level = CY_LOCK_LEVEL;
--- a/usr/src/uts/common/os/clock.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/os/clock.c	Mon Oct 05 09:17:26 2009 -0400
@@ -67,6 +67,8 @@
 #include <sys/task.h>
 #include <sys/sdt.h>
 #include <sys/ddi_timer.h>
+#include <sys/random.h>
+#include <sys/modctl.h>
 
 /*
  * for NTP support
@@ -87,6 +89,7 @@
 extern sysinfo_t	sysinfo;
 extern vminfo_t	vminfo;
 extern int	idleswtch;	/* flag set while idle in pswtch() */
+extern hrtime_t volatile devinfo_freeze;
 
 /*
  * high-precision avenrun values.  These are needed to make the
@@ -274,6 +277,10 @@
 /* patchable via /etc/system */
 int tod_validate_enable = 1;
 
+/* Diagnose/Limit messages about delay(9F) called from interrupt context */
+int			delay_from_interrupt_diagnose = 0;
+volatile uint32_t	delay_from_interrupt_msg = 20;
+
 /*
  * On non-SPARC systems, TOD validation must be deferred until gethrtime
  * returns non-zero values (after mach_clkinit's execution).
@@ -1582,37 +1589,91 @@
 static void
 delay_wakeup(void *arg)
 {
-	kthread_t *t = arg;
+	kthread_t	*t = arg;
 
 	mutex_enter(&t->t_delay_lock);
 	cv_signal(&t->t_delay_cv);
 	mutex_exit(&t->t_delay_lock);
 }
 
+/*
+ * The delay(9F) man page indicates that it can only be called from user or
+ * kernel context - detect and diagnose bad calls. The following macro will
+ * produce a limited number of messages identifying bad callers.  This is done
+ * in a macro so that caller() is meaningful. When a bad caller is identified,
+ * switching to 'drv_usecwait(TICK_TO_USEC(ticks));' may be appropriate.
+ */
+#define	DELAY_CONTEXT_CHECK()	{					\
+	uint32_t	m;						\
+	char		*f;						\
+	ulong_t		off;						\
+									\
+	m = delay_from_interrupt_msg;					\
+	if (delay_from_interrupt_diagnose && servicing_interrupt() &&	\
+	    !panicstr && !devinfo_freeze &&				\
+	    atomic_cas_32(&delay_from_interrupt_msg, m ? m : 1, m-1)) {	\
+		f = modgetsymname((uintptr_t)caller(), &off);		\
+		cmn_err(CE_WARN, "delay(9F) called from "		\
+		    "interrupt context: %s`%s",				\
+		    mod_containing_pc(caller()), f ? f : "...");	\
+	}								\
+}
+
+/*
+ * delay_common: common delay code.
+ */
+static void
+delay_common(clock_t ticks)
+{
+	kthread_t	*t = curthread;
+	clock_t		deadline;
+	clock_t		timeleft;
+	callout_id_t	id;
+
+	/* If timeouts aren't running all we can do is spin. */
+	if (panicstr || devinfo_freeze) {
+		/* Convert delay(9F) call into drv_usecwait(9F) call. */
+		if (ticks > 0)
+			drv_usecwait(TICK_TO_USEC(ticks));
+		return;
+	}
+
+	deadline = lbolt + ticks;
+	while ((timeleft = deadline - lbolt) > 0) {
+		mutex_enter(&t->t_delay_lock);
+		id = timeout_default(delay_wakeup, t, timeleft);
+		cv_wait(&t->t_delay_cv, &t->t_delay_lock);
+		mutex_exit(&t->t_delay_lock);
+		(void) untimeout_default(id, 0);
+	}
+}
+
+/*
+ * Delay specified number of clock ticks.
+ */
 void
 delay(clock_t ticks)
 {
-	kthread_t *t = curthread;
-	clock_t deadline = lbolt + ticks;
-	clock_t timeleft;
-	timeout_id_t id;
-	extern hrtime_t volatile devinfo_freeze;
+	DELAY_CONTEXT_CHECK();
+
+	delay_common(ticks);
+}
 
-	if ((panicstr || devinfo_freeze) && ticks > 0) {
-		/*
-		 * Timeouts aren't running, so all we can do is spin.
-		 */
-		drv_usecwait(TICK_TO_USEC(ticks));
-		return;
-	}
+/*
+ * Delay a random number of clock ticks between 1 and ticks.
+ */
+void
+delay_random(clock_t ticks)
+{
+	int	r;
 
-	while ((timeleft = deadline - lbolt) > 0) {
-		mutex_enter(&t->t_delay_lock);
-		id = timeout(delay_wakeup, t, timeleft);
-		cv_wait(&t->t_delay_cv, &t->t_delay_lock);
-		mutex_exit(&t->t_delay_lock);
-		(void) untimeout(id);
-	}
+	DELAY_CONTEXT_CHECK();
+
+	(void) random_get_pseudo_bytes((void *)&r, sizeof (r));
+	if (ticks == 0)
+		ticks = 1;
+	ticks = (r % ticks) + 1;
+	delay_common(ticks);
 }
 
 /*
@@ -1621,20 +1682,31 @@
 int
 delay_sig(clock_t ticks)
 {
-	clock_t deadline = lbolt + ticks;
-	clock_t rc;
+	kthread_t	*t = curthread;
+	clock_t		deadline;
+	clock_t		rc;
 
-	mutex_enter(&curthread->t_delay_lock);
+	/* If timeouts aren't running all we can do is spin. */
+	if (panicstr || devinfo_freeze) {
+		if (ticks > 0)
+			drv_usecwait(TICK_TO_USEC(ticks));
+		return (0);
+	}
+
+	deadline = lbolt + ticks;
+	mutex_enter(&t->t_delay_lock);
 	do {
-		rc = cv_timedwait_sig(&curthread->t_delay_cv,
-		    &curthread->t_delay_lock, deadline);
+		rc = cv_timedwait_sig(&t->t_delay_cv,
+		    &t->t_delay_lock, deadline);
+		/* loop until past deadline or signaled */
 	} while (rc > 0);
-	mutex_exit(&curthread->t_delay_lock);
+	mutex_exit(&t->t_delay_lock);
 	if (rc == 0)
 		return (EINTR);
 	return (0);
 }
 
+
 #define	SECONDS_PER_DAY 86400
 
 /*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/os/damap.c	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,1258 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/note.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/sunndi.h>
+#include <sys/kstat.h>
+#include <sys/conf.h>
+#include <sys/ddi_timer.h>
+#include <sys/devctl.h>
+#include <sys/callb.h>
+#include <sys/sysevent.h>
+#include <sys/taskq.h>
+#include <sys/ddi.h>
+#include <sys/bitset.h>
+#include <sys/damap.h>
+#include <sys/damap_impl.h>
+
+#ifdef DEBUG
+static int damap_debug = 0;
+#endif /* DEBUG */
+
+static void dam_addrset_activate(dam_t *, bitset_t *);
+static void dam_addrset_release(dam_t *, bitset_t *);
+static void dam_activate_taskq(void *);
+static void dam_addr_stable_cb(void *);
+static void dam_set_stable_cb(void *);
+static void dam_sched_tmo(dam_t *, clock_t, void (*tmo_cb)());
+static void dam_add_report(dam_t *, dam_da_t *, id_t, int);
+static void dam_release(dam_t *, id_t);
+static void dam_release_report(dam_t *, id_t);
+static void dam_deactivate_addr(dam_t *, id_t);
+static id_t dam_get_addrid(dam_t *, char *);
+static int dam_kstat_create(dam_t *);
+static void dam_kstat_destroy(dam_t *);
+
+#define	DAM_INCR_STAT(mapp, stat)				\
+	if ((mapp)->dam_kstatsp) {				\
+		struct dam_kstats *stp = (mapp)->dam_kstatsp->ks_data;	\
+		stp->stat.value.ui32++;				\
+	}
+
+#define	DAM_SET_STAT(mapp, stat, val)				\
+	if ((mapp)->dam_kstatsp) {				\
+		struct dam_kstats *stp = (mapp)->dam_kstatsp->ks_data;	\
+		stp->stat.value.ui32 = (val);			\
+	}
+
+/*
+ * Create new device address map
+ *
+ * ident:		map name (kstat)
+ * size:		max # of map entries
+ * rptmode:		type or mode of reporting
+ * stable_usec:		# of quiescent microseconds before report/map is stable
+ *
+ * activate_arg:	address provider activation-callout private
+ * activate_cb:		address provider activation callback handler
+ * deactivate_cb:	address provider deactivation callback handler
+ *
+ * config_arg:		configuration-callout private
+ * config_cb:		class configuration callout
+ * unconfig_cb:		class unconfiguration callout
+ *
+ * damapp:		pointer to map handle (return)
+ *
+ * Returns:	DAM_SUCCESS
+ *		DAM_EINVAL	Invalid argument(s)
+ *		DAM_FAILURE	General failure
+ */
+int
+damap_create(char *ident, size_t size, damap_rptmode_t rptmode,
+    clock_t stable_usec,
+    void *activate_arg, damap_activate_cb_t activate_cb,
+    damap_deactivate_cb_t deactivate_cb,
+    void *config_arg, damap_configure_cb_t configure_cb,
+    damap_unconfig_cb_t unconfig_cb,
+    damap_t **damapp)
+{
+	dam_t *mapp;
+	void *softstate_p;
+
+	DTRACE_PROBE1(damap__create__entry, char *, ident);
+	if ((configure_cb == NULL) || (unconfig_cb == NULL))
+		return (DAM_EINVAL);
+
+	if (ddi_soft_state_init(&softstate_p, sizeof (dam_da_t), size) !=
+	    DDI_SUCCESS)
+		return (DAM_FAILURE);
+
+	mapp = kmem_zalloc(sizeof (*mapp), KM_SLEEP);
+	if (ddi_strid_init(&mapp->dam_addr_hash, size) != DDI_SUCCESS) {
+		ddi_soft_state_fini(&softstate_p);
+		kmem_free(mapp, sizeof (*mapp));
+		return (DAM_FAILURE);
+	}
+
+	mapp->dam_da = softstate_p;
+	mapp->dam_stabletmo = drv_usectohz(stable_usec);
+	mapp->dam_size = size;
+	mapp->dam_high = 1;
+	mapp->dam_rptmode = rptmode;
+
+	mapp->dam_activate_arg = activate_arg;
+	mapp->dam_activate_cb = (activate_cb_t)activate_cb;
+	mapp->dam_deactivate_cb = (deactivate_cb_t)deactivate_cb;
+
+	mapp->dam_config_arg = config_arg;
+	mapp->dam_configure_cb = (configure_cb_t)configure_cb;
+	mapp->dam_unconfig_cb = (unconfig_cb_t)unconfig_cb;
+
+	if (ident)
+		mapp->dam_name = i_ddi_strdup(ident, KM_SLEEP);
+
+	bitset_init(&mapp->dam_active_set);
+	bitset_resize(&mapp->dam_active_set, size);
+	bitset_init(&mapp->dam_stable_set);
+	bitset_resize(&mapp->dam_stable_set, size);
+	bitset_init(&mapp->dam_report_set);
+	bitset_resize(&mapp->dam_report_set, size);
+	mutex_init(&mapp->dam_lock, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&mapp->dam_cv, NULL, CV_DRIVER, NULL);
+	mapp->dam_taskqp = ddi_taskq_create(NULL, ident, 1, TASKQ_DEFAULTPRI,
+	    0);
+	*damapp = (damap_t *)mapp;
+	if (dam_kstat_create(mapp) != DDI_SUCCESS) {
+		damap_destroy((damap_t *)mapp);
+		return (DAM_FAILURE);
+	}
+
+	DTRACE_PROBE1(damap__create__exit, dam_t *, mapp);
+	return (DAM_SUCCESS);
+}
+
+/*
+ * Destroy device address map
+ *
+ * damapp:	address map
+ *
+ * Returns:	DAM_SUCCESS
+ *		DAM_EINVAL	Invalid argument(s)
+ *		DAM_FAILURE	General failure
+ */
+void
+damap_destroy(damap_t *damapp)
+{
+	int i;
+	dam_t *mapp = (dam_t *)damapp;
+
+	ASSERT(mapp);
+
+	DTRACE_PROBE2(damap__destroy__entry, dam_t *, mapp, char *,
+	    mapp->dam_name);
+
+	DAM_FLAG_SET(mapp, DAM_DESTROYPEND);
+	(void) damap_sync(damapp);
+
+	/*
+	 * cancel pending timeouts and kill off the taskq
+	 */
+	dam_sched_tmo(mapp, 0, NULL);
+	ddi_taskq_wait(mapp->dam_taskqp);
+	ddi_taskq_destroy(mapp->dam_taskqp);
+
+	for (i = 1; i < mapp->dam_high; i++) {
+		if (ddi_get_soft_state(mapp->dam_da, i) == NULL)
+			continue;
+		if (DAM_IN_REPORT(mapp, i))
+			dam_release_report(mapp, i);
+		if (DAM_IS_STABLE(mapp, i))
+			dam_deactivate_addr(mapp, i);
+		ddi_strid_free(mapp->dam_addr_hash, i);
+		ddi_soft_state_free(mapp->dam_da, i);
+	}
+	ddi_strid_fini(&mapp->dam_addr_hash);
+	ddi_soft_state_fini(&mapp->dam_da);
+	bitset_fini(&mapp->dam_active_set);
+	bitset_fini(&mapp->dam_stable_set);
+	bitset_fini(&mapp->dam_report_set);
+	dam_kstat_destroy(mapp);
+	mutex_destroy(&mapp->dam_lock);
+	cv_destroy(&mapp->dam_cv);
+	if (mapp->dam_name)
+		kmem_free(mapp->dam_name, strlen(mapp->dam_name) + 1);
+	kmem_free(mapp, sizeof (*mapp));
+	DTRACE_PROBE(damap__destroy__exit);
+}
+
+/*
+ * Wait for map stability.
+ *
+ * damapp:	address map
+ */
+int
+damap_sync(damap_t *damapp)
+{
+
+#define	WAITFOR_FLAGS (DAM_SETADD | DAM_SPEND | MAP_LOCK)
+
+	dam_t *mapp = (dam_t *)damapp;
+	int   none_active;
+
+	ASSERT(mapp);
+
+	DTRACE_PROBE1(damap__sync__entry, dam_t *, mapp);
+
+	mutex_enter(&mapp->dam_lock);
+	while ((mapp->dam_flags & WAITFOR_FLAGS) ||
+	    (!bitset_is_null(&mapp->dam_report_set)) || (mapp->dam_tid != 0)) {
+		cv_wait(&mapp->dam_cv, &mapp->dam_lock);
+	}
+
+	none_active = bitset_is_null(&mapp->dam_active_set);
+
+	mutex_exit(&mapp->dam_lock);
+	DTRACE_PROBE2(damap__sync__exit, dam_t *, mapp, int, none_active);
+
+	return (none_active);
+}
+
+/*
+ * Get the name of a device address map
+ *
+ * damapp:	address map
+ *
+ * Returns:	name
+ */
+char *
+damap_name(damap_t *damapp)
+{
+	dam_t *mapp = (dam_t *)damapp;
+
+	return (mapp ? mapp->dam_name : "UNKNOWN_damap");
+}
+
+/*
+ * Report an address to per-address report
+ *
+ * damapp:	address map handle
+ * address:	address in ascii string representation
+ * rindx:	index if address stabilizes
+ * nvl:		optional nvlist of configuration-private data
+ * addr_priv:	optional provider-private (passed to activate/deactivate cb)
+ *
+ * Returns:	DAM_SUCCESS
+ *		DAM_EINVAL	Invalid argument(s)
+ *		DAM_MAPFULL	address map exhausted
+ */
+int
+damap_addr_add(damap_t *damapp, char *address, damap_id_t *ridx, nvlist_t *nvl,
+    void *addr_priv)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	id_t addrid;
+	dam_da_t *passp;
+
+	DTRACE_PROBE2(damap__addr__add__entry, dam_t *, mapp,
+	    char *, address);
+	if (!mapp || !address || (mapp->dam_rptmode != DAMAP_REPORT_PERADDR) ||
+	    (mapp->dam_flags & DAM_DESTROYPEND))
+		return (DAM_EINVAL);
+
+	DAM_LOCK(mapp, ADDR_LOCK);
+	if ((addrid = dam_get_addrid(mapp, address)) == 0) {
+		DAM_UNLOCK(mapp, ADDR_LOCK);
+		return (DAM_MAPFULL);
+	}
+
+	passp = ddi_get_soft_state(mapp->dam_da, addrid);
+	ASSERT(passp != NULL);
+
+	/*
+	 * If re-reporting the same address (add or remove) clear
+	 * the existing report
+	 */
+	if (DAM_IN_REPORT(mapp, addrid)) {
+		DAM_INCR_STAT(mapp, dam_rereport);
+		dam_release_report(mapp, addrid);
+		passp->da_jitter++;
+	}
+	passp->da_ppriv_rpt = addr_priv;
+	if (nvl)
+		(void) nvlist_dup(nvl, &passp->da_nvl_rpt, KM_SLEEP);
+
+	dam_add_report(mapp, passp, addrid, RPT_ADDR_ADD);
+	if (ridx != NULL)
+		*ridx = (damap_id_t)addrid;
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+	DTRACE_PROBE3(damap__addr__add__exit, dam_t *, mapp, char *,
+	    address, int, addrid);
+	return (DAM_SUCCESS);
+}
+
+/*
+ * Report removal of address from per-address report
+ *
+ * damapp:	address map
+ * address:	address in ascii string representation
+ *
+ * Returns:	DAM_SUCCESS
+ *		DAM_EINVAL	Invalid argument(s)
+ *		DAM_FAILURE	General failure
+ */
+int
+damap_addr_del(damap_t *damapp, char *address)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	id_t addrid;
+	dam_da_t *passp;
+
+	DTRACE_PROBE2(damap__addr__del__entry, dam_t *, mapp,
+	    char *, address);
+	if (!mapp || !address || (mapp->dam_rptmode != DAMAP_REPORT_PERADDR) ||
+	    (mapp->dam_flags & DAM_DESTROYPEND))
+		return (DAM_EINVAL);
+
+	DAM_LOCK(mapp, ADDR_LOCK);
+	if (!(addrid = ddi_strid_str2id(mapp->dam_addr_hash, address))) {
+		DAM_UNLOCK(mapp, ADDR_LOCK);
+		return (DAM_SUCCESS);
+	}
+	passp = ddi_get_soft_state(mapp->dam_da, addrid);
+	ASSERT(passp);
+	if (DAM_IN_REPORT(mapp, addrid)) {
+		DAM_INCR_STAT(mapp, dam_rereport);
+		dam_release_report(mapp, addrid);
+		passp->da_jitter++;
+	}
+	dam_add_report(mapp, passp, addrid, RPT_ADDR_DEL);
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+	DTRACE_PROBE3(damap__addr__del__exit, dam_t *, mapp,
+	    char *, address, int, addrid);
+	return (DAM_SUCCESS);
+}
+
+/*
+ * Initiate full-set report
+ *
+ * damapp:	address map
+ *
+ * Returns:	DAM_SUCCESS
+ *		DAM_EINVAL	Invalid argument(s)
+ */
+int
+damap_addrset_begin(damap_t *damapp)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	int i;
+
+	DTRACE_PROBE1(damap__addrset__begin__entry, dam_t *, mapp);
+
+	if ((mapp->dam_rptmode != DAMAP_REPORT_FULLSET) ||
+	    (mapp->dam_flags & DAM_DESTROYPEND))
+		return (DAM_EINVAL);
+
+	DAM_LOCK(mapp, MAP_LOCK);
+	/*
+	 * reset any pending reports
+	 */
+	if (mapp->dam_flags & DAM_SETADD) {
+		/*
+		 * cancel stabilization timeout
+		 */
+		dam_sched_tmo(mapp, 0, NULL);
+		DAM_INCR_STAT(mapp, dam_rereport);
+		DAM_UNLOCK(mapp, MAP_LOCK);
+		DAM_LOCK(mapp, ADDR_LOCK);
+		for (i = 1; i < mapp->dam_high; i++) {
+			if (DAM_IN_REPORT(mapp, i))
+				dam_release_report(mapp, i);
+		}
+		DAM_UNLOCK(mapp, ADDR_LOCK);
+		DAM_LOCK(mapp, MAP_LOCK);
+	}
+	DAM_FLAG_SET(mapp, DAM_SETADD);
+	bitset_zero(&mapp->dam_report_set);
+	DAM_UNLOCK(mapp, MAP_LOCK);
+	DTRACE_PROBE(damap__addrset__begin__exit);
+	return (DAM_SUCCESS);
+}
+
+/*
+ * Report address to full-set report
+ *
+ * damapp:	address map handle
+ * address:	address in ascii string representation
+ * rindx:	index if address stabilizes
+ * nvl:		optional nvlist of configuration-private data
+ * addr_priv:	optional provider-private data (passed to activate/release cb)
+ *
+ * Returns:	DAM_SUCCESS
+ *		DAM_EINVAL	Invalid argument(s)
+ *		DAM_MAPFULL	address map exhausted
+ *		DAM_FAILURE	General failure
+ */
+int
+damap_addrset_add(damap_t *damapp, char *address, damap_id_t *ridx,
+    nvlist_t *nvl, void *addr_priv)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	id_t addrid;
+	dam_da_t *passp;
+
+	DTRACE_PROBE2(damap__addrset__add__entry, dam_t *, mapp,
+	    char *, address);
+
+	if (!mapp || !address || (mapp->dam_rptmode != DAMAP_REPORT_FULLSET) ||
+	    (mapp->dam_flags & DAM_DESTROYPEND))
+		return (DAM_EINVAL);
+
+	if (!(mapp->dam_flags & DAM_SETADD))
+		return (DAM_FAILURE);
+
+	DAM_LOCK(mapp, ADDR_LOCK);
+	if ((addrid = dam_get_addrid(mapp, address)) == 0) {
+		DAM_UNLOCK(mapp, ADDR_LOCK);
+		return (DAM_MAPFULL);
+	}
+
+	passp = ddi_get_soft_state(mapp->dam_da, addrid);
+	ASSERT(passp);
+	if (DAM_IN_REPORT(mapp, addrid)) {
+		dam_release_report(mapp, addrid);
+		passp->da_jitter++;
+	}
+	passp->da_ppriv_rpt = addr_priv;
+	if (nvl)
+		(void) nvlist_dup(nvl, &passp->da_nvl_rpt, KM_SLEEP);
+	DAM_LOCK(mapp, MAP_LOCK);
+	bitset_add(&mapp->dam_report_set, addrid);
+	DAM_UNLOCK(mapp, MAP_LOCK);
+	if (ridx)
+		*ridx = (damap_id_t)addrid;
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+	DTRACE_PROBE3(damap__addr__addset__exit, dam_t *, mapp, char *,
+	    address, int, addrid);
+	return (DAM_SUCCESS);
+}
+
+/*
+ * Commit full-set report for stabilization
+ *
+ * damapp:	address map handle
+ * flags:	(currently 0)
+ *
+ * Returns:	DAM_SUCCESS
+ *		DAM_EINVAL	Invalid argument(s)
+ *		DAM_FAILURE	General failure
+ */
+int
+damap_addrset_end(damap_t *damapp, int flags)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	int i;
+
+	DTRACE_PROBE1(damap__addrset__end__entry, dam_t *, mapp);
+
+	if (!mapp || (mapp->dam_rptmode != DAMAP_REPORT_FULLSET) ||
+	    (mapp->dam_flags & DAM_DESTROYPEND))
+		return (DAM_EINVAL);
+
+	if (!(mapp->dam_flags & DAM_SETADD))
+		return (DAM_FAILURE);
+
+	if (flags & DAMAP_RESET) {
+		DAM_LOCK(mapp, MAP_LOCK);
+		dam_sched_tmo(mapp, 0, NULL);
+		DAM_UNLOCK(mapp, MAP_LOCK);
+		DAM_LOCK(mapp, ADDR_LOCK);
+		for (i = 1; i < mapp->dam_high; i++)
+			if (DAM_IN_REPORT(mapp, i))
+				dam_release_report(mapp, i);
+		DAM_UNLOCK(mapp, ADDR_LOCK);
+	} else {
+		mapp->dam_last_update = gethrtime();
+		DAM_LOCK(mapp, MAP_LOCK);
+		dam_sched_tmo(mapp, mapp->dam_stabletmo, dam_set_stable_cb);
+		DAM_UNLOCK(mapp, MAP_LOCK);
+	}
+	DTRACE_PROBE(damap__addrset__end__exit);
+	return (DAM_SUCCESS);
+}
+
+/*
+ * Return nvlist registered with reported address
+ *
+ * damapp:	address map handle
+ * aid:		address ID
+ *
+ * Returns:	nvlist_t *	provider supplied via damap_addr{set}_add())
+ *		NULL
+ */
+nvlist_t *
+damap_id2nvlist(damap_t *damapp, damap_id_t addrid)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	id_t aid = (id_t)addrid;
+	dam_da_t *pass;
+
+	if (ddi_strid_id2str(mapp->dam_addr_hash, aid)) {
+		if (pass = ddi_get_soft_state(mapp->dam_da, aid))
+			return (pass->da_nvl);
+	}
+	return (NULL);
+}
+
+/*
+ * Return address string
+ *
+ * damapp:	address map handle
+ * aid:		address ID
+ *
+ * Returns:	char *		Address string
+ *		NULL
+ */
+char *
+damap_id2addr(damap_t *damapp, damap_id_t aid)
+{
+	dam_t *mapp = (dam_t *)damapp;
+
+	return (ddi_strid_id2str(mapp->dam_addr_hash, (id_t)aid));
+}
+
+/*
+ * Hold address reference in map
+ *
+ * damapp:	address map handle
+ * aid:		address ID
+ *
+ * Returns:	DAM_SUCCESS
+ *		DAM_FAILURE
+ */
+int
+damap_id_hold(damap_t *damapp, damap_id_t aid)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	dam_da_t *passp;
+
+
+	DAM_LOCK(mapp, ADDR_LOCK);
+	passp = ddi_get_soft_state(mapp->dam_da, (id_t)aid);
+	if (!passp) {
+		DAM_UNLOCK(mapp, ADDR_LOCK);
+		return (DAM_FAILURE);
+	}
+	passp->da_ref++;
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+	return (DAM_SUCCESS);
+}
+
+/*
+ * Release address reference in map
+ *
+ * damapp:	address map handle
+ * aid:		address ID
+ */
+void
+damap_id_rele(damap_t *damapp, damap_id_t addrid)
+{
+	dam_t *mapp = (dam_t *)damapp;
+
+	DAM_LOCK(mapp, ADDR_LOCK);
+	dam_release(mapp, (id_t)addrid);
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+}
+
+/*
+ * Return current reference count on address reference in map
+ *
+ * damapp:	address map handle
+ * aid:		address ID
+ *
+ * Returns:	DAM_SUCCESS
+ *		DAM_FAILURE
+ */
+int
+damap_id_ref(damap_t *damapp, damap_id_t aid)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	dam_da_t *passp;
+	int ref = -1;
+
+	DAM_LOCK(mapp, ADDR_LOCK);
+	passp = ddi_get_soft_state(mapp->dam_da, (id_t)aid);
+	if (passp)
+		ref = passp->da_ref;
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+	return (ref);
+}
+
+/*
+ * Return next address ID in list
+ *
+ * damapp:	address map handle
+ * damap_list:	address ID list passed to config|unconfig
+ *		returned by look by lookup_all
+ * last:	last ID returned, 0 is start of list
+ *
+ * Returns:	addrid		Next ID from the list
+ *		0		End of the list
+ */
+damap_id_t
+damap_id_next(damap_t *damapp, damap_id_list_t damap_list, damap_id_t last)
+{
+	int i, start;
+	dam_t *mapp = (dam_t *)damapp;
+	bitset_t *dam_list = (bitset_t *)damap_list;
+
+	if (!mapp || !dam_list)
+		return ((damap_id_t)0);
+
+	start = (int)last + 1;
+	for (i = start; i < mapp->dam_high; i++)
+		if (bitset_in_set(dam_list, i))
+			return ((damap_id_t)i);
+	return ((damap_id_t)0);
+}
+
+/*
+ * Set config private data
+ *
+ * damapp:	address map handle
+ * aid:		address ID
+ * cfg_priv:	configuration private data
+ *
+ */
+void
+damap_id_priv_set(damap_t *damapp, damap_id_t aid, void *cfg_priv)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	dam_da_t *passp;
+
+
+	DAM_LOCK(mapp, ADDR_LOCK);
+	passp = ddi_get_soft_state(mapp->dam_da, (id_t)aid);
+	if (!passp) {
+		DAM_UNLOCK(mapp, ADDR_LOCK);
+		return;
+	}
+	passp->da_cfg_priv = cfg_priv;
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+}
+
+/*
+ * Get config private data
+ *
+ * damapp:	address map handle
+ * aid:		address ID
+ *
+ * Returns:	configuration private data
+ */
+void *
+damap_id_priv_get(damap_t *damapp, damap_id_t aid)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	dam_da_t *passp;
+	void *rv;
+
+
+	DAM_LOCK(mapp, ADDR_LOCK);
+	passp = ddi_get_soft_state(mapp->dam_da, (id_t)aid);
+	if (!passp) {
+		DAM_UNLOCK(mapp, ADDR_LOCK);
+		return (NULL);
+	}
+	rv = passp->da_cfg_priv;
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+	return (rv);
+}
+
+/*
+ * Lookup a single address in the active address map
+ *
+ * damapp:	address map handle
+ * address:	address string
+ *
+ * Returns:	ID of active/stable address
+ *		0	Address not in stable set
+ *
+ * Future: Allow the caller to wait for stabilize before returning not found.
+ */
+damap_id_t
+damap_lookup(damap_t *damapp, char *address)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	id_t addrid = 0;
+	dam_da_t *passp = NULL;
+
+	DAM_LOCK(mapp, ADDR_LOCK);
+	addrid = ddi_strid_str2id(mapp->dam_addr_hash, address);
+	if (addrid) {
+		DAM_LOCK(mapp, MAP_LOCK);
+		if (DAM_IS_STABLE(mapp, addrid)) {
+			passp = ddi_get_soft_state(mapp->dam_da, addrid);
+			ASSERT(passp);
+			if (passp) {
+				passp->da_ref++;
+			} else {
+				addrid = 0;
+			}
+		} else {
+			addrid = 0;
+		}
+		DAM_UNLOCK(mapp, MAP_LOCK);
+	}
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+	return ((damap_id_t)addrid);
+}
+
+
+/*
+ * Return the list of stable addresses in the map
+ *
+ * damapp:	address map handle
+ * id_listp:	pointer to list of address IDs in stable map (returned)
+ *
+ * Returns:	# of entries returned in alist
+ */
+int
+damap_lookup_all(damap_t *damapp, damap_id_list_t *id_listp)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	int mapsz = mapp->dam_size;
+	int n_ids, i;
+	bitset_t *bsp;
+	dam_da_t *passp;
+
+	bsp = kmem_alloc(sizeof (*bsp), KM_SLEEP);
+	bitset_init(bsp);
+	bitset_resize(bsp, mapsz);
+	DAM_LOCK(mapp, MAP_LOCK);
+	bitset_copy(&mapp->dam_active_set, bsp);
+	DAM_UNLOCK(mapp, MAP_LOCK);
+	DAM_LOCK(mapp, ADDR_LOCK);
+	for (n_ids = 0, i = 1; i < mapsz; i++) {
+		if (bitset_in_set(bsp, i)) {
+			passp = ddi_get_soft_state(mapp->dam_da, i);
+			ASSERT(passp);
+			if (passp) {
+				passp->da_ref++;
+				n_ids++;
+			}
+		}
+	}
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+	if (n_ids) {
+		*id_listp = (damap_id_list_t)bsp;
+		return (n_ids);
+	} else {
+		*id_listp = (damap_id_list_t)NULL;
+		bitset_fini(bsp);
+		kmem_free(bsp, sizeof (*bsp));
+		return (0);
+	}
+}
+
+/*
+ * Release the address list returned by damap_lookup_all()
+ *
+ * mapp:	address map handle
+ * id_list:	list of address IDs returned in damap_lookup_all()
+ */
+void
+damap_id_list_rele(damap_t *damapp, damap_id_list_t id_list)
+{
+	dam_t *mapp = (dam_t *)damapp;
+	int i;
+
+	if (id_list == NULL)
+		return;
+
+	DAM_LOCK(mapp, ADDR_LOCK);
+	for (i = 1; i < mapp->dam_high; i++) {
+		if (bitset_in_set((bitset_t *)id_list, i))
+			(void) dam_release(mapp, i);
+	}
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+	bitset_fini((bitset_t *)id_list);
+	kmem_free((void *)id_list, sizeof (bitset_t));
+}
+
+/*
+ * Activate a set of stabilized addresses
+ */
+static void
+dam_addrset_activate(dam_t *mapp, bitset_t *active_set)
+{
+	dam_da_t *passp;
+	char *addrstr;
+	int i;
+	uint32_t n_active = 0;
+
+	for (i = 1; i < mapp->dam_high; i++) {
+		if (bitset_in_set(&mapp->dam_active_set, i))
+			n_active++;
+		if (!bitset_in_set(active_set, i))
+			continue;
+		n_active++;
+		passp = ddi_get_soft_state(mapp->dam_da, i);
+		ASSERT(passp);
+		if (mapp->dam_activate_cb) {
+			addrstr = ddi_strid_id2str(mapp->dam_addr_hash, i);
+			(*mapp->dam_activate_cb)(
+			    mapp->dam_activate_arg, addrstr, i,
+			    &passp->da_ppriv_rpt);
+		}
+		DTRACE_PROBE2(damap__addrset__activate, dam_t *, mapp, int, i);
+		DAM_LOCK(mapp, MAP_LOCK);
+		bitset_add(&mapp->dam_active_set, i);
+		/*
+		 * copy the reported nvlist and provider private data
+		 */
+		passp->da_nvl = passp->da_nvl_rpt;
+		passp->da_ppriv = passp->da_ppriv_rpt;
+		passp->da_ppriv_rpt = NULL;
+		passp->da_nvl_rpt = NULL;
+		passp->da_last_stable = gethrtime();
+		passp->da_stable_cnt++;
+		DAM_UNLOCK(mapp, MAP_LOCK);
+		DAM_SET_STAT(mapp, dam_numstable, n_active);
+	}
+}
+
+/*
+ * Release a set of stabilized addresses
+ */
+static void
+dam_addrset_release(dam_t *mapp, bitset_t *release_set)
+{
+	int i;
+
+	DAM_LOCK(mapp, ADDR_LOCK);
+	for (i = 1; i < mapp->dam_high; i++) {
+		if (bitset_in_set(release_set, i)) {
+			DTRACE_PROBE2(damap__addrset__release, dam_t *, mapp,
+			    int, i);
+			DAM_LOCK(mapp, MAP_LOCK);
+			bitset_del(&mapp->dam_active_set, i);
+			DAM_UNLOCK(mapp, MAP_LOCK);
+			(void) dam_release(mapp, i);
+		}
+	}
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+}
+
+/*
+ * release a previously activated address
+ */
+static void
+dam_release(dam_t *mapp, id_t addrid)
+{
+	dam_da_t *passp;
+
+	DAM_ASSERT_LOCKED(mapp, ADDR_LOCK);
+	passp = ddi_get_soft_state(mapp->dam_da, addrid);
+	ASSERT(passp);
+
+	/*
+	 * invoke the deactivation callback to notify
+	 * this address is no longer active
+	 */
+	dam_deactivate_addr(mapp, addrid);
+
+	/*
+	 * allow pending reports for this address to stabilize
+	 */
+	if (DAM_IN_REPORT(mapp, addrid))
+		return;
+
+	/*
+	 * defer teardown until outstanding references are released
+	 */
+	if (--passp->da_ref) {
+		passp->da_flags |= DA_RELE;
+		return;
+	}
+	ddi_strid_free(mapp->dam_addr_hash, addrid);
+	ddi_soft_state_free(mapp->dam_da, addrid);
+}
+
+/*
+ * process stabilized address reports
+ */
+static void
+dam_activate_taskq(void *arg)
+{
+	dam_t *mapp = (dam_t *)arg;
+	bitset_t delta;
+	bitset_t cfg;
+	bitset_t uncfg;
+	int has_cfg, has_uncfg;
+
+	bitset_init(&delta);
+	bitset_resize(&delta, mapp->dam_size);
+	bitset_init(&cfg);
+	bitset_resize(&cfg, mapp->dam_size);
+	bitset_init(&uncfg);
+	bitset_resize(&uncfg, mapp->dam_size);
+
+	DTRACE_PROBE1(damap__activate__taskq__entry, dam_t, mapp);
+	DAM_LOCK(mapp, MAP_LOCK);
+	if (!bitset_xor(&mapp->dam_active_set, &mapp->dam_stable_set,
+	    &delta)) {
+		bitset_zero(&mapp->dam_stable_set);
+		DAM_FLAG_CLR(mapp, DAM_SPEND);
+		DAM_UNLOCK(mapp, MAP_LOCK);
+		bitset_fini(&uncfg);
+		bitset_fini(&cfg);
+		bitset_fini(&delta);
+		return;
+	}
+	has_cfg = bitset_and(&delta, &mapp->dam_stable_set, &cfg);
+	has_uncfg = bitset_and(&delta, &mapp->dam_active_set, &uncfg);
+	DAM_UNLOCK(mapp, MAP_LOCK);
+	if (has_cfg) {
+		dam_addrset_activate(mapp, &cfg);
+		(*mapp->dam_configure_cb)(mapp->dam_config_arg, mapp, &cfg);
+	}
+	if (has_uncfg) {
+		(*mapp->dam_unconfig_cb)(mapp->dam_config_arg, mapp, &uncfg);
+		dam_addrset_release(mapp, &uncfg);
+	}
+	DAM_LOCK(mapp, MAP_LOCK);
+	bitset_zero(&mapp->dam_stable_set);
+	DAM_FLAG_CLR(mapp, DAM_SPEND);
+	mapp->dam_last_stable = gethrtime();
+	mapp->dam_stable_cnt++;
+	DAM_INCR_STAT(mapp, dam_stable);
+	DAM_UNLOCK(mapp, MAP_LOCK);
+	bitset_fini(&uncfg);
+	bitset_fini(&cfg);
+	bitset_fini(&delta);
+	DTRACE_PROBE1(damap__activate__taskq__exit, dam_t, mapp);
+}
+
+/*
+ * per-address stabilization timeout
+ */
+static void
+dam_addr_stable_cb(void *arg)
+{
+	dam_t *mapp = (dam_t *)arg;
+	int i;
+	dam_da_t *passp;
+	int spend = 0;
+	int tpend = 0;
+	int64_t	next_tmov = mapp->dam_stabletmo;
+	int64_t tmo_delta;
+	int64_t ts = lbolt64;
+
+	DTRACE_PROBE1(damap__addr__stable__cb__entry, dam_t *, mapp);
+	DAM_LOCK(mapp, MAP_LOCK);
+	if (mapp->dam_tid == 0) {
+		DAM_UNLOCK(mapp, MAP_LOCK);
+		return;
+	}
+	mapp->dam_tid = 0;
+	/*
+	 * If still under stabilization, reschedule timeout,
+	 * else dispatch the task to activate & deactivate the stable
+	 * set.
+	 */
+	if (mapp->dam_flags & DAM_SPEND) {
+		DAM_INCR_STAT(mapp, dam_stable_blocked);
+		mapp->dam_stable_overrun++;
+		dam_sched_tmo(mapp, mapp->dam_stabletmo, dam_addr_stable_cb);
+		DAM_UNLOCK(mapp, MAP_LOCK);
+		DTRACE_PROBE1(damap__addr__stable__cb__overrun,
+		    dam_t *, mapp);
+		return;
+	}
+
+	bitset_copy(&mapp->dam_active_set, &mapp->dam_stable_set);
+	for (i = 1; i < mapp->dam_high; i++) {
+		if (!bitset_in_set(&mapp->dam_report_set, i))
+			continue;
+		/*
+		 * Stabilize each address
+		 */
+		passp = ddi_get_soft_state(mapp->dam_da, i);
+		ASSERT(passp);
+		if (!passp) {
+			cmn_err(CE_WARN, "Clearing report no softstate %d", i);
+			bitset_del(&mapp->dam_report_set, i);
+			continue;
+		}
+
+		/* report has stabilized */
+		if (passp->da_deadline <= ts) {
+			bitset_del(&mapp->dam_report_set, i);
+			if (passp->da_flags & DA_RELE) {
+				DTRACE_PROBE2(damap__addr__stable__del,
+				    dam_t *, mapp, int, i);
+				bitset_del(&mapp->dam_stable_set, i);
+			} else {
+				DTRACE_PROBE2(damap__addr__stable__add,
+				    dam_t *, mapp, int, i);
+				bitset_add(&mapp->dam_stable_set, i);
+			}
+			spend++;
+			continue;
+		}
+
+		/*
+		 * not stabilized, determine next (future) map timeout
+		 */
+		tpend++;
+		tmo_delta = passp->da_deadline - ts;
+		if (tmo_delta < next_tmov)
+			next_tmov = tmo_delta;
+	}
+
+	/*
+	 * schedule taskq activation of stabilized reports
+	 */
+	if (spend) {
+		if (ddi_taskq_dispatch(mapp->dam_taskqp, dam_activate_taskq,
+		    mapp, DDI_NOSLEEP) == DDI_SUCCESS) {
+			DAM_FLAG_SET(mapp, DAM_SPEND);
+		} else
+			tpend++;
+	}
+
+	/*
+	 * schedule timeout to handle future stabalization of active reports
+	 */
+	if (tpend)
+		dam_sched_tmo(mapp, (clock_t)next_tmov, dam_addr_stable_cb);
+	DAM_UNLOCK(mapp, MAP_LOCK);
+	DTRACE_PROBE1(damap__addr__stable__cb__exit, dam_t *, mapp);
+}
+
+/*
+ * fullset stabilization timeout
+ */
+static void
+dam_set_stable_cb(void *arg)
+{
+	dam_t *mapp = (dam_t *)arg;
+
+	DTRACE_PROBE1(damap__set__stable__cb__enter, dam_t *, mapp);
+
+	DAM_LOCK(mapp, MAP_LOCK);
+	if (mapp->dam_tid == 0) {
+		DAM_UNLOCK(mapp, MAP_LOCK);
+		return;
+	}
+	mapp->dam_tid = 0;
+
+	/*
+	 * If still under stabilization, reschedule timeout,
+	 * else dispatch the task to activate & deactivate the stable
+	 * set.
+	 */
+	if (mapp->dam_flags & DAM_SPEND) {
+		DAM_INCR_STAT(mapp, dam_stable_blocked);
+		mapp->dam_stable_overrun++;
+		dam_sched_tmo(mapp, mapp->dam_stabletmo, dam_set_stable_cb);
+		DTRACE_PROBE1(damap__set__stable__cb__overrun,
+		    dam_t *, mapp);
+	} else if (ddi_taskq_dispatch(mapp->dam_taskqp, dam_activate_taskq,
+	    mapp, DDI_NOSLEEP) == DDI_FAILURE) {
+		dam_sched_tmo(mapp, mapp->dam_stabletmo, dam_set_stable_cb);
+	} else {
+		bitset_copy(&mapp->dam_report_set, &mapp->dam_stable_set);
+		bitset_zero(&mapp->dam_report_set);
+		DAM_FLAG_CLR(mapp, DAM_SETADD);
+		DAM_FLAG_SET(mapp, DAM_SPEND);
+	}
+	DAM_UNLOCK(mapp, MAP_LOCK);
+	DTRACE_PROBE1(damap__set__stable__cb__exit, dam_t *, mapp);
+}
+
+/*
+ * reschedule map timeout 'tmo_ms' ticks
+ */
+static void
+dam_sched_tmo(dam_t *mapp, clock_t tmo_ms, void (*tmo_cb)())
+{
+	timeout_id_t tid;
+
+	if ((tid = mapp->dam_tid) != 0) {
+		mapp->dam_tid = 0;
+		DAM_UNLOCK(mapp, MAP_LOCK);
+		(void) untimeout(tid);
+		DAM_LOCK(mapp, MAP_LOCK);
+	}
+
+	if (tmo_cb && (tmo_ms != 0))
+		mapp->dam_tid = timeout(tmo_cb, mapp, tmo_ms);
+}
+
+/*
+ * record report addition or removal of an address
+ */
+static void
+dam_add_report(dam_t *mapp, dam_da_t *passp, id_t addrid, int report)
+{
+	ASSERT(!DAM_IN_REPORT(mapp, addrid));
+	passp->da_last_report = gethrtime();
+	mapp->dam_last_update = gethrtime();
+	passp->da_report_cnt++;
+	passp->da_deadline = lbolt64 + mapp->dam_stabletmo;
+	if (report == RPT_ADDR_DEL)
+		passp->da_flags |= DA_RELE;
+	else if (report == RPT_ADDR_ADD)
+		passp->da_flags &= ~DA_RELE;
+	DAM_LOCK(mapp, MAP_LOCK);
+	bitset_add(&mapp->dam_report_set, addrid);
+	dam_sched_tmo(mapp, mapp->dam_stabletmo, dam_addr_stable_cb);
+	DAM_UNLOCK(mapp, MAP_LOCK);
+
+}
+
+/*
+ * release an address report
+ */
+static void
+dam_release_report(dam_t *mapp, id_t addrid)
+{
+	dam_da_t *passp;
+
+	passp = ddi_get_soft_state(mapp->dam_da, addrid);
+	ASSERT(passp);
+	passp->da_ppriv_rpt = NULL;
+	if (passp->da_nvl_rpt)
+		nvlist_free(passp->da_nvl_rpt);
+	passp->da_nvl_rpt = NULL;
+	DAM_LOCK(mapp, MAP_LOCK);
+	bitset_del(&mapp->dam_report_set, addrid);
+	DAM_UNLOCK(mapp, MAP_LOCK);
+}
+
+/*
+ * deactivate a previously stable address
+ */
+static void
+dam_deactivate_addr(dam_t *mapp, id_t addrid)
+{
+	dam_da_t *passp;
+
+	passp = ddi_get_soft_state(mapp->dam_da, addrid);
+	ASSERT(passp);
+	if (passp == NULL)
+		return;
+	DAM_UNLOCK(mapp, ADDR_LOCK);
+	if (mapp->dam_deactivate_cb)
+		(*mapp->dam_deactivate_cb)(
+		    mapp->dam_activate_arg,
+		    ddi_strid_id2str(mapp->dam_addr_hash,
+		    addrid), addrid, passp->da_ppriv);
+	DAM_LOCK(mapp, ADDR_LOCK);
+	passp->da_ppriv = NULL;
+	if (passp->da_nvl)
+		nvlist_free(passp->da_nvl);
+	passp->da_nvl = NULL;
+}
+
+/*
+ * return the map ID of an address
+ */
+static id_t
+dam_get_addrid(dam_t *mapp, char *address)
+{
+	damap_id_t addrid;
+	dam_da_t *passp;
+
+	if ((addrid = ddi_strid_str2id(mapp->dam_addr_hash, address)) == 0) {
+		if ((addrid = ddi_strid_fixed_alloc(mapp->dam_addr_hash,
+		    address)) == (damap_id_t)0) {
+			return (0);
+		}
+		if (ddi_soft_state_zalloc(mapp->dam_da, addrid) !=
+		    DDI_SUCCESS) {
+			ddi_strid_free(mapp->dam_addr_hash, addrid);
+			return (0);
+		}
+		if (addrid >= mapp->dam_high)
+			mapp->dam_high = addrid + 1;
+	}
+	passp = ddi_get_soft_state(mapp->dam_da, addrid);
+	if (passp == NULL)
+		return (0);
+	passp->da_ref++;
+	if (passp->da_addr == NULL)
+		passp->da_addr = ddi_strid_id2str(
+		    mapp->dam_addr_hash, addrid); /* for mdb */
+	return (addrid);
+}
+
+/*
+ * create and install map statistics
+ */
+static int
+dam_kstat_create(dam_t *mapp)
+{
+	kstat_t			*mapsp;
+	struct dam_kstats	*statsp;
+
+	mapsp = kstat_create("dam", 0, mapp->dam_name, "damap",
+	    KSTAT_TYPE_NAMED,
+	    sizeof (struct dam_kstats) / sizeof (kstat_named_t), 0);
+	if (mapsp == NULL) {
+		return (DDI_FAILURE);
+	}
+
+	statsp = (struct dam_kstats *)mapsp->ks_data;
+	kstat_named_init(&statsp->dam_stable, "stable cycles",
+	    KSTAT_DATA_UINT32);
+	kstat_named_init(&statsp->dam_stable_blocked,
+	    "stable cycle overrun", KSTAT_DATA_UINT32);
+	kstat_named_init(&statsp->dam_rereport,
+	    "restarted reports", KSTAT_DATA_UINT32);
+	kstat_named_init(&statsp->dam_numstable,
+	    "# of stable map entries", KSTAT_DATA_UINT32);
+	kstat_install(mapsp);
+	mapp->dam_kstatsp = mapsp;
+	return (DDI_SUCCESS);
+}
+
+/*
+ * destroy map stats
+ */
+static void
+dam_kstat_destroy(dam_t *mapp)
+{
+
+	kstat_delete(mapp->dam_kstatsp);
+}
--- a/usr/src/uts/common/os/devcfg.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/os/devcfg.c	Mon Oct 05 09:17:26 2009 -0400
@@ -179,8 +179,6 @@
 static void unlink_from_driver_list(dev_info_t *);
 static void add_to_dn_list(struct devnames *, dev_info_t *);
 static void remove_from_dn_list(struct devnames *, dev_info_t *);
-static dev_info_t *find_child_by_callback(dev_info_t *, char *, char *,
-    int (*)(dev_info_t *, char *, int));
 static dev_info_t *find_duplicate_child();
 static void add_global_props(dev_info_t *);
 static void remove_global_props(dev_info_t *);
@@ -277,11 +275,14 @@
 	 * DEVI_PSEUDO_NODEID		DDI_NC_PSEUDO		A
 	 * DEVI_SID_NODEID		DDI_NC_PSEUDO		A,P
 	 * DEVI_SID_HIDDEN_NODEID	DDI_NC_PSEUDO		A,P,H
+	 * DEVI_SID_HP_NODEID		DDI_NC_PSEUDO		A,P,h
+	 * DEVI_SID_HP_HIDDEN_NODEID	DDI_NC_PSEUDO		A,P,H,h
 	 * other			DDI_NC_PROM		P
 	 *
 	 * Where A = DDI_AUTO_ASSIGNED_NODEID (auto-assign a nodeid)
 	 * and	 P = DDI_PERSISTENT
 	 * and	 H = DDI_HIDDEN_NODE
+	 * and	 h = DDI_HOTPLUG_NODE
 	 *
 	 * auto-assigned nodeids are also auto-freed.
 	 */
@@ -289,12 +290,23 @@
 	switch (nodeid) {
 	case DEVI_SID_HIDDEN_NODEID:
 		devi->devi_node_attributes |= DDI_HIDDEN_NODE;
-		/*FALLTHROUGH*/
+		goto sid;
+
+	case DEVI_SID_HP_NODEID:
+		devi->devi_node_attributes |= DDI_HOTPLUG_NODE;
+		goto sid;
+
+	case DEVI_SID_HP_HIDDEN_NODEID:
+		devi->devi_node_attributes |= DDI_HIDDEN_NODE;
+		devi->devi_node_attributes |= DDI_HOTPLUG_NODE;
+		goto sid;
+
 	case DEVI_SID_NODEID:
-		devi->devi_node_attributes |= DDI_PERSISTENT;
+sid:		devi->devi_node_attributes |= DDI_PERSISTENT;
 		if ((elem = kmem_zalloc(sizeof (*elem), flag)) == NULL)
 			goto fail;
 		/*FALLTHROUGH*/
+
 	case DEVI_PSEUDO_NODEID:
 		devi->devi_node_attributes |= DDI_AUTO_ASSIGNED_NODEID;
 		devi->devi_node_class = DDI_NC_PSEUDO;
@@ -303,9 +315,11 @@
 			/*NOTREACHED*/
 		}
 		break;
+
 	default:
 		if ((elem = kmem_zalloc(sizeof (*elem), flag)) == NULL)
 			goto fail;
+
 		/*
 		 * the nodetype is 'prom', try to 'take' the nodeid now.
 		 * This requires memory allocation, so check for failure.
@@ -318,7 +332,7 @@
 		devi->devi_nodeid = nodeid;
 		devi->devi_node_class = DDI_NC_PROM;
 		devi->devi_node_attributes = DDI_PERSISTENT;
-
+		break;
 	}
 
 	if (ndi_dev_is_persistent_node((dev_info_t *)devi)) {
@@ -2142,7 +2156,9 @@
 	/*
 	 * Walk the child list to find a match
 	 */
-
+	if (head == NULL)
+		return (NULL);
+	ASSERT(DEVI_BUSY_OWNED(ddi_get_parent(head)));
 	for (dip = head; dip; dip = ddi_get_next_sibling(dip)) {
 		if (by == FIND_NODE_BY_NODENAME) {
 			/* match node name */
@@ -2210,12 +2226,15 @@
  * Find a child of a given name and address, using a callback to name
  * unnamed children. cname is the binding name.
  */
-static dev_info_t *
-find_child_by_callback(dev_info_t *pdip, char *cname, char *caddr,
-    int (*name_node)(dev_info_t *, char *, int))
-{
-	return (find_sibling(ddi_get_child(pdip), cname, caddr,
-	    FIND_NODE_BY_DRIVER|FIND_ADDR_BY_CALLBACK, name_node));
+dev_info_t *
+ndi_devi_findchild_by_callback(dev_info_t *pdip, char *dname, char *ua,
+    int (*make_ua)(dev_info_t *, char *, int))
+{
+	int	by = FIND_ADDR_BY_CALLBACK;
+
+	ASSERT(DEVI_BUSY_OWNED(pdip));
+	by |= dname ? FIND_NODE_BY_DRIVER : FIND_NODE_BY_ADDR;
+	return (find_sibling(ddi_get_child(pdip), dname, ua, by, make_ua));
 }
 
 /*
@@ -2531,15 +2550,15 @@
  * It returns DDI_SUCCESS if the node is merged and DDI_FAILURE otherwise.
  */
 int
-ndi_merge_node(dev_info_t *dip, int (*name_node)(dev_info_t *, char *, int))
+ndi_merge_node(dev_info_t *dip, int (*make_ua)(dev_info_t *, char *, int))
 {
 	dev_info_t *hwdip;
 
 	ASSERT(ndi_dev_is_persistent_node(dip) == 0);
 	ASSERT(ddi_get_name_addr(dip) != NULL);
 
-	hwdip = find_child_by_callback(ddi_get_parent(dip),
-	    ddi_binding_name(dip), ddi_get_name_addr(dip), name_node);
+	hwdip = ndi_devi_findchild_by_callback(ddi_get_parent(dip),
+	    ddi_binding_name(dip), ddi_get_name_addr(dip), make_ua);
 
 	/*
 	 * Look for the hardware node that is the target of the merge;
@@ -4506,11 +4525,17 @@
 	char *status;
 
 	if (!DEVI_NEED_REPORT(dip) ||
-	    (i_ddi_node_state(dip) < DS_INITIALIZED)) {
+	    (i_ddi_node_state(dip) < DS_INITIALIZED) ||
+	    ndi_dev_is_hidden_node(dip)) {
 		return;
 	}
 
-	if (DEVI_IS_DEVICE_OFFLINE(dip)) {
+	/* Invalidate the devinfo snapshot cache */
+	i_ddi_di_cache_invalidate();
+
+	if (DEVI_IS_DEVICE_REMOVED(dip)) {
+		status = "removed";
+	} else if (DEVI_IS_DEVICE_OFFLINE(dip)) {
 		status = "offline";
 	} else if (DEVI_IS_DEVICE_DOWN(dip)) {
 		status = "down";
@@ -4547,26 +4572,25 @@
 static int
 i_log_devfs_add_devinfo(dev_info_t *dip, uint_t flags)
 {
-	int se_err;
-	char *pathname;
-	sysevent_t *ev;
-	sysevent_id_t eid;
-	sysevent_value_t se_val;
-	sysevent_attr_list_t *ev_attr_list = NULL;
-	char *class_name;
-	int no_transport = 0;
-
-	ASSERT(dip);
-
-	/*
-	 * Invalidate the devinfo snapshot cache
-	 */
-	i_ddi_di_cache_invalidate(KM_SLEEP);
+	int			se_err;
+	char			*pathname;
+	sysevent_t		*ev;
+	sysevent_id_t		eid;
+	sysevent_value_t	se_val;
+	sysevent_attr_list_t	*ev_attr_list = NULL;
+	char			*class_name;
+	int			no_transport = 0;
+
+	ASSERT(dip && ddi_get_parent(dip) &&
+	    DEVI_BUSY_OWNED(ddi_get_parent(dip)));
 
 	/* do not generate ESC_DEVFS_DEVI_ADD event during boot */
 	if (!i_ddi_io_initialized())
 		return (DDI_SUCCESS);
 
+	/* Invalidate the devinfo snapshot cache */
+	i_ddi_di_cache_invalidate();
+
 	ev = sysevent_alloc(EC_DEVFS, ESC_DEVFS_DEVI_ADD, EP_DDI, SE_SLEEP);
 
 	pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
@@ -4649,18 +4673,19 @@
 i_log_devfs_remove_devinfo(char *pathname, char *class_name, char *driver_name,
     int instance, uint_t flags)
 {
-	sysevent_t *ev;
-	sysevent_id_t eid;
-	sysevent_value_t se_val;
-	sysevent_attr_list_t *ev_attr_list = NULL;
-	int se_err;
-	int no_transport = 0;
-
-	i_ddi_di_cache_invalidate(KM_SLEEP);
+	sysevent_t		*ev;
+	sysevent_id_t		eid;
+	sysevent_value_t	se_val;
+	sysevent_attr_list_t	*ev_attr_list = NULL;
+	int			se_err;
+	int			no_transport = 0;
 
 	if (!i_ddi_io_initialized())
 		return (DDI_SUCCESS);
 
+	/* Invalidate the devinfo snapshot cache */
+	i_ddi_di_cache_invalidate();
+
 	ev = sysevent_alloc(EC_DEVFS, ESC_DEVFS_DEVI_REMOVE, EP_DDI, SE_SLEEP);
 
 	se_val.value_type = SE_DATA_TYPE_STRING;
@@ -4739,6 +4764,37 @@
 	return (DDI_SUCCESS);
 }
 
+static void
+i_ddi_log_devfs_device_remove(dev_info_t *dip)
+{
+	char	*path;
+
+	ASSERT(dip && ddi_get_parent(dip) &&
+	    DEVI_BUSY_OWNED(ddi_get_parent(dip)));
+	ASSERT(DEVI_IS_DEVICE_REMOVED(dip));
+
+	ASSERT(i_ddi_node_state(dip) >= DS_INITIALIZED);
+	if (i_ddi_node_state(dip) < DS_INITIALIZED)
+		return;
+
+	path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+	(void) i_log_devfs_remove_devinfo(ddi_pathname(dip, path),
+	    i_ddi_devi_class(dip), (char *)ddi_driver_name(dip),
+	    ddi_get_instance(dip), 0);
+	kmem_free(path, MAXPATHLEN);
+}
+
+static void
+i_ddi_log_devfs_device_insert(dev_info_t *dip)
+{
+	ASSERT(dip && ddi_get_parent(dip) &&
+	    DEVI_BUSY_OWNED(ddi_get_parent(dip)));
+	ASSERT(!DEVI_IS_DEVICE_REMOVED(dip));
+
+	(void) i_log_devfs_add_devinfo(dip, 0);
+}
+
+
 /*
  * log an event that a dev_info branch has been configured or unconfigured.
  */
@@ -4756,6 +4812,9 @@
 	if (!i_ddi_io_initialized())
 		return (DDI_SUCCESS);
 
+	/* Invalidate the devinfo snapshot cache */
+	i_ddi_di_cache_invalidate();
+
 	ev = sysevent_alloc(EC_DEVFS, subclass, EP_DDI, SE_SLEEP);
 
 	se_val.value_type = SE_DATA_TYPE_STRING;
@@ -5461,7 +5520,6 @@
 	 * by the BUS_CONFIG_ONE.
 	 */
 	ASSERT(*dipp);
-
 	error = devi_config_common(*dipp, flags, DDI_MAJOR_T_NONE);
 
 	pm_post_config(dip, devnm);
@@ -6365,7 +6423,7 @@
 	}
 	ndi_devi_enter(pdip, &circ);
 
-	if (i_ddi_node_state(dip) == DS_READY) {
+	if (i_ddi_devi_attached(dip)) {
 		/*
 		 * If dip is in DS_READY state, there may be cached dv_nodes
 		 * referencing this dip, so we invoke devfs code path.
@@ -6380,11 +6438,21 @@
 			ndi_devi_exit(vdip, v_circ);
 
 		/*
-		 * If we own parent lock, this is part of a branch
-		 * operation. We skip the devfs_clean() step.
+		 * If we are explictly told to clean, then clean. If we own the
+		 * parent lock then this is part of a branch operation, and we
+		 * skip the devfs_clean() step.
+		 *
+		 * NOTE: A thread performing a devfs file system lookup/
+		 * bus_config can't call devfs_clean to unconfig without
+		 * causing rwlock problems in devfs. For ndi_devi_offline, this
+		 * means that the NDI_DEVFS_CLEAN flag is safe from ioctl code
+		 * or from an async hotplug thread, but is not safe from a
+		 * nexus driver's bus_config implementation.
 		 */
-		if (!DEVI_BUSY_OWNED(pdip))
+		if ((flags & NDI_DEVFS_CLEAN) ||
+		    (!DEVI_BUSY_OWNED(pdip)))
 			(void) devfs_clean(pdip, devname + 1, DV_CLEAN_FORCE);
+
 		kmem_free(devname, MAXNAMELEN + 1);
 
 		rval = devi_unconfig_branch(dip, NULL, flags|NDI_UNCONFIG,
@@ -6416,7 +6484,7 @@
 }
 
 /*
- * Find the child dev_info node of parent nexus 'p' whose name
+ * Find the child dev_info node of parent nexus 'p' whose unit address
  * matches "cname@caddr".  Recommend use of ndi_devi_findchild() instead.
  */
 dev_info_t *
@@ -6436,7 +6504,7 @@
 }
 
 /*
- * Find the child dev_info node of parent nexus 'p' whose name
+ * Find the child dev_info node of parent nexus 'p' whose unit address
  * matches devname "name@addr".  Permits caller to hold the parent.
  */
 dev_info_t *
@@ -6573,7 +6641,7 @@
  * Some callers expect to be able to perform a hold_devi() while in a context
  * where using ndi_devi_enter() to ensure the hold might cause deadlock (see
  * open-from-attach code in consconfig_dacf.c). Such special-case callers
- * must ensure that an ndi_devi_enter(parent)/ndi_devi_hold() from a safe
+ * must ensure that an ndi_devi_enter(parent)/ndi_hold_devi() from a safe
  * context is already active. The hold_devi() implementation must accommodate
  * these callers.
  */
@@ -6601,7 +6669,7 @@
 		if (DEVI(dip)->devi_instance == instance) {
 			/*
 			 * To accommodate callers that can't block in
-			 * ndi_devi_enter() we do an ndi_devi_hold(), and
+			 * ndi_devi_enter() we do an ndi_hold_devi(), and
 			 * afterwards check that the node is in a state where
 			 * the hold prevents detach(). If we did not manage to
 			 * prevent detach then we ndi_rele_devi() and perform
@@ -7713,7 +7781,7 @@
 }
 
 void
-i_ddi_di_cache_invalidate(int kmflag)
+i_ddi_di_cache_invalidate()
 {
 	int	cache_valid;
 
@@ -7729,13 +7797,18 @@
 	/* Invalidate the in-core cache and dispatch free on valid->invalid */
 	cache_valid = atomic_swap_uint(&di_cache.cache_valid, 0);
 	if (cache_valid) {
+		/*
+		 * This is an optimization to start cleaning up a cached
+		 * snapshot early.  For this reason, it is OK for
+		 * taskq_dispatach to fail (and it is OK to not track calling
+		 * context relative to sleep, and assume NOSLEEP).
+		 */
 		(void) taskq_dispatch(system_taskq, free_cache_task, NULL,
-		    (kmflag == KM_SLEEP) ? TQ_SLEEP : TQ_NOSLEEP);
+		    TQ_NOSLEEP);
 	}
 
 	if (di_cache_debug) {
-		cmn_err(CE_NOTE, "invalidation with km_flag: %s",
-		    kmflag == KM_SLEEP ? "KM_SLEEP" : "KM_NOSLEEP");
+		cmn_err(CE_NOTE, "invalidation");
 	}
 }
 
@@ -7865,6 +7938,100 @@
 }
 
 /*
+ * Maintain DEVI_DEVICE_REMOVED hotplug devi_state for remove/reinsert hotplug
+ * of open devices. Currently, because of tight coupling between the devfs file
+ * system and the Solaris device tree, a driver can't always make the device
+ * tree state (esp devi_node_state) match device hardware hotplug state. Until
+ * resolved, to overcome this deficiency we use the following interfaces that
+ * maintain the DEVI_DEVICE_REMOVED devi_state status bit.  These interface
+ * report current state, and drive operation (like events and cache
+ * invalidation) when a driver changes remove/insert state of an open device.
+ *
+ * The ndi_devi_device_isremoved() returns 1 if the device is currently removed.
+ *
+ * The ndi_devi_device_remove() interface declares the device as removed, and
+ * returns 1 if there was a state change associated with this declaration.
+ *
+ * The ndi_devi_device_insert() declares the device as inserted, and returns 1
+ * if there was a state change associated with this declaration.
+ */
+int
+ndi_devi_device_isremoved(dev_info_t *dip)
+{
+	return (DEVI_IS_DEVICE_REMOVED(dip));
+}
+
+int
+ndi_devi_device_remove(dev_info_t *dip)
+{
+	ASSERT(dip && ddi_get_parent(dip) &&
+	    DEVI_BUSY_OWNED(ddi_get_parent(dip)));
+
+	/* Return if already marked removed. */
+	if (ndi_devi_device_isremoved(dip))
+		return (0);
+
+	/* Mark the device as having been physically removed. */
+	mutex_enter(&(DEVI(dip)->devi_lock));
+	ndi_devi_set_hidden(dip);	/* invisible: lookup/snapshot */
+	DEVI_SET_DEVICE_REMOVED(dip);
+	DEVI_SET_EVREMOVE(dip);		/* this clears EVADD too */
+	mutex_exit(&(DEVI(dip)->devi_lock));
+
+	/* report remove (as 'removed') */
+	i_ndi_devi_report_status_change(dip, NULL);
+
+	/*
+	 * Invalidate the cache to ensure accurate
+	 * (di_state() & DI_DEVICE_REMOVED).
+	 */
+	i_ddi_di_cache_invalidate();
+
+	/*
+	 * Generate sysevent for those interested in removal (either directly
+	 * via EC_DEVFS or indirectly via devfsadmd generated EC_DEV).
+	 */
+	i_ddi_log_devfs_device_remove(dip);
+
+	return (1);		/* DEVICE_REMOVED state changed */
+}
+
+int
+ndi_devi_device_insert(dev_info_t *dip)
+{
+	ASSERT(dip && ddi_get_parent(dip) &&
+	    DEVI_BUSY_OWNED(ddi_get_parent(dip)));
+
+	/* Return if not marked removed. */
+	if (!ndi_devi_device_isremoved(dip))
+		return (0);
+
+	/* Mark the device as having been physically reinserted. */
+	mutex_enter(&(DEVI(dip)->devi_lock));
+	ndi_devi_clr_hidden(dip);	/* visible: lookup/snapshot */
+	DEVI_SET_DEVICE_REINSERTED(dip);
+	DEVI_SET_EVADD(dip);		/* this clears EVREMOVE too */
+	mutex_exit(&(DEVI(dip)->devi_lock));
+
+	/* report insert (as 'online') */
+	i_ndi_devi_report_status_change(dip, NULL);
+
+	/*
+	 * Invalidate the cache to ensure accurate
+	 * (di_state() & DI_DEVICE_REMOVED).
+	 */
+	i_ddi_di_cache_invalidate();
+
+	/*
+	 * Generate sysevent for those interested in removal (either directly
+	 * via EC_DEVFS or indirectly via devfsadmd generated EC_DEV).
+	 */
+	i_ddi_log_devfs_device_insert(dip);
+
+	return (1);		/* DEVICE_REMOVED state changed */
+}
+
+/*
  * ibt_hw_is_present() returns 0 when there is no IB hardware actively
  * running.  This is primarily useful for modules like rpcmod which
  * needs a quick check to decide whether or not it should try to use
--- a/usr/src/uts/common/os/id_space.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/os/id_space.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <sys/id_space.h>
 #include <sys/debug.h>
@@ -39,17 +36,19 @@
  *   unless there are no larger slots remaining in the range.  In this case,
  *   the ID space will return the first available slot in the lower part of the
  *   range (viewing the previous identifier as a partitioning element).  If no
- *   slots are available, id_alloc() will sleep until an identifier becomes
- *   available.  Accordingly, id_space allocations must be initiated from
- *   contexts where sleeping is acceptable.  id_alloc_nosleep() will return
- *   -1 if no slots are available or if the system is low on memory.  If
- *   id_alloc_nosleep() fails, callers should not try to extend the ID
- *   space.  This is to avoid making a possible low-memory situation
- *   worse.
+ *   slots are available, id_alloc()/id_allocff() will sleep until an
+ *   identifier becomes available.  Accordingly, id_space allocations must be
+ *   initiated from contexts where sleeping is acceptable.  id_alloc_nosleep()/
+ *   id_allocff_nosleep() will return -1 if no slots are available or if the
+ *   system is low on memory.  If id_alloc_nosleep() fails, callers should
+ *   not try to extend the ID space.  This is to avoid making a possible
+ *   low-memory situation worse.
  *
  *   As an ID space is designed for representing a range of id_t's, there
- *   is a preexisting maximal range: [0, MAXUID].  ID space requests
- *   outside that range will fail on a DEBUG kernel.
+ *   is a preexisting maximal range: [0, MAXUID].  ID space requests outside
+ *   that range will fail on a DEBUG kernel.  The id_allocff*() functions
+ *   return the first available id, and should be used when there is benifit
+ *   to having a compact allocated range.
  *
  *   (Presently, the id_space_t abstraction supports only direct allocations; ID
  *   reservation, in which an ID is allocated but placed in a internal
@@ -112,6 +111,29 @@
 }
 
 /*
+ * Allocate an id_t from specified ID space using FIRSTFIT.
+ * Caller must be in a context in which VM_SLEEP is legal.
+ */
+id_t
+id_allocff(id_space_t *isp)
+{
+	return ((id_t)(uintptr_t)
+	    vmem_alloc(isp, 1, VM_SLEEP | VM_FIRSTFIT) - 1);
+}
+
+/*
+ * Allocate an id_t from specified ID space using FIRSTFIT
+ * Returns -1 on failure (see module block comments for more information on
+ * failure modes).
+ */
+id_t
+id_allocff_nosleep(id_space_t *isp)
+{
+	return ((id_t)(uintptr_t)
+	    vmem_alloc(isp, 1, VM_NOSLEEP | VM_FIRSTFIT) - 1);
+}
+
+/*
  * Free a previously allocated ID.
  * No restrictions on caller's context.
  */
--- a/usr/src/uts/common/os/lgrp.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/os/lgrp.c	Mon Oct 05 09:17:26 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -225,7 +225,13 @@
 static void	lgrp_part_add_cpu(struct cpu *, lgrp_id_t);
 static void	lgrp_part_del_cpu(struct cpu *);
 
+/*
+ * lgroup framework initialization
+ */
+static void	lgrp_main_init(void);
+static void	lgrp_main_mp_init(void);
 static void	lgrp_root_init(void);
+static void	lgrp_setup(void);
 
 /*
  * lpl topology
@@ -284,7 +290,7 @@
 }
 
 /*
- * Build full lgroup topology
+ * Setup root lgroup
  */
 static void
 lgrp_root_init(void)
@@ -352,28 +358,52 @@
 
 /*
  * Initialize the lgroup framework and allow the platform to do the same
+ *
+ * This happens in stages during boot and is all funnelled through this routine
+ * (see definition of lgrp_init_stages_t to see what happens at each stage and
+ * when)
  */
 void
-lgrp_init(void)
+lgrp_init(lgrp_init_stages_t stage)
 {
 	/*
 	 * Initialize the platform
 	 */
-	lgrp_plat_init();
-
-	/*
-	 * Set max number of lgroups supported on this platform which must be
-	 * less than the max number of lgroups supported by the common lgroup
-	 * framework (eg. NLGRPS_MAX is max elements in lgrp_table[], etc.)
-	 */
-	nlgrpsmax = lgrp_plat_max_lgrps();
-	ASSERT(nlgrpsmax <= NLGRPS_MAX);
+	lgrp_plat_init(stage);
+
+	switch (stage) {
+	case LGRP_INIT_STAGE1:
+		/*
+		 * Set max number of lgroups supported on this platform which
+		 * must be less than the max number of lgroups supported by the
+		 * common lgroup framework (eg. NLGRPS_MAX is max elements in
+		 * lgrp_table[], etc.)
+		 */
+		nlgrpsmax = lgrp_plat_max_lgrps();
+		ASSERT(nlgrpsmax <= NLGRPS_MAX);
+		break;
+
+	case LGRP_INIT_STAGE2:
+		lgrp_setup();
+		break;
+
+	case LGRP_INIT_STAGE4:
+		lgrp_main_init();
+		break;
+
+	case LGRP_INIT_STAGE5:
+		lgrp_main_mp_init();
+		break;
+
+	default:
+		break;
+	}
 }
 
 /*
  * Create the root and cpu0's lgroup, and set t0's home.
  */
-void
+static void
 lgrp_setup(void)
 {
 	/*
@@ -389,16 +419,6 @@
 }
 
 /*
- * Lgroup initialization is split in two parts. The first part
- * (lgrp_main_init()) is called right before start_other_cpus() in main. The
- * second part (lgrp_main_mp_init()) is called right after start_other_cpus()
- * when all CPUs are brought online and all distance information is available.
- *
- * When lgrp_main_init() is complete it sets lgrp_initialized. The
- * lgrp_main_mp_init() sets lgrp_topo_initialized.
- */
-
-/*
  * true when lgrp initialization has been completed.
  */
 int	lgrp_initialized = 0;
@@ -412,7 +432,7 @@
  * Init routine called after startup(), /etc/system has been processed,
  * and cpu0 has been added to an lgroup.
  */
-void
+static void
 lgrp_main_init(void)
 {
 	cpu_t		*cp = CPU;
@@ -488,7 +508,6 @@
 	lgrp_kstat_create(cp);
 	mutex_exit(&cpu_lock);
 
-	lgrp_plat_main_init();
 	lgrp_initialized = 1;
 }
 
@@ -496,7 +515,7 @@
  * Finish lgrp initialization after all CPUS are brought on-line.
  * This routine is called after start_other_cpus().
  */
-void
+static void
 lgrp_main_mp_init(void)
 {
 	klgrpset_t changed;
--- a/usr/src/uts/common/os/main.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/os/main.c	Mon Oct 05 09:17:26 2009 -0400
@@ -370,8 +370,6 @@
 	extern int	pm_adjust_timestamps(dev_info_t *, void *);
 	extern void	start_other_cpus(int);
 	extern void	sysevent_evc_thrinit();
-	extern void	lgrp_main_init(void);
-	extern void	lgrp_main_mp_init(void);
 #if defined(__x86)
 	extern void	fastboot_post_startup(void);
 #endif
@@ -388,9 +386,9 @@
 	ASSERT_STACK_ALIGNED();
 
 	/*
-	 * Setup the first lgroup, and home t0
+	 * Setup root lgroup and leaf lgroup for CPU 0
 	 */
-	lgrp_setup();
+	lgrp_init(LGRP_INIT_STAGE2);
 
 	/*
 	 * Once 'startup()' completes, the thread_reaper() daemon would be
@@ -419,8 +417,10 @@
 	/*
 	 * May need to probe to determine latencies from CPU 0 after
 	 * gethrtime() comes alive in cbe_init() and before enabling interrupts
+	 * and copy and release any temporary memory allocated with BOP_ALLOC()
+	 * before release_bootstrap() frees boot memory
 	 */
-	lgrp_plat_probe();
+	lgrp_init(LGRP_INIT_STAGE3);
 
 	/*
 	 * Call all system initialization functions.
@@ -533,11 +533,10 @@
 	sysevent_evc_thrinit();
 
 	/*
-	 * main lgroup initialization
-	 * This must be done after post_startup(), but before
+	 * This must be done after post_startup() but before
 	 * start_other_cpus()
 	 */
-	lgrp_main_init();
+	lgrp_init(LGRP_INIT_STAGE4);
 
 	/*
 	 * Perform MP initialization, if any.
@@ -555,7 +554,7 @@
 	/*
 	 * Finish lgrp initialization after all CPUS are brought online.
 	 */
-	lgrp_main_mp_init();
+	lgrp_init(LGRP_INIT_STAGE5);
 
 	/*
 	 * After mp_init(), number of cpus are known (this is
--- a/usr/src/uts/common/os/modctl.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/os/modctl.c	Mon Oct 05 09:17:26 2009 -0400
@@ -661,7 +661,7 @@
 	 * driver now exists.
 	 */
 	i_ddi_bind_devs();
-	i_ddi_di_cache_invalidate(KM_SLEEP);
+	i_ddi_di_cache_invalidate();
 
 error:
 	if (mc.num_aliases > 0) {
@@ -711,7 +711,7 @@
 	(void) i_ddi_unload_drvconf(major);
 	i_ddi_unbind_devs(major);
 	i_ddi_bind_devs();
-	i_ddi_di_cache_invalidate(KM_SLEEP);
+	i_ddi_di_cache_invalidate();
 
 	/* purge all the bindings to this driver */
 	purge_mbind(major, mb_hashtab);
--- a/usr/src/uts/common/os/sunddi.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/os/sunddi.c	Mon Oct 05 09:17:26 2009 -0400
@@ -83,6 +83,7 @@
 #include <net/if.h>
 #include <sys/rctl.h>
 #include <sys/zone.h>
+#include <sys/ddi.h>
 
 extern	pri_t	minclsyspri;
 
@@ -106,7 +107,6 @@
 static	struct	ddi_umem_cookie *ddi_umem_unlock_head = NULL;
 static	struct	ddi_umem_cookie *ddi_umem_unlock_tail = NULL;
 
-
 /*
  * DDI(Sun) Function and flag definitions:
  */
@@ -4607,7 +4607,7 @@
 {
 	/* for now we invalidate the entire cached snapshot */
 	if (dip && dp)
-		i_ddi_di_cache_invalidate(KM_SLEEP);
+		i_ddi_di_cache_invalidate();
 }
 
 /* ARGSUSED */
@@ -4615,7 +4615,7 @@
 ddi_prop_cache_invalidate(dev_t dev, dev_info_t *dip, char *name, int flags)
 {
 	/* for now we invalidate the entire cached snapshot */
-	i_ddi_di_cache_invalidate(KM_SLEEP);
+	i_ddi_di_cache_invalidate();
 }
 
 
@@ -5760,7 +5760,7 @@
 	se_flag = (servicing_interrupt()) ? SE_NOSLEEP : SE_SLEEP;
 	kmem_flag = (se_flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
 
-	i_ddi_di_cache_invalidate(kmem_flag);
+	i_ddi_di_cache_invalidate();
 
 #ifdef DEBUG
 	if ((se_flag == SE_NOSLEEP) && sunddi_debug) {
@@ -5867,7 +5867,7 @@
 		return (DDI_SUCCESS);
 	}
 
-	i_ddi_di_cache_invalidate(KM_SLEEP);
+	i_ddi_di_cache_invalidate();
 
 	ev = sysevent_alloc(EC_DEVFS, ESC_DEVFS_MINOR_REMOVE, EP_DDI, SE_SLEEP);
 	if (ev == NULL) {
@@ -6254,10 +6254,10 @@
 }
 
 /*
- * The next five routines comprise generic storage management utilities
- * for driver soft state structures (in "the old days," this was done
- * with a statically sized array - big systems and dynamic loading
- * and unloading make heap allocation more attractive)
+ * The ddi_soft_state_* routines comprise generic storage management utilities
+ * for driver soft state structures (in "the old days," this was done with
+ * statically sized array - big systems and dynamic loading and unloading
+ * make heap allocation more attractive).
  */
 
 /*
@@ -6286,9 +6286,9 @@
 int
 ddi_soft_state_init(void **state_p, size_t size, size_t n_items)
 {
-	struct i_ddi_soft_state *ss;
-
-	if (state_p == NULL || *state_p != NULL || size == 0)
+	i_ddi_soft_state	*ss;
+
+	if (state_p == NULL || size == 0)
 		return (EINVAL);
 
 	ss = kmem_zalloc(sizeof (*ss), KM_SLEEP);
@@ -6310,11 +6310,9 @@
 	ss->array = kmem_zalloc(ss->n_items * sizeof (void *), KM_SLEEP);
 
 	*state_p = ss;
-
 	return (0);
 }
 
-
 /*
  * Allocate a state structure of size 'size' to be associated
  * with item 'item'.
@@ -6325,11 +6323,11 @@
 int
 ddi_soft_state_zalloc(void *state, int item)
 {
-	struct i_ddi_soft_state *ss;
-	void **array;
-	void *new_element;
-
-	if ((ss = state) == NULL || item < 0)
+	i_ddi_soft_state	*ss = (i_ddi_soft_state *)state;
+	void			**array;
+	void			*new_element;
+
+	if ((state == NULL) || (item < 0))
 		return (DDI_FAILURE);
 
 	mutex_enter(&ss->lock);
@@ -6360,9 +6358,9 @@
 	 * Check if the array is big enough, if not, grow it.
 	 */
 	if (item >= ss->n_items) {
-		void	**new_array;
-		size_t	new_n_items;
-		struct i_ddi_soft_state *dirty;
+		void			**new_array;
+		size_t			new_n_items;
+		struct i_ddi_soft_state	*dirty;
 
 		/*
 		 * Allocate a new array of the right length, copy
@@ -6415,7 +6413,6 @@
 	return (DDI_SUCCESS);
 }
 
-
 /*
  * Fetch a pointer to the allocated soft state structure.
  *
@@ -6438,9 +6435,9 @@
 void *
 ddi_get_soft_state(void *state, int item)
 {
-	struct i_ddi_soft_state *ss = state;
-
-	ASSERT(ss != NULL && item >= 0);
+	i_ddi_soft_state	*ss = (i_ddi_soft_state *)state;
+
+	ASSERT((ss != NULL) && (item >= 0));
 
 	if (item < ss->n_items && ss->array != NULL)
 		return (ss->array[item]);
@@ -6460,12 +6457,12 @@
 void
 ddi_soft_state_free(void *state, int item)
 {
-	struct i_ddi_soft_state *ss;
-	void **array;
-	void *element;
-	static char msg[] = "ddi_soft_state_free:";
-
-	if ((ss = state) == NULL) {
+	i_ddi_soft_state	*ss = (i_ddi_soft_state *)state;
+	void			**array;
+	void			*element;
+	static char		msg[] = "ddi_soft_state_free:";
+
+	if (ss == NULL) {
 		cmn_err(CE_WARN, "%s null handle: %s",
 		    msg, mod_containing_pc(caller()));
 		return;
@@ -6492,7 +6489,6 @@
 		kmem_free(element, ss->size);
 }
 
-
 /*
  * Free the entire set of pointers, and any
  * soft state structures contained therein.
@@ -6509,11 +6505,12 @@
 void
 ddi_soft_state_fini(void **state_p)
 {
-	struct i_ddi_soft_state *ss, *dirty;
-	int item;
-	static char msg[] = "ddi_soft_state_fini:";
-
-	if (state_p == NULL || (ss = *state_p) == NULL) {
+	i_ddi_soft_state	*ss, *dirty;
+	int			item;
+	static char		msg[] = "ddi_soft_state_fini:";
+
+	if (state_p == NULL ||
+	    (ss = (i_ddi_soft_state *)(*state_p)) == NULL) {
 		cmn_err(CE_WARN, "%s null handle: %s",
 		    msg, mod_containing_pc(caller()));
 		return;
@@ -6546,6 +6543,287 @@
 	*state_p = NULL;
 }
 
+#define	SS_N_ITEMS_PER_HASH	16
+#define	SS_MIN_HASH_SZ		16
+#define	SS_MAX_HASH_SZ		4096
+
+int
+ddi_soft_state_bystr_init(ddi_soft_state_bystr **state_p, size_t size,
+    int n_items)
+{
+	i_ddi_soft_state_bystr	*sss;
+	int			hash_sz;
+
+	ASSERT(state_p && size && n_items);
+	if ((state_p == NULL) || (size == 0) || (n_items == 0))
+		return (EINVAL);
+
+	/* current implementation is based on hash, convert n_items to hash */
+	hash_sz = n_items / SS_N_ITEMS_PER_HASH;
+	if (hash_sz < SS_MIN_HASH_SZ)
+		hash_sz = SS_MIN_HASH_SZ;
+	else if (hash_sz > SS_MAX_HASH_SZ)
+		hash_sz = SS_MAX_HASH_SZ;
+
+	/* allocate soft_state pool */
+	sss = kmem_zalloc(sizeof (*sss), KM_SLEEP);
+	sss->ss_size = size;
+	sss->ss_mod_hash = mod_hash_create_strhash("soft_state_bystr",
+	    hash_sz, mod_hash_null_valdtor);
+	*state_p = (ddi_soft_state_bystr *)sss;
+	return (0);
+}
+
+int
+ddi_soft_state_bystr_zalloc(ddi_soft_state_bystr *state, const char *str)
+{
+	i_ddi_soft_state_bystr	*sss = (i_ddi_soft_state_bystr *)state;
+	void			*sso;
+	char			*dup_str;
+
+	ASSERT(sss && str && sss->ss_mod_hash);
+	if ((sss == NULL) || (str == NULL) || (sss->ss_mod_hash == NULL))
+		return (DDI_FAILURE);
+	sso = kmem_zalloc(sss->ss_size, KM_SLEEP);
+	dup_str = i_ddi_strdup((char *)str, KM_SLEEP);
+	if (mod_hash_insert(sss->ss_mod_hash,
+	    (mod_hash_key_t)dup_str, (mod_hash_val_t)sso) == 0)
+		return (DDI_SUCCESS);
+
+	/*
+	 * The only error from an strhash insert is caused by a duplicate key.
+	 * We refuse to tread on an existing elements, so free and fail.
+	 */
+	kmem_free(dup_str, strlen(dup_str) + 1);
+	kmem_free(sso, sss->ss_size);
+	return (DDI_FAILURE);
+}
+
+void *
+ddi_soft_state_bystr_get(ddi_soft_state_bystr *state, const char *str)
+{
+	i_ddi_soft_state_bystr	*sss = (i_ddi_soft_state_bystr *)state;
+	void			*sso;
+
+	ASSERT(sss && str && sss->ss_mod_hash);
+	if ((sss == NULL) || (str == NULL) || (sss->ss_mod_hash == NULL))
+		return (NULL);
+
+	if (mod_hash_find(sss->ss_mod_hash,
+	    (mod_hash_key_t)str, (mod_hash_val_t *)&sso) == 0)
+		return (sso);
+	return (NULL);
+}
+
+void
+ddi_soft_state_bystr_free(ddi_soft_state_bystr *state, const char *str)
+{
+	i_ddi_soft_state_bystr	*sss = (i_ddi_soft_state_bystr *)state;
+	void			*sso;
+
+	ASSERT(sss && str && sss->ss_mod_hash);
+	if ((sss == NULL) || (str == NULL) || (sss->ss_mod_hash == NULL))
+		return;
+
+	(void) mod_hash_remove(sss->ss_mod_hash,
+	    (mod_hash_key_t)str, (mod_hash_val_t *)&sso);
+	kmem_free(sso, sss->ss_size);
+}
+
+void
+ddi_soft_state_bystr_fini(ddi_soft_state_bystr **state_p)
+{
+	i_ddi_soft_state_bystr	*sss;
+
+	ASSERT(state_p);
+	if (state_p == NULL)
+		return;
+
+	sss = (i_ddi_soft_state_bystr *)(*state_p);
+	if (sss == NULL)
+		return;
+
+	ASSERT(sss->ss_mod_hash);
+	if (sss->ss_mod_hash) {
+		mod_hash_destroy_strhash(sss->ss_mod_hash);
+		sss->ss_mod_hash = NULL;
+	}
+
+	kmem_free(sss, sizeof (*sss));
+	*state_p = NULL;
+}
+
+/*
+ * The ddi_strid_* routines provide string-to-index management utilities.
+ */
+/* allocate and initialize an strid set */
+int
+ddi_strid_init(ddi_strid **strid_p, int n_items)
+{
+	i_ddi_strid	*ss;
+	int		hash_sz;
+
+	if (strid_p == NULL)
+		return (DDI_FAILURE);
+
+	/* current implementation is based on hash, convert n_items to hash */
+	hash_sz = n_items / SS_N_ITEMS_PER_HASH;
+	if (hash_sz < SS_MIN_HASH_SZ)
+		hash_sz = SS_MIN_HASH_SZ;
+	else if (hash_sz > SS_MAX_HASH_SZ)
+		hash_sz = SS_MAX_HASH_SZ;
+
+	ss = kmem_alloc(sizeof (*ss), KM_SLEEP);
+	ss->strid_space = id_space_create("strid", 1, n_items);
+	ss->strid_bystr = mod_hash_create_strhash("strid_bystr", hash_sz,
+	    mod_hash_null_valdtor);
+	ss->strid_byid = mod_hash_create_idhash("strid_byid", hash_sz,
+	    mod_hash_null_valdtor);
+	*strid_p = (ddi_strid *)ss;
+	return (DDI_SUCCESS);
+}
+
+#define	ID_FIXED_SIZE	0x1
+
+/* allocate an id mapping within the specified set for str, return id */
+static id_t
+i_ddi_strid_alloc(ddi_strid *strid, char *str, int flags)
+{
+	i_ddi_strid	*ss = (i_ddi_strid *)strid;
+	id_t		id;
+	char		*s;
+
+	ASSERT(ss && str);
+	if ((ss == NULL) || (str == NULL))
+		return (0);
+
+	/*
+	 * Allocate an id using VM_FIRSTFIT in order to keep allocated id
+	 * range as compressed as possible.  This is important to minimize
+	 * the amount of space used when the id is used as a ddi_soft_state
+	 * index by the caller.
+	 *
+	 * If ID_FIXED_SIZE, use the _nosleep variant to fail rather
+	 * than sleep in id_allocff()
+	 */
+	if (flags & ID_FIXED_SIZE) {
+		id = id_allocff_nosleep(ss->strid_space);
+		if (id == (id_t)-1)
+			return (0);
+	} else {
+		id = id_allocff(ss->strid_space);
+	}
+
+	/*
+	 * NOTE: since we create and destroy in unison we can save space by
+	 * using bystr key as the byid value.  This means destroy must occur
+	 * in (byid, bystr) order.
+	 */
+	s = i_ddi_strdup(str, KM_SLEEP);
+	if (mod_hash_insert(ss->strid_bystr, (mod_hash_key_t)s,
+	    (mod_hash_val_t)(intptr_t)id) != 0) {
+		ddi_strid_free(strid, id);
+		return (0);
+	}
+	if (mod_hash_insert(ss->strid_byid, (mod_hash_key_t)(intptr_t)id,
+	    (mod_hash_val_t)s) != 0) {
+		ddi_strid_free(strid, id);
+		return (0);
+	}
+
+	/* NOTE: s if freed on mod_hash_destroy by mod_hash_strval_dtor */
+	return (id);
+}
+
+/* allocate an id mapping within the specified set for str, return id */
+id_t
+ddi_strid_alloc(ddi_strid *strid, char *str)
+{
+	return (i_ddi_strid_alloc(strid, str, 0));
+}
+
+/* allocate an id mapping within the specified set for str, return id */
+id_t
+ddi_strid_fixed_alloc(ddi_strid *strid, char *str)
+{
+	return (i_ddi_strid_alloc(strid, str, ID_FIXED_SIZE));
+}
+
+/* return the id within the specified strid given the str */
+id_t
+ddi_strid_str2id(ddi_strid *strid, char *str)
+{
+	i_ddi_strid	*ss = (i_ddi_strid *)strid;
+	id_t		id = 0;
+	mod_hash_val_t	hv;
+
+	ASSERT(ss && str);
+	if (ss && str && (mod_hash_find(ss->strid_bystr,
+	    (mod_hash_key_t)str, &hv) == 0))
+		id = (int)(intptr_t)hv;
+	return (id);
+}
+
+/* return str within the specified strid given the id */
+char *
+ddi_strid_id2str(ddi_strid *strid, id_t id)
+{
+	i_ddi_strid	*ss = (i_ddi_strid *)strid;
+	char		*str = NULL;
+	mod_hash_val_t	hv;
+
+	ASSERT(ss && id > 0);
+	if (ss && (id > 0) && (mod_hash_find(ss->strid_byid,
+	    (mod_hash_key_t)(uintptr_t)id, &hv) == 0))
+		str = (char *)hv;
+	return (str);
+}
+
+/* free the id mapping within the specified strid */
+void
+ddi_strid_free(ddi_strid *strid, id_t id)
+{
+	i_ddi_strid	*ss = (i_ddi_strid *)strid;
+	char		*str;
+
+	ASSERT(ss && id > 0);
+	if ((ss == NULL) || (id <= 0))
+		return;
+
+	/* bystr key is byid value: destroy order must be (byid, bystr) */
+	str = ddi_strid_id2str(strid, id);
+	(void) mod_hash_destroy(ss->strid_byid, (mod_hash_key_t)(uintptr_t)id);
+	id_free(ss->strid_space, id);
+
+	if (str)
+		(void) mod_hash_destroy(ss->strid_bystr, (mod_hash_key_t)str);
+}
+
+/* destroy the strid set */
+void
+ddi_strid_fini(ddi_strid **strid_p)
+{
+	i_ddi_strid	*ss;
+
+	ASSERT(strid_p);
+	if (strid_p == NULL)
+		return;
+
+	ss = (i_ddi_strid *)(*strid_p);
+	if (ss == NULL)
+		return;
+
+	/* bystr key is byid value: destroy order must be (byid, bystr) */
+	if (ss->strid_byid)
+		mod_hash_destroy_hash(ss->strid_byid);
+	if (ss->strid_byid)
+		mod_hash_destroy_hash(ss->strid_bystr);
+	if (ss->strid_space)
+		id_space_destroy(ss->strid_space);
+	kmem_free(ss, sizeof (*ss));
+	*strid_p = NULL;
+}
+
 /*
  * This sets the devi_addr entry in the dev_info structure 'dip' to 'name'.
  * Storage is double buffered to prevent updates during devi_addr use -
--- a/usr/src/uts/common/os/sunmdi.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/os/sunmdi.c	Mon Oct 05 09:17:26 2009 -0400
@@ -73,12 +73,15 @@
 #include <sys/debug.h>
 int	mdi_debug = 1;
 int	mdi_debug_logonly = 0;
-#define	MDI_DEBUG(level, stmnt) \
-	    if (mdi_debug >= (level)) i_mdi_log stmnt
-static void i_mdi_log(int, dev_info_t *, const char *fmt, ...);
+#define	MDI_DEBUG(dbglevel, pargs) if (mdi_debug >= (dbglevel))	i_mdi_log pargs
+#define	MDI_WARN	CE_WARN, __func__
+#define	MDI_NOTE	CE_NOTE, __func__
+#define	MDI_CONT	CE_CONT, __func__
+static void i_mdi_log(int, const char *, dev_info_t *, const char *, ...);
 #else	/* !DEBUG */
-#define	MDI_DEBUG(level, stmnt)
+#define	MDI_DEBUG(dbglevel, pargs)
 #endif	/* DEBUG */
+int	mdi_debug_consoleonly = 0;
 
 extern pri_t	minclsyspri;
 extern int	modrootloaded;
@@ -167,6 +170,7 @@
 static kmutex_t		mdi_pathmap_mutex;
 static mod_hash_t	*mdi_pathmap_bypath;		/* "path"->instance */
 static mod_hash_t	*mdi_pathmap_byinstance;	/* instance->"path" */
+static mod_hash_t	*mdi_pathmap_sbyinstance;	/* inst->shortpath */
 
 /*
  * MDI component property name/value string definitions
@@ -333,6 +337,9 @@
 	mdi_pathmap_byinstance = mod_hash_create_idhash(
 	    "mdi_pathmap_byinstance", mdi_pathmap_hash_size,
 	    mod_hash_null_valdtor);
+	mdi_pathmap_sbyinstance = mod_hash_create_idhash(
+	    "mdi_pathmap_sbyinstance", mdi_pathmap_hash_size,
+	    mod_hash_null_valdtor);
 }
 
 /*
@@ -618,7 +625,6 @@
 	mdi_phci_t		*ph;
 	mdi_vhci_t		*vh;
 	char			*data;
-	char			*pathname;
 
 	/*
 	 * Some subsystems, like fcp, perform pHCI registration from a
@@ -630,9 +636,6 @@
 	 */
 	ASSERT(DEVI_BUSY_CHANGING(ddi_get_parent(pdip)));
 
-	pathname = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
-	(void) ddi_pathname(pdip, pathname);
-
 	/*
 	 * Check for mpxio-disable property. Enable mpxio if the property is
 	 * missing or not set to "yes".
@@ -641,20 +644,15 @@
 	if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, 0, "mpxio-disable",
 	    &data) == DDI_SUCCESS)) {
 		if (strcmp(data, "yes") == 0) {
-			MDI_DEBUG(1, (CE_CONT, pdip,
-			    "?%s (%s%d) multipath capabilities "
-			    "disabled via %s.conf.\n", pathname,
-			    ddi_driver_name(pdip), ddi_get_instance(pdip),
+			MDI_DEBUG(1, (MDI_CONT, pdip,
+			    "?multipath capabilities disabled via %s.conf.",
 			    ddi_driver_name(pdip)));
 			ddi_prop_free(data);
-			kmem_free(pathname, MAXPATHLEN);
 			return (MDI_FAILURE);
 		}
 		ddi_prop_free(data);
 	}
 
-	kmem_free(pathname, MAXPATHLEN);
-
 	/*
 	 * Search for a matching vHCI
 	 */
@@ -713,21 +711,20 @@
 	mdi_phci_t		*ph;
 	mdi_phci_t		*tmp;
 	mdi_phci_t		*prev = NULL;
+	mdi_pathinfo_t		*pip;
 
 	ASSERT(DEVI_BUSY_CHANGING(ddi_get_parent(pdip)));
 
 	ph = i_devi_get_phci(pdip);
 	if (ph == NULL) {
-		MDI_DEBUG(1, (CE_WARN, pdip,
-		    "!pHCI unregister: Not a valid pHCI"));
+		MDI_DEBUG(1, (MDI_WARN, pdip, "!not a valid pHCI"));
 		return (MDI_FAILURE);
 	}
 
 	vh = ph->ph_vhci;
 	ASSERT(vh != NULL);
 	if (vh == NULL) {
-		MDI_DEBUG(1, (CE_WARN, pdip,
-		    "!pHCI unregister: Not a valid vHCI"));
+		MDI_DEBUG(1, (MDI_WARN, pdip, "!not a valid vHCI"));
 		return (MDI_FAILURE);
 	}
 
@@ -754,6 +751,13 @@
 	vh->vh_phci_count--;
 	MDI_VHCI_PHCI_UNLOCK(vh);
 
+	/* Walk remaining pathinfo nodes and disassociate them from pHCI */
+	MDI_PHCI_LOCK(ph);
+	for (pip = (mdi_pathinfo_t *)ph->ph_path_head; pip;
+	    pip = (mdi_pathinfo_t *)MDI_PI(pip)->pi_phci_link)
+		MDI_PI(pip)->pi_phci = NULL;
+	MDI_PHCI_UNLOCK(ph);
+
 	i_mdi_log_sysevent(pdip, ph->ph_vhci->vh_class,
 	    ESC_DDI_INITIATOR_UNREGISTER);
 	vhcache_phci_remove(vh->vh_config, ph);
@@ -823,8 +827,15 @@
 		} else if (DEVI_IS_DETACHING(phci_dip)) {
 			vcircular = -1;
 			break;
+		} else if (servicing_interrupt()) {
+			/*
+			 * Don't delay an interrupt (and ensure adaptive
+			 * mutex inversion support).
+			 */
+			ndi_devi_enter(vdip, &vcircular);
+			break;
 		} else {
-			delay(1);
+			delay_random(2);
 		}
 	}
 
@@ -896,6 +907,9 @@
 	/* Verify calling context */
 	ASSERT(MDI_PHCI(phci_dip));
 
+	/* Keep hold on pHCI until we reenter in mdi_devi_enter_phci */
+	ndi_hold_devi(phci_dip);
+
 	pcircular = (short)(circular & 0xFFFF);
 	ndi_devi_exit(phci_dip, pcircular);
 }
@@ -910,6 +924,9 @@
 
 	ndi_devi_enter(phci_dip, &pcircular);
 
+	/* Drop hold from mdi_devi_exit_phci. */
+	ndi_rele_devi(phci_dip);
+
 	/* verify matching mdi_devi_exit_phci/mdi_devi_enter_phci use */
 	ASSERT(pcircular == ((short)(*circular & 0xFFFF)));
 }
@@ -1037,15 +1054,24 @@
 	if (pip) {
 		/* Reverse locking is requested. */
 		while (MDI_PHCI_TRYLOCK(ph) == 0) {
-			/*
-			 * tryenter failed. Try to grab again
-			 * after a small delay
-			 */
-			MDI_PI_HOLD(pip);
-			MDI_PI_UNLOCK(pip);
-			delay(1);
-			MDI_PI_LOCK(pip);
-			MDI_PI_RELE(pip);
+			if (servicing_interrupt()) {
+				MDI_PI_HOLD(pip);
+				MDI_PI_UNLOCK(pip);
+				MDI_PHCI_LOCK(ph);
+				MDI_PI_LOCK(pip);
+				MDI_PI_RELE(pip);
+				break;
+			} else {
+				/*
+				 * tryenter failed. Try to grab again
+				 * after a small delay
+				 */
+				MDI_PI_HOLD(pip);
+				MDI_PI_UNLOCK(pip);
+				delay_random(2);
+				MDI_PI_LOCK(pip);
+				MDI_PI_RELE(pip);
+			}
 		}
 	} else {
 		MDI_PHCI_LOCK(ph);
@@ -1083,8 +1109,8 @@
 	ASSERT(cdip == NULL);
 	if (cdip) {
 		cmn_err(CE_WARN,
-		    "i_mdi_devinfo_create: client dip %p already exists",
-			(void *)cdip);
+		    "i_mdi_devinfo_create: client %s@%s already exists",
+			name ? name : "", guid ? guid : "");
 	}
 
 	ndi_devi_alloc_sleep(vh->vh_dip, name, DEVI_SID_NODEID, &cdip);
@@ -1169,10 +1195,10 @@
 
 	if (i_mdi_is_child_present(vdip, cdip) == MDI_SUCCESS ||
 	    (flags & MDI_CLIENT_FLAGS_DEV_NOT_SUPPORTED)) {
-		rv = ndi_devi_offline(cdip, NDI_DEVI_REMOVE);
+		rv = ndi_devi_offline(cdip, NDI_DEVFS_CLEAN | NDI_DEVI_REMOVE);
 		if (rv != NDI_SUCCESS) {
-			MDI_DEBUG(1, (CE_NOTE, NULL, "!i_mdi_devinfo_remove:"
-			    " failed. cdip = %p\n", (void *)cdip));
+			MDI_DEBUG(1, (MDI_NOTE, cdip,
+			    "!failed: cdip %p", (void *)cdip));
 		}
 		/*
 		 * Convert to MDI error code
@@ -1252,15 +1278,24 @@
 		 * Reverse locking is requested.
 		 */
 		while (MDI_CLIENT_TRYLOCK(ct) == 0) {
-			/*
-			 * tryenter failed. Try to grab again
-			 * after a small delay
-			 */
-			MDI_PI_HOLD(pip);
-			MDI_PI_UNLOCK(pip);
-			delay(1);
-			MDI_PI_LOCK(pip);
-			MDI_PI_RELE(pip);
+			if (servicing_interrupt()) {
+				MDI_PI_HOLD(pip);
+				MDI_PI_UNLOCK(pip);
+				MDI_CLIENT_LOCK(ct);
+				MDI_PI_LOCK(pip);
+				MDI_PI_RELE(pip);
+				break;
+			} else {
+				/*
+				 * tryenter failed. Try to grab again
+				 * after a small delay
+				 */
+				MDI_PI_HOLD(pip);
+				MDI_PI_UNLOCK(pip);
+				delay_random(2);
+				MDI_PI_LOCK(pip);
+				MDI_PI_RELE(pip);
+			}
 		}
 	} else {
 		MDI_CLIENT_LOCK(ct);
@@ -1543,8 +1578,8 @@
 	if (online_count == 0) {
 		if (standby_count == 0) {
 			state = MDI_CLIENT_STATE_FAILED;
-			MDI_DEBUG(2, (CE_NOTE, NULL, "!client state: failed"
-			    " ct = %p\n", (void *)ct));
+			MDI_DEBUG(2, (MDI_NOTE, ct->ct_dip,
+			    "client state failed: ct = %p", (void *)ct));
 		} else if (standby_count == 1) {
 			state = MDI_CLIENT_STATE_DEGRADED;
 		} else {
@@ -1937,15 +1972,9 @@
 			MDI_PI_UNLOCK(pip);
 			pip = next;
 		}
-		if (pip == NULL) {
-			MDI_DEBUG(4, (CE_NOTE, NULL,
-			    "!lba %llx, no pip !!\n",
-				bp->b_lblkno));
-		} else {
-			MDI_DEBUG(4, (CE_NOTE, NULL,
-			    "!lba %llx, no pip for path_index, "
-			    "pip %p\n", bp->b_lblkno, (void *)pip));
-		}
+		MDI_DEBUG(4, (MDI_NOTE, ct->ct_dip,
+		    "lba %llx: path %s %p",
+		    bp->b_lblkno, mdi_pi_spathname(pip), (void *)pip));
 	}
 	return (MDI_FAILURE);
 }
@@ -2026,7 +2055,6 @@
 
 	/* determine type of arg based on flags */
 	if (flags & MDI_SELECT_PATH_INSTANCE) {
-		flags &= ~MDI_SELECT_PATH_INSTANCE;
 		path_instance = (int)(intptr_t)arg;
 		start_pip = NULL;
 	} else {
@@ -2056,8 +2084,8 @@
 			 * Client is not ready to accept any I/O requests.
 			 * Fail this request.
 			 */
-			MDI_DEBUG(2, (CE_NOTE, cdip, "!mdi_select_path: "
-			    "client state offline ct = %p\n", (void *)ct));
+			MDI_DEBUG(2, (MDI_NOTE, cdip,
+			    "client state offline ct = %p", (void *)ct));
 			MDI_CLIENT_UNLOCK(ct);
 			return (MDI_FAILURE);
 		}
@@ -2067,8 +2095,8 @@
 			 * Check for Failover is in progress. If so tell the
 			 * caller that this device is busy.
 			 */
-			MDI_DEBUG(2, (CE_NOTE, cdip, "!mdi_select_path: "
-			    "client failover in progress ct = %p\n",
+			MDI_DEBUG(2, (MDI_NOTE, cdip,
+			    "client failover in progress ct = %p",
 			    (void *)ct));
 			MDI_CLIENT_UNLOCK(ct);
 			return (MDI_BUSY);
@@ -2080,8 +2108,8 @@
 		 * (standby) and let the probe/attach process to continue.
 		 */
 		if (MDI_CLIENT_IS_DETACHED(ct) || !i_ddi_devi_attached(cdip)) {
-			MDI_DEBUG(4, (CE_NOTE, cdip, "!Devi is onlining "
-			    "ct = %p\n", (void *)ct));
+			MDI_DEBUG(4, (MDI_NOTE, cdip,
+			    "devi is onlining ct = %p", (void *)ct));
 			MDI_CLIENT_UNLOCK(ct);
 			return (MDI_DEVI_ONLINING);
 		}
@@ -2111,9 +2139,19 @@
 			return (MDI_FAILURE);
 		}
 
-		/* verify state of path */
+		/*
+		 * Verify state of path. When asked to select a specific
+		 * path_instance, we select the requested path in any
+		 * state (ONLINE, OFFLINE, STANDBY, FAULT) other than INIT.
+		 * We don't however select paths where the pHCI has detached.
+		 * NOTE: last pathinfo node of an opened client device may
+		 * exist in an OFFLINE state after the pHCI associated with
+		 * that path has detached (but pi_phci will be NULL if that
+		 * has occurred).
+		 */
 		MDI_PI_LOCK(pip);
-		if (MDI_PI(pip)->pi_state != MDI_PATHINFO_STATE_ONLINE) {
+		if ((MDI_PI(pip)->pi_state == MDI_PATHINFO_STATE_INIT) ||
+		    (MDI_PI(pip)->pi_phci == NULL)) {
 			MDI_PI_UNLOCK(pip);
 			MDI_CLIENT_UNLOCK(ct);
 			return (MDI_FAILURE);
@@ -2125,7 +2163,6 @@
 		 */
 		MDI_PI_HOLD(pip);
 		MDI_PI_UNLOCK(pip);
-		ct->ct_path_last = pip;
 		*ret_pip = pip;
 		MDI_CLIENT_UNLOCK(ct);
 		return (MDI_SUCCESS);
@@ -2213,7 +2250,7 @@
 				return (MDI_SUCCESS);
 			}
 		}
-		/*  FALLTHROUGH */
+		/* FALLTHROUGH */
 	case LOAD_BALANCE_RR:
 		/*
 		 * Load balancing is Round Robin. Start looking for a online
@@ -2598,8 +2635,8 @@
 	mdi_client_t		*ct;
 	mdi_pathinfo_t		*pip = NULL;
 
-	MDI_DEBUG(2, (CE_NOTE, pdip, "!mdi_pi_find: %s %s",
-	    caddr ? caddr : "NULL", paddr ? paddr : "NULL"));
+	MDI_DEBUG(2, (MDI_NOTE, pdip,
+	    "caddr@%s paddr@%s", caddr ? caddr : "", paddr ? paddr : ""));
 	if ((pdip == NULL) || (paddr == NULL)) {
 		return (NULL);
 	}
@@ -2608,8 +2645,7 @@
 		/*
 		 * Invalid pHCI device, Nothing more to do.
 		 */
-		MDI_DEBUG(2, (CE_WARN, pdip,
-		    "!mdi_pi_find: invalid phci"));
+		MDI_DEBUG(2, (MDI_WARN, pdip, "invalid phci"));
 		return (NULL);
 	}
 
@@ -2618,8 +2654,7 @@
 		/*
 		 * Invalid vHCI device, Nothing more to do.
 		 */
-		MDI_DEBUG(2, (CE_WARN, pdip,
-		    "!mdi_pi_find: invalid vhci"));
+		MDI_DEBUG(2, (MDI_WARN, pdip, "invalid vhci"));
 		return (NULL);
 	}
 
@@ -2633,8 +2668,8 @@
 		 */
 		MDI_PHCI_LOCK(ph);
 		if (MDI_PHCI_IS_OFFLINE(ph)) {
-			MDI_DEBUG(2, (CE_WARN, pdip,
-			    "!mdi_pi_find: offline phci %p", (void *)ph));
+			MDI_DEBUG(2, (MDI_WARN, pdip,
+			    "offline phci %p", (void *)ph));
 			MDI_PHCI_UNLOCK(ph);
 			return (NULL);
 		}
@@ -2647,8 +2682,8 @@
 			pip = (mdi_pathinfo_t *)MDI_PI(pip)->pi_phci_link;
 		}
 		MDI_PHCI_UNLOCK(ph);
-		MDI_DEBUG(2, (CE_NOTE, pdip, "!mdi_pi_find: found %p",
-		    (void *)pip));
+		MDI_DEBUG(2, (MDI_NOTE, pdip,
+		    "found %s %p", mdi_pi_spathname(pip), (void *)pip));
 		return (pip);
 	}
 
@@ -2676,8 +2711,8 @@
 		 * created yet.
 		 */
 		MDI_VHCI_CLIENT_UNLOCK(vh);
-		MDI_DEBUG(2, (CE_NOTE, pdip, "!mdi_pi_find: client not "
-		    "found for caddr %s", caddr ? caddr : "NULL"));
+		MDI_DEBUG(2, (MDI_NOTE, pdip,
+		    "client not found for caddr @%s", caddr ? caddr : ""));
 		return (NULL);
 	}
 
@@ -2705,7 +2740,8 @@
 		pip = (mdi_pathinfo_t *)MDI_PI(pip)->pi_client_link;
 	}
 	MDI_CLIENT_UNLOCK(ct);
-	MDI_DEBUG(2, (CE_NOTE, pdip, "!mdi_pi_find: found:: %p", (void *)pip));
+	MDI_DEBUG(2, (MDI_NOTE, pdip,
+	    "found: %s %p", mdi_pi_spathname(pip), (void *)pip));
 	return (pip);
 }
 
@@ -2742,9 +2778,9 @@
 	int		rv = MDI_NOMEM;
 	int		path_allocated = 0;
 
-	MDI_DEBUG(2, (CE_NOTE, pdip, "!mdi_pi_alloc_compatible: %s %s %s",
-	    cname ? cname : "NULL", caddr ? caddr : "NULL",
-	    paddr ? paddr : "NULL"));
+	MDI_DEBUG(2, (MDI_NOTE, pdip,
+	    "cname %s: caddr@%s paddr@%s",
+	    cname ? cname : "", caddr ? caddr : "", paddr ? paddr : ""));
 
 	if (pdip == NULL || cname == NULL || caddr == NULL || paddr == NULL ||
 	    ret_pip == NULL) {
@@ -2757,8 +2793,8 @@
 	/* No allocations on detaching pHCI */
 	if (DEVI_IS_DETACHING(pdip)) {
 		/* Invalid pHCI device, return failure */
-		MDI_DEBUG(1, (CE_WARN, pdip,
-		    "!mdi_pi_alloc: detaching pHCI=%p", (void *)pdip));
+		MDI_DEBUG(1, (MDI_WARN, pdip,
+		    "!detaching pHCI=%p", (void *)pdip));
 		return (MDI_FAILURE);
 	}
 
@@ -2766,8 +2802,8 @@
 	ASSERT(ph != NULL);
 	if (ph == NULL) {
 		/* Invalid pHCI device, return failure */
-		MDI_DEBUG(1, (CE_WARN, pdip,
-		    "!mdi_pi_alloc: invalid pHCI=%p", (void *)pdip));
+		MDI_DEBUG(1, (MDI_WARN, pdip,
+		    "!invalid pHCI=%p", (void *)pdip));
 		return (MDI_FAILURE);
 	}
 
@@ -2775,8 +2811,8 @@
 	vh = ph->ph_vhci;
 	if (vh == NULL) {
 		/* Invalid vHCI device, return failure */
-		MDI_DEBUG(1, (CE_WARN, pdip,
-		    "!mdi_pi_alloc: invalid vHCI=%p", (void *)pdip));
+		MDI_DEBUG(1, (MDI_WARN, pdip,
+		    "!invalid vHCI=%p", (void *)pdip));
 		MDI_PHCI_UNLOCK(ph);
 		return (MDI_FAILURE);
 	}
@@ -2786,8 +2822,8 @@
 		 * Do not allow new node creation when pHCI is in
 		 * offline/suspended states
 		 */
-		MDI_DEBUG(1, (CE_WARN, pdip,
-		    "mdi_pi_alloc: pHCI=%p is not ready", (void *)ph));
+		MDI_DEBUG(1, (MDI_WARN, pdip,
+		    "pHCI=%p is not ready", (void *)ph));
 		MDI_PHCI_UNLOCK(ph);
 		return (MDI_BUSY);
 	}
@@ -2857,8 +2893,8 @@
 	MDI_PHCI_UNLOCK(ph);
 	*ret_pip = pip;
 
-	MDI_DEBUG(2, (CE_NOTE, pdip,
-	    "!mdi_pi_alloc_compatible: alloc %p", (void *)pip));
+	MDI_DEBUG(2, (MDI_NOTE, pdip,
+	    "alloc %s %p", mdi_pi_spathname(pip), (void *)pip));
 
 	if (path_allocated)
 		vhcache_pi_add(vh->vh_config, MDI_PI(pip));
@@ -2888,7 +2924,7 @@
 	mdi_pathinfo_t	*pip;
 	int		ct_circular;
 	int		ph_circular;
-	static char	path[MAXPATHLEN];
+	static char	path[MAXPATHLEN];	/* mdi_pathmap_mutex protects */
 	char		*path_persistent;
 	int		path_instance;
 	mod_hash_val_t	hv;
@@ -2927,7 +2963,7 @@
 	 */
         mutex_enter(&mdi_pathmap_mutex);
 	(void) ddi_pathname(ph->ph_dip, path);
-	(void) sprintf(path + strlen(path), "/%s@%s", 
+	(void) sprintf(path + strlen(path), "/%s@%s",
 	    mdi_pi_get_node_name(pip), mdi_pi_get_addr(pip));
         if (mod_hash_find(mdi_pathmap_bypath, (mod_hash_key_t)path, &hv) == 0) {
                 path_instance = (uint_t)(intptr_t)hv;
@@ -2941,6 +2977,15 @@
 		(void) mod_hash_insert(mdi_pathmap_byinstance,
 		    (mod_hash_key_t)(intptr_t)path_instance,
 		    (mod_hash_val_t)path_persistent);
+
+		/* create shortpath name */
+		(void) snprintf(path, sizeof(path), "%s%d/%s@%s",
+		    ddi_driver_name(ph->ph_dip), ddi_get_instance(ph->ph_dip),
+		    mdi_pi_get_node_name(pip), mdi_pi_get_addr(pip));
+		path_persistent = i_ddi_strdup(path, KM_SLEEP);
+		(void) mod_hash_insert(mdi_pathmap_sbyinstance,
+		    (mod_hash_key_t)(intptr_t)path_instance,
+		    (mod_hash_val_t)path_persistent);
         }
         mutex_exit(&mdi_pathmap_mutex);
 	MDI_PI(pip)->pi_path_instance = path_instance;
@@ -3000,6 +3045,29 @@
 }
 
 /*
+ * mdi_pi_spathname_by_instance():
+ *	Lookup of "shortpath" by 'path_instance'. Return "shortpath".
+ *	NOTE: returned "shortpath" remains valid forever (until reboot).
+ */
+char *
+mdi_pi_spathname_by_instance(int path_instance)
+{
+	char		*path;
+	mod_hash_val_t	hv;
+
+	/* mdi_pathmap lookup of "path" by 'path_instance' */
+	mutex_enter(&mdi_pathmap_mutex);
+	if (mod_hash_find(mdi_pathmap_sbyinstance,
+	    (mod_hash_key_t)(intptr_t)path_instance, &hv) == 0)
+		path = (char *)hv;
+	else
+		path = NULL;
+	mutex_exit(&mdi_pathmap_mutex);
+	return (path);
+}
+
+
+/*
  * i_mdi_phci_add_path():
  * 		Add a mdi_pathinfo node to pHCI list.
  * Notes:
@@ -3068,8 +3136,9 @@
 		/*
 		 * Invalid pHCI device, return failure
 		 */
-		MDI_DEBUG(1, (CE_WARN, NULL,
-		    "!mdi_pi_free: invalid pHCI pip=%p", (void *)pip));
+		MDI_DEBUG(1, (MDI_WARN, NULL,
+		    "!invalid pHCI: pip %s %p",
+		    mdi_pi_spathname(pip), (void *)pip));
 		MDI_PI_UNLOCK(pip);
 		return (MDI_FAILURE);
 	}
@@ -3078,8 +3147,9 @@
 	ASSERT(vh != NULL);
 	if (vh == NULL) {
 		/* Invalid pHCI device, return failure */
-		MDI_DEBUG(1, (CE_WARN, NULL,
-		    "!mdi_pi_free: invalid vHCI pip=%p", (void *)pip));
+		MDI_DEBUG(1, (MDI_WARN, ph->ph_dip,
+		    "!invalid vHCI: pip %s %p",
+		    mdi_pi_spathname(pip), (void *)pip));
 		MDI_PI_UNLOCK(pip);
 		return (MDI_FAILURE);
 	}
@@ -3090,8 +3160,9 @@
 		/*
 		 * Invalid Client device, return failure
 		 */
-		MDI_DEBUG(1, (CE_WARN, NULL,
-		    "!mdi_pi_free: invalid client pip=%p", (void *)pip));
+		MDI_DEBUG(1, (MDI_WARN, ph->ph_dip,
+		    "!invalid client: pip %s %p",
+		    mdi_pi_spathname(pip), (void *)pip));
 		MDI_PI_UNLOCK(pip);
 		return (MDI_FAILURE);
 	}
@@ -3106,8 +3177,8 @@
 		/*
 		 * Node is busy
 		 */
-		MDI_DEBUG(1, (CE_WARN, ct->ct_dip,
-		    "!mdi_pi_free: pathinfo node is busy pip=%p", (void *)pip));
+		MDI_DEBUG(1, (MDI_WARN, ct->ct_dip,
+		    "!busy: pip %s %p", mdi_pi_spathname(pip), (void *)pip));
 		MDI_PI_UNLOCK(pip);
 		return (MDI_BUSY);
 	}
@@ -3116,9 +3187,10 @@
 		/*
 		 * Give a chance for pending I/Os to complete.
 		 */
-		MDI_DEBUG(1, (CE_NOTE, ct->ct_dip, "!mdi_pi_free: "
-		    "%d cmds still pending on path: %p\n",
-		    MDI_PI(pip)->pi_ref_cnt, (void *)pip));
+		MDI_DEBUG(1, (MDI_NOTE, ct->ct_dip,
+		    "!%d cmds still pending on path: %s %p",
+		    MDI_PI(pip)->pi_ref_cnt,
+		    mdi_pi_spathname(pip), (void *)pip));
 		if (cv_timedwait(&MDI_PI(pip)->pi_ref_cv,
 		    &MDI_PI(pip)->pi_mutex,
 		    ddi_get_lbolt() + drv_usectohz(60 * 1000000)) == -1) {
@@ -3126,14 +3198,13 @@
 			 * The timeout time reached without ref_cnt being zero
 			 * being signaled.
 			 */
-			MDI_DEBUG(1, (CE_NOTE, ct->ct_dip,
-			    "!mdi_pi_free: "
-			    "Timeout reached on path %p without the cond\n",
-			    (void *)pip));
-			MDI_DEBUG(1, (CE_NOTE, ct->ct_dip,
-			    "!mdi_pi_free: "
-			    "%d cmds still pending on path: %p\n",
-			    MDI_PI(pip)->pi_ref_cnt, (void *)pip));
+			MDI_DEBUG(1, (MDI_NOTE, ct->ct_dip,
+			    "!Timeout reached on path %s %p without the cond",
+			    mdi_pi_spathname(pip), (void *)pip));
+			MDI_DEBUG(1, (MDI_NOTE, ct->ct_dip,
+			    "!%d cmds still pending on path %s %p",
+			    MDI_PI(pip)->pi_ref_cnt,
+			    mdi_pi_spathname(pip), (void *)pip));
 			MDI_PI_UNLOCK(pip);
 			return (MDI_BUSY);
 		}
@@ -3172,7 +3243,7 @@
 	 */
 	if (rv == MDI_SUCCESS) {
 		if (client_held) {
-			MDI_DEBUG(4, (CE_NOTE, ct->ct_dip, "mdi_pi_free "
+			MDI_DEBUG(4, (MDI_NOTE, ct->ct_dip,
 			    "i_mdi_pm_rele_client\n"));
 			i_mdi_pm_rele_client(ct, 1);
 		}
@@ -3356,8 +3427,9 @@
 		 * Invalid pHCI device, fail the request
 		 */
 		MDI_PI_UNLOCK(pip);
-		MDI_DEBUG(1, (CE_WARN, NULL,
-		    "!mdi_pi_state_change: invalid phci pip=%p", (void *)pip));
+		MDI_DEBUG(1, (MDI_WARN, NULL,
+		    "!invalid phci: pip %s %p",
+		    mdi_pi_spathname(pip), (void *)pip));
 		return (MDI_FAILURE);
 	}
 
@@ -3368,8 +3440,9 @@
 		 * Invalid vHCI device, fail the request
 		 */
 		MDI_PI_UNLOCK(pip);
-		MDI_DEBUG(1, (CE_WARN, NULL,
-		    "!mdi_pi_state_change: invalid vhci pip=%p", (void *)pip));
+		MDI_DEBUG(1, (MDI_WARN, ph->ph_dip,
+		    "!invalid vhci: pip %s %p",
+		    mdi_pi_spathname(pip), (void *)pip));
 		return (MDI_FAILURE);
 	}
 
@@ -3380,9 +3453,9 @@
 		 * Invalid client device, fail the request
 		 */
 		MDI_PI_UNLOCK(pip);
-		MDI_DEBUG(1, (CE_WARN, NULL,
-		    "!mdi_pi_state_change: invalid client pip=%p",
-		    (void *)pip));
+		MDI_DEBUG(1, (MDI_WARN, ph->ph_dip,
+		    "!invalid client: pip %s %p",
+		    mdi_pi_spathname(pip), (void *)pip));
 		return (MDI_FAILURE);
 	}
 
@@ -3397,9 +3470,10 @@
 		if (f != NULL) {
 			rv = (*f)(vh->vh_dip, pip, 0);
 			if (rv != MDI_SUCCESS) {
-				MDI_DEBUG(1, (CE_WARN, ct->ct_dip,
-				    "!vo_pi_init: failed vHCI=0x%p, pip=0x%p",
-				    (void *)vh, (void *)pip));
+				MDI_DEBUG(1, (MDI_WARN, ct->ct_dip,
+				    "!vo_pi_init failed: vHCI %p, pip %s %p",
+				    (void *)vh, mdi_pi_spathname(pip),
+				    (void *)pip));
 				return (MDI_FAILURE);
 			}
 		}
@@ -3413,9 +3487,8 @@
 	 */
 	i_mdi_phci_lock(ph, pip);
 	if (MDI_PHCI_IS_READY(ph) == 0) {
-		MDI_DEBUG(1, (CE_WARN, ct->ct_dip,
-		    "!mdi_pi_state_change: pHCI not ready, pHCI=%p",
-		    (void *)ph));
+		MDI_DEBUG(1, (MDI_WARN, ct->ct_dip,
+		    "!pHCI not ready, pHCI=%p", (void *)ph));
 		MDI_PI_UNLOCK(pip);
 		i_mdi_phci_unlock(ph);
 		return (MDI_BUSY);
@@ -3474,15 +3547,18 @@
 		 * ndi_devi_offline() cannot hold pip or ct locks.
 		 */
 		MDI_PI_UNLOCK(pip);
+
 		/*
-		 * Don't offline the client dev_info node unless we have
-		 * no available paths left at all.
+		 * If this is a user initiated path online->offline operation
+		 * who's success would transition a client from DEGRADED to
+		 * FAILED then only proceed if we can offline the client first.
 		 */
 		cdip = ct->ct_dip;
-		if ((flag & NDI_DEVI_REMOVE) &&
-		    (ct->ct_path_count == 1)) {
+		if ((flag & NDI_USER_REQ) &&
+		    MDI_PI_IS_ONLINE(pip) &&
+		    (MDI_CLIENT_STATE(ct) == MDI_CLIENT_STATE_DEGRADED)) {
 			i_mdi_client_unlock(ct);
-			rv = ndi_devi_offline(cdip, 0);
+			rv = ndi_devi_offline(cdip, NDI_DEVFS_CLEAN);
 			if (rv != NDI_SUCCESS) {
 				/*
 				 * Convert to MDI error code
@@ -3521,8 +3597,8 @@
 		MDI_CLIENT_SET_DEV_NOT_SUPPORTED(ct);
 	}
 	if (rv != MDI_SUCCESS) {
-		MDI_DEBUG(2, (CE_WARN, ct->ct_dip,
-		    "!vo_pi_state_change: failed rv = %x", rv));
+		MDI_DEBUG(2, (MDI_WARN, ct->ct_dip,
+		    "vo_pi_state_change failed: rv %x", rv));
 	}
 	if (MDI_PI_IS_TRANSIENT(pip)) {
 		if (rv == MDI_SUCCESS) {
@@ -3576,9 +3652,9 @@
 						 * Reset client flags to
 						 * offline.
 						 */
-						MDI_DEBUG(1, (CE_WARN, cdip,
-						    "!ndi_devi_online: failed "
-						    " Error: %x", rv));
+						MDI_DEBUG(1, (MDI_WARN, cdip,
+						    "!ndi_devi_online failed "
+						    "error %x", rv));
 						MDI_CLIENT_SET_OFFLINE(ct);
 					}
 					if (rv != NDI_SUCCESS) {
@@ -3596,11 +3672,12 @@
 				 * This is the last path case for
 				 * non-user initiated events.
 				 */
-				if (((flag & NDI_DEVI_REMOVE) == 0) &&
+				if (((flag & NDI_USER_REQ) == 0) &&
 				    cdip && (i_ddi_node_state(cdip) >=
 				    DS_INITIALIZED)) {
 					MDI_CLIENT_UNLOCK(ct);
-					rv = ndi_devi_offline(cdip, 0);
+					rv = ndi_devi_offline(cdip,
+					    NDI_DEVFS_CLEAN);
 					MDI_CLIENT_LOCK(ct);
 
 					if (rv != NDI_SUCCESS) {
@@ -3610,9 +3687,9 @@
 						 * online as the path could not
 						 * be offlined.
 						 */
-						MDI_DEBUG(1, (CE_WARN, cdip,
-						    "!ndi_devi_offline: failed "
-						    " Error: %x", rv));
+						MDI_DEBUG(1, (MDI_WARN, cdip,
+						    "!ndi_devi_offline failed: "
+						    "error %x", rv));
 						MDI_CLIENT_SET_ONLINE(ct);
 					}
 				}
@@ -3663,8 +3740,6 @@
 	mdi_client_t	*ct = MDI_PI(pip)->pi_client;
 	int		client_held = 0;
 	int		rv;
-	int		se_flag;
-	int		kmem_flag;
 
 	ASSERT(ct != NULL);
 	rv = i_mdi_pi_state_change(pip, MDI_PATHINFO_STATE_ONLINE, flags);
@@ -3673,8 +3748,8 @@
 
 	MDI_PI_LOCK(pip);
 	if (MDI_PI(pip)->pi_pm_held == 0) {
-		MDI_DEBUG(4, (CE_NOTE, ct->ct_dip, "mdi_pi_online "
-		    "i_mdi_pm_hold_pip %p\n", (void *)pip));
+		MDI_DEBUG(4, (MDI_NOTE, ct->ct_dip,
+		    "i_mdi_pm_hold_pip %p", (void *)pip));
 		i_mdi_pm_hold_pip(pip);
 		client_held = 1;
 	}
@@ -3686,19 +3761,12 @@
 			rv = i_mdi_power_all_phci(ct);
 		}
 
-		MDI_DEBUG(4, (CE_NOTE, ct->ct_dip, "mdi_pi_online "
-		    "i_mdi_pm_hold_client %p\n", (void *)ct));
+		MDI_DEBUG(4, (MDI_NOTE, ct->ct_dip,
+		    "i_mdi_pm_hold_client %p", (void *)ct));
 		i_mdi_pm_hold_client(ct, 1);
 		MDI_CLIENT_UNLOCK(ct);
 	}
 
-	/* determine interrupt context */
-	se_flag = (servicing_interrupt()) ? SE_NOSLEEP : SE_SLEEP;
-	kmem_flag = (se_flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
-
-	/* A new path is online.  Invalidate DINFOCACHE snap shot. */
-	i_ddi_di_cache_invalidate(kmem_flag);
-
 	return (rv);
 }
 
@@ -3741,8 +3809,16 @@
 {
 	int	ret, client_held = 0;
 	mdi_client_t	*ct;
-	int		se_flag;
-	int		kmem_flag;
+
+	/*
+	 * Original code overloaded NDI_DEVI_REMOVE to this interface, and
+	 * used it to mean "user initiated operation" (i.e. devctl). Callers
+	 * should now just use NDI_USER_REQ.
+	 */
+	if (flags & NDI_DEVI_REMOVE) {
+		flags &= ~NDI_DEVI_REMOVE;
+		flags |= NDI_USER_REQ;
+	}
 
 	ret = i_mdi_pi_state_change(pip, MDI_PATHINFO_STATE_OFFLINE, flags);
 
@@ -3756,18 +3832,11 @@
 		if (client_held) {
 			ct = MDI_PI(pip)->pi_client;
 			MDI_CLIENT_LOCK(ct);
-			MDI_DEBUG(4, (CE_NOTE, ct->ct_dip,
-			    "mdi_pi_offline i_mdi_pm_rele_client\n"));
+			MDI_DEBUG(4, (MDI_NOTE, ct->ct_dip,
+			    "i_mdi_pm_rele_client\n"));
 			i_mdi_pm_rele_client(ct, 1);
 			MDI_CLIENT_UNLOCK(ct);
 		}
-
-		/* determine interrupt context */
-		se_flag = (servicing_interrupt()) ? SE_NOSLEEP : SE_SLEEP;
-		kmem_flag = (se_flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
-
-		/* pathinfo is offlined. update DINFOCACHE. */
-		i_ddi_di_cache_invalidate(kmem_flag);
 	}
 
 	return (ret);
@@ -3794,9 +3863,10 @@
 		/*
 		 * Give a chance for pending I/Os to complete.
 		 */
-		MDI_DEBUG(1, (CE_NOTE, ct->ct_dip, "!i_mdi_pi_offline: "
-		    "%d cmds still pending on path: %p\n",
-		    MDI_PI(pip)->pi_ref_cnt, (void *)pip));
+		MDI_DEBUG(1, (MDI_NOTE, ct->ct_dip,
+		    "!%d cmds still pending on path %s %p",
+		    MDI_PI(pip)->pi_ref_cnt, mdi_pi_spathname(pip),
+		    (void *)pip));
 		if (cv_timedwait(&MDI_PI(pip)->pi_ref_cv,
 		    &MDI_PI(pip)->pi_mutex,
 		    ddi_get_lbolt() + drv_usectohz(60 * 1000000)) == -1) {
@@ -3804,12 +3874,13 @@
 			 * The timeout time reached without ref_cnt being zero
 			 * being signaled.
 			 */
-			MDI_DEBUG(1, (CE_NOTE, ct->ct_dip, "!i_mdi_pi_offline: "
-			    "Timeout reached on path %p without the cond\n",
-			    (void *)pip));
-			MDI_DEBUG(1, (CE_NOTE, ct->ct_dip, "!i_mdi_pi_offline: "
-			    "%d cmds still pending on path: %p\n",
-			    MDI_PI(pip)->pi_ref_cnt, (void *)pip));
+			MDI_DEBUG(1, (MDI_NOTE, ct->ct_dip,
+			    "!Timeout reached on path %s %p without the cond",
+			    mdi_pi_spathname(pip), (void *)pip));
+			MDI_DEBUG(1, (MDI_NOTE, ct->ct_dip,
+			    "!%d cmds still pending on path %s %p",
+			    MDI_PI(pip)->pi_ref_cnt,
+			    mdi_pi_spathname(pip), (void *)pip));
 		}
 	}
 	vh = ct->ct_vhci;
@@ -3825,9 +3896,10 @@
 		MDI_PI_UNLOCK(pip);
 		if ((rv = (*f)(vdip, pip, MDI_PATHINFO_STATE_OFFLINE, 0,
 		    flags)) != MDI_SUCCESS) {
-			MDI_DEBUG(1, (CE_WARN, ct->ct_dip,
-			    "!vo_path_offline failed "
-			    "vdip %p, pip %p", (void *)vdip, (void *)pip));
+			MDI_DEBUG(1, (MDI_WARN, ct->ct_dip,
+			    "!vo_path_offline failed: vdip %s%d %p: path %s %p",
+			    ddi_driver_name(vdip), ddi_get_instance(vdip),
+			    (void *)vdip, mdi_pi_spathname(pip), (void *)pip));
 		}
 		MDI_PI_LOCK(pip);
 	}
@@ -3856,7 +3928,8 @@
 				    (i_ddi_node_state(cdip) >=
 				    DS_INITIALIZED)) {
 					MDI_CLIENT_UNLOCK(ct);
-					rv = ndi_devi_offline(cdip, 0);
+					rv = ndi_devi_offline(cdip,
+					    NDI_DEVFS_CLEAN);
 					MDI_CLIENT_LOCK(ct);
 					if (rv != NDI_SUCCESS) {
 						/*
@@ -3864,9 +3937,9 @@
 						 * Reset client flags to
 						 * online.
 						 */
-						MDI_DEBUG(4, (CE_WARN, cdip,
-						    "!ndi_devi_offline: failed "
-						    " Error: %x", rv));
+						MDI_DEBUG(4, (MDI_WARN, cdip,
+						    "ndi_devi_offline failed: "
+						    "error %x", rv));
 						MDI_CLIENT_SET_ONLINE(ct);
 					}
 				}
@@ -3895,8 +3968,8 @@
 	/*
 	 * Change in the mdi_pathinfo node state will impact the client state
 	 */
-	MDI_DEBUG(2, (CE_NOTE, NULL, "!i_mdi_pi_offline ct = %p pip = %p",
-	    (void *)ct, (void *)pip));
+	MDI_DEBUG(2, (MDI_NOTE, ct->ct_dip,
+	    "ct = %p pip = %p", (void *)ct, (void *)pip));
 	return (rv);
 }
 
@@ -3966,6 +4039,25 @@
 	return (mdi_pi_pathname_by_instance(mdi_pi_get_path_instance(pip)));
 }
 
+/*
+ * mdi_pi_spathname():
+ *		Return pointer to shortpath to pathinfo node. Used for debug
+ *		messages, so return "" instead of NULL when unknown.
+ */
+char *
+mdi_pi_spathname(mdi_pathinfo_t *pip)
+{
+	char	*spath = "";
+
+	if (pip) {
+		spath = mdi_pi_spathname_by_instance(
+		    mdi_pi_get_path_instance(pip));
+		if (spath == NULL)
+			spath = "";
+	}
+	return (spath);
+}
+
 char *
 mdi_pi_pathname_obp(mdi_pathinfo_t *pip, char *path)
 {
@@ -4007,7 +4099,7 @@
 		(void) strncat(obp_path, component, MAXPATHLEN);
 	}
 	rc = mdi_prop_update_string(pip, "obp-path", obp_path);
-	
+
 	if (obp_path)
 		kmem_free(obp_path, MAXPATHLEN);
 	return (rc);
@@ -4040,8 +4132,12 @@
 mdi_pi_get_phci(mdi_pathinfo_t *pip)
 {
 	dev_info_t	*dip = NULL;
+	mdi_phci_t	*ph;
+
 	if (pip) {
-		dip = MDI_PI(pip)->pi_phci->ph_dip;
+		ph = MDI_PI(pip)->pi_phci;
+		if (ph)
+			dip = ph->ph_dip;
 	}
 	return (dip);
 }
@@ -4082,6 +4178,7 @@
 mdi_pi_get_phci_private(mdi_pathinfo_t *pip)
 {
 	caddr_t	pprivate = NULL;
+
 	if (pip) {
 		pprivate = MDI_PI(pip)->pi_pprivate;
 	}
@@ -4125,6 +4222,16 @@
 }
 
 /*
+ * mdi_pi_get_flags():
+ *		Get the mdi_pathinfo node flags.
+ */
+uint_t
+mdi_pi_get_flags(mdi_pathinfo_t *pip)
+{
+	return (pip ? MDI_PI(pip)->pi_flags : 0);
+}
+
+/*
  * Note that the following function needs to be the new interface for
  * mdi_pi_get_state when mpxio gets integrated to ON.
  */
@@ -4188,6 +4295,9 @@
 		ext_state = MDI_PI(pip)->pi_state & MDI_PATHINFO_EXT_STATE_MASK;
 		MDI_PI(pip)->pi_state = state;
 		MDI_PI(pip)->pi_state |= ext_state;
+
+		/* Path has changed state, invalidate DINFOCACHE snap shot. */
+		i_ddi_di_cache_invalidate();
 	}
 }
 
@@ -4258,7 +4368,7 @@
 		while (nvp) {
 			nvpair_t	*next;
 			next = nvlist_next_nvpair(MDI_PI(pip)->pi_prop, nvp);
-			(void) snprintf(nvp_name, MAXNAMELEN, "%s",
+			(void) snprintf(nvp_name, sizeof(nvp_name), "%s",
 			    nvpair_name(nvp));
 			(void) nvlist_remove_all(MDI_PI(pip)->pi_prop,
 			    nvp_name);
@@ -4632,19 +4742,21 @@
 static void
 i_mdi_report_path_state(mdi_client_t *ct, mdi_pathinfo_t *pip)
 {
-	char		*phci_path, *ct_path;
+	char		*ct_path;
 	char		*ct_status;
 	char		*status;
-	dev_info_t	*dip = ct->ct_dip;
+	dev_info_t	*cdip = ct->ct_dip;
 	char		lb_buf[64];
+	int		report_lb_c = 0, report_lb_p = 0;
 
 	ASSERT(MDI_CLIENT_LOCKED(ct));
-	if ((dip == NULL) || (ddi_get_instance(dip) == -1) ||
+	if ((cdip == NULL) || (ddi_get_instance(cdip) == -1) ||
 	    (MDI_CLIENT_IS_REPORT_DEV_NEEDED(ct) == 0)) {
 		return;
 	}
 	if (MDI_CLIENT_STATE(ct) == MDI_CLIENT_STATE_OPTIMAL) {
 		ct_status = "optimal";
+		report_lb_c = 1;
 	} else if (MDI_CLIENT_STATE(ct) == MDI_CLIENT_STATE_DEGRADED) {
 		ct_status = "degraded";
 	} else if (MDI_CLIENT_STATE(ct) == MDI_CLIENT_STATE_FAILED) {
@@ -4653,10 +4765,15 @@
 		ct_status = "unknown";
 	}
 
-	if (MDI_PI_IS_OFFLINE(pip)) {
+	lb_buf[0] = 0;		/* not interested in load balancing config */
+
+	if (MDI_PI_FLAGS_IS_DEVICE_REMOVED(pip)) {
+		status = "removed";
+	} else if (MDI_PI_IS_OFFLINE(pip)) {
 		status = "offline";
 	} else if (MDI_PI_IS_ONLINE(pip)) {
 		status = "online";
+		report_lb_p = 1;
 	} else if (MDI_PI_IS_STANDBY(pip)) {
 		status = "standby";
 	} else if (MDI_PI_IS_FAULT(pip)) {
@@ -4665,31 +4782,44 @@
 		status = "unknown";
 	}
 
-	if (ct->ct_lb == LOAD_BALANCE_LBA) {
-		(void) snprintf(lb_buf, sizeof (lb_buf),
-		    "%s, region-size: %d", mdi_load_balance_lba,
-			ct->ct_lb_args->region_size);
-	} else if (ct->ct_lb == LOAD_BALANCE_NONE) {
-		(void) snprintf(lb_buf, sizeof (lb_buf),
-		    "%s", mdi_load_balance_none);
-	} else {
-		(void) snprintf(lb_buf, sizeof (lb_buf), "%s",
-		    mdi_load_balance_rr);
-	}
-
-	if (dip) {
+	if (cdip) {
 		ct_path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
-		phci_path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
-		cmn_err(CE_CONT, "?%s (%s%d) multipath status: %s, "
-		    "path %s (%s%d) to target address: %s is %s"
-		    " Load balancing: %s\n",
-		    ddi_pathname(dip, ct_path), ddi_driver_name(dip),
-		    ddi_get_instance(dip), ct_status,
-		    ddi_pathname(MDI_PI(pip)->pi_phci->ph_dip, phci_path),
-		    ddi_driver_name(MDI_PI(pip)->pi_phci->ph_dip),
-		    ddi_get_instance(MDI_PI(pip)->pi_phci->ph_dip),
-		    MDI_PI(pip)->pi_addr, status, lb_buf);
-		kmem_free(phci_path, MAXPATHLEN);
+
+		/*
+		 * NOTE: Keeping "multipath status: %s" and
+		 * "Load balancing: %s" format unchanged in case someone
+		 * scrubs /var/adm/messages looking for these messages.
+		 */
+		if (report_lb_c && report_lb_p) {
+			if (ct->ct_lb == LOAD_BALANCE_LBA) {
+				(void) snprintf(lb_buf, sizeof (lb_buf),
+				    "%s, region-size: %d", mdi_load_balance_lba,
+				    ct->ct_lb_args->region_size);
+			} else if (ct->ct_lb == LOAD_BALANCE_NONE) {
+				(void) snprintf(lb_buf, sizeof (lb_buf),
+				    "%s", mdi_load_balance_none);
+			} else {
+				(void) snprintf(lb_buf, sizeof (lb_buf), "%s",
+				    mdi_load_balance_rr);
+			}
+
+			cmn_err(mdi_debug_consoleonly ? CE_NOTE : CE_CONT,
+			    "?%s (%s%d) multipath status: %s: "
+			    "path %d %s is %s: Load balancing: %s\n",
+			    ddi_pathname(cdip, ct_path), ddi_driver_name(cdip),
+			    ddi_get_instance(cdip), ct_status,
+			    mdi_pi_get_path_instance(pip),
+			    mdi_pi_spathname(pip), status, lb_buf);
+		} else {
+			cmn_err(mdi_debug_consoleonly ? CE_NOTE : CE_CONT,
+			    "?%s (%s%d) multipath status: %s: "
+			    "path %d %s is %s\n",
+			    ddi_pathname(cdip, ct_path), ddi_driver_name(cdip),
+			    ddi_get_instance(cdip), ct_status,
+			    mdi_pi_get_path_instance(pip),
+			    mdi_pi_spathname(pip), status);
+		}
+
 		kmem_free(ct_path, MAXPATHLEN);
 		MDI_CLIENT_CLEAR_REPORT_DEV_NEEDED(ct);
 	}
@@ -4700,13 +4830,20 @@
  * i_mdi_log():
  *		Utility function for error message management
  *
- */
-/*PRINTFLIKE3*/
+ *		NOTE: Implementation takes care of trailing \n for cmn_err,
+ *		MDI_DEBUG should not terminate fmt strings with \n.
+ *
+ *		NOTE: If the level is >= 2, and there is no leading !?^
+ *		then a leading ! is implied (but can be overriden via
+ *		mdi_debug_consoleonly). If you are using kmdb on the console,
+ *		consider setting mdi_debug_consoleonly to 1 as an aid.
+ */
+/*PRINTFLIKE4*/
 static void
-i_mdi_log(int level, dev_info_t *dip, const char *fmt, ...)
+i_mdi_log(int level, const char *func, dev_info_t *dip, const char *fmt, ...)
 {
 	char		name[MAXNAMELEN];
-	char		buf[MAXNAMELEN];
+	char		buf[512];
 	char		*bp;
 	va_list		ap;
 	int		log_only = 0;
@@ -4714,14 +4851,14 @@
 	int		console_only = 0;
 
 	if (dip) {
-		(void) snprintf(name, MAXNAMELEN, "%s%d: ",
+		(void) snprintf(name, sizeof(name), "%s%d: ",
 		    ddi_driver_name(dip), ddi_get_instance(dip));
 	} else {
 		name[0] = 0;
 	}
 
 	va_start(ap, fmt);
-	(void) vsnprintf(buf, MAXNAMELEN, fmt, ap);
+	(void) vsnprintf(buf, sizeof(buf), fmt, ap);
 	va_end(ap);
 
 	switch (buf[0]) {
@@ -4738,6 +4875,8 @@
 		console_only = 1;
 		break;
 	default:
+		if (level >= 2)
+			log_only = 1;		/* ! implied */
 		bp = buf;
 		break;
 	}
@@ -4746,22 +4885,41 @@
 		boot_only = 0;
 		console_only = 0;
 	}
+	if (mdi_debug_consoleonly) {
+		log_only = 0;
+		boot_only = 0;
+		console_only = 1;
+		level = CE_NOTE;
+		goto console;
+	}
 
 	switch (level) {
 	case CE_NOTE:
 		level = CE_CONT;
 		/* FALLTHROUGH */
 	case CE_CONT:
+		if (boot_only) {
+			cmn_err(level, "?mdi: %s%s: %s\n", name, func, bp);
+		} else if (console_only) {
+			cmn_err(level, "^mdi: %s%s: %s\n", name, func, bp);
+		} else if (log_only) {
+			cmn_err(level, "!mdi: %s%s: %s\n", name, func, bp);
+		} else {
+			cmn_err(level, "mdi: %s%s: %s\n", name, func, bp);
+		}
+		break;
+
 	case CE_WARN:
 	case CE_PANIC:
+	console:
 		if (boot_only) {
-			cmn_err(level, "?mdi: %s%s", name, bp);
+			cmn_err(level, "?mdi: %s%s: %s", name, func, bp);
 		} else if (console_only) {
-			cmn_err(level, "^mdi: %s%s", name, bp);
+			cmn_err(level, "^mdi: %s%s: %s", name, func, bp);
 		} else if (log_only) {
-			cmn_err(level, "!mdi: %s%s", name, bp);
+			cmn_err(level, "!mdi: %s%s: %s", name, func, bp);
 		} else {
-			cmn_err(level, "mdi: %s%s", name, bp);
+			cmn_err(level, "mdi: %s%s: %s", name, func, bp);
 		}
 		break;
 	default:
@@ -4791,8 +4949,8 @@
 	if (ct->ct_power_cnt == 0)
 		(void) i_mdi_power_all_phci(ct);
 
-	MDI_DEBUG(4, (CE_NOTE, ct_dip, "i_mdi_client_online "
-	    "i_mdi_pm_hold_client %p\n", (void *)ct));
+	MDI_DEBUG(4, (MDI_NOTE, ct_dip,
+	    "i_mdi_pm_hold_client %p", (void *)ct));
 	i_mdi_pm_hold_client(ct, 1);
 
 	MDI_CLIENT_UNLOCK(ct);
@@ -4887,8 +5045,8 @@
 	 * critical services.
 	 */
 	ph = i_devi_get_phci(dip);
-	MDI_DEBUG(2, (CE_NOTE, dip, "!mdi_phci_offline called %p %p\n",
-	    (void *)dip, (void *)ph));
+	MDI_DEBUG(2, (MDI_NOTE, dip,
+	    "called %p %p", (void *)dip, (void *)ph));
 	if (ph == NULL) {
 		return (rv);
 	}
@@ -4896,8 +5054,8 @@
 	MDI_PHCI_LOCK(ph);
 
 	if (MDI_PHCI_IS_OFFLINE(ph)) {
-		MDI_DEBUG(1, (CE_WARN, dip, "!pHCI %p already offlined",
-		    (void *)ph));
+		MDI_DEBUG(1, (MDI_WARN, dip,
+		    "!pHCI already offlined: %p", (void *)dip));
 		MDI_PHCI_UNLOCK(ph);
 		return (NDI_SUCCESS);
 	}
@@ -4906,10 +5064,10 @@
 	 * Check to see if the pHCI can be offlined
 	 */
 	if (ph->ph_unstable) {
-		MDI_DEBUG(1, (CE_WARN, dip,
-		    "!One or more target devices are in transient "
-		    "state. This device can not be removed at "
-		    "this moment. Please try again later."));
+		MDI_DEBUG(1, (MDI_WARN, dip,
+		    "!One or more target devices are in transient state. "
+		    "This device can not be removed at this moment. "
+		    "Please try again later."));
 		MDI_PHCI_UNLOCK(ph);
 		return (NDI_BUSY);
 	}
@@ -4930,11 +5088,10 @@
 			/*
 			 * Failover is in progress, Fail the DR
 			 */
-			MDI_DEBUG(1, (CE_WARN, dip,
-			    "!pHCI device (%s%d) is Busy. %s",
-			    ddi_driver_name(dip), ddi_get_instance(dip),
-			    "This device can not be removed at "
-			    "this moment. Please try again later."));
+			MDI_DEBUG(1, (MDI_WARN, dip,
+			    "!pHCI device is busy. "
+			    "This device can not be removed at this moment. "
+			    "Please try again later."));
 			MDI_PI_UNLOCK(pip);
 			i_mdi_client_unlock(ct);
 			MDI_PHCI_UNLOCK(ph);
@@ -4952,7 +5109,8 @@
 		    MDI_CLIENT_STATE_FAILED)) {
 			i_mdi_client_unlock(ct);
 			MDI_PHCI_UNLOCK(ph);
-			if (ndi_devi_offline(cdip, 0) != NDI_SUCCESS) {
+			if (ndi_devi_offline(cdip,
+			    NDI_DEVFS_CLEAN) != NDI_SUCCESS) {
 				/*
 				 * ndi_devi_offline() failed.
 				 * This pHCI provides the critical path
@@ -4960,11 +5118,10 @@
 				 * Return busy.
 				 */
 				MDI_PHCI_LOCK(ph);
-				MDI_DEBUG(1, (CE_WARN, dip,
-				    "!pHCI device (%s%d) is Busy. %s",
-				    ddi_driver_name(dip), ddi_get_instance(dip),
-				    "This device can not be removed at "
-				    "this moment. Please try again later."));
+				MDI_DEBUG(1, (MDI_WARN, dip,
+				    "!pHCI device is busy. "
+				    "This device can not be removed at this "
+				    "moment. Please try again later."));
 				failed_pip = pip;
 				break;
 			} else {
@@ -5004,7 +5161,8 @@
 					MDI_PI_UNLOCK(pip);
 					i_mdi_client_unlock(ct);
 					MDI_PHCI_UNLOCK(ph);
-					(void) ndi_devi_offline(cdip, 0);
+					(void) ndi_devi_offline(cdip,
+						NDI_DEVFS_CLEAN);
 					MDI_PHCI_LOCK(ph);
 					pip = next;
 					continue;
@@ -5039,7 +5197,7 @@
 	/*
 	 * Give a chance for any pending commands to execute
 	 */
-	delay(1);
+	delay_random(5);
 	MDI_PHCI_LOCK(ph);
 	pip = ph->ph_path_head;
 	while (pip != NULL) {
@@ -5048,11 +5206,10 @@
 		MDI_PI_LOCK(pip);
 		ct = MDI_PI(pip)->pi_client;
 		if (!MDI_PI_IS_OFFLINE(pip)) {
-			MDI_DEBUG(1, (CE_WARN, dip,
-			    "!pHCI device (%s%d) is Busy. %s",
-			    ddi_driver_name(dip), ddi_get_instance(dip),
-			    "This device can not be removed at "
-			    "this moment. Please try again later."));
+			MDI_DEBUG(1, (MDI_WARN, dip,
+			    "!pHCI device is busy. "
+			    "This device can not be removed at this moment. "
+			    "Please try again later."));
 			MDI_PI_UNLOCK(pip);
 			MDI_PHCI_SET_ONLINE(ph);
 			MDI_PHCI_UNLOCK(ph);
@@ -5166,7 +5323,7 @@
 		if ((MDI_CLIENT_IS_FAILOVER_IN_PROGRESS(ct)) ||
 		    (ct->ct_unstable)) {
 			/*
-			 * Failover is in progress, can't check for constraints 
+			 * Failover is in progress, can't check for constraints
 			 */
 			MDI_PI_UNLOCK(pip);
 			i_mdi_client_unlock(ct);
@@ -5201,7 +5358,7 @@
 }
 
 /*
- * offline the path(s) hanging off the PHCI. If the
+ * offline the path(s) hanging off the pHCI. If the
  * last path to any client, check that constraints
  * have been applied.
  */
@@ -5291,8 +5448,9 @@
 	 * Cannot offline pip(s)
 	 */
 	if (unstable) {
-		cmn_err(CE_WARN, "PHCI in transient state, cannot "
-		    "retire, dip = %p", (void *)dip);
+		cmn_err(CE_WARN, "%s%d: mdi_phci_retire_finalize: "
+		    "pHCI in transient state, cannot retire",
+		    ddi_driver_name(dip), ddi_get_instance(dip));
 		MDI_PHCI_UNLOCK(ph);
 		return;
 	}
@@ -5317,7 +5475,7 @@
 	/*
 	 * Give a chance for any pending commands to execute
 	 */
-	delay(1);
+	delay_random(5);
 	MDI_PHCI_LOCK(ph);
 	pip = ph->ph_path_head;
 	while (pip != NULL) {
@@ -5326,8 +5484,10 @@
 		MDI_PI_LOCK(pip);
 		ct = MDI_PI(pip)->pi_client;
 		if (!MDI_PI_IS_OFFLINE(pip)) {
-			cmn_err(CE_WARN, "PHCI busy, cannot offline path: "
-			    "PHCI dip = %p", (void *)dip);
+			cmn_err(CE_WARN, "mdi_phci_retire_finalize: "
+			    "path %d %s busy, cannot offline",
+			    mdi_pi_get_path_instance(pip),
+			    mdi_pi_spathname(pip));
 			MDI_PI_UNLOCK(pip);
 			MDI_PHCI_SET_ONLINE(ph);
 			MDI_PHCI_UNLOCK(ph);
@@ -5365,8 +5525,8 @@
 	 * accordingly
 	 */
 	ct = i_devi_get_client(dip);
-	MDI_DEBUG(2, (CE_NOTE, dip, "!i_mdi_client_offline called %p %p\n",
-	    (void *)dip, (void *)ct));
+	MDI_DEBUG(2, (MDI_NOTE, dip,
+	    "called %p %p", (void *)dip, (void *)ct));
 	if (ct != NULL) {
 		MDI_CLIENT_LOCK(ct);
 		if (ct->ct_unstable) {
@@ -5374,10 +5534,10 @@
 			 * One or more paths are in transient state,
 			 * Dont allow offline of a client device
 			 */
-			MDI_DEBUG(1, (CE_WARN, dip,
-			    "!One or more paths to this device is "
-			    "in transient state. This device can not "
-			    "be removed at this moment. "
+			MDI_DEBUG(1, (MDI_WARN, dip,
+			    "!One or more paths to "
+			    "this device are in transient state. "
+			    "This device can not be removed at this moment. "
 			    "Please try again later."));
 			MDI_CLIENT_UNLOCK(ct);
 			return (NDI_BUSY);
@@ -5387,11 +5547,10 @@
 			 * Failover is in progress, Dont allow DR of
 			 * a client device
 			 */
-			MDI_DEBUG(1, (CE_WARN, dip,
-			    "!Client device (%s%d) is Busy. %s",
-			    ddi_driver_name(dip), ddi_get_instance(dip),
-			    "This device can not be removed at "
-			    "this moment. Please try again later."));
+			MDI_DEBUG(1, (MDI_WARN, dip,
+			    "!Client device is Busy. "
+			    "This device can not be removed at this moment. "
+			    "Please try again later."));
 			MDI_CLIENT_UNLOCK(ct);
 			return (NDI_BUSY);
 		}
@@ -5443,26 +5602,26 @@
 		MDI_PHCI_LOCK(ph);
 		switch (cmd) {
 		case DDI_ATTACH:
-			MDI_DEBUG(2, (CE_NOTE, dip,
-			    "!pHCI post_attach: called %p\n", (void *)ph));
+			MDI_DEBUG(2, (MDI_NOTE, dip,
+			    "phci post_attach called %p", (void *)ph));
 			if (error == DDI_SUCCESS) {
 				MDI_PHCI_SET_ATTACH(ph);
 			} else {
-				MDI_DEBUG(1, (CE_NOTE, dip,
-				    "!pHCI post_attach: failed error=%d\n",
+				MDI_DEBUG(1, (MDI_NOTE, dip,
+				    "!pHCI post_attach failed: error %d",
 				    error));
 				MDI_PHCI_SET_DETACH(ph);
 			}
 			break;
 
 		case DDI_RESUME:
-			MDI_DEBUG(2, (CE_NOTE, dip,
-			    "!pHCI post_resume: called %p\n", (void *)ph));
+			MDI_DEBUG(2, (MDI_NOTE, dip,
+			    "pHCI post_resume: called %p", (void *)ph));
 			if (error == DDI_SUCCESS) {
 				MDI_PHCI_SET_RESUME(ph);
 			} else {
-				MDI_DEBUG(1, (CE_NOTE, dip,
-				    "!pHCI post_resume: failed error=%d\n",
+				MDI_DEBUG(1, (MDI_NOTE, dip,
+				    "!pHCI post_resume failed: error %d",
 				    error));
 				MDI_PHCI_SET_SUSPEND(ph);
 			}
@@ -5478,15 +5637,15 @@
 		MDI_CLIENT_LOCK(ct);
 		switch (cmd) {
 		case DDI_ATTACH:
-			MDI_DEBUG(2, (CE_NOTE, dip,
-			    "!Client post_attach: called %p\n", (void *)ct));
+			MDI_DEBUG(2, (MDI_NOTE, dip,
+			    "client post_attach called %p", (void *)ct));
 			if (error != DDI_SUCCESS) {
-				MDI_DEBUG(1, (CE_NOTE, dip,
-				    "!Client post_attach: failed error=%d\n",
+				MDI_DEBUG(1, (MDI_NOTE, dip,
+				    "!client post_attach failed: error %d",
 				    error));
 				MDI_CLIENT_SET_DETACH(ct);
-				MDI_DEBUG(4, (CE_WARN, dip,
-				    "mdi_post_attach i_mdi_pm_reset_client\n"));
+				MDI_DEBUG(4, (MDI_WARN, dip,
+				    "i_mdi_pm_reset_client"));
 				i_mdi_pm_reset_client(ct);
 				break;
 			}
@@ -5503,13 +5662,13 @@
 			break;
 
 		case DDI_RESUME:
-			MDI_DEBUG(2, (CE_NOTE, dip,
-			    "!Client post_attach: called %p\n", (void *)ct));
+			MDI_DEBUG(2, (MDI_NOTE, dip,
+			    "client post_attach: called %p", (void *)ct));
 			if (error == DDI_SUCCESS) {
 				MDI_CLIENT_SET_RESUME(ct);
 			} else {
-				MDI_DEBUG(1, (CE_NOTE, dip,
-				    "!Client post_resume: failed error=%d\n",
+				MDI_DEBUG(1, (MDI_NOTE, dip,
+				    "!client post_resume failed: error %d",
 				    error));
 				MDI_CLIENT_SET_SUSPEND(ct);
 			}
@@ -5559,17 +5718,16 @@
 	MDI_PHCI_LOCK(ph);
 	switch (cmd) {
 	case DDI_DETACH:
-		MDI_DEBUG(2, (CE_NOTE, dip,
-		    "!pHCI pre_detach: called %p\n", (void *)ph));
+		MDI_DEBUG(2, (MDI_NOTE, dip,
+		    "pHCI pre_detach: called %p", (void *)ph));
 		if (!MDI_PHCI_IS_OFFLINE(ph)) {
 			/*
 			 * mdi_pathinfo nodes are still attached to
 			 * this pHCI. Fail the detach for this pHCI.
 			 */
-			MDI_DEBUG(2, (CE_WARN, dip,
-			    "!pHCI pre_detach: "
-			    "mdi_pathinfo nodes are still attached "
-			    "%p\n", (void *)ph));
+			MDI_DEBUG(2, (MDI_WARN, dip,
+			    "pHCI pre_detach: paths are still attached %p",
+			    (void *)ph));
 			rv = DDI_FAILURE;
 			break;
 		}
@@ -5584,8 +5742,8 @@
 		 * client devices before pHCI can be suspended.
 		 */
 
-		MDI_DEBUG(2, (CE_NOTE, dip,
-		    "!pHCI pre_suspend: called %p\n", (void *)ph));
+		MDI_DEBUG(2, (MDI_NOTE, dip,
+		    "pHCI pre_suspend: called %p", (void *)ph));
 		/*
 		 * Suspend all the client devices accessible through this pHCI
 		 */
@@ -5608,8 +5766,8 @@
 					 * Suspend of one of the client
 					 * device has failed.
 					 */
-					MDI_DEBUG(1, (CE_WARN, dip,
-					    "!Suspend of device (%s%d) failed.",
+					MDI_DEBUG(1, (MDI_WARN, dip,
+					    "!suspend of device (%s%d) failed.",
 					    ddi_driver_name(cdip),
 					    ddi_get_instance(cdip)));
 					failed_pip = pip;
@@ -5676,14 +5834,16 @@
 	MDI_CLIENT_LOCK(ct);
 	switch (cmd) {
 	case DDI_DETACH:
-		MDI_DEBUG(2, (CE_NOTE, dip,
-		    "!Client pre_detach: called %p\n", (void *)ct));
+		MDI_DEBUG(2, (MDI_NOTE, dip,
+		    "client pre_detach: called %p",
+		     (void *)ct));
 		MDI_CLIENT_SET_DETACH(ct);
 		break;
 
 	case DDI_SUSPEND:
-		MDI_DEBUG(2, (CE_NOTE, dip,
-		    "!Client pre_suspend: called %p\n", (void *)ct));
+		MDI_DEBUG(2, (MDI_NOTE, dip,
+		    "client pre_suspend: called %p",
+		    (void *)ct));
 		MDI_CLIENT_SET_SUSPEND(ct);
 		break;
 
@@ -5736,15 +5896,17 @@
 	 */
 	switch (cmd) {
 	case DDI_DETACH:
-		MDI_DEBUG(2, (CE_NOTE, dip,
-		    "!pHCI post_detach: called %p\n", (void *)ph));
+		MDI_DEBUG(2, (MDI_NOTE, dip,
+		    "pHCI post_detach: called %p",
+		    (void *)ph));
 		if (error != DDI_SUCCESS)
 			MDI_PHCI_SET_ATTACH(ph);
 		break;
 
 	case DDI_SUSPEND:
-		MDI_DEBUG(2, (CE_NOTE, dip,
-		    "!pHCI post_suspend: called %p\n", (void *)ph));
+		MDI_DEBUG(2, (MDI_NOTE, dip,
+		    "pHCI post_suspend: called %p",
+		    (void *)ph));
 		if (error != DDI_SUCCESS)
 			MDI_PHCI_SET_RESUME(ph);
 		break;
@@ -5769,14 +5931,14 @@
 	 */
 	switch (cmd) {
 	case DDI_DETACH:
-		MDI_DEBUG(2, (CE_NOTE, dip,
-		    "!Client post_detach: called %p\n", (void *)ct));
+		MDI_DEBUG(2, (MDI_NOTE, dip,
+		    "client post_detach: called %p", (void *)ct));
 		if (DEVI_IS_ATTACHING(ct->ct_dip)) {
-			MDI_DEBUG(4, (CE_NOTE, dip, "i_mdi_client_post_detach "
+			MDI_DEBUG(4, (MDI_NOTE, dip,
 			    "i_mdi_pm_rele_client\n"));
 			i_mdi_pm_rele_client(ct, ct->ct_path_count);
 		} else {
-			MDI_DEBUG(4, (CE_NOTE, dip, "i_mdi_client_post_detach "
+			MDI_DEBUG(4, (MDI_NOTE, dip,
 			    "i_mdi_pm_reset_client\n"));
 			i_mdi_pm_reset_client(ct);
 		}
@@ -5785,8 +5947,8 @@
 		break;
 
 	case DDI_SUSPEND:
-		MDI_DEBUG(2, (CE_NOTE, dip,
-		    "!Client post_suspend: called %p\n", (void *)ct));
+		MDI_DEBUG(2, (MDI_NOTE, dip,
+		    "called %p", (void *)ct));
 		if (error != DDI_SUCCESS)
 			MDI_CLIENT_SET_RESUME(ct);
 		break;
@@ -5926,18 +6088,19 @@
 {
 	mdi_phci_t	*ph;
 
-	ph = i_devi_get_phci(mdi_pi_get_phci(pip));
+	ph = MDI_PI(pip)->pi_phci;
 	if (ph == NULL) {
-		MDI_DEBUG(1, (CE_NOTE, NULL, "!mdi_pi_enable_path:"
-			" failed. pip: %p ph = NULL\n", (void *)pip));
+		MDI_DEBUG(1, (MDI_NOTE, mdi_pi_get_phci(pip),
+		    "!failed: path %s %p: NULL ph",
+		    mdi_pi_spathname(pip), (void *)pip));
 		return (MDI_FAILURE);
 	}
 
 	(void) i_mdi_enable_disable_path(pip, ph->ph_vhci, flags,
 		MDI_ENABLE_OP);
-	MDI_DEBUG(5, (CE_NOTE, NULL, "!mdi_pi_enable_path:"
-		" Returning success pip = %p. ph = %p\n",
-		(void *)pip, (void *)ph));
+	MDI_DEBUG(5, (MDI_NOTE, ph->ph_dip,
+	    "!returning success pip = %p. ph = %p",
+	    (void *)pip, (void *)ph));
 	return (MDI_SUCCESS);
 
 }
@@ -5952,18 +6115,19 @@
 {
 	mdi_phci_t	*ph;
 
-	ph = i_devi_get_phci(mdi_pi_get_phci(pip));
+	ph = MDI_PI(pip)->pi_phci;
 	if (ph == NULL) {
-		MDI_DEBUG(1, (CE_NOTE, NULL, "!mdi_pi_disable_path:"
-			" failed. pip: %p ph = NULL\n", (void *)pip));
+		MDI_DEBUG(1, (MDI_NOTE, mdi_pi_get_phci(pip),
+		    "!failed: path %s %p: NULL ph",
+		    mdi_pi_spathname(pip), (void *)pip));
 		return (MDI_FAILURE);
 	}
 
 	(void) i_mdi_enable_disable_path(pip,
-			ph->ph_vhci, flags, MDI_DISABLE_OP);
-	MDI_DEBUG(5, (CE_NOTE, NULL, "!mdi_pi_disable_path:"
-		"Returning success pip = %p. ph = %p",
-		(void *)pip, (void *)ph));
+	    ph->ph_vhci, flags, MDI_DISABLE_OP);
+	MDI_DEBUG(5, (MDI_NOTE, ph->ph_dip,
+	    "!returning success pip = %p. ph = %p",
+	    (void *)pip, (void *)ph));
 	return (MDI_SUCCESS);
 }
 
@@ -6035,8 +6199,8 @@
 			MDI_EXT_STATE_CHANGE | sync_flag |
 			op | MDI_BEFORE_STATE_CHANGE);
 		if (rv != MDI_SUCCESS) {
-			MDI_DEBUG(2, (CE_WARN, vh->vh_dip,
-			"!vo_pi_state_change: failed rv = %x", rv));
+			MDI_DEBUG(2, (MDI_WARN, vh->vh_dip,
+			    "vo_pi_state_change: failed rv = %x", rv));
 		}
 	}
 	MDI_PI_LOCK(pip);
@@ -6076,8 +6240,8 @@
 			MDI_EXT_STATE_CHANGE | sync_flag |
 			op | MDI_AFTER_STATE_CHANGE);
 		if (rv != MDI_SUCCESS) {
-			MDI_DEBUG(2, (CE_WARN, vh->vh_dip,
-			"!vo_pi_state_change: failed rv = %x", rv));
+			MDI_DEBUG(2, (MDI_WARN, vh->vh_dip,
+			    "vo_pi_state_change failed: rv = %x", rv));
 		}
 	}
 	return (next);
@@ -6099,18 +6263,18 @@
 	int		found_it;
 
 	ph = i_devi_get_phci(pdip);
-	MDI_DEBUG(5, (CE_NOTE, NULL, "!i_mdi_pi_enable_disable: "
-		"Op = %d pdip = %p cdip = %p\n", op, (void *)pdip,
-		(void *)cdip));
+	MDI_DEBUG(5, (MDI_NOTE, cdip ? cdip : pdip,
+	    "!op = %d pdip = %p cdip = %p", op, (void *)pdip,
+	    (void *)cdip));
 	if (ph == NULL) {
-		MDI_DEBUG(1, (CE_NOTE, NULL, "!i_mdi_pi_enable_disable:"
-			"Op %d failed. ph = NULL\n", op));
+		MDI_DEBUG(1, (MDI_NOTE, cdip ? cdip : pdip,
+		    "!failed: operation %d: NULL ph", op));
 		return (MDI_FAILURE);
 	}
 
 	if ((op != MDI_ENABLE_OP) && (op != MDI_DISABLE_OP)) {
-		MDI_DEBUG(1, (CE_NOTE, NULL, "!i_mdi_pi_enable_disable: "
-			"Op Invalid operation = %d\n", op));
+		MDI_DEBUG(1, (MDI_NOTE, cdip ? cdip : pdip,
+		    "!failed: invalid operation %d", op));
 		return (MDI_FAILURE);
 	}
 
@@ -6120,8 +6284,8 @@
 		/*
 		 * Need to mark the Phci as enabled/disabled.
 		 */
-		MDI_DEBUG(3, (CE_NOTE, NULL, "!i_mdi_pi_enable_disable: "
-		"Op %d for the phci\n", op));
+		MDI_DEBUG(4, (MDI_NOTE, cdip ? cdip : pdip,
+		    "op %d for the phci", op));
 		MDI_PHCI_LOCK(ph);
 		switch (flags) {
 			case USER_DISABLE:
@@ -6147,9 +6311,8 @@
 				break;
 			default:
 				MDI_PHCI_UNLOCK(ph);
-				MDI_DEBUG(1, (CE_NOTE, NULL,
-				"!i_mdi_pi_enable_disable:"
-				" Invalid flag argument= %d\n", flags));
+				MDI_DEBUG(1, (MDI_NOTE, cdip ? cdip : pdip,
+				    "!invalid flag argument= %d", flags));
 		}
 
 		/*
@@ -6168,9 +6331,8 @@
 		 */
 		ct = i_devi_get_client(cdip);
 		if (ct == NULL) {
-			MDI_DEBUG(1, (CE_NOTE, NULL,
-			"!i_mdi_pi_enable_disable:"
-			" failed. ct = NULL operation = %d\n", op));
+			MDI_DEBUG(1, (MDI_NOTE, cdip ? cdip : pdip,
+			    "!failed: operation = %d: NULL ct", op));
 			return (MDI_FAILURE);
 		}
 
@@ -6192,18 +6354,17 @@
 
 		MDI_CLIENT_UNLOCK(ct);
 		if (found_it == 0) {
-			MDI_DEBUG(1, (CE_NOTE, NULL,
-			"!i_mdi_pi_enable_disable:"
-			" failed. Could not find corresponding pip\n"));
+			MDI_DEBUG(1, (MDI_NOTE, cdip ? cdip : pdip,
+			    "!failed. Could not find corresponding pip\n"));
 			return (MDI_FAILURE);
 		}
 
 		(void) i_mdi_enable_disable_path(pip, vh, flags, op);
 	}
 
-	MDI_DEBUG(5, (CE_NOTE, NULL, "!i_mdi_pi_enable_disable: "
-		"Op %d Returning success pdip = %p cdip = %p\n",
-		op, (void *)pdip, (void *)cdip));
+	MDI_DEBUG(5, (MDI_NOTE, cdip ? cdip : pdip,
+	    "!op %d returning success pdip = %p cdip = %p",
+	    op, (void *)pdip, (void *)cdip));
 	return (MDI_SUCCESS);
 }
 
@@ -6223,19 +6384,17 @@
 	}
 
 	ph_dip = mdi_pi_get_phci(pip);
-	MDI_DEBUG(4, (CE_NOTE, ph_dip, "i_mdi_pm_hold_pip for %s%d %p\n",
-	    ddi_driver_name(ph_dip), ddi_get_instance(ph_dip), (void *)pip));
+	MDI_DEBUG(4, (MDI_NOTE, ph_dip,
+	    "%s %p", mdi_pi_spathname(pip), (void *)pip));
 	if (ph_dip == NULL) {
 		return;
 	}
 
 	MDI_PI_UNLOCK(pip);
-	MDI_DEBUG(4, (CE_NOTE, ph_dip, "kidsupcnt was %d\n",
+	MDI_DEBUG(4, (MDI_NOTE, ph_dip, "kidsupcnt was %d",
 	    DEVI(ph_dip)->devi_pm_kidsupcnt));
-
 	pm_hold_power(ph_dip);
-
-	MDI_DEBUG(4, (CE_NOTE, ph_dip, "kidsupcnt is %d\n",
+	MDI_DEBUG(4, (MDI_NOTE, ph_dip, "kidsupcnt is %d",
 	    DEVI(ph_dip)->devi_pm_kidsupcnt));
 	MDI_PI_LOCK(pip);
 
@@ -6262,17 +6421,17 @@
 	ph_dip = mdi_pi_get_phci(pip);
 	ASSERT(ph_dip != NULL);
 
+	MDI_DEBUG(4, (MDI_NOTE, ph_dip,
+	    "%s %p", mdi_pi_spathname(pip), (void *)pip));
+
 	MDI_PI_UNLOCK(pip);
-	MDI_DEBUG(4, (CE_NOTE, ph_dip, "i_mdi_pm_rele_pip for %s%d %p\n",
-	    ddi_driver_name(ph_dip), ddi_get_instance(ph_dip), (void *)pip));
-
-	MDI_DEBUG(4, (CE_NOTE, ph_dip, "kidsupcnt was %d\n",
-	    DEVI(ph_dip)->devi_pm_kidsupcnt));
+	MDI_DEBUG(4, (MDI_NOTE, ph_dip,
+	    "kidsupcnt was %d", DEVI(ph_dip)->devi_pm_kidsupcnt));
 	pm_rele_power(ph_dip);
-	MDI_DEBUG(4, (CE_NOTE, ph_dip, "kidsupcnt is %d\n",
-	    DEVI(ph_dip)->devi_pm_kidsupcnt));
-
+	MDI_DEBUG(4, (MDI_NOTE, ph_dip,
+	    "kidsupcnt is %d", DEVI(ph_dip)->devi_pm_kidsupcnt));
 	MDI_PI_LOCK(pip);
+
 	MDI_PI(pip)->pi_pm_held = 0;
 }
 
@@ -6282,9 +6441,9 @@
 	ASSERT(MDI_CLIENT_LOCKED(ct));
 
 	ct->ct_power_cnt += incr;
-	MDI_DEBUG(4, (CE_NOTE, ct->ct_dip, "i_mdi_pm_hold_client %p "
-	    "ct_power_cnt = %d incr = %d\n", (void *)ct,
-	    ct->ct_power_cnt, incr));
+	MDI_DEBUG(4, (MDI_NOTE, ct->ct_dip,
+	    "%p ct_power_cnt = %d incr = %d",
+	    (void *)ct, ct->ct_power_cnt, incr));
 	ASSERT(ct->ct_power_cnt >= 0);
 }
 
@@ -6312,8 +6471,8 @@
 
 	if (i_ddi_devi_attached(ct->ct_dip)) {
 		ct->ct_power_cnt -= decr;
-		MDI_DEBUG(4, (CE_NOTE, ct->ct_dip, "i_mdi_pm_rele_client %p "
-		    "ct_power_cnt = %d decr = %d\n",
+		MDI_DEBUG(4, (MDI_NOTE, ct->ct_dip,
+		    "%p ct_power_cnt = %d decr = %d",
 		    (void *)ct, ct->ct_power_cnt, decr));
 	}
 
@@ -6327,8 +6486,8 @@
 static void
 i_mdi_pm_reset_client(mdi_client_t *ct)
 {
-	MDI_DEBUG(4, (CE_NOTE, ct->ct_dip, "i_mdi_pm_reset_client %p "
-	    "ct_power_cnt = %d\n", (void *)ct, ct->ct_power_cnt));
+	MDI_DEBUG(4, (MDI_NOTE, ct->ct_dip,
+	    "%p ct_power_cnt = %d", (void *)ct, ct->ct_power_cnt));
 	ASSERT(MDI_CLIENT_LOCKED(ct));
 	ct->ct_power_cnt = 0;
 	i_mdi_rele_all_phci(ct);
@@ -6350,15 +6509,15 @@
 	MDI_PI_UNLOCK(pip);
 
 	/* bring all components of phci to full power */
-	MDI_DEBUG(4, (CE_NOTE, ph_dip, "i_mdi_power_one_phci "
-	    "pm_powerup for %s%d %p\n", ddi_driver_name(ph_dip),
+	MDI_DEBUG(4, (MDI_NOTE, ph_dip,
+	    "pm_powerup for %s%d %p", ddi_driver_name(ph_dip),
 	    ddi_get_instance(ph_dip), (void *)pip));
 
 	ret = pm_powerup(ph_dip);
 
 	if (ret == DDI_FAILURE) {
-		MDI_DEBUG(4, (CE_NOTE, ph_dip, "i_mdi_power_one_phci "
-		    "pm_powerup FAILED for %s%d %p\n",
+		MDI_DEBUG(4, (MDI_NOTE, ph_dip,
+		    "pm_powerup FAILED for %s%d %p",
 		    ddi_driver_name(ph_dip), ddi_get_instance(ph_dip),
 		    (void *)pip));
 
@@ -6456,10 +6615,10 @@
 	MDI_CLIENT_LOCK(ct);
 	switch (op) {
 	case BUS_POWER_PRE_NOTIFICATION:
-		MDI_DEBUG(4, (CE_NOTE, bpc->bpc_dip, "mdi_bus_power "
+		MDI_DEBUG(4, (MDI_NOTE, bpc->bpc_dip,
 		    "BUS_POWER_PRE_NOTIFICATION:"
-		    "%s@%s, olevel=%d, nlevel=%d, comp=%d\n",
-		    PM_NAME(bpc->bpc_dip), PM_ADDR(bpc->bpc_dip),
+		    "%s@%s, olevel=%d, nlevel=%d, comp=%d",
+		    ddi_node_name(bpc->bpc_dip), PM_ADDR(bpc->bpc_dip),
 		    bpc->bpc_olevel, bpc->bpc_nlevel, bpc->bpc_comp));
 
 		/* serialize power level change per client */
@@ -6480,17 +6639,17 @@
 		 */
 		if (bpc->bpc_nlevel > 0) {
 			if (!DEVI_IS_ATTACHING(ct->ct_dip)) {
-				MDI_DEBUG(4, (CE_NOTE, bpc->bpc_dip,
-				    "mdi_bus_power i_mdi_pm_hold_client\n"));
+				MDI_DEBUG(4, (MDI_NOTE, bpc->bpc_dip,
+				    "i_mdi_pm_hold_client\n"));
 				i_mdi_pm_hold_client(ct, ct->ct_path_count);
 			}
 		}
 		break;
 	case BUS_POWER_POST_NOTIFICATION:
-		MDI_DEBUG(4, (CE_NOTE, bpc->bpc_dip, "mdi_bus_power "
+		MDI_DEBUG(4, (MDI_NOTE, bpc->bpc_dip,
 		    "BUS_POWER_POST_NOTIFICATION:"
-		    "%s@%s, olevel=%d, nlevel=%d, comp=%d result=%d\n",
-		    PM_NAME(bpc->bpc_dip), PM_ADDR(bpc->bpc_dip),
+		    "%s@%s, olevel=%d, nlevel=%d, comp=%d result=%d",
+		    ddi_node_name(bpc->bpc_dip), PM_ADDR(bpc->bpc_dip),
 		    bpc->bpc_olevel, bpc->bpc_nlevel, bpc->bpc_comp,
 		    *(int *)result));
 
@@ -6505,21 +6664,21 @@
 		/* release the hold we did in pre-notification */
 		if (bpc->bpc_nlevel > 0 && (*(int *)result != DDI_SUCCESS) &&
 		    !DEVI_IS_ATTACHING(ct->ct_dip)) {
-			MDI_DEBUG(4, (CE_NOTE, bpc->bpc_dip,
-			    "mdi_bus_power i_mdi_pm_rele_client\n"));
+			MDI_DEBUG(4, (MDI_NOTE, bpc->bpc_dip,
+			    "i_mdi_pm_rele_client\n"));
 			i_mdi_pm_rele_client(ct, ct->ct_path_count);
 		}
 
 		if (bpc->bpc_nlevel == 0 && (*(int *)result == DDI_SUCCESS)) {
 			/* another thread might started attaching */
 			if (DEVI_IS_ATTACHING(ct->ct_dip)) {
-				MDI_DEBUG(4, (CE_NOTE, bpc->bpc_dip,
-				    "mdi_bus_power i_mdi_pm_rele_client\n"));
+				MDI_DEBUG(4, (MDI_NOTE, bpc->bpc_dip,
+				    "i_mdi_pm_rele_client\n"));
 				i_mdi_pm_rele_client(ct, ct->ct_path_count);
 			/* detaching has been taken care in pm_post_unconfig */
 			} else if (!DEVI_IS_DETACHING(ct->ct_dip)) {
-				MDI_DEBUG(4, (CE_NOTE, bpc->bpc_dip,
-				    "mdi_bus_power i_mdi_pm_reset_client\n"));
+				MDI_DEBUG(4, (MDI_NOTE, bpc->bpc_dip,
+				    "i_mdi_pm_reset_client\n"));
 				i_mdi_pm_reset_client(ct);
 			}
 		}
@@ -6531,10 +6690,10 @@
 
 	/* need to do more */
 	case BUS_POWER_HAS_CHANGED:
-		MDI_DEBUG(4, (CE_NOTE, bphc->bphc_dip, "mdi_bus_power "
+		MDI_DEBUG(4, (MDI_NOTE, bphc->bphc_dip,
 		    "BUS_POWER_HAS_CHANGED:"
-		    "%s@%s, olevel=%d, nlevel=%d, comp=%d\n",
-		    PM_NAME(bphc->bphc_dip), PM_ADDR(bphc->bphc_dip),
+		    "%s@%s, olevel=%d, nlevel=%d, comp=%d",
+		    ddi_node_name(bphc->bphc_dip), PM_ADDR(bphc->bphc_dip),
 		    bphc->bphc_olevel, bphc->bphc_nlevel, bphc->bphc_comp));
 
 		if (bphc->bphc_nlevel > 0 &&
@@ -6542,14 +6701,14 @@
 			if (ct->ct_power_cnt == 0) {
 				ret = i_mdi_power_all_phci(ct);
 			}
-			MDI_DEBUG(4, (CE_NOTE, bphc->bphc_dip,
-			    "mdi_bus_power i_mdi_pm_hold_client\n"));
+			MDI_DEBUG(4, (MDI_NOTE, bphc->bphc_dip,
+			    "i_mdi_pm_hold_client\n"));
 			i_mdi_pm_hold_client(ct, ct->ct_path_count);
 		}
 
 		if (bphc->bphc_nlevel == 0 && bphc->bphc_olevel != -1) {
-			MDI_DEBUG(4, (CE_NOTE, bphc->bphc_dip,
-			    "mdi_bus_power i_mdi_pm_rele_client\n"));
+			MDI_DEBUG(4, (MDI_NOTE, bphc->bphc_dip,
+			    "i_mdi_pm_rele_client\n"));
 			i_mdi_pm_rele_client(ct, ct->ct_path_count);
 		}
 		break;
@@ -6575,23 +6734,20 @@
 
 	if (!MDI_CLIENT_IS_FAILED(ct)) {
 		MDI_CLIENT_UNLOCK(ct);
-		MDI_DEBUG(4, (CE_NOTE, child,
-		    "i_mdi_pm_pre_config_one already configured\n"));
+		MDI_DEBUG(4, (MDI_NOTE, child, "already configured\n"));
 		return (MDI_SUCCESS);
 	}
 
 	if (ct->ct_powercnt_config) {
 		MDI_CLIENT_UNLOCK(ct);
-		MDI_DEBUG(4, (CE_NOTE, child,
-		    "i_mdi_pm_pre_config_one ALREADY held\n"));
+		MDI_DEBUG(4, (MDI_NOTE, child, "already held\n"));
 		return (MDI_SUCCESS);
 	}
 
 	if (ct->ct_power_cnt == 0) {
 		ret = i_mdi_power_all_phci(ct);
 	}
-	MDI_DEBUG(4, (CE_NOTE, child,
-	    "i_mdi_pm_pre_config_one i_mdi_pm_hold_client\n"));
+	MDI_DEBUG(4, (MDI_NOTE, child, "i_mdi_pm_hold_client\n"));
 	i_mdi_pm_hold_client(ct, ct->ct_path_count);
 	ct->ct_powercnt_config = 1;
 	ct->ct_powercnt_reset = 0;
@@ -6644,23 +6800,20 @@
 		cv_wait(&ct->ct_powerchange_cv, &ct->ct_mutex);
 
 	if (!i_ddi_devi_attached(ct->ct_dip)) {
-		MDI_DEBUG(4, (CE_NOTE, child,
-		    "i_mdi_pm_pre_unconfig node detached already\n"));
+		MDI_DEBUG(4, (MDI_NOTE, child, "node detached already\n"));
 		MDI_CLIENT_UNLOCK(ct);
 		return (MDI_SUCCESS);
 	}
 
 	if (MDI_CLIENT_IS_POWERED_DOWN(ct) &&
 	    (flags & NDI_AUTODETACH)) {
-		MDI_DEBUG(4, (CE_NOTE, child,
-		    "i_mdi_pm_pre_unconfig auto-modunload\n"));
+		MDI_DEBUG(4, (MDI_NOTE, child, "auto-modunload\n"));
 		MDI_CLIENT_UNLOCK(ct);
 		return (MDI_FAILURE);
 	}
 
 	if (ct->ct_powercnt_unconfig) {
-		MDI_DEBUG(4, (CE_NOTE, child,
-		    "i_mdi_pm_pre_unconfig ct_powercnt_held\n"));
+		MDI_DEBUG(4, (MDI_NOTE, child, "ct_powercnt_held\n"));
 		MDI_CLIENT_UNLOCK(ct);
 		*held = 1;
 		return (MDI_SUCCESS);
@@ -6669,8 +6822,7 @@
 	if (ct->ct_power_cnt == 0) {
 		ret = i_mdi_power_all_phci(ct);
 	}
-	MDI_DEBUG(4, (CE_NOTE, child,
-	    "i_mdi_pm_pre_unconfig i_mdi_pm_hold_client\n"));
+	MDI_DEBUG(4, (MDI_NOTE, child, "i_mdi_pm_hold_client\n"));
 	i_mdi_pm_hold_client(ct, ct->ct_path_count);
 	ct->ct_powercnt_unconfig = 1;
 	ct->ct_powercnt_reset = 0;
@@ -6728,16 +6880,14 @@
 		cv_wait(&ct->ct_powerchange_cv, &ct->ct_mutex);
 
 	if (ct->ct_powercnt_reset || !ct->ct_powercnt_config) {
-		MDI_DEBUG(4, (CE_NOTE, child,
-		    "i_mdi_pm_post_config_one NOT configured\n"));
+		MDI_DEBUG(4, (MDI_NOTE, child, "not configured\n"));
 		MDI_CLIENT_UNLOCK(ct);
 		return;
 	}
 
 	/* client has not been updated */
 	if (MDI_CLIENT_IS_FAILED(ct)) {
-		MDI_DEBUG(4, (CE_NOTE, child,
-		    "i_mdi_pm_post_config_one NOT configured\n"));
+		MDI_DEBUG(4, (MDI_NOTE, child, "client failed\n"));
 		MDI_CLIENT_UNLOCK(ct);
 		return;
 	}
@@ -6747,15 +6897,13 @@
 	    !DEVI_IS_ATTACHING(ct->ct_dip)) ||
 	    (!i_ddi_devi_attached(ct->ct_dip) &&
 	    !DEVI_IS_ATTACHING(ct->ct_dip))) {
-		MDI_DEBUG(4, (CE_NOTE, child,
-		    "i_mdi_pm_post_config i_mdi_pm_reset_client\n"));
+		MDI_DEBUG(4, (MDI_NOTE, child, "i_mdi_pm_reset_client\n"));
 		i_mdi_pm_reset_client(ct);
 	} else {
 		mdi_pathinfo_t  *pip, *next;
 		int	valid_path_count = 0;
 
-		MDI_DEBUG(4, (CE_NOTE, child,
-		    "i_mdi_pm_post_config i_mdi_pm_rele_client\n"));
+		MDI_DEBUG(4, (MDI_NOTE, child, "i_mdi_pm_rele_client\n"));
 		pip = ct->ct_path_head;
 		while (pip != NULL) {
 			MDI_PI_LOCK(pip);
@@ -6812,8 +6960,7 @@
 		cv_wait(&ct->ct_powerchange_cv, &ct->ct_mutex);
 
 	if (!ct->ct_powercnt_unconfig || ct->ct_powercnt_reset) {
-		MDI_DEBUG(4, (CE_NOTE, child,
-		    "i_mdi_pm_post_unconfig NOT held\n"));
+		MDI_DEBUG(4, (MDI_NOTE, child, "not held\n"));
 		MDI_CLIENT_UNLOCK(ct);
 		return;
 	}
@@ -6823,15 +6970,13 @@
 	    i_ddi_devi_attached(ct->ct_dip)) ||
 	    (!i_ddi_devi_attached(ct->ct_dip) &&
 	    !DEVI_IS_ATTACHING(ct->ct_dip))) {
-		MDI_DEBUG(4, (CE_NOTE, child,
-		    "i_mdi_pm_post_unconfig i_mdi_pm_reset_client\n"));
+		MDI_DEBUG(4, (MDI_NOTE, child, "i_mdi_pm_reset_client\n"));
 		i_mdi_pm_reset_client(ct);
 	} else {
 		mdi_pathinfo_t  *pip, *next;
 		int	valid_path_count = 0;
 
-		MDI_DEBUG(4, (CE_NOTE, child,
-		    "i_mdi_pm_post_unconfig i_mdi_pm_rele_client\n"));
+		MDI_DEBUG(4, (MDI_NOTE, child, "i_mdi_pm_rele_client\n"));
 		pip = ct->ct_path_head;
 		while (pip != NULL) {
 			MDI_PI_LOCK(pip);
@@ -6857,8 +7002,7 @@
 	ASSERT(MDI_VHCI(vdip));
 
 	if (!held) {
-		MDI_DEBUG(4, (CE_NOTE, vdip,
-		    "i_mdi_pm_post_unconfig held = %d\n", held));
+		MDI_DEBUG(4, (MDI_NOTE, vdip, "held = %d", held));
 		return;
 	}
 
@@ -6898,8 +7042,8 @@
 		client_dip = ndi_devi_findchild(vdip, devnm);
 	}
 
-	MDI_DEBUG(4, (CE_NOTE, vdip, "mdi_power op = %d %s %p\n",
-	    op, devnm ? devnm : "NULL", (void *)client_dip));
+	MDI_DEBUG(4, (MDI_NOTE, vdip,
+	    "op = %d %s %p", op, devnm ? devnm : "", (void *)client_dip));
 
 	switch (op) {
 	case MDI_PM_PRE_CONFIG:
@@ -6932,18 +7076,18 @@
 		if (op == MDI_PM_HOLD_POWER) {
 			if (ct->ct_power_cnt == 0) {
 				(void) i_mdi_power_all_phci(ct);
-				MDI_DEBUG(4, (CE_NOTE, client_dip,
-				    "mdi_power i_mdi_pm_hold_client\n"));
+				MDI_DEBUG(4, (MDI_NOTE, client_dip,
+				    "i_mdi_pm_hold_client\n"));
 				i_mdi_pm_hold_client(ct, ct->ct_path_count);
 			}
 		} else {
 			if (DEVI_IS_ATTACHING(ct->ct_dip)) {
-				MDI_DEBUG(4, (CE_NOTE, client_dip,
-				    "mdi_power i_mdi_pm_rele_client\n"));
+				MDI_DEBUG(4, (MDI_NOTE, client_dip,
+				    "i_mdi_pm_rele_client\n"));
 				i_mdi_pm_rele_client(ct, ct->ct_path_count);
 			} else {
-				MDI_DEBUG(4, (CE_NOTE, client_dip,
-				    "mdi_power i_mdi_pm_reset_client\n"));
+				MDI_DEBUG(4, (MDI_NOTE, client_dip,
+				    "i_mdi_pm_reset_client\n"));
 				i_mdi_pm_reset_client(ct);
 			}
 		}
@@ -7093,6 +7237,60 @@
 	}
 }
 
+int
+mdi_pi_ishidden(mdi_pathinfo_t *pip)
+{
+	return (MDI_PI_FLAGS_IS_HIDDEN(pip));
+}
+
+int
+mdi_pi_device_isremoved(mdi_pathinfo_t *pip)
+{
+	return (MDI_PI_FLAGS_IS_DEVICE_REMOVED(pip));
+}
+
+/*
+ * When processing hotplug, if mdi_pi_offline-mdi_pi_free fails then this
+ * interface is used to represent device removal.
+ */
+int
+mdi_pi_device_remove(mdi_pathinfo_t *pip)
+{
+	MDI_PI_LOCK(pip);
+	if (mdi_pi_device_isremoved(pip)) {
+		MDI_PI_UNLOCK(pip);
+		return (0);
+	}
+	MDI_PI_FLAGS_SET_DEVICE_REMOVED(pip);
+	MDI_PI_FLAGS_SET_HIDDEN(pip);
+	MDI_PI_UNLOCK(pip);
+
+	i_ddi_di_cache_invalidate();
+
+	return (1);
+}
+
+/*
+ * When processing hotplug, if a path marked mdi_pi_device_isremoved()
+ * is now accessible then this interfaces is used to represent device insertion.
+ */
+int
+mdi_pi_device_insert(mdi_pathinfo_t *pip)
+{
+	MDI_PI_LOCK(pip);
+	if (!mdi_pi_device_isremoved(pip)) {
+		MDI_PI_UNLOCK(pip);
+		return (0);
+	}
+	MDI_PI_FLAGS_CLR_DEVICE_REMOVED(pip);
+	MDI_PI_FLAGS_CLR_HIDDEN(pip);
+	MDI_PI_UNLOCK(pip);
+
+	i_ddi_di_cache_invalidate();
+
+	return (1);
+}
+
 /*
  * List of vhci class names:
  * A vhci class name must be in this list only if the corresponding vhci
@@ -7196,7 +7394,7 @@
 			vhcache->vhcache_flags |= MDI_VHCI_CACHE_SETUP_DONE;
 		else  {
 			cmn_err(CE_WARN,
-			    "%s: data file corrupted, will recreate\n",
+			    "%s: data file corrupted, will recreate",
 			    vhc->vhc_vhcache_filename);
 		}
 		rw_exit(&vhcache->vhcache_lock);
@@ -7269,7 +7467,7 @@
 	while ((vhc->vhc_flags & MDI_VHC_VHCACHE_FLUSH_THREAD) ||
 	    vhc->vhc_acc_thrcount != 0) {
 		mutex_exit(&vhc->vhc_lock);
-		delay(1);
+		delay_random(5);
 		mutex_enter(&vhc->vhc_lock);
 	}
 
@@ -7314,7 +7512,7 @@
 
 	while (vhc->vhc_flags & MDI_VHC_VHCACHE_FLUSH_THREAD) {
 		mutex_exit(&vhc->vhc_lock);
-		delay(1);
+		delay_random(5);
 		mutex_enter(&vhc->vhc_lock);
 	}
 
@@ -7542,9 +7740,9 @@
  *	...
  * where pi_addr1, pi_addr2, ... are bus specific addresses of pathinfo nodes
  * (so called pi_addrs, for example: "w2100002037cd9f72,0");
- * phci-ids are integers that identify PHCIs to which the
+ * phci-ids are integers that identify pHCIs to which the
  * the bus specific address belongs to. These integers are used as an index
- * into to the phcis string array in the main nvlist to get the PHCI path.
+ * into to the phcis string array in the main nvlist to get the pHCI path.
  */
 static int
 mainnvl_to_vhcache(mdi_vhci_cache_t *vhcache, nvlist_t *nvl)
@@ -8977,8 +9175,8 @@
 	 * the vhci node.
 	 */
 	if (DEVI_BUSY_OWNED(vdip)) {
-		MDI_DEBUG(2, (CE_NOTE, vdip, "!MDI: vhci bus config: "
-		    "vhci dip is busy owned %p\n", (void *)vdip));
+		MDI_DEBUG(2, (MDI_NOTE, vdip,
+		    "vhci dip is busy owned %p", (void *)vdip));
 		goto default_bus_config;
 	}
 
@@ -9063,10 +9261,10 @@
 		kmem_free(filename, strlen(filename) + 1);
 		return (nvl);
 	} else if (err == EIO)
-		cmn_err(CE_WARN, "%s: I/O error, will recreate\n", filename);
+		cmn_err(CE_WARN, "%s: I/O error, will recreate", filename);
 	else if (err == EINVAL)
 		cmn_err(CE_WARN,
-		    "%s: data file corrupted, will recreate\n", filename);
+		    "%s: data file corrupted, will recreate", filename);
 
 	kmem_free(filename, strlen(filename) + 1);
 	return (NULL);
@@ -9298,8 +9496,7 @@
 	return;
 
 alloc_failed:
-	MDI_DEBUG(1, (CE_WARN, dip,
-	    "!i_mdi_log_sysevent: Unable to send sysevent"));
+	MDI_DEBUG(1, (MDI_WARN, dip, "!unable to send sysevent"));
 }
 
 char **
--- a/usr/src/uts/common/os/sunndi.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/os/sunndi.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2174,6 +2174,12 @@
 	return ((DEVI(dip)->devi_node_attributes & DDI_HIDDEN_NODE) != 0);
 }
 
+int
+ndi_dev_is_hotplug_node(dev_info_t *dip)
+{
+	return ((DEVI(dip)->devi_node_attributes & DDI_HOTPLUG_NODE) != 0);
+}
+
 void
 ndi_devi_set_hidden(dev_info_t *dip)
 {
@@ -2556,15 +2562,15 @@
 void
 ndi_flavorv_set(dev_info_t *self, ndi_flavor_t child_flavor, void *v)
 {
-	ASSERT(child_flavor < DEVI(self)->devi_flavorv_n &&
-	    DEVI(self)->devi_flavorv != NULL);
-	if (child_flavor > DEVI(self)->devi_flavorv_n ||
-	    DEVI(self)->devi_flavorv == NULL) {
-		return;
-	}
 	if (child_flavor == NDI_FLAVOR_VANILLA) {
 		ddi_set_driver_private(self, v);
 	} else {
+		ASSERT(child_flavor < DEVI(self)->devi_flavorv_n &&
+		    DEVI(self)->devi_flavorv != NULL);
+		if (child_flavor > DEVI(self)->devi_flavorv_n ||
+		    DEVI(self)->devi_flavorv == NULL) {
+			return;
+		}
 		DEVI(self)->devi_flavorv[child_flavor - 1] = v;
 	}
 }
@@ -2572,15 +2578,15 @@
 void	*
 ndi_flavorv_get(dev_info_t *self, ndi_flavor_t child_flavor)
 {
-	ASSERT(child_flavor < DEVI(self)->devi_flavorv_n &&
-	    DEVI(self)->devi_flavorv != NULL);
-	if (child_flavor > DEVI(self)->devi_flavorv_n ||
-	    DEVI(self)->devi_flavorv == NULL) {
-		return (NULL);
-	}
 	if (child_flavor == NDI_FLAVOR_VANILLA) {
 		return (ddi_get_driver_private(self));
 	} else {
+		ASSERT(child_flavor < DEVI(self)->devi_flavorv_n &&
+		    DEVI(self)->devi_flavorv != NULL);
+		if (child_flavor > DEVI(self)->devi_flavorv_n ||
+		    DEVI(self)->devi_flavorv == NULL) {
+			return (NULL);
+		}
 		return (DEVI(self)->devi_flavorv[child_flavor - 1]);
 	}
 }
--- a/usr/src/uts/common/sys/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -150,6 +150,8 @@
 	cyclic_impl.h		\
 	dacf.h			\
 	dacf_impl.h		\
+	damap.h		\
+	damap_impl.h		\
 	dc_ki.h			\
 	ddi.h			\
 	ddifm.h			\
--- a/usr/src/uts/common/sys/autoconf.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/autoconf.h	Mon Oct 05 09:17:26 2009 -0400
@@ -265,7 +265,7 @@
 extern int i_ddi_io_initialized(void);
 extern dev_info_t *i_ddi_create_branch(dev_info_t *, int);
 extern void i_ddi_add_devimap(dev_info_t *dip);
-extern void i_ddi_di_cache_invalidate(int kmflag);
+extern void i_ddi_di_cache_invalidate(void);
 extern void i_ddi_di_cache_free(struct di_cache *cache);
 
 /* devname_state - for /dev to denote reconfig and system available */
--- a/usr/src/uts/common/sys/bitset.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/bitset.h	Mon Oct 05 09:17:26 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -72,6 +72,20 @@
 int		bitset_is_null(bitset_t *);
 uint_t		bitset_find(bitset_t *);
 
+/*
+ * Bitset computations
+ */
+int		bitset_and(bitset_t *, bitset_t *, bitset_t *);
+int		bitset_or(bitset_t *, bitset_t *, bitset_t *);
+int		bitset_xor(bitset_t *, bitset_t *, bitset_t *);
+
+/*
+ * Miscellaneous bitset operations
+ */
+void		bitset_zero(bitset_t *);
+void		bitset_copy(bitset_t *, bitset_t *);
+int		bitset_match(bitset_t *, bitset_t *);
+
 #endif	/* !_KERNEL && !_KMEMUSER */
 
 #ifdef	__cplusplus
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/damap.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,156 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_DAMAP_H
+#define	_SYS_DAMAP_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Delta (device) Address Map Interfaces
+ *
+ * These interfaces provide time-stablized sets of 'addresses',
+ * where addresses are string representations of device
+ * or bus-specific address.  The mechanisms include interfaces to
+ * report and remove address from a map, time stabilization, callouts
+ * to higher-level configuration and unconfiguration actions, and
+ * address lookup functions.
+ *
+ * Per Address Reports
+ * With per-address reporting, the caller reports the addition and removal
+ * each address visible to it. Each report is independently time stabilized;
+ * Once a report has stabilized, the reported address is either
+ * activated & configured, or unconfigured & released.
+ *
+ * Full Set Reports
+ * When using fullset reporting, the report provider enumerates the entire
+ * set of addresses visible to the provider at a given point in time.
+ * The entire set is then stabilized.
+ * Upon stabilizing, any newly reported addresses are activated & configured
+ * and any previously active addresses which are no longer visible are
+ * automatically unconfigured and released, freeing the provider from
+ * the need to explicitly unconfigure addresses no longer present.
+ *
+ * Stabilization
+ * Once an address has been reported (or reported as removed), the report
+ * is time stabilized before the framework initiates a configuration
+ * or unconfiguration action.  If the address is re-reported while undergoing
+ * stabilization, the timer is reset for either the address or the full
+ * set of addresses reported to the map.
+ *
+ * Activation/Release
+ * Once a reported address has passed its stabilization, the address is
+ * 'activated' by the framework.  Once activated, the address is passed
+ * to a configuration callout to perform whatever actions are necessary.
+ * If a reported address is deleted or fails to stabilize, the address
+ * is released by the map.
+ * A report provider may register callback functions to be invoked
+ * as part of the address activation & release process.  In addition to
+ * the callbacks, a provider can also supply a handle to provider-private
+ * data at the time an address is reported.  This handle is returned to
+ * provider as an argument to the activation & release callbacks.
+ *
+ * Lookup/Access
+ * The set of stable addresses contained in a map can be obtained by
+ * calling interfaces to lookup either a single address or the full
+ * list of stable addresses.
+ */
+
+/*
+ * damap_t:		Handle to a delta address map
+ * damap_id_t:  	Handle to an entry of damap_t
+ * damap_id_list_t:	List of damap_id_handles
+ */
+typedef struct __damap_dm *damap_t;
+typedef struct __damap_id_list *damap_id_list_t;
+typedef id_t damap_id_t;
+
+#define	NODAM (damap_id_t)0
+
+/*
+ * activate_cb:		Provider callback when reported address is activated
+ * deactivate_cb:	Provider callback when address has been released
+ *
+ * configure_cb:	Class callout to configure newly activated addresses
+ * unconfig_cb:		Class callout to unconfigure deactivated addresses
+ */
+typedef void (*damap_activate_cb_t)(void *, char *, int, void **);
+typedef void (*damap_deactivate_cb_t)(void *, char *, int, void *);
+
+typedef void (*damap_configure_cb_t)(void *, damap_t *, damap_id_list_t);
+typedef void (*damap_unconfig_cb_t)(void *, damap_t *, damap_id_list_t);
+
+/*
+ * Map reporting mode
+ */
+typedef enum {DAMAP_REPORT_PERADDR, DAMAP_REPORT_FULLSET} damap_rptmode_t;
+
+#define	DAMAP_RESET 1		/* flag to damap_addrset_end */
+
+int	damap_create(char *, size_t, damap_rptmode_t, clock_t,
+	    void *, damap_activate_cb_t, damap_deactivate_cb_t,
+	    void *, damap_configure_cb_t, damap_unconfig_cb_t,
+	    damap_t **);
+void	damap_destroy(damap_t *);
+
+char	*damap_name(damap_t *);
+int	damap_sync(damap_t *);
+
+int	damap_addr_add(damap_t *, char *, damap_id_t *, nvlist_t *, void *);
+int	damap_addr_del(damap_t *, char *);
+int	damap_addrid_del(damap_t *, int);
+
+int	damap_addrset_begin(damap_t *);
+int	damap_addrset_add(damap_t *, char *, damap_id_t *, nvlist_t *, void *);
+int	damap_addrset_end(damap_t *, int);
+int	damap_addrset_reset(damap_t *, int);
+
+damap_id_t	damap_id_next(damap_t *, damap_id_list_t, damap_id_t);
+char		*damap_id2addr(damap_t *, damap_id_t);
+nvlist_t	*damap_id2nvlist(damap_t *, damap_id_t);
+int		damap_id_hold(damap_t *, damap_id_t);
+void		damap_id_rele(damap_t *, damap_id_t);
+int		damap_id_ref(damap_t *, damap_id_t);
+void		damap_id_list_rele(damap_t *, damap_id_list_t);
+void		*damap_id_priv_get(damap_t *, damap_id_t);
+void		damap_id_priv_set(damap_t *, damap_id_t, void *);
+damap_id_t	damap_lookup(damap_t *, char *);
+int		damap_lookup_all(damap_t *, damap_id_list_t *);
+
+#define	DAM_SUCCESS	0
+#define	DAM_EEXIST	1
+#define	DAM_MAPFULL	2
+#define	DAM_EINVAL	3
+#define	DAM_FAILURE	4
+#define	DAM_SHAME	5
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_DAMAP_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/damap_impl.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,181 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_DAMAP_IMPL_H
+#define	_SYS_DAMAP_IMPL_H
+
+#include <sys/isa_defs.h>
+#include <sys/dditypes.h>
+#include <sys/time.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/ddi_implfuncs.h>
+#include <sys/ddi_isa.h>
+#include <sys/model.h>
+#include <sys/devctl.h>
+#include <sys/nvpair.h>
+#include <sys/sysevent.h>
+#include <sys/bitset.h>
+#include <sys/sdt.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct dam dam_t;
+
+/*
+ * activate_cb:		Provider callback when reported address is activated
+ * deactivate_cb:	Provider callback when address has been released
+ *
+ * configure_cb:	Class callout to configure newly activated addresses
+ * unconfig_cb:		Class callout to unconfigure deactivated addresses
+ */
+typedef void (*activate_cb_t)(void *, char *addr, int idx, void **privp);
+typedef void (*deactivate_cb_t)(void *, char *addr, int idx, void *priv);
+
+typedef void (*configure_cb_t)(void *, dam_t *mapp, bitset_t *config_ids);
+typedef void (*unconfig_cb_t)(void *, dam_t *mapp, bitset_t *unconfig_ids);
+
+struct dam {
+	char		*dam_name;
+	int		dam_flags;		/* state and locks */
+	int		dam_options;		/* options at map creation */
+	int		dam_rptmode;		/* report mode */
+	clock_t		dam_stabletmo;		/* stabilization (ticks) */
+	uint_t		dam_size;		/* max index for addr hash */
+	id_t		dam_high;		/* highest index allocated */
+	timeout_id_t	dam_tid;		/* timeout(9F) ID */
+
+	void		*dam_activate_arg;	/* activation private */
+	activate_cb_t	dam_activate_cb;	/* activation callback */
+	deactivate_cb_t	dam_deactivate_cb;	/* deactivation callback */
+
+	void		*dam_config_arg;	/* config-private */
+	configure_cb_t	dam_configure_cb;	/* configure callout */
+	unconfig_cb_t	dam_unconfig_cb;	/* unconfigure callout */
+
+	ddi_strid	*dam_addr_hash;		/* addresss to ID hash */
+	bitset_t	dam_active_set;		/* activated address set */
+	bitset_t	dam_stable_set;		/* stable address set */
+	bitset_t	dam_report_set;		/* reported address set */
+	void		*dam_da;		/* per-address soft state */
+	ddi_taskq_t	*dam_taskqp;		/* taskq for map activation */
+	hrtime_t	dam_last_update;	/* last map update */
+	hrtime_t	dam_last_stable;	/* last map stable */
+	int		dam_stable_cnt;		/* # of times map stabilized */
+	int		dam_stable_overrun;
+	kcondvar_t	dam_cv;
+	kmutex_t	dam_lock;
+	kstat_t		*dam_kstatsp;
+};
+
+/*
+ * damap.dam_flags
+ */
+#define	ADDR_LOCK		0x1000	/* per-addr lock */
+#define	MAP_LOCK		0x2000	/* global map lock */
+#define	DAM_LOCK(m, l)		{					\
+	mutex_enter(&(m)->dam_lock);					\
+	while ((m)->dam_flags & (l))					\
+		cv_wait(&(m)->dam_cv, &(m)->dam_lock);			\
+	(m)->dam_flags |= (l);						\
+	mutex_exit(&(m)->dam_lock);					\
+}
+#define	DAM_UNLOCK(m, l)	{					\
+	mutex_enter(&(m)->dam_lock);					\
+	(m)->dam_flags &= ~(l);						\
+	cv_signal(&(m)->dam_cv);					\
+	mutex_exit(&(m)->dam_lock);					\
+}
+#define	DAM_ASSERT_LOCKED(m, l) ASSERT((m)->dam_flags & (l))
+
+#define	DAM_SPEND		0x10	/* stable pending */
+#define	DAM_DESTROYPEND		0x20	/* in process of being destroyed */
+#define	DAM_SETADD		0x100	/* fullset update pending */
+#define	DAM_FLAG_SET(m, f)	{					\
+	mutex_enter(&(m)->dam_lock);					\
+	(m)->dam_flags |= f;						\
+	mutex_exit(&(m)->dam_lock);					\
+}
+#define	DAM_FLAG_CLR(m, f)	{					\
+	mutex_enter(&(m)->dam_lock);					\
+	(m)->dam_flags &= ~f;						\
+	mutex_exit(&(m)->dam_lock);					\
+}
+
+/*
+ * per address softstate stucture
+ */
+typedef struct {
+	uint_t		da_flags;	/* flags */
+	int		da_jitter;	/* address report count */
+	int		da_ref;		/* refcount on address */
+	void		*da_ppriv;	/* stable provider private */
+	void		*da_cfg_priv;	/* config/unconfig private */
+	nvlist_t	*da_nvl;	/* stable nvlist */
+	void		*da_ppriv_rpt;	/* reported provider-private */
+	nvlist_t	*da_nvl_rpt;	/* reported nvlist */
+	int64_t		da_deadline;	/* lbolt64 value when stable */
+	hrtime_t	da_last_report;	/* timestamp of last report */
+	int		da_report_cnt;	/* # of times address reported */
+	hrtime_t	da_last_stable;	/* timestamp of last stable address */
+	int		da_stable_cnt;	/* # of times address has stabilized */
+	char		*da_addr;	/* string in dam_addr_hash (for mdb) */
+} dam_da_t;
+
+/*
+ * dam_da_t.da_flags
+ */
+#define	DA_INIT			0x1	/* address initizized */
+#define	DA_ACTIVE		0x2	/* address stable */
+#define	DA_RELE			0x4	/* adddress released */
+
+
+/*
+ * report type
+ */
+#define	RPT_ADDR_ADD		0
+#define	RPT_ADDR_DEL		1
+
+#define	DAM_IN_REPORT(m, i)	(bitset_in_set(&(m)->dam_report_set, (i)))
+#define	DAM_IS_STABLE(m, i)	(bitset_in_set(&(m)->dam_active_set, (i)))
+
+/*
+ * DAM statistics
+ */
+struct dam_kstats {
+	struct kstat_named dam_stable;
+	struct kstat_named dam_stable_blocked;
+	struct kstat_named dam_rereport;
+	struct kstat_named dam_numstable;
+};
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_DAMAP_IMPL_H */
--- a/usr/src/uts/common/sys/ddi_impldefs.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/ddi_impldefs.h	Mon Oct 05 09:17:26 2009 -0400
@@ -40,6 +40,9 @@
 #include <sys/ddidmareq.h>
 #include <sys/ddi_intr.h>
 #include <sys/ddi_isa.h>
+#include <sys/id_space.h>
+#include <sys/modhash.h>
+#include <sys/bitset.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -105,7 +108,7 @@
 		} port;
 		uint64_t type64;
 	} info;
-	void	 *priv_p;
+	void	*priv_p;
 } devi_port_t;
 
 typedef struct devi_bus_priv {
@@ -145,7 +148,7 @@
 	struct dev_info *devi_next;	/* Next instance of this device */
 	kmutex_t devi_lock;		/* Protects per-devinfo data */
 
-	/* logical parents for busop primitives	 */
+	/* logical parents for busop primitives */
 
 	struct dev_info *devi_bus_map_fault;	/* bus_map_fault parent	 */
 	struct dev_info *devi_bus_dma_map;	/* bus_dma_map parent	 */
@@ -395,12 +398,13 @@
 
 #define	DEVI_SET_DEVICE_REMOVED(dip)	{				\
 	ASSERT(mutex_owned(&DEVI(dip)->devi_lock));			\
-	DEVI(dip)->devi_state |= DEVI_DEVICE_REMOVED;			\
+	DEVI(dip)->devi_state |= DEVI_DEVICE_REMOVED | DEVI_S_REPORT;	\
 	}
 
 #define	DEVI_SET_DEVICE_REINSERTED(dip)	{				\
 	ASSERT(mutex_owned(&DEVI(dip)->devi_lock));			\
 	DEVI(dip)->devi_state &= ~DEVI_DEVICE_REMOVED;			\
+	DEVI(dip)->devi_state |= DEVI_S_REPORT;				\
 	}
 
 /* Bus state change macros */
@@ -691,11 +695,14 @@
  *
  * DDI_HIDDEN_NODE indicates that the node should not show up in snapshots
  * or in /devices.
+ *
+ * DDI_HOTPLUG_NODE indicates that the node created by nexus hotplug.
  */
 #define	DDI_PERSISTENT			0x01
 #define	DDI_AUTO_ASSIGNED_NODEID	0x02
 #define	DDI_VHCI_NODE			0x04
 #define	DDI_HIDDEN_NODE			0x08
+#define	DDI_HOTPLUG_NODE		0x10
 
 #define	DEVI_VHCI_NODE(dip)						\
 	(DEVI(dip)->devi_node_attributes & DDI_VHCI_NODE)
@@ -741,16 +748,35 @@
 #define	sparc_pd_getintr(dev, n)	(&DEVI_PD(dev)->par_intr[(n)])
 #define	sparc_pd_getrng(dev, n)		(&DEVI_PD(dev)->par_rng[(n)])
 
+#ifdef _KERNEL
 /*
- * This data structure is entirely private to the soft state allocator.
+ * This data structure is private to the indexed soft state allocator.
  */
-struct i_ddi_soft_state {
+typedef struct i_ddi_soft_state {
 	void		**array;	/* the array of pointers */
-	kmutex_t	lock;	/* serialize access to this struct */
-	size_t		size;	/* how many bytes per state struct */
+	kmutex_t	lock;		/* serialize access to this struct */
+	size_t		size;		/* how many bytes per state struct */
 	size_t		n_items;	/* how many structs herein */
 	struct i_ddi_soft_state *next;	/* 'dirty' elements */
-};
+} i_ddi_soft_state;
+
+/*
+ * This data structure is private to the stringhashed soft state allocator.
+ */
+typedef struct i_ddi_soft_state_bystr {
+	size_t		ss_size;	/* how many bytes per state struct */
+	mod_hash_t	*ss_mod_hash;	/* hash implementation */
+} i_ddi_soft_state_bystr;
+
+/*
+ * This data structure is private to the ddi_strid_* implementation
+ */
+typedef struct i_ddi_strid {
+	id_space_t	*strid_space;
+	mod_hash_t	*strid_byid;
+	mod_hash_t	*strid_bystr;
+} i_ddi_strid;
+#endif /* _KERNEL */
 
 /*
  * Solaris DDI DMA implementation structure and function definitions.
--- a/usr/src/uts/common/sys/devctl.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/devctl.h	Mon Oct 05 09:17:26 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -41,7 +41,7 @@
  * and nexus driver devctl IOCTL interface.
  *
  * Applications and nexus drivers may not access the contents of this
- * structure directly.  Instead, drivers must use the ndi_dc_XXX(9n)
+ * structure directly.  Instead, drivers must use the ndi_dc_*(9n)
  * interfaces, while applications must use the interfaces provided by
  * libdevice.so.1.
  */
@@ -230,6 +230,9 @@
 #define	BUS_QUIESCED	0x20
 #define	BUS_SHUTDOWN	0x40
 
+#define	DEVICE_STATES_ASCII	"Dev_Online", "Dev_Busy", "Dev_Offline", \
+	"Dev_Down", "Bus_Active", "Bus_Quiesced", "Bus_Shutdown"
+
 #define	DC_DEVI_NODENAME	"ndi_dc.devi_nodename"
 
 #define	DEVCTL_CONSTRUCT	0x1
--- a/usr/src/uts/common/sys/devinfo_impl.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/devinfo_impl.h	Mon Oct 05 09:17:26 2009 -0400
@@ -317,6 +317,7 @@
 	uint_t		path_snap_state; /* describes valid fields */
 	int		path_instance;	/* path instance */
 	uint64_t 	user_private_data;
+	uint_t		path_flags;	/* path flags */
 };
 
 /*
@@ -331,6 +332,11 @@
 #define	DI_PATH_SNAP_LINKS	0x40	/* linkages have been postprocessed */
 
 /*
+ * Flags for path_flags
+ */
+#define	DI_PATH_FLAGS_DEVICE_REMOVED	0x01	/* peer of DI_DEVICE_REMOVED */
+
+/*
  * path properties
  */
 struct di_path_prop {
--- a/usr/src/uts/common/sys/fs/zfs.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/fs/zfs.h	Mon Oct 05 09:17:26 2009 -0400
@@ -692,7 +692,7 @@
 /*
  * Note: This is encoded on-disk, so new events must be added to the
  * end, and unused events can not be removed.  Be sure to edit
- * zpool_main.c: hist_event_table[].
+ * libzfs_pool.c: hist_event_table[].
  */
 typedef enum history_internal_events {
 	LOG_NO_EVENT = 0,
--- a/usr/src/uts/common/sys/hook_event.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/hook_event.h	Mon Oct 05 09:17:26 2009 -0400
@@ -76,7 +76,8 @@
 	NE_DOWN,
 	NE_ADDRESS_CHANGE,
 	NE_LIF_UP,
-	NE_LIF_DOWN
+	NE_LIF_DOWN,
+	NE_IFINDEX_CHANGE
 } nic_event_t;
 
 typedef void *nic_event_data_t;
--- a/usr/src/uts/common/sys/id_space.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/id_space.h	Mon Oct 05 09:17:26 2009 -0400
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_ID_SPACE_H
 #define	_ID_SPACE_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -47,6 +44,8 @@
 void id_space_extend(id_space_t *, id_t, id_t);
 id_t id_alloc(id_space_t *);
 id_t id_alloc_nosleep(id_space_t *);
+id_t id_allocff(id_space_t *);
+id_t id_allocff_nosleep(id_space_t *);
 void id_free(id_space_t *, id_t);
 
 #endif /* _KERNEL */
--- a/usr/src/uts/common/sys/lgrp.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/lgrp.h	Mon Oct 05 09:17:26 2009 -0400
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -295,6 +295,32 @@
 } lgrp_config_flag_t;
 
 /*
+ * Stages of lgroup framework initialization (done through lgrp_init()):
+ *
+ * 1) Initialize common and platform specific code (called in mlsetup())
+ *
+ * 2) Setup root lgroup and add CPU 0 to lgroup(s) (called near beginning of
+ *    main() before startup())
+ *
+ * 3) Probe from CPU 0 and copy and release any BOP_ALLOC-ed memory temporarily
+ *    allocated before kernel memory allocator is setup (called in main()
+ *    after startup(), gethrtime() is setup, and before interrupts enabled)
+ *
+ * 4) Check for null proc LPA on Starcat, collapse lgroup topology (if
+ *    necessary), setup lgroup kstats, etc. (called before start_other_cpus())
+ *
+ * 5) Finish any lgroup initialization needed including updating lgroup
+ *    topology after all CPUs started (called after start_other_cpus())
+ */
+typedef enum lgrp_init_stages {
+	LGRP_INIT_STAGE1,
+	LGRP_INIT_STAGE2,
+	LGRP_INIT_STAGE3,
+	LGRP_INIT_STAGE4,
+	LGRP_INIT_STAGE5
+} lgrp_init_stages_t;
+
+/*
  * Memory allocation policies
  */
 typedef enum lgrp_mem_policy {
@@ -510,8 +536,7 @@
  * lgroup management
  */
 int	lgrp_optimizations(void);
-void	lgrp_init(void);
-void	lgrp_setup(void);
+void	lgrp_init(lgrp_init_stages_t);
 lgrp_t	*lgrp_create(void);
 void	lgrp_destroy(lgrp_t *);
 void	lgrp_config(lgrp_config_flag_t, uintptr_t, uintptr_t);
@@ -588,8 +613,7 @@
 
 
 /* platform interfaces */
-void	lgrp_plat_init(void);
-void	lgrp_plat_main_init(void);
+void	lgrp_plat_init(lgrp_init_stages_t);
 lgrp_t	*lgrp_plat_alloc(lgrp_id_t lgrpid);
 void	lgrp_plat_config(lgrp_config_flag_t, uintptr_t);
 lgrp_handle_t	lgrp_plat_cpu_to_hand(processorid_t);
@@ -598,7 +622,6 @@
 pgcnt_t	lgrp_plat_mem_size(lgrp_handle_t, lgrp_mem_query_t);
 int	lgrp_plat_latency(lgrp_handle_t, lgrp_handle_t);
 lgrp_handle_t	lgrp_plat_root_hand(void);
-void	lgrp_plat_probe(void);
 
 extern uint32_t		lgrp_expand_proc_thresh;
 extern uint32_t		lgrp_expand_proc_diff;
--- a/usr/src/uts/common/sys/mdi_impldefs.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/mdi_impldefs.h	Mon Oct 05 09:17:26 2009 -0400
@@ -342,7 +342,7 @@
 	struct mdi_phci		*ph_next;	/* next pHCI link	*/
 	struct mdi_phci		*ph_prev;	/* prev pHCI link	*/
 	dev_info_t		*ph_dip;	/* pHCI devi handle	*/
-	struct mdi_vhci 	*ph_vhci;	/* pHCI back ref. to vHCI */
+	struct mdi_vhci		*ph_vhci;	/* pHCI back ref. to vHCI */
 
 	/* protected by MDI_PHCI_LOCK ph_mutex... */
 	kmutex_t		ph_mutex;	/* per-pHCI mutex	*/
@@ -398,7 +398,7 @@
 #define	MDI_PHCI_IS_READY(ph)						\
 	    (((ph)->ph_flags & MDI_PHCI_DISABLE_MASK) == 0)
 
-#define	MDI_PHCI_SET_OFFLINE(ph) 					{\
+#define	MDI_PHCI_SET_OFFLINE(ph)					{\
 	    ASSERT(MDI_PHCI_LOCKED(ph));				\
 	    (ph)->ph_flags |= MDI_PHCI_FLAGS_OFFLINE;			}
 #define	MDI_PHCI_SET_ONLINE(ph)						{\
@@ -407,7 +407,7 @@
 #define	MDI_PHCI_IS_OFFLINE(ph)						\
 	    ((ph)->ph_flags & MDI_PHCI_FLAGS_OFFLINE)
 
-#define	MDI_PHCI_SET_SUSPEND(ph) 					{\
+#define	MDI_PHCI_SET_SUSPEND(ph)					{\
 	    ASSERT(MDI_PHCI_LOCKED(ph));				\
 	    (ph)->ph_flags |= MDI_PHCI_FLAGS_SUSPEND;			}
 #define	MDI_PHCI_SET_RESUME(ph)						{\
@@ -501,7 +501,7 @@
  * OPTIMAL	- Client device has at least one redundant path.
  * DEGRADED	- No redundant paths (critical).  Failure in the current active
  *		  path would result in data access failures.
- * FAILED 	- No paths are available to access this device.
+ * FAILED	- No paths are available to access this device.
  *
  * Locking order:
  *
@@ -517,7 +517,7 @@
 	char			*ct_drvname;	/* client driver name	*/
 	char			*ct_guid;	/* client guid		*/
 	client_lb_t		ct_lb;		/* load balancing scheme */
-	client_lb_args_t	*ct_lb_args; 	/* load balancing args */
+	client_lb_args_t	*ct_lb_args;	/* load balancing args */
 
 
 	/* protected by MDI_CLIENT_LOCK ct_mutex... */
@@ -726,8 +726,9 @@
 	kcondvar_t		pi_ref_cv;	/* condition variable	*/
 	struct mdi_pi_kstats	*pi_kstats;	/* aggregate kstats */
 	int			pi_pm_held;	/* phci's kidsup incremented */
-	int			pi_preferred;	/* Preferred path 	*/
+	int			pi_preferred;	/* Preferred path	*/
 	void			*pi_vprivate;	/* vhci private info	*/
+	uint_t			pi_flags;	/* path flags */
 };
 
 /*
@@ -959,6 +960,26 @@
 #define	MDI_PI_IS_SUSPENDED(pip)					\
 	((MDI_PI(pip))->pi_phci->ph_flags & MDI_PHCI_FLAGS_SUSPEND)
 
+#define	MDI_PI_FLAGS_SET_HIDDEN(pip)					{\
+	ASSERT(MDI_PI_LOCKED(pip));					\
+	MDI_PI(pip)->pi_flags |= MDI_PATHINFO_FLAGS_HIDDEN;		}
+#define	MDI_PI_FLAGS_CLR_HIDDEN(pip)					{\
+	ASSERT(MDI_PI_LOCKED(pip));					\
+	MDI_PI(pip)->pi_flags &= ~MDI_PATHINFO_FLAGS_HIDDEN;		}
+#define	MDI_PI_FLAGS_IS_HIDDEN(pip)					\
+	((MDI_PI(pip)->pi_flags & MDI_PATHINFO_FLAGS_HIDDEN) ==		\
+	MDI_PATHINFO_FLAGS_HIDDEN)
+
+#define	MDI_PI_FLAGS_SET_DEVICE_REMOVED(pip)				{\
+	ASSERT(MDI_PI_LOCKED(pip));					\
+	MDI_PI(pip)->pi_flags |= MDI_PATHINFO_FLAGS_DEVICE_REMOVED;	}
+#define	MDI_PI_FLAGS_CLR_DEVICE_REMOVED(pip)				{\
+	ASSERT(MDI_PI_LOCKED(pip));					\
+	MDI_PI(pip)->pi_flags &= ~MDI_PATHINFO_FLAGS_DEVICE_REMOVED;	}
+#define	MDI_PI_FLAGS_IS_DEVICE_REMOVED(pip)				\
+	((MDI_PI(pip)->pi_flags & MDI_PATHINFO_FLAGS_DEVICE_REMOVED) ==	\
+	MDI_PATHINFO_FLAGS_DEVICE_REMOVED)
+
 /*
  * mdi_vhcache_client, mdi_vhcache_pathinfo, and mdi_vhcache_phci structures
  * hold the vhci to phci client mappings of the on-disk vhci busconfig cache.
--- a/usr/src/uts/common/sys/scsi/adapters/mpapi_impl.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/scsi/adapters/mpapi_impl.h	Mon Oct 05 09:17:26 2009 -0400
@@ -19,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _SYS_SCSI_ADAPTERS_MPAPI_IMPL_H
 #define	_SYS_SCSI_ADAPTERS_MPAPI_IMPL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/sunmdi.h>
 #include <sys/sunddi.h>
 #include <sys/mdi_impldefs.h>
@@ -253,6 +251,7 @@
 #define	MP_DRVR_PATH_STATE_REMOVED		5
 #define	MP_DRVR_PATH_STATE_TRANSITIONING	6
 #define	MP_DRVR_PATH_STATE_UNKNOWN		7
+#define	MP_DRVR_PATH_STATE_UNINIT		8
 
 
 /* Structure for MP_PROPRIETARY_LOAD_BALANCE_PROPERTIES */
--- a/usr/src/uts/common/sys/scsi/adapters/mpapi_scsi_vhci.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/scsi/adapters/mpapi_scsi_vhci.h	Mon Oct 05 09:17:26 2009 -0400
@@ -19,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _SYS_SCSI_ADAPTERS_MPAPI_SCSI_VHCI_H
 #define	_SYS_SCSI_ADAPTERS_MPAPI_SCSI_VHCI_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -110,11 +108,18 @@
 
 /*
  * Structure to maintain mpapi path data.
+ *
+ * The hide flag is set when pip was detroyed or should
+ * have been destroyed(MDI_PATHINFO_FLAGS_DEVICE_REMOVED).
+ * The valid flag is set to 0 when the path is neither online
+ * nor standby state. When hide flag is set the valid flag set
+ * to 0 also.
  */
 typedef struct mpapi_path_data {
 	void			*resp; /* pip */
 	char			*path_name;
 	int			valid;
+	int			hide;
 	char			pclass[MPAPI_SCSI_MAXPCLASSLEN];
 	mp_path_prop_t		prop;
 } mpapi_path_data_t;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/ata.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,279 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Misc ATA definitions
+ */
+#ifndef	_ATA_H
+#define	_ATA_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include "ata8-acs.h"
+#include "atapi7v3.h"
+
+/*
+ * IDENTIFY Data
+ */
+typedef struct {
+	uint16_t	word0;
+	uint16_t	word1;
+	uint16_t	word2;
+	uint16_t	word3;
+	uint16_t	word4;
+	uint16_t	word5;
+	uint16_t	word6;
+	uint16_t	word7;
+	uint16_t	word8;
+	uint16_t	word9;
+	uint16_t	serial_number[10];
+	uint16_t	word20;
+	uint16_t	word21;
+	uint16_t	word22;
+	uint16_t	firmware_revision[4];
+	uint16_t	model_number[20];
+	uint16_t	word47;
+	uint16_t	word48;
+	uint16_t	word49;
+	uint16_t	word50;
+	uint16_t	word51;
+	uint16_t	word52;
+	uint16_t	word53;
+	uint16_t	word54;
+	uint16_t	word55;
+	uint16_t	word56;
+	uint16_t	word57;
+	uint16_t	word58;
+	uint16_t	word59;
+	uint16_t	word60;
+	uint16_t	word61;
+	uint16_t	word62;
+	uint16_t	word63;
+	uint16_t	word64;
+	uint16_t	word65;
+	uint16_t	word66;
+	uint16_t	word67;
+	uint16_t	word68;
+	uint16_t	word69;
+	uint16_t	word70;
+	uint16_t	word71;
+	uint16_t	word72;
+	uint16_t	word73;
+	uint16_t	word74;
+	uint16_t	word75;
+	uint16_t	word76;
+	uint16_t	word77;
+	uint16_t	word78;
+	uint16_t	word79;
+	uint16_t	word80;
+	uint16_t	word81;
+	uint16_t	word82;
+	uint16_t	word83;
+	uint16_t	word84;
+	uint16_t	word85;
+	uint16_t	word86;
+	uint16_t	word87;
+	uint16_t	word88;
+	uint16_t	word89;
+	uint16_t	word90;
+	uint16_t	word91;
+	uint16_t	word92;
+	uint16_t	word93;
+	uint16_t	word94;
+	uint16_t	word95;
+	uint16_t	word96;
+	uint16_t	word97;
+	uint16_t	word98;
+	uint16_t	word99;
+	uint16_t	word100;
+	uint16_t	word101;
+	uint16_t	word102;
+	uint16_t	word103;
+	uint16_t	word104;
+	uint16_t	word105;
+	uint16_t	word106;
+	uint16_t	word107;
+	uint16_t	word108;
+	uint16_t	word109;
+	uint16_t	word110;
+	uint16_t	word111;
+	uint16_t	word112;
+	uint16_t	word113;
+	uint16_t	word114;
+	uint16_t	word115;
+	uint16_t	word116;
+	uint16_t	word117;
+	uint16_t	word118;
+	uint16_t	word119;
+	uint16_t	word120;
+	uint16_t	word121;
+	uint16_t	word122;
+	uint16_t	word123;
+	uint16_t	word124;
+	uint16_t	word125;
+	uint16_t	word126;
+	uint16_t	word127;
+	uint16_t	word128;
+	uint16_t	word129;
+	uint16_t	word130;
+	uint16_t	word131;
+	uint16_t	word132;
+	uint16_t	word133;
+	uint16_t	word134;
+	uint16_t	word135;
+	uint16_t	word136;
+	uint16_t	word137;
+	uint16_t	word138;
+	uint16_t	word139;
+	uint16_t	word140;
+	uint16_t	word141;
+	uint16_t	word142;
+	uint16_t	word143;
+	uint16_t	word144;
+	uint16_t	word145;
+	uint16_t	word146;
+	uint16_t	word147;
+	uint16_t	word148;
+	uint16_t	word149;
+	uint16_t	word150;
+	uint16_t	word151;
+	uint16_t	word152;
+	uint16_t	word153;
+	uint16_t	word154;
+	uint16_t	word155;
+	uint16_t	word156;
+	uint16_t	word157;
+	uint16_t	word158;
+	uint16_t	word159;
+	uint16_t	word160;
+	uint16_t	word161;
+	uint16_t	word162;
+	uint16_t	word163;
+	uint16_t	word164;
+	uint16_t	word165;
+	uint16_t	word166;
+	uint16_t	word167;
+	uint16_t	word168;
+	uint16_t	word169;
+	uint16_t	word170;
+	uint16_t	word171;
+	uint16_t	word172;
+	uint16_t	word173;
+	uint16_t	word174;
+	uint16_t	word175;
+	uint16_t	word176;
+	uint16_t	word177;
+	uint16_t	word178;
+	uint16_t	word179;
+	uint16_t	word180;
+	uint16_t	word181;
+	uint16_t	word182;
+	uint16_t	word183;
+	uint16_t	word184;
+	uint16_t	word185;
+	uint16_t	word186;
+	uint16_t	word187;
+	uint16_t	word188;
+	uint16_t	word189;
+	uint16_t	word190;
+	uint16_t	word191;
+	uint16_t	word192;
+	uint16_t	word193;
+	uint16_t	word194;
+	uint16_t	word195;
+	uint16_t	word196;
+	uint16_t	word197;
+	uint16_t	word198;
+	uint16_t	word199;
+	uint16_t	word200;
+	uint16_t	word201;
+	uint16_t	word202;
+	uint16_t	word203;
+	uint16_t	word204;
+	uint16_t	word205;
+	uint16_t	word206;
+	uint16_t	word207;
+	uint16_t	word208;
+	uint16_t	word209;
+	uint16_t	word210;
+	uint16_t	word211;
+	uint16_t	word212;
+	uint16_t	word213;
+	uint16_t	word214;
+	uint16_t	word215;
+	uint16_t	word216;
+	uint16_t	word217;
+	uint16_t	word218;
+	uint16_t	word219;
+	uint16_t	word220;
+	uint16_t	word221;
+	uint16_t	word222;
+	uint16_t	word223;
+	uint16_t	word224;
+	uint16_t	word225;
+	uint16_t	word226;
+	uint16_t	word227;
+	uint16_t	word228;
+	uint16_t	word229;
+	uint16_t	word230;
+	uint16_t	word231;
+	uint16_t	word232;
+	uint16_t	word233;
+	uint16_t	word234;
+	uint16_t	word235;
+	uint16_t	word236;
+	uint16_t	word237;
+	uint16_t	word238;
+	uint16_t	word239;
+	uint16_t	word240;
+	uint16_t	word241;
+	uint16_t	word242;
+	uint16_t	word243;
+	uint16_t	word244;
+	uint16_t	word245;
+	uint16_t	word246;
+	uint16_t	word247;
+	uint16_t	word248;
+	uint16_t	word249;
+	uint16_t	word250;
+	uint16_t	word251;
+	uint16_t	word252;
+	uint16_t	word253;
+	uint16_t	word254;
+	uint16_t	word255;
+} ata_identify_t;
+
+#define	LBA_CAPACITY(ati)						\
+	((LE_16(ati->word83) & (1 << 10)) == 0)?			\
+	(LE_16(ati->word60) | ((LE_16(ati->word61)) << 16)) :		\
+	((LE_16(ati->word100)) | ((LE_16(ati->word101)) << 16) |	\
+	(((uint64_t)LE_16(ati->word102)) << 32) |			\
+	(((uint64_t)LE_16(ati->word103)) << 48))
+
+
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _ATA_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/ata8-acs.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,122 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * ATA8-ACS Definitions (subset) Working Draft AT Attachment 8 - ATA/ATAPI
+ * Command Set (D1699r4c)
+ */
+#ifndef	_ATA8_ACS_H
+#define	_ATA8_ACS_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * ATA Command Set
+ */
+enum ata_opcode {
+	ATA_NOP					= 0x00,
+	CFA_REQUEST_EXTENDED_ERROR		= 0x03,
+	DEVICE_RESET				= 0x08,
+	READ_SECTORS				= 0x20,
+	READ_SECTORS_EXT			= 0x24,
+	READ_DMA_EXT				= 0x25,
+	READ_DMA_QUEUED_EXT			= 0x26,
+	READ_NATIVE_MAX_ADDRESS_EXT		= 0x27,
+	READ_MULTIPLE_EXT			= 0x29,
+	READ_STREAM_DMA_EXT			= 0x2A,
+	READ_STREAM_EXT				= 0x2B,
+	READ_LOG_EXT				= 0x2F,
+	WRITE_SECTORS				= 0x30,
+	WRITE_SECTORS_EXT			= 0x34,
+	WRITE_DMA_EXT				= 0x35,
+	WRITE_DMA_QUEUED_EXT			= 0x36,
+	SET_MAX_ADDRESS_EXT			= 0x37,
+	CFA_WRITE_SECTORS_WITHOUT_ERASE		= 0x38,
+	WRITE_MULTIPLE_EXT			= 0x39,
+	WRITE_STREAM_DMA_EXT			= 0x3A,
+	WRITE_STREAM_EXT			= 0x3B,
+	WRITE_DMA_FUA_EXT			= 0x3D,
+	WRITE_DMA_QUEUED_FUA_EXT		= 0x3E,
+	WRITE_LOG_EXT				= 0x3F,
+	READ_VERIFY_SECTORS			= 0x40,
+	READ_VERIFY_SECTORS_EXT			= 0x42,
+	WRITE_UNCORRECTABLE_EXT			= 0x45,
+	READ_LOG_DMA_EXT			= 0x47,
+	CONFIGURE_STREAM			= 0x51,
+	WRITE_LOG_DMA_EXT			= 0x57,
+	TRUSTED_NON_DATA			= 0x5B,
+	TRUSTED_RECEIVE				= 0x5C,
+	TRUSTED_RECEIVE_DMA			= 0x5D,
+	TRUSTED_SEND				= 0x5E,
+	TRUSTED_SEND_DMA			= 0x5E,
+	READ_FPDMA_QUEUED			= 0x60,
+	WRITE_FPDMA_QUEUED			= 0x61,
+	CFA_TRANSLATE_SECTOR			= 0x87,
+	EXECUTE_DEVICE_DIAGNOSTIC		= 0x90,
+	DOWNLOAD_MICROCODE			= 0x92,
+	PACKET					= 0xA0,
+	IDENTIFY_PACKET_DEVICE			= 0xA1,
+	SERVICE					= 0xA2,
+	SMART					= 0xB0,
+	DEVICE_CONFIGURATION_OVERLAY		= 0xB1,
+	NV_CACHE				= 0xB6,
+	CFA_ERASE_SECTORS			= 0xC0,
+	READ_MULTIPLE				= 0xC4,
+	WRITE_MULTIPLE				= 0xC5,
+	SET_MULTIPLE_MODE			= 0xC6,
+	READ_DMA_QUEUED				= 0xC7,
+	READ_DMA				= 0xC8,
+	WRITE_DMA				= 0xCA,
+	WRITE_DMA_QUEUED			= 0xCC,
+	CFA_WRITE_MULTIPLE_WITHOUT_ERASE	= 0xCD,
+	WRITE_MULTIPLE_FUA_EXT			= 0xCE,
+	CHECK_MEDIA_CARD_TYPE			= 0xD1,
+	STANDBY_IMMEDIATE			= 0xE0,
+	IDLE_IMMEDIATE				= 0xE1,
+	STANDBY					= 0xE2,
+	IDLE					= 0xE3,
+	ATA_READ_BUFFER				= 0xE4,
+	CHECK_POWER_MODE			= 0xE5,
+	SLEEP					= 0xE6,
+	FLUSH_CACHE				= 0xE7,
+	ATA_WRITE_BUFFER			= 0xE8,
+	FLUSH_CACHE_EXT				= 0xEA,
+	IDENTIFY_DEVICE				= 0xEC,
+	MEDIA_EJECT				= 0xED,
+	SET_FEATURES				= 0xEF,
+	SECURITY_SET_PASSWORD			= 0xF1,
+	SECURITY_UNLOCK				= 0xF2,
+	SECURITY_ERASE_PREPARE			= 0xF3,
+	SECURITY_ERASE_UNIT			= 0xF4,
+	SECURITY_FREEZE_LOCK			= 0xF5,
+	SECURITY_DISABLE_PASSWORD		= 0xF6,
+	READ_NATIVE_MAX_ADDRESS			= 0xF8,
+	SET_MAX_ADDRESS				= 0xF9
+};
+
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _ATA8_ACS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/atapi7v3.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,228 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * ATAPI-7 Definitions (subset) that include Serial ATA
+ * ATA/ATAPI-7 V3 (d1532v3r4b-ATA-ATAPI-7)
+ */
+#ifndef	_ATAPI7V3_H
+#define	_ATAPI7V3_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Register - Host to Device FIS
+ */
+typedef struct {
+	uint8_t 	fis_type;
+	uint8_t 	idcbits;
+	uint8_t		cmd;
+	uint8_t		features;
+#define	FEATURE_LBA	0x40
+	uint8_t		lba_low;
+	uint8_t		lba_mid;
+	uint8_t		lba_hi;
+	uint8_t		device;
+	uint8_t		lba_low_exp;
+	uint8_t		lba_mid_exp;
+	uint8_t		lba_hi_exp;
+	uint8_t		features_exp;
+	uint8_t		sector_count;
+	uint8_t		sector_count_exp;
+	uint8_t		reserved0;
+	uint8_t		control;
+	uint8_t		reserved1[4];
+} register_h2d_fis_t;
+
+/*
+ * Register - Device to Host FIS
+ */
+typedef struct {
+	uint8_t		fis_type;
+	uint8_t		idcbits;
+	uint8_t		status;
+	uint8_t		error;
+	uint8_t		lba_low;
+	uint8_t		lba_mid;
+	uint8_t		lba_hi;
+	uint8_t		device;
+	uint8_t		lba_low_exp;
+	uint8_t		lba_mid_exp;
+	uint8_t		lba_hi_exp;
+	uint8_t		reserved0;
+	uint8_t		sector_count;
+	uint8_t		sector_count_exp;
+	uint8_t		reserved1[6];
+} register_d2h_fis_t;
+
+typedef struct {
+	uint8_t		fis_type;
+	uint8_t		idcbits;
+	uint8_t		status_bits;
+#define	STATUS_HI_MASK	0xE
+#define	STATUS_HI_SHIFT	4
+#define	STATUS_LO_MASK	0x7
+	uint8_t		error;
+	uint8_t		reserved;
+} set_device_bits_fis_t;
+
+typedef struct {
+	uint8_t		fis_type;
+	uint8_t		reserved[3];
+} dma_activate_fis_type;
+
+typedef struct {
+	uint8_t		fis_type;
+	uint8_t		idcbits;
+	uint8_t		reserved0[2];
+	uint32_t	dma_buffer_id_lo;
+	uint32_t	dma_buffer_id_hi;
+	uint32_t	reserved1;
+	uint32_t	dma_buffer_offset;
+	uint32_t	dma_buffer_count;
+	uint32_t	reserved2;
+} dma_fpactivate_fis_t;
+
+typedef struct {
+	uint8_t		fis_type;
+	uint8_t		reserved0;
+	uint8_t		bist_bits;
+	uint8_t		reserved1;
+	uint8_t		data[8];
+} bist_activate_fis_t;
+#define	BIST_T	0x80
+#define	BIST_A	0x40
+#define	BIST_S	0x20
+#define	BIST_L	0x10
+#define	BIST_F	0x08
+#define	BIST_P	0x04
+#define	BIST_V	0x01
+
+typedef struct {
+	uint8_t		fis_type;
+	uint8_t		idcbits;
+	uint8_t		status;
+	uint8_t		error;
+	uint8_t		lba_low;
+	uint8_t		lba_mid;
+	uint8_t		lba_high;
+	uint8_t		device;
+	uint8_t		lba_low_exp;
+	uint8_t		lba_mid_exp;
+	uint8_t		lba_high_exp;
+	uint8_t		reserved0;
+	uint8_t		sector_count;
+	uint8_t		sector_count_exp;
+	uint8_t		reserved1;
+	uint8_t		E_status;
+	uint16_t	transfer_count;
+	uint16_t	reserved2;
+} pio_setup_fis_t;
+
+typedef struct {
+	uint8_t		fis_type;
+	uint32_t	dwords[1];
+} bidirectional_fis_t;
+
+/*
+ * FIS Types
+ */
+
+#define	FIS_REG_H2DEV		0x27	/* 5 DWORDS */
+#define	FIS_REG_D2H		0x34	/* 5 DWORDS */
+#define	FIS_SET_DEVICE_BITS	0xA1	/* 2 DWORDS */
+#define	FIS_DMA_ACTIVATE	0x39	/* 1 DWORD */
+#define	FIS_DMA_FPSETUP		0x41	/* 7 DWORDS */
+#define	FIS_BIST_ACTIVATE	0x58	/* 3 DWORDS */
+#define	FIS_PIO_SETUP		0x5F	/* 5 DWORDS */
+#define	FIS_BI			0x46	/* 1 DWORD min, 2048 DWORD max */
+
+/*
+ * IDC bits
+ */
+#define	C_BIT	0x80
+#define	I_BIT	0x40
+#define	D_BIT	0x20
+
+/*
+ * 28-Bit Command Mapping from ACS to FIS
+ *
+ * ACS Field       	FIS Field
+ * --------------------------------------
+ * Feature (7:0)	-> Feature
+ * Count (7:0)		-> Sector Count
+ * LBA (7:0)		-> LBA Low
+ * LBA (15:8)		-> LBA Mid
+ * LBA (23:16)		-> LBA High
+ * LBA (27:24)		-> Device (3:0)
+ * Device (15:12)	-> Device (7:4)
+ * Command		-> Command
+ *
+ * 48- Bit Command Mapping from ACS to FIS
+ *
+ * ACS Field       	FIS Field
+ * --------------------------------------
+ * Feature (7:0)	-> Feature
+ * Feature (15:8)	-> Feature (exp)
+ * Count (7:0)		-> Sector Count
+ * Count (15:8)		-> Sector Count (exp)
+ * LBA (7:0)		-> LBA Low
+ * LBA (15:8)		-> LBA Mid
+ * LBA (23:16)		-> LBA High
+ * LBA (31:24)		-> LBA Low (exp)
+ * LBA (39:32)		-> LBA Mid (exp)
+ * LBA (47:40)		-> LBA High (exp)
+ * Device (15:12)	-> Device (7:4)
+ * Command		-> Command
+ *
+ * FIS (FIS_REG_H2DEV) layout:
+ *
+ * 31.........24 23...........16 15....................8.7.............0
+ * FEATURE	| COMMAND	| C R R RESERVED	| FIS TYPE 0x27
+ * DEVICE   	| LBA HIGH	| LBA MID		| LBA LOW
+ * FEATURE(exp)	| LBA HIGH(exp)	| LBA MID(exp)		| LBA LOW(exp)
+ * CONTROL	| RESERVED	| Sector Count(exp)	| Sector Count
+ * RESERVED 	| RESERVED	| RESERVED		| RESERVED
+ *
+ * FIS (FIS_REG_D2H) layout:
+ *
+ * 31.........24 23...........16 15....................8.7.............0
+ * ERROR        | STATUS        | R I R RESERVED	| FIS TYPE 0x34
+ * DEVICE   	| LBA HIGH	| LBA MID		| LBA LOW
+ * RESERVED	| LBA HIGH(exp)	| LBA MID(exp)		| LBA LOW(exp)
+ * RESERVED 	| RESERVED	| Sector Count(exp)	| Sector Count
+ * RESERVED 	| RESERVED	| RESERVED		| RESERVED
+ */
+
+
+/*
+ * Reasonable size to reserve for holding the most common FIS types.
+ */
+typedef	uint32_t fis_t[5];
+
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _ATAPI7V3_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,627 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is the principle header file for the PMCS driver
+ */
+#ifndef _PMCS_H
+#define	_PMCS_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+
+#include <sys/cpuvar.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/modctl.h>
+#include <sys/pci.h>
+#include <sys/pcie.h>
+#include <sys/isa_defs.h>
+#include <sys/sunmdi.h>
+#include <sys/mdi_impldefs.h>
+#include <sys/scsi/scsi.h>
+#include <sys/scsi/impl/scsi_reset_notify.h>
+#include <sys/scsi/impl/sas_transport.h>
+#include <sys/scsi/generic/sas.h>
+#include <sys/atomic.h>
+#include <sys/byteorder.h>
+#include <sys/bitmap.h>
+#include <sys/queue.h>
+#include <sys/sdt.h>
+#include <sys/ddifm.h>
+#include <sys/fm/protocol.h>
+#include <sys/fm/util.h>
+#include <sys/fm/io/ddi.h>
+#include <sys/scsi/impl/spc3_types.h>
+
+typedef struct pmcs_hw pmcs_hw_t;
+typedef struct pmcs_iport pmcs_iport_t;
+typedef struct pmcs_phy pmcs_phy_t;
+typedef struct lsas_cmd lsas_cmd_t;
+typedef struct lsas_result lsas_result_t;
+typedef struct lsata_cmd lsata_cmd_t;
+typedef struct lsata_result lsata_result_t;
+typedef struct pmcwork pmcwork_t;
+typedef struct pmcs_cmd pmcs_cmd_t;
+typedef	struct pmcs_xscsi pmcs_xscsi_t;
+typedef	struct pmcs_lun pmcs_lun_t;
+typedef struct pmcs_chunk pmcs_chunk_t;
+
+#include <sys/scsi/adapters/pmcs/pmcs_param.h>
+#include <sys/scsi/adapters/pmcs/pmcs_reg.h>
+#include <sys/scsi/adapters/pmcs/pmcs_mpi.h>
+#include <sys/scsi/adapters/pmcs/pmcs_iomb.h>
+#include <sys/scsi/adapters/pmcs/pmcs_sgl.h>
+
+#include <sys/scsi/adapters/pmcs/smp_defs.h>
+#include <sys/scsi/adapters/pmcs/ata.h>
+#include <sys/scsi/adapters/pmcs/pmcs_def.h>
+#include <sys/scsi/adapters/pmcs/pmcs_proto.h>
+#include <sys/scsi/adapters/pmcs/pmcs_scsa.h>
+#include <sys/scsi/adapters/pmcs/pmcs_smhba.h>
+
+#define	PMCS_MAX_UA_SIZE	32
+
+struct pmcs_xscsi {
+	uint32_t
+		ca		:	1,		/* SATA specific */
+		ncq		:	1,		/* SATA specific */
+		pio		:	1,		/* SATA specific */
+		special_needed	:	1,		/* SATA specific */
+		special_running	:	1,		/* SATA specific */
+		reset_success	:	1,		/* last reset ok */
+		reset_wait	:	1,		/* wait for reset */
+		resetting	:	1,		/* now resetting */
+		recover_wait	:	1,		/* wait for recovery */
+		recovering	:	1,		/* now recovering */
+		event_recovery	:	1,		/* event recovery */
+		draining	:	1,
+		dying		:	1,
+		new		:	1,
+		assigned	:	1,
+		dev_gone	:	1,
+		phy_addressable	:	1,		/* Direct attach SATA */
+		dev_state	:	4;
+	uint16_t			maxdepth;
+	uint16_t			qdepth;
+	uint16_t			actv_cnt;
+	uint16_t			target_num;
+	/* statlock protects both target stats and the special queue (sq) */
+	kmutex_t			statlock;
+	int32_t				ref_count;
+	dev_info_t 			*dip;	/* Solaris device dip */
+	pmcs_phy_t			*phy;
+	STAILQ_HEAD(wqh, pmcs_cmd)	wq;
+	pmcs_cmd_t			*wq_recovery_tail;	/* See below */
+	kmutex_t			wqlock;
+	STAILQ_HEAD(aqh, pmcs_cmd)	aq;
+	kmutex_t			aqlock;
+	STAILQ_HEAD(sqh, pmcs_cmd)	sq;		/* SATA specific */
+	uint32_t			tagmap;		/* SATA specific */
+	pmcs_hw_t			*pwp;
+	ddi_soft_state_bystr		*lun_sstate;
+	uint64_t			capacity;	/* SATA specific */
+	char				unit_address[PMCS_MAX_UA_SIZE];
+	kcondvar_t			reset_cv;
+	kcondvar_t			abort_cv;
+	char				*ua;
+	pmcs_dtype_t			dtype;
+};
+
+/*
+ * wq_recovery_tail in the pmcs_xscsi structure is a pointer to a command in
+ * the wait queue (wq).  That pointer is the last command in the wait queue
+ * that needs to be reissued after device state recovery is complete.  Commands
+ * that need to be retried are reinserted into the wq after wq_recovery_tail
+ * to maintain the order in which the commands were originally submitted.
+ */
+
+#define	PMCS_INVALID_TARGET_NUM		(uint16_t)-1
+
+#define	PMCS_TGT_WAIT_QUEUE		0x01
+#define	PMCS_TGT_ACTIVE_QUEUE		0x02
+#define	PMCS_TGT_SPECIAL_QUEUE		0x04
+#define	PMCS_TGT_ALL_QUEUES		0xff
+
+/*
+ * LUN representation.  Just a LUN (number) and pointer to the target
+ * structure (pmcs_xscsi).
+ */
+
+struct pmcs_lun {
+	pmcs_xscsi_t	*target;
+	uint64_t	lun_num;	/* lun64 */
+	scsi_lun_t	scsi_lun;	/* Wire format */
+	char		unit_address[PMCS_MAX_UA_SIZE];
+};
+
+/*
+ * Interrupt coalescing values
+ */
+#define	PMCS_MAX_IO_COMPS_PER_INTR	12
+#define	PMCS_MAX_IO_COMPS_HIWAT_SHIFT	6
+#define	PMCS_MAX_IO_COMPS_LOWAT_SHIFT	10
+#define	PMCS_QUANTUM_TIME_USECS		(1000000 / 10)	/* 1/10th sec. */
+#define	PMCS_MAX_COAL_TIMER		0x200	/* Don't set > than this */
+#define	PMCS_MAX_CQ_THREADS		4
+#define	PMCS_COAL_TIMER_GRAN		2	/* Go up/down by 2 usecs */
+#define	PMCS_INTR_THRESHOLD(x)		((x) * 6 / 10)
+
+/*
+ * This structure is used to maintain state with regard to I/O interrupt
+ * coalescing.
+ */
+
+typedef struct pmcs_io_intr_coal_s {
+	hrtime_t	nsecs_between_intrs;
+	hrtime_t	last_io_comp;
+	clock_t		quantum;
+	uint32_t	num_io_completions;
+	uint32_t	num_intrs;
+	uint32_t	max_io_completions;
+	uint32_t	intr_latency;
+	uint32_t	intr_threshold;
+	uint16_t	intr_coal_timer;
+	boolean_t	timer_on;
+	boolean_t	stop_thread;
+	boolean_t	int_cleared;
+} pmcs_io_intr_coal_t;
+
+typedef struct pmcs_cq_thr_info_s {
+	kthread_t	*cq_thread;
+	kmutex_t	cq_thr_lock;
+	kcondvar_t	cq_cv;
+	pmcs_hw_t	*cq_pwp;
+} pmcs_cq_thr_info_t;
+
+typedef struct pmcs_cq_info_s {
+	uint32_t		cq_threads;
+	uint32_t		cq_next_disp_thr;
+	boolean_t		cq_stop;
+	pmcs_cq_thr_info_t	*cq_thr_info;
+} pmcs_cq_info_t;
+
+typedef struct pmcs_iocomp_cb_s {
+	pmcwork_t		*pwrk;
+	char			iomb[PMCS_QENTRY_SIZE << 1];
+	struct pmcs_iocomp_cb_s	*next;
+} pmcs_iocomp_cb_t;
+
+typedef struct pmcs_iqp_trace_s {
+	char		*head;
+	char		*curpos;
+	uint32_t	size_left;
+} pmcs_iqp_trace_t;
+
+/*
+ * Used by string-based softstate as hint to possible size.
+ */
+
+#define	PMCS_TGT_SSTATE_SZ		64
+#define	PMCS_LUN_SSTATE_SZ		4
+
+/*
+ * HBA iport node softstate
+ */
+#define	PMCS_IPORT_INVALID_PORT_ID	0xffff
+
+struct pmcs_iport {
+	kmutex_t	lock;		/* iport lock */
+	list_node_t	list_node;	/* list node for pwp->iports list_t */
+	kmutex_t	refcnt_lock;	/* refcnt lock */
+	kcondvar_t	refcnt_cv;	/* refcnt cv */
+	int		refcnt;		/* refcnt for this iport */
+	dev_info_t	*dip;		/* iport dip */
+	pmcs_hw_t	*pwp;		/* back pointer to HBA state */
+	pmcs_phy_t	*pptr;		/* pointer to this port's primary phy */
+	enum {				/* unit address state in the phymap */
+		UA_INACTIVE,
+		UA_PEND_ACTIVATE,
+		UA_ACTIVE,
+		UA_PEND_DEACTIVATE
+	} ua_state;
+	char		*ua;		/* unit address (phy mask) */
+	int		portid;		/* portid */
+	int		report_skip;	/* skip or report during discovery */
+	list_t		phys;		/* list of phys on this port */
+	int		nphy;		/* number of phys in this port */
+	scsi_hba_tgtmap_t	*iss_tgtmap;	/* tgtmap */
+	ddi_soft_state_bystr	*tgt_sstate;	/* tgt softstate */
+};
+
+struct pmcs_chunk {
+	pmcs_chunk_t		*next;
+	ddi_acc_handle_t	acc_handle;
+	ddi_dma_handle_t	dma_handle;
+	uint8_t			*addrp;
+	uint64_t		dma_addr;
+};
+
+/*
+ * HBA node (i.e. non-iport) softstate
+ */
+struct pmcs_hw {
+	/*
+	 * Identity
+	 */
+	dev_info_t	*dip;
+
+	/*
+	 * 16 possible initiator PHY WWNs
+	 */
+	uint64_t	sas_wwns[PMCS_MAX_PORTS];
+
+	/*
+	 * Card State
+	 */
+	enum pwpstate {
+		STATE_NIL,
+		STATE_PROBING,
+		STATE_RUNNING,
+		STATE_UNPROBING,
+		STATE_DEAD
+	} state;
+
+	uint32_t
+		fw_disable_update	: 1,
+		fw_force_update		: 1,
+		blocked			: 1,
+		stuck			: 1,
+		locks_initted		: 1,
+		mpi_table_setup		: 1,
+		hba_attached		: 1,
+		iports_attached		: 1,
+		suspended		: 1,
+		separate_ports		: 1,
+		fwlog			: 4,
+		phymode			: 3,
+		physpeed		: 3,
+		resource_limited	: 1,
+		configuring		: 1,
+		ds_err_recovering	: 1;
+
+	/*
+	 * This HBA instance's iportmap and list of iport states.
+	 * Note: iports_lock protects iports, iports_attached, and
+	 * num_iports on the HBA softstate.
+	 */
+	krwlock_t		iports_lock;
+	scsi_hba_iportmap_t	*hss_iportmap;
+	list_t			iports;
+	int			num_iports;
+
+	sas_phymap_t		*hss_phymap;
+	int			phymap_active;
+
+	/*
+	 * Locks
+	 */
+	kmutex_t	lock;
+	kmutex_t	dma_lock;
+	kmutex_t	axil_lock;
+	kcondvar_t	drain_cv;
+
+	/*
+	 * FMA Capabilities
+	 */
+	int		fm_capabilities;
+
+	/*
+	 * Register Access Handles
+	 */
+	ddi_device_acc_attr_t 	dev_acc_attr;
+	ddi_device_acc_attr_t	reg_acc_attr;
+	ddi_acc_handle_t 	pci_acc_handle;
+	ddi_acc_handle_t 	msg_acc_handle;
+	ddi_acc_handle_t 	top_acc_handle;
+	ddi_acc_handle_t	mpi_acc_handle;
+	ddi_acc_handle_t	gsm_acc_handle;
+	ddi_acc_handle_t	iqp_acchdls[PMCS_MAX_IQ];
+	ddi_acc_handle_t	oqp_acchdls[PMCS_MAX_IQ];
+	ddi_acc_handle_t	cip_acchdls;
+	ddi_acc_handle_t	fwlog_acchdl;
+	ddi_acc_handle_t	regdump_acchdl;
+
+	/*
+	 * DMA Handles
+	 */
+	ddi_dma_attr_t		iqp_dma_attr;
+	ddi_dma_attr_t		oqp_dma_attr;
+	ddi_dma_attr_t		cip_dma_attr;
+	ddi_dma_attr_t		fwlog_dma_attr;
+	ddi_dma_attr_t		regdump_dma_attr;
+	ddi_dma_handle_t 	iqp_handles[PMCS_MAX_IQ];
+	ddi_dma_handle_t 	oqp_handles[PMCS_MAX_OQ];
+	ddi_dma_handle_t	cip_handles;
+	ddi_dma_handle_t	fwlog_hndl;
+	ddi_dma_handle_t	regdump_hndl;
+
+	/*
+	 * Register Pointers
+	 */
+	uint32_t	*msg_regs;	/* message unit registers */
+	uint32_t	*top_regs;	/* top unit registers */
+	uint32_t	*mpi_regs;	/* message passing unit registers */
+	uint32_t	*gsm_regs;	/* GSM registers */
+
+	/*
+	 * Message Passing and other offsets.
+	 *
+	 * mpi_offset is the offset within the fourth register set (mpi_regs)
+	 * that contains the base of the MPI structures. Since this is actually
+	 * set by the card firmware, it can change from startup to startup.
+	 *
+	 * The other offsets (gst, iqc, oqc) are for similar tables in
+	 * MPI space, typically only accessed during setup.
+	 */
+	uint32_t	mpi_offset;
+	uint32_t	mpi_gst_offset;
+	uint32_t	mpi_iqc_offset;
+	uint32_t	mpi_oqc_offset;
+
+	/*
+	 * Inbound and outbound queue depth
+	 */
+	uint32_t	ioq_depth;
+
+	/*
+	 * Kernel addresses and offsets for Inbound Queue Producer Indices
+	 *
+	 * See comments in pmcs_iomb.h about Inbound Queues. Since it
+	 * is relatively expensive to go across the PCIe bus to read or
+	 * write inside the card, we maintain shadow copies in kernel
+	 * memory and update the card as needed.
+	 */
+	uint32_t	shadow_iqpi[PMCS_MAX_IQ];
+	uint32_t	iqpi_offset[PMCS_MAX_IQ];
+	uint32_t	*iqp[PMCS_MAX_IQ];
+	kmutex_t	iqp_lock[PMCS_NIQ];
+
+	pmcs_iqp_trace_t	*iqpt;
+
+	/*
+	 * Kernel addresses and offsets for Outbound Queue Consumer Indices
+	 */
+	uint32_t	*oqp[PMCS_MAX_OQ];
+	uint32_t	oqci_offset[PMCS_MAX_OQ];
+
+	/*
+	 * Driver's copy of the outbound queue indices
+	 */
+
+	uint32_t	oqci[PMCS_NOQ];
+	uint32_t	oqpi[PMCS_NOQ];
+
+	/*
+	 * DMA addresses for both Inbound and Outbound queues.
+	 */
+	uint64_t	oqaddr[PMCS_MAX_OQ];
+	uint64_t	iqaddr[PMCS_MAX_IQ];
+
+	/*
+	 * Producer/Queue Host Memory Pointers and scratch areas,
+	 * as well as DMA scatter/gather chunk areas.
+	 *
+	 * See discussion in pmcs_def.h about how this is laid out.
+	 */
+	uint8_t		*cip;
+	uint64_t	ciaddr;
+
+	/*
+	 * Scratch area pointer and DMA addrress for SATA and SMP operations.
+	 */
+	void			*scratch;
+	uint64_t		scratch_dma;
+	volatile uint8_t	scratch_locked;	/* Scratch area ownership */
+
+	/*
+	 * Firmware log pointer
+	 */
+	uint32_t	*fwlogp;
+	uint64_t	fwaddr;
+
+	/*
+	 * Internal register dump region and flash chunk DMA info
+	 */
+
+	caddr_t		regdumpp;
+	uint32_t	*flash_chunkp;
+	uint64_t	flash_chunk_addr;
+
+	/*
+	 * Card information, some determined during MPI setup
+	 */
+	uint32_t	fw;		/* firmware version */
+	uint8_t		max_iq;		/* maximum inbound queues this card */
+	uint8_t 	max_oq;		/* "" outbound "" */
+	uint8_t		nphy;		/* number of phys this card */
+	uint8_t		chiprev;	/* chip revision */
+	uint16_t	max_cmd;	/* max number of commands supported */
+	uint16_t	max_dev;	/* max number of devices supported */
+	uint16_t	last_wq_dev;	/* last dev whose wq was serviced */
+
+
+	/*
+	 * Interrupt Setup stuff.
+	 *
+	 * int_type defines the kind of interrupt we're using with this card.
+	 * oqvec defines the relationship between an Outbound Queue Number and
+	 * a MSI-X vector.
+	 */
+	enum {
+		PMCS_INT_NONE,
+		PMCS_INT_TIMER,
+		PMCS_INT_MSI,
+		PMCS_INT_MSIX,
+		PMCS_INT_FIXED
+	} int_type;
+	uint8_t			oqvec[PMCS_NOQ];
+
+	/*
+	 * Interrupt handle table and size
+	 */
+	ddi_intr_handle_t	*ih_table;
+	size_t			ih_table_size;
+
+	timeout_id_t		wdhandle;
+	uint32_t		intr_mask;
+	int			intr_cnt;
+	int			intr_cap;
+	uint32_t		odb_auto_clear;
+
+	/*
+	 * DMA S/G chunk list
+	 */
+	int		nchunks;
+	pmcs_chunk_t	*dma_chunklist;
+
+	/*
+	 * Front of the DMA S/G chunk freelist
+	 */
+	pmcs_dmachunk_t	*dma_freelist;
+
+	/*
+	 * PHY and Discovery Related Stuff
+	 *
+	 * The PMC chip can have up to 16 local phys. We build a level-first
+	 * traversal tree of phys starting with the physical phys on the
+	 * chip itself (i.e., treating the chip as if it were an expander).
+	 *
+	 * Our discovery process goes through a level and discovers what
+	 * each entity is (and it's phy number within that expander's
+	 * address space). It then configures each non-empty item (SAS,
+	 * SATA/STP, EXPANDER). For expanders, it then performs
+	 * discover on that expander itself via REPORT GENERAL and
+	 * DISCOVERY SMP commands, attaching the discovered entities
+	 * to the next level. Then we step down a level and continue
+	 * (and so on).
+	 *
+	 * The PMC chip maintains an I_T_NEXUS notion based upon our
+	 * registering each new device found (getting back a device handle).
+	 *
+	 * Like with the number of physical PHYS being a maximum of 16,
+	 * there are a maximum number of PORTS also being 16. Some
+	 * events apply to PORTS entirely, so we track PORTS as well.
+	 */
+	pmcs_phy_t		*root_phys;	/* HBA PHYs (level 0) */
+	pmcs_phy_t		*ports[PMCS_MAX_PORTS];
+	kmutex_t		dead_phylist_lock;	/* Protects dead_phys */
+	pmcs_phy_t		*dead_phys;	/* PHYs waiting to be freed */
+
+	kmem_cache_t		*phy_cache;
+
+	/*
+	 * Discovery-related items.
+	 * config_lock: Protects config_changed and should never be held
+	 * outside of getting or setting the value of config_changed.
+	 * config_changed: Boolean indicating whether discovery needs to
+	 * be restarted.
+	 * configuring: 1 = discovery is running, 0 = discovery not running.
+	 * NOTE: configuring is now in the bitfield above.
+	 */
+	kmutex_t		config_lock;
+	volatile boolean_t	config_changed;
+
+	/*
+	 * Work Related Stuff
+	 *
+	 * Each command given to the PMC chip has an associated work structure.
+	 * See the discussion in pmcs_def.h about work structures.
+	 */
+	pmcwork_t	*work;		/* pool of work structures */
+	STAILQ_HEAD(wfh, pmcwork) wf;	/* current freelist */
+	STAILQ_HEAD(pfh, pmcwork) pf;	/* current pending freelist */
+	uint16_t	wserno;		/* rolling serial number */
+	kmutex_t	wfree_lock;	/* freelist/actvlist/wserno lock */
+	kmutex_t	pfree_lock;	/* freelist/actvlist/wserno lock */
+
+	/*
+	 * Solaris/SCSA items.
+	 */
+	scsi_hba_tran_t		*tran;
+	sas_hba_tran_t		*smp_tran;
+	struct scsi_reset_notify_entry	*reset_notify_listf;
+
+	/*
+	 * Thread Level stuff.
+	 *
+	 * A number of tasks are done off worker thread taskq.
+	 */
+	ddi_taskq_t 		*tq;		/* For the worker thread */
+	volatile ulong_t	work_flags;
+
+	/*
+	 * Solaris target representation.
+	 * targets = array of pointers to xscsi structures
+	 * allocated by ssoftstate.
+	 */
+	pmcs_xscsi_t			**targets;
+
+	STAILQ_HEAD(dqh, pmcs_cmd)	dq;	/* dead commands */
+	STAILQ_HEAD(cqh, pmcs_cmd)	cq;	/* completed commands */
+	kmutex_t			cq_lock;
+	kmem_cache_t			*iocomp_cb_cache;
+	pmcs_iocomp_cb_t		*iocomp_cb_head;
+	pmcs_iocomp_cb_t		*iocomp_cb_tail;
+
+	uint16_t			debug_mask;
+	uint16_t			phyid_block_mask;
+	uint16_t			phys_started;
+	uint32_t			hipri_queue;
+	uint32_t			mpibar;
+	uint32_t			intr_pri;
+
+	pmcs_io_intr_coal_t		io_intr_coal;
+	pmcs_cq_info_t			cq_info;
+	kmutex_t			ict_lock;
+	kcondvar_t			ict_cv;
+	kthread_t			*ict_thread;
+
+#ifdef	DEBUG
+	kmutex_t	dbglock;
+	uint32_t	ltags[256];
+	uint32_t	ftags[256];
+	hrtime_t	ltime[256];
+	hrtime_t	ftime[256];
+	uint16_t	ftag_lines[256];
+	uint8_t		lti;			/* last tag index */
+	uint8_t		fti;			/* first tag index */
+#endif
+};
+
+extern void 		*pmcs_softc_state;
+extern void 		*pmcs_iport_softstate;
+
+/*
+ * Some miscellaneous, oft used strings
+ */
+extern const char pmcs_nowrk[];
+extern const char pmcs_nomsg[];
+extern const char pmcs_timeo[];
+
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _PMCS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_def.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,514 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#ifndef	_PMCS_DEF_H
+#define	_PMCS_DEF_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+	NOTHING,	/* nothing connected here */
+	SATA,		/* SATA connection */
+	SAS,		/* direct or indirect SAS connection */
+	EXPANDER,	/* connection to an expander */
+	NEW		/* Brand new device (pending state) */
+} pmcs_dtype_t;
+
+/*
+ * This structure defines a PHY device that represents what we
+ * are connected to.
+ *
+ * The eight real physical PHYs that are in the PMC8X6G are represented
+ * as an array of eight of these structures which define what these
+ * real PHYs are connected to.
+ *
+ * Depending upon what is actually connected to each PHY, the
+ * type set will define what we're connected to. If it is
+ * a direct SATA connection, the phy will describe a SATA endpoint
+ * If it is a direct SAS connection, it will describe a SAS
+ * endpoint.
+ *
+ * If it is an EXPANDER, this will describe the edge of an expander.
+ * As we perform discovery on what is in an EXPANDER we define an
+ * additional list of phys that represent what the Expander is connected to.
+ */
+#define	PMCS_HW_MIN_LINK_RATE	SAS_LINK_RATE_1_5GBIT
+#define	PMCS_HW_MAX_LINK_RATE	SAS_LINK_RATE_6GBIT
+
+#define	PMCS_INVALID_DEVICE_ID	0xffffffff
+
+struct pmcs_phy {
+	pmcs_phy_t	*sibling;	/* sibling phy */
+	pmcs_phy_t 	*parent;	/* parent phy */
+	pmcs_phy_t 	*children;	/* head of list of children */
+	pmcs_phy_t 	*dead_next;	/* dead PHY list link */
+	list_node_t	list_node;	/* list element */
+	uint32_t	device_id;	/* PMC8X6G device handle */
+	uint32_t
+		ncphy 		: 8,	/* # of contained phys for expander */
+		hw_event_ack	: 24;	/* XXX: first level phy event acked */
+	uint8_t		phynum;		/* phy number on parent expander */
+	uint8_t		width;		/* how many phys wide */
+	uint8_t		ds_recovery_retries; /* # error retry attempts */
+	pmcs_dtype_t	dtype;		/* current dtype of the phy */
+	pmcs_dtype_t	pend_dtype;	/* new dtype (pending change) */
+	uint32_t
+		level		: 8,	/* level in expander tree */
+		tolerates_sas2	: 1,	/* tolerates SAS2 SMP */
+		spinup_hold	: 1,	/* spinup hold needs releasing */
+		atdt		: 3,	/* attached device type */
+		portid		: 4,	/* PMC8X6G port context */
+		link_rate   	: 4,	/* current supported speeds */
+		valid_device_id	: 1,	/* device id is valid */
+		abort_sent	: 1,	/* we've sent an abort */
+		abort_pending	: 1,	/* we have an abort pending */
+		need_rl_ext	: 1,	/* need SATA RL_EXT recocvery */
+		subsidiary	: 1,	/* this is part of a wide phy */
+		configured	: 1,	/* is configured */
+		dead		: 1,	/* dead */
+		changed		: 1;	/* this phy is changing */
+	clock_t		config_stop;	/* When config attempts will stop */
+	hrtime_t	abort_all_start;
+	kcondvar_t	abort_all_cv;	/* Wait for ABORT_ALL completion */
+	kmutex_t	phy_lock;
+	volatile uint32_t ref_count;	/* Targets & work on this PHY */
+	uint8_t 	sas_address[8];	/* SAS address for this PHY */
+	struct {
+	uint32_t
+		prog_min_rate	:4,
+		hw_min_rate	:4,
+		prog_max_rate	:4,
+		hw_max_rate	:4,
+		reserved	:16;
+	} state;
+	char		path[32];	/* path name for this phy */
+	pmcs_hw_t	*pwp;		/* back ptr to hba struct */
+	pmcs_iport_t	*iport;		/* back ptr to the iport handle */
+	pmcs_xscsi_t	*target;	/* back ptr to current target */
+	kstat_t		*phy_stats;	/* kstats for this phy */
+};
+
+/* maximum number of ds recovery retries (ds_recovery_retries) */
+#define	PMCS_MAX_DS_RECOVERY_RETRIES	4
+
+
+/*
+ * Inbound and Outbound Queue Related Definitions.
+ *
+ * The PMC8X6G has a programmable number of inbound and outbound circular
+ * queues for use in message passing between the host and the PMC8X6G
+ * (up to 64 queues for the Rev C Chip). This driver does not use all
+ * possible queues.
+ *
+ * Each Queue is given 4K of consistent memory and we set a 64 byte size for
+ * the queue entry size (this gives us 256 queue entries per queue).
+ *
+ * This allocation then continues up a further PMCS_SCRATCH_SIZE bytes
+ * that the driver uses as a temporary scratch area for things like
+ * SMP discovery.
+ *
+ * This control area looks like this:
+ *
+ * Offset			What
+ * ------------------------------------------------
+ * 0					IQ 0 Consumer Index
+ * 4					IQ 1 Consumer Index
+ * 8..255				...
+ * 252..255				IQ 63 Consumer Index
+ * 256					OQ 0 Producer Index
+ * 260					OQ 1 Producer Index
+ * 264..259				....
+ * 508..511				OQ 63 Producer Index
+ * 512..512+PMCS_SCRATCH_SIZE-1		Scratch area.
+ */
+#define	IQCI_BASE_OFFSET	0
+#define	IQ_OFFSET(qnum)		(IQCI_BASE_OFFSET + (qnum << 2))
+#define	OQPI_BASE_OFFSET	256
+#define	OQ_OFFSET(qnum)		(OQPI_BASE_OFFSET + (qnum << 2))
+
+/*
+ * Work related structures. Each one of these structures is paired
+ * with *any* command that is fed to the PMC8X6G via one of the
+ * Inbound Queues. The work structure has a tag to compare with
+ * the message that comes back out of an Outbound Queue. The
+ * work structure also points to the phy which this command is
+ * tied to. It also has a pointer a callback function (if defined).
+ * See that TAG Architecture below for the various kinds of
+ * dispositions of a work structure.
+ */
+
+/*
+ * Work Structure States
+ *
+ * NIL			->	READY
+ * READY		->	NIL
+ * READY		->	ONCHIP
+ * ONCHIP		->	INTR
+ * INTR			->	READY
+ * INTR			->	NIL
+ * INTR			->	ABORTED
+ * INTR			->	TIMED_OUT
+ * ABORTED		->	NIL
+ * TIMED_OUT		->	NIL
+ */
+typedef enum {
+	PMCS_WORK_STATE_NIL = 0,
+	PMCS_WORK_STATE_READY,
+	PMCS_WORK_STATE_ONCHIP,
+	PMCS_WORK_STATE_INTR,
+	PMCS_WORK_STATE_IOCOMPQ,
+	PMCS_WORK_STATE_ABORTED,
+	PMCS_WORK_STATE_TIMED_OUT
+} pmcs_work_state_t;
+
+struct pmcwork {
+	STAILQ_ENTRY(pmcwork)	next;
+	kmutex_t		lock;
+	kcondvar_t		sleep_cv;
+	void			*ptr;	/* linkage or callback function */
+	void 			*arg;	/* command specific data */
+	pmcs_phy_t 		*phy;	/* phy who owns this command */
+	pmcs_xscsi_t		*xp;	/* Back pointer to xscsi struct */
+	volatile uint32_t	htag;	/* tag for this structure */
+	uint32_t
+			timer	:	27,
+			onwire	:	1,
+			dead	:	1,
+			state	:	3;
+	hrtime_t		start;	/* timestamp start */
+	uint32_t		ssp_event; /* ssp event */
+	pmcs_dtype_t		dtype;	/* stash, incase phy gets cleared */
+
+	/* DEBUG-only fields from here on */
+	void			*last_ptr;
+	void			*last_arg;
+	pmcs_phy_t		*last_phy;
+	pmcs_xscsi_t		*last_xp;
+	uint32_t		last_htag;
+	pmcs_work_state_t	last_state;
+	hrtime_t		finish;
+};
+
+#define	PMCS_REC_EVENT	0xffffffff	/* event recovery */
+
+/*
+ * This structure defines a PMC-Sierra defined firmware header.
+ */
+#pragma	pack(4)
+typedef struct {
+	char 		vendor_id[8];
+	uint8_t		product_id;
+	uint8_t		hwrev;
+	uint8_t		destination_partition;
+	uint8_t		reserved0;
+	uint8_t		fwrev[4];
+	uint32_t	firmware_length;
+	uint32_t	crc;
+	uint32_t	start_address;
+	uint8_t		data[];
+} pmcs_fw_hdr_t;
+#pragma	pack()
+
+/*
+ * Offlevel work as a bit pattern.
+ */
+#define	PMCS_WORK_DISCOVER		0
+#define	PMCS_WORK_REM_DEVICES		2
+#define	PMCS_WORK_ABORT_HANDLE		3
+#define	PMCS_WORK_SPINUP_RELEASE	4
+#define	PMCS_WORK_SAS_HW_ACK		5
+#define	PMCS_WORK_SATA_RUN		6
+#define	PMCS_WORK_RUN_QUEUES		7
+#define	PMCS_WORK_ADD_DMA_CHUNKS	8
+#define	PMCS_WORK_DS_ERR_RECOVERY	9
+#define	PMCS_WORK_SSP_EVT_RECOVERY	10
+
+/*
+ * The actual values as they appear in work_flags
+ */
+#define	PMCS_WORK_FLAG_DISCOVER		(1 << 0)
+#define	PMCS_WORK_FLAG_REM_DEVICES	(1 << 2)
+#define	PMCS_WORK_FLAG_ABORT_HANDLE	(1 << 3)
+#define	PMCS_WORK_FLAG_SPINUP_RELEASE	(1 << 4)
+#define	PMCS_WORK_FLAG_SAS_HW_ACK	(1 << 5)
+#define	PMCS_WORK_FLAG_SATA_RUN		(1 << 6)
+#define	PMCS_WORK_FLAG_RUN_QUEUES	(1 << 7)
+#define	PMCS_WORK_FLAG_ADD_DMA_CHUNKS	(1 << 8)
+#define	PMCS_WORK_FLAG_DS_ERR_RECOVERY	(1 << 9)
+#define	PMCS_WORK_FLAG_SSP_EVT_RECOVERY (1 << 10)
+
+/*
+ * This structure is used by this function to test MPI (and interrupts)
+ * after MPI has been started to make sure it's working reliably.
+ */
+typedef struct {
+	uint32_t signature;
+	uint32_t count;
+	uint32_t *ptr;
+} echo_test_t;
+#define	ECHO_SIGNATURE	0xbebebeef
+
+/*
+ * Tag Architecture. The PMC has 32 bit tags for MPI messages.
+ * We use this tag this way.
+ *
+ * bits		what
+ * ------------------------
+ * 31		done bit
+ * 30..28	tag type
+ * 27..12	rolling serial number
+ * 11..0	index into work area to get pmcwork structure
+ *
+ * A tag type of NONE means that nobody is waiting on any results,
+ * so the interrupt code frees the work structure that has this
+ * tag.
+ *
+ * A tag type of CBACK means that the the interrupt handler
+ * takes the tag 'arg' in the work structure to be a callback
+ * function pointer (see pmcs_cb_t). The callee is responsible
+ * for freeing the work structure that has this tag.
+ *
+ * A tag type of WAIT means that the issuer of the work needs
+ * be woken up from interrupt level when the command completes
+ * (or times out). If work structure tag 'arg' is non-null,
+ * up to 2*PMCS_QENTRY_SIZE bits of data from the Outbound Queue
+ * entry may be copied to the area pointed to by 'arg'. This
+ * allows issuers to get directly at the results of the command
+ * they issed. The synchronization point for the issuer and the
+ * interrupt code for command done notification is the setting
+ * of the 'DONE' bit in the tag as stored in the work structure.
+ */
+#define	PMCS_TAG_TYPE_FREE	0
+#define	PMCS_TAG_TYPE_NONE	1
+#define	PMCS_TAG_TYPE_CBACK  	2
+#define	PMCS_TAG_TYPE_WAIT	3
+#define	PMCS_TAG_TYPE_SHIFT	28
+#define	PMCS_TAG_SERNO_SHIFT	12
+#define	PMCS_TAG_INDEX_SHIFT	0
+#define	PMCS_TAG_TYPE_MASK	0x70000000
+#define	PMCS_TAG_DONE		0x80000000
+#define	PMCS_TAG_SERNO_MASK	0x0ffff000
+#define	PMCS_TAG_INDEX_MASK	0x00000fff
+#define	PMCS_TAG_TYPE(x)		\
+	(((x) & PMCS_TAG_TYPE_MASK) >> PMCS_TAG_TYPE_SHIFT)
+#define	PMCS_TAG_SERNO(x)	\
+	(((x) & PMCS_TAG_SERNO_MASK) >> PMCS_TAG_SERNO_SHIFT)
+#define	PMCS_TAG_INDEX(x)	\
+	(((x) & PMCS_TAG_INDEX_MASK) >> PMCS_TAG_INDEX_SHIFT)
+#define	PMCS_TAG_FREE		0
+#define	PMCS_COMMAND_DONE(x)	\
+	(((x)->htag == PMCS_TAG_FREE) || (((x)->htag & PMCS_TAG_DONE) != 0))
+#define	PMCS_COMMAND_ACTIVE(x)	\
+	((x)->htag != PMCS_TAG_FREE && (x)->state == PMCS_WORK_STATE_ONCHIP)
+
+/*
+ * Miscellaneous Definitions
+ */
+#define	CLEAN_MESSAGE(m, x)	{	\
+	int _j = x;			\
+	while (_j < PMCS_MSG_SIZE) {	\
+		m[_j++] = 0;		\
+	}				\
+}
+
+#define	COPY_MESSAGE(t, f, a)	{	\
+	int _j;				\
+	for (_j = 0; _j < a; _j++) {	\
+		t[_j] = f[_j];		\
+	}				\
+	while (_j < PMCS_MSG_SIZE) {	\
+		t[_j++] = 0;		\
+	}				\
+}
+
+#define	PMCS_PHY_ADDRESSABLE(pp)			\
+	((pp)->level == 0 && (pp)->dtype == SATA &&	\
+	    ((pp)->sas_address[0] >> 4) != 5)
+
+#define	RESTART_DISCOVERY(pwp)				\
+	ASSERT(!mutex_owned(&pwp->config_lock));	\
+	mutex_enter(&pwp->config_lock);			\
+	pwp->config_changed = B_TRUE;			\
+	mutex_exit(&pwp->config_lock);			\
+	SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
+
+#define	RESTART_DISCOVERY_LOCKED(pwp)			\
+	ASSERT(mutex_owned(&pwp->config_lock));		\
+	pwp->config_changed = B_TRUE;			\
+	SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER);
+
+#define	PHY_CHANGED(pwp, p)						\
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s changed in %s line %d", \
+	    p->path, __func__, __LINE__);				\
+	p->changed = 1
+
+#define	PHY_CHANGED_AT_LOCATION(pwp, p, func, line)			\
+	pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, "%s changed in %s line %d", \
+	    p->path, func, line);					\
+	p->changed = 1
+
+#define	PHY_TYPE(pptr)					\
+	(((pptr)->dtype == NOTHING)?  "NOTHING" :	\
+	(((pptr)->dtype == SATA)? "SATA" :		\
+	(((pptr)->dtype == SAS)? "SAS" : "EXPANDER")))
+
+#define	IS_ROOT_PHY(pptr)	(pptr->parent == NULL)
+
+#define	PMCS_HIPRI(pwp, oq, c)				\
+	(pwp->hipri_queue & (1 << PMCS_IQ_OTHER)) ?	\
+	(PMCS_IOMB_HIPRI | PMCS_IOMB_IN_SAS(oq, c)) :	\
+	(PMCS_IOMB_IN_SAS(oq, c))
+
+#define	SCHEDULE_WORK(hwp, wrk)		\
+	(void) atomic_set_long_excl(&hwp->work_flags, wrk)
+
+/*
+ * Check to see if the requested work bit is set.  Either way, the bit will
+ * be cleared upon return.
+ */
+#define	WORK_SCHEDULED(hwp, wrk)	\
+	(atomic_clear_long_excl(&hwp->work_flags, wrk) == 0)
+
+/*
+ * Check to see if the requested work bit is set.  The value will not be
+ * changed in this case.  The atomic_xx_nv operations can be quite expensive
+ * so this should not be used in non-DEBUG code.
+ */
+#define	WORK_IS_SCHEDULED(hwp, wrk)	\
+	((atomic_and_ulong_nv(&hwp->work_flags, (ulong_t)-1) & (1 << wrk)) != 0)
+
+#define	WAIT_FOR(p, t, r)					\
+	r = 0;							\
+	while (!PMCS_COMMAND_DONE(p)) {				\
+		clock_t tmp = cv_timedwait(&p->sleep_cv,	\
+		    &p->lock, ddi_get_lbolt() +			\
+		    drv_usectohz(t * 1000));			\
+		if (!PMCS_COMMAND_DONE(p) && tmp < 0) {		\
+			r = 1;					\
+			break;					\
+		}						\
+	}
+
+/*
+ * Signal the next I/O completion thread to start running.
+ */
+
+#define	PMCS_CQ_RUN_LOCKED(hwp)						\
+	if (!STAILQ_EMPTY(&hwp->cq) || hwp->iocomp_cb_head) {		\
+		pmcs_cq_thr_info_t *cqti;				\
+		cqti = &hwp->cq_info.cq_thr_info			\
+		    [hwp->cq_info.cq_next_disp_thr];			\
+		hwp->cq_info.cq_next_disp_thr++;			\
+		if (hwp->cq_info.cq_next_disp_thr ==			\
+		    hwp->cq_info.cq_threads) {				\
+			hwp->cq_info.cq_next_disp_thr = 0;		\
+		}							\
+		mutex_enter(&cqti->cq_thr_lock);			\
+		cv_signal(&cqti->cq_cv);				\
+		mutex_exit(&cqti->cq_thr_lock);				\
+	}								\
+
+#define	PMCS_CQ_RUN(hwp)						\
+	mutex_enter(&hwp->cq_lock);					\
+	PMCS_CQ_RUN_LOCKED(hwp);					\
+	mutex_exit(&hwp->cq_lock);
+
+
+/*
+ * Watchdog/SCSA timer definitions
+ */
+/* usecs to SCSA watchdog ticks */
+#define	US2WT(x)	(x)/10
+
+/*
+ * More misc
+ */
+#define	BYTE0(x)	(((x) >>  0) & 0xff)
+#define	BYTE1(x)	(((x) >>  8) & 0xff)
+#define	BYTE2(x)	(((x) >> 16) & 0xff)
+#define	BYTE3(x)	(((x) >> 24) & 0xff)
+#define	BYTE4(x)	(((x) >> 32) & 0xff)
+#define	BYTE5(x)	(((x) >> 40) & 0xff)
+#define	BYTE6(x)	(((x) >> 48) & 0xff)
+#define	BYTE7(x)	(((x) >> 56) & 0xff)
+#define	WORD0(x)	(((x) >>  0) & 0xffff)
+#define	WORD1(x)	(((x) >> 16) & 0xffff)
+#define	WORD2(x)	(((x) >> 32) & 0xffff)
+#define	WORD3(x)	(((x) >> 48) & 0xffff)
+#define	DWORD0(x)	((uint32_t)(x))
+#define	DWORD1(x)	((uint32_t)(((uint64_t)x) >> 32))
+
+#define	SAS_ADDR_FMT	"0x%02x%02x%02x%02x%02x%02x%02x%02x"
+#define	SAS_ADDR_PRT(x)	x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7]
+
+#define	PMCS_VALID_LINK_RATE(r) \
+	((r == SAS_LINK_RATE_1_5GBIT) || (r == SAS_LINK_RATE_3GBIT) || \
+	(r == SAS_LINK_RATE_6GBIT))
+
+/*
+ * This is here to avoid inclusion of <sys/ctype.h> which is not lint clean.
+ */
+#define	HEXDIGIT(x)	(((x) >= '0' && (x) <= '9') || \
+	((x) >= 'a' && (x) <= 'f') || ((x) >= 'A' && (x) <= 'F'))
+
+
+typedef void (*pmcs_cb_t) (pmcs_hw_t *, pmcwork_t *, uint32_t *);
+
+/*
+ * Defines and structure used for tracing/logging information
+ */
+
+#define	PMCS_TBUF_ELEM_SIZE	120
+
+#ifdef DEBUG
+#define	PMCS_TBUF_NUM_ELEMS_DEF	100000
+#else
+#define	PMCS_TBUF_NUM_ELEMS_DEF	15000
+#endif
+
+typedef struct {
+	timespec_t	timestamp;
+	char		buf[PMCS_TBUF_ELEM_SIZE];
+} pmcs_tbuf_t;
+
+/*
+ * Firmware event log header format
+ */
+
+typedef struct pmcs_fw_event_hdr_s {
+	uint32_t	fw_el_signature;
+	uint32_t	fw_el_entry_start_offset;
+	uint32_t	fw_el_rsvd1;
+	uint32_t	fw_el_buf_size;
+	uint32_t	fw_el_rsvd2;
+	uint32_t	fw_el_oldest_idx;
+	uint32_t	fw_el_latest_idx;
+	uint32_t	fw_el_entry_size;
+} pmcs_fw_event_hdr_t;
+
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _PMCS_DEF_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_iomb.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,724 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * PMC 8x6G IOMB Definitions
+ */
+#ifndef	_PMCS_IOMB_H
+#define	_PMCS_IOMB_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * An IOMB (IO Message Buffer) is the principle means of communication
+ * between the PMC and the HOST. The host places IOMBs on the Inbound
+ * Queues (IQ) which are in HOST memory and updates a producer index
+ * within the PMC. The PMC pulls the IOMB off the IQ and updates a
+ * consumer index in HOST memory. If appropriate, when the PMC is
+ * done with the action requested by the IOMB, the PMC writes a
+ * reply IOMB to host memory and updates its producer index and
+ * interrupts the HOST.
+ */
+/*
+ * The first word of all IOMBs is always laid out thusly:
+ *
+ * |Byte 3       |Byte 2       |Byte 1      |Byte 0   |
+ * +-------------+-------------+----------------------+
+ * |V Resvd    BC|Resvd  OBID  |CAT   |  OPCODE       |
+ * +--------------------------------------------------+
+ *
+ * V == Valid
+ * BC = Buffer Count
+ * OBID = Outbound Queue ID
+ * CAT = Category
+ * OPCODE = Well, uh, OPCODE.
+ */
+
+#define	PMCS_IOMB_VALID		(1U << 31)
+#define	PMCS_IOMB_HIPRI		(1U << 30)
+#define	PMCS_IOMB_BC_SHIFT	(24)
+#define	PMCS_IOMB_BC_MASK	(0xf << PMCS_IOMB_BC_SHIFT)
+#define	PMCS_IOMB_OBID_SHIFT	(16)
+#define	PMCS_IOMB_OBID_MASK	(0xf << PMCS_IOMB_OBID_SHIFT)
+#define	PMCS_IOMB_CAT_SHIFT	(12)
+#define	PMCS_IOMB_CAT_MASK	(0xf << PMCS_IOMB_CAT_SHIFT)
+#define	PMCS_IOMB_OPCODE_MASK	(0xfff)
+
+
+#define	PMCS_IOMB_CAT_NET	0
+#define	PMCS_IOMB_CAT_FC	1
+#define	PMCS_IOMB_CAT_SAS	2
+#define	PMCS_IOMB_CAT_SCSI	3
+
+/*
+ * Shorthand
+ */
+#define	PMCS_IOMB_IN_SAS(q, opcode)					\
+	(PMCS_IOMB_VALID | (1 << PMCS_IOMB_BC_SHIFT)		|	\
+	(PMCS_IOMB_CAT_SAS << PMCS_IOMB_CAT_SHIFT)		|	\
+	((q << PMCS_IOMB_OBID_SHIFT) & PMCS_IOMB_OBID_MASK)	|	\
+	(opcode & PMCS_IOMB_OPCODE_MASK))
+
+/*
+ * PMC IOMB Inbound Queue Opcodes
+ */
+#define	PMCIN_ECHO							0x01
+#define	PMCIN_GET_INFO							0x02
+#define	PMCIN_GET_VPD							0x03
+#define	PMCIN_PHY_START							0x04
+#define	PMCIN_PHY_STOP							0x05
+#define	PMCIN_SSP_INI_IO_START						0x06
+#define	PMCIN_SSP_INI_TM_START						0x07
+#define	PMCIN_SSP_INI_EXT_IO_START					0x08
+#define	PMCIN_DEVICE_HANDLE_ACCEPT					0x09
+#define	PMCIN_SSP_TGT_IO_START						0x0A
+#define	PMCIN_SSP_TGT_RESPONSE_START					0x0B
+#define	PMCIN_SSP_INI_EDC_EXT_IO_START					0x0C
+#define	PMCIN_SSP_INI_EDC_EXT_IO_START1					0x0D
+#define	PMCIN_SSP_TGT_EDC_IO_START					0x0E
+#define	PMCIN_SSP_ABORT							0x0F
+#define	PMCIN_DEREGISTER_DEVICE_HANDLE					0x10
+#define	PMCIN_GET_DEVICE_HANDLE						0x11
+#define	PMCIN_SMP_REQUEST						0x12
+#define	PMCIN_SMP_RESPONSE						0x13
+#define	PMCIN_SMP_ABORT							0x14
+#define	PMCIN_ASSISTED_DISCOVERY					0x15
+#define	PMCIN_REGISTER_DEVICE						0x16
+#define	PMCIN_SATA_HOST_IO_START					0x17
+#define	PMCIN_SATA_ABORT						0x18
+#define	PMCIN_LOCAL_PHY_CONTROL						0x19
+#define	PMCIN_GET_DEVICE_INFO						0x1A
+#define	PMCIN_TWI							0x1B
+#define	PMCIN_FW_FLASH_UPDATE						0x20
+#define	PMCIN_SET_VPD							0x21
+#define	PMCIN_GPIO							0x22
+#define	PMCIN_SAS_DIAG_MODE_START_END					0x23
+#define	PMCIN_SAS_DIAG_EXECUTE						0x24
+#define	PMCIN_SAW_HW_EVENT_ACK						0x25
+#define	PMCIN_GET_TIME_STAMP						0x26
+#define	PMCIN_PORT_CONTROL						0x27
+#define	PMCIN_GET_NVMD_DATA						0x28
+#define	PMCIN_SET_NVMD_DATA						0x29
+#define	PMCIN_SET_DEVICE_STATE						0x2A
+#define	PMCIN_GET_DEVICE_STATE						0x2B
+
+/*
+ * General Inbound Queue related parameters (DWORD 4)
+ */
+#define	PMCIN_MESSAGE_REPORT		(1 << 2)
+#define	PMCIN_DS_ABORT_TASK		(1 << 3)
+#define	PMCIN_DS_IN_RECOVERY		(1 << 4)
+#define	PMCIN_DATADIR_NONE		(0x00 << 8)
+#define	PMCIN_DATADIR_2_INI		(0x01 << 8)
+#define	PMCIN_DATADIR_2_DEV		(0x02 << 8)
+
+
+/*
+ * SATA Host IO Start ATA Protocol Types
+ * (placed into DWORD 4)
+ */
+
+#define	SATA_PROTOCOL_SRST_ASSERT	(0x01 << 10)
+#define	SATA_PROTOCOL_SRT_DEASSERT	(0x02 << 10)
+#define	SATA_PROTOCOL_EXECDEVDIAG	(0x03 << 10)
+#define	SATA_PROTOCOL_NONDATA		(0x04 << 10)
+#define	SATA_PROTOCOL_PIO		(0x05 << 10)
+#define	SATA_PROTOCOL_DMA		(0x06 << 10)
+#define	SATA_PROTOCOL_FPDMA		(0x07 << 10)
+
+/*
+ * SAS Host IO Start TLR definitions
+ * (placed into DWORD 4)
+ */
+#define	SAS_TLR_ALL	0	/* SAS 1.1 and SAS 2.0 per device mode page */
+#define	SAS_TLR_ON	1	/* unconditionally on */
+#define	SAS_TLR_OFF	2	/* unconditionally off */
+#define	SAS_TLR_SAS2	3	/* SAS 2.0 per device mode page */
+
+/*
+ * IOP SMP Request Information
+ */
+#define	SMP_INDIRECT_RESPONSE		0x01
+#define	SMP_INDIRECT_REQUEST		0x02
+#define	SMP_PHY_OVERRIDE		0x04
+#define	SMP_REQUEST_LENGTH_SHIFT	16
+
+/*
+ * PHY Start related definitions
+ */
+#define	PHY_LINK_1_5			0x01
+#define	PHY_LINK_3			0x02
+#define	PHY_LINK_6			0x04
+#define	PHY_LINK_ALL			(PHY_LINK_1_5 | PHY_LINK_3 | PHY_LINK_6)
+#define	PHY_LINK_SHIFT			8
+
+#define	PHY_LM_SAS			1
+#define	PHY_LM_SATA			2
+#define	PHY_LM_AUTO			3
+#define	PHY_MODE_SHIFT			12
+
+#define	PHY_SPINUP_HOLD			(1 << 14)
+
+/*
+ * LOCAL PHY CONTROL related definitions
+ */
+
+/*
+ * Device Registration related definitions
+ */
+#define	PMCS_DEVREG_LINK_RATE_SHIFT	24
+#define	PMCS_DEVREG_TYPE_SATA		0
+#define	PMCS_DEVREG_TYPE_SAS		(1 << 28)
+#define	PMCS_DEVREG_TYPE_SATA_DIRECT	(1 << 29)
+
+#define	PMCS_PHYID_SHIFT		4	/* level 0 registration only */
+#define	PMCS_DEVREG_TLR			0x1	/* Transport Layer Retry */
+
+#define	PMCS_DEVREG_IT_NEXUS_TIMEOUT	200U
+
+#define	PMCS_DEVREG_HA			0x2	/* Host Assigned upper 16 */
+						/* bits for device ID. */
+/*
+ * These are used for getting/setting data in the NVRAM (SEEPROM, VPD, etc.)
+ */
+
+typedef struct pmcs_get_nvmd_cmd_s {
+	uint32_t		header;		/* DWORD 0 */
+	uint32_t		htag;		/* DWORD 1 */
+	uint8_t			tdas_nvmd;	/* DWORD 2 */
+	uint8_t			tbn_tdps;
+	uint8_t			tda;
+	uint8_t			ip;
+	uint8_t			doa[3];		/* DWORD 3 Data Offset Addr */
+	uint8_t			d_len;		/* Direct Pld Data Len */
+	uint32_t		rsvd[8];	/* DWORDS 4-11 */
+	uint32_t		ipbal;		/* 12 - Ind Pld buf addr low */
+	uint32_t		ipbah;		/* 13 - Ind Pld buf addr high */
+	uint32_t		ipdl;		/* 14 - Ind Pld data length */
+	uint32_t		rsvd3;
+} pmcs_get_nvmd_cmd_t;
+
+typedef struct pmcs_set_nvmd_cmd_s {
+	uint32_t		header;		/* DWORD 0 */
+	uint32_t		htag;		/* DWORD 1 */
+	uint8_t			tdas_nvmd;	/* DWORD 2 */
+	uint8_t			tbn_tdps;
+	uint8_t			tda;
+	uint8_t			ip;
+	uint8_t			doa[3];		/* DWORD 3 Data Offset Addr */
+	uint8_t			d_len;		/* Direct Pld Data Len */
+	uint32_t		signature;	/* DWORD 4 */
+	uint32_t		rsvd[7];	/* DWORDS 5-11 */
+	uint32_t		ipbal;		/* 12 - Ind Pld buf addr low */
+	uint32_t		ipbah;		/* 13 - Ind Pld buf addr high */
+	uint32_t		ipdl;		/* 14 - Ind Pld data length */
+	uint32_t		rsvd2;
+} pmcs_set_nvmd_cmd_t;
+
+#define	PMCIN_NVMD_DIRECT_PLD		0x00
+#define	PMCIN_NVMD_INDIRECT_PLD		0x80
+
+/* TWI bus number is upper 4 bits of tbn_tdps */
+#define	PMCIN_NVMD_TBN(x)		(x << 4)
+
+/* TWI Device Page Size bits (lower 4 bits of tbn_tdps */
+#define	PMCIN_NVMD_TDPS_1		0	/* 1 byte */
+#define	PMCIN_NVMD_TDPS_8		1	/* 8 bytes */
+#define	PMCIN_NVMD_TDPS_16		2	/* 16 bytes */
+#define	PMCIN_NVMD_TDPS_32		3	/* 32 bytes */
+
+/* TWI Device Address Size (upper 4 bits of tdas_nvmd) */
+#define	PMCIN_NVMD_TDAS_1		(0 << 4)	/* 1 byte */
+#define	PMCIN_NVMD_TDAS_2		(1 << 4)	/* 2 bytes */
+
+/*
+ * TWI Device Address
+ * The address used to access TWI device for the 2Kb SEEPROM device is
+ * arranged as follows:
+ *	Bits 7-4 are fixed (0xA)
+ *	Bits 3-1 are page numbers for each 256 byte page
+ *	Bit 0: Set to "1" to read, "0" to write
+ * Bit 0 is set/reset by the firmware based on whether the command is a
+ * SET or a GET.
+ */
+#define	PMCIN_TDA_BASE			0xA0
+#define	PMCIN_TDA_PAGE(x)		(PMCIN_TDA_BASE | (x << 1))
+
+/* NVM Device bits (lower 4 bits of tdas_nvmd) */
+#define	PMCIN_NVMD_TWI			0	/* TWI Device */
+#define	PMCIN_NVMD_SEEPROM		1	/* SEEPROM Device */
+#define	PMCIN_NVMD_VPD			4	/* VPD Flash Memory */
+#define	PMCIN_NVMD_AAP1			5	/* AAP1 Register Dump */
+#define	PMCIN_NVMD_IOP			6	/* IOP Register Dump */
+
+#define	PMCS_SEEPROM_PAGE_SIZE		256
+
+/*
+ * Minimum and maximum sizes of SPCBoot image
+ */
+#define	PMCS_SPCBOOT_MIN_SIZE		64
+#define	PMCS_SPCBOOT_MAX_SIZE		512
+
+#define	PMCS_SEEPROM_SIGNATURE		0xFEDCBA98
+
+/*
+ * Register dump information
+ *
+ * There are two 16KB regions for register dump information
+ */
+
+#define	PMCS_REGISTER_DUMP_FLASH_SIZE	(1024 * 16)
+#define	PMCS_REGISTER_DUMP_BLOCK_SIZE	4096	/* Must be read 4K at a time */
+#define	PMCS_FLASH_CHUNK_SIZE		4096	/* Must be read 4K at a time */
+#define	PMCS_REG_DUMP_SIZE		(1024 * 1024 * 12)
+#define	PMCS_NVMD_EVENT_LOG_OFFSET	0x10000
+#define	PMCS_IQP_TRACE_BUFFER_SIZE	(1024 * 512)
+
+/*
+ * The list of items we can retrieve via the GET_NVMD_DATA command
+ */
+
+typedef enum {
+	PMCS_NVMD_VPD = 0,
+	PMCS_NVMD_REG_DUMP,
+	PMCS_NVMD_EVENT_LOG,
+	PMCS_NVMD_SPCBOOT
+} pmcs_nvmd_type_t;
+
+/*
+ * Command types, descriptors and offsets for SAS_DIAG_EXECUTE.
+ */
+#define	PMCS_DIAG_CMD_DESC_SHIFT	8
+#define	PMCS_DIAG_CMD_SHIFT		13
+#define	PMCS_DIAG_REPORT_GET		0x04	/* Get counters */
+#define	PMCS_ERR_CNT_RESET		0x05	/* Clear counters */
+#define	PMCS_DISPARITY_ERR_CNT		0x02	/* Disparity error count */
+#define	PMCS_LOST_DWORD_SYNC_CNT	0x05	/* Lost DWord sync count */
+#define	PMCS_INVALID_DWORD_CNT		0x06	/* Invalid DWord count */
+#define	PMCS_RESET_FAILED_CNT		0x0C	/* PHY reset failed count */
+
+/*
+ * VPD data layout
+ */
+
+#define	PMCS_VPD_DATA_PAGE	2	/* VPD starts at offset 512 */
+#define	PMCS_VPD_VERSION	2	/* Expected version number */
+#define	PMCS_VPD_RO_BYTE	0x90	/* Start of "read-only" data */
+#define	PMCS_VPD_START		0x82	/* VPD start byte */
+#define	PMCS_VPD_END		0x78	/* VPD end byte */
+
+/*
+ * This structure defines the "header" for the VPD data.  Everything
+ * following this structure is self-defining.  The consumer just needs
+ * to allocate a buffer large enough for vpd_length + 3 bytes of data.
+ */
+
+typedef struct {
+	uint8_t		eeprom_version;
+	uint8_t		vpd_length[2];	/* # bytes that follow, little-endian */
+	uint8_t		hba_sas_wwid[8];
+	uint8_t		subsys_pid[2];
+	uint8_t		subsys_vid[2];
+	uint8_t		vpd_start_byte;	/* 0x82 */
+	uint8_t		strid_length[2]; /* little-endian */
+	/* strid_length bytes follow */
+} pmcs_vpd_header_t;
+
+typedef struct {
+	char		keyword[2];
+	uint8_t		value_length;
+	char		value[1];	/* Length is actually value_length */
+} pmcs_vpd_kv_t;
+
+/*
+ * From here on out are definitions related to Outbound Queues
+ * (completions of Inbound Queue requests and async events)
+ */
+
+/*
+ * PMC IOMB Outbound Queue Opcodes
+ */
+#define	PMCOUT_ECHO							0x01
+#define	PMCOUT_GET_INFO							0x02
+#define	PMCOUT_GET_VPD							0x03
+#define	PMCOUT_SAS_HW_EVENT						0x04
+#define	PMCOUT_SSP_COMPLETION						0x05
+#define	PMCOUT_SMP_COMPLETION						0x06
+#define	PMCOUT_LOCAL_PHY_CONTROL					0x07
+#define	PMCOUT_SAS_ASSISTED_DISCOVERY_EVENT				0x08
+#define	PMCOUT_SATA_ASSISTED_DISCOVERY_EVENT				0x09
+#define	PMCOUT_DEVICE_REGISTRATION					0x0A
+#define	PMCOUT_DEREGISTER_DEVICE_HANDLE					0x0B
+#define	PMCOUT_GET_DEVICE_HANDLE					0x0C
+#define	PMCOUT_SATA_COMPLETION						0x0D
+#define	PMCOUT_SATA_EVENT						0x0E
+#define	PMCOUT_SSP_EVENT						0x0F
+#define	PMCOUT_DEVICE_HANDLE_ARRIVED					0x10
+#define	PMCOUT_SMP_REQUEST_RECEIVED					0x11
+#define	PMCOUT_SSP_REQUEST_RECEIVED					0x12
+#define	PMCOUT_DEVICE_INFO						0x13
+#define	PMCOUT_FW_FLASH_UPDATE						0x14
+#define	PMCOUT_SET_VPD							0x15
+#define	PMCOUT_GPIO							0x16
+#define	PMCOUT_GPIO_EVENT						0x17
+#define	PMCOUT_GENERAL_EVENT						0x18
+#define	PMCOUT_TWI							0x19
+#define	PMCOUT_SSP_ABORT						0x1A
+#define	PMCOUT_SATA_ABORT						0x1B
+#define	PMCOUT_SAS_DIAG_MODE_START_END					0x1C
+#define	PMCOUT_SAS_DIAG_EXECUTE						0x1D
+#define	PMCOUT_GET_TIME_STAMP						0x1E
+#define	PMCOUT_SAS_HW_EVENT_ACK_ACK					0x1F
+#define	PMCOUT_PORT_CONTROL						0x20
+#define	PMCOUT_SKIP_ENTRIES						0x21
+#define	PMCOUT_SMP_ABORT						0x22
+#define	PMCOUT_GET_NVMD_DATA						0x23
+#define	PMCOUT_SET_NVMD_DATA						0x24
+#define	PMCOUT_DEVICE_HANDLE_REMOVED					0x25
+#define	PMCOUT_SET_DEVICE_STATE						0x26
+#define	PMCOUT_GET_DEVICE_STATE						0x27
+#define	PMCOUT_SET_DEVICE_INFO						0x28
+
+/*
+ * General Outbound Status Definitions
+ */
+#define	PMCOUT_STATUS_OK						0x00
+#define	PMCOUT_STATUS_ABORTED						0x01
+#define	PMCOUT_STATUS_OVERFLOW						0x02
+#define	PMCOUT_STATUS_UNDERFLOW						0x03
+#define	PMCOUT_STATUS_FAILED						0x04
+#define	PMCOUT_STATUS_ABORT_RESET					0x05
+#define	PMCOUT_STATUS_IO_NOT_VALID					0x06
+#define	PMCOUT_STATUS_NO_DEVICE						0x07
+#define	PMCOUT_STATUS_ILLEGAL_PARAMETER					0x08
+#define	PMCOUT_STATUS_LINK_FAILURE					0x09
+#define	PMCOUT_STATUS_PROG_ERROR					0x0A
+#define	PMCOUT_STATUS_EDC_IN_ERROR					0x0B
+#define	PMCOUT_STATUS_EDC_OUT_ERROR					0x0C
+#define	PMCOUT_STATUS_ERROR_HW_TIMEOUT					0x0D
+#define	PMCOUT_STATUS_XFER_ERR_BREAK					0x0E
+#define	PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY				0x0F
+#define	PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED			0x10
+#define	PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION			0x11
+#define	PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK				0x12
+#define	PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS			0x13
+#define	PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION			0x14
+#define	PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED	0x15
+#define	PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY			0x16
+#define	PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION			0x17
+#define	PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_EROOR			0x18
+#define	PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED			0x19
+#define	PMCOUT_STATUS_XFER_ERROR_ACK_NAK_TIMEOUT			0x1A
+#define	PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED				0x1B
+#define	PMCOUT_STATUS_XFER_ERROR_RX_FRAME				0x1C
+#define	PMCOUT_STATUS_IO_XFER_ERROR_DMA					0x1D
+#define	PMCOUT_STATUS_XFER_ERROR_CREDIT_TIMEOUT				0x1E
+#define	PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT			0x1F
+#define	PMCOUT_STATUS_XFER_ERROR_SATA					0x20
+#define	PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE			0x21
+#define	PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST			0x22
+#define	PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE			0x23
+#define	PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT			0x24
+#define	PMCOUT_STATUS_SMP_RESP_CONNECTION_ERROR				0x25
+#define	PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE			0x26
+#define	PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN				0x27
+#define	PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED			0x28
+/* 0x29 */
+/* 0x2A */
+/* 0x2B */
+/* 0x2C */
+/* 0x2D */
+/* 0x2E */
+/* 0x2F */
+#define	PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT		0x30
+#define	PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK	0x31
+#define	PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK	0x32
+/* 0x33 */
+#define	PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH			0x34
+#define	PMCOUT_STATUS_XFER_ERROR_ZERO_DATA_LEN				0x35
+#define	PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED				0x36
+#define	PMCOUT_STATUS_ERROR_INTERNAL_SMP_RESOURCE			0x37
+#define	PMCOUT_STATUS_IO_PORT_IN_RESET					0x38
+#define	PMCOUT_STATUS_IO_DS_NON_OPERATIONAL				0x39
+#define	PMCOUT_STATUS_IO_DS_IN_RECOVERY					0x3A
+#define	PMCOUT_STATUS_IO_ABORT_IN_PROGRESS				0x40
+
+/*
+ * Device State definitions
+ */
+#define	PMCS_DEVICE_STATE_OPERATIONAL		0x1
+#define	PMCS_DEVICE_STATE_PORT_IN_RESET		0x2
+#define	PMCS_DEVICE_STATE_IN_RECOVERY		0x3
+#define	PMCS_DEVICE_STATE_IN_ERROR		0x4
+#define	PMCS_DEVICE_STATE_NON_OPERATIONAL	0x7
+
+/*
+ * Reset Types
+ */
+#define	PMCS_SSP_LINK_RESET		0x1
+#define	PMCS_SSP_HARD_RESET		0x2
+#define	PMCS_SMP_HARD_RESET		0x3
+
+/*
+ * PHYOP for LOCAL_PHY_CONTROL Command
+ */
+#define	PMCS_PHYOP_LINK_RESET		0x01
+#define	PMCS_PHYOP_HARD_RESET		0x02
+
+/*
+ * Specialized status values
+ */
+/* PHY Stop Status Results */
+#define	IOP_PHY_STOP_OK		0x0
+#define	IOP_PHY_STOP_INVALID	0x1
+#define	IOP_PHY_STOP_ERROR	0x3
+#define	IOP_PHY_STOP_ALREADY	0x4
+
+/* PHY Start Status Results */
+#define	IOP_PHY_START_OK	0
+#define	IOP_PHY_START_INVALID	1
+#define	IOP_PHY_START_ALREADY	2
+#define	IOP_PHY_START_ERROR	3
+
+/* SET/GET_NVMD status results */
+#define	PMCS_NVMD_STAT_SUCCESS			0x0000
+#define	PMCS_NVMD_STAT_PLD_NVMD_COMB_ERR	0x0001
+#define	PMCS_NVMD_STAT_PLD_LEN_ERR		0x0002
+#define	PMCS_NVMD_STAT_TWI_DEV_NACK		0x2001
+#define	PMCS_NVMD_STAT_TWI_DEV_LOST_ARB		0x2002
+#define	PMCS_NVMD_STAT_TWI_TIMEOUT		0x2021
+#define	PMCS_NVMD_STAT_TWI_BUS_NACK		0x2081
+#define	PMCS_NVMD_STAT_TWI_DEV_ARB_FAIL		0x2082
+#define	PMCS_NVMD_STAT_TWI_BUS_SER_TIMEO	0x20FF
+#define	PMCS_NVMD_STAT_PART_NOT_IN_FLASH	0x9001
+#define	PMCS_NVMD_STAT_LEN_TOO_LARGE		0x9002
+#define	PMCS_NVMD_STAT_FLASH_PRGRM_FAIL		0x9003
+#define	PMCS_NVMD_STAT_DEVID_MATCH_FAIL		0x9004
+#define	PMCS_NVMD_STAT_VENDID_MATCH_FAIL	0x9005
+#define	PMCS_NVMD_STAT_SEC_ERASE_TIMEO		0x9006
+#define	PMCS_NVMD_STAT_SEC_ERASE_CWE		0x9007
+#define	PMCS_NVMD_STAT_FLASH_DEV_BUSY		0x9008
+#define	PMCS_NVMD_STAT_FLASH_DEV_NOT_SUP	0x9009
+#define	PMCS_NVMD_STAT_FLASH_NO_CFI		0x900A
+#define	PMCS_NVMD_STAT_ERASE_BLOCKS		0x900B
+#define	PMCS_NVMD_STAT_PART_READ_ONLY		0x900C
+#define	PMCS_NVMD_STAT_PART_INV_MAP_TYPE	0x900D
+#define	PMCS_NVMD_STAT_PART_INIT_STR_DIS	0x900E
+
+/*
+ * General Event Status Codes
+ */
+#define	INBOUND_IOMB_V_BIT_NOT_SET		0x1
+#define	INBOUND_IOMB_OPC_NOT_SUPPORTED		0x2
+
+/* Device Register Status Results */
+#define	PMCS_DEVREG_OK				0x0
+#define	PMCS_DEVREG_DEVICE_ALREADY_REGISTERED	0x2
+#define	PMCS_DEVREG_PHY_ALREADY_REGISTERED	0x4
+
+/*
+ * Flash Update responses
+ */
+#define	FLASH_UPDATE_COMPLETE_PENDING_REBOOT	0x0
+#define	FLASH_UPDATE_IN_PROGRESS		0x1
+#define	FLASH_UPDATE_HDR_ERR			0x2
+#define	FLASH_UPDATE_OFFSET_ERR			0x3
+#define	FLASH_UPDATE_UPDATE_CRC_ERR		0x4
+#define	FLASH_UPDATE_LENGTH_ERR			0x5
+#define	FLASH_UPDATE_HW_ERR			0x6
+#define	FLASH_UPDATE_DNLD_NOT_SUPPORTED		0x10
+#define	FLASH_UPDATE_DISABLED			0x11
+
+/*
+ * IOP SAS HW Event Related definitions
+ */
+
+#define	IOP_EVENT_LINK_RATE(x)		((x >> 28) & 0xf)
+#define	IOP_EVENT_STATUS(x) 		((x >> 24) & 0xf)
+#define	IOP_EVENT_EVENT(x)		((x >> 8) & 0xffff)
+#define	IOP_EVENT_PHYNUM(x)		((x >> 4) & 0xf)
+#define	IOP_EVENT_PORTID(x)		((x) & 0xf)
+
+
+#define	IOP_EVENT_PHY_STOP_STATUS		0x03
+#define	IOP_EVENT_SAS_PHY_UP			0x04
+#define	IOP_EVENT_SATA_PHY_UP			0x05
+#define	IOP_EVENT_SATA_SPINUP_HOLD		0x06
+#define	IOP_EVENT_PHY_DOWN			0x07
+#define	IOP_EVENT_PORT_INVALID			0x08	/* < fw 1.6 */
+#define	IOP_EVENT_BROADCAST_CHANGE		0x09
+#define	IOP_EVENT_BROADCAST_SES			0x0B
+#define	IOP_EVENT_PHY_ERR_INBOUND_CRC		0x0C
+#define	IOP_EVENT_HARD_RESET_RECEIVED		0x0D
+#define	IOP_EVENT_EVENT_ID_FRAME_TIMO		0x0F
+#define	IOP_EVENT_BROADCAST_EXP			0x10
+#define	IOP_EVENT_PHY_START_STATUS		0x11
+#define	IOP_EVENT_PHY_ERR_INVALID_DWORD		0x12
+#define	IOP_EVENT_PHY_ERR_DISPARITY_ERROR	0x13
+#define	IOP_EVENT_PHY_ERR_CODE_VIOLATION	0x14
+#define	IOP_EVENT_PHY_ERR_LOSS_OF_DWORD_SYN	0x15
+#define	IOP_EVENT_PHY_ERR_PHY_RESET_FAILD	0x16
+#define	IOP_EVENT_PORT_RECOVERY_TIMER_TMO	0x17
+#define	IOP_EVENT_PORT_RECOVER			0x18
+#define	IOP_EVENT_PORT_RESET_TIMER_TMO		0x19
+#define	IOP_EVENT_PORT_RESET_COMPLETE		0x20
+#define	IOP_EVENT_BROADCAST_ASYNC_EVENT		0x21
+
+
+#define	IOP_EVENT_PORT_STATE(x)		((x) & 0xf)
+#define	IOP_EVENT_NPIP(x)		(((x) >> 4) & 0xf)
+
+#define	IOP_EVENT_PS_NIL		0x0	/* PORT_ID not valid yet */
+#define	IOP_EVENT_PS_VALID		0x1	/* PORT_ID now valid */
+#define	IOP_EVENT_PS_LOSTCOMM		0x2	/* Link temporarily down */
+#define	IOP_EVENT_PS_IN_RESET		0x4	/* Port in reset */
+#define	IOP_EVENT_PS_INVALID		0x8	/* PORT_ID now dead */
+
+/*
+ * HW Event Acknowledge Response Values
+ */
+#define	SAS_HW_EVENT_ACK_OK		0x0
+#define	SAS_HW_EVENT_ACK_INVALID_SEA	0x1
+#define	SAS_HW_EVENT_ACK_INVALID_PHY	0x2
+#define	SAS_HW_EVENT_ACK_INVALID_PORT	0x4
+#define	SAS_HW_EVENT_ACK_INVALID_PARAM	0x8
+
+/*
+ * IOMB Queue definitions and Macros
+ */
+
+#define	ADDQI(ix, n, qd)	((ix + n) & (qd - 1))
+#define	INCQI(ix, qd)		ix = ADDQI(ix, 1, qd)
+#define	QI2O(ix, n, qd)		(ADDQI(ix, n, qd) * PMCS_QENTRY_SIZE)
+
+/*
+ * Inbound Queue Producer Indices live inside the PMC card.
+ *
+ * Inbound Queue Consumer indices live in host memory. We use the Consumer
+ * Index to return a pointer to an Inbound Queue entry. We then can fill
+ * it with an IOMB. We then update the the Producer index which kicks
+ * card to read the IOMB we just wrote.
+ *
+ * There is one mutex for each inbound queue that is held from the time
+ * we get an entry until we increment the producer index, or released
+ * manually if we don't actually send the message.
+ */
+
+/*
+ * NB: the appropriate iqp_lock must be held
+ */
+#define	GET_IQ_ENTRY(hwp, qnum)	\
+	((ADDQI(hwp->shadow_iqpi[qnum], 1, hwp->ioq_depth) == \
+	pmcs_rd_iqci(hwp, qnum)) ? NULL : \
+	&hwp->iqp[qnum][hwp->shadow_iqpi[qnum] * (PMCS_QENTRY_SIZE >> 2)])
+
+/*
+ * NB: This releases the lock on the Inbound Queue that GET_IO_IQ_ENTRY
+ * acquired below.
+ */
+#ifdef	DEBUG
+#define	INC_IQ_ENTRY(hwp, qnum)						\
+{									\
+	uint32_t htag;							\
+	ASSERT(mutex_owned(&(hwp)->iqp_lock[qnum]));			\
+	htag = hwp->iqp[qnum][(hwp->shadow_iqpi[qnum] *			\
+	    (PMCS_QENTRY_SIZE >> 2)) + 1];				\
+	mutex_enter(&(hwp)->dbglock);					\
+	pmcs_iqp_trace(hwp, qnum);					\
+	mutex_exit(&(hwp)->dbglock);					\
+	INCQI(hwp->shadow_iqpi[qnum], hwp->ioq_depth);			\
+	if (ddi_dma_sync(hwp->cip_handles, 0, 0,			\
+	    DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {			\
+		pmcs_prt(hwp, PMCS_PRT_DEBUG, "Condition failed at "	\
+		    " %s():%d", __func__, __LINE__);			\
+	}								\
+	pmcs_wr_iqpi(hwp, qnum, hwp->shadow_iqpi[qnum]);		\
+	mutex_exit(&(hwp)->iqp_lock[qnum]);				\
+	mutex_enter(&(hwp)->dbglock);					\
+	hwp->ftag_lines[hwp->fti] = __LINE__;				\
+	hwp->ftime[hwp->fti] = gethrtime();				\
+	hwp->ftags[hwp->fti++] = htag;					\
+	mutex_exit(&(hwp)->dbglock);					\
+}
+#else
+#define	INC_IQ_ENTRY(hwp, qnum)						\
+	INCQI(hwp->shadow_iqpi[qnum], hwp->ioq_depth);			\
+	if (ddi_dma_sync(hwp->cip_handles, 0, 0,			\
+	    DDI_DMA_SYNC_FORDEV) != DDI_SUCCESS) {			\
+		pmcs_prt(hwp, PMCS_PRT_DEBUG, "Condition failed at "	\
+		    " %s():%d", __func__, __LINE__);			\
+	}								\
+	pmcs_wr_iqpi(hwp, qnum, hwp->shadow_iqpi[qnum]);		\
+	mutex_exit(&(hwp)->iqp_lock[qnum])
+#endif
+
+
+/*
+ * NB: sucessfull acquisition of an IO Inbound Queue
+ * entry leaves the lock on that Inbound Queue held.
+ */
+#define	GET_IO_IQ_ENTRY(pwp, msg, did, iq)				\
+	iq = did & PMCS_IO_IQ_MASK;					\
+	mutex_enter(&(pwp)->iqp_lock[iq]);				\
+	msg = GET_IQ_ENTRY(pwp, iq);					\
+	if (msg == NULL) {						\
+		mutex_exit(&(pwp)->iqp_lock[iq]);			\
+		for (iq = 0; iq <= PMCS_NON_HIPRI_QUEUES; iq++) {	\
+			mutex_enter(&(pwp)->iqp_lock[iq]);		\
+			msg = GET_IQ_ENTRY(pwp, iq);			\
+			if (msg) {					\
+				break;					\
+			}						\
+			mutex_exit(&(pwp->iqp_lock[iq]));		\
+		}							\
+	}
+
+/*
+ * Outbound Queue Macros
+ *
+ * Outbound Queue Consumer indices live inside the card.
+ *
+ * Outbound Queue Producer indices live in host memory. When the card
+ * wants to send an IOMB, it uses the producer index to find the spot
+ * to write the IOMB. After it's done it updates the producer index
+ * and interrupts the host. The host reads the producer index (from
+ * host memory) and reads IOMBs up to but not including that index.
+ * It writes that index back to the consumer index on the card,
+ * signifying that it has read up to that which the card has sent.
+ */
+#define	GET_OQ_ENTRY(hwp, qn, ix, o) \
+	&hwp->oqp[qn][QI2O(ix, o, hwp->ioq_depth) >> 2]
+
+#define	STEP_OQ_ENTRY(hwp, qn, ix, n)	ix = ADDQI(ix, n, hwp->ioq_depth)
+
+#define	SYNC_OQ_ENTRY(hwp, qn, ci, pi) 		\
+	pmcs_wr_oqci(hwp, qn, ci);		\
+	(hwp)->oqci[qn] = ci;			\
+	(hwp)->oqpi[qn] = pi
+
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _PMCS_IOMB_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_mpi.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,222 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * PMC 8x6G Message Passing Interface Definitions
+ */
+#ifndef	_PMCS_MPI_H
+#define	_PMCS_MPI_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	PMCS_DWRD(x)	(x << 2)
+
+/*
+ * MPI Configuration Table Offsets
+ */
+#define	PMCS_MPI_AS	PMCS_DWRD(0)	/* ASCII Signature */
+#define	PMCS_SIGNATURE	0x53434D50
+
+#define	PMCS_MPI_IR	PMCS_DWRD(1)	/* Interface Revision */
+#define	PMCS_MPI_REVISION1	1
+
+#define	PMCS_MPI_FW	PMCS_DWRD(2)	/* Firmware Version */
+#define	PMCS_FW_TYPE(hwp)		(hwp->fw & 0xf)
+#define		PMCS_FW_TYPE_RELEASED		0
+#define		PMCS_FW_TYPE_DEVELOPMENT	1
+#define		PMCS_FW_TYPE_ALPHA		2
+#define		PMCS_FW_TYPE_BETA		3
+#define	PMCS_FW_VARIANT(hwp)		((hwp->fw >> 4) & 0xf)
+#define	PMCS_FW_MAJOR(hwp)		((hwp->fw >> 24) & 0xff)
+#define	PMCS_FW_MINOR(hwp)		((hwp->fw >> 16) & 0xff)
+#define	PMCS_FW_MICRO(hwp)		((hwp->fw >>  8) & 0xff)
+#define	PMCS_FW_REV(hwp)		((hwp->fw >> 8) & 0xffffff)
+#define	PMCS_FW_VERSION(maj, min, mic)	((maj << 16)|(min << 8)|mic)
+
+#define	PMCS_MPI_MOIO	PMCS_DWRD(3)	/* Maximum # of outstandiong I/Os */
+#define	PMCS_MPI_INFO0	PMCS_DWRD(4)	/* Maximum S/G Elem, Max Dev Handle */
+#define	PMCS_MSGL(x)	(x & 0xffff)
+#define	PMCS_MD(x)	((x >> 16) & 0xffff)
+
+#define	PMCS_MPI_INFO1	PMCS_DWRD(5)	/* Info #0 */
+
+#define	PMCS_MNIQ(x)	(x & 0xff)		/* Max # of Inbound Queues */
+#define	PMCS_MNOQ(x)	((x >> 8) & 0xff)	/* Max # of Outbound Queues */
+#define	PMCS_HPIQ(x)	((x >> 16) & 0x1)	/* High Pri Queue Supported */
+#define	PMCS_ICS(x)	((x >> 18) & 0x1)	/* Interrupt Coalescing */
+#define	PMCS_NPHY(x)	((x >> 19) & 0x3f)	/* Numbers of PHYs */
+#define	PMCS_SASREV(x)	((x >> 25) & 0x7)	/* SAS Revision Specification */
+
+#define	PMCS_MPI_GSTO	PMCS_DWRD(6)	/* General Status Table Offset */
+#define	PMCS_MPI_IQCTO	PMCS_DWRD(7)	/* Inbound Queue Config Table Offset */
+#define	PMCS_MPI_OQCTO	PMCS_DWRD(8)	/* Outbound Queue Config Table Offset */
+
+#define	PMCS_MPI_INFO2	PMCS_DWRD(9)	/* Info #1 */
+
+#define	IQ_NORMAL_PRI_DEPTH_SHIFT	0
+#define	IQ_NORMAL_PRI_DEPTH_MASK	0xff
+#define	IQ_HIPRI_PRI_DEPTH_SHIFT	8
+#define	IQ_HIPRI_PRI_DEPTH_MASK		0xff00
+#define	GENERAL_EVENT_OQ_SHIFT		16
+#define	GENERAL_EVENT_OQ_MASK		0xff0000
+#define	DEVICE_HANDLE_REMOVED_SHIFT	24
+#define	DEVICE_HANDLE_REMOVED_MASK	0xff000000ul
+
+#define	PMCS_MPI_EVQS	PMCS_DWRD(0xA)	/* SAS Event Queues */
+#define	PMCS_MPI_EVQSET(pwp, oq, phy)	{				\
+	uint32_t woff = phy / 4;					\
+	uint32_t shf = (phy % 4) * 8;					\
+	uint32_t tmp = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_EVQS + (woff << 2)); \
+	tmp &= ~(0xff << shf);						\
+	tmp |= ((oq & 0xff) << shf);					\
+	pmcs_wr_mpi_tbl(pwp, PMCS_MPI_EVQS + (woff << 2), tmp);		\
+}
+
+#define	PMCS_MPI_SNCQ	PMCS_DWRD(0xC)	/* Sata NCQ Notification Queues */
+#define	PMCS_MPI_NCQSET(pwp, oq, phy)	{				\
+	uint32_t woff = phy / 4;					\
+	uint32_t shf = (phy % 4) * 8;					\
+	uint32_t tmp = pmcs_rd_mpi_tbl(pwp, PMCS_MPI_SNCQ + (woff << 2)); \
+	tmp &= ~(0xff << shf);						\
+	tmp |= ((oq & 0xff) << shf);					\
+	pmcs_wr_mpi_tbl(pwp, PMCS_MPI_SNCQ + (woff << 2), tmp);		\
+}
+
+/*
+ * I_T Nexus Target Event Notification Queue
+ */
+#define	PMCS_MPI_IT_NTENQ	PMCS_DWRD(0xE)
+
+/*
+ * SSP Target Event Notification Queue
+ */
+#define	PMCS_MPI_SSP_TENQ	PMCS_DWRD(0x10)
+
+/*
+ * SMP Target Event Notification Queue
+ */
+#define	PMCS_MPI_SMP_TENQ	PMCS_DWRD(0x12)
+
+/*
+ * This specifies a log buffer in host memory for the MSGU.
+ */
+#define	PMCS_MPI_MELBAH	PMCS_DWRD(0x14)	/* MSGU Log Buffer high 32 bits */
+#define	PMCS_MPI_MELBAL	PMCS_DWRD(0x15)	/* MSGU Log Buffer low 32 bits */
+#define	PMCS_MPI_MELBS	PMCS_DWRD(0x16)	/* size in bytes of MSGU log buffer */
+#define	PMCS_MPI_MELSEV	PMCS_DWRD(0x17)	/* Log Severity */
+
+/*
+ * This specifies a log buffer in host memory for the IOP.
+ */
+#define	PMCS_MPI_IELBAH	PMCS_DWRD(0x18)	/* IOP Log Buffer high 32 bits */
+#define	PMCS_MPI_IELBAL	PMCS_DWRD(0x19)	/* IOP Log Buffer low 32 bits */
+#define	PMCS_MPI_IELBS	PMCS_DWRD(0x1A)	/* size in bytes of IOP log buffer */
+#define	PMCS_MPI_IELSEV	PMCS_DWRD(0x1B)	/* Log Severity */
+
+/*
+ * Fatal Error Handling
+ */
+#define	PMCS_MPI_FERR		PMCS_DWRD(0x1C)
+#define	PMCS_FERRIE		0x1	/* Fatal Err Interrupt Enable */
+#define	PMCS_FERIV_MASK		0xff00	/* Fatal Err Interrupt Mask */
+#define	PMCS_FERIV_SHIFT	8	/* Fatal Err Interrupt Shift */
+
+#define	PMCS_MPI_IRAE		0x20000	/* Interrupt Reassertion Enable */
+#define	PMCS_MPI_IRAU		0x40000	/* Interrupt Reassertion Unit */
+#define	PMCS_MPI_IRAD_MASK	0xfff80000 /* Reassertion Delay Mask */
+
+#define	PMCS_FERDOMSGU		PMCS_DWRD(0x1D)
+#define	PMCS_FERDLMSGU		PMCS_DWRD(0x1E)
+#define	PMCS_FERDOIOP		PMCS_DWRD(0x1F)
+#define	PMCS_FERDLIOP		PMCS_DWRD(0x20)
+
+/*
+ * MPI GST Table Offsets
+ */
+
+#define	PMCS_GST_BASE		0
+#define	PMCS_GST_IQFRZ0		(PMCS_GST_BASE + PMCS_DWRD(1))
+#define	PMCS_GST_IQFRZ1		(PMCS_GST_BASE + PMCS_DWRD(2))
+#define	PMCS_GST_MSGU_TICK	(PMCS_GST_BASE + PMCS_DWRD(3))
+#define	PMCS_GST_IOP_TICK	(PMCS_GST_BASE + PMCS_DWRD(4))
+#define	PMCS_GST_PHY_INFO(x)	(PMCS_GST_BASE + PMCS_DWRD(0x6) + PMCS_DWRD(x))
+#define	PMCS_GST_RERR_BASE	(PMCS_GST_BASE + PMCS_DWRD(0x11))
+#define	PMCS_GST_RERR_INFO(x)	(PMCS_GST_RERR_BASE + PMCS_DWRD(x))
+
+#define	PMCS_MPI_S(x)		((x) & 0x7)
+#define	PMCS_QF(x)		(((x) >> 3) & 0x1)
+#define	PMCS_GSTLEN(x)		(((x) >> 4) & 0x3fff)
+#define	PMCS_HMI_ERR(x)		(((x) >> 16) & 0xffff)
+
+#define	PMCS_MPI_STATE_NIL	0
+#define	PMCS_MPI_STATE_INIT	1
+#define	PMCS_MPI_STATE_DEINIT	2
+#define	PMCS_MPI_STATE_ERR	3
+
+/*
+ * MPI Inbound Queue Configuration Table Offsets
+ *
+ * Each Inbound Queue configuration area consumes 8 DWORDS (32 bit words),
+ * or 32 bytes.
+ */
+#define	PMCS_IQC_PARMX(x)	((x) << 5)
+#define	PMCS_IQBAHX(x)		(((x) << 5) + 4)
+#define	PMCS_IQBALX(x)		(((x) << 5) + 8)
+#define	PMCS_IQCIBAHX(x)	(((x) << 5) + 12)
+#define	PMCS_IQCIBALX(x)	(((x) << 5) + 16)
+#define	PMCS_IQPIBARX(x)	(((x) << 5) + 20)
+#define	PMCS_IQPIOFFX(x)	(((x) << 5) + 24)
+#define	PMCS_IQDX(x)		((x) & 0xffff)
+#define	PMCS_IQESX(x)		(((x) >> 16) & 0x3fff)
+#define	PMCS_IQPX(x)		(((x) >> 30) & 0x3)
+
+/*
+ * MPI Outbound Queue Configuration Table Offsets
+ *
+ * Each Outbound Queue configuration area consumes 9 DWORDS (32 bit words),
+ * or 36 bytes.
+ */
+#define	PMCS_OQC_PARMX(x)	(x * 36)
+#define	PMCS_OQBAHX(x)		((x * 36) + 4)
+#define	PMCS_OQBALX(x)		((x * 36) + 8)
+#define	PMCS_OQPIBAHX(x)	((x * 36) + 12)
+#define	PMCS_OQPIBALX(x)	((x * 36) + 16)
+#define	PMCS_OQCIBARX(x)	((x * 36) + 20)
+#define	PMCS_OQCIOFFX(x)	((x * 36) + 24)
+#define	PMCS_OQIPARM(x)		((x * 36) + 28)
+#define	PMCS_OQDICX(x)		((x * 36) + 32)
+
+#define	PMCS_OQDX(x)		((x) & 0xffff)
+#define	PMCS_OQESX(x)		(((x) >> 16) & 0x3fff)
+#define	PMCS_OQICT(x)		((x) & 0xffff)
+#define	PMCS_OQICC(x)		(((x) >> 16) & 0xff)
+#define	PMCS_OQIV(x)		(((x) >> 24) & 0xff)
+
+#define	OQIEX			(1 << 30)
+
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _PMCS_MPI_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_param.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,138 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * PMC Compile Time Tunable Parameters
+ */
+#ifndef	_PMCS_PARAM_H
+#define	_PMCS_PARAM_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Maximum number of microseconds we will try to configure a PHY
+ */
+#define	PMCS_MAX_CONFIG_TIME	(60 * 1000000)
+
+#define	PMCS_MAX_OQ		64	/* maximum number of OutBound Queues */
+#define	PMCS_MAX_IQ		64	/* maximum number of InBound Queues */
+
+#define	PMCS_MAX_PORTS		16	/* maximum port contexts */
+
+#define	PMCS_MAX_XPND		16	/* 16 levels of expansion */
+
+#define	PMCS_INDICES_SIZE	512
+
+#define	PMCS_MIN_CHUNK_PAGES	512
+#define	PMCS_ADDTL_CHUNK_PAGES	8
+
+/*
+ * Scratch area has to hold Max SMP Request and Max SMP Response,
+ * plus some slop.
+ */
+#define	PMCS_SCRATCH_SIZE	2304
+#define	PMCS_INITIAL_DMA_OFF	PMCS_INDICES_SIZE+PMCS_SCRATCH_SIZE
+#define	PMCS_CONTROL_SIZE	ptob(1)
+
+/*
+ * 2M bytes was allocated to firmware log and split between two logs
+ */
+#define	PMCS_FWLOG_SIZE		(2 << 20)
+#define	PMCS_FWLOG_MAX		5	/* maximum logging level */
+#define	SATLSIZE		1024
+
+/*
+ * PMCS_NQENTRY is tunable by setting pmcs-num-io-qentries
+ */
+#define	PMCS_NQENTRY		512	/* 512 entries per queue */
+#define	PMCS_MIN_NQENTRY	32	/* No less than 32 entries per queue */
+#define	PMCS_QENTRY_SIZE	64	/* 64 bytes per entry */
+#define	PMCS_MSG_SIZE		(PMCS_QENTRY_SIZE >> 2)
+
+/*
+ * Watchdog interval, in usecs.
+ * NB: Needs to be evenly divisible by 10
+ */
+#define	PMCS_WATCH_INTERVAL	250000	/* watchdog interval in us */
+
+/*
+ * Inbound Queue definitions
+ */
+#define	PMCS_NIQ		9	/* 9 Inbound Queues */
+#define	PMCS_IO_IQ_MASK		7	/* IO queues are 0..7 */
+#define	PMCS_IQ_OTHER		8	/* "Other" queue is 8 (HiPri) */
+#define	PMCS_NON_HIPRI_QUEUES	PMCS_IO_IQ_MASK
+
+/*
+ * Outbound Queue definitions
+ *
+ * Note that the OQ definitions map to bits set in
+ * the Outbound Doorbell register to indicate service
+ * is needed on one of these queues.
+ */
+#define	PMCS_NOQ		3	/* 3 Outbound Queues */
+
+#define	PMCS_OQ_IODONE		0	/* I/O completion Outbound Queue */
+#define	PMCS_OQ_GENERAL		1	/* General Outbound Queue */
+#define	PMCS_OQ_EVENTS		2	/* Event Outbound Queue */
+
+
+/*
+ * External Scatter Gather come in chunks- each this many deep.
+ */
+#define	PMCS_SGL_NCHUNKS	16	/* S/G List Chunk Size */
+#define	PMCS_MAX_CHUNKS		32	/* max chunks per command */
+
+/*
+ * MSI/MSI-X related definitions.
+ *
+ * These are the maximum number of interrupt vectors we could use.
+ */
+#define	PMCS_MAX_MSIX		(PMCS_NOQ + 1)
+#define	PMCS_MAX_MSI		PMCS_MAX_MSIX
+#define	PMCS_MAX_FIXED		1
+
+#define	PMCS_MSIX_IODONE	PMCS_OQ_IODONE	/* I/O Interrupt vector */
+#define	PMCS_MSIX_GENERAL	PMCS_OQ_GENERAL	/* General Interrupt vector */
+#define	PMCS_MSIX_EVENTS	PMCS_OQ_EVENTS	/* Events Interrupt vector */
+#define	PMCS_MSIX_FATAL		(PMCS_MAX_MSIX-1)	/* Fatal Int vector */
+
+#define	PMCS_FATAL_INTERRUPT	15	/* fatal interrupt OBDB bit */
+
+/*
+ * Blessed firmware version
+ */
+#define	PMCS_FIRMWARE_CODE_NAME		"firmware"
+#define	PMCS_FIRMWARE_ILA_NAME		"ila"
+#define	PMCS_FIRMWARE_SPCBOOT_NAME	"SPCBoot"
+#define	PMCS_FIRMWARE_START_SUF		".bin_start"
+#define	PMCS_FIRMWARE_END_SUF		".bin_end"
+#define	PMCS_FIRMWARE_FILENAME		"misc/pmcs/pmcs8001fw"
+#define	PMCS_FIRMWARE_VERSION_NAME	"pmcs8001_fwversion"
+
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _PMCS_PARAM_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_proto.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,353 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file provides prototype function definitions.
+ */
+#ifndef	_PMCS_PROTO_H
+#define	_PMCS_PROTO_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+
+typedef enum {
+	PMCS_PRT_DEBUG = 0,
+	PMCS_PRT_DEBUG1,
+	PMCS_PRT_DEBUG2,
+	PMCS_PRT_DEBUG3,
+	PMCS_PRT_DEBUG_CONFIG,
+	PMCS_PRT_DEBUG_IPORT,
+	PMCS_PRT_DEBUG_MAP,
+	PMCS_PRT_DEBUG_UNDERFLOW,
+	PMCS_PRT_DEBUG_SCSI_STATUS,
+	PMCS_PRT_DEBUG_PHY_LOCKING,
+	PMCS_PRT_DEBUG_DEV_STATE,
+	PMCS_PRT_DEBUG_DEVEL,
+	PMCS_PRT_INFO,
+	PMCS_PRT_WARN,
+	PMCS_PRT_ERR
+} pmcs_prt_level_t;
+
+#define	pmcs_prt(pwp, level, fmt...) {			\
+	int lvl = level;				\
+	if (((pwp->debug_mask & (1 << lvl)) != 0) ||	\
+	    (lvl > PMCS_PRT_DEBUG_DEVEL)) {		\
+		pmcs_prt_impl(pwp, lvl, fmt);		\
+	}						\
+}
+
+/*PRINTFLIKE3*/
+void
+pmcs_prt_impl(pmcs_hw_t *, pmcs_prt_level_t, const char *, ...)
+    __KPRINTFLIKE(3);
+
+boolean_t pmcs_assign_device(pmcs_hw_t *, pmcs_xscsi_t *);
+void pmcs_remove_device(pmcs_hw_t *, pmcs_phy_t *);
+void pmcs_handle_dead_phys(pmcs_hw_t *);
+
+int pmcs_acquire_scratch(pmcs_hw_t *, boolean_t);
+void pmcs_release_scratch(pmcs_hw_t *);
+
+/* get a work structure */
+pmcwork_t *pmcs_gwork(pmcs_hw_t *, uint32_t, pmcs_phy_t *);
+
+/* put a work structure */
+void pmcs_pwork(pmcs_hw_t *, struct pmcwork *);
+
+/* given a tag, find a work structure */
+pmcwork_t *pmcs_tag2wp(pmcs_hw_t *, uint32_t);
+
+/*
+ * Abort function
+ */
+int pmcs_abort(pmcs_hw_t *, pmcs_phy_t *, uint32_t, int, int);
+
+/*
+ * SSP Task Management Function
+ */
+int pmcs_ssp_tmf(pmcs_hw_t *, pmcs_phy_t *, uint8_t, uint32_t, uint64_t,
+    uint32_t *);
+
+/*
+ * Abort NCQ function
+ */
+int pmcs_sata_abort_ncq(pmcs_hw_t *, pmcs_phy_t *);
+
+/*
+ * Interrupt Functions
+ */
+void pmcs_general_intr(pmcs_hw_t *);
+void pmcs_iodone_intr(pmcs_hw_t *);
+void pmcs_event_intr(pmcs_hw_t *);
+void pmcs_timed_out(pmcs_hw_t *, uint32_t, const char *);
+
+/*
+ * Abort handler
+ */
+int pmcs_abort_handler(pmcs_hw_t *);
+
+/*
+ * Deregister all expander connected devices
+ */
+void pmcs_deregister_devices(pmcs_hw_t *, pmcs_phy_t *);
+int pmcs_register_device(pmcs_hw_t *, pmcs_phy_t *);
+void pmcs_deregister_device(pmcs_hw_t *, pmcs_phy_t *);
+
+/*
+ * endian transform a data structure
+ */
+void pmcs_endian_transform(pmcs_hw_t *, void *, void *, const uint8_t *);
+
+/* get the connection rate string */
+const char *pmcs_get_rate(unsigned int);
+
+/* get the device type string */
+const char *pmcs_get_typename(pmcs_dtype_t pmcs_dtype);
+
+/* get the SAS Task Management function name */
+const char *pmcs_tmf2str(int);
+
+/* get the PMC status string */
+const char *pmcs_status_str(uint32_t);
+
+/*
+ * WWN to Byte Array and vice versa conversion
+ */
+uint64_t pmcs_barray2wwn(uint8_t[8]);
+void pmcs_wwn2barray(uint64_t, uint8_t[8]);
+
+/*
+ * Print f/w version
+ */
+void pmcs_report_fwversion(pmcs_hw_t *);
+
+/*
+ * Build a device name.
+ */
+void pmcs_phy_name(pmcs_hw_t *, pmcs_phy_t *, char *, size_t);
+
+/*
+ * Find a PHY by device_id
+ */
+pmcs_phy_t *pmcs_find_phy_by_devid(pmcs_hw_t *, uint32_t);
+
+/*
+ * Find a PHY by wwn
+ */
+pmcs_phy_t *pmcs_find_phy_by_wwn(pmcs_hw_t *, uint64_t);
+
+/*
+ * Find a PHY by sas_address
+ */
+pmcs_phy_t *pmcs_find_phy_by_sas_address(pmcs_hw_t *, pmcs_iport_t *,
+    pmcs_phy_t *, char *);
+
+/*
+ * Print out a FIS
+ */
+void pmcs_fis_dump(pmcs_hw_t *, fis_t);
+
+/*
+ * Print an IOMB
+ */
+void pmcs_print_entry(pmcs_hw_t *, int, char *, void *);
+
+void pmcs_spinup_release(pmcs_hw_t *, pmcs_phy_t *phyp);
+
+/*
+ * Handler for events - can be called from interrupt level or from worker thread
+ */
+void pmcs_ack_events(pmcs_hw_t *);
+
+/*
+ * This function does some initial setup and hardware validation
+ */
+int pmcs_setup(pmcs_hw_t *);
+
+/*
+ * These functions start and stop the MPI (message passing interface)
+ */
+int pmcs_start_mpi(pmcs_hw_t *);
+int pmcs_stop_mpi(pmcs_hw_t *);
+
+/*
+ * This function checks firmware revisions against required revisions
+ * and attempts to flash new firmware (if possible).
+ */
+int pmcs_firmware_update(pmcs_hw_t *);
+
+/*
+ * This function runs ECHO commands to test both interrupts and queues
+ */
+int pmcs_echo_test(pmcs_hw_t *);
+
+/*
+ * These functions start, reset, and stop the physical chip PHYs
+ */
+int pmcs_start_phy(pmcs_hw_t *, int, int, int);
+int pmcs_start_phys(pmcs_hw_t *);
+void pmcs_stop_phy(pmcs_hw_t *, int);
+void pmcs_stop_phys(pmcs_hw_t *);
+
+/*
+ * These functions setup/teardown iport tgtmap
+ */
+int pmcs_iport_tgtmap_create(pmcs_iport_t *);
+int pmcs_iport_tgtmap_destroy(pmcs_iport_t *);
+
+/*
+ * Utility and wrapper functions for SAS_DIAG_EXECUTE
+ */
+int pmcs_sas_diag_execute(pmcs_hw_t *, uint32_t, uint32_t, uint8_t);
+int pmcs_get_diag_report(pmcs_hw_t *, uint32_t, uint8_t);
+int pmcs_clear_diag_counters(pmcs_hw_t *, uint8_t);
+
+/*
+ * Get current firmware timestamp
+ */
+int pmcs_get_time_stamp(pmcs_hw_t *, uint64_t *);
+
+/*
+ * Register Dump (including "internal" registers)
+ */
+void pmcs_register_dump(pmcs_hw_t *);
+void pmcs_iqp_trace(pmcs_hw_t *, uint32_t);
+void pmcs_register_dump_int(pmcs_hw_t *);
+int pmcs_dump_binary(pmcs_hw_t *, uint32_t *, uint32_t,
+    uint32_t, caddr_t, uint32_t);
+int pmcs_dump_feregs(pmcs_hw_t *, uint32_t *, uint8_t,
+    caddr_t, uint32_t);
+
+/*
+ * This function perform a soft reset.
+ * Hard reset is platform specific.
+ */
+int pmcs_soft_reset(pmcs_hw_t *, boolean_t);
+
+/*
+ * Some more reset functions
+ */
+int pmcs_reset_dev(pmcs_hw_t *, pmcs_phy_t *, uint64_t);
+int pmcs_reset_phy(pmcs_hw_t *, pmcs_phy_t *, uint8_t);
+
+/*
+ * These functions do topology configuration changes
+ */
+void pmcs_discover(pmcs_hw_t *);
+void pmcs_set_changed(pmcs_hw_t *, pmcs_phy_t *, boolean_t, int);
+void pmcs_kill_changed(pmcs_hw_t *, pmcs_phy_t *, int);
+void pmcs_clear_phy(pmcs_hw_t *, pmcs_phy_t *);
+int pmcs_kill_device(pmcs_hw_t *, pmcs_phy_t *);
+
+/*
+ * Firmware flash function
+ */
+int pmcs_fw_flash(pmcs_hw_t *, pmcs_fw_hdr_t *, uint32_t);
+
+/*
+ * Set a new value for the interrupt coalescing timer.  If it's being set
+ * to zero (disabling), then re-enable auto clear if necessary.  If it's
+ * being changed from zero, turn off auto clear if it was on.
+ */
+typedef enum {
+	DECREASE_TIMER = 0,
+	INCREASE_TIMER
+} pmcs_coal_timer_adj_t;
+
+void pmcs_check_intr_coal(void *arg);
+void pmcs_set_intr_coal_timer(pmcs_hw_t *pwp, pmcs_coal_timer_adj_t adj);
+
+/*
+ * Misc supporting routines
+ */
+void pmcs_check_iomb_status(pmcs_hw_t *pwp, uint32_t *iomb);
+void pmcs_clear_xp(pmcs_hw_t *, pmcs_xscsi_t *);
+
+int pmcs_run_sata_cmd(pmcs_hw_t *, pmcs_phy_t *, fis_t, uint32_t,
+    uint32_t, uint32_t);
+int pmcs_sata_identify(pmcs_hw_t *, pmcs_phy_t *);
+void pmcs_sata_work(pmcs_hw_t *);
+boolean_t pmcs_dma_setup(pmcs_hw_t *pwp, ddi_dma_attr_t *dma_attr,
+    ddi_acc_handle_t *acch, ddi_dma_handle_t *dmah, size_t length,
+    caddr_t *kvp, uint64_t *dma_addr);
+void pmcs_fm_ereport(pmcs_hw_t *pwp, char *detail);
+int pmcs_check_dma_handle(ddi_dma_handle_t handle);
+int pmcs_check_acc_handle(ddi_acc_handle_t handle);
+int pmcs_check_acc_dma_handle(pmcs_hw_t *pwp);
+int pmcs_get_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type, uint8_t nvmd,
+    uint32_t offset, char *buf, uint32_t size_left);
+boolean_t pmcs_set_nvmd(pmcs_hw_t *pwp, pmcs_nvmd_type_t nvmd_type,
+    uint8_t *buf, size_t len);
+void pmcs_complete_work_impl(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *iomb,
+    size_t amt);
+void pmcs_flush_target_queues(pmcs_hw_t *, pmcs_xscsi_t *, uint8_t);
+boolean_t pmcs_iport_has_targets(pmcs_hw_t *, pmcs_iport_t *);
+void pmcs_free_dma_chunklist(pmcs_hw_t *);
+int pmcs_get_dev_state(pmcs_hw_t *, pmcs_xscsi_t *, uint8_t *);
+int pmcs_set_dev_state(pmcs_hw_t *, pmcs_xscsi_t *, uint8_t);
+void pmcs_dev_state_recovery(pmcs_hw_t *, pmcs_phy_t *);
+int pmcs_send_err_recovery_cmd(pmcs_hw_t *, uint8_t, pmcs_xscsi_t *);
+void pmcs_start_ssp_event_recovery(pmcs_hw_t *pwp, pmcwork_t *pwrk,
+    uint32_t *iomb, size_t amt);
+void pmcs_ssp_event_recovery(pmcs_hw_t *);
+
+pmcs_iport_t *pmcs_get_iport_by_phy(pmcs_hw_t *pwp, pmcs_phy_t *pptr);
+void pmcs_rele_iport(pmcs_iport_t *iport);
+int pmcs_iport_configure_phys(pmcs_iport_t *iport);
+
+void pmcs_lock_phy(pmcs_phy_t *);
+void pmcs_unlock_phy(pmcs_phy_t *);
+
+void pmcs_destroy_target(pmcs_xscsi_t *);
+void pmcs_phymap_activate(void *, char *, void **);
+void pmcs_phymap_deactivate(void *, char *, void *);
+void pmcs_add_phy_to_iport(pmcs_iport_t *, pmcs_phy_t *);
+void pmcs_remove_phy_from_iport(pmcs_iport_t *, pmcs_phy_t *);
+void pmcs_free_all_phys(pmcs_hw_t *, pmcs_phy_t *);
+void pmcs_free_phys(pmcs_hw_t *, pmcs_phy_t *);
+
+int pmcs_phy_constructor(void *, void *, int);
+void pmcs_phy_destructor(void *, void *);
+
+void pmcs_inc_phy_ref_count(pmcs_phy_t *);
+void pmcs_dec_phy_ref_count(pmcs_phy_t *);
+
+/* Worker thread */
+void pmcs_worker(void *);
+
+pmcs_phy_t *pmcs_get_root_phy(pmcs_phy_t *);
+pmcs_xscsi_t *pmcs_get_target(pmcs_iport_t *, char *);
+
+void pmcs_fatal_handler(pmcs_hw_t *);
+
+/*
+ * Schedule device state recovery for this device immediately
+ */
+void pmcs_start_dev_state_recovery(pmcs_xscsi_t *, pmcs_phy_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _PMCS_PROTO_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_reg.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,378 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * PMC 8x6G register definitions
+ */
+#ifndef	_PMCS_REG_H
+#define	_PMCS_REG_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * PCI Constants
+ */
+#define	PMCS_VENDOR_ID	0x11F8
+#define	PMCS_DEVICE_ID	0x8001
+
+#define	PMCS_PM8001_REV_A	0
+#define	PMCS_PM8001_REV_B	1
+#define	PMCS_PM8001_REV_C	2
+
+#define	PMCS_REGSET_0		1
+#define	PMCS_REGSET_1		2
+#define	PMCS_REGSET_2		3
+#define	PMCS_REGSET_3		4
+
+
+/*
+ * PCIe BARs - 4 64KB memory regions
+ *
+ *	BAR0-1	64KiB
+ *	BAR2-3	64KiB
+ *	BAR4	64KiB
+ *	BAR5	64KiB
+ */
+
+/*
+ * The PMC 8x6G registers are defined by BARs in PCIe space.
+ *
+ * Four memory region BARS are used.
+ *
+ * The first is for the Messaging Unit.
+ *
+ * The second 64KiB region contains the PCS/PMA registers and some of the
+ * Top-Level registers.
+ *
+ * The third 64KiB region is a 64KiB window on the rest of the chip registers
+ * which can be shifted by writing a register in the second region.
+ *
+ * The fourth 64KiB region is for the message passing area.
+ */
+
+/*
+ * Messaging Unit Register Offsets
+ */
+#define	PMCS_MSGU_IBDB		0x04	/* Inbound Doorbell */
+#define	PMCS_MSGU_IBDB_CLEAR	0x20	/* InBound Doorbell Clear */
+#define	PMCS_MSGU_OBDB		0x3c	/* OutBound Doorbell */
+#define	PMCS_MSGU_OBDB_CLEAR	0x40	/* OutBound Doorbell Clear */
+#define	PMCS_MSGU_SCRATCH0	0x44	/* Scratchpad 0 */
+#define	PMCS_MSGU_SCRATCH1	0x48	/* Scratchpad 1 */
+#define	PMCS_MSGU_SCRATCH2	0x4C	/* Scratchpad 2 */
+#define	PMCS_MSGU_SCRATCH3	0x50	/* Scratchpad 3 */
+#define	PMCS_MSGU_HOST_SCRATCH0	0x54	/* Host Scratchpad 0 */
+#define	PMCS_MSGU_HOST_SCRATCH1	0x58	/* Host Scratchpad 1 */
+#define	PMCS_MSGU_HOST_SCRATCH2	0x5C	/* Host Scratchpad 2 */
+#define	PMCS_MSGU_HOST_SCRATCH3	0x60	/* Host Scratchpad 3 */
+#define	PMCS_MSGU_HOST_SCRATCH4	0x64	/* Host Scratchpad 4 */
+#define	PMCS_MSGU_HOST_SCRATCH5	0x68	/* Host Scratchpad 5 */
+#define	PMCS_MSGU_HOST_SCRATCH6	0x6C	/* Host Scratchpad 6 */
+#define	PMCS_MSGU_HOST_SCRATCH7	0x70	/* Host Scratchpad 7 */
+#define	PMCS_MSGU_OBDB_MASK	0x74	/* Outbound Doorbell Mask */
+
+/*
+ * Inbound Doorbell and Doorbell Clear Definitions
+ * NB: The Doorbell Clear register is only used on RevA/8000 parts.
+ */
+#define	PMCS_MSGU_IBDB_MPIIU	0x08	/* Initiate Unfreeze */
+#define	PMCS_MSGU_IBDB_MPIIF	0x04	/* Initiate Freeze */
+#define	PMCS_MSGU_IBDB_MPICTU	0x02	/* Initiate MPI Termination */
+#define	PMCS_MSGU_IBDB_MPIINI	0x01	/* Initiate MPI */
+
+/*
+ * Outbound Doorbell and Doorbell Clear Register
+ *
+ * The Doorbell Clear register is only used on RevA/8000 parts.
+ *
+ * Each bit of the ODR is mapped 1-to-1 to a MSI or MSI-X vector
+ * table entry. There are 32 MSI and 16 MSI-X entries. The top
+ * 16 bits are mapped to the low 16 bits for MSI-X. For legacy
+ * INT-X, any bit will generate a host interrupt.
+ *
+ * Each bit in the Outbound Doorbell Clear is used to clear the
+ * corresponding bit in the ODR. For INT-X it also then deasserts
+ * any interrupt condition.
+ */
+#define	PMCS_MSI_INTS	32
+#define	PMCS_MSIX_INTS	16
+
+/*
+ * Scratchpad 0 Definitions
+ *
+ * When the AAP is ready state (see Scratchpad 1), bits 31:26 is the offset
+ * within PCIe space for another BAR that, when mapped, will point to a region
+ * that conains the MPI Configuration table (the offset of which is in bits
+ * 25:0 of this register)
+ *
+ * When the AAP is in error state, this register contains additional error
+ * information.
+ */
+#define	PMCS_MSGU_MPI_BAR_SHIFT		26
+#define	PMCS_MSGU_MPI_OFFSET_MASK	((1 << PMCS_MSGU_MPI_BAR_SHIFT) - 1)
+
+/*
+ * Scratchpad 1 Definitions
+ *
+ * The bottom two bits are the AAP state of the 8x6G.
+ *
+ * When the AAP is in error state, bits 31:10 contain the error indicator.
+ *
+ */
+#define	PMCS_MSGU_AAP_STATE_MASK	0x03
+#define	PMCS_MSGU_AAP_STATE_POR		0
+#define	PMCS_MSGU_AAP_STATE_SOFT_RESET	1
+#define	PMCS_MSGU_AAP_STATE_ERROR	2
+#define	PMCS_MSGU_AAP_STATE_READY	3
+#define	PMCS_MSGU_AAP_SFR_PROGRESS	0x04
+#define	PMCS_MSGU_AAP_ERROR_MASK	0xfffffc00
+
+/*
+ * Scratchpad 2 Definitions
+ *
+ * Bits 31:10 contain error information if the IOP is in error state.
+ */
+#define	PMCS_MSGU_IOP_STATE_MASK	0x03
+#define	PMCS_MSGU_IOP_STATE_POR		0
+#define	PMCS_MSGU_IOP_STATE_SOFT_RESET	1
+#define	PMCS_MSGU_IOP_STATE_ERROR	2
+#define	PMCS_MSGU_IOP_STATE_READY	3
+
+#define	PMCS_MSGU_HOST_SOFT_RESET_READY	0x04
+#define	PMCS_MSGU_CPU_SOFT_RESET_READY	0x08
+
+/*
+ * Scratchpad 3 Definitions
+ *
+ * Contains additional error information if the IOP is in error state
+ * (see Scratchpad 2)
+ */
+
+/*
+ * Host Scratchpad 0
+ * Soft Reset Signature
+ */
+#define	HST_SFT_RESET_SIG		0x252ACBCD
+
+/*
+ * Host Scratchpad 1
+ *
+ * This is a bit mask for freeze or unfreeze operations for IQs 0..31
+ */
+
+/*
+ * Host Scratchpad 2
+ *
+ * This is a bit mask for freeze or unfreeze operations for IQs 32..63
+ */
+
+/*
+ * Outbound Doorbell Mask Register
+ *
+ * Each bit set here masks bits and interrupt assertion for the corresponding
+ * bit (and vector) in the ODR.
+ */
+
+/*
+ * GSM Registers
+ */
+#define	GSM_BASE_MASK				0x00ffff
+#define	NMI_EN_VPE0_IOP				0x60418
+#define	NMI_EN_VPE0_AAP1			0x70418
+#define	RB6_ACCESS				0x6A80C0
+#define	GSM_CFG_AND_RESET			0x700000
+#define	RAM_ECC_DOUBLE_ERROR_INDICATOR		0x700018
+#define	READ_ADR_PARITY_CHK_EN			0x700038
+#define	WRITE_ADR_PARITY_CHK_EN			0x700040
+#define	WRITE_DATA_PARITY_CHK_EN		0x700048
+#define	READ_ADR_PARITY_ERROR_INDICATOR		0x700058
+#define	WRITE_ADR_PARITY_ERROR_INDICATOR	0x700060
+#define	WRITE_DATA_PARITY_ERROR_INDICATOR	0x700068
+
+/*
+ * GSM Share Memory, IO Status Table and Ring Buffer
+ */
+#define	GSM_SM_BLKSZ				0x10000
+#define	GSM_SM_BASE				0x400000
+#define	IO_STATUS_TABLE_BASE			0x640000
+#define	RING_BUF_STORAGE_0			0x680000
+#define	RING_BUF_STORAGE_1			0x690000
+#define	RING_BUF_PTR_ACC_BASE			0x6A0000
+
+#define	IO_STATUS_TABLE_BLKNM			0x4
+#define	GSM_SM_BLKNM				0x10
+#define	RING_BUF_PTR_OFF			0x1000
+#define	RING_BUF_PTR_SIZE			0xFF8
+#define	RING_BUF_ACC_OFF			0x8000
+#define	RING_BUF_ACC_SIZE			0xFF8
+
+/*
+ * GSM Configuration and Reset Bits
+ */
+#define	MST_XCBI_SW_RSTB		(1 << 14)
+#define	COM_SLV_SW_RSTB			(1 << 13)
+#define	QSSP_SW_RSTB			(1 << 12)
+#define	RAAE_SW_RSTB			(1 << 11)
+#define	RB_1_SW_RSTB			(1 << 9)
+#define	SM_SW_RSTB			(1 << 8)
+
+#define	COHERENCY_GAP_SHIFT		4
+#define	COHERENCY_GAP_MASK		0xf0
+#define	COHERENCY_GAP_DEFAULT		(8 << COHERENCY_GAP_SHIFT)
+
+#define	COHERENCY_MODE			(1 << 3)
+#define	RB_WSTRB_ERRCHK_EN		(1 << 2)
+#define	RAAE_PORT2_EN			(1 << 1)
+#define	GSM_WCI_MODE			(1 << 0)
+#define	PMCS_SOFT_RESET_BITS		\
+	(COM_SLV_SW_RSTB|QSSP_SW_RSTB|RAAE_SW_RSTB|RB_1_SW_RSTB|SM_SW_RSTB)
+
+#define	RB6_NMI_SIGNATURE		0x00001234
+
+/*
+ * PMCS PCI Configuration Registers
+ */
+#define	PMCS_PCI_PMC			0x40
+#define	PMCS_PCI_PMCSR			0x44
+#define	PMCS_PCI_MSI			0x50
+#define	PMCS_PCI_MAL			0x54
+#define	PMCS_PCI_MAU			0x58
+#define	PMCS_PCI_MD			0x5C
+#define	PMCS_PCI_PCIE			0x70
+#define	PMCS_PCI_DEV_CAP		0x74
+#define	PMCS_PCI_DEV_CTRL		0x78
+#define	PMCS_PCI_LINK_CAP		0x7C
+#define	PMCS_PCI_LINK_CTRL		0x80
+#define	PMCS_PCI_MSIX_CAP		0xAC
+#define	PMCS_PCI_TBL_OFFSET		0xB0
+#define	PMCS_PCI_PBA_OFFSET		0xB4
+#define	PMCS_PCI_PCIE_CAP_HD		0x100
+#define	PMCS_PCI_UE_STAT		0x104
+#define	PMCS_PCI_UE_MASK		0x108
+#define	PMCS_PCI_UE_SEV			0x10C
+#define	PMCS_PCI_CE_STAT		0x110
+#define	PMCS_PCI_CE_MASK		0x114
+#define	PMCS_PCI_ADV_ERR_CTRL		0x118
+#define	PMCS_PCI_HD_LOG_DW		0x11C
+
+/*
+ * Top Level Registers
+ */
+/* these registers are in MEMBASE-III */
+#define	PMCS_SPC_RESET			0x0
+#define	PMCS_SPC_BOOT_STRAP		0x8
+#define	PMCS_SPC_DEVICE_ID		0x20
+#define	PMCS_DEVICE_REVISION		0x24
+/* these registers are in MEMBASE-II */
+#define	PMCS_EVENT_INT_ENABLE		0x3040
+#define	PMCS_EVENT_INT_STAT		0x3044
+#define	PMCS_ERROR_INT_ENABLE		0x3048
+#define	PMCS_ERROR_INT_STAT		0x304C
+#define	PMCS_AXI_TRANS			0x3258
+#define	PMCS_OBDB_AUTO_CLR		0x335C
+#define	PMCS_INT_COALESCING_TIMER	0x33C0
+#define	PMCS_INT_COALESCING_CONTROL	0x33C4
+
+
+/*
+ * Chip Reset Register Bits (PMCS_SPC_RESET)
+ *
+ * NB: all bits are inverted. That is, the normal state is '1'.
+ * When '0' is set, the action is taken.
+ */
+#define	PMCS_SPC_HARD_RESET		0x00
+#define	PMCS_SPC_HARD_RESET_CLR		0xffffffff
+
+
+#define	SW_DEVICE_RSTB			(1 << 31)
+#define	PCIE_PC_SXCBI_ARESETN		(1 << 26)
+#define	PMIC_CORE_RSTB			(1 << 25)
+#define	PMIC_SXCBI_ARESETN		(1 << 24)
+#define	LMS_SXCBI_ARESETN		(1 << 23)
+#define	PCS_SXCBI_ARESETN		(1 << 22)
+#define	PCIE_SFT_RSTB			(1 << 21)
+#define	PCIE_PWR_RSTB			(1 << 20)
+#define	PCIE_AL_SXCBI_ARESETN		(1 << 19)
+#define	BDMA_SXCBI_ARESETN		(1 << 18)
+#define	BDMA_CORE_RSTB			(1 << 17)
+#define	DDR2_RSTB			(1 << 16)
+#define	GSM_RSTB			(1 << 8)
+#define	PCS_RSTB			(1 << 7)
+#define	PCS_LM_RSTB			(1 << 6)
+#define	PCS_AAP2_SS_RSTB		(1 << 5)
+#define	PCS_AAP1_SS_RSTB		(1 << 4)
+#define	PCS_IOP_SS_RSTB			(1 << 3)
+#define	PCS_SPBC_RSTB			(1 << 2)
+#define	RAAE_RSTB			(1 << 1)
+#define	OSSP_RSTB			(1 << 0)
+
+
+/*
+ * Timer Enables Register
+ */
+#define	PMCS_TENABLE_WINDOW_OFFSET	0x30000
+#define	PMCS_TENABLE_BASE		0x0209C
+#define	PMCS_TENABLE_MULTIPLIER		0x04000
+
+/*
+ * Special register (MEMBASE-III) for Step 5.5 in soft reset sequence to set
+ * GPIO into tri-state mode (temporary workaround for 1.07.xx beta firmware)
+ */
+#define	PMCS_GPIO_TRISTATE_MODE_ADDR	0x9010C
+#define	PMCS_GPIO_TSMODE_BIT0		(1 << 0)
+#define	PMCS_GPIO_TSMODE_BIT1		(1 << 1)
+
+
+/*
+ * Register Access Inline Functions
+ */
+uint32_t pmcs_rd_msgunit(pmcs_hw_t *, uint32_t);
+uint32_t pmcs_rd_gsm_reg(pmcs_hw_t *, uint32_t);
+uint32_t pmcs_rd_topunit(pmcs_hw_t *, uint32_t);
+uint32_t pmcs_rd_mpi_tbl(pmcs_hw_t *, uint32_t);
+uint32_t pmcs_rd_gst_tbl(pmcs_hw_t *, uint32_t);
+uint32_t pmcs_rd_iqc_tbl(pmcs_hw_t *, uint32_t);
+uint32_t pmcs_rd_oqc_tbl(pmcs_hw_t *, uint32_t);
+uint32_t pmcs_rd_iqci(pmcs_hw_t *, uint32_t);
+uint32_t pmcs_rd_iqpi(pmcs_hw_t *, uint32_t);
+uint32_t pmcs_rd_oqci(pmcs_hw_t *, uint32_t);
+uint32_t pmcs_rd_oqpi(pmcs_hw_t *, uint32_t);
+
+void pmcs_wr_msgunit(pmcs_hw_t *, uint32_t, uint32_t);
+void pmcs_wr_gsm_reg(pmcs_hw_t *, uint32_t, uint32_t);
+void pmcs_wr_topunit(pmcs_hw_t *, uint32_t, uint32_t);
+void pmcs_wr_mpi_tbl(pmcs_hw_t *, uint32_t, uint32_t);
+void pmcs_wr_gst_tbl(pmcs_hw_t *, uint32_t, uint32_t);
+void pmcs_wr_iqc_tbl(pmcs_hw_t *, uint32_t, uint32_t);
+void pmcs_wr_oqc_tbl(pmcs_hw_t *, uint32_t, uint32_t);
+void pmcs_wr_iqci(pmcs_hw_t *, uint32_t, uint32_t);
+void pmcs_wr_iqpi(pmcs_hw_t *, uint32_t, uint32_t);
+void pmcs_wr_oqci(pmcs_hw_t *, uint32_t, uint32_t);
+void pmcs_wr_oqpi(pmcs_hw_t *, uint32_t, uint32_t);
+
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _PMCS_REG_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_scsa.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,92 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * SCSI (SCSA) midlayer interface for PMC drier.
+ */
+#ifndef _PMCS_SCSA_H
+#define	_PMCS_SCSA_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/scsi/scsi_types.h>
+
+#define	ADDR2TRAN(ap)		((ap)->a_hba_tran)
+#define	ADDR2PMC(ap)		(ITRAN2PMC(ADDR2TRAN(ap)))
+
+#define	CMD2TRAN(cmd)		(CMD2PKT(cmd)->pkt_address.a_hba_tran)
+#define	CMD2PMC(cmd)		(ITRAN2PMC(CMD2TRAN(cmd)))
+
+#define	PKT2ADDR(pkt)		(&((pkt)->pkt_address))
+#define	PKT2CMD(pkt)		((pmcs_cmd_t *)(pkt->pkt_ha_private))
+#define	CMD2PKT(sp)		(sp->cmd_pkt)
+#define	PMCS_STATUS_LEN		264
+
+#define	TRAN2PMC(tran)		((pmcs_hw_t *)(tran)->tran_hba_private)
+#define	ITRAN2PMC(tran) \
+	(((pmcs_iport_t *)(tran)->tran_hba_private)->pwp)
+#define	ITRAN2IPORT(tran) \
+	((pmcs_iport_t *)(tran)->tran_hba_private)
+
+/*
+ * Wrapper around scsi_pkt.
+ */
+struct pmcs_cmd {
+	struct scsi_pkt		*cmd_pkt;	/* actual SCSI Packet */
+	STAILQ_ENTRY(pmcs_cmd)	cmd_next;	/* linked list */
+	pmcs_dmachunk_t		*cmd_clist;	/* list of dma chunks */
+	pmcs_xscsi_t		*cmd_target;	/* Pointer to target */
+	pmcs_lun_t		*cmd_lun;	/* Pointer to LU */
+	uint32_t		cmd_tag;	/* PMC htag */
+	uint8_t			cmd_satltag;	/* SATL tag */
+};
+
+#define	SCSA_CDBLEN(sp)		sp->cmd_pkt->pkt_cdblen
+#define	SCSA_STSLEN(sp)		sp->cmd_pkt->pkt_scblen
+#define	SCSA_TGTLEN(sp)		sp->cmd_pkt->pkt_tgtlen
+
+#define	PMCS_WQ_RUN_SUCCESS	0
+#define	PMCS_WQ_RUN_FAIL_RES	1 /* Failed to alloc rsrcs (e.g. DMA chunks) */
+#define	PMCS_WQ_RUN_FAIL_OTHER	2 /* Any other failure */
+
+int pmcs_scsa_init(pmcs_hw_t *, const ddi_dma_attr_t *);
+
+void pmcs_latch_status(pmcs_hw_t *, pmcs_cmd_t *, uint8_t, uint8_t *,
+    size_t, char *);
+size_t pmcs_set_resid(struct scsi_pkt *, size_t, uint32_t);
+boolean_t pmcs_scsa_wq_run_one(pmcs_hw_t *, pmcs_xscsi_t *);
+void pmcs_scsa_wq_run(pmcs_hw_t *);
+void pmcs_scsa_cq_run(void *);
+
+int pmcs_config_one(pmcs_hw_t *, uint64_t, int, long, dev_info_t **);
+
+dev_info_t *pmcs_find_child_smp(pmcs_hw_t *, char *);
+int pmcs_config_one_smp(pmcs_hw_t *, uint64_t, dev_info_t **);
+
+int pmcs_run_sata_special(pmcs_hw_t *, pmcs_xscsi_t *);
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _PMCS_SCSA_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_sgl.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,86 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+#ifndef	_PMCS_SGL_H
+#define	_PMCS_SGL_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * This is the strict physical representation of an external
+ * S/G list entry that the PMCS hardware uses. We manage them
+ * in chunks.
+ */
+typedef struct {
+	uint32_t	sglal;	/* Low 32 bit DMA address */
+	uint32_t	sglah;	/* High 32 bit DMA address */
+	uint32_t	sglen;	/* Length */
+	uint32_t	flags;
+} pmcs_dmasgl_t;
+
+/*
+ * If this is bit is set in flags, then the address
+ * described by this structure is an array of SGLs,
+ * the last of which may contain *another* flag
+ * to continue the list.
+ */
+#define	PMCS_DMASGL_EXTENSION	(1U << 31)
+
+#define	PMCS_SGL_CHUNKSZ	(PMCS_SGL_NCHUNKS * (sizeof (pmcs_dmasgl_t)))
+
+/*
+ * This is how we keep track of chunks- we have a linked list of
+ * chunk pointers that are either on the free list or are tagged
+ * off of a SCSA command. We used to maintain offsets indices
+ * within the sglen area of the lest element of a chunk, but this
+ * is marked reserved and may not be reliably used future firmware
+ * revisions.
+ */
+typedef struct pmcs_dmachunk pmcs_dmachunk_t;
+struct pmcs_dmachunk {
+	pmcs_dmachunk_t	*nxt;
+	pmcs_dmasgl_t	*chunks;
+	unsigned long	addr;
+	ddi_acc_handle_t	acc_handle;
+	ddi_dma_handle_t	dma_handle;
+};
+
+/*
+ * DMA related functions
+ */
+int pmcs_dma_load(pmcs_hw_t *, pmcs_cmd_t *, uint32_t *);
+void pmcs_dma_unload(pmcs_hw_t *, pmcs_cmd_t *);
+
+/*
+ * After allocating some DMA chunks, insert them
+ * into the free list and set them up for use.
+ */
+void pmcs_idma_chunks(pmcs_hw_t *, pmcs_dmachunk_t *,
+    pmcs_chunk_t *, unsigned long);
+
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _PMCS_SGL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/pmcs_smhba.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * SM-HBA interfaces/definitions for PMC-S driver.
+ */
+#ifndef _PMCS_SMHBA_H
+#define	_PMCS_SMHBA_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/* Leverage definition of data_type_t in nvpair.h */
+#include <sys/nvpair.h>
+
+#define	PMCS_NUM_PHYS		"num-phys"
+#define	PMCS_NUM_PHYS_HBA	"num-phys-hba"
+#define	PMCS_SMHBA_SUPPORTED	"sm-hba-supported"
+#define	PMCS_DRV_VERSION	"driver-version"
+#define	PMCS_HWARE_VERSION	"hardware-version"
+#define	PMCS_FWARE_VERSION	"firmware-version"
+#define	PMCS_SUPPORTED_PROTOCOL	"supported-protocol"
+
+#define	PMCS_MANUFACTURER	"Manufacturer"
+#define	PMCS_SERIAL_NUMBER	"SerialNumber"
+#define	PMCS_MODEL_NAME		"ModelName"
+
+/*
+ * Interfaces to add properties required for SM-HBA
+ *
+ * _add_xxx_prop() interfaces add only 1 prop that is specified in the args.
+ * _set_xxx_props() interfaces add more than 1 prop for a set of phys/devices.
+ */
+void pmcs_smhba_add_hba_prop(pmcs_hw_t *, data_type_t, char *, void *);
+void pmcs_smhba_add_iport_prop(pmcs_iport_t *, data_type_t, char *, void *);
+void pmcs_smhba_add_tgt_prop(pmcs_xscsi_t *, data_type_t, char *, void *);
+
+void pmcs_smhba_set_scsi_device_props(pmcs_hw_t *, pmcs_phy_t *,
+    struct scsi_device *);
+void pmcs_smhba_set_phy_props(pmcs_iport_t *);
+
+/*
+ * Misc routines supporting SM-HBA
+ */
+void pmcs_smhba_log_sysevent(pmcs_hw_t *, char *, char *, pmcs_phy_t *);
+
+
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _PMCS_SMHBA_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/adapters/pmcs/smp_defs.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,1100 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This header file will only be used for pmcs driver temporarily.
+ * Will be deleted after smp_frames.h is updated.
+ */
+
+#ifndef _SYS_SCSI_GENERIC_SMP_FRAMES_H
+#define	_SYS_SCSI_GENERIC_SMP_FRAMES_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/sysmacros.h>
+
+/*
+ * The definitions of smp frame types and functions conforming to SAS-1.1 and
+ * SAS-2.  Consumers are expected to determine protocol support by examining
+ * the response to the REPORT GENERAL function.
+ */
+
+/* Redefinition conflict with smp_frames.h. */
+
+/*
+ * typedef enum smp_frame_type {
+ * 	SMP_FRAME_TYPE_REQUEST	= 0x40,
+ *	SMP_FRAME_TYPE_RESPONSE	= 0x41
+ *	} smp_frame_type_t;
+ */
+
+typedef enum smp_function {
+	SMP_FUNC_REPORT_GENERAL			= 0x00,
+	SMP_FUNC_REPORT_MANUFACTURER_INFO	= 0x01,
+	SMP_FUNC_READ_GPIO_REGISTER		= 0x02,
+	SMP_FUNC_REPORT_SELF_CONFIG_STATUS	= 0x03,
+	SMP_FUNC_REPORT_ZONE_PERM_TABLE		= 0x04,
+	SMP_FUNC_REPORT_ZONE_MANAGER_PASSWORD	= 0x05,
+	SMP_FUNC_REPORT_BROADCAST		= 0x06,
+	SMP_FUNC_DISCOVER			= 0x10,
+	SMP_FUNC_REPORT_PHY_ERROR_LOG		= 0x11,
+	SMP_FUNC_REPORT_PHY_SATA		= 0x12,
+	SMP_FUNC_REPORT_ROUTE_INFO		= 0x13,
+	SMP_FUNC_REPORT_PHY_EVENT		= 0x14,
+	SMP_FUNC_DISCOVER_LIST			= 0x20,
+	SMP_FUNC_REPORT_PHY_EVENT_LIST		= 0x21,
+	SMP_FUNC_REPORT_EXP_ROUTE_TABLE_LIST	= 0x22,
+	SMP_FUNC_CONFIG_GENERAL			= 0x80,
+	SMP_FUNC_ENABLE_DISABLE_ZONING		= 0x81,
+	SMP_FUNC_WRITE_GPIO_REGISTER		= 0x82,
+	SMP_FUNC_ZONED_BROADCAST		= 0x85,
+	SMP_FUNC_ZONE_LOCK			= 0x86,
+	SMP_FUNC_ZONE_ACTIVATE			= 0x87,
+	SMP_FUNC_ZONE_UNLOCK			= 0x88,
+	SMP_FUNC_CONFIG_ZONE_MANAGER_PASSWORD	= 0x89,
+	SMP_FUNC_CONFIG_ZONE_PHY_INFO		= 0x8A,
+	SMP_FUNC_CONFIG_ZONE_PERM_TABLE		= 0x8B,
+	SMP_FUNC_CONFIG_ROUTE_INFO		= 0x90,
+	SMP_FUNC_PHY_CONTROL			= 0x91,
+	SMP_FUNC_PHY_TEST_FUNCTION		= 0x92,
+	SMP_FUNC_CONFIG_PHY_EVENT		= 0x93
+} smp_function_t;
+
+typedef enum smp_result {
+	SMP_RES_FUNCTION_ACCEPTED		= 0x00,
+	SMP_RES_UNKNOWN_FUNCTION		= 0x01,
+	SMP_RES_FUNCTION_FAILED			= 0x02,
+	SMP_RES_INVALID_REQUEST_FRAME_LENGTH	= 0x03,
+	SMP_RES_INVALID_EXPANDER_CHANGE_COUNT	= 0x04,
+	SMP_RES_BUSY				= 0x05,
+	SMP_RES_INCOMPLETE_DESCRIPTOR_LIST	= 0x06,
+	SMP_RES_PHY_DOES_NOT_EXIST		= 0x10,
+	SMP_RES_INDEX_DOES_NOT_EXIST		= 0x11,
+	SMP_RES_PHY_DOES_NOT_SUPPORT_SATA	= 0x12,
+	SMP_RES_UNKNOWN_PHY_OPERATION		= 0x13,
+	SMP_RES_UNKNOWN_PHY_TEST_FUNCTION	= 0x14,
+	SMP_RES_PHY_TEST_IN_PROGRESS		= 0x15,
+	SMP_RES_PHY_VACANT			= 0x16,
+	SMP_RES_UNKNOWN_PHY_EVENT_SOURCE	= 0x17,
+	SMP_RES_UNKNOWN_DESCRIPTOR_TYPE		= 0x18,
+	SMP_RES_UNKNOWN_PHY_FILTER		= 0x19,
+	SMP_RES_AFFILIATION_VIOLATION		= 0x1A,
+	SMP_RES_ZONE_VIOLATION			= 0x20,
+	SMP_RES_NO_MANAGEMENT_ACCESS_RIGHTS	= 0x21,
+	SMP_RES_UNKNOWN_ENABLE_DISABLE_ZONING	= 0x22,
+	SMP_RES_ZONE_LOCK_VIOLATION		= 0x23,
+	SMP_RES_NOT_ACTIVATED			= 0x24,
+	SMP_RES_ZONE_GROUP_OUT_OF_RANGE		= 0x25,
+	SMP_RES_NO_PHYSICAL_PRESENCE		= 0x26,
+	SMP_RES_SAVING_NOT_SUPPORTED		= 0x27,
+	SMP_RES_SOURCE_ZONE_GROUP_DNE		= 0x28,
+	SMP_RES_NONE				= -1
+} smp_result_t;
+
+/*
+ * SAS-2 10.4.3.2 request frame format
+ */
+typedef struct smp_request_frame {
+	uint8_t srf_frame_type;
+	uint8_t srf_function;
+	uint8_t srf_allocated_response_len;	/* reserved in SAS-1 */
+	uint8_t srf_request_len;
+	uint8_t srf_data[1];
+} smp_request_frame_t;
+
+/*
+ * SAS-2 10.4.3.3 response frame format
+ */
+typedef struct smp_response_frame {
+	uint8_t srf_frame_type;
+	uint8_t srf_function;
+	uint8_t srf_result;
+	uint8_t srf_response_len;	/* reserved in SAS-1 */
+	uint8_t srf_data[1];
+} smp_response_frame_t;
+
+typedef uint8_t smp_crc_t[4];
+
+#ifdef offsetof
+#define	SMP_REQ_MINLEN	\
+	(offsetof(smp_request_frame_t, srf_data[0]) + sizeof (smp_crc_t))
+#define	SMP_RESP_MINLEN	\
+	(offsetof(smp_response_frame_t, srf_data[0]) + sizeof (smp_crc_t))
+#endif	/* offsetof */
+
+#pragma	pack(1)
+
+/*
+ * SAS-2 10.4.3.4 REPORT GENERAL (no additional request bytes)
+ */
+typedef struct smp_report_general_resp {
+	uint16_t srgr_exp_change_count;
+	uint16_t srgr_exp_route_indexes;
+	DECL_BITFIELD2(
+	    _reserved1		:7,
+	    srgr_long_response	:1);
+	uint8_t srgr_number_of_phys;
+	DECL_BITFIELD7(
+	    srgr_externally_configurable_route_table	:1,
+	    srgr_configuring				:1,
+	    srgr_configures_others			:1,
+	    srgr_open_reject_retry_supported		:1,
+	    srgr_stp_continue_awt			:1,
+	    _reserved2					:2,
+	    srgr_table_to_table_supported		:1);
+	uint8_t _reserved3;
+	uint64_t srgr_enclosure_logical_identifier;
+	uint8_t _reserved4[8];
+	uint8_t _reserved5[2];
+	uint16_t srgr_stp_bus_inactivity_time_limit;
+	uint16_t srgr_stp_maximum_connect_time_limit;
+	uint16_t srgr_stp_smp_nexus_loss_time;
+	DECL_BITFIELD7(
+	    srgr_zoning_enabled				:1,
+	    srgr_zoning_supported			:1,
+	    srgr_physical_presence_asserted		:1,
+	    srgr_physical_presence_supported		:1,
+	    srgr_zone_locked				:1,
+	    _reserved6					:1,
+	    srgr_number_of_zone_grps			:2);
+	DECL_BITFIELD6(
+	    srgr_saving_zoning_enabled_supported	:1,
+	    srgr_saving_zone_perm_table_supported	:1,
+	    srgr_saving_zone_phy_info_supported		:1,
+	    srgr_saving_zone_mgr_password_supported	:1,
+	    srgr_saving					:1,
+	    _reserved7					:4);
+	uint16_t srgr_max_routed_sas_addrs;
+	uint64_t srgr_active_zm_sas_addr;
+	uint16_t srgr_zone_lock_inactivity_limit;
+	uint8_t _reserved8[2];
+	uint8_t _reserved9;
+	uint8_t srgr_first_encl_conn_elem_idx;
+	uint8_t srgr_number_encl_conn_elem_idxs;
+	uint8_t _reserved10;
+	DECL_BITFIELD2(
+	    _reserved11					:7,
+	    srgr_reduced_functionality			:1);
+	uint8_t srgr_time_to_reduced_functionality;
+	uint8_t srgr_initial_time_to_reduced_functionality;
+	uint8_t srgr_max_reduced_functionality_time;
+	uint16_t srgr_last_self_conf_status_descr_idx;
+	uint16_t srgr_max_stored_self_config_status_descrs;
+	uint16_t srgr_last_phy_event_list_descr_idx;
+	uint16_t srgr_max_stored_phy_event_list_descrs;
+	uint16_t srgr_stp_reject_to_open_limit;
+	uint8_t _reserved12[2];
+} smp_report_general_resp_t;
+
+typedef enum smp_n_zone_grps {
+	SMP_ZONE_GROUPS_128	= 0x0,
+	SMP_ZONE_GROUPS_256	= 0x1
+} smp_n_zone_grps_t;
+
+/*
+ * SAS-2 10.4.3.5 REPORT MANUFACTURER INFORMATION (no additional request bytes)
+ */
+typedef struct smp_report_manufacturer_info_resp {
+	uint16_t srmir_exp_change_count;
+	uint8_t _reserved1[2];
+	DECL_BITFIELD2(
+	    srmir_sas_1_1_format	:1,
+	    _reserved2			:7);
+	uint8_t _reserved3[3];
+	char srmir_vendor_identification[8];
+	char srmir_product_identification[16];
+	char srmir_product_revision_level[4];
+	char srmir_component_vendor_identification[8];
+	uint16_t srmir_component_id;
+	uint8_t srmir_component_revision_level;
+	uint8_t _reserved4;
+	uint8_t srmir_vs_52[8];
+} smp_report_manufacturer_info_resp_t;
+
+/*
+ * SAS-2 10.4.3.6 REPORT SELF_CONFIGURATION STATUS
+ */
+typedef struct smp_report_self_config_status_req {
+	uint8_t _reserved1[2];
+	uint16_t srscsr_starting_self_config_status_descr_idx;
+} smp_report_self_config_status_req_t;
+
+typedef struct smp_report_self_config_status_resp {
+	uint16_t srscsr_exp_change_count;
+	uint16_t srscsr_starting_self_config_status_descr_idx;
+	uint16_t srscsr_number_self_config_status_descrs;
+	uint16_t srscsr_last_self_config_status_descr_idx;
+	uint8_t srscsr_self_config_status_descr_len;
+	uint8_t _reserved1[3];
+	uint8_t srscsr_descrs[1];
+} smp_report_self_config_status_resp_t;
+
+typedef struct smp_self_config_status_descr {
+	uint8_t sscsd_status_type;
+	DECL_BITFIELD2(
+	    sscsd_final		:1,
+	    _reserved1		:7);
+	uint8_t _reserved2;
+	uint8_t sscsd_phy_identifier;
+	uint8_t _reserved3[4];
+	uint64_t sscsd_sas_addr;
+} smp_self_config_status_descr_t;
+
+typedef enum smp_self_config_status_type {
+	SMP_SCST_NONSPECIFIC_ERROR	= 0x01,
+	SMP_SCST_CONNECTION		= 0x02,
+	SMP_SCST_ROUTE_TABLE_FULL	= 0x03,
+	SMP_SCST_NOMEM			= 0x04,
+	SMP_SCST_PHY_LAYER_ERROR	= 0x20,
+	SMP_SCST_LOST_SYNC		= 0x21,
+	SMP_SCST_LINK_LAYER_ERROR	= 0x40,
+	SMP_SCST_OPEN_TIMEOUT		= 0x41,
+	SMP_SCST_ABANDON_OPEN_REJECT	= 0x42,
+	SMP_SCST_RETRY_OPEN_REJECTS	= 0x43,
+	SMP_SCST_NEXUS_LOSS		= 0x44,
+	SMP_SCST_BREAK			= 0x45,
+	SMP_SCST_CRC_ERROR		= 0x46,
+	SMP_SCST_PORT_LAYER_ERROR	= 0x60,
+	SMP_SCST_RESPONSE_TIMEOUT	= 0x61,
+	SMP_SCST_TRANSPORT_LAYER_ERROR	= 0x80,
+	SMP_SCST_APP_LAYER_ERROR	= 0xA0,
+	SMP_SCST_RESPONSE_TOO_SHORT	= 0xA1,
+	SMP_SCST_UNSUPPORTED_VALUES	= 0xA2,
+	SMP_SCST_INCONSISTENT		= 0xA3,
+	SMP_SCST_CONFIGURING		= 0xA4
+} smp_self_config_status_type_t;
+
+/*
+ * SAS-2 10.4.3.7 REPORT ZONE PERMISSION TABLE
+ */
+typedef struct smp_report_zone_perm_table_req {
+	DECL_BITFIELD2(
+	    srzptr_report_type		:2,
+	    _reserved1			:6);
+	uint8_t _reserved2;
+	uint8_t srzptr_starting_src_zone_grp;
+	uint8_t srzptr_max_zone_perm_descrs;
+} smp_report_zone_perm_table_req_t;
+
+typedef enum smp_zone_perm_table_report_type {
+	SMP_ZPTRT_CURRENT		= 0x0,
+	SMP_ZPTRT_SHADOW		= 0x1,
+	SMP_ZPTRT_SAVED			= 0x2,
+	SMP_ZPTRT_DEFAULT		= 0x3
+} smp_zone_perm_table_report_type_t;
+
+typedef struct smp_report_zone_perm_table_resp {
+	uint16_t srzptr_exp_change_count;
+	DECL_BITFIELD3(
+	    srzptr_report_type		:2,
+	    _reserved1			:5,
+	    srzptr_zone_locked		:1);
+	DECL_BITFIELD2(
+	    _reserved2			:6,
+	    srzptr_number_zone_grps	:2);
+	uint8_t _reserved3[6];
+	uint8_t srzptr_starting_src_zone_grp;
+	uint8_t srzptr_number_zone_perm_descrs;
+	uint8_t srzptr_descrs[1];
+} smp_report_zone_perm_table_resp_t;
+
+typedef uint8_t smp_zone_perm_descr128_t[16];
+typedef uint8_t smp_zone_perm_descr256_t[32];
+
+#define	SMP_ZONE_PERM_BIT128(__d, __z)	\
+	((__d)[15 - ((__z) >> 3)] & (1 << ((__z) & 7)))
+
+#define	SMP_ZONE_PERM_SET128(__d, __z)	\
+	((__d)[15 - ((__z) >> 3)] |= (1 << ((__z) & 7)))
+
+#define	SMP_ZONE_PERM_CLR128(__d, __z)	\
+	((__d)[15 - ((__z) >> 3)] &= ~(1 << ((__z) & 7)))
+
+#define	SMP_ZONE_PERM_BIT256(__d, __z)	\
+	((__d)[31 - ((__z) >> 3)] & (1 << ((__z) & 7)))
+
+#define	SMP_ZONE_PERM_SET256(__d, __z)	\
+	((__d)[31 - ((__z) >> 3)] |= (1 << ((__z) & 7)))
+
+#define	SMP_ZONE_PERM_CLR256(__d, __z)	\
+	((__d)[31 - ((__z) >> 3)] &= ~(1 << ((__z) & 7)))
+
+/*
+ * SAS-2 10.4.3.8 REPORT ZONE MANAGER PASSWORD (no additional request bytes)
+ */
+typedef struct smp_report_zone_mgr_password_resp {
+	uint16_t srzmpr_exp_change_count;
+	uint8_t _reserved1[2];
+	uint8_t srzmpr_zone_mgr_password[32];
+} smp_report_zone_mgr_password_resp_t;
+
+/*
+ * SAS-2 10.4.3.9 REPORT BROADCAST
+ */
+typedef struct smp_report_broadcast_req {
+	DECL_BITFIELD2(
+	    srbr_broadcast_type		:4,
+	    _reserved1			:4);
+	uint8_t _reserved2[3];
+} smp_report_broadcast_req_t;
+
+typedef enum smp_broadcast_type {
+	SMP_BROADCAST_CHANGE		= 0x0,
+	SMP_BROADCAST_RESERVED_CHANGE_0	= 0x1,
+	SMP_BROADCAST_RESERVED_CHANGE_1	= 0x2,
+	SMP_BROADCAST_SES		= 0x3,
+	SMP_BROADCAST_EXPANDER		= 0x4,
+	SMP_BROADCAST_ASYNC_EVENT	= 0x5,
+	SMP_BROADCAST_RESERVED_3	= 0x6,
+	SMP_BROADCAST_RESERVED_4	= 0x7,
+	SMP_BROADCAST_ZONE_ACTIVATE	= 0x8
+} smp_broadcast_type_t;
+
+typedef struct smp_broadcast_descr {
+	DECL_BITFIELD2(
+	    sbd_broadcast_type		:4,
+	    _reserved1			:4);
+	uint8_t sbd_phy_identifier;
+	DECL_BITFIELD2(
+	    sbd_broadcast_reason	:4,
+	    _reserved2			:4);
+	uint16_t sbd_broadcast_count;
+	uint8_t _reserved3[10];
+} smp_broadcast_descr_t;
+
+typedef struct smp_report_broadcast_resp {
+	uint16_t srbr_exp_change_count;
+	DECL_BITFIELD2(
+	    srbr_broadcast_type		:4,
+	    _reserved1			:4);
+	uint8_t srbr_number_broadcast_descrs;
+	smp_broadcast_descr_t srbr_descrs[1];
+} smp_report_broadcast_resp_t;
+
+/*
+ * SAS-2 10.4.3.10 DISCOVER
+ */
+typedef struct smp_discover_req {
+	uint8_t _reserved1[4];
+	DECL_BITFIELD2(
+	    sdr_ignore_zone_grp		:1,
+	    _reserved2			:7);
+	uint8_t sdr_phy_identifier;
+	uint8_t _reserved3[2];
+} smp_discover_req_t;
+
+typedef struct smp_snw3_phy_cap {
+	DECL_BITFIELD4(
+	    sspc_requested_logical_link_rate	:4,	/* smp_link_rate_t */
+	    _reserved1				:2,
+	    sspc_tx_ssc_type			:1,
+	    sspc_start				:1);
+	DECL_BITFIELD7(
+	    _reserved2				:2,
+	    sspc_g3_ssc				:1,
+	    sspc_g3_no_ssc			:1,
+	    sspc_g2_ssc				:1,
+	    sspc_g2_no_ssc			:1,
+	    sspc_g1_ssc				:1,
+	    sspc_g1_no_ssc			:1);
+	uint8_t _reserved3;
+	DECL_BITFIELD2(
+	    sspc_parity		:1,
+	    _reserved4		:7);
+} smp_snw3_phy_cap_t;
+
+typedef struct smp_discover_resp {
+	uint16_t sdr_exp_change_count;
+	uint8_t _reserved1[3];
+	uint8_t sdr_phy_identifier;
+	uint8_t _reserved2[2];
+	DECL_BITFIELD3(
+	    sdr_attached_reason		:4,
+	    sdr_attached_device_type	:3,
+	    _reserved3			:1);
+	DECL_BITFIELD2(
+	    sdr_negotiated_logical_link_rate	:4,	/* smp_link_rate_t */
+	    _reserved4				:4);
+	DECL_BITFIELD5(
+	    sdr_attached_sata_host	:1,
+	    sdr_attached_smp_initiator	:1,
+	    sdr_attached_stp_initiator	:1,
+	    sdr_attached_ssp_initiator	:1,
+	    _reserved5			:4);
+	DECL_BITFIELD6(
+	    sdr_attached_sata_device		:1,
+	    sdr_attached_smp_target		:1,
+	    sdr_attached_stp_target		:1,
+	    sdr_attached_ssp_target		:1,
+	    _reserved6				:3,
+	    sdr_attached_sata_port_selector	:1);
+	uint64_t sdr_sas_addr;
+	uint64_t sdr_attached_sas_addr;
+	uint8_t sdr_attached_phy_identifier;
+	DECL_BITFIELD4(
+	    sdr_attached_break_reply_capable		:1,
+	    sdr_attached_requested_inside_zpsds		:1,
+	    sdr_attached_inside_zpsds_persistent	:1,
+	    _reserved7					:5);
+	uint8_t _reserved8[6];
+	DECL_BITFIELD2(
+	    sdr_hw_min_phys_link_rate	:4,	/* smp_link_rate_t */
+	    sdr_prog_min_phys_link_rate	:4);	/* smp_link_rate_t */
+	DECL_BITFIELD2(
+	    sdr_hw_max_phys_link_rate	:4,	/* smp_link_rate_t */
+	    sdr_prog_max_phys_link_rate	:4);	/* smp_link_rate_t */
+	uint8_t sdr_phy_change_count;
+	DECL_BITFIELD3(
+	    sdr_partial_pwy_timeout	:4,
+	    _reserved9			:3,
+	    sdr_virtual_phy		:1);
+	DECL_BITFIELD2(
+	    sdr_routing_attr		:4,	/* smp_routing_attr_t */
+	    _reserved10			:4);
+	DECL_BITFIELD2(
+	    sdr_connector_type		:7,
+	    _reserved11			:1);
+	uint8_t sdr_connector_element_index;
+	uint8_t sdr_connector_physical_link;
+	uint8_t _reserved12[2];
+	uint8_t sdr_vendor[2];
+	uint64_t sdr_attached_device_name;
+	DECL_BITFIELD8(
+	    sdr_zoning_enabled				:1,
+	    sdr_inside_zpsds				:1,
+	    sdr_zone_group_persistent			:1,
+	    _reserved13					:1,
+	    sdr_requested_inside_zpsds			:1,
+	    sdr_inside_zpsds_persistent			:1,
+	    sdr_requested_inside_zpsds_changed_by_exp	:1,
+	    _reserved14					:1);
+	uint8_t _reserved15[2];
+	uint8_t sdr_zone_group;
+	uint8_t sdr_self_config_status;
+	uint8_t sdr_self_config_levels_completed;
+	uint8_t _reserved16[2];
+	uint64_t sdr_self_config_sas_addr;
+	smp_snw3_phy_cap_t sdr_prog_phy_cap;
+	smp_snw3_phy_cap_t sdr_current_phy_cap;
+	smp_snw3_phy_cap_t sdr_attached_phy_cap;
+	uint8_t _reserved17[6];
+	DECL_BITFIELD2(
+	    sdr_negotiated_phys_link_rate	:4,	/* smp_link_rate_t */
+	    sdr_reason				:4);
+	DECL_BITFIELD3(
+	    sdr_hw_muxing_supported	:1,
+	    sdr_negotiated_ssc		:1,
+	    _reserved18			:6);
+	DECL_BITFIELD7(
+	    sdr_default_zoning_enabled		:1,
+	    _reserved19				:1,
+	    sdr_default_zone_group_persistent	:1,
+	    _reserved20				:1,
+	    sdr_default_requested_inside_zpsds	:1,
+	    sdr_default_inside_zpsds_persistent	:1,
+	    _reserved21				:2);
+	uint8_t _reserved22[2];
+	uint8_t sdr_default_zone_group;
+	DECL_BITFIELD7(
+	    sdr_saved_zoning_enabled		:1,
+	    _reserved23				:1,
+	    sdr_saved_zone_group_persistent	:1,
+	    _reserved24				:1,
+	    sdr_saved_requested_inside_zpsds	:1,
+	    sdr_saved_inside_zpsds_persistent	:1,
+	    _reserved25				:2);
+	uint8_t _reserved26[2];
+	uint8_t saved_zone_group;
+	DECL_BITFIELD6(
+	    _reserved27				:2,
+	    sdr_shadow_zone_group_persistent	:1,
+	    _reserved28				:1,
+	    sdr_shadow_requested_inside_zpsds	:1,
+	    sdr_shadow_inside_zpsds_persistent	:1,
+	    _reserved29				:2);
+	uint8_t _reserved30[2];
+	uint8_t sdr_shadow_zone_group;
+} smp_discover_resp_t;
+
+typedef enum smp_link_rate {
+	SMP_LINK_RATE_NO_CHANGE = 0x0,
+	SMP_LINK_RATE_DISABLED = 0x1,
+	SMP_LINK_RATE_RESET_PROBLEM = 0x2,
+	SMP_LINK_RATE_SPINUP_HOLD = 0x3,
+	SMP_LINK_RATE_PORT_SELECTOR = 0x4,
+	SMP_LINK_RATE_RESET = 0x5,
+	SMP_LINK_RATE_UNSUPPORTED = 0x6,
+	SMP_LINK_RATE_1_5 = 0x8,
+	SMP_LINK_RATE_3 = 0x9,
+	SMP_LINK_RATE_6 = 0xA
+} smp_link_rate_t;
+
+typedef enum smp_device_type {
+	SMP_DEV_NONE = 0x0,
+	SMP_DEV_SAS_SATA = 0x1,
+	SMP_DEV_EXPANDER = 0x2,
+	SMP_DEV_EXPANDER_OLD = 0x3
+} smp_device_type_t;
+
+typedef enum smp_routing_attr {
+	SMP_ROUTING_DIRECT = 0x0,
+	SMP_ROUTING_SUBTRACTIVE = 0x1,
+	SMP_ROUTING_TABLE = 0x2
+} smp_routing_attr_t;
+
+/*
+ * SAS-2 10.4.3.11 REPORT PHY ERROR LOG
+ */
+typedef struct smp_report_phy_error_log_req {
+	uint8_t _reserved1[5];
+	uint8_t srpelr_phy_identifier;
+	uint8_t _reserved2[2];
+} smp_report_phy_error_log_req_t;
+
+typedef struct smp_report_phy_error_log_resp {
+	uint16_t srpelr_exp_change_count;
+	uint8_t _reserved1[3];
+	uint8_t srpelr_phy_identifier;
+	uint8_t _reserved2[2];
+	uint32_t srpelr_invalid_dword_count;
+	uint32_t srpelr_running_disparity_error_count;
+	uint32_t srpelr_loss_dword_sync_count;
+	uint32_t srpelr_phy_reset_problem_count;
+} smp_report_phy_error_log_resp_t;
+
+/*
+ * SAS-2 10.4.3.12 REPORT PHY SATA
+ */
+typedef struct smp_report_phy_sata_req {
+	uint8_t _reserved1[5];
+	uint8_t srpsr_phy_identifier;
+	uint8_t srpsr_affiliation_context;
+	uint8_t _reserved2;
+} smp_report_phy_sata_req_t;
+
+typedef struct smp_report_phy_sata_resp {
+	uint16_t srpsr_exp_change_count;
+	uint8_t _reserved1[3];
+	uint8_t srpsr_phy_identifier;
+	uint8_t _reserved2;
+	DECL_BITFIELD4(
+	    srpsr_affiliation_valid		:1,
+	    srpsr_affiliations_supported	:1,
+	    srpsr_stp_nexus_loss		:1,
+	    _reserved3				:5);
+	uint8_t _reserved4[4];
+	uint64_t srpsr_stp_sas_addr;
+	uint8_t srpsr_register_device_host_fis[20];
+	uint8_t _reserved5[4];
+	uint64_t srpsr_affiliated_stp_init_sas_addr;
+	uint64_t srpsr_stp_nexus_loss_sas_addr;
+	uint8_t _reserved6;
+	uint8_t srpsr_affiliation_context;
+	uint8_t srpsr_current_affiliation_contexts;
+	uint8_t srpsr_max_affiliation_contexts;
+} smp_report_phy_sata_resp_t;
+
+/*
+ * SAS-2 10.4.3.13 REPORT ROUTE INFORMATION
+ */
+typedef struct smp_report_route_info_req {
+	uint8_t _reserved1[2];
+	uint16_t srrir_exp_route_index;
+	uint8_t _reserved2;
+	uint8_t srrir_phy_identifier;
+	uint8_t _reserved3[2];
+} smp_report_route_info_req_t;
+
+typedef struct smp_report_route_info_resp {
+	uint16_t srrir_exp_change_count;
+	uint16_t srrir_exp_route_index;
+	uint8_t _reserved1;
+	uint8_t srrir_phy_identifier;
+	uint8_t _reserved2[2];
+	DECL_BITFIELD2(
+	    _reserved3				:7,
+	    srrir_exp_route_entry_disabled	:1);
+	uint8_t _reserved4[3];
+	uint64_t srrir_routed_sas_addr;
+	uint8_t _reserved5[16];
+} smp_report_route_info_resp_t;
+
+/*
+ * SAS-2 10.4.3.14 SAS-2 REPORT PHY EVENT
+ */
+typedef struct smp_report_phy_event_req {
+	uint8_t _reserved1;
+	uint8_t _reserved2[4];
+	uint8_t srper_phy_identifier;
+	uint8_t _reserved3[2];
+} smp_report_phy_event_req_t;
+
+typedef struct smp_phy_event_report_descr {
+	uint8_t _reserved1[3];
+	uint8_t sped_phy_event_source;
+	uint32_t sped_phy_event;
+	uint32_t sped_peak_detector_threshold;
+} smp_phy_event_report_descr_t;
+
+typedef struct smp_report_phy_event_resp {
+	uint16_t srper_exp_change_count;
+	uint8_t _reserved1[3];
+	uint8_t srper_phy_identifier;
+	uint8_t _reserved2[5];
+	uint8_t srper_n_phy_event_descrs;
+	smp_phy_event_report_descr_t srper_phy_event_descrs[1];
+} smp_report_phy_event_resp_t;
+
+/*
+ * SAS-2 10.4.3.15 SAS-2 DISCOVER LIST
+ */
+typedef struct smp_discover_list_req {
+	uint8_t _reserved1[4];
+	uint8_t sdlr_starting_phy_identifier;
+	uint8_t sdlr_max_descrs;
+	DECL_BITFIELD3(
+	    sdlr_phy_filter		:4,
+	    _reserved2			:3,
+	    sdlr_ignore_zone_group	:1);
+	DECL_BITFIELD2(
+	    sdlr_descr_type		:4,
+	    _reserved3			:4);
+	uint8_t _reserved4[4];
+	uint8_t sdlr_vendor[12];
+} smp_discover_list_req_t;
+
+typedef struct smp_discover_short_descr {
+	uint8_t sdsd_phy_identifier;
+	uint8_t sdsd_function_result;
+	DECL_BITFIELD3(
+	    sdsd_attached_reason	:4,
+	    sdsd_attached_device_type	:3,
+	    _restricted1		:1);
+	DECL_BITFIELD2(
+	    sdsd_negotiated_logical_link_rate	:4,	/* smp_link_rate_t */
+	    _restricted2			:4);
+	DECL_BITFIELD5(
+	    sdsd_attached_sata_host	:1,
+	    sdsd_attached_smp_initiator	:1,
+	    sdsd_attached_stp_initiator	:1,
+	    sdsd_attached_ssp_initiator	:1,
+	    _restricted3		:4);
+	DECL_BITFIELD6(
+	    sdsd_attached_sata_device		:1,
+	    sdsd_attached_smp_target		:1,
+	    sdsd_attached_stp_target		:1,
+	    sdsd_attached_ssp_target		:1,
+	    _restricted4			:3,
+	    sdsd_attached_sata_port_selector	:1);
+	DECL_BITFIELD3(
+	    sdsd_routing_attribute	:4,		/* smp_routing_attr_t */
+	    _reserved1			:3,
+	    sdsd_virtual_phy		:1);
+	DECL_BITFIELD2(
+	    _reserved2			:4,
+	    sdsd_reason			:4);
+	uint8_t sdsd_zone_group;
+	DECL_BITFIELD7(
+	    _reserved3				:1,
+	    sdsd_inside_zpsds			:1,
+	    sdsd_zone_group_persistent		:1,
+	    _reserved4				:1,
+	    sdsd_requested_insize_zpsds		:1,
+	    sdsd_inside_zpsds_persistent	:1,
+	    _restricted5			:2);
+	uint8_t sdsd_attached_phy_identifier;
+	uint8_t sdsd_phy_change_count;
+	uint64_t sdsd_attached_sas_addr;
+	uint8_t _reserved5[4];
+} smp_discover_short_descr_t;
+
+typedef struct smp_discover_long_descr {
+	uint8_t _reserved1[2];
+	uint8_t sdld_function_result;
+	uint8_t _reserved2[1];
+	smp_discover_resp_t sdld_response;
+} smp_discover_long_descr_t;
+
+#define	SMP_DISCOVER_RESP(_ld)	\
+	(((smp_discover_long_descr_t *)(_ld))->sdld_function_result ==	\
+	SMP_FUNCTION_ACCEPTED ?	\
+	&((smp_discover_long_descr_t *)(_ld))->sdld_response :	\
+	NULL)
+
+typedef struct smp_discover_list_resp {
+	uint16_t sdlr_exp_change_count;
+	uint8_t _reserved1[2];
+	uint8_t sdlr_starting_phy_identifier;
+	uint8_t sdlr_n_descrs;
+	DECL_BITFIELD2(
+	    sdlr_phy_filter		:4,
+	    _reserved2			:4);
+	DECL_BITFIELD2(
+	    sdlr_descr_type		:4,
+	    _reserved3			:4);
+	uint8_t sdlr_descr_length;
+	uint8_t _reserved4[3];
+	DECL_BITFIELD5(
+	    sdlr_externally_configurable_route_table	:1,
+	    sdlr_configuring				:1,
+	    _reserved5					:4,
+	    sdlr_zoning_enabled				:1,
+	    sdlr_zoning_supported			:1);
+	uint8_t _reserved6;
+	uint16_t sdlr_last_sc_status_descr_index;
+	uint16_t sdlr_last_phy_event_list_descr_index;
+	uint8_t _reserved7[10];
+	uint8_t sdlr_vendor[16];
+	uint8_t sdlr_descrs[1];	/* short or long format */
+} smp_discover_list_resp_t;
+
+/*
+ * SAS-2 10.4.3.16 REPORT PHY EVENT LIST
+ */
+typedef struct smp_report_phy_event_list_req {
+	uint8_t _reserved1[2];
+	uint16_t srpelr_starting_descr_index;
+} smp_report_phy_event_list_req_t;
+
+typedef struct smp_phy_event_list_descr {
+	uint8_t _reserved1[2];
+	uint8_t speld_phy_identifier;
+	uint8_t speld_phy_event_source;
+	uint32_t speld_phy_event;
+	uint32_t speld_peak_detector_threshold;
+} smp_phy_event_list_descr_t;
+
+typedef struct smp_report_phy_event_list_resp {
+	uint16_t srpelr_exp_change_count;
+	uint16_t srpelr_starting_descr_index;
+	uint16_t srpelr_last_descr_index;
+	uint8_t srpelr_phy_event_list_descr_length;
+	uint8_t _reserved1[3];
+	uint8_t srpelr_n_descrs;
+	smp_phy_event_list_descr_t srpelr_descrs[1];
+} smp_report_phy_event_list_resp_t;
+
+/*
+ * SAS-2 10.4.3.17 REPORT EXPANDER ROUTE TABLE LIST
+ */
+typedef struct smp_report_exp_route_table_list_req {
+	uint8_t _reserved1[4];
+	uint16_t srertlr_max_descrs;
+	uint16_t srertlr_starting_routed_sas_addr_index;
+	uint8_t _reserved2[7];
+	uint8_t srertlr_starting_phy_identifier;
+	uint8_t _reserved3[8];
+} smp_report_exp_route_table_list_req_t;
+
+typedef struct smp_route_table_descr {
+	uint64_t srtd_routed_sas_addr;
+	uint8_t srtd_phy_bitmap[6];
+	DECL_BITFIELD2(
+	    _reserved1			:7,
+	    srtd_zone_group_valid	:1);
+	uint8_t srtd_zone_group;
+} smp_route_table_descr_t;
+
+#define	SMP_ROUTE_PHY(_d, _s, _i)	\
+	((_d)->srtd_phy_bitmap[(48 - (_i) + (_s)) >> 3] & \
+	(1 << ((48 - (_i) + (_s)) & 7)))
+
+typedef struct smp_report_exp_route_table_list_resp {
+	uint16_t srertlr_exp_change_count;
+	uint16_t srertlr_route_table_change_count;
+	DECL_BITFIELD3(
+	    _reserved1		:1,
+	    srertlr_configuring	:1,
+	    _reserved2		:6);
+	uint8_t _reserved3;
+	uint16_t srertlr_n_descrs;
+	uint16_t srertlr_first_routed_sas_addr_index;
+	uint16_t srertlr_last_routed_sas_addr_index;
+	uint8_t _reserved4[3];
+	uint8_t srertlr_starting_phy_identifier;
+	uint8_t _reserved5[12];
+	smp_route_table_descr_t srertlr_descrs[1];
+} smp_report_exp_route_table_list_resp_t;
+
+/*
+ * SAS-2 10.4.3.18 CONFIGURE GENERAL (no additional response)
+ */
+typedef struct smp_config_general_req {
+	uint16_t scgr_expected_exp_change_count;
+	uint8_t _reserved1[2];
+	DECL_BITFIELD6(
+	    scgr_update_stp_bus_inactivity			:1,
+	    scgr_update_stp_max_conn				:1,
+	    scgr_update_stp_smp_nexus_loss			:1,
+	    scgr_update_initial_time_to_reduced_functionality	:1,
+	    scgr_update_stp_reject_to_open			:1,
+	    _reserved2						:3);
+	uint8_t _reserved3;
+	uint16_t scgr_stp_bus_inactivity;
+	uint16_t scgr_stp_max_conn;
+	uint16_t scgr_stp_smp_nexus_loss;
+	uint8_t scgr_initial_time_to_reduced_functionality;
+	uint8_t _reserved4;
+	uint16_t scgr_stp_reject_to_open;
+} smp_config_general_req_t;
+
+/*
+ * SAS-2 10.4.3.19 ENABLE DISABLE ZONING (no additional response)
+ */
+typedef struct smp_enable_disable_zoning_req {
+	uint16_t sedzr_expected_exp_change_count;
+	DECL_BITFIELD2(
+	    sedzr_save	:2,		/* smp_zoning_save_t */
+	    _reserved1	:6);
+	uint8_t _reserved2;
+	DECL_BITFIELD2(
+	    sedzr_enable_disable_zoning	:2,
+	    _reserved3			:6);
+	uint8_t _reserved4[3];
+} smp_enable_disable_zoning_req_t;
+
+typedef enum smp_zoning_save {
+	SMP_ZONING_SAVE_CURRENT = 0x0,
+	SMP_ZONING_SAVE_SAVED = 0x1,
+	SMP_ZONING_SAVE_BOTH_IF_SUPP = 0x2,
+	SMP_ZONING_SAVE_BOTH = 0x3
+} smp_zoning_save_t;
+
+typedef enum smp_zoning_enable_op {
+	SMP_ZONING_ENABLE_OP_NONE = 0x0,
+	SMP_ZONING_ENABLE_OP_ENABLE = 0x1,
+	SMP_ZONING_ENABLE_OP_DISABLE = 0x2
+} smp_zoning_enable_op_t;
+
+/*
+ * SAS-2 10.4.3.20 ZONED BROADCAST (no additional response)
+ */
+typedef struct smp_zoned_broadcast_req {
+	uint8_t _restricted1[2];
+	DECL_BITFIELD2(
+	    szbr_broadcast_type	:4,
+	    _reserved		:4);
+	uint8_t szbr_n_broadcast_source_zone_groups;
+	uint8_t szbr_broadcast_source_zone_groups[1];
+} smp_zoned_broadcast_req_t;
+
+/*
+ * SAS-2 10.4.3.21 ZONE LOCK
+ */
+typedef struct smp_zone_lock_req {
+	uint16_t szlr_expected_exp_change_count;
+	uint16_t szlr_zone_lock_inactivity_timeout;
+	uint8_t szlr_zone_manager_password[32];
+} smp_zone_lock_req_t;
+
+typedef struct smp_zone_lock_resp {
+	uint8_t _reserved1[4];
+	uint64_t szlr_active_zone_manager_sas_addr;
+} smp_zone_lock_resp_t;
+
+/*
+ * SAS-2 10.4.3.22 ZONE ACTIVATE (no additional response)
+ */
+typedef struct smp_zone_activate_req {
+	uint16_t szar_expected_exp_change_count;
+	uint8_t _reserved1[2];
+} smp_zone_activate_req_t;
+
+/*
+ * SAS-2 10.4.3.23 ZONE UNLOCK (no additional response)
+ */
+typedef struct smp_zone_unlock_req {
+	uint8_t _restricted1[2];
+	DECL_BITFIELD2(
+	    szur_activate_required	:1,
+	    _reserved1			:7);
+	uint8_t _reserved2;
+} smp_zone_unlock_req_t;
+
+/*
+ * SAS-2 10.4.3.24 CONFIGURE ZONE MANAGER PASSWORD (no additional response)
+ */
+typedef struct smp_config_zone_manager_password_req {
+	uint16_t sczmpr_expected_exp_change_count;
+	DECL_BITFIELD2(
+	    sczmpr_save		:2,		/* smp_zoning_save_t */
+	    _reserved1		:6);
+	uint8_t _reserved2;
+	uint8_t sczmpr_zone_manager_password[32];
+	uint8_t sczmpr_new_zone_manager_password[32];
+} smp_config_zone_manager_password_req_t;
+
+/*
+ * SAS-2 10.4.3.25 CONFIGURE ZONE PHY INFORMATION (no additional response)
+ */
+typedef struct smp_zone_phy_config_descr {
+	uint8_t szpcd_phy_identifier;
+	DECL_BITFIELD6(
+	    _reserved1				:2,
+	    szpcd_zone_group_persistent		:1,
+	    _reserved2				:1,
+	    szpcd_requested_inside_zpsds	:1,
+	    szpcd_inside_zpsds_persistent	:1,
+	    _reserved3				:2);
+	uint8_t _reserved4;
+	uint8_t szpcd_zone_group;
+} smp_zone_phy_config_descr_t;
+
+typedef struct smp_config_zone_phy_info_req {
+	uint16_t sczpir_expected_exp_change_count;
+	DECL_BITFIELD2(
+	    sczpir_save		:2,		/* smp_zoning_save_t */
+	    _reserved1		:6);
+	uint8_t sczpir_n_descrs;
+	smp_zone_phy_config_descr_t sczpir_descrs[1];
+} smp_config_zone_phy_info_req_t;
+
+/*
+ * SAS-2 10.4.3.26 CONFIGURE ZONE PERMISSION TABLE (no additional response)
+ */
+typedef struct smp_config_zone_perm_table_req {
+	uint16_t sczptr_expected_exp_change_count;
+	uint8_t sczptr_starting_source_zone_group;
+	uint8_t sczptr_n_descrs;
+	DECL_BITFIELD3(
+	    sczptr_save			:2,	/* smp_zoning_save_t */
+	    _reserved1			:4,
+	    sczptr_n_zone_groups	:2);	/* smp_n_zone_grps_t */
+	uint8_t _reserved2[7];
+	uint8_t sczptr_descrs[1];	/* smp_zone_perm_descrXXX_t */
+} smp_config_zone_perm_table_req_t;
+
+/*
+ * SAS-2 10.4.3.27 CONFIGURE ROUTE INFORMATION (no additional response)
+ */
+typedef struct smp_config_route_info_req {
+	uint16_t scrir_expected_exp_change_count;
+	uint16_t scrir_exp_route_index;
+	uint8_t _reserved1;
+	uint8_t scrir_phy_identifier;
+	uint8_t _reserved2[2];
+	DECL_BITFIELD2(
+	    _reserved3				:7,
+	    scrir_disable_exp_route_entry	:1);
+	uint8_t _reserved4[3];
+	uint64_t scrir_routed_sas_addr;
+	uint8_t _reserved5[16];
+} smp_config_route_info_req_t;
+
+/*
+ * SAS-2 10.4.3.28 PHY CONTROL (no additional response)
+ */
+typedef struct smp_phy_control_req {
+	uint16_t spcr_expected_exp_change_count;
+	uint8_t _reserved1[3];
+	uint8_t spcr_phy_identifier;
+	uint8_t spcr_phy_operation;
+	DECL_BITFIELD2(
+	    spcr_update_partial_pwy_timeout	:1,
+	    _reserved2				:7);
+	uint8_t _reserved3[12];
+	uint64_t spcr_attached_device_name;
+	DECL_BITFIELD2(
+	    _reserved4				:4,
+	    spcr_prog_min_phys_link_rate	:4);	/* smp_link_rate_t */
+	DECL_BITFIELD2(
+	    _reserved5				:4,
+	    spcr_prog_max_phys_link_rate	:4);	/* smp_link_rate_t */
+	uint8_t _reserved6[2];
+	DECL_BITFIELD2(
+	    spcr_partial_pwy_timeout	:4,
+	    _reserved7			:4);
+	uint8_t _reserved8[3];
+} smp_phy_control_req_t;
+
+typedef enum smp_phy_op {
+	SMP_PHY_OP_NOP = 0x00,
+	SMP_PHY_OP_LINK_RESET = 0x01,
+	SMP_PHY_OP_HARD_RESET = 0x02,
+	SMP_PHY_OP_DISABLE = 0x03,
+	SMP_PHY_OP_CLEAR_ERROR_LOG = 0x05,
+	SMP_PHY_OP_CLEAR_AFFILIATION = 0x06,
+	SMP_PHY_OP_TRANSMIT_SATA_PORT_SELECTION_SIGNAL = 0x07,
+	SMP_PHY_OP_CLEAR_STP_NEXUS_LOSS = 0x08,
+	SMP_PHY_OP_SET_ATTACHED_DEVICE_NAME = 0x09
+} smp_phy_op_t;
+
+/*
+ * SAS-2 10.4.3.29 PHY TEST FUNCTION (no additional response)
+ */
+typedef struct smp_phy_test_function_req {
+	uint16_t sptfr_expected_exp_change_count;
+	uint8_t _reserved1[3];
+	uint8_t sptfr_phy_identifier;
+	uint8_t sptfr_phy_test_function;
+	uint8_t sptfr_phy_test_pattern;		/* smp_phy_test_function_t */
+	uint8_t _reserved2[3];
+	DECL_BITFIELD4(
+	    sptfr_test_pattern_phys_link_rate	:4,	/* smp_link_rate_t */
+	    sptfr_test_pattern_ssc		:2,
+	    sptfr_test_pattern_sata		:1,
+	    _reserved3				:1);
+	uint8_t _reserved4[3];
+	uint8_t sptfr_phy_test_pattern_dwords_ctl;
+	uint8_t sptfr_phy_test_pattern_dwords[8];
+	uint8_t _reserved5[12];
+} smp_phy_test_function_req_t;
+
+typedef enum smp_phy_test_function {
+	SMP_PHY_TEST_FN_STOP = 0x00,
+	SMP_PHY_TEST_FN_TRANSMIT_PATTERN = 0x01
+} smp_phy_test_function_t;
+
+/*
+ * SAS-2 10.4.3.30 CONFIGURE PHY EVENT (no additional response)
+ */
+typedef struct smp_phy_event_config_descr {
+	uint8_t _reserved1[3];
+	uint8_t specd_phy_event_source;
+	uint32_t specd_peak_value_detector_threshold;
+} smp_phy_event_config_descr_t;
+
+typedef struct smp_config_phy_event_req {
+	uint16_t scper_expected_exp_change_count;
+	DECL_BITFIELD2(
+	    scper_clear_peaks	:1,
+	    _reserved1		:7);
+	uint8_t _reserved2[2];
+	uint8_t scper_phy_identifier;
+	uint8_t _reserved3;
+	uint8_t scper_n_descrs;
+	smp_phy_event_config_descr_t scper_descrs[1];
+} smp_config_phy_event_req_t;
+
+#pragma	pack()
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_SCSI_GENERIC_SMP_FRAMES_H */
--- a/usr/src/uts/common/sys/scsi/conf/autoconf.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/scsi/conf/autoconf.h	Mon Oct 05 09:17:26 2009 -0400
@@ -19,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _SYS_SCSI_CONF_AUTOCONF_H
 #define	_SYS_SCSI_CONF_AUTOCONF_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -75,6 +73,9 @@
 #define	SCSI_OPTIONS_NLUNS_8		0x20000
 #define	SCSI_OPTIONS_NLUNS_16		0x30000
 #define	SCSI_OPTIONS_NLUNS_32		0x40000
+#define	SCSI_OPTIONS_NLUNS_64		0x50000
+#define	SCSI_OPTIONS_NLUNS_128		0x60000
+#define	SCSI_OPTIONS_NLUNS_256		0x70000
 
 #define	SCSI_OPTIONS_NLUNS(n)		((n) & SCSI_OPTIONS_NLUNS_MASK)
 
@@ -153,14 +154,41 @@
 #define	SCSI_DEFAULT_SELECTION_TIMEOUT	250
 
 /*
- * Kernel references
+ * SCSI subsystem scsi_enumeration options.
+ *
+ * Knob for SPI (SCSI Parallel Intrconnect) enumeration. Unless an HBA defines
+ * it's own tran_bus_config, SPI enumeration is used. The "scsi_enumeration"
+ * knob determines how SPI enumeration is performed.
+ *
+ * The global variable "scsi_enumeration" is used as the default value of the
+ * "scsi-enumeration" property. In addition to enabling/disabling enumeration
+ * (bit 0), target and lun threading can be specified.  Having things
+ * multi-threaded does not guarantee reduce configuration time, however when
+ * the bus is marginal multi-threading can substaintaly reduce configuration
+ * time because targets negotiate to stable transfer speeds in parallel - so
+ * all targets have stabalized by the time the sequential attach(9E) operations
+ * begin.  Running multi-threaded also helps verification of framework and HBA
+ * locking: a BUS_CONFIG_ALL is equivalent to every target and lun combination
+ * getting a BUS_CONFIG_ONE from a separate thread at the same time.  A disable
+ * mechanism is provided to accomidate buggy HBAs (set scsi-enumeration=7
+ * driver.conf). Values are:
+ *
+ *	0	driver.conf enumeration
+ *	1	dynamic enumeration with target and lun multi-threading.
+ *	3	dynamic enumeration with lun multi-threading disabled.
+ *	5	dynamic enumeration with target multi-threading disabled;
+ *	7	dynamic enumeration with target/lun multi-threading disabled.
  */
+#define	SCSI_ENUMERATION_ENABLE			0x1
+#define	SCSI_ENUMERATION_MT_LUN_DISABLE		0x2
+#define	SCSI_ENUMERATION_MT_TARGET_DISABLE	0x4
 
 #ifdef	_KERNEL
 /*
  * Global SCSI config variables / options
  */
 extern int		scsi_options;
+extern int		scsi_enumeration;
 extern unsigned int	scsi_reset_delay;	/* specified in milli seconds */
 extern int		scsi_tag_age_limit;
 extern int		scsi_watchdog_tick;
--- a/usr/src/uts/common/sys/scsi/conf/device.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/scsi/conf/device.h	Mon Oct 05 09:17:26 2009 -0400
@@ -141,6 +141,13 @@
 	void			*sd_pathinfo;
 
 	/*
+	 * Counter that prevents demotion of DS_INITIALIZED node (esp loss of
+	 * devi_addr) by causing DDI_CTLOPS_UNINITCHILD failure - devi_ref
+	 * will not protect demotion of DS_INITIALIZED node.
+	 */
+	int			sd_uninit_prevent;
+
+	/*
 	 * The 'sd_tran_safe' field is a grotty hack that allows direct-access
 	 * (non-scsa) drivers (like chs, ata, and mlx - which all make cmdk
 	 * children) to *illegally* put their own vector in the scsi_address(9S)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/scsi/generic/sas.h	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,199 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * SAS Common Structures and Definitions
+ * sas2r14, simplified/reduced
+ */
+#ifndef	_SAS_H
+#define	_SAS_H
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/sysmacros.h>
+/*
+ * SAS Address Frames
+ * Trailing 4 byte CRC not included.
+ */
+typedef struct {
+	DECL_BITFIELD3(
+	    address_frame_type		:4,
+	    device_type			:3,
+	    reserved0			:1);
+	DECL_BITFIELD2(
+	    reason			:4,
+	    reserved1			:4);
+	DECL_BITFIELD5(
+	    restricted0			:1,
+	    smp_ini_port		:1,
+	    stp_ini_port		:1,
+	    ssp_ini_port		:1,
+	    reserved2			:4);
+	DECL_BITFIELD5(
+	    restricted1			:1,
+	    smp_tgt_port		:1,
+	    stp_tgt_port		:1,
+	    ssp_tgt_port		:1,
+	    reserved3			:4);
+	uint8_t		device_name[8];
+	uint8_t		sas_address[8];
+	uint8_t		phy_identifier;
+	DECL_BITFIELD4(
+	    break_reply_capable		:1,
+	    requested_inside_zpsds	:1,
+	    inside_zpsds_persistent	:1,
+	    reserved4			:5);
+	uint8_t		reserved5[6];
+} sas_identify_af_t;
+
+typedef struct {
+	DECL_BITFIELD3(
+	    address_frame_type		:4,
+	    protocol			:3,
+	    ini_port			:1);
+	DECL_BITFIELD2(
+	    connection_rate		:4,
+	    features			:4);
+	uint16_t 	itag;			/* initiator connection tag */
+	uint8_t 	sas_dst[8];		/* destination sas address */
+	uint8_t 	sas_src[8];		/* source sas address */
+	uint8_t 	src_zone_group;		/* source zone group  */
+	uint8_t 	path_block_count;	/* pathway blocked count */
+	uint16_t	arb_wait_time;		/* arbitration wait time */
+	uint8_t 	compat[4];		/* 'more' compatible features */
+} sas_open_af_t;
+
+#define	SAS_AF_IDENTIFY			0
+#define	SAS_AF_OPEN			1
+
+#define	SAS_IF_DTYPE_ENDPOINT		1
+#define	SAS_IF_DTYPE_EDGE		2
+#define	SAS_IF_DTYPE_FANOUT		3	/* obsolete */
+
+#define	SAS_OF_PROTO_SMP		0
+#define	SAS_OF_PROTO_SSP		1
+#define	SAS_OF_PROTO_STP		2
+
+#define	SAS_SSP_SUPPORT			0x8
+#define	SAS_STP_SUPPORT			0x4
+#define	SAS_SMP_SUPPORT			0x2
+
+
+#define	SAS_CONNRATE_1_5_GBPS		0x8
+#define	SAS_CONNRATE_3_0_GBPS		0x9
+#define	SAS_CONNRATE_6_0_GBPS		0xA
+
+#define	SAS_SATA_SUPPORT		0x1
+#define	SAS_ATTACHED_NAME_OFFSET	52	/* SAS-2 only */
+
+/*
+ * SSP Definitions
+ */
+typedef struct {
+	uint8_t		lun[8];
+	uint8_t		reserved0;
+	DECL_BITFIELD3(
+	    task_attribute	:2,
+	    command_priority	:4,
+	    enable_first_burst	:1);
+	uint8_t		reserved1;
+	DECL_BITFIELD2(
+	    reserved2		:2,
+	    addi_cdb_len	:6);
+	uint8_t		cdb[16];
+	/* additional cdb bytes go here, followed by 4 byte CRC */
+} sas_ssp_cmd_iu_t;
+
+#define	SAS_CMD_TASK_ATTR_SIMPLE	0x00
+#define	SAS_CMD_TASK_ATTR_HEAD		0x01
+#define	SAS_CMD_TASK_ATTR_ORDERED	0x02
+#define	SAS_CMD_TASK_ATTR_ACA		0x04
+
+typedef struct {
+	uint8_t		reserved0[8];
+	uint16_t	status_qualifier;
+	DECL_BITFIELD2(
+	    datapres		:2,
+	    reserved1		:6);
+	uint8_t		status;
+	uint8_t		reserved2[4];
+	uint32_t	sense_data_length;
+	uint32_t	response_data_length;
+	uint8_t		rspd[];
+	/* response data followed by sense data goes here */
+} sas_ssp_rsp_iu_t;
+
+/* length of bytes up to response data */
+#define	SAS_RSP_HDR_SIZE		24
+
+#define	SAS_RSP_DATAPRES_NO_DATA	0x00
+#define	SAS_RSP_DATAPRES_RESPONSE_DATA	0x01
+#define	SAS_RSP_DATAPRES_SENSE_DATA	0x02
+
+/*
+ * When the RSP IU is type RESPONSE_DATA,
+ * the first 4 bytes of response data should
+ * be a Big Endian representation of one of
+ * these codes.
+ */
+#define	SAS_RSP_TMF_COMPLETE		0x00
+#define	SAS_RSP_INVALID_FRAME		0x02
+#define	SAS_RSP_TMF_NOT_SUPPORTED	0x04
+#define	SAS_RSP_TMF_FAILED		0x05
+#define	SAS_RSP_TMF_SUCCEEDED		0x08
+#define	SAS_RSP_TMF_INCORRECT_LUN	0x09
+#define	SAS_RSP_OVERLAPPED_OIPTTA	0x0A
+
+/*
+ * Task Management Functions- should be in a SAM definition file
+ */
+#define	SAS_ABORT_TASK			0x01
+#define	SAS_ABORT_TASK_SET		0x02
+#define	SAS_CLEAR_TASK_SET		0x04
+#define	SAS_LOGICAL_UNIT_RESET		0x08
+#define	SAS_I_T_NEXUS_RESET		0x10
+#define	SAS_CLEAR_ACA			0x40
+#define	SAS_QUERY_TASK			0x80
+#define	SAS_QUERY_TASK_SET		0x81
+#define	SAS_QUERY_UNIT_ATTENTION	0x82
+
+/*
+ * PHY size changed from SAS1.1 to SAS2.
+ */
+#define	SAS_PHYNUM_MAX			127
+#define	SAS_PHYNUM_MASK			0x7f
+
+#define	SAS2_PHYNUM_MAX			254
+#define	SAS2_PHYNUM_MASK		0xff
+
+
+/*
+ * Maximum SMP payload size, including CRC
+ */
+#define	SAS_SMP_MAX_PAYLOAD		1032
+#ifdef	__cplusplus
+}
+#endif
+#endif	/* _SAS_H */
--- a/usr/src/uts/common/sys/scsi/generic/smp_frames.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/scsi/generic/smp_frames.h	Mon Oct 05 09:17:26 2009 -0400
@@ -20,14 +20,12 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#ifndef _SYS_SCSI_IMPL_SMP_FRAME_H
-#define	_SYS_SCSI_IMPL_SMP_FRAME_H
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#ifndef _SYS_SCSI_GENERIC_SMP_FRAMES_H
+#define	_SYS_SCSI_GENERIC_SMP_FRAMES_H
 
 #ifdef	__cplusplus
 extern "C" {
@@ -36,86 +34,1058 @@
 #include <sys/sysmacros.h>
 
 /*
- * The definitions of smp frame types and functions conforming to SAS v1.1.
- * The SAS v2.0 will be supported in the future when it is publicly released.
+ * The definitions of smp frame types and functions conforming to SAS-1.1 and
+ * SAS-2.  Consumers are expected to determine protocol support by examining
+ * the response to the REPORT GENERAL function.
  */
 
-typedef enum  {
+typedef enum smp_frame_type {
 	SMP_FRAME_TYPE_REQUEST		= 0x40,
 	SMP_FRAME_TYPE_RESPONSE		= 0x41
-} smp_frame_types;
+} smp_frame_type_t;
 
-typedef enum {
-	SMP_REPORT_GENERAL			= 0x00,
-	SMP_REPORT_MANUFACTURER_INFO	= 0x01,
-	SMP_DISCOVER				= 0x10,
-	SMP_REPORT_PHY_ERROR_LOG		= 0x11,
-	SMP_PHY_SATA				= 0x12,
-	SMP_REPORT_ROUTE_INFORMATION	= 0x13,
-	SMP_CONFIG_ROUTE_INFORMATION	= 0x90,
-	SMP_PHY_CONTROL			= 0x91,
-	SMP_PHY_TEST_FUNCTION		= 0x92
-} smp_func_types;
+typedef enum smp_function {
+	SMP_FUNC_REPORT_GENERAL			= 0x00,
+	SMP_FUNC_REPORT_MANUFACTURER_INFO	= 0x01,
+	SMP_FUNC_READ_GPIO_REGISTER		= 0x02,
+	SMP_FUNC_REPORT_SELF_CONFIG_STATUS	= 0x03,
+	SMP_FUNC_REPORT_ZONE_PERM_TABLE		= 0x04,
+	SMP_FUNC_REPORT_ZONE_MANAGER_PASSWORD	= 0x05,
+	SMP_FUNC_REPORT_BROADCAST		= 0x06,
+	SMP_FUNC_DISCOVER			= 0x10,
+	SMP_FUNC_REPORT_PHY_ERROR_LOG		= 0x11,
+	SMP_FUNC_REPORT_PHY_SATA		= 0x12,
+	SMP_FUNC_REPORT_ROUTE_INFO		= 0x13,
+	SMP_FUNC_REPORT_PHY_EVENT		= 0x14,
+	SMP_FUNC_DISCOVER_LIST			= 0x20,
+	SMP_FUNC_REPORT_PHY_EVENT_LIST		= 0x21,
+	SMP_FUNC_REPORT_EXP_ROUTE_TABLE_LIST	= 0x22,
+	SMP_FUNC_CONFIG_GENERAL			= 0x80,
+	SMP_FUNC_ENABLE_DISABLE_ZONING		= 0x81,
+	SMP_FUNC_WRITE_GPIO_REGISTER		= 0x82,
+	SMP_FUNC_ZONED_BROADCAST		= 0x85,
+	SMP_FUNC_ZONE_LOCK			= 0x86,
+	SMP_FUNC_ZONE_ACTIVATE			= 0x87,
+	SMP_FUNC_ZONE_UNLOCK			= 0x88,
+	SMP_FUNC_CONFIG_ZONE_MANAGER_PASSWORD	= 0x89,
+	SMP_FUNC_CONFIG_ZONE_PHY_INFO		= 0x8A,
+	SMP_FUNC_CONFIG_ZONE_PERM_TABLE		= 0x8B,
+	SMP_FUNC_CONFIG_ROUTE_INFO		= 0x90,
+	SMP_FUNC_PHY_CONTROL			= 0x91,
+	SMP_FUNC_PHY_TEST_FUNCTION		= 0x92,
+	SMP_FUNC_CONFIG_PHY_EVENT		= 0x93
+} smp_function_t;
 
-/*
- * The reqsize and rspsize in usmp_req and usmp_rsp are reserved in
- * SAS v1.1, and the fields should be zero if target device is SAS v1.1
- * compliant.
- */
+typedef enum smp_result {
+	SMP_RES_FUNCTION_ACCEPTED		= 0x00,
+	SMP_RES_UNKNOWN_FUNCTION		= 0x01,
+	SMP_RES_FUNCTION_FAILED			= 0x02,
+	SMP_RES_INVALID_REQUEST_FRAME_LENGTH	= 0x03,
+	SMP_RES_INVALID_EXPANDER_CHANGE_COUNT	= 0x04,
+	SMP_RES_BUSY				= 0x05,
+	SMP_RES_INCOMPLETE_DESCRIPTOR_LIST	= 0x06,
+	SMP_RES_PHY_DOES_NOT_EXIST		= 0x10,
+	SMP_RES_INDEX_DOES_NOT_EXIST		= 0x11,
+	SMP_RES_PHY_DOES_NOT_SUPPORT_SATA	= 0x12,
+	SMP_RES_UNKNOWN_PHY_OPERATION		= 0x13,
+	SMP_RES_UNKNOWN_PHY_TEST_FUNCTION	= 0x14,
+	SMP_RES_PHY_TEST_IN_PROGRESS		= 0x15,
+	SMP_RES_PHY_VACANT			= 0x16,
+	SMP_RES_UNKNOWN_PHY_EVENT_SOURCE	= 0x17,
+	SMP_RES_UNKNOWN_DESCRIPTOR_TYPE		= 0x18,
+	SMP_RES_UNKNOWN_PHY_FILTER		= 0x19,
+	SMP_RES_AFFILIATION_VIOLATION		= 0x1A,
+	SMP_RES_ZONE_VIOLATION			= 0x20,
+	SMP_RES_NO_MANAGEMENT_ACCESS_RIGHTS	= 0x21,
+	SMP_RES_UNKNOWN_ENABLE_DISABLE_ZONING	= 0x22,
+	SMP_RES_ZONE_LOCK_VIOLATION		= 0x23,
+	SMP_RES_NOT_ACTIVATED			= 0x24,
+	SMP_RES_ZONE_GROUP_OUT_OF_RANGE		= 0x25,
+	SMP_RES_NO_PHYSICAL_PRESENCE		= 0x26,
+	SMP_RES_SAVING_NOT_SUPPORTED		= 0x27,
+	SMP_RES_SOURCE_ZONE_GROUP_DNE		= 0x28,
+	SMP_RES_NONE				= -1
+} smp_result_t;
 
 #pragma	pack(1)
-typedef struct usmp_req {
-	uint8_t		smpo_frametype;
-	uint8_t		smpo_function;
-	uint8_t		smpo_reserved;
-	uint8_t		smpo_reqsize;
-	uint8_t		smpo_msgframe[1];
-} usmp_req_t;
+
+/*
+ * SAS-2 10.4.3.2 request frame format
+ */
+typedef struct smp_request_frame {
+	uint8_t srf_frame_type;
+	uint8_t srf_function;
+	uint8_t srf_allocated_response_len;	/* reserved in SAS-1 */
+	uint8_t srf_request_len;
+	uint8_t srf_data[1];
+} smp_request_frame_t;
+
+/*
+ * SAS-2 10.4.3.3 response frame format
+ */
+typedef struct smp_response_frame {
+	uint8_t srf_frame_type;
+	uint8_t srf_function;
+	uint8_t srf_result;
+	uint8_t srf_response_len;	/* reserved in SAS-1 */
+	uint8_t srf_data[1];
+} smp_response_frame_t;
+
+typedef uint8_t smp_crc_t[4];
+
+#ifdef offsetof
+#define	SMP_REQ_MINLEN	\
+	(offsetof(smp_request_frame_t, srf_data[0]) + sizeof (smp_crc_t))
+#define	SMP_RESP_MINLEN	\
+	(offsetof(smp_response_frame_t, srf_data[0]) + sizeof (smp_crc_t))
+#endif	/* offsetof */
+
+/*
+ * SAS-2 10.4.3.4 REPORT GENERAL (no additional request bytes)
+ */
+typedef struct smp_report_general_resp {
+	uint16_t srgr_exp_change_count;
+	uint16_t srgr_exp_route_indexes;
+	DECL_BITFIELD2(
+	    _reserved1		:7,
+	    srgr_long_response	:1);
+	uint8_t srgr_number_of_phys;
+	DECL_BITFIELD7(
+	    srgr_externally_configurable_route_table	:1,
+	    srgr_configuring				:1,
+	    srgr_configures_others			:1,
+	    srgr_open_reject_retry_supported		:1,
+	    srgr_stp_continue_awt			:1,
+	    _reserved2					:2,
+	    srgr_table_to_table_supported		:1);
+	uint8_t _reserved3;
+	uint64_t srgr_enclosure_logical_identifier;
+	uint8_t _reserved4[8];
+	uint8_t _reserved5[2];
+	uint16_t srgr_stp_bus_inactivity_time_limit;
+	uint16_t srgr_stp_maximum_connect_time_limit;
+	uint16_t srgr_stp_smp_nexus_loss_time;
+	DECL_BITFIELD7(
+	    srgr_zoning_enabled				:1,
+	    srgr_zoning_supported			:1,
+	    srgr_physical_presence_asserted		:1,
+	    srgr_physical_presence_supported		:1,
+	    srgr_zone_locked				:1,
+	    _reserved6					:1,
+	    srgr_number_of_zone_grps			:2);
+	DECL_BITFIELD6(
+	    srgr_saving_zoning_enabled_supported	:1,
+	    srgr_saving_zone_perm_table_supported	:1,
+	    srgr_saving_zone_phy_info_supported		:1,
+	    srgr_saving_zone_mgr_password_supported	:1,
+	    srgr_saving					:1,
+	    _reserved7					:4);
+	uint16_t srgr_max_routed_sas_addrs;
+	uint64_t srgr_active_zm_sas_addr;
+	uint16_t srgr_zone_lock_inactivity_limit;
+	uint8_t _reserved8[2];
+	uint8_t _reserved9;
+	uint8_t srgr_first_encl_conn_elem_idx;
+	uint8_t srgr_number_encl_conn_elem_idxs;
+	uint8_t _reserved10;
+	DECL_BITFIELD2(
+	    _reserved11					:7,
+	    srgr_reduced_functionality			:1);
+	uint8_t srgr_time_to_reduced_functionality;
+	uint8_t srgr_initial_time_to_reduced_functionality;
+	uint8_t srgr_max_reduced_functionality_time;
+	uint16_t srgr_last_self_conf_status_descr_idx;
+	uint16_t srgr_max_stored_self_config_status_descrs;
+	uint16_t srgr_last_phy_event_list_descr_idx;
+	uint16_t srgr_max_stored_phy_event_list_descrs;
+	uint16_t srgr_stp_reject_to_open_limit;
+	uint8_t _reserved12[2];
+} smp_report_general_resp_t;
+
+typedef enum smp_n_zone_grps {
+	SMP_ZONE_GROUPS_128	= 0x0,
+	SMP_ZONE_GROUPS_256	= 0x1
+} smp_n_zone_grps_t;
+
+/*
+ * SAS-2 10.4.3.5 REPORT MANUFACTURER INFORMATION (no additional request bytes)
+ */
+typedef struct smp_report_manufacturer_info_resp {
+	uint16_t srmir_exp_change_count;
+	uint8_t _reserved1[2];
+	DECL_BITFIELD2(
+	    srmir_sas_1_1_format	:1,
+	    _reserved2			:7);
+	uint8_t _reserved3[3];
+	char srmir_vendor_identification[8];
+	char srmir_product_identification[16];
+	char srmir_product_revision_level[4];
+	char srmir_component_vendor_identification[8];
+	uint16_t srmir_component_id;
+	uint8_t srmir_component_revision_level;
+	uint8_t _reserved4;
+	uint8_t srmir_vs_52[8];
+} smp_report_manufacturer_info_resp_t;
+
+/*
+ * SAS-2 10.4.3.6 REPORT SELF_CONFIGURATION STATUS
+ */
+typedef struct smp_report_self_config_status_req {
+	uint8_t _reserved1[2];
+	uint16_t srscsr_starting_self_config_status_descr_idx;
+} smp_report_self_config_status_req_t;
+
+typedef struct smp_report_self_config_status_resp {
+	uint16_t srscsr_exp_change_count;
+	uint16_t srscsr_starting_self_config_status_descr_idx;
+	uint16_t srscsr_number_self_config_status_descrs;
+	uint16_t srscsr_last_self_config_status_descr_idx;
+	uint8_t srscsr_self_config_status_descr_len;
+	uint8_t _reserved1[3];
+	uint8_t srscsr_descrs[1];
+} smp_report_self_config_status_resp_t;
+
+typedef struct smp_self_config_status_descr {
+	uint8_t sscsd_status_type;
+	DECL_BITFIELD2(
+	    sscsd_final		:1,
+	    _reserved1		:7);
+	uint8_t _reserved2;
+	uint8_t sscsd_phy_identifier;
+	uint8_t _reserved3[4];
+	uint64_t sscsd_sas_addr;
+} smp_self_config_status_descr_t;
+
+typedef enum smp_self_config_status_type {
+	SMP_SCST_NONSPECIFIC_ERROR	= 0x01,
+	SMP_SCST_CONNECTION		= 0x02,
+	SMP_SCST_ROUTE_TABLE_FULL	= 0x03,
+	SMP_SCST_NOMEM			= 0x04,
+	SMP_SCST_PHY_LAYER_ERROR	= 0x20,
+	SMP_SCST_LOST_SYNC		= 0x21,
+	SMP_SCST_LINK_LAYER_ERROR	= 0x40,
+	SMP_SCST_OPEN_TIMEOUT		= 0x41,
+	SMP_SCST_ABANDON_OPEN_REJECT	= 0x42,
+	SMP_SCST_RETRY_OPEN_REJECTS	= 0x43,
+	SMP_SCST_NEXUS_LOSS		= 0x44,
+	SMP_SCST_BREAK			= 0x45,
+	SMP_SCST_CRC_ERROR		= 0x46,
+	SMP_SCST_PORT_LAYER_ERROR	= 0x60,
+	SMP_SCST_RESPONSE_TIMEOUT	= 0x61,
+	SMP_SCST_TRANSPORT_LAYER_ERROR	= 0x80,
+	SMP_SCST_APP_LAYER_ERROR	= 0xA0,
+	SMP_SCST_RESPONSE_TOO_SHORT	= 0xA1,
+	SMP_SCST_UNSUPPORTED_VALUES	= 0xA2,
+	SMP_SCST_INCONSISTENT		= 0xA3,
+	SMP_SCST_CONFIGURING		= 0xA4
+} smp_self_config_status_type_t;
+
+/*
+ * SAS-2 10.4.3.7 REPORT ZONE PERMISSION TABLE
+ */
+typedef struct smp_report_zone_perm_table_req {
+	DECL_BITFIELD2(
+	    srzptr_report_type		:2,
+	    _reserved1			:6);
+	uint8_t _reserved2;
+	uint8_t srzptr_starting_src_zone_grp;
+	uint8_t srzptr_max_zone_perm_descrs;
+} smp_report_zone_perm_table_req_t;
+
+typedef enum smp_zone_perm_table_report_type {
+	SMP_ZPTRT_CURRENT		= 0x0,
+	SMP_ZPTRT_SHADOW		= 0x1,
+	SMP_ZPTRT_SAVED			= 0x2,
+	SMP_ZPTRT_DEFAULT		= 0x3
+} smp_zone_perm_table_report_type_t;
+
+typedef struct smp_report_zone_perm_table_resp {
+	uint16_t srzptr_exp_change_count;
+	DECL_BITFIELD3(
+	    srzptr_report_type		:2,
+	    _reserved1			:5,
+	    srzptr_zone_locked		:1);
+	DECL_BITFIELD2(
+	    _reserved2			:6,
+	    srzptr_number_zone_grps	:2);
+	uint8_t _reserved3[6];
+	uint8_t srzptr_starting_src_zone_grp;
+	uint8_t srzptr_number_zone_perm_descrs;
+	uint8_t srzptr_descrs[1];
+} smp_report_zone_perm_table_resp_t;
+
+typedef uint8_t smp_zone_perm_descr128_t[16];
+typedef uint8_t smp_zone_perm_descr256_t[32];
+
+#define	SMP_ZONE_PERM_BIT128(__d, __z)	\
+	((__d)[15 - ((__z) >> 3)] & (1 << ((__z) & 7)))
+
+#define	SMP_ZONE_PERM_SET128(__d, __z)	\
+	((__d)[15 - ((__z) >> 3)] |= (1 << ((__z) & 7)))
+
+#define	SMP_ZONE_PERM_CLR128(__d, __z)	\
+	((__d)[15 - ((__z) >> 3)] &= ~(1 << ((__z) & 7)))
+
+#define	SMP_ZONE_PERM_BIT256(__d, __z)	\
+	((__d)[31 - ((__z) >> 3)] & (1 << ((__z) & 7)))
+
+#define	SMP_ZONE_PERM_SET256(__d, __z)	\
+	((__d)[31 - ((__z) >> 3)] |= (1 << ((__z) & 7)))
+
+#define	SMP_ZONE_PERM_CLR256(__d, __z)	\
+	((__d)[31 - ((__z) >> 3)] &= ~(1 << ((__z) & 7)))
+
+/*
+ * SAS-2 10.4.3.8 REPORT ZONE MANAGER PASSWORD (no additional request bytes)
+ */
+typedef struct smp_report_zone_mgr_password_resp {
+	uint16_t srzmpr_exp_change_count;
+	uint8_t _reserved1[2];
+	uint8_t srzmpr_zone_mgr_password[32];
+} smp_report_zone_mgr_password_resp_t;
 
-typedef struct usmp_rsp {
-	uint8_t		smpi_frametype;
-	uint8_t		smpi_function;
-	uint8_t		smpi_result;
-	uint8_t		smpi_rspsize;
-	uint8_t		smpi_msgframe[1];
-} usmp_rsp_t;
+/*
+ * SAS-2 10.4.3.9 REPORT BROADCAST
+ */
+typedef struct smp_report_broadcast_req {
+	DECL_BITFIELD2(
+	    srbr_broadcast_type		:4,
+	    _reserved1			:4);
+	uint8_t _reserved2[3];
+} smp_report_broadcast_req_t;
+
+typedef enum smp_broadcast_type {
+	SMP_BROADCAST_CHANGE		= 0x0,
+	SMP_BROADCAST_RESERVED_CHANGE_0	= 0x1,
+	SMP_BROADCAST_RESERVED_CHANGE_1	= 0x2,
+	SMP_BROADCAST_SES		= 0x3,
+	SMP_BROADCAST_EXPANDER		= 0x4,
+	SMP_BROADCAST_ASYNC_EVENT	= 0x5,
+	SMP_BROADCAST_RESERVED_3	= 0x6,
+	SMP_BROADCAST_RESERVED_4	= 0x7,
+	SMP_BROADCAST_ZONE_ACTIVATE	= 0x8
+} smp_broadcast_type_t;
+
+typedef struct smp_broadcast_descr {
+	DECL_BITFIELD2(
+	    sbd_broadcast_type		:4,
+	    _reserved1			:4);
+	uint8_t sbd_phy_identifier;
+	DECL_BITFIELD2(
+	    sbd_broadcast_reason	:4,
+	    _reserved2			:4);
+	uint16_t sbd_broadcast_count;
+	uint8_t _reserved3[10];
+} smp_broadcast_descr_t;
+
+typedef struct smp_report_broadcast_resp {
+	uint16_t srbr_exp_change_count;
+	DECL_BITFIELD2(
+	    srbr_broadcast_type		:4,
+	    _reserved1			:4);
+	uint8_t srbr_number_broadcast_descrs;
+	smp_broadcast_descr_t srbr_descrs[1];
+} smp_report_broadcast_resp_t;
+
+/*
+ * SAS-2 10.4.3.10 DISCOVER
+ */
+typedef struct smp_discover_req {
+	uint8_t _reserved1[4];
+	DECL_BITFIELD2(
+	    sdr_ignore_zone_grp		:1,
+	    _reserved2			:7);
+	uint8_t sdr_phy_identifier;
+	uint8_t _reserved3[2];
+} smp_discover_req_t;
+
+typedef struct smp_snw3_phy_cap {
+	DECL_BITFIELD4(
+	    sspc_requested_logical_link_rate	:4,	/* smp_link_rate_t */
+	    _reserved1				:2,
+	    sspc_tx_ssc_type			:1,
+	    sspc_start				:1);
+	DECL_BITFIELD7(
+	    _reserved2				:2,
+	    sspc_g3_ssc				:1,
+	    sspc_g3_no_ssc			:1,
+	    sspc_g2_ssc				:1,
+	    sspc_g2_no_ssc			:1,
+	    sspc_g1_ssc				:1,
+	    sspc_g1_no_ssc			:1);
+	uint8_t _reserved3;
+	DECL_BITFIELD2(
+	    sspc_parity		:1,
+	    _reserved4		:7);
+} smp_snw3_phy_cap_t;
 
-struct smp_crc {
-	uint8_t code[4];
-};
+typedef struct smp_discover_resp {
+	uint16_t sdr_exp_change_count;
+	uint8_t _reserved1[3];
+	uint8_t sdr_phy_identifier;
+	uint8_t _reserved2[2];
+	DECL_BITFIELD3(
+	    sdr_attached_reason		:4,
+	    sdr_attached_device_type	:3,
+	    _reserved3			:1);
+	DECL_BITFIELD2(
+	    sdr_negotiated_logical_link_rate	:4,	/* smp_link_rate_t */
+	    _reserved4				:4);
+	DECL_BITFIELD5(
+	    sdr_attached_sata_host	:1,
+	    sdr_attached_smp_initiator	:1,
+	    sdr_attached_stp_initiator	:1,
+	    sdr_attached_ssp_initiator	:1,
+	    _reserved5			:4);
+	DECL_BITFIELD6(
+	    sdr_attached_sata_device		:1,
+	    sdr_attached_smp_target		:1,
+	    sdr_attached_stp_target		:1,
+	    sdr_attached_ssp_target		:1,
+	    _reserved6				:3,
+	    sdr_attached_sata_port_selector	:1);
+	uint64_t sdr_sas_addr;
+	uint64_t sdr_attached_sas_addr;
+	uint8_t sdr_attached_phy_identifier;
+	DECL_BITFIELD4(
+	    sdr_attached_break_reply_capable		:1,
+	    sdr_attached_requested_inside_zpsds		:1,
+	    sdr_attached_inside_zpsds_persistent	:1,
+	    _reserved7					:5);
+	uint8_t _reserved8[6];
+	DECL_BITFIELD2(
+	    sdr_hw_min_phys_link_rate	:4,	/* smp_link_rate_t */
+	    sdr_prog_min_phys_link_rate	:4);	/* smp_link_rate_t */
+	DECL_BITFIELD2(
+	    sdr_hw_max_phys_link_rate	:4,	/* smp_link_rate_t */
+	    sdr_prog_max_phys_link_rate	:4);	/* smp_link_rate_t */
+	uint8_t sdr_phy_change_count;
+	DECL_BITFIELD3(
+	    sdr_partial_pwy_timeout	:4,
+	    _reserved9			:3,
+	    sdr_virtual_phy		:1);
+	DECL_BITFIELD2(
+	    sdr_routing_attr		:4,	/* smp_routing_attr_t */
+	    _reserved10			:4);
+	DECL_BITFIELD2(
+	    sdr_connector_type		:7,
+	    _reserved11			:1);
+	uint8_t sdr_connector_element_index;
+	uint8_t sdr_connector_physical_link;
+	uint8_t _reserved12[2];
+	uint8_t sdr_vendor[2];
+	uint64_t sdr_attached_device_name;
+	DECL_BITFIELD8(
+	    sdr_zoning_enabled				:1,
+	    sdr_inside_zpsds				:1,
+	    sdr_zone_group_persistent			:1,
+	    _reserved13					:1,
+	    sdr_requested_inside_zpsds			:1,
+	    sdr_inside_zpsds_persistent			:1,
+	    sdr_requested_inside_zpsds_changed_by_exp	:1,
+	    _reserved14					:1);
+	uint8_t _reserved15[2];
+	uint8_t sdr_zone_group;
+	uint8_t sdr_self_config_status;
+	uint8_t sdr_self_config_levels_completed;
+	uint8_t _reserved16[2];
+	uint64_t sdr_self_config_sas_addr;
+	smp_snw3_phy_cap_t sdr_prog_phy_cap;
+	smp_snw3_phy_cap_t sdr_current_phy_cap;
+	smp_snw3_phy_cap_t sdr_attached_phy_cap;
+	uint8_t _reserved17[6];
+	DECL_BITFIELD2(
+	    sdr_negotiated_phys_link_rate	:4,	/* smp_link_rate_t */
+	    sdr_reason				:4);
+	DECL_BITFIELD3(
+	    sdr_hw_muxing_supported	:1,
+	    sdr_negotiated_ssc		:1,
+	    _reserved18			:6);
+	DECL_BITFIELD7(
+	    sdr_default_zoning_enabled		:1,
+	    _reserved19				:1,
+	    sdr_default_zone_group_persistent	:1,
+	    _reserved20				:1,
+	    sdr_default_requested_inside_zpsds	:1,
+	    sdr_default_inside_zpsds_persistent	:1,
+	    _reserved21				:2);
+	uint8_t _reserved22[2];
+	uint8_t sdr_default_zone_group;
+	DECL_BITFIELD7(
+	    sdr_saved_zoning_enabled		:1,
+	    _reserved23				:1,
+	    sdr_saved_zone_group_persistent	:1,
+	    _reserved24				:1,
+	    sdr_saved_requested_inside_zpsds	:1,
+	    sdr_saved_inside_zpsds_persistent	:1,
+	    _reserved25				:2);
+	uint8_t _reserved26[2];
+	uint8_t saved_zone_group;
+	DECL_BITFIELD6(
+	    _reserved27				:2,
+	    sdr_shadow_zone_group_persistent	:1,
+	    _reserved28				:1,
+	    sdr_shadow_requested_inside_zpsds	:1,
+	    sdr_shadow_inside_zpsds_persistent	:1,
+	    _reserved29				:2);
+	uint8_t _reserved30[2];
+	uint8_t sdr_shadow_zone_group;
+} smp_discover_resp_t;
+
+typedef enum smp_link_rate {
+	SMP_LINK_RATE_NO_CHANGE = 0x0,
+	SMP_LINK_RATE_DISABLED = 0x1,
+	SMP_LINK_RATE_RESET_PROBLEM = 0x2,
+	SMP_LINK_RATE_SPINUP_HOLD = 0x3,
+	SMP_LINK_RATE_PORT_SELECTOR = 0x4,
+	SMP_LINK_RATE_RESET = 0x5,
+	SMP_LINK_RATE_UNSUPPORTED = 0x6,
+	SMP_LINK_RATE_1_5 = 0x8,
+	SMP_LINK_RATE_3 = 0x9,
+	SMP_LINK_RATE_6 = 0xA
+} smp_link_rate_t;
+
+typedef enum smp_device_type {
+	SMP_DEV_NONE = 0x0,
+	SMP_DEV_SAS_SATA = 0x1,
+	SMP_DEV_EXPANDER = 0x2,
+	SMP_DEV_EXPANDER_OLD = 0x3
+} smp_device_type_t;
+
+typedef enum smp_routing_attr {
+	SMP_ROUTING_DIRECT = 0x0,
+	SMP_ROUTING_SUBTRACTIVE = 0x1,
+	SMP_ROUTING_TABLE = 0x2
+} smp_routing_attr_t;
+
+/*
+ * SAS-2 10.4.3.11 REPORT PHY ERROR LOG
+ */
+typedef struct smp_report_phy_error_log_req {
+	uint8_t _reserved1[5];
+	uint8_t srpelr_phy_identifier;
+	uint8_t _reserved2[2];
+} smp_report_phy_error_log_req_t;
+
+typedef struct smp_report_phy_error_log_resp {
+	uint16_t srpelr_exp_change_count;
+	uint8_t _reserved1[3];
+	uint8_t srpelr_phy_identifier;
+	uint8_t _reserved2[2];
+	uint32_t srpelr_invalid_dword_count;
+	uint32_t srpelr_running_disparity_error_count;
+	uint32_t srpelr_loss_dword_sync_count;
+	uint32_t srpelr_phy_reset_problem_count;
+} smp_report_phy_error_log_resp_t;
+
+/*
+ * SAS-2 10.4.3.12 REPORT PHY SATA
+ */
+typedef struct smp_report_phy_sata_req {
+	uint8_t _reserved1[5];
+	uint8_t srpsr_phy_identifier;
+	uint8_t srpsr_affiliation_context;
+	uint8_t _reserved2;
+} smp_report_phy_sata_req_t;
 
-struct smp_report_general_req {
-	uint8_t		frametype;
-	uint8_t		function;
-	uint8_t 	reserved_byte2;
-	uint8_t 	reqsize;
-	struct smp_crc	crc;
-};
+typedef struct smp_report_phy_sata_resp {
+	uint16_t srpsr_exp_change_count;
+	uint8_t _reserved1[3];
+	uint8_t srpsr_phy_identifier;
+	uint8_t _reserved2;
+	DECL_BITFIELD4(
+	    srpsr_affiliation_valid		:1,
+	    srpsr_affiliations_supported	:1,
+	    srpsr_stp_nexus_loss		:1,
+	    _reserved3				:5);
+	uint8_t _reserved4[4];
+	uint64_t srpsr_stp_sas_addr;
+	uint8_t srpsr_register_device_host_fis[20];
+	uint8_t _reserved5[4];
+	uint64_t srpsr_affiliated_stp_init_sas_addr;
+	uint64_t srpsr_stp_nexus_loss_sas_addr;
+	uint8_t _reserved6;
+	uint8_t srpsr_affiliation_context;
+	uint8_t srpsr_current_affiliation_contexts;
+	uint8_t srpsr_max_affiliation_contexts;
+} smp_report_phy_sata_resp_t;
+
+/*
+ * SAS-2 10.4.3.13 REPORT ROUTE INFORMATION
+ */
+typedef struct smp_report_route_info_req {
+	uint8_t _reserved1[2];
+	uint16_t srrir_exp_route_index;
+	uint8_t _reserved2;
+	uint8_t srrir_phy_identifier;
+	uint8_t _reserved3[2];
+} smp_report_route_info_req_t;
+
+typedef struct smp_report_route_info_resp {
+	uint16_t srrir_exp_change_count;
+	uint16_t srrir_exp_route_index;
+	uint8_t _reserved1;
+	uint8_t srrir_phy_identifier;
+	uint8_t _reserved2[2];
+	DECL_BITFIELD2(
+	    _reserved3				:7,
+	    srrir_exp_route_entry_disabled	:1);
+	uint8_t _reserved4[3];
+	uint64_t srrir_routed_sas_addr;
+	uint8_t _reserved5[16];
+} smp_report_route_info_resp_t;
+
+/*
+ * SAS-2 10.4.3.14 SAS-2 REPORT PHY EVENT
+ */
+typedef struct smp_report_phy_event_req {
+	uint8_t _reserved1;
+	uint8_t _reserved2[4];
+	uint8_t srper_phy_identifier;
+	uint8_t _reserved3[2];
+} smp_report_phy_event_req_t;
+
+typedef struct smp_phy_event_report_descr {
+	uint8_t _reserved1[3];
+	uint8_t sped_phy_event_source;
+	uint32_t sped_phy_event;
+	uint32_t sped_peak_detector_threshold;
+} smp_phy_event_report_descr_t;
+
+typedef struct smp_report_phy_event_resp {
+	uint16_t srper_exp_change_count;
+	uint8_t _reserved1[3];
+	uint8_t srper_phy_identifier;
+	uint8_t _reserved2[5];
+	uint8_t srper_n_phy_event_descrs;
+	smp_phy_event_report_descr_t srper_phy_event_descrs[1];
+} smp_report_phy_event_resp_t;
+
+/*
+ * SAS-2 10.4.3.15 SAS-2 DISCOVER LIST
+ */
+typedef struct smp_discover_list_req {
+	uint8_t _reserved1[4];
+	uint8_t sdlr_starting_phy_identifier;
+	uint8_t sdlr_max_descrs;
+	DECL_BITFIELD3(
+	    sdlr_phy_filter		:4,
+	    _reserved2			:3,
+	    sdlr_ignore_zone_group	:1);
+	DECL_BITFIELD2(
+	    sdlr_descr_type		:4,
+	    _reserved3			:4);
+	uint8_t _reserved4[4];
+	uint8_t sdlr_vendor[12];
+} smp_discover_list_req_t;
+
+typedef struct smp_discover_short_descr {
+	uint8_t sdsd_phy_identifier;
+	uint8_t sdsd_function_result;
+	DECL_BITFIELD3(
+	    sdsd_attached_reason	:4,
+	    sdsd_attached_device_type	:3,
+	    _restricted1		:1);
+	DECL_BITFIELD2(
+	    sdsd_negotiated_logical_link_rate	:4,	/* smp_link_rate_t */
+	    _restricted2			:4);
+	DECL_BITFIELD5(
+	    sdsd_attached_sata_host	:1,
+	    sdsd_attached_smp_initiator	:1,
+	    sdsd_attached_stp_initiator	:1,
+	    sdsd_attached_ssp_initiator	:1,
+	    _restricted3		:4);
+	DECL_BITFIELD6(
+	    sdsd_attached_sata_device		:1,
+	    sdsd_attached_smp_target		:1,
+	    sdsd_attached_stp_target		:1,
+	    sdsd_attached_ssp_target		:1,
+	    _restricted4			:3,
+	    sdsd_attached_sata_port_selector	:1);
+	DECL_BITFIELD3(
+	    sdsd_routing_attribute	:4,		/* smp_routing_attr_t */
+	    _reserved1			:3,
+	    sdsd_virtual_phy		:1);
+	DECL_BITFIELD2(
+	    _reserved2			:4,
+	    sdsd_reason			:4);
+	uint8_t sdsd_zone_group;
+	DECL_BITFIELD7(
+	    _reserved3				:1,
+	    sdsd_inside_zpsds			:1,
+	    sdsd_zone_group_persistent		:1,
+	    _reserved4				:1,
+	    sdsd_requested_insize_zpsds		:1,
+	    sdsd_inside_zpsds_persistent	:1,
+	    _restricted5			:2);
+	uint8_t sdsd_attached_phy_identifier;
+	uint8_t sdsd_phy_change_count;
+	uint64_t sdsd_attached_sas_addr;
+	uint8_t _reserved5[4];
+} smp_discover_short_descr_t;
+
+typedef struct smp_discover_long_descr {
+	uint8_t _reserved1[2];
+	uint8_t sdld_function_result;
+	uint8_t _reserved2[1];
+	smp_discover_resp_t sdld_response;
+} smp_discover_long_descr_t;
+
+#define	SMP_DISCOVER_RESP(_ld)	\
+	(((smp_discover_long_descr_t *)(_ld))->sdld_function_result ==	\
+	SMP_FUNCTION_ACCEPTED ?	\
+	&((smp_discover_long_descr_t *)(_ld))->sdld_response :	\
+	NULL)
+
+typedef struct smp_discover_list_resp {
+	uint16_t sdlr_exp_change_count;
+	uint8_t _reserved1[2];
+	uint8_t sdlr_starting_phy_identifier;
+	uint8_t sdlr_n_descrs;
+	DECL_BITFIELD2(
+	    sdlr_phy_filter		:4,
+	    _reserved2			:4);
+	DECL_BITFIELD2(
+	    sdlr_descr_type		:4,
+	    _reserved3			:4);
+	uint8_t sdlr_descr_length;
+	uint8_t _reserved4[3];
+	DECL_BITFIELD5(
+	    sdlr_externally_configurable_route_table	:1,
+	    sdlr_configuring				:1,
+	    _reserved5					:4,
+	    sdlr_zoning_enabled				:1,
+	    sdlr_zoning_supported			:1);
+	uint8_t _reserved6;
+	uint16_t sdlr_last_sc_status_descr_index;
+	uint16_t sdlr_last_phy_event_list_descr_index;
+	uint8_t _reserved7[10];
+	uint8_t sdlr_vendor[16];
+	uint8_t sdlr_descrs[1];	/* short or long format */
+} smp_discover_list_resp_t;
+
+/*
+ * SAS-2 10.4.3.16 REPORT PHY EVENT LIST
+ */
+typedef struct smp_report_phy_event_list_req {
+	uint8_t _reserved1[2];
+	uint16_t srpelr_starting_descr_index;
+} smp_report_phy_event_list_req_t;
+
+typedef struct smp_phy_event_list_descr {
+	uint8_t _reserved1[2];
+	uint8_t speld_phy_identifier;
+	uint8_t speld_phy_event_source;
+	uint32_t speld_phy_event;
+	uint32_t speld_peak_detector_threshold;
+} smp_phy_event_list_descr_t;
+
+typedef struct smp_report_phy_event_list_resp {
+	uint16_t srpelr_exp_change_count;
+	uint16_t srpelr_starting_descr_index;
+	uint16_t srpelr_last_descr_index;
+	uint8_t srpelr_phy_event_list_descr_length;
+	uint8_t _reserved1[3];
+	uint8_t srpelr_n_descrs;
+	smp_phy_event_list_descr_t srpelr_descrs[1];
+} smp_report_phy_event_list_resp_t;
+
+/*
+ * SAS-2 10.4.3.17 REPORT EXPANDER ROUTE TABLE LIST
+ */
+typedef struct smp_report_exp_route_table_list_req {
+	uint8_t _reserved1[4];
+	uint16_t srertlr_max_descrs;
+	uint16_t srertlr_starting_routed_sas_addr_index;
+	uint8_t _reserved2[7];
+	uint8_t srertlr_starting_phy_identifier;
+	uint8_t _reserved3[8];
+} smp_report_exp_route_table_list_req_t;
+
+typedef struct smp_route_table_descr {
+	uint64_t srtd_routed_sas_addr;
+	uint8_t srtd_phy_bitmap[6];
+	DECL_BITFIELD2(
+	    _reserved1			:7,
+	    srtd_zone_group_valid	:1);
+	uint8_t srtd_zone_group;
+} smp_route_table_descr_t;
+
+#define	SMP_ROUTE_PHY(_d, _s, _i)	\
+	((_d)->srtd_phy_bitmap[(48 - (_i) + (_s)) >> 3] & \
+	(1 << ((48 - (_i) + (_s)) & 7)))
+
+typedef struct smp_report_exp_route_table_list_resp {
+	uint16_t srertlr_exp_change_count;
+	uint16_t srertlr_route_table_change_count;
+	DECL_BITFIELD3(
+	    _reserved1		:1,
+	    srertlr_configuring	:1,
+	    _reserved2		:6);
+	uint8_t _reserved3;
+	uint16_t srertlr_n_descrs;
+	uint16_t srertlr_first_routed_sas_addr_index;
+	uint16_t srertlr_last_routed_sas_addr_index;
+	uint8_t _reserved4[3];
+	uint8_t srertlr_starting_phy_identifier;
+	uint8_t _reserved5[12];
+	smp_route_table_descr_t srertlr_descrs[1];
+} smp_report_exp_route_table_list_resp_t;
 
-struct smp_report_general_rsp {
-	uint8_t		frametype;
-	uint8_t		function;
-	uint8_t 	result;
-	uint8_t 	rspsize;
-	uint8_t 	expand_change_count1;
-	uint8_t 	expand_change_count0;
-	uint8_t 	expand_route_index1;
-	uint8_t 	expand_route_index0;
-	uint8_t 	reserved_byte8;
-	uint8_t		num_of_phy;
+/*
+ * SAS-2 10.4.3.18 CONFIGURE GENERAL (no additional response)
+ */
+typedef struct smp_config_general_req {
+	uint16_t scgr_expected_exp_change_count;
+	uint8_t _reserved1[2];
+	DECL_BITFIELD6(
+	    scgr_update_stp_bus_inactivity			:1,
+	    scgr_update_stp_max_conn				:1,
+	    scgr_update_stp_smp_nexus_loss			:1,
+	    scgr_update_initial_time_to_reduced_functionality	:1,
+	    scgr_update_stp_reject_to_open			:1,
+	    _reserved2						:3);
+	uint8_t _reserved3;
+	uint16_t scgr_stp_bus_inactivity;
+	uint16_t scgr_stp_max_conn;
+	uint16_t scgr_stp_smp_nexus_loss;
+	uint8_t scgr_initial_time_to_reduced_functionality;
+	uint8_t _reserved4;
+	uint16_t scgr_stp_reject_to_open;
+} smp_config_general_req_t;
+
+/*
+ * SAS-2 10.4.3.19 ENABLE DISABLE ZONING (no additional response)
+ */
+typedef struct smp_enable_disable_zoning_req {
+	uint16_t sedzr_expected_exp_change_count;
+	DECL_BITFIELD2(
+	    sedzr_save	:2,		/* smp_zoning_save_t */
+	    _reserved1	:6);
+	uint8_t _reserved2;
+	DECL_BITFIELD2(
+	    sedzr_enable_disable_zoning	:2,
+	    _reserved3			:6);
+	uint8_t _reserved4[3];
+} smp_enable_disable_zoning_req_t;
+
+typedef enum smp_zoning_save {
+	SMP_ZONING_SAVE_CURRENT = 0x0,
+	SMP_ZONING_SAVE_SAVED = 0x1,
+	SMP_ZONING_SAVE_BOTH_IF_SUPP = 0x2,
+	SMP_ZONING_SAVE_BOTH = 0x3
+} smp_zoning_save_t;
+
+typedef enum smp_zoning_enable_op {
+	SMP_ZONING_ENABLE_OP_NONE = 0x0,
+	SMP_ZONING_ENABLE_OP_ENABLE = 0x1,
+	SMP_ZONING_ENABLE_OP_DISABLE = 0x2
+} smp_zoning_enable_op_t;
+
+/*
+ * SAS-2 10.4.3.20 ZONED BROADCAST (no additional response)
+ */
+typedef struct smp_zoned_broadcast_req {
+	uint8_t _restricted1[2];
+	DECL_BITFIELD2(
+	    szbr_broadcast_type	:4,
+	    _reserved		:4);
+	uint8_t szbr_n_broadcast_source_zone_groups;
+	uint8_t szbr_broadcast_source_zone_groups[1];
+} smp_zoned_broadcast_req_t;
+
+/*
+ * SAS-2 10.4.3.21 ZONE LOCK
+ */
+typedef struct smp_zone_lock_req {
+	uint16_t szlr_expected_exp_change_count;
+	uint16_t szlr_zone_lock_inactivity_timeout;
+	uint8_t szlr_zone_manager_password[32];
+} smp_zone_lock_req_t;
+
+typedef struct smp_zone_lock_resp {
+	uint8_t _reserved1[4];
+	uint64_t szlr_active_zone_manager_sas_addr;
+} smp_zone_lock_resp_t;
+
+/*
+ * SAS-2 10.4.3.22 ZONE ACTIVATE (no additional response)
+ */
+typedef struct smp_zone_activate_req {
+	uint16_t szar_expected_exp_change_count;
+	uint8_t _reserved1[2];
+} smp_zone_activate_req_t;
+
+/*
+ * SAS-2 10.4.3.23 ZONE UNLOCK (no additional response)
+ */
+typedef struct smp_zone_unlock_req {
+	uint8_t _restricted1[2];
+	DECL_BITFIELD2(
+	    szur_activate_required	:1,
+	    _reserved1			:7);
+	uint8_t _reserved2;
+} smp_zone_unlock_req_t;
+
+/*
+ * SAS-2 10.4.3.24 CONFIGURE ZONE MANAGER PASSWORD (no additional response)
+ */
+typedef struct smp_config_zone_manager_password_req {
+	uint16_t sczmpr_expected_exp_change_count;
+	DECL_BITFIELD2(
+	    sczmpr_save		:2,		/* smp_zoning_save_t */
+	    _reserved1		:6);
+	uint8_t _reserved2;
+	uint8_t sczmpr_zone_manager_password[32];
+	uint8_t sczmpr_new_zone_manager_password[32];
+} smp_config_zone_manager_password_req_t;
+
+/*
+ * SAS-2 10.4.3.25 CONFIGURE ZONE PHY INFORMATION (no additional response)
+ */
+typedef struct smp_zone_phy_config_descr {
+	uint8_t szpcd_phy_identifier;
+	DECL_BITFIELD6(
+	    _reserved1				:2,
+	    szpcd_zone_group_persistent		:1,
+	    _reserved2				:1,
+	    szpcd_requested_inside_zpsds	:1,
+	    szpcd_inside_zpsds_persistent	:1,
+	    _reserved3				:2);
+	uint8_t _reserved4;
+	uint8_t szpcd_zone_group;
+} smp_zone_phy_config_descr_t;
+
+typedef struct smp_config_zone_phy_info_req {
+	uint16_t sczpir_expected_exp_change_count;
+	DECL_BITFIELD2(
+	    sczpir_save		:2,		/* smp_zoning_save_t */
+	    _reserved1		:6);
+	uint8_t sczpir_n_descrs;
+	smp_zone_phy_config_descr_t sczpir_descrs[1];
+} smp_config_zone_phy_info_req_t;
+
+/*
+ * SAS-2 10.4.3.26 CONFIGURE ZONE PERMISSION TABLE (no additional response)
+ */
+typedef struct smp_config_zone_perm_table_req {
+	uint16_t sczptr_expected_exp_change_count;
+	uint8_t sczptr_starting_source_zone_group;
+	uint8_t sczptr_n_descrs;
 	DECL_BITFIELD3(
-	    crt			:1,
-	    configuring		:1,
-	    reserved_byte10	:6);
-	uint8_t		reserved_byte11;
-	uint64_t	identifier;
-	uint8_t		reserved_byte20[8];
-	struct smp_crc	crc;
-};
+	    sczptr_save			:2,	/* smp_zoning_save_t */
+	    _reserved1			:4,
+	    sczptr_n_zone_groups	:2);	/* smp_n_zone_grps_t */
+	uint8_t _reserved2[7];
+	uint8_t sczptr_descrs[1];	/* smp_zone_perm_descrXXX_t */
+} smp_config_zone_perm_table_req_t;
+
+/*
+ * SAS-2 10.4.3.27 CONFIGURE ROUTE INFORMATION (no additional response)
+ */
+typedef struct smp_config_route_info_req {
+	uint16_t scrir_expected_exp_change_count;
+	uint16_t scrir_exp_route_index;
+	uint8_t _reserved1;
+	uint8_t scrir_phy_identifier;
+	uint8_t _reserved2[2];
+	DECL_BITFIELD2(
+	    _reserved3				:7,
+	    scrir_disable_exp_route_entry	:1);
+	uint8_t _reserved4[3];
+	uint64_t scrir_routed_sas_addr;
+	uint8_t _reserved5[16];
+} smp_config_route_info_req_t;
+
+/*
+ * SAS-2 10.4.3.28 PHY CONTROL (no additional response)
+ */
+typedef struct smp_phy_control_req {
+	uint16_t spcr_expected_exp_change_count;
+	uint8_t _reserved1[3];
+	uint8_t spcr_phy_identifier;
+	uint8_t spcr_phy_operation;
+	DECL_BITFIELD2(
+	    spcr_update_partial_pwy_timeout	:1,
+	    _reserved2				:7);
+	uint8_t _reserved3[12];
+	uint64_t spcr_attached_device_name;
+	DECL_BITFIELD2(
+	    _reserved4				:4,
+	    spcr_prog_min_phys_link_rate	:4);	/* smp_link_rate_t */
+	DECL_BITFIELD2(
+	    _reserved5				:4,
+	    spcr_prog_max_phys_link_rate	:4);	/* smp_link_rate_t */
+	uint8_t _reserved6[2];
+	DECL_BITFIELD2(
+	    spcr_partial_pwy_timeout	:4,
+	    _reserved7			:4);
+	uint8_t _reserved8[3];
+} smp_phy_control_req_t;
+
+typedef enum smp_phy_op {
+	SMP_PHY_OP_NOP = 0x00,
+	SMP_PHY_OP_LINK_RESET = 0x01,
+	SMP_PHY_OP_HARD_RESET = 0x02,
+	SMP_PHY_OP_DISABLE = 0x03,
+	SMP_PHY_OP_CLEAR_ERROR_LOG = 0x05,
+	SMP_PHY_OP_CLEAR_AFFILIATION = 0x06,
+	SMP_PHY_OP_TRANSMIT_SATA_PORT_SELECTION_SIGNAL = 0x07,
+	SMP_PHY_OP_CLEAR_STP_NEXUS_LOSS = 0x08,
+	SMP_PHY_OP_SET_ATTACHED_DEVICE_NAME = 0x09
+} smp_phy_op_t;
+
+/*
+ * SAS-2 10.4.3.29 PHY TEST FUNCTION (no additional response)
+ */
+typedef struct smp_phy_test_function_req {
+	uint16_t sptfr_expected_exp_change_count;
+	uint8_t _reserved1[3];
+	uint8_t sptfr_phy_identifier;
+	uint8_t sptfr_phy_test_function;
+	uint8_t sptfr_phy_test_pattern;		/* smp_phy_test_function_t */
+	uint8_t _reserved2[3];
+	DECL_BITFIELD4(
+	    sptfr_test_pattern_phys_link_rate	:4,	/* smp_link_rate_t */
+	    sptfr_test_pattern_ssc		:2,
+	    sptfr_test_pattern_sata		:1,
+	    _reserved3				:1);
+	uint8_t _reserved4[3];
+	uint8_t sptfr_phy_test_pattern_dwords_ctl;
+	uint8_t sptfr_phy_test_pattern_dwords[8];
+	uint8_t _reserved5[12];
+} smp_phy_test_function_req_t;
+
+typedef enum smp_phy_test_function {
+	SMP_PHY_TEST_FN_STOP = 0x00,
+	SMP_PHY_TEST_FN_TRANSMIT_PATTERN = 0x01
+} smp_phy_test_function_t;
+
+/*
+ * SAS-2 10.4.3.30 CONFIGURE PHY EVENT (no additional response)
+ */
+typedef struct smp_phy_event_config_descr {
+	uint8_t _reserved1[3];
+	uint8_t specd_phy_event_source;
+	uint32_t specd_peak_value_detector_threshold;
+} smp_phy_event_config_descr_t;
+
+typedef struct smp_config_phy_event_req {
+	uint16_t scper_expected_exp_change_count;
+	DECL_BITFIELD2(
+	    scper_clear_peaks	:1,
+	    _reserved1		:7);
+	uint8_t _reserved2[2];
+	uint8_t scper_phy_identifier;
+	uint8_t _reserved3;
+	uint8_t scper_n_descrs;
+	smp_phy_event_config_descr_t scper_descrs[1];
+} smp_config_phy_event_req_t;
+
 #pragma	pack()
 
 #ifdef	__cplusplus
 }
 #endif
 
-#endif	/* _SYS_SCSI_IMPL_SMP_FRAME_H */
+#endif	/* _SYS_SCSI_GENERIC_SMP_FRAMES_H */
--- a/usr/src/uts/common/sys/scsi/impl/sas_transport.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/scsi/impl/sas_transport.h	Mon Oct 05 09:17:26 2009 -0400
@@ -67,7 +67,8 @@
 	size_t		pkt_reqsize;
 	size_t		pkt_rspsize;
 	int		pkt_timeout;
-	uchar_t		pkt_reason;	/* code from errno.h */
+	uchar_t		pkt_reason;		/* code from errno.h */
+	uchar_t		pkt_will_retry;		/* will retry on EAGAIN */
 	sas_addr_t	*pkt_address;
 } smp_pkt_t;
 
@@ -87,22 +88,234 @@
 	int		(*tran_smp_start)(
 				struct smp_pkt		*pkt);
 
+	int		(*tran_smp_init)(
+				dev_info_t		*self,
+				dev_info_t		*child,
+				sas_hba_tran_t		*tran,
+				smp_device_t		*smp);
+
+	void		(*tran_smp_free)(
+				dev_info_t		*self,
+				dev_info_t		*child,
+				sas_hba_tran_t		*tran,
+				smp_device_t		*smp);
 };
 
-extern sas_hba_tran_t *sas_hba_tran_alloc(dev_info_t *dip, int flags);
+extern sas_hba_tran_t *sas_hba_tran_alloc(dev_info_t *dip);
 extern int	sas_hba_attach_setup(dev_info_t *dip, sas_hba_tran_t *smp);
 extern int	sas_hba_detach(dev_info_t *self);
 extern void	sas_hba_tran_free(sas_hba_tran_t *smp);
 
+extern int	smp_hba_bus_config(dev_info_t *, char *, dev_info_t **);
+extern int	smp_hba_bus_config_taddr(dev_info_t *, char *);
+
+
 extern int	sas_smp_transport(struct smp_pkt *pkt);
 extern int	sas_ifgetcap(struct sas_addr *ap, char *cap);
 
 extern int	sas_hba_probe_smp(struct smp_device *smp_devp);
 extern int	sas_hba_lookup_capstr(char *capstr);
 
+/*
+ * Phymap support
+ */
+typedef struct __sas_phymap    sas_phymap_t;
+typedef enum { PHYMAP_MODE_SIMPLE } sas_phymap_mode_t;
+typedef void (*sas_phymap_activate_cb_t)
+		(void *phymap_priv, char *ua, void **ua_privp);
+typedef void (*sas_phymap_deactivate_cb_t)
+		(void *phymap_priv, char *ua, void *ua_priv);
+
+extern int	sas_phymap_create(dev_info_t		*hba_dip,
+				clock_t			settle_us,
+				sas_phymap_mode_t	mode,
+				void			*mode_argument,
+				void			*phymap_priv,
+				sas_phymap_activate_cb_t activate_cb,
+				sas_phymap_deactivate_cb_t deactivate_cb,
+				sas_phymap_t		**phymapp);
+
+void		sas_phymap_destroy(sas_phymap_t		*phymap);
+
+extern int	sas_phymap_phy_add(sas_phymap_t		*phymap,
+				int			phy,
+				uint64_t		local_sas_address,
+				uint64_t		remote_sas_address);
+
+extern int	sas_phymap_phy_rem(sas_phymap_t		*phymap,
+				int			phy);
+
+extern char	*sas_phymap_lookup_ua(sas_phymap_t	*phymap,
+				uint64_t		local_sas_address,
+				uint64_t		remote_sas_address);
+
+extern void	*sas_phymap_lookup_uapriv(sas_phymap_t	*phymap,
+				char			*ua);
+
+extern int	sas_phymap_uahasphys(sas_phymap_t	*phymap,
+				char			*ua);
+
+typedef struct __sas_phymap_phys	sas_phymap_phys_t;
+extern sas_phymap_phys_t *sas_phymap_ua2phys(sas_phymap_t *, char *ua);
+extern int	sas_phymap_phys_next(sas_phymap_phys_t *);
+void		sas_phymap_phys_free(sas_phymap_phys_t *);
+
+extern char	*sas_phymap_phy2ua(sas_phymap_t	*, int);
+void		sas_phymap_ua_free(char	*);
+
+
 #endif /* defined(_KERNEL) */
 
 
+#define	KSTAT_SAS_PHY_CLASS	"SAS_phy_stat"
+
+/*
+ * Format of the ks_name field for SAS Phy Stat
+ *
+ * driver_name.initiator_port_SAS_address.initiator_port_instance_number.phyid
+ * Example: pmcs.5000c50000d756aa.2.0
+ *
+ * driver_name:
+ *     driver name from di_driver_name() on SAS initiator port devinfo node.
+ * initiator_port_SAS_address:
+ *     SAS address of the initiator port that phy stat is reported for.
+ * initiator_port_instance_number:
+ *     instance number of the initiator port that phy stat is reported for.
+ * phyid:
+ *     prop phyIdentifier under initiator port node.
+ */
+
+/* Port Protocol - kstat structure definition */
+
+typedef struct sas_port_protocol_stats {
+	kstat_named_t	seconds_since_last_reset;
+	kstat_named_t	input_requests;
+	kstat_named_t	output_requests;
+	kstat_named_t	control_requests;
+	kstat_named_t	input_megabytes;
+	kstat_named_t	output_megabytes;
+} sas_port_protocol_stats_t;
+
+/* Port - kstat structure definition */
+
+typedef struct sas_port_stats {
+	kstat_named_t	seconds_since_last_reset;
+	kstat_named_t	tx_frames;
+	kstat_named_t	tx_words;
+	kstat_named_t	rx_frames;
+	kstat_named_t	rx_words;
+} sas_port_stats_t;
+
+/* PHY - kstat structure definition */
+
+typedef struct sas_phy_stats {
+	kstat_named_t	seconds_since_last_reset;
+	kstat_named_t	tx_frames;
+	kstat_named_t	tx_words;
+	kstat_named_t	rx_frames;
+	kstat_named_t	rx_words;
+	kstat_named_t	invalid_dword_count;
+	kstat_named_t	running_disparity_error_count;
+	kstat_named_t	loss_of_dword_sync_count;
+	kstat_named_t	phy_reset_problem_count;
+} sas_phy_stats_t;
+
+
+/*
+ * Supported Protocol property
+ */
+#define	SAS_PROTOCOL_SSP	0x00000001
+#define	SAS_PROTOCOL_STP	0x00000010
+#define	SAS_PROTOCOL_SMP	0x00000100
+#define	SAS_PROTOCOL_SATA	0x00001000
+
+
+/*
+ * Definition - Negotiated Physical Link Rate
+ * Based on Table 288 (Section 10.4.3.10) of the spec (SAS-2 r-15), these
+ * constants represent "Negotiated physical link rate"
+ * (and implicitly the State of the phy).
+ */
+
+#define	SAS_LINK_RATE_UNKNOWN		0x0 /* Phy is enabled. */
+					    /* Speed is unknown */
+#define	SAS_LINK_RATE_DISABLED		0x1 /* Phy is disabled. */
+					    /* Speed is undefined */
+#define	SAS_LINK_RATE_FAILED		0x2 /* Phy is enabled. */
+					    /* Failed speed negotiation. */
+#define	SAS_LINK_RATE_SATASPINUP	0x3 /* Phy is enabled. */
+					    /* Detected a SATA device and */
+					    /* entered the SATA Spinup hold */
+					    /* state */
+#define	SAS_LINK_RATE_SATAPORTSEL	0x4 /* Phy enabled. */
+					    /* The phy is attached to a */
+					    /* Port Selector (SATA-2.6). */
+#define	SAS_LINK_RATE_RESET_IN_PROGRESS	0x5 /* Phy is enabled. */
+					    /* Expander is performing SMP */
+					    /* PHY CONTROL Link/Hard Reset */
+#define	SAS_LINK_RATE_PHY_UNSUPPORTED	0x6 /* Phy is enabled. */
+					    /* Unsupported phy settings */
+#define	SAS_LINK_RATE_RESERVED		0x7 /* Undefined. Reserved. */
+#define	SAS_LINK_RATE_1_5GBIT		0x8 /* Phy enabled at 1.5 GBit/sec */
+#define	SAS_LINK_RATE_3GBIT		0x9 /* Phy enabled at 3 GBit/sec */
+#define	SAS_LINK_RATE_6GBIT		0xA /* Phy enabled at 6 GBit/sec. */
+
+
+/*
+ * Definition - "phy-info" property
+ *
+ * The property is an nvlist_array that represents an array of the
+ * nvlists on a per HBA basis. The individual elements of the array
+ * (the nvlists) represent the following properties for each phy of the HBA
+ */
+#define	SAS_PHY_INFO		"phy-info"		/* Phy property name */
+#define	SAS_PHY_INFO_NVL	"phy-info-nvl"		/* NVL array name */
+
+#define	SAS_PHY_ID		"PhyIdentifier"		/* DATA_TYPE_UINT8 */
+#define	SAS_NEG_LINK_RATE	"NegotiatedLinkRate"	/* DATA_TYPE_INT8 */
+#define	SAS_PROG_MIN_LINK_RATE	"ProgrammedMinLinkRate"	/* DATA_TYPE_INT8 */
+#define	SAS_HW_MIN_LINK_RATE	"HardwareMinLinkRate"	/* DATA_TYPE_INT8 */
+#define	SAS_PROG_MAX_LINK_RATE	"ProgrammedMaxLinkRate"	/* DATA_TYPE_INT8 */
+#define	SAS_HW_MAX_LINK_RATE	"HardwareMaxLinkRate"	/* DATA_TYPE_INT8 */
+
+
+/*
+ * Event definitions
+ */
+/* Event Class */
+#define	EC_HBA				"EC_hba"
+
+/* Event Sub-Class */
+#define	ESC_SAS_HBA_PORT_BROADCAST	"ESC_sas_hba_port_broadcast"
+
+/* Event Types for above Subclass */
+#define	SAS_PORT_BROADCAST_CHANGE	"port_broadcast_change"
+#define	SAS_PORT_BROADCAST_SES		"port_broadcast_ses"
+#define	SAS_PORT_BROADCAST_D24_0	"port_broadcast_d24_0"
+#define	SAS_PORT_BROADCAST_D27_4	"port_broadcast_d27_4"
+#define	SAS_PORT_BROADCAST_D01_4	"port_broadcast_d01_4"
+#define	SAS_PORT_BROADCAST_D04_7	"port_broadcast_d04_7"
+#define	SAS_PORT_BROADCAST_D16_7	"port_broadcast_d16_7"
+#define	SAS_PORT_BROADCAST_D29_7	"port_broadcast_d29_7"
+
+/* Event Sub-Class */
+#define	ESC_SAS_PHY_EVENT		"ESC_sas_phy_event"
+
+/* Event Types for above Subclass */
+#define	SAS_PHY_ONLINE			"port_online"
+#define	SAS_PHY_OFFLINE			"port_offline"
+#define	SAS_PHY_REMOVE			"port_remove"
+
+/* Event Payload Names */
+#define	SAS_DRV_INST			"driver_instance"
+#define	SAS_PORT_ADDR			"port_address"
+#define	SAS_DEVFS_PATH			"devfs_path"
+#define	SAS_EVENT_TYPE			"event_type"
+#define	SAS_LINK_RATE			"link_rate"
+/* SAS_PHY_ID - Defined Above */
+
+
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/uts/common/sys/scsi/impl/transport.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/scsi/impl/transport.h	Mon Oct 05 09:17:26 2009 -0400
@@ -39,6 +39,12 @@
 #ifdef	_KERNEL
 
 /*
+ * Opaque  handles to address maps
+ */
+typedef struct __scsi_iportmap	scsi_hba_iportmap_t;
+typedef struct __scsi_tgtmap	scsi_hba_tgtmap_t;
+
+/*
  * SCSI transport structures
  *
  *	As each Host Adapter makes itself known to the system,
@@ -54,7 +60,9 @@
 
 struct scsi_hba_tran {
 	/*
-	 * Ptr to the device info structure for this particular HBA.
+	 * Ptr to the device info structure for this particular HBA. If a SCSA
+	 * HBA driver separates initiator port function from HBA function,
+	 * this field still refers to the HBA and is used to manage DMA.
 	 */
 	dev_info_t	*tran_hba_dip;
 
@@ -64,8 +72,8 @@
 	void		*tran_hba_private;	/* HBA softstate */
 
 	/*
-	 * The following two fields are only used in the SCSI_HBA_TRAN_CLONE
-	 * case. Consider using SCSI_HBA_ADDR_COMPLEX instead.
+	 * The following two fields are only used in the deprecated
+	 * SCSI_HBA_TRAN_CLONE case. Use SCSI_HBA_ADDR_COMPLEX instead.
 	 */
 	void			*tran_tgt_private;
 	struct scsi_device	*tran_sd;
@@ -215,8 +223,7 @@
 	 * open_flag: bit field indicating which minor nodes are open.
 	 *	0 = closed, 1 = shared open, all bits 1 = excl open.
 	 *
-	 * XXX Unused if hba driver chooses to implement own
-	 *	xxopen(9e) entry point
+	 * NOTE: Unused if HBA driver implements its own open(9e) entry point.
 	 */
 	kmutex_t		tran_open_lock;
 	uint64_t		tran_open_flag;
@@ -246,7 +253,7 @@
 				void			*result);
 
 	/*
-	 * Inter-Connect type of trasnport as defined in
+	 * Inter-Connect type of transport as defined in
 	 * usr/src/uts/common/sys/scsi/impl/services.h
 	 */
 	int		tran_interconnect_type;
@@ -286,6 +293,16 @@
 	 */
 	dev_info_t	*tran_iport_dip;
 
+	/*
+	 * map of initiator ports below HBA
+	 */
+	scsi_hba_iportmap_t	*tran_iportmap;
+
+	/*
+	 * map of targets below initiator
+	 */
+	scsi_hba_tgtmap_t	*tran_tgtmap;
+
 #ifdef	SCSI_SIZE_CLEAN_VERIFY
 	/*
 	 * Must be last: Building a driver with-and-without
@@ -306,7 +323,7 @@
 	scsi_hba_tran::tran_open_flag
 	scsi_hba_tran::tran_pkt_cache_ptr))
 /*
- * we only modify the dma atributes (like dma_attr_granular) upon
+ * we only modify the dma attributes (like dma_attr_granular) upon
  * attach and in response to a setcap. It is also up to the target
  * driver to not have any outstanding I/Os when it is changing the
  * capabilities of the transport.
@@ -373,7 +390,22 @@
 				struct scsi_device	*sd,
 				int			(*callback)(void));
 
-char			*scsi_get_device_type_string(
+int		scsi_hba_probe_pi(
+				struct scsi_device	*sd,
+				int			(*callback)(void),
+				int			pi);
+
+int		scsi_hba_ua_get_reportdev(
+				struct scsi_device	*sd,
+				char			*ba,
+				int			len);
+
+int		scsi_hba_ua_get(
+				struct scsi_device	*sd,
+				char			*ua,
+				int			len);
+
+char		*scsi_get_device_type_string(
 				char			*prop_name,
 				dev_info_t		*hba_dip,
 				struct scsi_device	*sd);
@@ -443,8 +475,7 @@
 				char			*nodename,
 				char			**compatible);
 
-
-int		scsi_hba_prop_update_inqstring(
+int		scsi_device_prop_update_inqstring(
 				struct scsi_device	*sd,
 				char			*name,
 				char			*data,
@@ -453,8 +484,12 @@
 void		scsi_hba_pkt_comp(
 				struct scsi_pkt		*pkt);
 
+int		scsi_device_identity(
+				struct scsi_device	*sd,
+				int			(*callback)(void));
+
 char		*scsi_hba_iport_unit_address(
-				dev_info_t		*self);
+				dev_info_t		*dip);
 
 int		scsi_hba_iport_register(
 				dev_info_t		*dip,
@@ -466,6 +501,8 @@
 dev_info_t	*scsi_hba_iport_find(
 				dev_info_t		*pdip,
 				char			*portnm);
+
+
 /*
  * Flags for scsi_hba_attach
  *
@@ -496,8 +533,8 @@
  *				same driver. The driver can distinguish context
  *				by calling scsi_hba_iport_unit_address().
  *
- * SCSI_HBA_TRAN_CLONE		Consider using SCSI_HBA_ADDR_COMPLEX instead.
- *				SCSI_HBA_TRAN_CLONE is a KLUDGE to address
+ * ::SCSI_HBA_TRAN_CLONE	Deprecated: use SCSI_HBA_ADDR_COMPLEX instead.
+ *				SCSI_HBA_TRAN_CLONE was a KLUDGE to address
  *				limitations of the scsi_address(9S) structure
  *				via duplication of scsi_hba_tran(9S) and
  *				use of tran_tgt_private.
@@ -507,10 +544,10 @@
 #define	SCSI_HBA_TRAN_PHCI	0x02	/* treat HBA as mpxio 'pHCI' */
 #define	SCSI_HBA_TRAN_CDB	0x04	/* allocate cdb */
 #define	SCSI_HBA_TRAN_SCB	0x08	/* allocate sense */
+#define	SCSI_HBA_HBA		0x10	/* all HBA children are iports */
 
 #define	SCSI_HBA_ADDR_SPI	0x20	/* scsi_address in SPI form */
 #define	SCSI_HBA_ADDR_COMPLEX	0x40	/* scsi_address is COMPLEX */
-#define	SCSI_HBA_HBA		0x80	/* all HBA children are iport */
 
 /* upper bits used to record SCSA configuration state */
 #define	SCSI_HBA_SCSA_PHCI	0x10000	/* need mdi_phci_unregister */
@@ -526,8 +563,8 @@
  * Support extra flavors for SCSA children
  */
 #define	SCSA_FLAVOR_SCSI_DEVICE	NDI_FLAVOR_VANILLA
-#define	SCSA_FLAVOR_IPORT	1
-#define	SCSA_FLAVOR_SMP		2
+#define	SCSA_FLAVOR_SMP		1
+#define	SCSA_FLAVOR_IPORT	2
 #define	SCSA_NFLAVORS		3
 
 /*
@@ -536,6 +573,80 @@
 #define	SCSI_HBA_MAX_IPORTS	32
 
 /*
+ * SCSI iport map interfaces
+ */
+int	scsi_hba_iportmap_create(
+				dev_info_t		*hba_dip,
+				clock_t			stable_ms,
+				int			n_entries,
+				scsi_hba_iportmap_t	**iportmapp);
+
+int	scsi_hba_iportmap_iport_add(
+				scsi_hba_iportmap_t	*iportmap,
+				char			*iport_addr,
+				void			*iport_priv);
+
+int	scsi_hba_iportmap_iport_remove(
+				scsi_hba_iportmap_t	*iportmap,
+				char			*iport_addr);
+
+void	scsi_hba_iportmap_destroy(scsi_hba_iportmap_t	*iportmap);
+
+/*
+ * SCSI target map interfaces
+ */
+typedef enum { SCSI_TM_FULLSET = 0, SCSI_TM_PERADDR }	scsi_tgtmap_mode_t;
+typedef enum {
+    SCSI_TGT_SCSI_DEVICE = 0, SCSI_TGT_SMP_DEVICE, SCSI_TGT_NTYPES
+}	scsi_tgtmap_tgt_type_t;
+
+typedef void	(*scsi_tgt_activate_cb_t)(
+				void			*tgtmap_priv,
+				char			*tgt_addr,
+				scsi_tgtmap_tgt_type_t	tgt_type,
+				void			**tgt_privp);
+typedef void	(*scsi_tgt_deactivate_cb_t)(
+				void			*tgtmap_priv,
+				char			*tgt_addr,
+				scsi_tgtmap_tgt_type_t	tgt_type,
+				void			*tgt_priv);
+int	scsi_hba_tgtmap_create(
+				dev_info_t		*iport_dip,
+				scsi_tgtmap_mode_t	rpt_mode,
+				clock_t			stable_ms,
+				int			n_entries,
+				void			*tgtmap_priv,
+				scsi_tgt_activate_cb_t	activate_cb,
+				scsi_tgt_deactivate_cb_t deactivate_cb,
+				scsi_hba_tgtmap_t	**tgtmapp);
+
+int	scsi_hba_tgtmap_set_begin(scsi_hba_tgtmap_t	*tgtmap);
+
+int	scsi_hba_tgtmap_set_add(
+				scsi_hba_tgtmap_t	*tgtmap,
+				scsi_tgtmap_tgt_type_t	tgt_type,
+				char			*tgt_addr,
+				void			*tgt_priv);
+
+int	scsi_hba_tgtmap_set_end(
+				scsi_hba_tgtmap_t	*tgtmap,
+				uint_t			flags);
+
+int	scsi_hba_tgtmap_tgt_add(
+				scsi_hba_tgtmap_t	*tgtmap,
+				scsi_tgtmap_tgt_type_t	tgt_type,
+				char			*tgt_addr,
+				void			*tgt_priv);
+
+int	scsi_hba_tgtmap_tgt_remove(
+				scsi_hba_tgtmap_t	*tgtmap,
+				scsi_tgtmap_tgt_type_t	tgt_type,
+				char			*tgt_addr);
+
+void	scsi_hba_tgtmap_destroy(scsi_hba_tgtmap_t	*tgt_map);
+
+
+/*
  * For minor nodes created by the SCSA framework, minor numbers are
  * formed by left-shifting instance by INST_MINOR_SHIFT and OR in a
  * number less than 64.
--- a/usr/src/uts/common/sys/scsi/scsi_address.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/scsi/scsi_address.h	Mon Oct 05 09:17:26 2009 -0400
@@ -114,6 +114,10 @@
 #define	SCSI_ADDR_PROP_LUN64		"lun64"		/* int64 */
 #define	SCSI_ADDR_PROP_SFUNC		"sfunc"		/* int */
 
+#define	SCSI_ADDR_PROP_IPORTUA		"scsi-iport"	/* string */
+
+#define	SCSI_ADDR_PROP_SATA_PHY		"sata-phy"	/* int */
+
 /*
  * Addressing property names, values are in string form compatible
  * with the SCSI_ADDR_PROP_TARGET_PORT part of the related
@@ -165,6 +169,7 @@
 char		*scsi_wwn_to_wwnstr(uint64_t wwn,
 		    int unit_address_form, char *wwnstr);
 void		scsi_wwnstr_hexcase(char *wwnstr, int lower_case);
+const char	*scsi_wwnstr_skip_ua_prefix(const char *wwnstr);
 void		scsi_free_wwnstr(char *wwnstr);
 #endif	/* _KERNEL */
 
--- a/usr/src/uts/common/sys/scsi/scsi_ctl.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/scsi/scsi_ctl.h	Mon Oct 05 09:17:26 2009 -0400
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_SYS_SCSI_SCSI_CTL_H
 #define	_SYS_SCSI_SCSI_CTL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/scsi/scsi_types.h>
 
 #ifdef	__cplusplus
@@ -56,12 +53,6 @@
 #define	SCSI_RESET_CANCEL	0x02	/* cancel the reset notification */
 
 /*
- * Define for scsi_get_bus_addr/scsi_get_name first argument.
- */
-#define	SCSI_GET_INITIATOR_ID	((struct scsi_device *)NULL)
-					/* return initiator-id */
-
-/*
  * Define for scsi_get_name string length.
  * This is needed because MAXNAMELEN is not part of DDI.
  */
@@ -98,9 +89,8 @@
  * Other functions
  */
 int	scsi_clear_aca(struct scsi_address *ap);
-int	scsi_get_bus_addr(struct scsi_device *devp, char *name, int len);
-int	scsi_get_name(struct scsi_device *devp, char *name, int len);
-
+int	scsi_ua_get_reportdev(struct scsi_device *sd, char *ba, int len);
+int	scsi_ua_get(struct scsi_device *sd, char *ua, int len);
 #endif	/* _KERNEL */
 
 #ifdef	__cplusplus
--- a/usr/src/uts/common/sys/scsi/scsi_pkt.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/scsi/scsi_pkt.h	Mon Oct 05 09:17:26 2009 -0400
@@ -19,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_SYS_SCSI_SCSI_PKT_H
 #define	_SYS_SCSI_SCSI_PKT_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/scsi/scsi_types.h>
 
 #ifdef	__cplusplus
@@ -106,6 +104,9 @@
 	/* private: iff scsi_pkt_allocated_correctly() */
 	int	pkt_path_instance;	/* pHCI transport path */
 
+	/* stage-temporary: iff scsi_pkt_allocated_correctly() */
+	void	*pkt_stmp;		/* temporary for current pkt stage */
+
 #ifdef	SCSI_SIZE_CLEAN_VERIFY
 	/*
 	 * Must be last: Building a driver with-and-without
--- a/usr/src/uts/common/sys/scsi/scsi_types.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/scsi/scsi_types.h	Mon Oct 05 09:17:26 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -57,6 +57,11 @@
 #include <sys/cmn_err.h>
 #include <sys/debug.h>
 #include <sys/devops.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/stat.h>
+#include <sys/sunndi.h>
+#include <sys/devctl.h>
 #endif	/* _KERNEL */
 
 /*
@@ -87,22 +92,9 @@
 #include <sys/scsi/generic/message.h>
 #include <sys/scsi/generic/mode.h>
 
-
 /*
  * Sun SCSI type definitions
  */
 #include <sys/scsi/impl/types.h>
 
-/*
- * For drivers which do not include these - must be last
- */
-#ifdef	_KERNEL
-#include <sys/ddi.h>
-#include <sys/sunddi.h>
-#include <sys/stat.h>
-#include <sys/sunndi.h>
-#include <sys/devctl.h>
-#include <sys/ddifm.h>
-#endif	/* _KERNEL */
-
 #endif	/* _SYS_SCSI_SCSI_TYPES_H */
--- a/usr/src/uts/common/sys/scsi/targets/smp.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/scsi/targets/smp.h	Mon Oct 05 09:17:26 2009 -0400
@@ -20,15 +20,13 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _SYS_SCSI_TARGETS_SMP_H
 #define	_SYS_SCSI_TARGETS_SMP_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <sys/scsi/scsi.h>
 
@@ -47,17 +45,19 @@
 #define	SMP_EXOPENED	2
 
 typedef struct smp_state {
-	struct smp_device *smp_dev;		/* pointer to smp_device */
-	kmutex_t smp_mutex;			/* mutex */
-	uint32_t smp_open_flag;		/* open flag */
+	struct smp_device	*smp_dev;	/* pointer to smp_device */
+	kmutex_t		smp_mutex;	/* mutex */
+	uint32_t		smp_open_flag;	/* open flag */
+	kcondvar_t		smp_cv;		/* condition variable */
+	uint32_t		smp_busy;	/* busy */
 } smp_state_t;
 
 #define	SMP_ESTIMATED_NUM_DEVS	4		/* for soft-state allocation */
-#define	SMP_DEFAULT_RETRY_TIMES	3
+#define	SMP_DEFAULT_RETRY_TIMES	5
 
 #define	SMP_FLAG_REQBUF		0x1
 #define	SMP_FLAG_RSPBUF		0x2
-#define	SMP_FLAG_XFER			0x4
+#define	SMP_FLAG_XFER		0x4
 
 #endif /* defined(_KERNEL) */
 
--- a/usr/src/uts/common/sys/stmf_ioctl.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/stmf_ioctl.h	Mon Oct 05 09:17:26 2009 -0400
@@ -66,6 +66,7 @@
 #define	STMF_IOCTL_REG_LU_LIST			(STMF_IOCTL | 32)
 #define	STMF_IOCTL_VE_LU_LIST			(STMF_IOCTL | 33)
 #define	STMF_IOCTL_LU_VE_LIST			(STMF_IOCTL | 34)
+#define	STMF_IOCTL_VALIDATE_VIEW		(STMF_IOCTL | 35)
 
 typedef	struct stmf_iocdata {
 	uint32_t	stmf_version;
--- a/usr/src/uts/common/sys/sunddi.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/sunddi.h	Mon Oct 05 09:17:26 2009 -0400
@@ -116,6 +116,8 @@
 #define	DEVI_PSEUDO_NODEID	((int)-1)
 #define	DEVI_SID_NODEID		((int)-2)
 #define	DEVI_SID_HIDDEN_NODEID	((int)-3)
+#define	DEVI_SID_HP_NODEID	((int)-4)
+#define	DEVI_SID_HP_HIDDEN_NODEID ((int)-5)
 
 #define	DEVI_PSEUDO_NEXNAME	"pseudo"
 #define	DEVI_ISA_NEXNAME	"isa"
@@ -236,7 +238,6 @@
 						/* Fabric Devices */
 #define	DDI_NT_IB_ATTACHMENT_POINT	"ddi_ctl:attachment_point:ib"
 						/* IB devices */
-#define	DDI_NT_SMARTCARD_READER	"ddi_smartcard_reader" /* Smartcard reader */
 
 #define	DDI_NT_AV_ASYNC "ddi_av:async"		/* asynchronous AV device */
 #define	DDI_NT_AV_ISOCH "ddi_av:isoch"		/* isochronous AV device */
@@ -1556,43 +1557,84 @@
 ddi_fls(long mask);
 
 /*
- * The next five routines comprise generic storage management utilities
- * for driver soft state structures.
+ * The ddi_soft_state* routines comprise generic storage management utilities
+ * for driver soft state structures.  Two types of soft_state indexes are
+ * supported: 'integer index', and 'string index'.
  */
+typedef	struct __ddi_soft_state_bystr	ddi_soft_state_bystr;
 
 /*
- * Allocate a set of pointers to 'n_items' objects of size 'size'
- * bytes.  Each pointer is initialized to nil. 'n_items' is a hint i.e.
- * zero is allowed.
+ * Initialize a soft_state set, establishing the 'size' of soft state objects
+ * in the set.
+ *
+ * For an 'integer indexed' soft_state set, the initial set will accommodate
+ * 'n_items' objects - 'n_items' is a hint (i.e. zero is allowed), allocations
+ * that exceed 'n_items' have additional overhead.
+ *
+ * For a 'string indexed' soft_state set, 'n_items' should be the typical
+ * number of soft state objects in the set - 'n_items' is a hint, there may
+ * be additional overhead if the hint is too small (and wasted memory if the
+ * hint is too big).
  */
 int
 ddi_soft_state_init(void **state_p, size_t size, size_t n_items);
+int
+ddi_soft_state_bystr_init(ddi_soft_state_bystr **state_p,
+    size_t size, int n_items);
 
 /*
- * Allocate a state structure of size 'size' to be associated
- * with item 'item'.
+ * Allocate a soft state object associated with either 'integer index' or
+ * 'string index' from a soft_state set.
  */
 int
 ddi_soft_state_zalloc(void *state, int item);
+int
+ddi_soft_state_bystr_zalloc(ddi_soft_state_bystr *state, const char *str);
 
 /*
- * Fetch a pointer to the allocated soft state structure
- * corresponding to 'item.'
+ * Get the pointer to the allocated soft state object associated with
+ * either 'integer index' or 'string index'.
  */
 void *
 ddi_get_soft_state(void *state, int item);
+void *
+ddi_soft_state_bystr_get(ddi_soft_state_bystr *state, const char *str);
 
 /*
- * Free the state structure corresponding to 'item.'
+ * Free the soft state object associated with either 'integer index'
+ * or 'string index'.
  */
 void
 ddi_soft_state_free(void *state, int item);
+void
+ddi_soft_state_bystr_free(ddi_soft_state_bystr *state, const char *str);
 
 /*
- * Free the handle, and any associated soft state structures.
+ * Free the soft state set and any associated soft state objects.
  */
 void
 ddi_soft_state_fini(void **state_p);
+void
+ddi_soft_state_bystr_fini(ddi_soft_state_bystr **state_p);
+
+/*
+ * The ddi_strid_* routines provide string-to-index management utilities.
+ */
+typedef	struct __ddi_strid	ddi_strid;
+int
+ddi_strid_init(ddi_strid **strid_p, int n_items);
+id_t
+ddi_strid_alloc(ddi_strid *strid, char *str);
+id_t
+ddi_strid_fixed_alloc(ddi_strid *strid, char *str);
+id_t
+ddi_strid_str2id(ddi_strid *strid, char *str);
+char *
+ddi_strid_id2str(ddi_strid *strid, id_t id);
+void
+ddi_strid_free(ddi_strid *strid, id_t id);
+void
+ddi_strid_fini(ddi_strid **strid_p);
 
 /*
  * Set the addr field of the name in dip to name
--- a/usr/src/uts/common/sys/sunmdi.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/sunmdi.h	Mon Oct 05 09:17:26 2009 -0400
@@ -87,7 +87,11 @@
 #define	MDI_COMPONENT_CLIENT	0x4
 
 /*
- * mdi_pathinfo node state utility definitions
+ * mdi_pathinfo node state utility definitions (bits in mdi_pathinfo_state_t)
+ *
+ * NOTE: having mdi_pathinfo_state_t contain both state and flags is error
+ * prone.  For new flags, please consider using MDI_PATHINFO_FLAG_ (and
+ * moving existing EXT_STATE_MASK flags over would be good too).
  */
 #define	MDI_PATHINFO_STATE_TRANSIENT			0x00010000
 #define	MDI_PATHINFO_STATE_USER_DISABLE			0x00100000
@@ -96,6 +100,12 @@
 #define	MDI_PATHINFO_STATE_MASK				0x0000FFFF
 #define	MDI_PATHINFO_EXT_STATE_MASK			0xFFF00000
 
+/*
+ * mdi_pathinfo flags definitions
+ */
+#define	MDI_PATHINFO_FLAGS_HIDDEN			0x00000001
+#define	MDI_PATHINFO_FLAGS_DEVICE_REMOVED		0x00000002
+
 #define	USER_DISABLE			1
 #define	DRIVER_DISABLE			2
 #define	DRIVER_DISABLE_TRANSIENT	3
@@ -191,6 +201,12 @@
 int mdi_pi_disable_path(mdi_pathinfo_t *, int);
 int mdi_pi_enable_path(mdi_pathinfo_t *, int);
 
+int mdi_pi_ishidden(mdi_pathinfo_t *);
+
+int mdi_pi_device_isremoved(mdi_pathinfo_t *);
+int mdi_pi_device_remove(mdi_pathinfo_t *);
+int mdi_pi_device_insert(mdi_pathinfo_t *);
+
 /*
  * MPxIO-PM stuff
  */
@@ -228,11 +244,14 @@
 char *mdi_pi_get_node_name(mdi_pathinfo_t *);
 char *mdi_pi_get_addr(mdi_pathinfo_t *);
 mdi_pathinfo_state_t mdi_pi_get_state(mdi_pathinfo_t *);
+uint_t mdi_pi_get_flags(mdi_pathinfo_t *);
 int mdi_pi_get_path_instance(mdi_pathinfo_t *);
-char *mdi_pi_pathname_by_instance(int path_instance);
+char *mdi_pi_pathname_by_instance(int);
 char *mdi_pi_pathname(mdi_pathinfo_t *);
 char *mdi_pi_pathname_obp(mdi_pathinfo_t *, char *);
 int mdi_pi_pathname_obp_set(mdi_pathinfo_t *, char *);
+char *mdi_pi_spathname_by_instance(int);
+char *mdi_pi_spathname(mdi_pathinfo_t *);
 
 /*
  * mdi_pathinfo Property handling functions
--- a/usr/src/uts/common/sys/sunndi.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/sunndi.h	Mon Oct 05 09:17:26 2009 -0400
@@ -318,6 +318,7 @@
 #define	NDI_DRV_CONF_REPROBE	0x04000000 /* reprobe conf-enum'd nodes only */
 #define	NDI_DETACH_DRIVER	0x08000000 /* performing driver_detach */
 #define	NDI_MTC_OFF		0x10000000 /* disable multi-threading */
+#define	NDI_USER_REQ		0x20000000 /* user requested operation */
 
 /* ndi interface flag values */
 #define	NDI_SLEEP		0x000000
@@ -342,6 +343,26 @@
 ndi_devi_findchild(dev_info_t *p, char *devname);
 
 /*
+ * Find the child dev_info node of parent nexus 'p' whose name
+ * matches "dname"@"ua". If a child doesn't have a "ua"
+ * value, it calls the function "make_ua" to create it.
+ */
+dev_info_t *
+ndi_devi_findchild_by_callback(dev_info_t *p, char *dname, char *ua,
+    int (*make_ua)(dev_info_t *, char *, int));
+
+/*
+ * Maintain DEVI_DEVICE_REMOVED hotplug devi_state for remove/reinsert hotplug
+ * of open devices.
+ */
+int
+ndi_devi_device_isremoved(dev_info_t *dip);
+int
+ndi_devi_device_remove(dev_info_t *dip);
+int
+ndi_devi_device_insert(dev_info_t *dip);
+
+/*
  * generate debug msg via NDI_DEVI_DEBUG flag
  */
 #define	NDI_DEBUG(flags, args)	\
@@ -462,10 +483,10 @@
 } ndi_event_definition_t;
 
 typedef struct ndi_event_cookie {
-	ndi_event_definition_t 	*definition;	/* Event Description */
+	ndi_event_definition_t	*definition;	/* Event Description */
 	dev_info_t		*ddip;		/* Devi defining this event */
 	ndi_event_callbacks_t	*callback_list; /* Cb's reg'd to w/ this evt */
-	struct ndi_event_cookie *next_cookie; 	/* Next cookie def'd in hdl */
+	struct ndi_event_cookie *next_cookie;	/* Next cookie def'd in hdl */
 } ndi_event_cookie_t;
 
 
@@ -522,7 +543,7 @@
  * get an event cookie
  */
 int
-ndi_event_retrieve_cookie(ndi_event_hdl_t 	handle,
+ndi_event_retrieve_cookie(ndi_event_hdl_t	handle,
 	dev_info_t		*child_dip,
 	char			*eventname,
 	ddi_eventcookie_t	*cookiep,
@@ -532,7 +553,7 @@
  * add an event callback info to the ndi event handle
  */
 int
-ndi_event_add_callback(ndi_event_hdl_t 	handle,
+ndi_event_add_callback(ndi_event_hdl_t	handle,
 	dev_info_t		*child_dip,
 	ddi_eventcookie_t	cookie,
 	void			(*event_callback)
@@ -591,7 +612,7 @@
  * name given an event_tag
  */
 char *
-ndi_event_tag_to_name(ndi_event_hdl_t 	handle, int event_tag);
+ndi_event_tag_to_name(ndi_event_hdl_t	handle, int event_tag);
 
 dev_info_t *
 ndi_devi_config_vhci(char *, int);
@@ -635,9 +656,9 @@
 					/* restricted to		*/
 
 	uint64_t	ra_boundlen;	/* Length of the area, starting	*/
-					/* from ra_boundbase, for the 	*/
+					/* from ra_boundbase, for the	*/
 					/* allocated resource to be	*/
-					/* restricted to.    		*/
+					/* restricted to.   		*/
 
 	uint64_t	ra_align_mask;	/* Alignment mask used for	*/
 					/* allocated base address	*/
@@ -728,6 +749,11 @@
 int ndi_dev_is_persistent_node(dev_info_t *);
 
 /*
+ * ndi_dev_is_hotplug_node: Return non-zero if the node was created by hotplug.
+ */
+int ndi_dev_is_hotplug_node(dev_info_t *);
+
+/*
  * ndi_dev_is_hidden_node: Return non-zero if the node is hidden.
  */
 int ndi_dev_is_hidden_node(dev_info_t *);
@@ -761,8 +787,8 @@
 void ndi_clr_dma_fault(ddi_dma_handle_t dh);
 
 /* Driver.conf property merging */
-int ndi_merge_node(dev_info_t *, int (*)(dev_info_t *, char *, int));
-void ndi_merge_wildcard_node(dev_info_t *);
+int	ndi_merge_node(dev_info_t *, int (*)(dev_info_t *, char *, int));
+void	ndi_merge_wildcard_node(dev_info_t *);
 
 /*
  * Ndi 'flavor' support: These interfaces are to support a nexus driver
--- a/usr/src/uts/common/sys/systm.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/systm.h	Mon Oct 05 09:17:26 2009 -0400
@@ -181,6 +181,7 @@
 clock_t untimeout_default(callout_id_t, int);
 void delay(clock_t);
 int delay_sig(clock_t);
+void delay_random(clock_t);
 int nodev();
 int nulldev();
 major_t getudev(void);
--- a/usr/src/uts/common/sys/usb/scsa2usb/scsa2usb.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/common/sys/usb/scsa2usb/scsa2usb.h	Mon Oct 05 09:17:26 2009 -0400
@@ -117,6 +117,9 @@
 #define	MS_PNY_VID	0x154b	/* Vendor ID of PNY Corp */
 #define	MS_PNY_PID0	0x16	/* PNY floppy drive */
 
+#define	MS_WD_VID	0x1058	/* Vendor ID of Western Digital */
+#define	MS_WD_PID   0x1001  /* PID for Western Digital USB External HDD */
+
 /*
  * The AMI virtual floppy device is not a real USB storage device, but
  * emulated by the SP firmware shipped together with important Sun x86
@@ -198,6 +201,7 @@
 #define	SCSA2USB_ATTRS_USE_CSW_RESIDUE	0x800	/* for residue checking */
 #define	SCSA2USB_ATTRS_NO_MEDIA_CHECK	0x1000	/* for media checking */
 #define	SCSA2USB_ATTRS_NO_CAP_ADJUST	0x2000	/* for CAPACITY adjusting */
+#define	SCSA2USB_ATTRS_INQUIRY_EVPD	0x4000	/* SCMD_INQUIRY with evpd */
 #define	SCSA2USB_ATTRS_REDUCED_CMD	\
 	(SCSA2USB_ATTRS_DOORLOCK|SCSA2USB_ATTRS_MODE_SENSE| \
 	SCSA2USB_ATTRS_START_STOP|SCSA2USB_ATTRS_INQUIRY| \
--- a/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_util.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/i86pc/io/acpi/acpidev/acpidev_util.c	Mon Oct 05 09:17:26 2009 -0400
@@ -232,6 +232,7 @@
 	ACPI_STATUS rc;
 	ssize_t len;
 	ACPI_BUFFER buf;
+	ACPI_OBJECT *obj;
 	ACPI_SUBTABLE_HEADER *ap;
 	ACPI_TABLE_MADT *mp = NULL;
 
@@ -252,17 +253,23 @@
 	} else if (method != NULL) {
 		/*
 		 * Otherwise, if we have an evaluate method, we get the walk
-		 * buffer from a successful invocation of AcpiEvaluateObject.
+		 * buffer from a successful invocation of
+		 * AcpiEvaluateObjectTyped().
 		 */
 		ASSERT(hdl != NULL);
-		rc = AcpiEvaluateObject(hdl, method, NULL, &buf);
-		if (ACPI_FAILURE(rc) && rc != AE_NOT_FOUND) {
-			cmn_err(CE_WARN, "!acpidev: failed to evaluate %s "
-			    "in acpidev_walk_apic().", method);
+		rc = AcpiEvaluateObjectTyped(hdl, method, NULL, &buf,
+		    ACPI_TYPE_BUFFER);
+		if (ACPI_SUCCESS(rc)) {
+			ASSERT(buf.Length >= sizeof (*obj));
+			obj = buf.Pointer;
+			ap = (ACPI_SUBTABLE_HEADER *)obj->Buffer.Pointer;
+			len = obj->Buffer.Length;
+		} else {
+			if (rc != AE_NOT_FOUND)
+				cmn_err(CE_WARN, "!acpidev: failed to evaluate "
+				    "%s in acpidev_walk_apic().", method);
 			return (rc);
 		}
-		ap = (ACPI_SUBTABLE_HEADER *)buf.Pointer;
-		len = buf.Length;
 	} else {
 		/* As a last resort, walk the MADT table. */
 		rc = AcpiGetTable(ACPI_SIG_MADT, 1, (ACPI_TABLE_HEADER **)&mp);
--- a/usr/src/uts/i86pc/os/lgrpplat.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/i86pc/os/lgrpplat.c	Mon Oct 05 09:17:26 2009 -0400
@@ -254,9 +254,11 @@
 static int				lgrp_plat_apic_ncpus = 0;
 
 /*
- * CPU to node ID mapping table (only used for SRAT)
+ * CPU to node ID mapping table (only used for SRAT) and its max number of
+ * entries
  */
-static cpu_node_map_t			lgrp_plat_cpu_node[NCPU];
+static cpu_node_map_t			*lgrp_plat_cpu_node = NULL;
+static uint_t				lgrp_plat_cpu_node_nentries = 0;
 
 /*
  * Latency statistics
@@ -385,12 +387,10 @@
 
 lgrp_handle_t	lgrp_plat_cpu_to_hand(processorid_t id);
 
-void		lgrp_plat_init(void);
+void		lgrp_plat_init(lgrp_init_stages_t stage);
 
 int		lgrp_plat_latency(lgrp_handle_t from, lgrp_handle_t to);
 
-void		lgrp_plat_main_init(void);
-
 int		lgrp_plat_max_lgrps(void);
 
 pgcnt_t		lgrp_plat_mem_size(lgrp_handle_t plathand,
@@ -412,11 +412,14 @@
     int node_cnt, cpu_node_map_t *cpu_node, int nentries, uint32_t apicid,
     uint32_t domain);
 
-static int	lgrp_plat_cpu_to_node(cpu_t *cp, cpu_node_map_t *cpu_node);
+static int	lgrp_plat_cpu_to_node(cpu_t *cp, cpu_node_map_t *cpu_node,
+    int cpu_node_nentries);
 
 static int	lgrp_plat_domain_to_node(node_domain_map_t *node_domain,
     int node_cnt, uint32_t domain);
 
+static void	lgrp_plat_get_numa_config(void);
+
 static void	lgrp_plat_latency_adjust(node_phys_addr_map_t *node_memory,
     lgrp_plat_latency_stats_t *lat_stats,
     lgrp_plat_probe_stats_t *probe_stats);
@@ -424,6 +427,8 @@
 static int	lgrp_plat_latency_verify(node_phys_addr_map_t *node_memory,
     lgrp_plat_latency_stats_t *lat_stats);
 
+static void	lgrp_plat_main_init(void);
+
 static pgcnt_t	lgrp_plat_mem_size_default(lgrp_handle_t, lgrp_mem_query_t);
 
 static int	lgrp_plat_node_domain_update(node_domain_map_t *node_domain,
@@ -438,9 +443,8 @@
     node_phys_addr_map_t *node_memory);
 
 static hrtime_t	lgrp_plat_probe_time(int to, cpu_node_map_t *cpu_node,
-    lgrp_plat_probe_mem_config_t *probe_mem_config,
-    lgrp_plat_latency_stats_t *lat_stats,
-    lgrp_plat_probe_stats_t *probe_stats);
+    int cpu_node_nentries, lgrp_plat_probe_mem_config_t *probe_mem_config,
+    lgrp_plat_latency_stats_t *lat_stats, lgrp_plat_probe_stats_t *probe_stats);
 
 static int	lgrp_plat_process_cpu_apicids(cpu_node_map_t *cpu_node);
 
@@ -452,6 +456,8 @@
     cpu_node_map_t *cpu_node, int cpu_count,
     node_phys_addr_map_t *node_memory);
 
+static void	lgrp_plat_release_bootstrap(void);
+
 static int	lgrp_plat_srat_domains(struct srat *tp,
     uint32_t *prox_domain_min);
 
@@ -728,7 +734,7 @@
 		return (LGRP_DEFAULT_HANDLE);
 
 	hand = (lgrp_handle_t)lgrp_plat_cpu_to_node(cpu[id],
-	    lgrp_plat_cpu_node);
+	    lgrp_plat_cpu_node, lgrp_plat_cpu_node_nentries);
 
 	ASSERT(hand != (lgrp_handle_t)-1);
 	if (hand == (lgrp_handle_t)-1)
@@ -742,166 +748,63 @@
  * Platform-specific initialization of lgroups
  */
 void
-lgrp_plat_init(void)
+lgrp_plat_init(lgrp_init_stages_t stage)
 {
 #if defined(__xpv)
-	/*
-	 * XXPV	For now, the hypervisor treats all memory equally.
-	 */
-	lgrp_plat_node_cnt = max_mem_nodes = 1;
 #else	/* __xpv */
-	uint_t		probe_op;
 	u_longlong_t	value;
-
-	/*
-	 * Get boot property for lgroup topology height limit
-	 */
-	if (bootprop_getval(BP_LGRP_TOPO_LEVELS, &value) == 0)
-		(void) lgrp_topo_ht_limit_set((int)value);
-
-	/*
-	 * Get boot property for enabling/disabling SRAT
-	 */
-	if (bootprop_getval(BP_LGRP_SRAT_ENABLE, &value) == 0)
-		lgrp_plat_srat_enable = (int)value;
-
-	/*
-	 * Get boot property for enabling/disabling SLIT
-	 */
-	if (bootprop_getval(BP_LGRP_SLIT_ENABLE, &value) == 0)
-		lgrp_plat_slit_enable = (int)value;
-
-	/*
-	 * Initialize as a UMA machine
-	 */
-	if (lgrp_topo_ht_limit() == 1) {
+#endif	/* __xpv */
+
+	switch (stage) {
+	case LGRP_INIT_STAGE1:
+#if defined(__xpv)
+		/*
+		 * XXPV	For now, the hypervisor treats all memory equally.
+		 */
 		lgrp_plat_node_cnt = max_mem_nodes = 1;
-		return;
-	}
-
-	/*
-	 * Read boot property with CPU to APIC ID mapping table/array and fill
-	 * in CPU to node ID mapping table with APIC ID for each CPU
-	 */
-	lgrp_plat_apic_ncpus =
-	    lgrp_plat_process_cpu_apicids(lgrp_plat_cpu_node);
-
-	/*
-	 * Determine which CPUs and memory are local to each other and number
-	 * of NUMA nodes by reading ACPI System Resource Affinity Table (SRAT)
-	 */
-	if (lgrp_plat_apic_ncpus > 0) {
-		int	retval;
-
-		retval = lgrp_plat_process_srat(srat_ptr,
-		    &lgrp_plat_prox_domain_min,
-		    lgrp_plat_node_domain, lgrp_plat_cpu_node,
-		    lgrp_plat_apic_ncpus, lgrp_plat_node_memory);
-		if (retval <= 0) {
-			lgrp_plat_srat_error = retval;
-			lgrp_plat_node_cnt = 1;
-		} else {
-			lgrp_plat_srat_error = 0;
-			lgrp_plat_node_cnt = retval;
+#else	/* __xpv */
+		/*
+		 * Get boot property for lgroup topology height limit
+		 */
+		if (bootprop_getval(BP_LGRP_TOPO_LEVELS, &value) == 0)
+			(void) lgrp_topo_ht_limit_set((int)value);
+
+		/*
+		 * Get boot property for enabling/disabling SRAT
+		 */
+		if (bootprop_getval(BP_LGRP_SRAT_ENABLE, &value) == 0)
+			lgrp_plat_srat_enable = (int)value;
+
+		/*
+		 * Get boot property for enabling/disabling SLIT
+		 */
+		if (bootprop_getval(BP_LGRP_SLIT_ENABLE, &value) == 0)
+			lgrp_plat_slit_enable = (int)value;
+
+		/*
+		 * Initialize as a UMA machine
+		 */
+		if (lgrp_topo_ht_limit() == 1) {
+			lgrp_plat_node_cnt = max_mem_nodes = 1;
+			return;
 		}
+
+		lgrp_plat_get_numa_config();
+#endif	/* __xpv */
+		break;
+
+	case LGRP_INIT_STAGE3:
+		lgrp_plat_probe();
+		lgrp_plat_release_bootstrap();
+		break;
+
+	case LGRP_INIT_STAGE4:
+		lgrp_plat_main_init();
+		break;
+
+	default:
+		break;
 	}
-
-	/*
-	 * Try to use PCI config space registers on Opteron if there's an error
-	 * processing CPU to APIC ID mapping or SRAT
-	 */
-	if ((lgrp_plat_apic_ncpus <= 0 || lgrp_plat_srat_error != 0) &&
-	    is_opteron())
-		opt_get_numa_config(&lgrp_plat_node_cnt, &lgrp_plat_mem_intrlv,
-		    lgrp_plat_node_memory);
-
-	/*
-	 * Don't bother to setup system for multiple lgroups and only use one
-	 * memory node when memory is interleaved between any nodes or there is
-	 * only one NUMA node
-	 *
-	 * NOTE: May need to change this for Dynamic Reconfiguration (DR)
-	 *	 when and if it happens for x86/x64
-	 */
-	if (lgrp_plat_mem_intrlv || lgrp_plat_node_cnt == 1) {
-		lgrp_plat_node_cnt = max_mem_nodes = 1;
-		(void) lgrp_topo_ht_limit_set(1);
-		return;
-	}
-
-	/*
-	 * Leaf lgroups on x86/x64 architectures contain one physical
-	 * processor chip. Tune lgrp_expand_proc_thresh and
-	 * lgrp_expand_proc_diff so that lgrp_choose() will spread
-	 * things out aggressively.
-	 */
-	lgrp_expand_proc_thresh = LGRP_LOADAVG_THREAD_MAX / 2;
-	lgrp_expand_proc_diff = 0;
-
-	/*
-	 * There should be one memnode (physical page free list(s)) for
-	 * each node
-	 */
-	max_mem_nodes = lgrp_plat_node_cnt;
-
-	/*
-	 * Initialize min and max latency before reading SLIT or probing
-	 */
-	lgrp_plat_lat_stats.latency_min = -1;
-	lgrp_plat_lat_stats.latency_max = 0;
-
-	/*
-	 * Determine how far each NUMA node is from each other by
-	 * reading ACPI System Locality Information Table (SLIT) if it
-	 * exists
-	 */
-	lgrp_plat_slit_error = lgrp_plat_process_slit(slit_ptr,
-	    lgrp_plat_node_cnt, lgrp_plat_node_memory,
-	    &lgrp_plat_lat_stats);
-	if (lgrp_plat_slit_error == 0)
-		return;
-
-	/*
-	 * Probe to determine latency between NUMA nodes when SLIT
-	 * doesn't exist or make sense
-	 */
-	lgrp_plat_probe_flags |= LGRP_PLAT_PROBE_ENABLE;
-
-	/*
-	 * Specify whether to probe using vendor ID register or page copy
-	 * if hasn't been specified already or is overspecified
-	 */
-	probe_op = lgrp_plat_probe_flags &
-	    (LGRP_PLAT_PROBE_PGCPY|LGRP_PLAT_PROBE_VENDOR);
-
-	if (probe_op == 0 ||
-	    probe_op == (LGRP_PLAT_PROBE_PGCPY|LGRP_PLAT_PROBE_VENDOR)) {
-		lgrp_plat_probe_flags &=
-		    ~(LGRP_PLAT_PROBE_PGCPY|LGRP_PLAT_PROBE_VENDOR);
-		if (is_opteron())
-			lgrp_plat_probe_flags |=
-			    LGRP_PLAT_PROBE_VENDOR;
-		else
-			lgrp_plat_probe_flags |= LGRP_PLAT_PROBE_PGCPY;
-	}
-
-	/*
-	 * Probing errors can mess up the lgroup topology and
-	 * force us fall back to a 2 level lgroup topology.
-	 * Here we bound how tall the lgroup topology can grow
-	 * in hopes of avoiding any anamolies in probing from
-	 * messing up the lgroup topology by limiting the
-	 * accuracy of the latency topology.
-	 *
-	 * Assume that nodes will at least be configured in a
-	 * ring, so limit height of lgroup topology to be less
-	 * than number of nodes on a system with 4 or more
-	 * nodes
-	 */
-	if (lgrp_plat_node_cnt >= 4 && lgrp_topo_ht_limit() ==
-	    lgrp_topo_ht_limit_default())
-		(void) lgrp_topo_ht_limit_set(lgrp_plat_node_cnt - 1);
-#endif	/* __xpv */
 }
 
 
@@ -943,7 +846,8 @@
 	 * Probe from current CPU if its lgroup latencies haven't been set yet
 	 * and we are trying to get latency from current CPU to some node
 	 */
-	node = lgrp_plat_cpu_to_node(CPU, lgrp_plat_cpu_node);
+	node = lgrp_plat_cpu_to_node(CPU, lgrp_plat_cpu_node,
+	    lgrp_plat_cpu_node_nentries);
 	ASSERT(node >= 0 && node < lgrp_plat_node_cnt);
 	if (lgrp_plat_lat_stats.latencies[src][src] == 0 && node == src)
 		lgrp_plat_probe();
@@ -953,117 +857,6 @@
 
 
 /*
- * Platform-specific initialization
- */
-void
-lgrp_plat_main_init(void)
-{
-	int	curnode;
-	int	ht_limit;
-	int	i;
-
-	/*
-	 * Print a notice that MPO is disabled when memory is interleaved
-	 * across nodes....Would do this when it is discovered, but can't
-	 * because it happens way too early during boot....
-	 */
-	if (lgrp_plat_mem_intrlv)
-		cmn_err(CE_NOTE,
-		    "MPO disabled because memory is interleaved\n");
-
-	/*
-	 * Don't bother to do any probing if it is disabled, there is only one
-	 * node, or the height of the lgroup topology less than or equal to 2
-	 */
-	ht_limit = lgrp_topo_ht_limit();
-	if (!(lgrp_plat_probe_flags & LGRP_PLAT_PROBE_ENABLE) ||
-	    max_mem_nodes == 1 || ht_limit <= 2) {
-		/*
-		 * Setup lgroup latencies for 2 level lgroup topology
-		 * (ie. local and remote only) if they haven't been set yet
-		 */
-		if (ht_limit == 2 && lgrp_plat_lat_stats.latency_min == -1 &&
-		    lgrp_plat_lat_stats.latency_max == 0)
-			lgrp_plat_2level_setup(lgrp_plat_node_memory,
-			    &lgrp_plat_lat_stats);
-		return;
-	}
-
-	if (lgrp_plat_probe_flags & LGRP_PLAT_PROBE_VENDOR) {
-		/*
-		 * Should have been able to probe from CPU 0 when it was added
-		 * to lgroup hierarchy, but may not have been able to then
-		 * because it happens so early in boot that gethrtime() hasn't
-		 * been initialized.  (:-(
-		 */
-		curnode = lgrp_plat_cpu_to_node(CPU, lgrp_plat_cpu_node);
-		ASSERT(curnode >= 0 && curnode < lgrp_plat_node_cnt);
-		if (lgrp_plat_lat_stats.latencies[curnode][curnode] == 0)
-			lgrp_plat_probe();
-
-		return;
-	}
-
-	/*
-	 * When probing memory, use one page for every sample to determine
-	 * lgroup topology and taking multiple samples
-	 */
-	if (lgrp_plat_probe_mem_config.probe_memsize == 0)
-		lgrp_plat_probe_mem_config.probe_memsize = PAGESIZE *
-		    lgrp_plat_probe_nsamples;
-
-	/*
-	 * Map memory in each node needed for probing to determine latency
-	 * topology
-	 */
-	for (i = 0; i < lgrp_plat_node_cnt; i++) {
-		int	mnode;
-
-		/*
-		 * Skip this node and leave its probe page NULL
-		 * if it doesn't have any memory
-		 */
-		mnode = plat_lgrphand_to_mem_node((lgrp_handle_t)i);
-		if (!mem_node_config[mnode].exists) {
-			lgrp_plat_probe_mem_config.probe_va[i] = NULL;
-			continue;
-		}
-
-		/*
-		 * Allocate one kernel virtual page
-		 */
-		lgrp_plat_probe_mem_config.probe_va[i] = vmem_alloc(heap_arena,
-		    lgrp_plat_probe_mem_config.probe_memsize, VM_NOSLEEP);
-		if (lgrp_plat_probe_mem_config.probe_va[i] == NULL) {
-			cmn_err(CE_WARN,
-			    "lgrp_plat_main_init: couldn't allocate memory");
-			return;
-		}
-
-		/*
-		 * Get PFN for first page in each node
-		 */
-		lgrp_plat_probe_mem_config.probe_pfn[i] =
-		    mem_node_config[mnode].physbase;
-
-		/*
-		 * Map virtual page to first page in node
-		 */
-		hat_devload(kas.a_hat, lgrp_plat_probe_mem_config.probe_va[i],
-		    lgrp_plat_probe_mem_config.probe_memsize,
-		    lgrp_plat_probe_mem_config.probe_pfn[i],
-		    PROT_READ | PROT_WRITE | HAT_PLAT_NOCACHE,
-		    HAT_LOAD_NOCONSIST);
-	}
-
-	/*
-	 * Probe from current CPU
-	 */
-	lgrp_plat_probe();
-}
-
-
-/*
  * Return the maximum number of lgrps supported by the platform.
  * Before lgrp topology is known it returns an estimate based on the number of
  * nodes. Once topology is known it returns the actual maximim number of lgrps
@@ -1189,7 +982,8 @@
 	/*
 	 * Determine ID of node containing current CPU
 	 */
-	from = lgrp_plat_cpu_to_node(CPU, lgrp_plat_cpu_node);
+	from = lgrp_plat_cpu_to_node(CPU, lgrp_plat_cpu_node,
+	    lgrp_plat_cpu_node_nentries);
 	ASSERT(from >= 0 && from < lgrp_plat_node_cnt);
 	if (srat_ptr && lgrp_plat_srat_enable && !lgrp_plat_srat_error)
 		ASSERT(lgrp_plat_node_domain[from].exists);
@@ -1215,8 +1009,9 @@
 			 * probed yet or don't have memory
 			 */
 			probe_time = lgrp_plat_probe_time(to,
-			    lgrp_plat_cpu_node, &lgrp_plat_probe_mem_config,
-			    &lgrp_plat_lat_stats, &lgrp_plat_probe_stats);
+			    lgrp_plat_cpu_node, lgrp_plat_cpu_node_nentries,
+			    &lgrp_plat_probe_mem_config, &lgrp_plat_lat_stats,
+			    &lgrp_plat_probe_stats);
 			if (probe_time == 0)
 				continue;
 
@@ -1343,7 +1138,8 @@
  * Get node ID for given CPU
  */
 static int
-lgrp_plat_cpu_to_node(cpu_t *cp, cpu_node_map_t *cpu_node)
+lgrp_plat_cpu_to_node(cpu_t *cp, cpu_node_map_t *cpu_node,
+    int cpu_node_nentries)
 {
 	processorid_t	cpuid;
 
@@ -1369,7 +1165,7 @@
 	 * Return -1 when CPU to node ID mapping entry doesn't exist for given
 	 * CPU
 	 */
-	if (!cpu_node[cpuid].exists)
+	if (cpuid >= cpu_node_nentries || !cpu_node[cpuid].exists)
 		return (-1);
 
 	return (cpu_node[cpuid].node);
@@ -1403,6 +1199,159 @@
 
 
 /*
+ * Get NUMA configuration of machine
+ */
+static void
+lgrp_plat_get_numa_config(void)
+{
+	uint_t		probe_op;
+
+	/*
+	 * Read boot property with CPU to APIC ID mapping table/array to
+	 * determine number of CPUs
+	 */
+	lgrp_plat_apic_ncpus = lgrp_plat_process_cpu_apicids(NULL);
+
+	/*
+	 * Determine which CPUs and memory are local to each other and number
+	 * of NUMA nodes by reading ACPI System Resource Affinity Table (SRAT)
+	 */
+	if (lgrp_plat_apic_ncpus > 0) {
+		int	retval;
+
+		/*
+		 * Temporarily allocate boot memory to use for CPU to node
+		 * mapping since kernel memory allocator isn't alive yet
+		 */
+		lgrp_plat_cpu_node = (cpu_node_map_t *)BOP_ALLOC(bootops,
+		    NULL, lgrp_plat_apic_ncpus * sizeof (cpu_node_map_t),
+		    sizeof (int));
+
+		ASSERT(lgrp_plat_cpu_node != NULL);
+		if (lgrp_plat_cpu_node) {
+			lgrp_plat_cpu_node_nentries = lgrp_plat_apic_ncpus;
+			bzero(lgrp_plat_cpu_node, lgrp_plat_cpu_node_nentries *
+			    sizeof (cpu_node_map_t));
+		}
+
+		/*
+		 * Fill in CPU to node ID mapping table with APIC ID for each
+		 * CPU
+		 */
+		(void) lgrp_plat_process_cpu_apicids(lgrp_plat_cpu_node);
+
+		retval = lgrp_plat_process_srat(srat_ptr,
+		    &lgrp_plat_prox_domain_min,
+		    lgrp_plat_node_domain, lgrp_plat_cpu_node,
+		    lgrp_plat_apic_ncpus, lgrp_plat_node_memory);
+		if (retval <= 0) {
+			lgrp_plat_srat_error = retval;
+			lgrp_plat_node_cnt = 1;
+		} else {
+			lgrp_plat_srat_error = 0;
+			lgrp_plat_node_cnt = retval;
+		}
+	}
+
+	/*
+	 * Try to use PCI config space registers on Opteron if there's an error
+	 * processing CPU to APIC ID mapping or SRAT
+	 */
+	if ((lgrp_plat_apic_ncpus <= 0 || lgrp_plat_srat_error != 0) &&
+	    is_opteron())
+		opt_get_numa_config(&lgrp_plat_node_cnt, &lgrp_plat_mem_intrlv,
+		    lgrp_plat_node_memory);
+
+	/*
+	 * Don't bother to setup system for multiple lgroups and only use one
+	 * memory node when memory is interleaved between any nodes or there is
+	 * only one NUMA node
+	 *
+	 * NOTE: May need to change this for Dynamic Reconfiguration (DR)
+	 *	 when and if it happens for x86/x64
+	 */
+	if (lgrp_plat_mem_intrlv || lgrp_plat_node_cnt == 1) {
+		lgrp_plat_node_cnt = max_mem_nodes = 1;
+		(void) lgrp_topo_ht_limit_set(1);
+		return;
+	}
+
+	/*
+	 * Leaf lgroups on x86/x64 architectures contain one physical
+	 * processor chip. Tune lgrp_expand_proc_thresh and
+	 * lgrp_expand_proc_diff so that lgrp_choose() will spread
+	 * things out aggressively.
+	 */
+	lgrp_expand_proc_thresh = LGRP_LOADAVG_THREAD_MAX / 2;
+	lgrp_expand_proc_diff = 0;
+
+	/*
+	 * There should be one memnode (physical page free list(s)) for
+	 * each node
+	 */
+	max_mem_nodes = lgrp_plat_node_cnt;
+
+	/*
+	 * Initialize min and max latency before reading SLIT or probing
+	 */
+	lgrp_plat_lat_stats.latency_min = -1;
+	lgrp_plat_lat_stats.latency_max = 0;
+
+	/*
+	 * Determine how far each NUMA node is from each other by
+	 * reading ACPI System Locality Information Table (SLIT) if it
+	 * exists
+	 */
+	lgrp_plat_slit_error = lgrp_plat_process_slit(slit_ptr,
+	    lgrp_plat_node_cnt, lgrp_plat_node_memory,
+	    &lgrp_plat_lat_stats);
+	if (lgrp_plat_slit_error == 0)
+		return;
+
+	/*
+	 * Probe to determine latency between NUMA nodes when SLIT
+	 * doesn't exist or make sense
+	 */
+	lgrp_plat_probe_flags |= LGRP_PLAT_PROBE_ENABLE;
+
+	/*
+	 * Specify whether to probe using vendor ID register or page copy
+	 * if hasn't been specified already or is overspecified
+	 */
+	probe_op = lgrp_plat_probe_flags &
+	    (LGRP_PLAT_PROBE_PGCPY|LGRP_PLAT_PROBE_VENDOR);
+
+	if (probe_op == 0 ||
+	    probe_op == (LGRP_PLAT_PROBE_PGCPY|LGRP_PLAT_PROBE_VENDOR)) {
+		lgrp_plat_probe_flags &=
+		    ~(LGRP_PLAT_PROBE_PGCPY|LGRP_PLAT_PROBE_VENDOR);
+		if (is_opteron())
+			lgrp_plat_probe_flags |=
+			    LGRP_PLAT_PROBE_VENDOR;
+		else
+			lgrp_plat_probe_flags |= LGRP_PLAT_PROBE_PGCPY;
+	}
+
+	/*
+	 * Probing errors can mess up the lgroup topology and
+	 * force us fall back to a 2 level lgroup topology.
+	 * Here we bound how tall the lgroup topology can grow
+	 * in hopes of avoiding any anamolies in probing from
+	 * messing up the lgroup topology by limiting the
+	 * accuracy of the latency topology.
+	 *
+	 * Assume that nodes will at least be configured in a
+	 * ring, so limit height of lgroup topology to be less
+	 * than number of nodes on a system with 4 or more
+	 * nodes
+	 */
+	if (lgrp_plat_node_cnt >= 4 && lgrp_topo_ht_limit() ==
+	    lgrp_topo_ht_limit_default())
+		(void) lgrp_topo_ht_limit_set(lgrp_plat_node_cnt - 1);
+}
+
+
+/*
  * Latencies must be within 1/(2**LGRP_LAT_TOLERANCE_SHIFT) of each other to
  * be considered same
  */
@@ -1749,6 +1698,118 @@
 
 
 /*
+ * Platform-specific initialization
+ */
+static void
+lgrp_plat_main_init(void)
+{
+	int	curnode;
+	int	ht_limit;
+	int	i;
+
+	/*
+	 * Print a notice that MPO is disabled when memory is interleaved
+	 * across nodes....Would do this when it is discovered, but can't
+	 * because it happens way too early during boot....
+	 */
+	if (lgrp_plat_mem_intrlv)
+		cmn_err(CE_NOTE,
+		    "MPO disabled because memory is interleaved\n");
+
+	/*
+	 * Don't bother to do any probing if it is disabled, there is only one
+	 * node, or the height of the lgroup topology less than or equal to 2
+	 */
+	ht_limit = lgrp_topo_ht_limit();
+	if (!(lgrp_plat_probe_flags & LGRP_PLAT_PROBE_ENABLE) ||
+	    max_mem_nodes == 1 || ht_limit <= 2) {
+		/*
+		 * Setup lgroup latencies for 2 level lgroup topology
+		 * (ie. local and remote only) if they haven't been set yet
+		 */
+		if (ht_limit == 2 && lgrp_plat_lat_stats.latency_min == -1 &&
+		    lgrp_plat_lat_stats.latency_max == 0)
+			lgrp_plat_2level_setup(lgrp_plat_node_memory,
+			    &lgrp_plat_lat_stats);
+		return;
+	}
+
+	if (lgrp_plat_probe_flags & LGRP_PLAT_PROBE_VENDOR) {
+		/*
+		 * Should have been able to probe from CPU 0 when it was added
+		 * to lgroup hierarchy, but may not have been able to then
+		 * because it happens so early in boot that gethrtime() hasn't
+		 * been initialized.  (:-(
+		 */
+		curnode = lgrp_plat_cpu_to_node(CPU, lgrp_plat_cpu_node,
+		    lgrp_plat_cpu_node_nentries);
+		ASSERT(curnode >= 0 && curnode < lgrp_plat_node_cnt);
+		if (lgrp_plat_lat_stats.latencies[curnode][curnode] == 0)
+			lgrp_plat_probe();
+
+		return;
+	}
+
+	/*
+	 * When probing memory, use one page for every sample to determine
+	 * lgroup topology and taking multiple samples
+	 */
+	if (lgrp_plat_probe_mem_config.probe_memsize == 0)
+		lgrp_plat_probe_mem_config.probe_memsize = PAGESIZE *
+		    lgrp_plat_probe_nsamples;
+
+	/*
+	 * Map memory in each node needed for probing to determine latency
+	 * topology
+	 */
+	for (i = 0; i < lgrp_plat_node_cnt; i++) {
+		int	mnode;
+
+		/*
+		 * Skip this node and leave its probe page NULL
+		 * if it doesn't have any memory
+		 */
+		mnode = plat_lgrphand_to_mem_node((lgrp_handle_t)i);
+		if (!mem_node_config[mnode].exists) {
+			lgrp_plat_probe_mem_config.probe_va[i] = NULL;
+			continue;
+		}
+
+		/*
+		 * Allocate one kernel virtual page
+		 */
+		lgrp_plat_probe_mem_config.probe_va[i] = vmem_alloc(heap_arena,
+		    lgrp_plat_probe_mem_config.probe_memsize, VM_NOSLEEP);
+		if (lgrp_plat_probe_mem_config.probe_va[i] == NULL) {
+			cmn_err(CE_WARN,
+			    "lgrp_plat_main_init: couldn't allocate memory");
+			return;
+		}
+
+		/*
+		 * Get PFN for first page in each node
+		 */
+		lgrp_plat_probe_mem_config.probe_pfn[i] =
+		    mem_node_config[mnode].physbase;
+
+		/*
+		 * Map virtual page to first page in node
+		 */
+		hat_devload(kas.a_hat, lgrp_plat_probe_mem_config.probe_va[i],
+		    lgrp_plat_probe_mem_config.probe_memsize,
+		    lgrp_plat_probe_mem_config.probe_pfn[i],
+		    PROT_READ | PROT_WRITE | HAT_PLAT_NOCACHE,
+		    HAT_LOAD_NOCONSIST);
+	}
+
+	/*
+	 * Probe from current CPU
+	 */
+	lgrp_plat_probe();
+}
+
+
+/*
  * Return the number of free, allocatable, or installed
  * pages in an lgroup
  * This is a copy of the MAX_MEM_NODES == 1 version of the routine
@@ -2026,7 +2087,7 @@
  * Return time needed to probe from current CPU to memory in given node
  */
 static hrtime_t
-lgrp_plat_probe_time(int to, cpu_node_map_t *cpu_node,
+lgrp_plat_probe_time(int to, cpu_node_map_t *cpu_node, int cpu_node_nentries,
     lgrp_plat_probe_mem_config_t *probe_mem_config,
     lgrp_plat_latency_stats_t *lat_stats, lgrp_plat_probe_stats_t *probe_stats)
 {
@@ -2044,7 +2105,7 @@
 	/*
 	 * Determine ID of node containing current CPU
 	 */
-	from = lgrp_plat_cpu_to_node(CPU, cpu_node);
+	from = lgrp_plat_cpu_to_node(CPU, cpu_node, cpu_node_nentries);
 	ASSERT(from >= 0 && from < lgrp_plat_node_cnt);
 
 	/*
@@ -2139,7 +2200,8 @@
 
 /*
  * Read boot property with CPU to APIC ID array, fill in CPU to node ID
- * mapping table with APIC ID for each CPU, and return number of CPU APIC IDs.
+ * mapping table with APIC ID for each CPU (if pointer to table isn't NULL),
+ * and return number of CPU APIC IDs.
  *
  * NOTE: This code assumes that CPU IDs are assigned in order that they appear
  *       in in cpu_apicid_array boot property which is based on and follows
@@ -2157,17 +2219,11 @@
 	int	n;
 
 	/*
-	 * Nothing to do when no array to fill in or not enough CPUs
-	 */
-	if (cpu_node == NULL)
-		return (-1);
-
-	/*
 	 * Check length of property value
 	 */
 	boot_prop_len = BOP_GETPROPLEN(bootops, boot_prop_name);
 	if (boot_prop_len <= 0 || boot_prop_len > sizeof (cpu_apicid_array))
-		return (-2);
+		return (-1);
 
 	/*
 	 * Calculate number of entries in array and return when there's just
@@ -2175,13 +2231,20 @@
 	 */
 	n = boot_prop_len / sizeof (uint8_t);
 	if (n == 1)
-		return (-3);
+		return (-2);
 
 	/*
 	 * Get CPU to APIC ID property value
 	 */
 	if (BOP_GETPROP(bootops, boot_prop_name, cpu_apicid_array) < 0)
-		return (-4);
+		return (-3);
+
+	/*
+	 * Just return number of CPU APIC IDs if CPU to node mapping table is
+	 * NULL
+	 */
+	if (cpu_node == NULL)
+		return (n);
 
 	/*
 	 * Fill in CPU to node ID mapping table with APIC ID for each CPU
@@ -2404,6 +2467,27 @@
 
 
 /*
+ * Allocate permanent memory for any temporary memory that we needed to
+ * allocate using BOP_ALLOC() before kmem_alloc() and VM system were
+ * initialized and copy everything from temporary to permanent memory since
+ * temporary boot memory will eventually be released during boot
+ */
+static void
+lgrp_plat_release_bootstrap(void)
+{
+	void	*buf;
+	size_t	size;
+
+	if (lgrp_plat_cpu_node_nentries > 0) {
+		size = lgrp_plat_cpu_node_nentries * sizeof (cpu_node_map_t);
+		buf = kmem_alloc(size, KM_SLEEP);
+		bcopy(lgrp_plat_cpu_node, buf, size);
+		lgrp_plat_cpu_node = buf;
+	}
+}
+
+
+/*
  * Return number of proximity domains given in ACPI SRAT
  */
 static int
--- a/usr/src/uts/i86pc/os/mlsetup.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/i86pc/os/mlsetup.c	Mon Oct 05 09:17:26 2009 -0400
@@ -155,6 +155,22 @@
 		cpuid_feature_edx_exclude = (uint32_t)prop_value;
 
 	/*
+	 * Initialize idt0, gdt0, ldt0_default, ktss0 and dftss.
+	 */
+	init_desctbls();
+
+	/*
+	 * lgrp_init() and possibly cpuid_pass1() need PCI config
+	 * space access
+	 */
+#if defined(__xpv)
+	if (DOMAIN_IS_INITDOMAIN(xen_info))
+		pci_cfgspace_init();
+#else
+	pci_cfgspace_init();
+#endif
+
+	/*
 	 * The first lightweight pass (pass0) through the cpuid data
 	 * was done in locore before mlsetup was called.  Do the next
 	 * pass in C code.
@@ -167,11 +183,6 @@
 	 */
 	x86_feature = cpuid_pass1(cpu[0]);
 
-	/*
-	 * Initialize idt0, gdt0, ldt0_default, ktss0 and dftss.
-	 */
-	init_desctbls();
-
 #if !defined(__xpv)
 
 	if (get_hwenv() == HW_XEN_HVM)
@@ -339,14 +350,6 @@
 
 	cpu_vm_data_init(CPU);
 
-	/* lgrp_init() needs PCI config space access */
-#if defined(__xpv)
-	if (DOMAIN_IS_INITDOMAIN(xen_info))
-		pci_cfgspace_init();
-#else
-	pci_cfgspace_init();
-#endif
-
 	rp->r_fp = 0;	/* terminate kernel stack traces! */
 
 	prom_init("kernel", (void *)NULL);
@@ -364,7 +367,7 @@
 	/*
 	 * Initialize the lgrp framework
 	 */
-	lgrp_init();
+	lgrp_init(LGRP_INIT_STAGE1);
 
 	if (boothowto & RB_HALT) {
 		prom_printf("unix: kernel halted by -h flag\n");
--- a/usr/src/uts/i86pc/sys/acpidev.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/i86pc/sys/acpidev.h	Mon Oct 05 09:17:26 2009 -0400
@@ -79,7 +79,7 @@
 #define	ACPIDEV_TYPE_SCOPE		"acpiscope"
 #define	ACPIDEV_TYPE_DEVICE		"acpidevice"
 #define	ACPIDEV_TYPE_CONTAINER		"acpicontainer"
-#define	ACPIDEV_TYPE_CPU		"cpu"
+#define	ACPIDEV_TYPE_CPU		"acpicpu"
 #define	ACPIDEV_TYPE_MEMORY		"acpimemory"
 
 /* Device property names for ACPI objects. */
--- a/usr/src/uts/intel/Makefile.intel.shared	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/intel/Makefile.intel.shared	Mon Oct 05 09:17:26 2009 -0400
@@ -434,6 +434,7 @@
 #
 DRV_KMODS	+= options
 DRV_KMODS	+= scsi_vhci
+DRV_KMODS	+= pmcs
 DRV_KMODS	+= arcmsr
 DRV_KMODS	+= fcp
 DRV_KMODS	+= fcip
--- a/usr/src/uts/intel/io/intel_nb5000/nb5000.h	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/intel/io/intel_nb5000/nb5000.h	Mon Oct 05 09:17:26 2009 -0400
@@ -319,7 +319,7 @@
 #define	EMASK_5000_FBD_RES	(EMASK_FBD_M24|EMASK_FBD_M16)
 #define	EMASK_FBD_RES	(nb_chipset == INTEL_NB_5400 ? 0 : EMASK_5000_FBD_RES)
 
-#define	EMASK_FBD_FATAL	(EMASK_FBD_M23|EMASK_FBD_M3|EMASK_FBD_M2|EMASK_FBD_M1)
+#define	EMASK_FBD_FATAL	(EMASK_FBD_M3|EMASK_FBD_M2|EMASK_FBD_M1)
 #define	EMASK_FBD_NF (EMASK_FBD_M28|EMASK_FBD_M27|EMASK_FBD_M26|EMASK_FBD_M25| \
 	EMASK_FBD_M22|EMASK_FBD_M21|EMASK_FBD_M20|EMASK_FBD_M19|EMASK_FBD_M18| \
 	EMASK_FBD_M17|EMASK_FBD_M15|EMASK_FBD_M14|EMASK_FBD_M13|EMASK_FBD_M12| \
@@ -332,6 +332,9 @@
 	EMASK_FBD_M15|EMASK_FBD_M14|EMASK_FBD_M13|EMASK_FBD_M12| \
 	EMASK_FBD_M11|EMASK_FBD_M10|EMASK_FBD_M9|EMASK_FBD_M8|EMASK_FBD_M7| \
 	EMASK_FBD_M6|EMASK_FBD_M5|EMASK_FBD_M4)
+#define	EMASK_7300_FBD_FATAL	(EMASK_FBD_M23|EMASK_FBD_M3|EMASK_FBD_M2| \
+	EMASK_FBD_M1)
+#define	EMASK_7300_FBD_NF	EMASK_FBD_NF
 
 /* FERR_NF_MEM: MC First non-fatal errors */
 #define	ERR_MEM_CH_SHIFT	28	/* channel index in nf_mem */
@@ -429,8 +432,10 @@
 #define	EMASK_INT_B2	0x02	/* B2Msk Multi-tag hit SF */
 #define	EMASK_INT_B1	0x01	/* B1Msk DM parity error */
 
-/* MCH 5000 errata 2 */
+/* MCH 5000 errata 2: disable B1 */
 #define	EMASK_INT_5000	EMASK_INT_B1
+/* MCH 5100: mask all except B3 and B5 */
+#define	EMASK_INT_5100	(~(EMASK_INT_B5|EMASK_INT_B3) & 0xff)
 /* MCH 7300 errata 17 & 20 */
 #define	EMASK_INT_7300	(EMASK_INT_B3|EMASK_INT_B1)
 /* MCH 7300 errata 17,20 & 21 */
@@ -440,6 +445,9 @@
 #define	EMASK_INT_FATAL (EMASK_INT_B7|EMASK_INT_B4|EMASK_INT_B3|EMASK_INT_B2| \
 	EMASK_INT_B1)
 #define	EMASK_INT_NF	(EMASK_INT_B8|EMASK_INT_B6|EMASK_INT_B5)
+#define	EMASK_INT_5100_FATAL	(EMASK_INT_B3|EMASK_INT_B1)
+#define	EMASK_INT_5100_NF	(EMASK_INT_B5)
+
 #define	GE_FBD_FATAL ((nb_chipset == INTEL_NB_5400) ? GE_FERR_FBD_FATAL : \
 	(nb_chipset == INTEL_NB_5100) ? 0 : \
 	(GE_FERR_FBD0_FATAL|GE_FERR_FBD1_FATAL|GE_FERR_FBD2_FATAL| \
--- a/usr/src/uts/intel/io/intel_nb5000/nb5000_init.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/intel/io/intel_nb5000/nb5000_init.c	Mon Oct 05 09:17:26 2009 -0400
@@ -120,6 +120,8 @@
 uint_t nb5000_mask_bios_fbd = EMASK_FBD_FATAL;
 uint_t nb5400_mask_poll_fbd = EMASK_5400_FBD_NF;
 uint_t nb5400_mask_bios_fbd = EMASK_5400_FBD_FATAL;
+uint_t nb7300_mask_poll_fbd = EMASK_7300_FBD_NF;
+uint_t nb7300_mask_bios_fbd = EMASK_7300_FBD_FATAL;
 
 int nb5100_reset_emask_mem = 1;
 uint_t nb5100_mask_poll_mem = EMASK_MEM_NF;
@@ -129,6 +131,7 @@
 uint_t nb5000_mask_poll_fsb = EMASK_FSB_NF;
 uint_t nb5000_mask_bios_fsb = EMASK_FSB_FATAL;
 
+uint_t nb5100_emask_int = EMASK_INT_5100;
 uint_t nb5400_emask_int = EMASK_INT_5400;
 
 uint_t nb7300_emask_int = EMASK_INT_7300;
@@ -137,6 +140,8 @@
 int nb5000_reset_emask_int = 1;
 uint_t nb5000_mask_poll_int = EMASK_INT_NF;
 uint_t nb5000_mask_bios_int = EMASK_INT_FATAL;
+uint_t nb5100_mask_poll_int = EMASK_INT_5100_NF;
+uint_t nb5100_mask_bios_int = EMASK_INT_5100_FATAL;
 
 uint_t nb_mask_poll_thr = EMASK_THR_NF;
 uint_t nb_mask_bios_thr = EMASK_THR_FATAL;
@@ -970,8 +975,17 @@
 	uint32_t err2_int;
 	uint32_t mcerr_int;
 	uint32_t emask_int;
+	uint32_t nb_mask_bios_int;
+	uint32_t nb_mask_poll_int;
 	uint16_t stepping;
 
+	if (nb_chipset == INTEL_NB_5100) {
+		nb_mask_bios_int = nb5100_mask_bios_int;
+		nb_mask_poll_int = nb5100_mask_poll_int;
+	} else {
+		nb_mask_bios_int = nb5000_mask_bios_int;
+		nb_mask_poll_int = nb5000_mask_poll_int;
+	}
 	err0_int = ERR0_INT_RD();
 	err1_int = ERR1_INT_RD();
 	err2_int = ERR2_INT_RD();
@@ -990,12 +1004,12 @@
 	MCERR_INT_WR(ERR_INT_ALL);
 	EMASK_INT_WR(ERR_INT_ALL);
 
-	mcerr_int &= ~nb5000_mask_bios_int;
-	mcerr_int |= nb5000_mask_bios_int & (~err0_int | ~err1_int | ~err2_int);
-	mcerr_int |= nb5000_mask_poll_int;
-	err0_int |= nb5000_mask_poll_int;
-	err1_int |= nb5000_mask_poll_int;
-	err2_int |= nb5000_mask_poll_int;
+	mcerr_int &= ~nb_mask_bios_int;
+	mcerr_int |= nb_mask_bios_int & (~err0_int | ~err1_int | ~err2_int);
+	mcerr_int |= nb_mask_poll_int;
+	err0_int |= nb_mask_poll_int;
+	err1_int |= nb_mask_poll_int;
+	err2_int |= nb_mask_poll_int;
 
 	l_mcerr_int = mcerr_int;
 	ERR0_INT_WR(err0_int);
@@ -1012,6 +1026,8 @@
 		} else if (nb_chipset == INTEL_NB_5400) {
 			EMASK_5400_INT_WR(nb5400_emask_int |
 			    (emask_int & EMASK_INT_RES));
+		} else if (nb_chipset == INTEL_NB_5100) {
+			EMASK_5000_INT_WR(nb5100_emask_int);
 		} else {
 			EMASK_5000_INT_WR(nb5000_emask_int);
 		}
@@ -1077,11 +1093,16 @@
 	MCERR_FBD_WR(0xffffffff);
 	EMASK_FBD_WR(0xffffffff);
 
-	if (nb_chipset == INTEL_NB_7300 && nb_mode == NB_MEMORY_MIRROR) {
-		/* MCH 7300 errata 34 */
-		emask_bios_fbd = nb5000_mask_bios_fbd & ~EMASK_FBD_M23;
-		emask_poll_fbd = nb5000_mask_poll_fbd;
-		mcerr_fbd |= EMASK_FBD_M23;
+	if (nb_chipset == INTEL_NB_7300) {
+		if (nb_mode == NB_MEMORY_MIRROR) {
+			/* MCH 7300 errata 34 */
+			emask_bios_fbd = nb7300_mask_bios_fbd & ~EMASK_FBD_M23;
+			emask_poll_fbd = nb7300_mask_poll_fbd;
+			mcerr_fbd |= EMASK_FBD_M23;
+		} else {
+			emask_bios_fbd = nb7300_mask_bios_fbd;
+			emask_poll_fbd = nb7300_mask_poll_fbd;
+		}
 	} else if (nb_chipset == INTEL_NB_5400) {
 		emask_bios_fbd = nb5400_mask_bios_fbd;
 		emask_poll_fbd = nb5400_mask_poll_fbd;
--- a/usr/src/uts/intel/os/driver_aliases	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/intel/os/driver_aliases	Mon Oct 05 09:17:26 2009 -0400
@@ -54,3 +54,5 @@
 isa "pciclass,060100"
 hme "pci108e,1001"
 acpinex "acpivirtnex"
+nulldriver "scsa,probe"
+nulldriver "scsa,nodev"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/pmcs/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,97 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+#	This makefile drives the production of the pmcs driver kernel module.
+#
+#	intel architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../../../../src/uts
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= pmcs
+OBJECTS		= $(PMCS_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(PMCS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/io/scsi/adapters/pmcs
+WARLOCK_OUT     = $(PMCS_OBJS:%.o=%.ll)
+WARLOCK_OK      = $(MODULE).ok
+WLCMD_DIR       = $(UTSBASE)/common/io/warlock
+
+#
+#	Kernel Module Dependencies
+#
+LDFLAGS += -dy -Nmisc/scsi
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY) $(CONFMOD)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+include $(CONF_SRCDIR)/pmcs8001fw.version
+
+#
+# Add additional flags
+#
+PMCS_DRV_FLGS	= -DMODNAME=\"${MODULE}\"
+CPPFLAGS	+= $(PMCS_DRV_FLGS) \
+	-DPMCS_FIRMWARE_VERSION=${PMCS_FW_VERSION} \
+	-DPMCS_FIRMWARE_VERSION_STRING=\"${PMCS_FW_VERSION_STRING}\"
+
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+all:		$(ALL_DEPS)
+
+def:		$(DEF_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- a/usr/src/uts/sfmmu/vm/hat_sfmmu.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/sfmmu/vm/hat_sfmmu.c	Mon Oct 05 09:17:26 2009 -0400
@@ -8505,6 +8505,7 @@
 	iment->iment_next = NULL;
 	iment->iment_prev = NULL;
 	iment->iment_hat =  NULL;
+	iment->iment_base_va = 0;
 }
 
 /*
@@ -14959,8 +14960,6 @@
 				ASSERT(ism_ment->iment_hat == scdp->scd_sfmmup);
 				ASSERT(ism_ment->iment_base_va ==
 				    rgnp->rgn_saddr);
-				ism_ment->iment_hat = NULL;
-				ism_ment->iment_base_va = 0;
 				mutex_enter(&ism_mlist_lock);
 				iment_sub(ism_ment, ism_hatid);
 				mutex_exit(&ism_mlist_lock);
--- a/usr/src/uts/sparc/Makefile.sparc.shared	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/sparc/Makefile.sparc.shared	Mon Oct 05 09:17:26 2009 -0400
@@ -320,6 +320,7 @@
 DRV_KMODS	+= iscsi
 DRV_KMODS	+= emlxs
 DRV_KMODS	+= srpt
+DRV_KMODS	+= pmcs
 
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= ifp
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= uata
--- a/usr/src/uts/sparc/os/driver_aliases	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/sparc/os/driver_aliases	Mon Oct 05 09:17:26 2009 -0400
@@ -168,3 +168,5 @@
 ncp "SUNW,vf-mau"
 n2rng "SUNW,n2-rng"
 n2rng "SUNW,vf-rng"
+nulldriver "scsa,probe"
+nulldriver "scsa,nodev"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/pmcs/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -0,0 +1,97 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+#	This makefile drives the production of the pmcs driver kernel module.
+#
+#	SPARC architecture dependent
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../../../../src/uts
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= pmcs
+OBJECTS		= $(PMCS_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(PMCS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/io/scsi/adapters/pmcs
+WARLOCK_OUT     = $(PMCS_OBJS:%.o=%.ll)
+WARLOCK_OK      = $(MODULE).ok
+WLCMD_DIR       = $(UTSBASE)/common/io/warlock
+
+#
+#	Kernel Module Dependencies
+#
+LDFLAGS += -dy -Nmisc/scsi
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY) $(CONFMOD)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+include $(CONF_SRCDIR)/pmcs8001fw.version
+
+#
+# Add additional flags
+#
+PMCS_DRV_FLGS	= -DMODNAME=\"${MODULE}\" -DDISABLE_MSIX
+CPPFLAGS	+= $(PMCS_DRV_FLGS) \
+	-DPMCS_FIRMWARE_VERSION=${PMCS_FW_VERSION} \
+	-DPMCS_FIRMWARE_VERSION_STRING=\"${PMCS_FW_VERSION_STRING}\"
+
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+all:		$(ALL_DEPS)
+
+def:		$(DEF_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
--- a/usr/src/uts/sparc/warlock/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/sparc/warlock/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -52,7 +52,7 @@
 #	lock_lint rules
 #
 all:	warlock warlock.1394 warlock.ecpp warlock.scsi \
-	warlock.smartcard warlock.usb warlock.ib warlock.sata warlock.wc
+	warlock.usb warlock.ib warlock.sata warlock.wc
 
 warlock: $(MODULE).ok 
 
@@ -109,9 +109,6 @@
 warlock.ecpp:
 	@cd ../ecpp; $(MAKE) clean; $(MAKE) warlock
 
-warlock.smartcard:
-$(CLOSED_BUILD)	@cd $(CLOSED)/uts/sun4u/scmi2c; $(MAKE) clean; $(MAKE) warlock
-
 warlock.ib:
 	@cd ../ibmf; $(MAKE) clean; $(MAKE) warlock
 	@cd ../ib; $(MAKE) clean; $(MAKE) warlock
--- a/usr/src/uts/sun4/io/efcode/fcode.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/sun4/io/efcode/fcode.c	Mon Oct 05 09:17:26 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -838,6 +838,15 @@
 	st = fc_states + m;
 	ASSERT(m < fc_max_opens && FC_STATE_ACTIVE(st->state));
 
+	/*
+	 * It's an error if we're not in state FC_STATE_IN_PROGRESS
+	 */
+	if (st->state != FC_STATE_IN_PROGRESS) {
+		cmn_err(CE_CONT, "fc_ioctl: fc_get_fcode: wrong state (%d)\n",
+		    st->state);
+		return (EINVAL);
+	}
+
 	ASSERT(st->req != NULL);
 	rp = st->req->handle;
 
@@ -892,6 +901,16 @@
 	st = fc_states + m;
 	ASSERT(m < fc_max_opens && FC_STATE_ACTIVE(st->state));
 
+	/*
+	 * It's an error if we're not in state FC_STATE_IN_PROGRESS.
+	 */
+	if (st->state != FC_STATE_IN_PROGRESS) {
+		cmn_err(CE_CONT,
+		    "fc_ioctl:fc_set_fcode_error: wrong state (%d)\n",
+		    st->state);
+		return (EINVAL);
+	}
+
 	ASSERT(st->req != NULL);
 	fp = st->req;
 
--- a/usr/src/uts/sun4/os/lgrpplat.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/sun4/os/lgrpplat.c	Mon Oct 05 09:17:26 2009 -0400
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -19,14 +18,12 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-
 #include <sys/cpuvar.h>
 #include <sys/lgrp.h>
 #include <sys/memnode.h>
@@ -100,34 +97,35 @@
 lgrp_handle_t lgrp_default_handle = LGRP_DEFAULT_HANDLE;
 
 void
-lgrp_plat_init(void)
+lgrp_plat_init(lgrp_init_stages_t stage)
 {
 	int i;
 
-	/*
-	 * Initialize lookup tables to invalid values so we catch
-	 * any illegal use of them.
-	 */
-	for (i = 0; i < MAX_MEM_NODES; i++) {
-		memnode_to_lgrphand[i] = -1;
-		lgrphand_to_memnode[i] = -1;
-	}
+	switch (stage) {
+	case LGRP_INIT_STAGE1:
+		/*
+		 * Initialize lookup tables to invalid values so we catch
+		 * any illegal use of them.
+		 */
+		for (i = 0; i < MAX_MEM_NODES; i++) {
+			memnode_to_lgrphand[i] = -1;
+			lgrphand_to_memnode[i] = -1;
+		}
 
-	if (lgrp_topo_ht_limit() == 1) {
-		max_mem_nodes = 1;
-		return;
-	}
-
-	if (&plat_lgrp_cpu_to_hand)
-		max_mem_nodes = MAX_MEM_NODES;
+		if (lgrp_topo_ht_limit() == 1) {
+			max_mem_nodes = 1;
+			return;
+		}
 
-	if (&plat_lgrp_init)
-		plat_lgrp_init();
-}
+		if (&plat_lgrp_cpu_to_hand)
+			max_mem_nodes = MAX_MEM_NODES;
 
-void
-lgrp_plat_main_init(void)
-{
+		if (&plat_lgrp_init)
+			plat_lgrp_init();
+		break;
+	default:
+		break;
+	}
 }
 
 /* ARGSUSED */
@@ -352,11 +350,3 @@
 		return (NULL);
 	return (lgrp);
 }
-
-/*
- * Probe memory in each node from current CPU to determine latency topology
- */
-void
-lgrp_plat_probe(void)
-{
-}
--- a/usr/src/uts/sun4/os/mlsetup.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/sun4/os/mlsetup.c	Mon Oct 05 09:17:26 2009 -0400
@@ -273,7 +273,7 @@
 	 * lgroup framework initialization. This must be done prior
 	 * to devices being mapped.
 	 */
-	lgrp_init();
+	lgrp_init(LGRP_INIT_STAGE1);
 
 	cpu_setup();
 
--- a/usr/src/uts/sun4u/Makefile.sun4u.shared	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/sun4u/Makefile.sun4u.shared	Mon Oct 05 09:17:26 2009 -0400
@@ -20,7 +20,7 @@
 #
 
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #	This makefile contains the common definitions for the sun4u unix
@@ -401,7 +401,6 @@
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= m1535ppm
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= memtest
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= mi2cv
-$(CLOSED_BUILD)CLOSED_DRV_KMODS	+= scmi2c
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= smbus_ara
 
 #
--- a/usr/src/uts/sun4u/io/pci/pci.c	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/sun4u/io/pci/pci.c	Mon Oct 05 09:17:26 2009 -0400
@@ -340,14 +340,6 @@
 		if (pcitool_init(dip) != DDI_SUCCESS) {
 			goto err_bad_pcitool_nodes;
 		}
-
-		/*
-		 * Due to unresolved hardware issues, disable PCIPM until
-		 * the problem is fully understood.
-		 *
-		 * pci_pwr_setup(pci_p, dip);
-		 */
-
 		ddi_report_dev(dip);
 
 		pci_p->pci_state = PCI_ATTACHED;
--- a/usr/src/uts/sun4u/sys/Makefile	Mon Sep 28 10:38:31 2009 -0400
+++ b/usr/src/uts/sun4u/sys/Makefile	Mon Oct 05 09:17:26 2009 -0400
@@ -110,8 +110,6 @@
 	memtestio_sr.h		\
 	memtestio_u.h
 
-$(CLOSED_BUILD)CLOSED_I2CHDRS =	clients/scmi2c.h
-
 I2CHDRS =	clients/max1617.h misc/i2c_svc.h clients/i2c_client.h	\
 		clients/hpc3130.h clients/lm75.h	\
 		clients/pcf8591.h clients/ssc050.h $(CLOSED_I2CHDRS)