changeset 906:1b5a12bcb15b

PSARC 2005/477 Broadcom 582[0125] support for dca crypto driver 6316121 Add DCA crypto driver to Solaris
author gm89044
date Mon, 14 Nov 2005 11:14:44 -0800
parents 920e9b2e0899
children 23d918baa212
files usr/src/cmd/fm/dicts/Makefile usr/src/cmd/fm/dicts/SCA1000.dict usr/src/cmd/fm/dicts/SCA1000.po usr/src/cmd/fm/dicts/SCA500.dict usr/src/cmd/fm/dicts/SCA500.po usr/src/cmd/fm/eversholt/files/common/sca1000.esc usr/src/cmd/fm/eversholt/files/common/sca500.esc usr/src/cmd/fm/eversholt/files/i386/Makefile usr/src/cmd/fm/eversholt/files/sparc/Makefile usr/src/pkgdefs/Makefile usr/src/pkgdefs/SUNW0on/prototype_com usr/src/pkgdefs/SUNWdcaf/Makefile usr/src/pkgdefs/SUNWdcaf/pkginfo.tmpl usr/src/pkgdefs/SUNWdcaf/prototype_com usr/src/pkgdefs/SUNWdcaf/prototype_i386 usr/src/pkgdefs/SUNWdcaf/prototype_sparc usr/src/pkgdefs/SUNWdcar/Makefile usr/src/pkgdefs/SUNWdcar/pkginfo.tmpl usr/src/pkgdefs/SUNWdcar/postinstall usr/src/pkgdefs/SUNWdcar/preremove usr/src/pkgdefs/SUNWdcar/prototype_com usr/src/pkgdefs/SUNWdcar/prototype_i386 usr/src/pkgdefs/SUNWdcar/prototype_sparc usr/src/uts/common/Makefile.files usr/src/uts/common/crypto/io/dca.c usr/src/uts/common/crypto/io/dca.conf usr/src/uts/common/crypto/io/dca_3des.c usr/src/uts/common/crypto/io/dca_debug.c usr/src/uts/common/crypto/io/dca_dsa.c usr/src/uts/common/crypto/io/dca_kstat.c usr/src/uts/common/crypto/io/dca_rng.c usr/src/uts/common/crypto/io/dca_rsa.c usr/src/uts/common/sys/crypto/dca.h usr/src/uts/intel/Makefile usr/src/uts/intel/dca/Makefile usr/src/uts/sparc/Makefile usr/src/uts/sparc/dca/Makefile
diffstat 37 files changed, 9963 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/fm/dicts/Makefile	Mon Nov 14 09:42:30 2005 -0800
+++ b/usr/src/cmd/fm/dicts/Makefile	Mon Nov 14 11:14:44 2005 -0800
@@ -32,7 +32,9 @@
 	SMF \
 	SUNOS \
 	PCI \
-	ZFS
+	ZFS \
+	SCA500 \
+	SCA1000
 
 sparc_DCNAMES = \
 	SUN4U \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/dicts/SCA1000.dict	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,32 @@
+#
+# 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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+# cmd/fm/SCA1000.dict
+#
+
+FMDICT: name=SCA1000 version=1 maxkey=1
+
+fault.io.sca1000.hw=0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/dicts/SCA1000.po	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,42 @@
+#
+# 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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+# cmd/fm/SCA1000.po
+#
+# code: SCA1000-8000-0V
+# keys: fault.io.sca1000.hw
+#
+msgid "SCA1000-8000-0V.type"
+msgstr "Fault"
+msgid "SCA1000-8000-0V.severity"
+msgstr "Major"
+msgid "SCA1000-8000-0V.description"
+msgstr "Hardware fault detected on Sun Crypto Accelerator 1000 card.  Refer to %s for more information."
+msgid "SCA1000-8000-0V.response"
+msgstr "The module has been disabled and un-registered as a hardware provider to the Solaris Cryptographic Framework."
+msgid "SCA1000-8000-0V.impact"
+msgstr "The card will no longer function as a cryptographic accelerator."
+msgid "SCA1000-8000-0V.action"
+msgstr "Verify hardware installation and run diagnostics on the card using sunvts.  Contact Sun for support."
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/dicts/SCA500.dict	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,32 @@
+#
+# 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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+#
+# cmd/fm/SCA500.dict
+#
+
+FMDICT: name=SCA500 version=1 maxkey=1
+
+fault.io.sca500.hw=0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/dicts/SCA500.po	Mon Nov 14 11:14:44 2005 -0800
@@ -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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+#
+# cmd/fm/SCA500.po
+#
+# code: SCA500-8000-0D
+# keys: fault.io.sca500.hw
+#
+msgid "SCA500-8000-0D.type"
+msgstr "Fault"
+msgid "SCA500-8000-0D.severity"
+msgstr "Major"
+msgid "SCA500-8000-0D.description"
+msgstr "Hardware fault detected on Sun Crypto Accelerator 500 card.  Refer to %s for more information."
+msgid "SCA500-8000-0D.response"
+msgstr "The module has been disabled and un-registered as a hardware provider to the Solaris Cryptographic Framework."
+msgid "SCA500-8000-0D.impact"
+msgstr "The card will no longer function as a cryptographic accelerator."
+msgid "SCA500-8000-0D.action"
+msgstr "Verify hardware installation and run diagnostics on the card using sunvts.  Contact Sun for support."
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/eversholt/files/common/sca1000.esc	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,80 @@
+/*
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * sca1000.esc: Deimos eversholt source code
+ */
+
+#pragma dictionary "SCA1000"
+
+/*
+ * For Deimos, there is one ASRU that includes both the driver and the card, 
+ * and there are two FRUs, the driver and the card.
+ */
+
+#define P pcibus/pcidev/pcifn
+
+asru P;
+fru P;
+
+/*
+ * The hardware card may generate faults.
+ * The driver passes the user data to the hardware and returns the hardware
+ * generated results to the user. It does check input and output data errors.
+ * However, it returns these errors to the calling application. Since the 
+ * driver itself does not do any data manipulation or generation, it does not
+ * have upsets.
+ */
+
+/*
+ * Declare faults
+ */
+event fault.io.sca1000.hw@P, FITrate=50, ASRU=P, FRU=P;
+
+/*
+ * Hardware faults lead to the following error
+ */
+event error.io.sca1000.hw.device@P;		/* DMA device errors */
+event error.io.sca1000.hw.timeout@P;		/* Device hang */
+
+/*
+ * Declare ereports
+ */
+event ereport.io.sca1000.hw.device@P;
+event ereport.io.sca1000.hw.timeout@P;
+
+/*
+ * Declear event propagations for faults
+ */
+prop fault.io.sca1000.hw@P(0)
+		-> error.io.sca1000.hw.device@P,
+		    error.io.sca1000.hw.timeout@P;
+
+prop error.io.sca1000.hw.device@P
+		-> ereport.io.sca1000.hw.device@P;
+prop error.io.sca1000.hw.timeout@P
+		-> ereport.io.sca1000.hw.timeout@P;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/fm/eversholt/files/common/sca500.esc	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,81 @@
+/*
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * sca500.esc: Deimos eversholt source code
+ */
+
+#pragma dictionary "SCA500"
+
+/*
+ * For Deimos, there is one ASRU that includes both the driver and the card, 
+ * and there are two FRUs, the driver and the card.
+ */
+
+#define P pcibus/pcidev/pcifn
+
+asru P;
+fru P;
+
+/*
+ * The hardware card may generate faults.
+ * The driver passes the user data to the hardware and returns the hardware
+ * generated results to the user. It does check input and output data errors.
+ * However, it returns these errors to the calling application. Since the 
+ * driver itself does not do any data manipulation or generation, it does not
+ * have upsets.
+ */
+
+/*
+ * Declare faults
+ */
+event fault.io.sca500.hw@P, FITrate=50, ASRU=P, FRU=P;
+
+/*
+ * Hardware faults lead to the following error
+ */
+event error.io.sca500.hw.device@P;		/* DMA device errors */
+event error.io.sca500.hw.timeout@P;		/* Device hang */
+
+/*
+ * Declare ereports
+ */
+event ereport.io.sca500.hw.device@P;
+event ereport.io.sca500.hw.timeout@P;
+
+/*
+ * Declear event propagations for faults
+ */
+prop fault.io.sca500.hw@P(0)
+		-> error.io.sca500.hw.device@P,
+		    error.io.sca500.hw.timeout@P;
+
+prop error.io.sca500.hw.device@P
+		-> ereport.io.sca500.hw.device@P;
+prop error.io.sca500.hw.timeout@P
+		-> ereport.io.sca500.hw.timeout@P;
--- a/usr/src/cmd/fm/eversholt/files/i386/Makefile	Mon Nov 14 09:42:30 2005 -0800
+++ b/usr/src/cmd/fm/eversholt/files/i386/Makefile	Mon Nov 14 11:14:44 2005 -0800
@@ -20,11 +20,11 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #ident	"%Z%%M%	%I%	%E% SMI"
 
-EFT_COMMON_FILES= pci.eft
+EFT_COMMON_FILES= pci.eft sca500.eft sca1000.eft
 
 include ../Makefile.com
--- a/usr/src/cmd/fm/eversholt/files/sparc/Makefile	Mon Nov 14 09:42:30 2005 -0800
+++ b/usr/src/cmd/fm/eversholt/files/sparc/Makefile	Mon Nov 14 11:14:44 2005 -0800
@@ -26,7 +26,7 @@
 #ident	"%Z%%M%	%I%	%E% SMI"
 
 SUBDIRS=sun4u sun4v SUNW,Sun-Fire-15000
-EFT_COMMON_FILES= pci.eft
+EFT_COMMON_FILES= pci.eft sca500.eft sca1000.eft
 
 include ../../../Makefile.subdirs
 
--- a/usr/src/pkgdefs/Makefile	Mon Nov 14 09:42:30 2005 -0800
+++ b/usr/src/pkgdefs/Makefile	Mon Nov 14 11:14:44 2005 -0800
@@ -339,6 +339,8 @@
 	SUNWcry \
 	SUNWcryr \
 	SUNWcryptoint \
+	SUNWdcaf \
+	SUNWdcar \
 	SUNWintgige \
 	SUNWkdcr \
 	SUNWkdcu \
--- a/usr/src/pkgdefs/SUNW0on/prototype_com	Mon Nov 14 09:42:30 2005 -0800
+++ b/usr/src/pkgdefs/SUNW0on/prototype_com	Mon Nov 14 11:14:44 2005 -0800
@@ -51,6 +51,8 @@
 f none usr/lib/locale/C/LC_MESSAGES/SUNOS.po             644 root bin
 f none usr/lib/locale/C/LC_MESSAGES/PCI.po               644 root bin
 f none usr/lib/locale/C/LC_MESSAGES/ZFS.po               644 root bin
+f none usr/lib/locale/C/LC_MESSAGES/SCA500.po            644 root bin
+f none usr/lib/locale/C/LC_MESSAGES/SCA1000.po           644 root bin
 f none usr/lib/locale/C/LC_MESSAGES/SUNW_OST_ADMIN.po    644 root sys
 f none usr/lib/locale/C/LC_MESSAGES/SUNW_OST_NETRPC.po   644 root sys
 f none usr/lib/locale/C/LC_MESSAGES/SUNW_OST_OSCMD.po    644 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWdcaf/Makefile	Mon Nov 14 11:14:44 2005 -0800
@@ -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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+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/SUNWdcaf/pkginfo.tmpl	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,56 @@
+#
+# 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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#pragma 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="SUNWdcaf"
+NAME="DCA Crypto Accelerator (usr)"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="usr"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="false"
+MAXINST="1000"
+CATEGORY="system"
+DESC="DCA Crypto Accelerator Supplemental Files (usr)"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+#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>"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWdcaf/prototype_com	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,60 @@
+#
+# 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 2005 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
+
+# packaging files
+i depend
+i pkginfo
+i copyright
+#
+# source locations relative to the prototype file
+#
+# SUNWdcaf
+#
+d none usr 755 root sys
+d none usr/lib 755 root bin
+d none usr/lib/fm 755 root bin
+d none usr/lib/fm/eft 755 root bin
+f none usr/lib/fm/eft/sca1000.eft 444 root bin
+f none usr/lib/fm/eft/sca500.eft 444 root bin
+d none usr/lib/fm/dict 755 root bin
+f none usr/lib/fm/dict/SCA1000.dict 444 root bin
+f none usr/lib/fm/dict/SCA500.dict 444 root bin
+d none usr/lib/locale 755 root bin
+d none usr/lib/locale/C 755 root bin
+d none usr/lib/locale/C/LC_MESSAGES 755 root bin
+f none usr/lib/locale/C/LC_MESSAGES/SCA1000.mo 444 root bin
+f none usr/lib/locale/C/LC_MESSAGES/SCA500.mo 444 root bin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWdcaf/prototype_i386	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,45 @@
+#
+# 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 2005 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
+#
+# SUNWdcaf
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWdcaf/prototype_sparc	Mon Nov 14 11:14:44 2005 -0800
@@ -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 2005 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
+#
+# SUNWdcaf
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWdcar/Makefile	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,39 @@
+#
+# 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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+#
+
+include ../Makefile.com
+
+DATAFILES += depend i.preserve
+
+.KEEP_STATE:
+
+all: $(FILES) postinstall preremove
+install: all pkg
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWdcar/pkginfo.tmpl	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,56 @@
+#
+# 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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#pragma 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="SUNWdcar"
+NAME="DCA Crypto Accelerator (Root)"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="root"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+MAXINST="1000"
+CATEGORY="system"
+DESC="DCA Crypto Accelerator driver"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none preserve"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+#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>"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWdcar/postinstall	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,117 @@
+#! /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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+#
+# install the Sun Crypto Accelerator 1000 device drivers.
+#
+
+PATH="/usr/bin:/usr/sbin:${PATH}"
+export PATH
+
+# Add hardware provider section for the dca driver
+# to /etc/crypto/kcf.conf
+
+pkg_start="# Start $PKGINST"
+pkg_end="# End $PKGINST"
+kcfconf=${BASEDIR}/etc/crypto/kcf.conf
+tmpfile=/tmp/$$kcfconf
+error=no
+
+#
+# If /etc/crypto/kcf.conf doesn't exist, bail immediately
+#
+if [ ! -f "$kcfconf" ]
+then
+	echo "$0: ERROR - $kcfconf doesn't exist"
+        exit 2
+fi
+
+#
+# If the package has been already installed, remove old entries
+#
+start=0
+end=0
+egrep -s "$pkg_start" $kcfconf && start=1
+egrep -s "$pkg_end" $kcfconf && end=1
+
+if [ $start -ne $end ] ; then
+	echo "$0: missing Start or End delimiters for $PKGINST in $kcfconf."
+	echo "$0: $kcfconf may be corrupted and was not updated."
+	error=yes
+	exit 2
+fi
+
+if [ $start -eq 1 ]
+then
+	sed -e "/$pkg_start/,/$pkg_end/d" $kcfconf > $tmpfile || error=yes
+else
+	cp $kcfconf $tmpfile || error=yes
+fi
+
+#
+# Append the delimiters for this package
+#
+echo "$pkg_start driver_names=dca" >> $tmpfile || error=yes
+echo "$pkg_end" >> $tmpfile || error=yes
+
+#
+# Install the updated config file and clean up the tmp file
+#
+if [ "$error" = no ]
+then
+	mv $tmpfile $kcfconf || error=yes
+fi
+rm -f $tmpfile
+
+#
+# All done, if any of the steps above fail, report the error
+#
+if [ "$error" = yes ]
+then
+	echo "$0: ERROR - failed to update $kcfconf."
+        exit 2
+fi
+
+NAMEMAJOR="${BASEDIR}/etc/name_to_major"
+
+if [ "${BASEDIR:=/}" = "/" ]
+then
+	ADD_DRV="/usr/sbin/add_drv"
+else
+	ADD_DRV="/usr/sbin/add_drv -b ${BASEDIR}"
+fi
+
+grep -w dca ${NAMEMAJOR} > /dev/null 2>&1
+if [ $? -ne 0 ]
+then
+    $ADD_DRV -i '"pci14e4,5820" "pci14e4,5821" "pci14e4,5822" "pci14e4,5825" "pci108e,5454" "pci108e,5455" "pci108e,5456"  "pci108e,5457"' \
+	dca || exit 1
+fi
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWdcar/preremove	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,105 @@
+#! /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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+#
+# Preremove script for SUNWdcar.
+#
+# This script removes the hardware provider section for the dca
+# driver from /etc/crypto/kcf.conf
+#
+DRV=dca
+
+NAMEMAJOR="${BASEDIR}/etc/name_to_major"
+
+#
+# Determine if we are on an alternate BASEDIR
+#
+if [ "${BASEDIR:=/}" = "/" ]
+then
+        REM_DRV="/usr/sbin/rem_drv"
+else
+        REM_DRV="/usr/sbin/rem_drv -b ${BASEDIR}"
+fi
+
+#
+# Remove the driver, but only if this has not already been done.
+#
+grep -w "${DRV}" ${NAMEMAJOR} > /dev/null 2>&1
+if [ $? -eq 0 ]; then
+    ${REM_DRV} ${DRV} || exit 1
+fi
+
+pkg_start="# Start $PKGINST"
+pkg_end="# End $PKGINST"
+kcfconf=${BASEDIR}/etc/crypto/kcf.conf
+tmpfile=/tmp/$$kcfconf
+error=no
+
+#
+# If /etc/crypto/kcf.conf doesn't exist, bail immediately
+#
+if [ ! -f "$kcfconf" ]
+then
+	echo "$0: ERROR - $kcfconf doesn't exist"
+	exit 2
+fi
+
+#
+# Strip all entries belonging to this package
+#
+start=0
+end=0
+egrep -s "$pkg_start" $kcfconf && start=1
+egrep -s "$pkg_end" $kcfconf && end=1
+
+if [ $start -ne $end ] ; then
+	echo "$0: missing Start or End delimiters for $PKGINST in $kcfconf."
+	echo "$0: $kcfconf may be corrupted and was not updated."
+	error=yes
+	exit 2
+fi
+
+if [ $start -eq 1 ]
+then
+	sed -e "/$pkg_start/,/$pkg_end/d" $kcfconf > $tmpfile || error=yes
+	if [ "$error" = no ]
+	then 
+		mv $tmpfile $kcfconf || error=yes
+	fi
+	rm -f $tmpfile
+else
+	echo "$0: WARNING - no entries to be removed from $kcfconf" 
+	exit 0
+fi
+
+if [ "$error" = yes ]
+then
+	echo "$0: ERROR - failed to update $kcfconf."
+	exit 2
+fi
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWdcar/prototype_com	Mon Nov 14 11:14:44 2005 -0800
@@ -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 2005 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
+
+# packaging files
+i depend
+i pkginfo
+i copyright
+i postinstall
+i preremove
+i i.preserve
+#
+# source locations relative to the prototype file
+#
+# SUNWdcar
+#
+d none kernel 755 root sys
+d none kernel/drv 755 root sys
+e preserve kernel/drv/dca.conf 644 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWdcar/prototype_i386	Mon Nov 14 11:14:44 2005 -0800
@@ -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 2005 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
+#
+# SUNWdcar
+#
+f none kernel/drv/dca 755 root sys
+d none kernel/drv/amd64 755 root sys
+f none kernel/drv/amd64/dca 755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWdcar/prototype_sparc	Mon Nov 14 11:14:44 2005 -0800
@@ -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 2005 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
+#
+# SUNWdcar
+#
+d none kernel/drv/sparcv9 755 root sys
+f none kernel/drv/sparcv9/dca 755 root sys
--- a/usr/src/uts/common/Makefile.files	Mon Nov 14 09:42:30 2005 -0800
+++ b/usr/src/uts/common/Makefile.files	Mon Nov 14 11:14:44 2005 -0800
@@ -1034,6 +1034,9 @@
 
 DPROV_OBJS +=	dprov.o
 
+DCA_OBJS +=	dca.o dca_3des.o dca_debug.o dca_dsa.o dca_kstat.o dca_rng.o \
+		dca_rsa.o
+
 AESPROV_OBJS +=	aes.o aes_cbc_crypt.o aes_impl.o 
 
 ARCFOURPROV_OBJS += arcfour.o arcfour_crypt.o
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/crypto/io/dca.c	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,4998 @@
+/*
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Deimos - cryptographic acceleration based upon Broadcom 582x.
+ */
+
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/cmn_err.h>
+#include <sys/varargs.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/kmem.h>
+#include <sys/ioccom.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/kstat.h>
+#include <sys/strsun.h>
+#include <sys/note.h>
+#include <sys/crypto/common.h>
+#include <sys/crypto/spi.h>
+#include <sys/ddifm.h>
+#include <sys/fm/protocol.h>
+#include <sys/fm/util.h>
+#include <sys/fm/io/ddi.h>
+#include <sys/crypto/dca.h>
+
+/*
+ * Core Deimos driver.
+ */
+
+static void		dca_enlist2(dca_listnode_t *, dca_listnode_t *,
+    kmutex_t *);
+static void		dca_rmlist2(dca_listnode_t *node, kmutex_t *);
+static dca_listnode_t	*dca_delist2(dca_listnode_t *q, kmutex_t *);
+static void		dca_free_context_list(dca_t *dca);
+static int		dca_free_context_low(crypto_ctx_t *ctx);
+static int		dca_attach(dev_info_t *, ddi_attach_cmd_t);
+static int		dca_detach(dev_info_t *, ddi_detach_cmd_t);
+static int		dca_suspend(dca_t *);
+static int		dca_resume(dca_t *);
+static int		dca_init(dca_t *);
+static int		dca_reset(dca_t *, int);
+static int		dca_initworklist(dca_t *, dca_worklist_t *);
+static void		dca_uninit(dca_t *);
+static void		dca_initq(dca_listnode_t *);
+static void		dca_enqueue(dca_listnode_t *, dca_listnode_t *);
+static dca_listnode_t	*dca_dequeue(dca_listnode_t *);
+static dca_listnode_t	*dca_unqueue(dca_listnode_t *);
+static dca_request_t	*dca_newreq(dca_t *);
+static dca_work_t	*dca_getwork(dca_t *, int);
+static void		dca_freework(dca_work_t *);
+static dca_work_t	*dca_newwork(dca_t *);
+static void		dca_destroywork(dca_work_t *);
+static void		dca_schedule(dca_t *, int);
+static void		dca_reclaim(dca_t *, int);
+static uint_t		dca_intr(char *);
+static void		dca_failure(dca_t *, ddi_fault_location_t,
+			    dca_fma_eclass_t index, uint64_t, int, char *, ...);
+static void		dca_jobtimeout(void *);
+static int		dca_drain(dca_t *);
+static void		dca_undrain(dca_t *);
+static void		dca_rejectjobs(dca_t *);
+
+#ifdef	SCHEDDELAY
+static void		dca_schedtimeout(void *);
+#endif
+
+/*
+ * We want these inlined for performance.
+ */
+#ifndef	DEBUG
+#pragma inline(dca_freereq, dca_getreq, dca_freework, dca_getwork)
+#pragma inline(dca_enqueue, dca_dequeue, dca_rmqueue, dca_done)
+#pragma inline(dca_reverse, dca_length)
+#endif
+
+/*
+ * Device operations.
+ */
+static struct dev_ops devops = {
+	DEVO_REV,		/* devo_rev */
+	0,			/* devo_refcnt */
+	nodev,			/* devo_getinfo */
+	nulldev,		/* devo_identify */
+	nulldev,		/* devo_probe */
+	dca_attach,		/* devo_attach */
+	dca_detach,		/* devo_detach */
+	nodev,			/* devo_reset */
+	NULL,			/* devo_cb_ops */
+	NULL,			/* devo_bus_ops */
+	ddi_power		/* devo_power */
+};
+
+#define	IDENT		"PCI Crypto Accelerator 2.0"
+#define	IDENT_SYM	"Crypto Accel Sym 2.0"
+#define	IDENT_ASYM	"Crypto Accel Asym 2.0"
+
+/* Space-padded, will be filled in dynamically during registration */
+#define	IDENT3	"PCI Crypto Accelerator Mod 2.0"
+
+#define	VENDOR	"Sun Microsystems, Inc."
+
+#define	STALETIME	(30 * SECOND)
+
+#define	crypto_prov_notify	crypto_provider_notification
+		/* A 28 char function name doesn't leave much line space */
+
+/*
+ * Module linkage.
+ */
+static struct modldrv modldrv = {
+	&mod_driverops,		/* drv_modops */
+	IDENT,			/* drv_linkinfo */
+	&devops,		/* drv_dev_ops */
+};
+
+extern struct mod_ops mod_cryptoops;
+
+static struct modlcrypto modlcrypto = {
+	&mod_cryptoops,
+	IDENT3
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,		/* ml_rev */
+	&modldrv,		/* ml_linkage */
+	&modlcrypto,
+	NULL
+};
+
+/*
+ * CSPI information (entry points, provider info, etc.)
+ */
+
+/* Mechanisms for the symmetric cipher provider */
+static crypto_mech_info_t dca_mech_info_tab1[] = {
+	/* DES-CBC */
+	{SUN_CKM_DES_CBC, DES_CBC_MECH_INFO_TYPE,
+	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT |
+	    CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC,
+	    DES_KEY_LEN, DES_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES},
+	/* 3DES-CBC */
+	{SUN_CKM_DES3_CBC, DES3_CBC_MECH_INFO_TYPE,
+	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT |
+	    CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC,
+	    DES3_KEY_LEN, DES3_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES}
+};
+
+/* Mechanisms for the asymmetric cipher provider */
+static crypto_mech_info_t dca_mech_info_tab2[] = {
+	/* DSA */
+	{SUN_CKM_DSA, DSA_MECH_INFO_TYPE,
+	    CRYPTO_FG_SIGN | CRYPTO_FG_VERIFY |
+	    CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_VERIFY_ATOMIC,
+	    DSA_MIN_KEY_LEN * 8, DSA_MAX_KEY_LEN * 8,
+	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
+
+	/* RSA */
+	{SUN_CKM_RSA_X_509, RSA_X_509_MECH_INFO_TYPE,
+	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_SIGN |
+	    CRYPTO_FG_SIGN_RECOVER | CRYPTO_FG_VERIFY |
+	    CRYPTO_FG_VERIFY_RECOVER |
+	    CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC |
+	    CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_SIGN_RECOVER_ATOMIC |
+	    CRYPTO_FG_VERIFY_ATOMIC | CRYPTO_FG_VERIFY_RECOVER_ATOMIC,
+	    RSA_MIN_KEY_LEN * 8, RSA_MAX_KEY_LEN * 8,
+	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
+	{SUN_CKM_RSA_PKCS, RSA_PKCS_MECH_INFO_TYPE,
+	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_SIGN |
+	    CRYPTO_FG_SIGN_RECOVER | CRYPTO_FG_VERIFY |
+	    CRYPTO_FG_VERIFY_RECOVER |
+	    CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC |
+	    CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_SIGN_RECOVER_ATOMIC |
+	    CRYPTO_FG_VERIFY_ATOMIC | CRYPTO_FG_VERIFY_RECOVER_ATOMIC,
+	    RSA_MIN_KEY_LEN * 8, RSA_MAX_KEY_LEN * 8,
+	    CRYPTO_KEYSIZE_UNIT_IN_BITS}
+};
+
+static void dca_provider_status(crypto_provider_handle_t, uint_t *);
+
+static crypto_control_ops_t dca_control_ops = {
+	dca_provider_status
+};
+
+static int dca_encrypt_init(crypto_ctx_t *, crypto_mechanism_t *,
+    crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
+static int dca_encrypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
+    crypto_req_handle_t);
+static int dca_encrypt_update(crypto_ctx_t *, crypto_data_t *,
+    crypto_data_t *, crypto_req_handle_t);
+static int dca_encrypt_final(crypto_ctx_t *, crypto_data_t *,
+    crypto_req_handle_t);
+static int dca_encrypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
+    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
+    crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
+
+static int dca_decrypt_init(crypto_ctx_t *, crypto_mechanism_t *,
+    crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
+static int dca_decrypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
+    crypto_req_handle_t);
+static int dca_decrypt_update(crypto_ctx_t *, crypto_data_t *,
+    crypto_data_t *, crypto_req_handle_t);
+static int dca_decrypt_final(crypto_ctx_t *, crypto_data_t *,
+    crypto_req_handle_t);
+static int dca_decrypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
+    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
+    crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
+
+static crypto_cipher_ops_t dca_cipher_ops = {
+	dca_encrypt_init,
+	dca_encrypt,
+	dca_encrypt_update,
+	dca_encrypt_final,
+	dca_encrypt_atomic,
+	dca_decrypt_init,
+	dca_decrypt,
+	dca_decrypt_update,
+	dca_decrypt_final,
+	dca_decrypt_atomic
+};
+
+static int dca_sign_init(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *,
+    crypto_spi_ctx_template_t, crypto_req_handle_t);
+static int dca_sign(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
+    crypto_req_handle_t);
+static int dca_sign_update(crypto_ctx_t *, crypto_data_t *,
+    crypto_req_handle_t);
+static int dca_sign_final(crypto_ctx_t *, crypto_data_t *,
+    crypto_req_handle_t);
+static int dca_sign_atomic(crypto_provider_handle_t, crypto_session_id_t,
+    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
+    crypto_spi_ctx_template_t, crypto_req_handle_t);
+static int dca_sign_recover_init(crypto_ctx_t *, crypto_mechanism_t *,
+    crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
+static int dca_sign_recover(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
+    crypto_req_handle_t);
+static int dca_sign_recover_atomic(crypto_provider_handle_t,
+    crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
+    crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
+
+static crypto_sign_ops_t dca_sign_ops = {
+	dca_sign_init,
+	dca_sign,
+	dca_sign_update,
+	dca_sign_final,
+	dca_sign_atomic,
+	dca_sign_recover_init,
+	dca_sign_recover,
+	dca_sign_recover_atomic
+};
+
+static int dca_verify_init(crypto_ctx_t *, crypto_mechanism_t *,
+    crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
+static int dca_verify(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
+    crypto_req_handle_t);
+static int dca_verify_update(crypto_ctx_t *, crypto_data_t *,
+    crypto_req_handle_t);
+static int dca_verify_final(crypto_ctx_t *, crypto_data_t *,
+    crypto_req_handle_t);
+static int dca_verify_atomic(crypto_provider_handle_t, crypto_session_id_t,
+    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
+    crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
+static int dca_verify_recover_init(crypto_ctx_t *, crypto_mechanism_t *,
+    crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
+static int dca_verify_recover(crypto_ctx_t *, crypto_data_t *,
+    crypto_data_t *, crypto_req_handle_t);
+static int dca_verify_recover_atomic(crypto_provider_handle_t,
+    crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
+    crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
+
+static crypto_verify_ops_t dca_verify_ops = {
+	dca_verify_init,
+	dca_verify,
+	dca_verify_update,
+	dca_verify_final,
+	dca_verify_atomic,
+	dca_verify_recover_init,
+	dca_verify_recover,
+	dca_verify_recover_atomic
+};
+
+static int dca_generate_random(crypto_provider_handle_t, crypto_session_id_t,
+    uchar_t *, size_t, crypto_req_handle_t);
+
+static crypto_random_number_ops_t dca_random_number_ops = {
+	NULL,
+	dca_generate_random
+};
+
+static int ext_info_sym(crypto_provider_handle_t prov,
+    crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq);
+static int ext_info_asym(crypto_provider_handle_t prov,
+    crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq);
+static int ext_info_base(crypto_provider_handle_t prov,
+    crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq, char *id);
+
+static crypto_provider_management_ops_t dca_provmanage_ops_1 = {
+	ext_info_sym,		/* ext_info */
+	NULL,			/* init_token */
+	NULL,			/* init_pin */
+	NULL			/* set_pin */
+};
+
+static crypto_provider_management_ops_t dca_provmanage_ops_2 = {
+	ext_info_asym,		/* ext_info */
+	NULL,			/* init_token */
+	NULL,			/* init_pin */
+	NULL			/* set_pin */
+};
+
+int dca_free_context(crypto_ctx_t *);
+
+static crypto_ctx_ops_t dca_ctx_ops = {
+	NULL,
+	dca_free_context
+};
+
+/* Operations for the symmetric cipher provider */
+static crypto_ops_t dca_crypto_ops1 = {
+	&dca_control_ops,
+	NULL,				/* digest_ops */
+	&dca_cipher_ops,
+	NULL,				/* mac_ops */
+	NULL,				/* sign_ops */
+	NULL,				/* verify_ops */
+	NULL,				/* dual_ops */
+	NULL,				/* cipher_mac_ops */
+	NULL,				/* random_number_ops */
+	NULL,				/* session_ops */
+	NULL,				/* object_ops */
+	NULL,				/* key_ops */
+	&dca_provmanage_ops_1,		/* management_ops */
+	&dca_ctx_ops
+};
+
+/* Operations for the asymmetric cipher provider */
+static crypto_ops_t dca_crypto_ops2 = {
+	&dca_control_ops,
+	NULL,				/* digest_ops */
+	&dca_cipher_ops,
+	NULL,				/* mac_ops */
+	&dca_sign_ops,
+	&dca_verify_ops,
+	NULL,				/* dual_ops */
+	NULL,				/* cipher_mac_ops */
+	&dca_random_number_ops,
+	NULL,				/* session_ops */
+	NULL,				/* object_ops */
+	NULL,				/* key_ops */
+	&dca_provmanage_ops_2,		/* management_ops */
+	&dca_ctx_ops
+};
+
+/* Provider information for the symmetric cipher provider */
+static crypto_provider_info_t dca_prov_info1 = {
+	CRYPTO_SPI_VERSION_1,
+	NULL,				/* pi_provider_description */
+	CRYPTO_HW_PROVIDER,
+	NULL,				/* pi_provider_dev */
+	NULL,				/* pi_provider_handle */
+	&dca_crypto_ops1,
+	sizeof (dca_mech_info_tab1)/sizeof (crypto_mech_info_t),
+	dca_mech_info_tab1,
+	0,				/* pi_logical_provider_count */
+	NULL				/* pi_logical_providers */
+};
+
+/* Provider information for the asymmetric cipher provider */
+static crypto_provider_info_t dca_prov_info2 = {
+	CRYPTO_SPI_VERSION_1,
+	NULL,				/* pi_provider_description */
+	CRYPTO_HW_PROVIDER,
+	NULL,				/* pi_provider_dev */
+	NULL,				/* pi_provider_handle */
+	&dca_crypto_ops2,
+	sizeof (dca_mech_info_tab2)/sizeof (crypto_mech_info_t),
+	dca_mech_info_tab2,
+	0,				/* pi_logical_provider_count */
+	NULL				/* pi_logical_providers */
+};
+
+/* Convenience macros */
+/* Retrieve the softc and instance number from a SPI crypto context */
+#define	DCA_SOFTC_FROM_CTX(ctx, softc, instance) {		\
+	(softc) = (dca_t *)(ctx)->cc_provider;			\
+	(instance) = ddi_get_instance((softc)->dca_dip);	\
+}
+
+#define	DCA_MECH_FROM_CTX(ctx) \
+	(((dca_request_t *)(ctx)->cc_provider_private)->dr_ctx.ctx_cm_type)
+
+static int dca_bindchains_one(dca_request_t *reqp, size_t cnt, int dr_offset,
+    caddr_t kaddr, ddi_dma_handle_t handle, uint_t flags,
+    dca_chain_t *head, int *n_chain);
+static uint64_t dca_ena(uint64_t ena);
+static caddr_t dca_bufdaddr_out(crypto_data_t *data);
+static char *dca_fma_eclass_string(char *model, dca_fma_eclass_t index);
+static int dca_check_acc_handle(dca_t *dca, ddi_acc_handle_t handle,
+    dca_fma_eclass_t eclass_index);
+
+static void dca_fma_init(dca_t *dca);
+static void dca_fma_fini(dca_t *dca);
+static int dca_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err,
+    const void *impl_data);
+
+
+static dca_device_t dca_devices[] = {
+	/* Broadcom vanilla variants */
+	{	0x14e4, 0x5820, "Broadcom 5820" },
+	{	0x14e4, 0x5821, "Broadcom 5821" },
+	{	0x14e4, 0x5822, "Broadcom 5822" },
+	{	0x14e4, 0x5825, "Broadcom 5825" },
+	/* Sun specific OEMd variants */
+	{	0x108e, 0x5454, "SCA" },
+	{	0x108e, 0x5455, "SCA 1000" },
+	{	0x108e, 0x5457, "SCA 500" },
+	/* subsysid should be 0x5457, but got 0x1 from HW. Assume both here. */
+	{	0x108e, 0x1, "SCA 500" },
+};
+
+/*
+ * Device attributes.
+ */
+static struct ddi_device_acc_attr dca_regsattr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_STRUCTURE_LE_ACC,
+	DDI_STRICTORDER_ACC,
+	DDI_FLAGERR_ACC
+};
+
+static struct ddi_device_acc_attr dca_devattr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_STRUCTURE_LE_ACC,
+	DDI_STRICTORDER_ACC,
+	DDI_FLAGERR_ACC
+};
+
+#if !defined(i386) && !defined(__i386)
+static struct ddi_device_acc_attr dca_bufattr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_NEVERSWAP_ACC,
+	DDI_STRICTORDER_ACC,
+	DDI_FLAGERR_ACC
+};
+#endif
+
+static struct ddi_dma_attr dca_dmaattr = {
+	DMA_ATTR_V0,		/* dma_attr_version */
+	0x0,			/* dma_attr_addr_lo */
+	0xffffffffUL,		/* dma_attr_addr_hi */
+	0x00ffffffUL,		/* dma_attr_count_max */
+	0x40,			/* dma_attr_align */
+	0x40,			/* dma_attr_burstsizes */
+	0x1,			/* dma_attr_minxfer */
+	0x00ffffffUL,		/* dma_attr_maxxfer */
+	0xffffffffUL,		/* dma_attr_seg */
+#if defined(i386) || defined(__i386) || defined(__amd64)
+	512,			/* dma_attr_sgllen */
+#else
+	1,			/* dma_attr_sgllen */
+#endif
+	1,			/* dma_attr_granular */
+	DDI_DMA_FLAGERR		/* dma_attr_flags */
+};
+
+static void	*dca_state = NULL;
+int	dca_mindma = 2500;
+
+/*
+ * FMA eclass string definitions. Note that these string arrays must be
+ * consistent with the dca_fma_eclass_t enum.
+ */
+static char *dca_fma_eclass_sca1000[] = {
+	"sca1000.hw.device",
+	"sca1000.hw.timeout",
+	"sca1000.none"
+};
+
+static char *dca_fma_eclass_sca500[] = {
+	"sca500.hw.device",
+	"sca500.hw.timeout",
+	"sca500.none"
+};
+
+/*
+ * DDI entry points.
+ */
+int
+_init(void)
+{
+	int rv;
+
+	DBG(NULL, DMOD, "dca: in _init");
+
+	if ((rv = ddi_soft_state_init(&dca_state, sizeof (dca_t), 1)) != 0) {
+		/* this should *never* happen! */
+		return (rv);
+	}
+
+	if ((rv = mod_install(&modlinkage)) != 0) {
+		/* cleanup here */
+		ddi_soft_state_fini(&dca_state);
+		return (rv);
+	}
+
+	return (0);
+}
+
+int
+_fini(void)
+{
+	int rv;
+
+	DBG(NULL, DMOD, "dca: in _fini");
+
+	if ((rv = mod_remove(&modlinkage)) == 0) {
+		/* cleanup here */
+		ddi_soft_state_fini(&dca_state);
+	}
+	return (rv);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	DBG(NULL, DMOD, "dca: in _info");
+
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+dca_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	ddi_acc_handle_t	pci;
+	int			instance;
+	ddi_iblock_cookie_t	ibc;
+	int			intr_added = 0;
+	dca_t			*dca;
+	ushort_t		venid;
+	ushort_t		devid;
+	ushort_t		revid;
+	ushort_t		subsysid;
+	ushort_t		subvenid;
+	int			i;
+	int			ret;
+	char			ID[64];
+	static char		*unknowndev = "Unknown device";
+
+#if DEBUG
+	/* these are only used for debugging */
+	ushort_t		pcicomm;
+	ushort_t		pcistat;
+	uchar_t			cachelinesz;
+	uchar_t			mingnt;
+	uchar_t			maxlat;
+	uchar_t			lattmr;
+#endif
+
+	instance = ddi_get_instance(dip);
+
+	DBG(NULL, DMOD, "dca: in dca_attach() for %d", instance);
+
+	switch (cmd) {
+	case DDI_RESUME:
+		if ((dca = (dca_t *)ddi_get_driver_private(dip)) == NULL) {
+			dca_diperror(dip, "no soft state in detach");
+			return (DDI_FAILURE);
+		}
+		/* assumption: we won't be DDI_DETACHed until we return */
+		return (dca_resume(dca));
+	case DDI_ATTACH:
+		break;
+	default:
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_slaveonly(dip) == DDI_SUCCESS) {
+		dca_diperror(dip, "slot does not support PCI bus-master");
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_intr_hilevel(dip, 0) != 0) {
+		dca_diperror(dip, "hilevel interrupts not supported");
+		return (DDI_FAILURE);
+	}
+
+	if (pci_config_setup(dip, &pci) != DDI_SUCCESS) {
+		dca_diperror(dip, "unable to setup PCI config handle");
+		return (DDI_FAILURE);
+	}
+
+	/* common PCI attributes */
+	venid = pci_config_get16(pci, PCI_VENID);
+	devid = pci_config_get16(pci, PCI_DEVID);
+	revid = pci_config_get8(pci, PCI_REVID);
+	subvenid = pci_config_get16(pci, PCI_SUBVENID);
+	subsysid = pci_config_get16(pci, PCI_SUBSYSID);
+
+	/*
+	 * Broadcom-specific timings.
+	 * We disable these timers/counters since they can cause
+	 * incorrect false failures when the bus is just a little
+	 * bit slow, or busy.
+	 */
+	pci_config_put8(pci, PCI_TRDYTO, 0);
+	pci_config_put8(pci, PCI_RETRIES, 0);
+
+	/* initialize PCI access settings */
+	pci_config_put16(pci, PCI_COMM, PCICOMM_SEE |
+	    PCICOMM_PEE | PCICOMM_BME | PCICOMM_MAE);
+
+	/* set up our PCI latency timer */
+	pci_config_put8(pci, PCI_LATTMR, 0x40);
+
+#if DEBUG
+	/* read registers (for debugging) */
+	pcicomm = pci_config_get16(pci, PCI_COMM);
+	pcistat = pci_config_get16(pci, PCI_STATUS);
+	cachelinesz = pci_config_get8(pci, PCI_CACHELINESZ);
+	mingnt = pci_config_get8(pci, PCI_MINGNT);
+	maxlat = pci_config_get8(pci, PCI_MAXLAT);
+	lattmr = pci_config_get8(pci, PCI_LATTMR);
+#endif
+
+	pci_config_teardown(&pci);
+
+	if (ddi_get_iblock_cookie(dip, 0, &ibc) != DDI_SUCCESS) {
+		dca_diperror(dip, "unable to get iblock cookie");
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_soft_state_zalloc(dca_state, instance) != DDI_SUCCESS) {
+		dca_diperror(dip, "unable to allocate soft state");
+		return (DDI_FAILURE);
+	}
+
+	dca = ddi_get_soft_state(dca_state, instance);
+	ASSERT(dca != NULL);
+	dca->dca_dip = dip;
+	WORKLIST(dca, MCR1)->dwl_prov = NULL;
+	WORKLIST(dca, MCR2)->dwl_prov = NULL;
+	/* figure pagesize */
+	dca->dca_pagesize = ddi_ptob(dip, 1);
+
+	/*
+	 * Search for the device in our supported devices table.  This
+	 * is here for two reasons.  First, we want to ensure that
+	 * only Sun-qualified (and presumably Sun-labeled) devices can
+	 * be used with this driver.  Second, some devices have
+	 * specific differences.  E.g. the 5821 has support for a
+	 * special mode of RC4, deeper queues, power management, and
+	 * other changes.  Also, the export versions of some of these
+	 * chips don't support RC4 or 3DES, so we catch that here.
+	 *
+	 * Note that we only look at the upper nibble of the device
+	 * id, which is used to distinguish export vs. domestic
+	 * versions of the chip.  (The lower nibble is used for
+	 * stepping information.)
+	 */
+	for (i = 0; i < (sizeof (dca_devices) / sizeof (dca_device_t)); i++) {
+		/*
+		 * Try to match the subsystem information first.
+		 */
+		if (subvenid && (subvenid == dca_devices[i].dd_vendor_id) &&
+		    subsysid && (subsysid == dca_devices[i].dd_device_id)) {
+			dca->dca_model = dca_devices[i].dd_model;
+			break;
+		}
+		/*
+		 * Failing that, try the generic vendor and device id.
+		 * Even if we find a match, we keep searching anyway,
+		 * since we would prefer to find a match based on the
+		 * subsystem ids.
+		 */
+		if ((venid == dca_devices[i].dd_vendor_id) &&
+		    (devid == dca_devices[i].dd_device_id)) {
+			dca->dca_model = dca_devices[i].dd_model;
+		}
+	}
+	/* try and handle an unrecognized device */
+	if (dca->dca_model == NULL) {
+		dca->dca_model = unknowndev;
+		dca_error(dca, "device not recognized, not supported");
+		DBG(dca, DPCI, "i=%d venid=%x devid=%x rev=%d",
+		    i, venid, devid, revid);
+	}
+
+	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "description",
+	    dca->dca_model) != DDI_SUCCESS) {
+		dca_error(dca, "unable to create description property");
+		return (DDI_FAILURE);
+	}
+
+	DBG(dca, DPCI, "PCI command=0x%x status=%x cachelinesz=%x",
+	    pcicomm, pcistat, cachelinesz);
+	DBG(dca, DPCI, "mingnt=0x%x maxlat=0x%x lattmr=0x%x",
+	    mingnt, maxlat, lattmr);
+
+	/*
+	 * initialize locks, etc.
+	 */
+	(void) mutex_init(&dca->dca_intrlock, NULL, MUTEX_DRIVER, ibc);
+
+	/* use RNGSHA1 by default */
+	if (ddi_getprop(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "rngdirect", 0) == 0) {
+		dca->dca_flags |= DCA_RNGSHA1;
+	}
+
+	/* initialize FMA */
+	dca_fma_init(dca);
+
+	/* initialize some key data structures */
+	if (dca_init(dca) != DDI_SUCCESS) {
+		goto failed;
+	}
+
+	/* initialize kstats */
+	dca_ksinit(dca);
+
+	/* setup access to registers */
+	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&dca->dca_regs,
+	    0, 0, &dca_regsattr, &dca->dca_regs_handle) != DDI_SUCCESS) {
+		dca_error(dca, "unable to map registers");
+		goto failed;
+	}
+
+	DBG(dca, DCHATTY, "MCR1 = %x", GETCSR(dca, CSR_MCR1));
+	DBG(dca, DCHATTY, "CONTROL = %x", GETCSR(dca, CSR_DMACTL));
+	DBG(dca, DCHATTY, "STATUS = %x", GETCSR(dca, CSR_DMASTAT));
+	DBG(dca, DCHATTY, "DMAEA = %x", GETCSR(dca, CSR_DMAEA));
+	DBG(dca, DCHATTY, "MCR2 = %x", GETCSR(dca, CSR_MCR2));
+
+	/* reset the chip */
+	if (dca_reset(dca, 0) < 0) {
+		goto failed;
+	}
+
+	/* initialize the chip */
+	PUTCSR(dca, CSR_DMACTL, DMACTL_BE32 | DMACTL_BE64);
+	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
+	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+		goto failed;
+	}
+
+	/* add the interrupt */
+	if (ddi_add_intr(dip, 0, &dca->dca_icookie, NULL, dca_intr,
+	    (void *)dca) != DDI_SUCCESS) {
+		DBG(dca, DWARN, "ddi_add_intr failed");
+		goto failed;
+	} else {
+		intr_added = 1;
+	}
+
+	/* enable interrupts on the device */
+	/*
+	 * XXX: Note, 5820A1 errata indicates that this may clobber
+	 * bits 24 and 23, which affect the speed of the RNG.  Since
+	 * we always want to run in full-speed mode, this should be
+	 * harmless.
+	 */
+	SETBIT(dca, CSR_DMACTL, DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE);
+	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
+	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+		goto failed;
+	}
+
+	/* register MCR1 with the crypto framework */
+	/* Be careful not to exceed 32 chars */
+	(void) sprintf(ID, "%s/%d %s",
+	    ddi_driver_name(dip), ddi_get_instance(dip), IDENT_SYM);
+	dca_prov_info1.pi_provider_description = ID;
+	dca_prov_info1.pi_provider_dev.pd_hw = dip;
+	dca_prov_info1.pi_provider_handle = dca;
+	if ((ret = crypto_register_provider(&dca_prov_info1,
+	    &WORKLIST(dca, MCR1)->dwl_prov)) != CRYPTO_SUCCESS) {
+		cmn_err(CE_WARN,
+		    "crypto_register_provider() failed (%d) for MCR1", ret);
+		goto failed;
+	}
+
+	/* register MCR2 with the crypto framework */
+	/* Be careful not to exceed 32 chars */
+	(void) sprintf(ID, "%s/%d %s",
+	    ddi_driver_name(dip), ddi_get_instance(dip), IDENT_ASYM);
+	dca_prov_info2.pi_provider_description = ID;
+	dca_prov_info2.pi_provider_dev.pd_hw = dip;
+	dca_prov_info2.pi_provider_handle = dca;
+	if ((ret = crypto_register_provider(&dca_prov_info2,
+	    &WORKLIST(dca, MCR2)->dwl_prov)) != CRYPTO_SUCCESS) {
+		cmn_err(CE_WARN,
+		    "crypto_register_provider() failed (%d) for MCR2", ret);
+		goto failed;
+	}
+
+	crypto_prov_notify(WORKLIST(dca, MCR1)->dwl_prov,
+		CRYPTO_PROVIDER_READY);
+	crypto_prov_notify(WORKLIST(dca, MCR2)->dwl_prov,
+		CRYPTO_PROVIDER_READY);
+
+	/* Initialize the local random number pool for this instance */
+	if ((ret = dca_random_init(dca)) != CRYPTO_SUCCESS) {
+		goto failed;
+	}
+
+	mutex_enter(&dca->dca_intrlock);
+	dca->dca_jobtid = timeout(dca_jobtimeout, (void *)dca,
+	    drv_usectohz(SECOND));
+	mutex_exit(&dca->dca_intrlock);
+
+	ddi_set_driver_private(dip, (caddr_t)dca);
+
+	ddi_report_dev(dip);
+
+	if (ddi_get_devstate(dca->dca_dip) != DDI_DEVSTATE_UP) {
+		ddi_fm_service_impact(dca->dca_dip, DDI_SERVICE_RESTORED);
+	}
+
+	return (DDI_SUCCESS);
+
+failed:
+	/* unregister from the crypto framework */
+	if (WORKLIST(dca, MCR1)->dwl_prov != NULL) {
+	    (void) crypto_unregister_provider(WORKLIST(dca, MCR1)->dwl_prov);
+	}
+	if (WORKLIST(dca, MCR2)->dwl_prov != NULL) {
+	    (void) crypto_unregister_provider(WORKLIST(dca, MCR2)->dwl_prov);
+	}
+	if (intr_added) {
+		CLRBIT(dca, CSR_DMACTL,
+		    DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE);
+		/* unregister intr handler */
+		ddi_remove_intr(dip, 0, dca->dca_icookie);
+	}
+	if (dca->dca_regs_handle) {
+		ddi_regs_map_free(&dca->dca_regs_handle);
+	}
+	if (dca->dca_intrstats) {
+		kstat_delete(dca->dca_intrstats);
+	}
+	if (dca->dca_ksp) {
+		kstat_delete(dca->dca_ksp);
+	}
+	dca_uninit(dca);
+
+	/* finalize FMA */
+	dca_fma_fini(dca);
+
+	mutex_destroy(&dca->dca_intrlock);
+	ddi_soft_state_free(dca_state, instance);
+	return (DDI_FAILURE);
+
+}
+
+int
+dca_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	int		instance;
+	dca_t		*dca;
+	timeout_id_t	tid;
+
+	instance = ddi_get_instance(dip);
+
+	DBG(NULL, DMOD, "dca: in dca_detach() for %d", instance);
+
+	switch (cmd) {
+	case DDI_SUSPEND:
+		if ((dca = (dca_t *)ddi_get_driver_private(dip)) == NULL) {
+			dca_diperror(dip, "no soft state in detach");
+			return (DDI_FAILURE);
+		}
+		/* assumption: we won't be DDI_DETACHed until we return */
+		return (dca_suspend(dca));
+
+	case DDI_DETACH:
+		break;
+	default:
+		return (DDI_FAILURE);
+	}
+
+	if ((dca = (dca_t *)ddi_get_driver_private(dip)) == NULL) {
+		dca_diperror(dip, "no soft state in detach");
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Unregister from kCF.
+	 * This needs to be done at the beginning of detach.
+	 */
+	if (WORKLIST(dca, MCR1)->dwl_prov != NULL) {
+	    if (crypto_unregister_provider(WORKLIST(dca, MCR1)->dwl_prov) !=
+		CRYPTO_SUCCESS) {
+		    dca_error(dca, "unable to unregister MCR1 from kcf");
+		    return (DDI_FAILURE);
+	    }
+	}
+
+	if (WORKLIST(dca, MCR2)->dwl_prov != NULL) {
+	    if (crypto_unregister_provider(WORKLIST(dca, MCR2)->dwl_prov) !=
+		CRYPTO_SUCCESS) {
+		    dca_error(dca, "unable to unregister MCR2 from kcf");
+		    return (DDI_FAILURE);
+	    }
+	}
+
+	/*
+	 * Cleanup the private context list. Once the
+	 * crypto_unregister_provider returns, it is safe to do so.
+	 */
+	dca_free_context_list(dca);
+
+	/* Cleanup the local random number pool */
+	dca_random_fini(dca);
+
+	/* send any jobs in the waitq back to kCF */
+	dca_rejectjobs(dca);
+
+	/* untimeout the timeouts */
+	mutex_enter(&dca->dca_intrlock);
+	tid = dca->dca_jobtid;
+	dca->dca_jobtid = 0;
+	mutex_exit(&dca->dca_intrlock);
+	if (tid) {
+		(void) untimeout(tid);
+	}
+
+	/* disable device interrupts */
+	CLRBIT(dca, CSR_DMACTL, DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE);
+
+	/* unregister interrupt handlers */
+	ddi_remove_intr(dip, 0, dca->dca_icookie);
+
+	/* release our regs handle */
+	ddi_regs_map_free(&dca->dca_regs_handle);
+
+	/* toss out kstats */
+	if (dca->dca_intrstats) {
+		kstat_delete(dca->dca_intrstats);
+	}
+	if (dca->dca_ksp) {
+		kstat_delete(dca->dca_ksp);
+	}
+
+	mutex_destroy(&dca->dca_intrlock);
+	dca_uninit(dca);
+
+	/* finalize FMA */
+	dca_fma_fini(dca);
+
+	ddi_soft_state_free(dca_state, instance);
+
+	return (DDI_SUCCESS);
+}
+
+int
+dca_resume(dca_t *dca)
+{
+	ddi_acc_handle_t	pci;
+
+	if (pci_config_setup(dca->dca_dip, &pci) != DDI_SUCCESS) {
+		dca_error(dca, "unable to setup PCI config handle");
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Reprogram registers in PCI configuration space.
+	 */
+
+	/* Broadcom-specific timers -- we disable them. */
+	pci_config_put8(pci, PCI_TRDYTO, 0);
+	pci_config_put8(pci, PCI_RETRIES, 0);
+
+	/* initialize PCI access settings */
+	pci_config_put16(pci, PCI_COMM, PCICOMM_SEE |
+	    PCICOMM_PEE | PCICOMM_BME | PCICOMM_MAE);
+
+	/* set up our PCI latency timer */
+	pci_config_put8(pci, PCI_LATTMR, 0x40);
+
+	pci_config_teardown(&pci);
+
+	if (dca_reset(dca, 0) < 0) {
+		dca_error(dca, "unable to reset device during resume");
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Now restore the card-specific CSRs.
+	 */
+
+	/* restore endianness settings */
+	PUTCSR(dca, CSR_DMACTL, DMACTL_BE32 | DMACTL_BE64);
+	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
+	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
+		return (DDI_FAILURE);
+
+	/* restore interrupt enables */
+	SETBIT(dca, CSR_DMACTL, DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE);
+	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
+	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
+		return (DDI_FAILURE);
+
+	/* resume scheduling jobs on the device */
+	dca_undrain(dca);
+
+	return (DDI_SUCCESS);
+}
+
+int
+dca_suspend(dca_t *dca)
+{
+	if ((dca_drain(dca)) != 0) {
+		return (DDI_FAILURE);
+	}
+	if (dca_reset(dca, 0) < 0) {
+		dca_error(dca, "unable to reset device during suspend");
+		return (DDI_FAILURE);
+	}
+	return (DDI_SUCCESS);
+}
+
+/*
+ * Hardware access stuff.
+ */
+int
+dca_reset(dca_t *dca, int failreset)
+{
+	int i;
+
+	if (dca->dca_regs_handle == NULL) {
+		return (-1);
+	}
+
+	PUTCSR(dca, CSR_DMACTL, DMACTL_RESET);
+	if (!failreset) {
+		if (dca_check_acc_handle(dca, dca->dca_regs_handle,
+		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
+			return (-1);
+	}
+
+	/* now wait for a reset */
+	for (i = 1; i < 100; i++) {
+		uint32_t	dmactl;
+		drv_usecwait(100);
+		dmactl = GETCSR(dca, CSR_DMACTL);
+		if (!failreset) {
+			if (dca_check_acc_handle(dca, dca->dca_regs_handle,
+			    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
+				return (-1);
+		}
+		if ((dmactl & DMACTL_RESET) == 0) {
+			DBG(dca, DCHATTY, "reset in %d usec", i * 100);
+			return (0);
+		}
+	}
+	if (!failreset) {
+		dca_failure(dca, DDI_DEVICE_FAULT,
+		    DCA_FM_ECLASS_NONE, dca_ena(0), CRYPTO_DEVICE_ERROR,
+		    "timeout waiting for reset after %d usec", i * 100);
+	}
+	return (-1);
+}
+
+int
+dca_initworklist(dca_t *dca, dca_worklist_t *wlp)
+{
+	int	i;
+	int	reqprealloc = wlp->dwl_hiwater + (MAXWORK * MAXREQSPERMCR);
+
+	/*
+	 * Set up work queue.
+	 */
+	mutex_init(&wlp->dwl_lock, NULL, MUTEX_DRIVER, dca->dca_icookie);
+	mutex_init(&wlp->dwl_freereqslock, NULL, MUTEX_DRIVER,
+	    dca->dca_icookie);
+	cv_init(&wlp->dwl_cv, NULL, CV_DRIVER, NULL);
+
+	mutex_enter(&wlp->dwl_lock);
+
+	dca_initq(&wlp->dwl_freereqs);
+	dca_initq(&wlp->dwl_waitq);
+	dca_initq(&wlp->dwl_freework);
+	dca_initq(&wlp->dwl_runq);
+
+	for (i = 0; i < MAXWORK; i++) {
+		dca_work_t		*workp;
+
+		if ((workp = dca_newwork(dca)) == NULL) {
+			dca_error(dca, "unable to allocate work");
+			mutex_exit(&wlp->dwl_lock);
+			return (DDI_FAILURE);
+		}
+		workp->dw_wlp = wlp;
+		dca_freework(workp);
+	}
+	mutex_exit(&wlp->dwl_lock);
+
+	for (i = 0; i < reqprealloc; i++) {
+		dca_request_t *reqp;
+
+		if ((reqp = dca_newreq(dca)) == NULL) {
+			dca_error(dca, "unable to allocate request");
+			return (DDI_FAILURE);
+		}
+		reqp->dr_dca = dca;
+		reqp->dr_wlp = wlp;
+		dca_freereq(reqp);
+	}
+	return (DDI_SUCCESS);
+}
+
+int
+dca_init(dca_t *dca)
+{
+	dca_worklist_t		*wlp;
+
+	/* Initialize the private context list and the corresponding lock. */
+	mutex_init(&dca->dca_ctx_list_lock, NULL, MUTEX_DRIVER, NULL);
+	dca_initq(&dca->dca_ctx_list);
+
+	/*
+	 * MCR1 algorithms.
+	 */
+	wlp = WORKLIST(dca, MCR1);
+	(void) sprintf(wlp->dwl_name, "dca%d:mcr1",
+		ddi_get_instance(dca->dca_dip));
+	wlp->dwl_lowater = ddi_getprop(DDI_DEV_T_ANY,
+	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
+	    "mcr1_lowater", MCR1LOWATER);
+	wlp->dwl_hiwater = ddi_getprop(DDI_DEV_T_ANY,
+	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
+	    "mcr1_hiwater", MCR1HIWATER);
+	wlp->dwl_reqspermcr = min(ddi_getprop(DDI_DEV_T_ANY,
+	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
+	    "mcr1_maxreqs", MCR1MAXREQS), MAXREQSPERMCR);
+	wlp->dwl_dca = dca;
+	wlp->dwl_mcr = MCR1;
+	if (dca_initworklist(dca, wlp) != DDI_SUCCESS) {
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * MCR2 algorithms.
+	 */
+	wlp = WORKLIST(dca, MCR2);
+	(void) sprintf(wlp->dwl_name, "dca%d:mcr2",
+		ddi_get_instance(dca->dca_dip));
+	wlp->dwl_lowater = ddi_getprop(DDI_DEV_T_ANY,
+	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
+	    "mcr2_lowater", MCR2LOWATER);
+	wlp->dwl_hiwater = ddi_getprop(DDI_DEV_T_ANY,
+	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
+	    "mcr2_hiwater", MCR2HIWATER);
+	wlp->dwl_reqspermcr = min(ddi_getprop(DDI_DEV_T_ANY,
+	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
+	    "mcr2_maxreqs", MCR2MAXREQS), MAXREQSPERMCR);
+	wlp->dwl_dca = dca;
+	wlp->dwl_mcr = MCR2;
+	if (dca_initworklist(dca, wlp) != DDI_SUCCESS) {
+		return (DDI_FAILURE);
+	}
+	return (DDI_SUCCESS);
+}
+
+/*
+ * Uninitialize worklists.  This routine should only be called when no
+ * active jobs (hence DMA mappings) exist.  One way to ensure this is
+ * to unregister from kCF before calling this routine.  (This is done
+ * e.g. in detach(9e).)
+ */
+void
+dca_uninit(dca_t *dca)
+{
+	int	mcr;
+
+	mutex_destroy(&dca->dca_ctx_list_lock);
+
+	for (mcr = MCR1; mcr <= MCR2; mcr++) {
+		dca_worklist_t	*wlp = WORKLIST(dca, mcr);
+		dca_work_t	*workp;
+		dca_request_t	*reqp;
+
+		if (dca->dca_regs_handle == NULL) {
+			continue;
+		}
+
+		mutex_enter(&wlp->dwl_lock);
+		while ((workp = dca_getwork(dca, mcr)) != NULL) {
+			dca_destroywork(workp);
+		}
+		mutex_exit(&wlp->dwl_lock);
+		while ((reqp = dca_getreq(dca, mcr, 0)) != NULL) {
+			dca_destroyreq(reqp);
+		}
+
+		mutex_destroy(&wlp->dwl_lock);
+		mutex_destroy(&wlp->dwl_freereqslock);
+		cv_destroy(&wlp->dwl_cv);
+		wlp->dwl_prov = NULL;
+	}
+}
+
+static void
+dca_enlist2(dca_listnode_t *q, dca_listnode_t *node, kmutex_t *lock)
+{
+	if (!q || !node)
+		return;
+
+	mutex_enter(lock);
+	node->dl_next2 = q;
+	node->dl_prev2 = q->dl_prev2;
+	node->dl_next2->dl_prev2 = node;
+	node->dl_prev2->dl_next2 = node;
+	mutex_exit(lock);
+}
+
+static void
+dca_rmlist2(dca_listnode_t *node, kmutex_t *lock)
+{
+	if (!node)
+		return;
+
+	mutex_enter(lock);
+	node->dl_next2->dl_prev2 = node->dl_prev2;
+	node->dl_prev2->dl_next2 = node->dl_next2;
+	node->dl_next2 = NULL;
+	node->dl_prev2 = NULL;
+	mutex_exit(lock);
+}
+
+static dca_listnode_t *
+dca_delist2(dca_listnode_t *q, kmutex_t *lock)
+{
+	dca_listnode_t *node;
+
+	mutex_enter(lock);
+	if ((node = q->dl_next2) == q) {
+		mutex_exit(lock);
+		return (NULL);
+	}
+
+	node->dl_next2->dl_prev2 = node->dl_prev2;
+	node->dl_prev2->dl_next2 = node->dl_next2;
+	node->dl_next2 = NULL;
+	node->dl_prev2 = NULL;
+	mutex_exit(lock);
+
+	return (node);
+}
+
+void
+dca_initq(dca_listnode_t *q)
+{
+	q->dl_next = q;
+	q->dl_prev = q;
+	q->dl_next2 = q;
+	q->dl_prev2 = q;
+}
+
+void
+dca_enqueue(dca_listnode_t *q, dca_listnode_t *node)
+{
+	/*
+	 * Enqueue submits at the "tail" of the list, i.e. just
+	 * behind the sentinel.
+	 */
+	node->dl_next = q;
+	node->dl_prev = q->dl_prev;
+	node->dl_next->dl_prev = node;
+	node->dl_prev->dl_next = node;
+}
+
+void
+dca_rmqueue(dca_listnode_t *node)
+{
+	node->dl_next->dl_prev = node->dl_prev;
+	node->dl_prev->dl_next = node->dl_next;
+	node->dl_next = NULL;
+	node->dl_prev = NULL;
+}
+
+dca_listnode_t *
+dca_dequeue(dca_listnode_t *q)
+{
+	dca_listnode_t *node;
+	/*
+	 * Dequeue takes from the "head" of the list, i.e. just after
+	 * the sentinel.
+	 */
+	if ((node = q->dl_next) == q) {
+		/* queue is empty */
+		return (NULL);
+	}
+	dca_rmqueue(node);
+	return (node);
+}
+
+/* this is the opposite of dequeue, it takes things off in LIFO order */
+dca_listnode_t *
+dca_unqueue(dca_listnode_t *q)
+{
+	dca_listnode_t *node;
+	/*
+	 * unqueue takes from the "tail" of the list, i.e. just before
+	 * the sentinel.
+	 */
+	if ((node = q->dl_prev) == q) {;
+		/* queue is empty */
+		return (NULL);
+	}
+	dca_rmqueue(node);
+	return (node);
+}
+
+dca_listnode_t *
+dca_peekqueue(dca_listnode_t *q)
+{
+	dca_listnode_t *node;
+
+	if ((node = q->dl_next) == q) {
+		return (NULL);
+	} else {
+		return (node);
+	}
+}
+
+/*
+ * Interrupt service routine.
+ */
+uint_t
+dca_intr(char *arg)
+{
+	dca_t		*dca = (dca_t *)arg;
+	uint32_t	status;
+
+	mutex_enter(&dca->dca_intrlock);
+	status = GETCSR(dca, CSR_DMASTAT);
+	PUTCSR(dca, CSR_DMASTAT, status & DMASTAT_INTERRUPTS);
+	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
+	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+		mutex_exit(&dca->dca_intrlock);
+		return ((uint_t)DDI_FAILURE);
+	}
+
+	DBG(dca, DINTR, "interrupted, status = 0x%x!", status);
+
+	if ((status & DMASTAT_INTERRUPTS) == 0) {
+		/* increment spurious interrupt kstat */
+		if (dca->dca_intrstats) {
+			KIOIP(dca)->intrs[KSTAT_INTR_SPURIOUS]++;
+		}
+		mutex_exit(&dca->dca_intrlock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+
+	if (dca->dca_intrstats) {
+		KIOIP(dca)->intrs[KSTAT_INTR_HARD]++;
+	}
+	if (status & DMASTAT_MCR1INT) {
+		DBG(dca, DINTR, "MCR1 interrupted");
+		mutex_enter(&(WORKLIST(dca, MCR1)->dwl_lock));
+		dca_schedule(dca, MCR1);
+		dca_reclaim(dca, MCR1);
+		mutex_exit(&(WORKLIST(dca, MCR1)->dwl_lock));
+	}
+
+	if (status & DMASTAT_MCR2INT) {
+		DBG(dca, DINTR, "MCR2 interrupted");
+		mutex_enter(&(WORKLIST(dca, MCR2)->dwl_lock));
+		dca_schedule(dca, MCR2);
+		dca_reclaim(dca, MCR2);
+		mutex_exit(&(WORKLIST(dca, MCR2)->dwl_lock));
+	}
+
+	if (status & DMASTAT_ERRINT) {
+		uint32_t	erraddr;
+		erraddr = GETCSR(dca, CSR_DMAEA);
+		mutex_exit(&dca->dca_intrlock);
+
+		/*
+		 * bit 1 of the error address indicates failure during
+		 * read if set, during write otherwise.
+		 */
+		dca_failure(dca, DDI_DEVICE_FAULT,
+		    DCA_FM_ECLASS_HW_DEVICE, dca_ena(0), CRYPTO_DEVICE_ERROR,
+		    "DMA master access error %s address 0x%x",
+		    erraddr & 0x1 ? "reading" : "writing", erraddr & ~1);
+		return (DDI_INTR_CLAIMED);
+	}
+
+	mutex_exit(&dca->dca_intrlock);
+
+	return (DDI_INTR_CLAIMED);
+}
+
+/*
+ * Reverse a string of bytes from s1 into s2.  The reversal happens
+ * from the tail of s1.  If len1 < len2, then null bytes will be
+ * padded to the end of s2.  If len2 < len1, then (presumably null)
+ * bytes will be dropped from the start of s1.
+ *
+ * The rationale here is that when s1 (source) is shorter, then we
+ * are reversing from big-endian ordering, into device ordering, and
+ * want to add some extra nulls to the tail (MSB) side of the device.
+ *
+ * Similarly, when s2 (dest) is shorter, then we are truncating what
+ * are presumably null MSB bits from the device.
+ *
+ * There is an expectation when reversing from the device back into
+ * big-endian, that the number of bytes to reverse and the target size
+ * will match, and no truncation or padding occurs.
+ */
+void
+dca_reverse(void *s1, void *s2, int len1, int len2)
+{
+	caddr_t	src, dst;
+
+	if (len1 == 0) {
+		if (len2) {
+			bzero(s2, len2);
+		}
+		return;
+	}
+	src = (caddr_t)s1 + len1 - 1;
+	dst = s2;
+	while ((src >= (caddr_t)s1) && (len2)) {
+		*dst++ = *src--;
+		len2--;
+	}
+	while (len2 > 0) {
+		*dst++ = 0;
+		len2--;
+	}
+}
+
+uint16_t
+dca_padfull(int num)
+{
+	if (num <= 512) {
+		return (BITS2BYTES(512));
+	}
+	if (num <= 768) {
+		return (BITS2BYTES(768));
+	}
+	if (num <= 1024) {
+		return (BITS2BYTES(1024));
+	}
+	if (num <= 1536) {
+		return (BITS2BYTES(1536));
+	}
+	if (num <= 2048) {
+		return (BITS2BYTES(2048));
+	}
+	return (0);
+}
+
+uint16_t
+dca_padhalf(int num)
+{
+	if (num <= 256) {
+		return (BITS2BYTES(256));
+	}
+	if (num <= 384) {
+		return (BITS2BYTES(384));
+	}
+	if (num <= 512) {
+		return (BITS2BYTES(512));
+	}
+	if (num <= 768) {
+		return (BITS2BYTES(768));
+	}
+	if (num <= 1024) {
+		return (BITS2BYTES(1024));
+	}
+	return (0);
+}
+
+dca_work_t *
+dca_newwork(dca_t *dca)
+{
+	dca_work_t		*workp;
+	size_t			size;
+	ddi_dma_cookie_t	c;
+	unsigned		nc;
+	int			rv;
+
+	workp = kmem_zalloc(sizeof (dca_work_t), KM_SLEEP);
+
+	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
+	    DDI_DMA_SLEEP, NULL, &workp->dw_mcr_dmah);
+	if (rv != 0) {
+		dca_error(dca, "unable to alloc MCR DMA handle");
+		dca_destroywork(workp);
+		return (NULL);
+	}
+
+	rv = ddi_dma_mem_alloc(workp->dw_mcr_dmah,
+	    ROUNDUP(MCR_SIZE, dca->dca_pagesize),
+	    &dca_devattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
+	    &workp->dw_mcr_kaddr, &size, &workp->dw_mcr_acch);
+	if (rv != 0) {
+		dca_error(dca, "unable to alloc MCR DMA memory");
+		dca_destroywork(workp);
+		return (NULL);
+	}
+
+	rv = ddi_dma_addr_bind_handle(workp->dw_mcr_dmah, NULL,
+	    workp->dw_mcr_kaddr, size, DDI_DMA_CONSISTENT | DDI_DMA_RDWR,
+	    DDI_DMA_SLEEP, NULL, &c, &nc);
+	if (rv != DDI_DMA_MAPPED) {
+		dca_error(dca, "unable to map MCR DMA memory");
+		dca_destroywork(workp);
+		return (NULL);
+	}
+
+	workp->dw_mcr_paddr = c.dmac_address;
+	return (workp);
+}
+
+void
+dca_destroywork(dca_work_t *workp)
+{
+	if (workp->dw_mcr_paddr) {
+		(void) ddi_dma_unbind_handle(workp->dw_mcr_dmah);
+	}
+	if (workp->dw_mcr_acch) {
+		ddi_dma_mem_free(&workp->dw_mcr_acch);
+	}
+	if (workp->dw_mcr_dmah) {
+		ddi_dma_free_handle(&workp->dw_mcr_dmah);
+	}
+	kmem_free(workp, sizeof (dca_work_t));
+}
+
+dca_request_t *
+dca_newreq(dca_t *dca)
+{
+	dca_request_t		*reqp;
+	size_t			size;
+	ddi_dma_cookie_t	c;
+	unsigned		nc;
+	int			rv;
+	int			n_chain = 0;
+
+	size = (DESC_SIZE * MAXFRAGS) + CTX_MAXLENGTH;
+
+	reqp = kmem_zalloc(sizeof (dca_request_t), KM_SLEEP);
+
+	reqp->dr_dca = dca;
+
+	/*
+	 * Setup the DMA region for the context and descriptors.
+	 */
+	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr, DDI_DMA_SLEEP,
+	    NULL, &reqp->dr_ctx_dmah);
+	if (rv != DDI_SUCCESS) {
+		dca_error(dca, "failure allocating request DMA handle");
+		dca_destroyreq(reqp);
+		return (NULL);
+	}
+
+	/* for driver hardening, allocate in whole pages */
+	rv = ddi_dma_mem_alloc(reqp->dr_ctx_dmah,
+	    ROUNDUP(size, dca->dca_pagesize), &dca_devattr, DDI_DMA_CONSISTENT,
+	    DDI_DMA_SLEEP, NULL, &reqp->dr_ctx_kaddr, &size,
+	    &reqp->dr_ctx_acch);
+	if (rv != DDI_SUCCESS) {
+		dca_error(dca, "unable to alloc request DMA memory");
+		dca_destroyreq(reqp);
+		return (NULL);
+	}
+
+	rv = ddi_dma_addr_bind_handle(reqp->dr_ctx_dmah, NULL,
+	    reqp->dr_ctx_kaddr, size, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
+	    DDI_DMA_SLEEP, 0, &c, &nc);
+	if (rv != DDI_DMA_MAPPED) {
+		dca_error(dca, "failed binding request DMA handle");
+		dca_destroyreq(reqp);
+		return (NULL);
+	}
+	reqp->dr_ctx_paddr = c.dmac_address;
+
+	reqp->dr_dma_size = size;
+
+	/*
+	 * Set up the dma for our scratch/shared buffers.
+	 */
+	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
+	    DDI_DMA_SLEEP, NULL, &reqp->dr_ibuf_dmah);
+	if (rv != DDI_SUCCESS) {
+		dca_error(dca, "failure allocating ibuf DMA handle");
+		dca_destroyreq(reqp);
+		return (NULL);
+	}
+	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
+	    DDI_DMA_SLEEP, NULL, &reqp->dr_obuf_dmah);
+	if (rv != DDI_SUCCESS) {
+		dca_error(dca, "failure allocating obuf DMA handle");
+		dca_destroyreq(reqp);
+		return (NULL);
+	}
+
+	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
+	    DDI_DMA_SLEEP, NULL, &reqp->dr_chain_in_dmah);
+	if (rv != DDI_SUCCESS) {
+		dca_error(dca, "failure allocating chain_in DMA handle");
+		dca_destroyreq(reqp);
+		return (NULL);
+	}
+
+	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
+	    DDI_DMA_SLEEP, NULL, &reqp->dr_chain_out_dmah);
+	if (rv != DDI_SUCCESS) {
+		dca_error(dca, "failure allocating chain_out DMA handle");
+		dca_destroyreq(reqp);
+		return (NULL);
+	}
+
+	/*
+	 * for driver hardening, allocate in whole pages.
+	 */
+	size = ROUNDUP(MAXPACKET, dca->dca_pagesize);
+#if defined(i386) || defined(__i386)
+	/*
+	 * Use kmem_alloc instead of ddi_dma_mem_alloc here since the latter
+	 * may fail on x86 platform if a physically contigous memory chunk
+	 * cannot be found. From initial testing, we did not see performance
+	 * degration as seen on Sparc.
+	 */
+	if ((reqp->dr_ibuf_kaddr = kmem_alloc(size, KM_SLEEP)) == NULL) {
+		dca_error(dca, "unable to alloc request ibuf memory");
+		dca_destroyreq(reqp);
+		return (NULL);
+	}
+	if ((reqp->dr_obuf_kaddr = kmem_alloc(size, KM_SLEEP)) == NULL) {
+		dca_error(dca, "unable to alloc request obuf memory");
+		dca_destroyreq(reqp);
+		return (NULL);
+	}
+#else
+	/*
+	 * We could kmem_alloc for sparc too. However, it gives worse
+	 * performance when transfering more than one page data. For example,
+	 * using 4 threads and 12032 byte data and 3DES on 900MHZ sparc system,
+	 * kmem_alloc uses 80% CPU and ddi_dma_mem_alloc uses 50% CPU for
+	 * the same throughput.
+	 */
+	rv = ddi_dma_mem_alloc(reqp->dr_ibuf_dmah,
+	    size, &dca_bufattr,
+	    DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &reqp->dr_ibuf_kaddr,
+	    &size, &reqp->dr_ibuf_acch);
+	if (rv != DDI_SUCCESS) {
+		dca_error(dca, "unable to alloc request DMA memory");
+		dca_destroyreq(reqp);
+		return (NULL);
+	}
+
+	rv = ddi_dma_mem_alloc(reqp->dr_obuf_dmah,
+	    size, &dca_bufattr,
+	    DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &reqp->dr_obuf_kaddr,
+	    &size, &reqp->dr_obuf_acch);
+	if (rv != DDI_SUCCESS) {
+		dca_error(dca, "unable to alloc request DMA memory");
+		dca_destroyreq(reqp);
+		return (NULL);
+	}
+#endif
+
+	/* Skip the used portion in the context page */
+	reqp->dr_offset = CTX_MAXLENGTH;
+	if ((rv = dca_bindchains_one(reqp, size, reqp->dr_offset,
+	    reqp->dr_ibuf_kaddr, reqp->dr_ibuf_dmah,
+	    DDI_DMA_WRITE | DDI_DMA_STREAMING,
+	    &reqp->dr_ibuf_head, &n_chain)) != DDI_SUCCESS) {
+		(void) dca_destroyreq(reqp);
+		return (NULL);
+	}
+	reqp->dr_ibuf_paddr = reqp->dr_ibuf_head.dc_buffer_paddr;
+	/* Skip the space used by the input buffer */
+	reqp->dr_offset += DESC_SIZE * n_chain;
+
+	if ((rv = dca_bindchains_one(reqp, size, reqp->dr_offset,
+	    reqp->dr_obuf_kaddr, reqp->dr_obuf_dmah,
+	    DDI_DMA_READ | DDI_DMA_STREAMING,
+	    &reqp->dr_obuf_head, &n_chain)) != DDI_SUCCESS) {
+		(void) dca_destroyreq(reqp);
+		return (NULL);
+	}
+	reqp->dr_obuf_paddr = reqp->dr_obuf_head.dc_buffer_paddr;
+	/* Skip the space used by the output buffer */
+	reqp->dr_offset += DESC_SIZE * n_chain;
+
+	DBG(dca, DCHATTY, "CTX is 0x%p, phys 0x%x, len %d",
+	    reqp->dr_ctx_kaddr, reqp->dr_ctx_paddr, CTX_MAXLENGTH);
+	return (reqp);
+}
+
+void
+dca_destroyreq(dca_request_t *reqp)
+{
+#if defined(i386) || defined(__i386)
+	dca_t		*dca = reqp->dr_dca;
+	size_t		size = ROUNDUP(MAXPACKET, dca->dca_pagesize);
+#endif
+
+	/*
+	 * Clean up DMA for the context structure.
+	 */
+	if (reqp->dr_ctx_paddr) {
+		(void) ddi_dma_unbind_handle(reqp->dr_ctx_dmah);
+	}
+
+	if (reqp->dr_ctx_acch) {
+		ddi_dma_mem_free(&reqp->dr_ctx_acch);
+	}
+
+	if (reqp->dr_ctx_dmah) {
+		ddi_dma_free_handle(&reqp->dr_ctx_dmah);
+	}
+
+	/*
+	 * Clean up DMA for the scratch buffer.
+	 */
+#if defined(i386) || defined(__i386)
+	if (reqp->dr_ibuf_dmah) {
+		(void) ddi_dma_unbind_handle(reqp->dr_ibuf_dmah);
+		ddi_dma_free_handle(&reqp->dr_ibuf_dmah);
+	}
+	if (reqp->dr_obuf_dmah) {
+		(void) ddi_dma_unbind_handle(reqp->dr_obuf_dmah);
+		ddi_dma_free_handle(&reqp->dr_obuf_dmah);
+	}
+
+	kmem_free(reqp->dr_ibuf_kaddr, size);
+	kmem_free(reqp->dr_obuf_kaddr, size);
+#else
+	if (reqp->dr_ibuf_paddr) {
+		(void) ddi_dma_unbind_handle(reqp->dr_ibuf_dmah);
+	}
+	if (reqp->dr_obuf_paddr) {
+		(void) ddi_dma_unbind_handle(reqp->dr_obuf_dmah);
+	}
+
+	if (reqp->dr_ibuf_acch) {
+		ddi_dma_mem_free(&reqp->dr_ibuf_acch);
+	}
+	if (reqp->dr_obuf_acch) {
+		ddi_dma_mem_free(&reqp->dr_obuf_acch);
+	}
+
+	if (reqp->dr_ibuf_dmah) {
+		ddi_dma_free_handle(&reqp->dr_ibuf_dmah);
+	}
+	if (reqp->dr_obuf_dmah) {
+		ddi_dma_free_handle(&reqp->dr_obuf_dmah);
+	}
+#endif
+	/*
+	 * These two DMA handles should have been unbinded in
+	 * dca_unbindchains() function
+	 */
+	if (reqp->dr_chain_in_dmah) {
+		ddi_dma_free_handle(&reqp->dr_chain_in_dmah);
+	}
+	if (reqp->dr_chain_out_dmah) {
+		ddi_dma_free_handle(&reqp->dr_chain_out_dmah);
+	}
+
+	kmem_free(reqp, sizeof (dca_request_t));
+}
+
+dca_work_t *
+dca_getwork(dca_t *dca, int mcr)
+{
+	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
+	dca_work_t	*workp;
+
+	ASSERT(mutex_owned(&wlp->dwl_lock));
+	workp = (dca_work_t *)dca_dequeue(&wlp->dwl_freework);
+	if (workp) {
+		int	nreqs;
+		bzero(workp->dw_mcr_kaddr, 8);
+
+		/* clear out old requests */
+		for (nreqs = 0; nreqs < MAXREQSPERMCR; nreqs++) {
+			workp->dw_reqs[nreqs] = NULL;
+		}
+	}
+	return (workp);
+}
+
+void
+dca_freework(dca_work_t *workp)
+{
+	ASSERT(mutex_owned(&workp->dw_wlp->dwl_lock));
+	dca_enqueue(&workp->dw_wlp->dwl_freework, (dca_listnode_t *)workp);
+}
+
+dca_request_t *
+dca_getreq(dca_t *dca, int mcr, int tryhard)
+{
+	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
+	dca_request_t	*reqp;
+
+	mutex_enter(&wlp->dwl_freereqslock);
+	reqp = (dca_request_t *)dca_dequeue(&wlp->dwl_freereqs);
+	mutex_exit(&wlp->dwl_freereqslock);
+	if (reqp) {
+		reqp->dr_flags = 0;
+		reqp->dr_callback = NULL;
+	} else if (tryhard) {
+		/*
+		 * failed to get a free one, try an allocation, the hard way.
+		 * XXX: Kstat desired here.
+		 */
+		if ((reqp = dca_newreq(dca)) != NULL) {
+			reqp->dr_wlp = wlp;
+			reqp->dr_dca = dca;
+			reqp->dr_flags = 0;
+			reqp->dr_callback = NULL;
+		}
+	}
+	return (reqp);
+}
+
+void
+dca_freereq(dca_request_t *reqp)
+{
+	reqp->dr_kcf_req = NULL;
+	if (!(reqp->dr_flags & DR_NOCACHE)) {
+		mutex_enter(&reqp->dr_wlp->dwl_freereqslock);
+		dca_enqueue(&reqp->dr_wlp->dwl_freereqs,
+		    (dca_listnode_t *)reqp);
+		mutex_exit(&reqp->dr_wlp->dwl_freereqslock);
+	}
+}
+
+/*
+ * Binds user buffers to DMA handles dynamically. On Sparc, a user buffer
+ * is mapped to a single physicall address. On x86, a user buffer is mapped
+ * to multiple physically addresses. These phsyical addresses are chained
+ * using the method specified in Broadcom BCM5820 specification
+ */
+int
+dca_bindchains(dca_request_t *reqp, size_t incnt, size_t outcnt)
+{
+	int			rv;
+	caddr_t			kaddr;
+	uint_t			flags;
+	int			n_chain = 0;
+
+	if (reqp->dr_flags & DR_INPLACE) {
+		flags = DDI_DMA_RDWR | DDI_DMA_CONSISTENT;
+	} else {
+		flags = DDI_DMA_WRITE | DDI_DMA_STREAMING;
+	}
+
+	/* first the input */
+	if (incnt) {
+		if ((kaddr = dca_bufdaddr(reqp->dr_in)) == NULL) {
+			DBG(NULL, DWARN, "unrecognised crypto data format");
+			return (DDI_FAILURE);
+		}
+		if ((rv = dca_bindchains_one(reqp, incnt, reqp->dr_offset,
+		    kaddr, reqp->dr_chain_in_dmah, flags,
+		    &reqp->dr_chain_in_head, &n_chain)) != DDI_SUCCESS) {
+			(void) dca_unbindchains(reqp);
+			return (rv);
+		}
+
+		/*
+		 * The offset and length are altered by the calling routine
+		 * reqp->dr_in->cd_offset += incnt;
+		 * reqp->dr_in->cd_length -= incnt;
+		 */
+		/* Save the first one in the chain for MCR */
+		reqp->dr_in_paddr = reqp->dr_chain_in_head.dc_buffer_paddr;
+		reqp->dr_in_next = reqp->dr_chain_in_head.dc_next_paddr;
+		reqp->dr_in_len = reqp->dr_chain_in_head.dc_buffer_length;
+	} else {
+		reqp->dr_in_paddr = NULL;
+		reqp->dr_in_next = 0;
+		reqp->dr_in_len = 0;
+	}
+
+	if (reqp->dr_flags & DR_INPLACE) {
+		reqp->dr_out_paddr = reqp->dr_in_paddr;
+		reqp->dr_out_len = reqp->dr_in_len;
+		reqp->dr_out_next = reqp->dr_in_next;
+		return (DDI_SUCCESS);
+	}
+
+	/* then the output */
+	if (outcnt) {
+		flags = DDI_DMA_READ | DDI_DMA_STREAMING;
+		if ((kaddr = dca_bufdaddr_out(reqp->dr_out)) == NULL) {
+			DBG(NULL, DWARN, "unrecognised crypto data format");
+			(void) dca_unbindchains(reqp);
+			return (DDI_FAILURE);
+		}
+		rv = dca_bindchains_one(reqp, outcnt, reqp->dr_offset +
+		    n_chain * DESC_SIZE, kaddr, reqp->dr_chain_out_dmah,
+		    flags, &reqp->dr_chain_out_head, &n_chain);
+		if (rv != DDI_SUCCESS) {
+			(void) dca_unbindchains(reqp);
+			return (DDI_FAILURE);
+		}
+
+		/* Save the first one in the chain for MCR */
+		reqp->dr_out_paddr = reqp->dr_chain_out_head.dc_buffer_paddr;
+		reqp->dr_out_next = reqp->dr_chain_out_head.dc_next_paddr;
+		reqp->dr_out_len = reqp->dr_chain_out_head.dc_buffer_length;
+	} else {
+		reqp->dr_out_paddr = NULL;
+		reqp->dr_out_next = 0;
+		reqp->dr_out_len = 0;
+	}
+
+	return (DDI_SUCCESS);
+}
+
+/*
+ * Unbind the user buffers from the DMA handles.
+ */
+int
+dca_unbindchains(dca_request_t *reqp)
+{
+	int rv = DDI_SUCCESS;
+	int rv1 = DDI_SUCCESS;
+
+	/* Clear the input chain */
+	if (reqp->dr_chain_in_head.dc_buffer_paddr != NULL) {
+		(void) ddi_dma_unbind_handle(reqp->dr_chain_in_dmah);
+		reqp->dr_chain_in_head.dc_buffer_paddr = 0;
+	}
+
+	/* Clear the output chain */
+	if (reqp->dr_chain_out_head.dc_buffer_paddr != NULL) {
+		(void) ddi_dma_unbind_handle(reqp->dr_chain_out_dmah);
+		reqp->dr_chain_out_head.dc_buffer_paddr = 0;
+	}
+
+	return ((rv != DDI_SUCCESS)? rv : rv1);
+}
+
+/*
+ * Build either input chain or output chain. It is single-item chain for Sparc,
+ * and possible mutiple-item chain for x86.
+ */
+static int
+dca_bindchains_one(dca_request_t *reqp, size_t cnt, int dr_offset,
+    caddr_t kaddr, ddi_dma_handle_t handle, uint_t flags,
+    dca_chain_t *head, int *n_chain)
+{
+	ddi_dma_cookie_t	c;
+	uint_t			nc;
+	int			rv;
+	caddr_t			chain_kaddr_pre;
+	caddr_t			chain_kaddr;
+	uint32_t		chain_paddr;
+	int 			i;
+
+	/* Advance past the context structure to the starting address */
+	chain_paddr = reqp->dr_ctx_paddr + dr_offset;
+	chain_kaddr = reqp->dr_ctx_kaddr + dr_offset;
+
+	/*
+	 * Bind the kernel address to the DMA handle. On x86, the actual
+	 * buffer is mapped into multiple physical addresses. On Sparc,
+	 * the actual buffer is mapped into a single address.
+	 */
+	rv = ddi_dma_addr_bind_handle(handle,
+	    NULL, kaddr, cnt, flags, DDI_DMA_DONTWAIT, NULL, &c, &nc);
+	if (rv != DDI_DMA_MAPPED) {
+		return (DDI_FAILURE);
+	}
+
+	(void) ddi_dma_sync(handle, 0, cnt, DDI_DMA_SYNC_FORDEV);
+	if ((rv = dca_check_dma_handle(reqp->dr_dca, handle,
+	    DCA_FM_ECLASS_NONE)) != DDI_SUCCESS) {
+		reqp->destroy = TRUE;
+		return (rv);
+	}
+
+	*n_chain = nc;
+
+	/* Setup the data buffer chain for DMA transfer */
+	chain_kaddr_pre = NULL;
+	head->dc_buffer_paddr = 0;
+	head->dc_next_paddr = 0;
+	head->dc_buffer_length = 0;
+	for (i = 0; i < nc; i++) {
+		/* PIO */
+		PUTDESC32(reqp, chain_kaddr, DESC_BUFADDR, c.dmac_address);
+		PUTDESC16(reqp, chain_kaddr, DESC_RSVD, 0);
+		PUTDESC16(reqp, chain_kaddr, DESC_LENGTH, c.dmac_size);
+
+		/* Remember the head of the chain */
+		if (head->dc_buffer_paddr == 0) {
+			head->dc_buffer_paddr = c.dmac_address;
+			head->dc_buffer_length = c.dmac_size;
+		}
+
+		/* Link to the previous one if one exists */
+		if (chain_kaddr_pre) {
+			PUTDESC32(reqp, chain_kaddr_pre, DESC_NEXT,
+			    chain_paddr);
+			if (head->dc_next_paddr == 0)
+				head->dc_next_paddr = chain_paddr;
+		}
+		chain_kaddr_pre = chain_kaddr;
+
+		/* Maintain pointers */
+		chain_paddr += DESC_SIZE;
+		chain_kaddr += DESC_SIZE;
+
+		/* Retrieve the next cookie if there is one */
+		if (i < nc-1)
+			ddi_dma_nextcookie(handle, &c);
+	}
+
+	/* Set the next pointer in the last entry to NULL */
+	PUTDESC32(reqp, chain_kaddr_pre, DESC_NEXT, 0);
+
+	return (DDI_SUCCESS);
+}
+
+/*
+ * Schedule some work.
+ */
+int
+dca_start(dca_t *dca, dca_request_t *reqp, int mcr, int dosched)
+{
+	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
+
+	mutex_enter(&wlp->dwl_lock);
+
+	DBG(dca, DCHATTY, "req=%p, in=%p, out=%p, ctx=%p, ibuf=%p, obuf=%p",
+	    reqp, reqp->dr_in, reqp->dr_out, reqp->dr_ctx_kaddr,
+	    reqp->dr_ibuf_kaddr, reqp->dr_obuf_kaddr);
+	DBG(dca, DCHATTY, "ctx paddr = %x, ibuf paddr = %x, obuf paddr = %x",
+	    reqp->dr_ctx_paddr, reqp->dr_ibuf_paddr, reqp->dr_obuf_paddr);
+	/* sync out the entire context and descriptor chains */
+	(void) ddi_dma_sync(reqp->dr_ctx_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
+	if (dca_check_dma_handle(dca, reqp->dr_ctx_dmah,
+	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+		reqp->destroy = TRUE;
+		mutex_exit(&wlp->dwl_lock);
+		return (CRYPTO_DEVICE_ERROR);
+	}
+
+	dca_enqueue(&wlp->dwl_waitq, (dca_listnode_t *)reqp);
+	wlp->dwl_count++;
+	wlp->dwl_lastsubmit = ddi_get_lbolt();
+	reqp->dr_wlp = wlp;
+
+	if ((wlp->dwl_count == wlp->dwl_hiwater) && (wlp->dwl_busy == 0)) {
+		/* we are fully loaded now, let kCF know */
+
+		wlp->dwl_flowctl++;
+		wlp->dwl_busy = 1;
+
+		crypto_prov_notify(wlp->dwl_prov, CRYPTO_PROVIDER_BUSY);
+	}
+
+	if (dosched) {
+#ifdef	SCHEDDELAY
+		/* possibly wait for more work to arrive */
+		if (wlp->dwl_count >= wlp->dwl_reqspermcr) {
+			dca_schedule(dca, mcr);
+		} else if (!wlp->dwl_schedtid) {
+			/* wait 1 msec for more work before doing it */
+			wlp->dwl_schedtid = timeout(dca_schedtimeout,
+			    (void *)wlp, drv_usectohz(MSEC));
+		}
+#else
+		dca_schedule(dca, mcr);
+#endif
+	}
+	mutex_exit(&wlp->dwl_lock);
+
+	return (CRYPTO_QUEUED);
+}
+
+void
+dca_schedule(dca_t *dca, int mcr)
+{
+	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
+	int		csr;
+	int		full;
+	uint32_t	status;
+
+	ASSERT(mutex_owned(&wlp->dwl_lock));
+	/*
+	 * If the card is draining or has an outstanding failure,
+	 * don't schedule any more work on it right now
+	 */
+	if (wlp->dwl_drain || (dca->dca_flags & DCA_FAILED)) {
+		return;
+	}
+
+	if (mcr == MCR2) {
+		csr = CSR_MCR2;
+		full = DMASTAT_MCR2FULL;
+	} else {
+		csr = CSR_MCR1;
+		full = DMASTAT_MCR1FULL;
+	}
+
+	for (;;) {
+		dca_work_t	*workp;
+		uint32_t	offset;
+		int		nreqs;
+
+		status = GETCSR(dca, CSR_DMASTAT);
+		if (dca_check_acc_handle(dca, dca->dca_regs_handle,
+		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
+			return;
+
+		if ((status & full) != 0)
+			break;
+
+#ifdef	SCHEDDELAY
+		/* if there isn't enough to do, don't bother now */
+		if ((wlp->dwl_count < wlp->dwl_reqspermcr) &&
+		    (ddi_get_lbolt() < (wlp->dwl_lastsubmit +
+			drv_usectohz(MSEC)))) {
+			/* wait a bit longer... */
+			if (wlp->dwl_schedtid == 0) {
+				wlp->dwl_schedtid = timeout(dca_schedtimeout,
+				    (void *)wlp, drv_usectohz(MSEC));
+			}
+			return;
+		}
+#endif
+
+		/* grab a work structure */
+		workp = dca_getwork(dca, mcr);
+
+		if (workp == NULL) {
+			/*
+			 * There must be work ready to be reclaimed,
+			 * in this case, since the chip can only hold
+			 * less work outstanding than there are total.
+			 */
+			dca_reclaim(dca, mcr);
+			continue;
+		}
+
+		nreqs = 0;
+		offset = MCR_CTXADDR;
+
+		while (nreqs < wlp->dwl_reqspermcr) {
+			dca_request_t	*reqp;
+
+			reqp = (dca_request_t *)dca_dequeue(&wlp->dwl_waitq);
+			if (reqp == NULL) {
+				/* nothing left to process */
+				break;
+			}
+			/*
+			 * Update flow control.
+			 */
+			wlp->dwl_count--;
+			if ((wlp->dwl_count == wlp->dwl_lowater) &&
+			    (wlp->dwl_busy))  {
+				wlp->dwl_busy = 0;
+				crypto_prov_notify(wlp->dwl_prov,
+				    CRYPTO_PROVIDER_READY);
+			}
+
+			/*
+			 * Context address.
+			 */
+			PUTMCR32(workp, offset, reqp->dr_ctx_paddr);
+			offset += 4;
+
+			/*
+			 * Input chain.
+			 */
+			/* input buffer address */
+			PUTMCR32(workp, offset, reqp->dr_in_paddr);
+			offset += 4;
+			/* next input buffer entry */
+			PUTMCR32(workp, offset, reqp->dr_in_next);
+			offset += 4;
+			/* input buffer length */
+			PUTMCR16(workp, offset, reqp->dr_in_len);
+			offset += 2;
+			/* zero the reserved field */
+			PUTMCR16(workp, offset, 0);
+			offset += 2;
+
+			/*
+			 * Overall length.
+			 */
+			/* reserved field */
+			PUTMCR16(workp, offset, 0);
+			offset += 2;
+			/* total packet length */
+			PUTMCR16(workp, offset, reqp->dr_pkt_length);
+			offset += 2;
+
+			/*
+			 * Output chain.
+			 */
+			/* output buffer address */
+			PUTMCR32(workp, offset, reqp->dr_out_paddr);
+			offset += 4;
+			/* next output buffer entry */
+			PUTMCR32(workp, offset, reqp->dr_out_next);
+			offset += 4;
+			/* output buffer length */
+			PUTMCR16(workp, offset, reqp->dr_out_len);
+			offset += 2;
+			/* zero the reserved field */
+			PUTMCR16(workp, offset, 0);
+			offset += 2;
+
+			/*
+			 * Note submission.
+			 */
+			workp->dw_reqs[nreqs] = reqp;
+			nreqs++;
+		}
+
+		if (nreqs == 0) {
+			/* nothing in the queue! */
+			dca_freework(workp);
+			return;
+		}
+
+		wlp->dwl_submit++;
+
+		PUTMCR16(workp, MCR_FLAGS, 0);
+		PUTMCR16(workp, MCR_COUNT, nreqs);
+
+		DBG(dca, DCHATTY,
+		    "posting work (phys %x, virt 0x%p) (%d reqs) to MCR%d",
+		    workp->dw_mcr_paddr, workp->dw_mcr_kaddr,
+		    nreqs, mcr);
+
+		workp->dw_lbolt = ddi_get_lbolt();
+		/* Make sure MCR is synced out to device. */
+		(void) ddi_dma_sync(workp->dw_mcr_dmah, 0, 0,
+			DDI_DMA_SYNC_FORDEV);
+		if (dca_check_dma_handle(dca, workp->dw_mcr_dmah,
+		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+			dca_destroywork(workp);
+			return;
+		}
+
+		PUTCSR(dca, csr, workp->dw_mcr_paddr);
+		if (dca_check_acc_handle(dca, dca->dca_regs_handle,
+		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+			dca_destroywork(workp);
+			return;
+		} else {
+			dca_enqueue(&wlp->dwl_runq, (dca_listnode_t *)workp);
+		}
+
+		DBG(dca, DCHATTY, "posted");
+	}
+}
+
+/*
+ * Reclaim completed work, called in interrupt context.
+ */
+void
+dca_reclaim(dca_t *dca, int mcr)
+{
+	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
+	dca_work_t	*workp;
+	ushort_t	flags;
+	int		nreclaimed = 0;
+	int		i;
+
+	DBG(dca, DRECLAIM, "worklist = 0x%p (MCR%d)", wlp, mcr);
+	ASSERT(mutex_owned(&wlp->dwl_lock));
+	/*
+	 * For each MCR in the submitted (runq), we check to see if
+	 * it has been processed.  If so, then we note each individual
+	 * job in the MCR, and and do the completion processing for
+	 * each of such job.
+	 */
+	for (;;) {
+
+		workp = (dca_work_t *)dca_peekqueue(&wlp->dwl_runq);
+		if (workp == NULL) {
+			break;
+		}
+
+		/* only sync the MCR flags, since that's all we need */
+		(void) ddi_dma_sync(workp->dw_mcr_dmah, 0, 4,
+			DDI_DMA_SYNC_FORKERNEL);
+		if (dca_check_dma_handle(dca, workp->dw_mcr_dmah,
+		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+			dca_rmqueue((dca_listnode_t *)workp);
+			dca_destroywork(workp);
+			return;
+		}
+
+		flags = GETMCR16(workp, MCR_FLAGS);
+		if ((flags & MCRFLAG_FINISHED) == 0) {
+			/* chip is still working on it */
+			DBG(dca, DRECLAIM,
+			    "chip still working on it (MCR%d)", mcr);
+			break;
+		}
+
+		/* its really for us, so remove it from the queue */
+		dca_rmqueue((dca_listnode_t *)workp);
+
+		/* if we were draining, signal on the cv */
+		if (wlp->dwl_drain && QEMPTY(&wlp->dwl_runq)) {
+			cv_signal(&wlp->dwl_cv);
+		}
+
+		/* update statistics, done under the lock */
+		for (i = 0; i < wlp->dwl_reqspermcr; i++) {
+			dca_request_t *reqp = workp->dw_reqs[i];
+			if (reqp == NULL) {
+				continue;
+			}
+			if (reqp->dr_byte_stat >= 0) {
+				dca->dca_stats[reqp->dr_byte_stat] +=
+				    reqp->dr_pkt_length;
+			}
+			if (reqp->dr_job_stat >= 0) {
+				dca->dca_stats[reqp->dr_job_stat]++;
+			}
+		}
+		mutex_exit(&wlp->dwl_lock);
+
+		for (i = 0; i < wlp->dwl_reqspermcr; i++) {
+			dca_request_t *reqp = workp->dw_reqs[i];
+
+			if (reqp == NULL) {
+				continue;
+			}
+
+			/* Do the callback. */
+			workp->dw_reqs[i] = NULL;
+			dca_done(reqp, CRYPTO_SUCCESS);
+
+			nreclaimed++;
+		}
+
+		mutex_enter(&wlp->dwl_lock);
+
+		/* now we can release the work */
+		dca_freework(workp);
+	}
+	DBG(dca, DRECLAIM, "reclaimed %d cmds", nreclaimed);
+}
+
+int
+dca_length(crypto_data_t *cdata)
+{
+	return (cdata->cd_length);
+}
+
+/*
+ * This is the callback function called from the interrupt when a kCF job
+ * completes.  It does some driver-specific things, and then calls the
+ * kCF-provided callback.  Finally, it cleans up the state for the work
+ * request and drops the reference count to allow for DR.
+ */
+void
+dca_done(dca_request_t *reqp, int err)
+{
+	uint64_t	ena = 0;
+
+	/* unbind any chains we were using */
+	if (dca_unbindchains(reqp) != DDI_SUCCESS) {
+		/* DMA failure */
+		ena = dca_ena(ena);
+		dca_failure(reqp->dr_dca, DDI_DATAPATH_FAULT,
+		    DCA_FM_ECLASS_NONE, ena, CRYPTO_DEVICE_ERROR,
+		    "fault on buffer DMA handle");
+		if (err == CRYPTO_SUCCESS) {
+			err = CRYPTO_DEVICE_ERROR;
+		}
+	}
+
+	if (reqp->dr_callback != NULL) {
+		reqp->dr_callback(reqp, err);
+	} else {
+		dca_freereq(reqp);
+	}
+}
+
+/*
+ * Call this when a failure is detected.  It will reset the chip,
+ * log a message, alert kCF, and mark jobs in the runq as failed.
+ */
+/* ARGSUSED */
+void
+dca_failure(dca_t *dca, ddi_fault_location_t loc, dca_fma_eclass_t index,
+    uint64_t ena, int errno, char *mess, ...)
+{
+	va_list	ap;
+	char	buf[256];
+	int	mcr;
+	char	*eclass;
+	int	have_mutex;
+
+	va_start(ap, mess);
+	(void) vsprintf(buf, mess, ap);
+	va_end(ap);
+
+	eclass = dca_fma_eclass_string(dca->dca_model, index);
+
+	if (DDI_FM_EREPORT_CAP(dca->fm_capabilities) &&
+	    index != DCA_FM_ECLASS_NONE) {
+		ddi_fm_ereport_post(dca->dca_dip, eclass, ena,
+		    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8,
+		    FM_EREPORT_VERS0, NULL);
+
+		/* Report the impact of the failure to the DDI. */
+		ddi_fm_service_impact(dca->dca_dip, DDI_SERVICE_LOST);
+	} else {
+		/* Just log the error string to the message log */
+		dca_error(dca, buf);
+	}
+
+	/*
+	 * Indicate a failure (keeps schedule from running).
+	 */
+	dca->dca_flags |= DCA_FAILED;
+
+	/*
+	 * Reset the chip.  This should also have as a side effect, the
+	 * disabling of all interrupts from the device.
+	 */
+	(void) dca_reset(dca, 1);
+
+	/*
+	 * Report the failure to kCF.
+	 */
+	for (mcr = MCR1; mcr <= MCR2; mcr++) {
+		if (WORKLIST(dca, mcr)->dwl_prov) {
+			crypto_prov_notify(WORKLIST(dca, mcr)->dwl_prov,
+			    CRYPTO_PROVIDER_FAILED);
+		}
+	}
+
+	/*
+	 * Return jobs not sent to hardware back to kCF.
+	 */
+	dca_rejectjobs(dca);
+
+	/*
+	 * From this point on, no new work should be arriving, and the
+	 * chip should not be doing any active DMA.
+	 */
+
+	/*
+	 * Now find all the work submitted to the device and fail
+	 * them.
+	 */
+	for (mcr = MCR1; mcr <= MCR2; mcr++) {
+		dca_worklist_t	*wlp;
+		int		i;
+
+		wlp = WORKLIST(dca, mcr);
+
+		if (wlp == NULL || wlp->dwl_waitq.dl_prev == NULL) {
+			continue;
+		}
+		for (;;) {
+			dca_work_t	*workp;
+
+			have_mutex = mutex_tryenter(&wlp->dwl_lock);
+			workp = (dca_work_t *)dca_dequeue(&wlp->dwl_runq);
+			if (workp == NULL) {
+				if (have_mutex)
+					mutex_exit(&wlp->dwl_lock);
+				break;
+			}
+			mutex_exit(&wlp->dwl_lock);
+
+			/*
+			 * Free up requests
+			 */
+			for (i = 0; i < wlp->dwl_reqspermcr; i++) {
+				dca_request_t *reqp = workp->dw_reqs[i];
+				if (reqp) {
+					if (reqp->dr_flags & DR_INPLACE) {
+						dca_done(reqp, errno);
+					} else {
+						/*
+						 * cause it to get retried
+						 * elsewhere (software)
+						 */
+						dca_done(reqp, CRYPTO_FAILED);
+					}
+					workp->dw_reqs[i] = NULL;
+				}
+			}
+
+			mutex_enter(&wlp->dwl_lock);
+			/*
+			 * If waiting to drain, signal on the waiter.
+			 */
+			if (wlp->dwl_drain && QEMPTY(&wlp->dwl_runq)) {
+				cv_signal(&wlp->dwl_cv);
+			}
+
+			/*
+			 * Return the work and request structures to
+			 * the free pool.
+			 */
+			dca_freework(workp);
+			if (have_mutex)
+				mutex_exit(&wlp->dwl_lock);
+		}
+	}
+
+}
+
+#ifdef	SCHEDDELAY
+/*
+ * Reschedule worklist as needed.
+ */
+void
+dca_schedtimeout(void *arg)
+{
+	dca_worklist_t	*wlp = (dca_worklist_t *)arg;
+	mutex_enter(&wlp->dwl_lock);
+	wlp->dwl_schedtid = 0;
+	dca_schedule(wlp->dwl_dca, wlp->dwl_mcr);
+	mutex_exit(&wlp->dwl_lock);
+}
+#endif
+
+/*
+ * Check for stalled jobs.
+ */
+void
+dca_jobtimeout(void *arg)
+{
+	int		mcr;
+	dca_t		*dca = (dca_t *)arg;
+	int		hung = 0;
+
+	for (mcr = MCR1; mcr <= MCR2; mcr++) {
+		dca_worklist_t	*wlp = WORKLIST(dca, mcr);
+		dca_work_t	*workp;
+		clock_t		when;
+
+		mutex_enter(&wlp->dwl_lock);
+		when = ddi_get_lbolt();
+
+		workp = (dca_work_t *)dca_peekqueue(&wlp->dwl_runq);
+		if (workp == NULL) {
+			/* nothing sitting in the queue */
+			mutex_exit(&wlp->dwl_lock);
+			continue;
+		}
+
+		if ((when - workp->dw_lbolt) < drv_usectohz(STALETIME)) {
+			/* request has been queued for less than STALETIME */
+			mutex_exit(&wlp->dwl_lock);
+			continue;
+		}
+
+		/* job has been sitting around for over 1 second, badness */
+		DBG(dca, DWARN, "stale job (0x%p) found in MCR%d!", workp,
+		    mcr);
+
+		/* put it back in the queue, until we reset the chip */
+		hung++;
+		mutex_exit(&wlp->dwl_lock);
+	}
+
+	if (hung) {
+		dca_failure(dca, DDI_DEVICE_FAULT,
+		    DCA_FM_ECLASS_HW_TIMEOUT, dca_ena(0), CRYPTO_DEVICE_ERROR,
+		    "timeout processing job.)");
+	}
+
+	/* reschedule ourself */
+	mutex_enter(&dca->dca_intrlock);
+	if (dca->dca_jobtid == 0) {
+		/* timeout has been canceled, prior to DR */
+		mutex_exit(&dca->dca_intrlock);
+		return;
+	}
+
+	/* check again in 1 second */
+	dca->dca_jobtid = timeout(dca_jobtimeout, arg,
+	    drv_usectohz(SECOND));
+	mutex_exit(&dca->dca_intrlock);
+}
+
+/*
+ * This returns all jobs back to kCF.  It assumes that processing
+ * on the worklist has halted.
+ */
+void
+dca_rejectjobs(dca_t *dca)
+{
+	int mcr;
+	int have_mutex;
+	for (mcr = MCR1; mcr <= MCR2; mcr++) {
+		dca_worklist_t	*wlp = WORKLIST(dca, mcr);
+		dca_request_t	*reqp;
+
+		if (wlp == NULL || wlp->dwl_waitq.dl_prev == NULL) {
+			continue;
+		}
+		have_mutex = mutex_tryenter(&wlp->dwl_lock);
+		for (;;) {
+			reqp = (dca_request_t *)dca_unqueue(&wlp->dwl_waitq);
+			if (reqp == NULL) {
+				break;
+			}
+			/* update flow control */
+			wlp->dwl_count--;
+			if ((wlp->dwl_count == wlp->dwl_lowater) &&
+			    (wlp->dwl_busy))  {
+				wlp->dwl_busy = 0;
+				crypto_prov_notify(wlp->dwl_prov,
+				    CRYPTO_PROVIDER_READY);
+			}
+			mutex_exit(&wlp->dwl_lock);
+
+			(void) dca_unbindchains(reqp);
+			reqp->dr_callback(reqp, EAGAIN);
+			mutex_enter(&wlp->dwl_lock);
+		}
+		if (have_mutex)
+			mutex_exit(&wlp->dwl_lock);
+	}
+}
+
+int
+dca_drain(dca_t *dca)
+{
+	int mcr;
+	for (mcr = MCR1; mcr <= MCR2; mcr++) {
+#ifdef	SCHEDDELAY
+		timeout_id_t	tid;
+#endif
+		dca_worklist_t *wlp = WORKLIST(dca, mcr);
+
+		mutex_enter(&wlp->dwl_lock);
+		wlp->dwl_drain = 1;
+
+		/* give it up to a second to drain from the chip */
+		if (!QEMPTY(&wlp->dwl_runq)) {
+			(void) cv_timedwait(&wlp->dwl_cv, &wlp->dwl_lock,
+			    ddi_get_time() + drv_usectohz(STALETIME));
+
+			if (!QEMPTY(&wlp->dwl_runq)) {
+				dca_error(dca, "unable to drain device");
+				mutex_exit(&wlp->dwl_lock);
+				dca_undrain(dca);
+				return (EBUSY);
+			}
+		}
+
+#ifdef	SCHEDDELAY
+		tid = wlp->dwl_schedtid;
+		mutex_exit(&wlp->dwl_lock);
+
+		/*
+		 * untimeout outside the lock -- this is safe because we
+		 * have set the drain flag, so dca_schedule() will not
+		 * reschedule another timeout
+		 */
+		if (tid) {
+			untimeout(tid);
+		}
+#else
+		mutex_exit(&wlp->dwl_lock);
+#endif
+	}
+	return (0);
+}
+
+void
+dca_undrain(dca_t *dca)
+{
+	int	mcr;
+
+	for (mcr = MCR1; mcr <= MCR2; mcr++) {
+		dca_worklist_t	*wlp = WORKLIST(dca, mcr);
+		mutex_enter(&wlp->dwl_lock);
+		wlp->dwl_drain = 0;
+		dca_schedule(dca, mcr);
+		mutex_exit(&wlp->dwl_lock);
+	}
+}
+
+/*
+ * Duplicate the crypto_data_t structure, but point to the original
+ * buffers.
+ */
+int
+dca_dupcrypto(crypto_data_t *input, crypto_data_t *ninput)
+{
+	ninput->cd_format = input->cd_format;
+	ninput->cd_offset = input->cd_offset;
+	ninput->cd_length = input->cd_length;
+	ninput->cd_miscdata = input->cd_miscdata;
+
+	switch (input->cd_format) {
+	case CRYPTO_DATA_RAW:
+		ninput->cd_raw.iov_base = input->cd_raw.iov_base;
+		ninput->cd_raw.iov_len = input->cd_raw.iov_len;
+		break;
+
+	case CRYPTO_DATA_UIO:
+		ninput->cd_uio = input->cd_uio;
+		break;
+
+	case CRYPTO_DATA_MBLK:
+		ninput->cd_mp = input->cd_mp;
+		break;
+
+	default:
+		DBG(NULL, DWARN,
+		    "dca_dupcrypto: unrecognised crypto data format");
+		return (CRYPTO_FAILED);
+	}
+
+	return (CRYPTO_SUCCESS);
+}
+
+/*
+ * Performs validation checks on the input and output data structures.
+ */
+int
+dca_verifyio(crypto_data_t *input, crypto_data_t *output)
+{
+	int	rv = CRYPTO_SUCCESS;
+
+	switch (input->cd_format) {
+	case CRYPTO_DATA_RAW:
+		break;
+
+	case CRYPTO_DATA_UIO:
+		/* we support only kernel buffer */
+		if (input->cd_uio->uio_segflg != UIO_SYSSPACE) {
+			DBG(NULL, DWARN, "non kernel input uio buffer");
+			rv = CRYPTO_ARGUMENTS_BAD;
+		}
+		break;
+
+	case CRYPTO_DATA_MBLK:
+		break;
+
+	default:
+		DBG(NULL, DWARN, "unrecognised input crypto data format");
+		rv = CRYPTO_ARGUMENTS_BAD;
+	}
+
+	switch (output->cd_format) {
+	case CRYPTO_DATA_RAW:
+		break;
+
+	case CRYPTO_DATA_UIO:
+		/* we support only kernel buffer */
+		if (output->cd_uio->uio_segflg != UIO_SYSSPACE) {
+			DBG(NULL, DWARN, "non kernel output uio buffer");
+			rv = CRYPTO_ARGUMENTS_BAD;
+		}
+		break;
+
+	case CRYPTO_DATA_MBLK:
+		break;
+
+	default:
+		DBG(NULL, DWARN, "unrecognised output crypto data format");
+		rv = CRYPTO_ARGUMENTS_BAD;
+	}
+
+	return (rv);
+}
+
+/*
+ * data: source crypto_data_t struct
+ * off:	offset into the source before commencing copy
+ * count: the amount of data to copy
+ * dest: destination buffer
+ */
+int
+dca_getbufbytes(crypto_data_t *data, size_t off, int count, uchar_t *dest)
+{
+	int rv = CRYPTO_SUCCESS;
+	uio_t *uiop;
+	uint_t vec_idx;
+	size_t cur_len;
+	mblk_t *mp;
+
+	if (count == 0) {
+		/* We don't want anything so we're done. */
+		return (rv);
+	}
+
+	/*
+	 * Sanity check that we haven't specified a length greater than the
+	 * offset adjusted size of the buffer.
+	 */
+	if (count > (data->cd_length - off)) {
+		return (CRYPTO_DATA_LEN_RANGE);
+	}
+
+	/* Add the internal crypto_data offset to the requested offset. */
+	off += data->cd_offset;
+
+	switch (data->cd_format) {
+	case CRYPTO_DATA_RAW:
+		bcopy(data->cd_raw.iov_base + off, dest, count);
+		break;
+
+	case CRYPTO_DATA_UIO:
+		/*
+		 * Jump to the first iovec containing data to be
+		 * processed.
+		 */
+		uiop = data->cd_uio;
+		for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
+		    off >= uiop->uio_iov[vec_idx].iov_len;
+		    off -= uiop->uio_iov[vec_idx++].iov_len);
+		if (vec_idx == uiop->uio_iovcnt) {
+			/*
+			 * The caller specified an offset that is larger than
+			 * the total size of the buffers it provided.
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+
+		/*
+		 * Now process the iovecs.
+		 */
+		while (vec_idx < uiop->uio_iovcnt && count > 0) {
+			cur_len = min(uiop->uio_iov[vec_idx].iov_len -
+			    off, count);
+			bcopy(uiop->uio_iov[vec_idx].iov_base + off, dest,
+			    cur_len);
+			count -= cur_len;
+			dest += cur_len;
+			vec_idx++;
+			off = 0;
+		}
+
+		if (vec_idx == uiop->uio_iovcnt && count > 0) {
+			/*
+			 * The end of the specified iovec's was reached but
+			 * the length requested could not be processed
+			 * (requested to digest more data than it provided).
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+		break;
+
+	case CRYPTO_DATA_MBLK:
+		/*
+		 * Jump to the first mblk_t containing data to be processed.
+		 */
+		for (mp = data->cd_mp; mp != NULL && off >= MBLKL(mp);
+		    off -= MBLKL(mp), mp = mp->b_cont);
+		if (mp == NULL) {
+			/*
+			 * The caller specified an offset that is larger than
+			 * the total size of the buffers it provided.
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+
+		/*
+		 * Now do the processing on the mblk chain.
+		 */
+		while (mp != NULL && count > 0) {
+			cur_len = min(MBLKL(mp) - off, count);
+			bcopy((char *)(mp->b_rptr + off), dest, cur_len);
+			count -= cur_len;
+			dest += cur_len;
+			mp = mp->b_cont;
+			off = 0;
+		}
+
+		if (mp == NULL && count > 0) {
+			/*
+			 * The end of the mblk was reached but the length
+			 * requested could not be processed, (requested to
+			 * digest more data than it provided).
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+		break;
+
+	default:
+		DBG(NULL, DWARN, "unrecognised crypto data format");
+		rv = CRYPTO_ARGUMENTS_BAD;
+	}
+	return (rv);
+}
+
+
+/*
+ * Performs the input, output or hard scatter/gather checks on the specified
+ * crypto_data_t struct. Returns true if the data is scatter/gather in nature
+ * ie fails the test.
+ */
+int
+dca_sgcheck(dca_t *dca, crypto_data_t *data, dca_sg_param_t val)
+{
+	uio_t *uiop;
+	mblk_t *mp;
+	int rv = FALSE;
+
+	switch (val) {
+	case DCA_SG_CONTIG:
+		/*
+		 * Check for a contiguous data buffer.
+		 */
+		switch (data->cd_format) {
+		case CRYPTO_DATA_RAW:
+			/* Contiguous in nature */
+			break;
+
+		case CRYPTO_DATA_UIO:
+			if (data->cd_uio->uio_iovcnt > 1)
+				rv = TRUE;
+			break;
+
+		case CRYPTO_DATA_MBLK:
+			mp = data->cd_mp;
+			if (mp->b_cont != NULL)
+				rv = TRUE;
+			break;
+
+		default:
+			DBG(NULL, DWARN, "unrecognised crypto data format");
+		}
+		break;
+
+	case DCA_SG_WALIGN:
+		/*
+		 * Check for a contiguous data buffer that is 32-bit word
+		 * aligned and is of word multiples in size.
+		 */
+		switch (data->cd_format) {
+		case CRYPTO_DATA_RAW:
+			if ((data->cd_raw.iov_len % sizeof (uint32_t)) ||
+			    ((uintptr_t)data->cd_raw.iov_base %
+			    sizeof (uint32_t))) {
+				rv = TRUE;
+			}
+			break;
+
+		case CRYPTO_DATA_UIO:
+			uiop = data->cd_uio;
+			if (uiop->uio_iovcnt > 1) {
+				return (TRUE);
+			}
+			/* So there is only one iovec */
+			if ((uiop->uio_iov[0].iov_len % sizeof (uint32_t)) ||
+			    ((uintptr_t)uiop->uio_iov[0].iov_base %
+			    sizeof (uint32_t))) {
+				rv = TRUE;
+			}
+			break;
+
+		case CRYPTO_DATA_MBLK:
+			mp = data->cd_mp;
+			if (mp->b_cont != NULL) {
+				return (TRUE);
+			}
+			/* So there is only one mblk in the chain */
+			if ((MBLKL(mp) % sizeof (uint32_t)) ||
+			    ((uintptr_t)mp->b_rptr % sizeof (uint32_t))) {
+				rv = TRUE;
+			}
+			break;
+
+		default:
+			DBG(NULL, DWARN, "unrecognised crypto data format");
+		}
+		break;
+
+	case DCA_SG_PALIGN:
+		/*
+		 * Check that the data buffer is page aligned and is of
+		 * page multiples in size.
+		 */
+		switch (data->cd_format) {
+		case CRYPTO_DATA_RAW:
+			if ((data->cd_length % dca->dca_pagesize) ||
+			    ((uintptr_t)data->cd_raw.iov_base %
+			    dca->dca_pagesize)) {
+				rv = TRUE;
+			}
+			break;
+
+		case CRYPTO_DATA_UIO:
+			uiop = data->cd_uio;
+			if ((uiop->uio_iov[0].iov_len % dca->dca_pagesize) ||
+			    ((uintptr_t)uiop->uio_iov[0].iov_base %
+			    dca->dca_pagesize)) {
+				rv = TRUE;
+			}
+			break;
+
+		case CRYPTO_DATA_MBLK:
+			mp = data->cd_mp;
+			if ((MBLKL(mp) % dca->dca_pagesize) ||
+			    ((uintptr_t)mp->b_rptr % dca->dca_pagesize)) {
+				rv = TRUE;
+			}
+			break;
+
+		default:
+			DBG(NULL, DWARN, "unrecognised crypto data format");
+		}
+		break;
+
+	default:
+		DBG(NULL, DWARN, "unrecognised scatter/gather param type");
+	}
+
+	return (rv);
+}
+
+/*
+ * Increments the cd_offset and decrements the cd_length as the data is
+ * gathered from the crypto_data_t struct.
+ * The data is reverse-copied into the dest buffer if the flag is true.
+ */
+int
+dca_gather(crypto_data_t *in, char *dest, int count, int reverse)
+{
+	int	rv = CRYPTO_SUCCESS;
+	uint_t	vec_idx;
+	uio_t	*uiop;
+	off_t	off = in->cd_offset;
+	size_t	cur_len;
+	mblk_t	*mp;
+
+	switch (in->cd_format) {
+	case CRYPTO_DATA_RAW:
+		if (count > in->cd_length) {
+			/*
+			 * The caller specified a length greater than the
+			 * size of the buffer.
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+		if (reverse)
+			dca_reverse(in->cd_raw.iov_base + off, dest, count,
+			    count);
+		else
+			bcopy(in->cd_raw.iov_base + in->cd_offset, dest, count);
+		in->cd_offset += count;
+		in->cd_length -= count;
+		break;
+
+	case CRYPTO_DATA_UIO:
+		/*
+		 * Jump to the first iovec containing data to be processed.
+		 */
+		uiop = in->cd_uio;
+		for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
+		    off >= uiop->uio_iov[vec_idx].iov_len;
+		    off -= uiop->uio_iov[vec_idx++].iov_len);
+		if (vec_idx == uiop->uio_iovcnt) {
+			/*
+			 * The caller specified an offset that is larger than
+			 * the total size of the buffers it provided.
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+
+		/*
+		 * Now process the iovecs.
+		 */
+		while (vec_idx < uiop->uio_iovcnt && count > 0) {
+			cur_len = min(uiop->uio_iov[vec_idx].iov_len -
+			    off, count);
+			count -= cur_len;
+			if (reverse) {
+				/* Fill the dest buffer from the end */
+				dca_reverse(uiop->uio_iov[vec_idx].iov_base +
+				    off, dest+count, cur_len, cur_len);
+			} else {
+				bcopy(uiop->uio_iov[vec_idx].iov_base + off,
+				    dest, cur_len);
+				dest += cur_len;
+			}
+			in->cd_offset += cur_len;
+			in->cd_length -= cur_len;
+			vec_idx++;
+			off = 0;
+		}
+
+		if (vec_idx == uiop->uio_iovcnt && count > 0) {
+			/*
+			 * The end of the specified iovec's was reached but
+			 * the length requested could not be processed
+			 * (requested to digest more data than it provided).
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+		break;
+
+	case CRYPTO_DATA_MBLK:
+		/*
+		 * Jump to the first mblk_t containing data to be processed.
+		 */
+		for (mp = in->cd_mp; mp != NULL && off >= MBLKL(mp);
+		    off -= MBLKL(mp), mp = mp->b_cont);
+		if (mp == NULL) {
+			/*
+			 * The caller specified an offset that is larger than
+			 * the total size of the buffers it provided.
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+
+		/*
+		 * Now do the processing on the mblk chain.
+		 */
+		while (mp != NULL && count > 0) {
+			cur_len = min(MBLKL(mp) - off, count);
+			count -= cur_len;
+			if (reverse) {
+				/* Fill the dest buffer from the end */
+				dca_reverse((char *)(mp->b_rptr + off),
+				    dest+count, cur_len, cur_len);
+			} else {
+				bcopy((char *)(mp->b_rptr + off), dest,
+				    cur_len);
+				dest += cur_len;
+			}
+			in->cd_offset += cur_len;
+			in->cd_length -= cur_len;
+			mp = mp->b_cont;
+			off = 0;
+		}
+
+		if (mp == NULL && count > 0) {
+			/*
+			 * The end of the mblk was reached but the length
+			 * requested could not be processed, (requested to
+			 * digest more data than it provided).
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+		break;
+
+	default:
+		DBG(NULL, DWARN, "dca_gather: unrecognised crypto data format");
+		rv = CRYPTO_ARGUMENTS_BAD;
+	}
+	return (rv);
+}
+
+/*
+ * Increments the cd_offset and decrements the cd_length as the data is
+ * gathered from the crypto_data_t struct.
+ */
+int
+dca_resid_gather(crypto_data_t *in, char *resid, int *residlen, char *dest,
+    int count)
+{
+	int	rv = CRYPTO_SUCCESS;
+	caddr_t	baddr;
+	uint_t	vec_idx;
+	uio_t	*uiop;
+	off_t	off = in->cd_offset;
+	size_t	cur_len;
+	mblk_t	*mp;
+
+	/* Process the residual first */
+	if (*residlen > 0) {
+		uint_t	num = min(count, *residlen);
+		bcopy(resid, dest, num);
+		*residlen -= num;
+		if (*residlen > 0) {
+			/*
+			 * Requested amount 'count' is less than what's in
+			 * the residual, so shuffle any remaining resid to
+			 * the front.
+			 */
+			baddr = resid + num;
+			bcopy(baddr, resid, *residlen);
+		}
+		dest += num;
+		count -= num;
+	}
+
+	/* Now process what's in the crypto_data_t structs */
+	switch (in->cd_format) {
+	case CRYPTO_DATA_RAW:
+		if (count > in->cd_length) {
+			/*
+			 * The caller specified a length greater than the
+			 * size of the buffer.
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+		bcopy(in->cd_raw.iov_base + in->cd_offset, dest, count);
+		in->cd_offset += count;
+		in->cd_length -= count;
+		break;
+
+	case CRYPTO_DATA_UIO:
+		/*
+		 * Jump to the first iovec containing data to be processed.
+		 */
+		uiop = in->cd_uio;
+		for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
+		    off >= uiop->uio_iov[vec_idx].iov_len;
+		    off -= uiop->uio_iov[vec_idx++].iov_len);
+		if (vec_idx == uiop->uio_iovcnt) {
+			/*
+			 * The caller specified an offset that is larger than
+			 * the total size of the buffers it provided.
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+
+		/*
+		 * Now process the iovecs.
+		 */
+		while (vec_idx < uiop->uio_iovcnt && count > 0) {
+			cur_len = min(uiop->uio_iov[vec_idx].iov_len -
+			    off, count);
+			bcopy(uiop->uio_iov[vec_idx].iov_base + off, dest,
+			    cur_len);
+			count -= cur_len;
+			dest += cur_len;
+			in->cd_offset += cur_len;
+			in->cd_length -= cur_len;
+			vec_idx++;
+			off = 0;
+		}
+
+		if (vec_idx == uiop->uio_iovcnt && count > 0) {
+			/*
+			 * The end of the specified iovec's was reached but
+			 * the length requested could not be processed
+			 * (requested to digest more data than it provided).
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+		break;
+
+	case CRYPTO_DATA_MBLK:
+		/*
+		 * Jump to the first mblk_t containing data to be processed.
+		 */
+		for (mp = in->cd_mp; mp != NULL && off >= MBLKL(mp);
+		    off -= MBLKL(mp), mp = mp->b_cont);
+		if (mp == NULL) {
+			/*
+			 * The caller specified an offset that is larger than
+			 * the total size of the buffers it provided.
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+
+		/*
+		 * Now do the processing on the mblk chain.
+		 */
+		while (mp != NULL && count > 0) {
+			cur_len = min(MBLKL(mp) - off, count);
+			bcopy((char *)(mp->b_rptr + off), dest, cur_len);
+			count -= cur_len;
+			dest += cur_len;
+			in->cd_offset += cur_len;
+			in->cd_length -= cur_len;
+			mp = mp->b_cont;
+			off = 0;
+		}
+
+		if (mp == NULL && count > 0) {
+			/*
+			 * The end of the mblk was reached but the length
+			 * requested could not be processed, (requested to
+			 * digest more data than it provided).
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+		break;
+
+	default:
+		DBG(NULL, DWARN,
+		    "dca_resid_gather: unrecognised crypto data format");
+		rv = CRYPTO_ARGUMENTS_BAD;
+	}
+	return (rv);
+}
+
+/*
+ * Appends the data to the crypto_data_t struct increasing cd_length.
+ * cd_offset is left unchanged.
+ * Data is reverse-copied if the flag is TRUE.
+ */
+int
+dca_scatter(const char *src, crypto_data_t *out, int count, int reverse)
+{
+	int	rv = CRYPTO_SUCCESS;
+	off_t	offset = out->cd_offset + out->cd_length;
+	uint_t	vec_idx;
+	uio_t	*uiop;
+	size_t	cur_len;
+	mblk_t	*mp;
+
+	switch (out->cd_format) {
+	case CRYPTO_DATA_RAW:
+		if (out->cd_raw.iov_len - offset < count) {
+			/* Trying to write out more than space available. */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+		if (reverse)
+			dca_reverse((void*) src, out->cd_raw.iov_base + offset,
+			    count, count);
+		else
+			bcopy(src, out->cd_raw.iov_base + offset, count);
+		out->cd_length += count;
+		break;
+
+	case CRYPTO_DATA_UIO:
+		/*
+		 * Jump to the first iovec that can be written to.
+		 */
+		uiop = out->cd_uio;
+		for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
+		    offset >= uiop->uio_iov[vec_idx].iov_len;
+		    offset -= uiop->uio_iov[vec_idx++].iov_len);
+		if (vec_idx == uiop->uio_iovcnt) {
+			/*
+			 * The caller specified an offset that is larger than
+			 * the total size of the buffers it provided.
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+
+		/*
+		 * Now process the iovecs.
+		 */
+		while (vec_idx < uiop->uio_iovcnt && count > 0) {
+			cur_len = min(uiop->uio_iov[vec_idx].iov_len -
+			    offset, count);
+			count -= cur_len;
+			if (reverse) {
+				dca_reverse((void*) (src+count),
+				    uiop->uio_iov[vec_idx].iov_base +
+				    offset, cur_len, cur_len);
+			} else {
+				bcopy(src, uiop->uio_iov[vec_idx].iov_base +
+				    offset, cur_len);
+				src += cur_len;
+			}
+			out->cd_length += cur_len;
+			vec_idx++;
+			offset = 0;
+		}
+
+		if (vec_idx == uiop->uio_iovcnt && count > 0) {
+			/*
+			 * The end of the specified iovec's was reached but
+			 * the length requested could not be processed
+			 * (requested to write more data than space provided).
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+		break;
+
+	case CRYPTO_DATA_MBLK:
+		/*
+		 * Jump to the first mblk_t that can be written to.
+		 */
+		for (mp = out->cd_mp; mp != NULL && offset >= MBLKL(mp);
+		    offset -= MBLKL(mp), mp = mp->b_cont);
+		if (mp == NULL) {
+			/*
+			 * The caller specified an offset that is larger than
+			 * the total size of the buffers it provided.
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+
+		/*
+		 * Now do the processing on the mblk chain.
+		 */
+		while (mp != NULL && count > 0) {
+			cur_len = min(MBLKL(mp) - offset, count);
+			count -= cur_len;
+			if (reverse) {
+				dca_reverse((void*) (src+count),
+				    (char *)(mp->b_rptr + offset), cur_len,
+				    cur_len);
+			} else {
+				bcopy(src, (char *)(mp->b_rptr + offset),
+				    cur_len);
+				src += cur_len;
+			}
+			out->cd_length += cur_len;
+			mp = mp->b_cont;
+			offset = 0;
+		}
+
+		if (mp == NULL && count > 0) {
+			/*
+			 * The end of the mblk was reached but the length
+			 * requested could not be processed, (requested to
+			 * digest more data than it provided).
+			 */
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+		break;
+
+	default:
+		DBG(NULL, DWARN, "unrecognised crypto data format");
+		rv = CRYPTO_ARGUMENTS_BAD;
+	}
+	return (rv);
+}
+
+/*
+ * Compare two byte arrays in reverse order.
+ * Return 0 if they are identical, 1 otherwise.
+ */
+int
+dca_bcmp_reverse(const void *s1, const void *s2, size_t n)
+{
+	int i;
+	caddr_t src, dst;
+
+	if (!n)
+		return (0);
+
+	src = ((caddr_t)s1) + n - 1;
+	dst = (caddr_t)s2;
+	for (i = 0; i < n; i++) {
+		if (*src != *dst)
+			return (1);
+		src--;
+		dst++;
+	}
+
+	return (0);
+}
+
+
+/*
+ * This calculates the size of a bignum in bits, specifically not counting
+ * leading zero bits.  This size calculation must be done *before* any
+ * endian reversal takes place (i.e. the numbers are in absolute big-endian
+ * order.)
+ */
+int
+dca_bitlen(unsigned char *bignum, int bytelen)
+{
+	unsigned char	msbyte;
+	int		i, j;
+
+	for (i = 0; i < bytelen - 1; i++) {
+		if (bignum[i] != 0) {
+			break;
+		}
+	}
+	msbyte = bignum[i];
+	for (j = 8; j > 1; j--) {
+		if (msbyte & 0x80) {
+			break;
+		}
+		msbyte <<= 1;
+	}
+	return ((8 * (bytelen - i - 1)) + j);
+}
+
+/*
+ * This compares to bignums (in big-endian order).  It ignores leading
+ * null bytes.  The result semantics follow bcmp, mempcmp, strcmp, etc.
+ */
+int
+dca_numcmp(caddr_t n1, int n1len, caddr_t n2, int n2len)
+{
+	while ((n1len > 1) && (*n1 == 0)) {
+		n1len--;
+		n1++;
+	}
+	while ((n2len > 1) && (*n2 == 0)) {
+		n2len--;
+		n2++;
+	}
+	if (n1len != n2len) {
+		return (n1len - n2len);
+	}
+	while ((n1len > 1) && (*n1 == *n2)) {
+		n1++;
+		n2++;
+		n1len--;
+	}
+	return ((int)(*(uchar_t *)n1) - (int)(*(uchar_t *)n2));
+}
+
+/*
+ * Return array of key attributes.
+ */
+crypto_object_attribute_t *
+dca_get_key_attr(crypto_key_t *key)
+{
+	if ((key->ck_format != CRYPTO_KEY_ATTR_LIST) ||
+	    (key->ck_count == 0)) {
+		return (NULL);
+	}
+
+	return (key->ck_attrs);
+}
+
+/*
+ * If attribute type exists valp points to it's 32-bit value.
+ */
+int
+dca_attr_lookup_uint32(crypto_object_attribute_t *attrp, uint_t atnum,
+    uint64_t atype, uint32_t *valp)
+{
+	crypto_object_attribute_t	*bap;
+
+	bap = dca_find_attribute(attrp, atnum, atype);
+	if (bap == NULL) {
+		return (CRYPTO_ATTRIBUTE_TYPE_INVALID);
+	}
+
+	*valp = *bap->oa_value;
+
+	return (CRYPTO_SUCCESS);
+}
+
+/*
+ * If attribute type exists data contains the start address of the value,
+ * and numelems contains it's length.
+ */
+int
+dca_attr_lookup_uint8_array(crypto_object_attribute_t *attrp, uint_t atnum,
+    uint64_t atype, void **data, unsigned int *numelems)
+{
+	crypto_object_attribute_t	*bap;
+
+	bap = dca_find_attribute(attrp, atnum, atype);
+	if (bap == NULL) {
+		return (CRYPTO_ATTRIBUTE_TYPE_INVALID);
+	}
+
+	*data = bap->oa_value;
+	*numelems = bap->oa_value_len;
+
+	return (CRYPTO_SUCCESS);
+}
+
+/*
+ * Finds entry of specified name. If it is not found dca_find_attribute returns
+ * NULL.
+ */
+crypto_object_attribute_t *
+dca_find_attribute(crypto_object_attribute_t *attrp, uint_t atnum,
+    uint64_t atype)
+{
+	while (atnum) {
+		if (attrp->oa_type == atype)
+			return (attrp);
+		atnum--;
+		attrp++;
+	}
+	return (NULL);
+}
+
+/*
+ * Return the address of the first data buffer. If the data format is
+ * unrecognised return NULL.
+ */
+caddr_t
+dca_bufdaddr(crypto_data_t *data)
+{
+	switch (data->cd_format) {
+	case CRYPTO_DATA_RAW:
+		return (data->cd_raw.iov_base + data->cd_offset);
+	case CRYPTO_DATA_UIO:
+		return (data->cd_uio->uio_iov[0].iov_base + data->cd_offset);
+	case CRYPTO_DATA_MBLK:
+		return ((char *)data->cd_mp->b_rptr + data->cd_offset);
+	default:
+		DBG(NULL, DWARN,
+		    "dca_bufdaddr: unrecognised crypto data format");
+		return (NULL);
+	}
+}
+
+static caddr_t
+dca_bufdaddr_out(crypto_data_t *data)
+{
+	size_t offset = data->cd_offset + data->cd_length;
+
+	switch (data->cd_format) {
+	case CRYPTO_DATA_RAW:
+		return (data->cd_raw.iov_base + offset);
+	case CRYPTO_DATA_UIO:
+		return (data->cd_uio->uio_iov[0].iov_base + offset);
+	case CRYPTO_DATA_MBLK:
+		return ((char *)data->cd_mp->b_rptr + offset);
+	default:
+		DBG(NULL, DWARN,
+		    "dca_bufdaddr_out: unrecognised crypto data format");
+		return (NULL);
+	}
+}
+
+/*
+ * Control entry points.
+ */
+
+/* ARGSUSED */
+static void
+dca_provider_status(crypto_provider_handle_t provider, uint_t *status)
+{
+	*status = CRYPTO_PROVIDER_READY;
+}
+
+/*
+ * Cipher (encrypt/decrypt) entry points.
+ */
+
+/* ARGSUSED */
+static int
+dca_encrypt_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_encrypt_init: started");
+
+	/* check mechanism */
+	switch (mechanism->cm_type) {
+	case DES_CBC_MECH_INFO_TYPE:
+		error = dca_3desctxinit(ctx, mechanism, key, KM_SLEEP,
+		    DR_ENCRYPT);
+		break;
+	case DES3_CBC_MECH_INFO_TYPE:
+		error = dca_3desctxinit(ctx, mechanism, key, KM_SLEEP,
+		    DR_ENCRYPT | DR_TRIPLE);
+		break;
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_encrypt_init: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_encrypt_init: done, err = 0x%x", error);
+
+	if (error == CRYPTO_SUCCESS)
+		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
+		    &softc->dca_ctx_list_lock);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_encrypt(crypto_ctx_t *ctx, crypto_data_t *plaintext,
+    crypto_data_t *ciphertext, crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_encrypt: started");
+
+	/* check mechanism */
+	switch (DCA_MECH_FROM_CTX(ctx)) {
+	case DES_CBC_MECH_INFO_TYPE:
+		error = dca_3des(ctx, plaintext, ciphertext, req, DR_ENCRYPT);
+		break;
+	case DES3_CBC_MECH_INFO_TYPE:
+		error = dca_3des(ctx, plaintext, ciphertext, req,
+		    DR_ENCRYPT | DR_TRIPLE);
+		break;
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsastart(ctx, plaintext, ciphertext, req,
+		    DCA_RSA_ENC);
+		break;
+	default:
+		/* Should never reach here */
+		cmn_err(CE_WARN, "dca_encrypt: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	if ((error != CRYPTO_QUEUED) && (error != CRYPTO_SUCCESS) &&
+	    (error != CRYPTO_BUFFER_TOO_SMALL)) {
+		ciphertext->cd_length = 0;
+	}
+
+	DBG(softc, DENTRY, "dca_encrypt: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_encrypt_update(crypto_ctx_t *ctx, crypto_data_t *plaintext,
+    crypto_data_t *ciphertext, crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_encrypt_update: started");
+
+	/* check mechanism */
+	switch (DCA_MECH_FROM_CTX(ctx)) {
+	case DES_CBC_MECH_INFO_TYPE:
+		error = dca_3desupdate(ctx, plaintext, ciphertext, req,
+		    DR_ENCRYPT);
+		break;
+	case DES3_CBC_MECH_INFO_TYPE:
+		error = dca_3desupdate(ctx, plaintext, ciphertext, req,
+		    DR_ENCRYPT | DR_TRIPLE);
+		break;
+	default:
+		/* Should never reach here */
+		cmn_err(CE_WARN, "dca_encrypt_update: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_encrypt_update: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_encrypt_final(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_encrypt_final: started");
+
+	/* check mechanism */
+	switch (DCA_MECH_FROM_CTX(ctx)) {
+	case DES_CBC_MECH_INFO_TYPE:
+		error = dca_3desfinal(ctx, ciphertext, DR_ENCRYPT);
+		break;
+	case DES3_CBC_MECH_INFO_TYPE:
+		error = dca_3desfinal(ctx, ciphertext, DR_ENCRYPT | DR_TRIPLE);
+		break;
+	default:
+		/* Should never reach here */
+		cmn_err(CE_WARN, "dca_encrypt_final: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_encrypt_final: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_encrypt_atomic(crypto_provider_handle_t provider,
+    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_data_t *plaintext, crypto_data_t *ciphertext,
+    crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc = (dca_t *)provider;
+
+	DBG(softc, DENTRY, "dca_encrypt_atomic: started");
+
+	if (ctx_template != NULL)
+		return (CRYPTO_ARGUMENTS_BAD);
+
+	/* check mechanism */
+	switch (mechanism->cm_type) {
+	case DES_CBC_MECH_INFO_TYPE:
+		error = dca_3desatomic(provider, session_id, mechanism, key,
+		    plaintext, ciphertext, KM_SLEEP, req,
+		    DR_ENCRYPT | DR_ATOMIC);
+		break;
+	case DES3_CBC_MECH_INFO_TYPE:
+		error = dca_3desatomic(provider, session_id, mechanism, key,
+		    plaintext, ciphertext, KM_SLEEP, req,
+		    DR_ENCRYPT | DR_TRIPLE | DR_ATOMIC);
+		break;
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsaatomic(provider, session_id, mechanism, key,
+		    plaintext, ciphertext, KM_SLEEP, req, DCA_RSA_ENC);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_encrypt_atomic: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	if ((error != CRYPTO_QUEUED) && (error != CRYPTO_SUCCESS)) {
+		ciphertext->cd_length = 0;
+	}
+
+	DBG(softc, DENTRY, "dca_encrypt_atomic: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_decrypt_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_decrypt_init: started");
+
+	/* check mechanism */
+	switch (mechanism->cm_type) {
+	case DES_CBC_MECH_INFO_TYPE:
+		error = dca_3desctxinit(ctx, mechanism, key, KM_SLEEP,
+		    DR_DECRYPT);
+		break;
+	case DES3_CBC_MECH_INFO_TYPE:
+		error = dca_3desctxinit(ctx, mechanism, key, KM_SLEEP,
+		    DR_DECRYPT | DR_TRIPLE);
+		break;
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_decrypt_init: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_decrypt_init: done, err = 0x%x", error);
+
+	if (error == CRYPTO_SUCCESS)
+		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
+		    &softc->dca_ctx_list_lock);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_decrypt(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
+    crypto_data_t *plaintext, crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_decrypt: started");
+
+	/* check mechanism */
+	switch (DCA_MECH_FROM_CTX(ctx)) {
+	case DES_CBC_MECH_INFO_TYPE:
+		error = dca_3des(ctx, ciphertext, plaintext, req, DR_DECRYPT);
+		break;
+	case DES3_CBC_MECH_INFO_TYPE:
+		error = dca_3des(ctx, ciphertext, plaintext, req,
+		    DR_DECRYPT | DR_TRIPLE);
+		break;
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsastart(ctx, ciphertext, plaintext, req,
+		    DCA_RSA_DEC);
+		break;
+	default:
+		/* Should never reach here */
+		cmn_err(CE_WARN, "dca_decrypt: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	if ((error != CRYPTO_QUEUED) && (error != CRYPTO_SUCCESS) &&
+	    (error != CRYPTO_BUFFER_TOO_SMALL)) {
+		if (plaintext)
+			plaintext->cd_length = 0;
+	}
+
+	DBG(softc, DENTRY, "dca_decrypt: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_decrypt_update(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
+    crypto_data_t *plaintext, crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_decrypt_update: started");
+
+	/* check mechanism */
+	switch (DCA_MECH_FROM_CTX(ctx)) {
+	case DES_CBC_MECH_INFO_TYPE:
+		error = dca_3desupdate(ctx, ciphertext, plaintext, req,
+		    DR_DECRYPT);
+		break;
+	case DES3_CBC_MECH_INFO_TYPE:
+		error = dca_3desupdate(ctx, ciphertext, plaintext, req,
+		    DR_DECRYPT | DR_TRIPLE);
+		break;
+	default:
+		/* Should never reach here */
+		cmn_err(CE_WARN, "dca_decrypt_update: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_decrypt_update: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_decrypt_final(crypto_ctx_t *ctx, crypto_data_t *plaintext,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_decrypt_final: started");
+
+	/* check mechanism */
+	switch (DCA_MECH_FROM_CTX(ctx)) {
+	case DES_CBC_MECH_INFO_TYPE:
+		error = dca_3desfinal(ctx, plaintext, DR_DECRYPT);
+		break;
+	case DES3_CBC_MECH_INFO_TYPE:
+		error = dca_3desfinal(ctx, plaintext, DR_DECRYPT | DR_TRIPLE);
+		break;
+	default:
+		/* Should never reach here */
+		cmn_err(CE_WARN, "dca_decrypt_final: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_decrypt_final: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_decrypt_atomic(crypto_provider_handle_t provider,
+    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_data_t *ciphertext, crypto_data_t *plaintext,
+    crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc = (dca_t *)provider;
+
+	DBG(softc, DENTRY, "dca_decrypt_atomic: started");
+
+	if (ctx_template != NULL)
+		return (CRYPTO_ARGUMENTS_BAD);
+
+	/* check mechanism */
+	switch (mechanism->cm_type) {
+	case DES_CBC_MECH_INFO_TYPE:
+		error = dca_3desatomic(provider, session_id, mechanism, key,
+		    ciphertext, plaintext, KM_SLEEP, req,
+		    DR_DECRYPT | DR_ATOMIC);
+		break;
+	case DES3_CBC_MECH_INFO_TYPE:
+		error = dca_3desatomic(provider, session_id, mechanism, key,
+		    ciphertext, plaintext, KM_SLEEP, req,
+		    DR_DECRYPT | DR_TRIPLE | DR_ATOMIC);
+		break;
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsaatomic(provider, session_id, mechanism, key,
+		    ciphertext, plaintext, KM_SLEEP, req, DCA_RSA_DEC);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_decrypt_atomic: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	if ((error != CRYPTO_QUEUED) && (error != CRYPTO_SUCCESS)) {
+		plaintext->cd_length = 0;
+	}
+
+	DBG(softc, DENTRY, "dca_decrypt_atomic: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/*
+ * Sign entry points.
+ */
+
+/* ARGSUSED */
+static int
+dca_sign_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_sign_init: started\n");
+
+	if (ctx_template != NULL)
+		return (CRYPTO_ARGUMENTS_BAD);
+
+	/* check mechanism */
+	switch (mechanism->cm_type) {
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
+		break;
+	case DSA_MECH_INFO_TYPE:
+		error = dca_dsainit(ctx, mechanism, key, KM_SLEEP,
+		    DCA_DSA_SIGN);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_sign_init: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_sign_init: done, err = 0x%x", error);
+
+	if (error == CRYPTO_SUCCESS)
+		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
+		    &softc->dca_ctx_list_lock);
+
+	return (error);
+}
+
+static int
+dca_sign(crypto_ctx_t *ctx, crypto_data_t *data,
+    crypto_data_t *signature, crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_sign: started\n");
+
+	/* check mechanism */
+	switch (DCA_MECH_FROM_CTX(ctx)) {
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsastart(ctx, data, signature, req, DCA_RSA_SIGN);
+		break;
+	case DSA_MECH_INFO_TYPE:
+		error = dca_dsa_sign(ctx, data, signature, req);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_sign: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_sign: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_sign_update(crypto_ctx_t *ctx, crypto_data_t *data,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_MECHANISM_INVALID;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_sign_update: started\n");
+
+	cmn_err(CE_WARN, "dca_sign_update: unexpected mech type "
+	    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+
+	DBG(softc, DENTRY, "dca_sign_update: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_sign_final(crypto_ctx_t *ctx, crypto_data_t *signature,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_MECHANISM_INVALID;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_sign_final: started\n");
+
+	cmn_err(CE_WARN, "dca_sign_final: unexpected mech type "
+	    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+
+	DBG(softc, DENTRY, "dca_sign_final: done, err = 0x%x", error);
+
+	return (error);
+}
+
+static int
+dca_sign_atomic(crypto_provider_handle_t provider,
+    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
+    crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc = (dca_t *)provider;
+
+	DBG(softc, DENTRY, "dca_sign_atomic: started\n");
+
+	if (ctx_template != NULL)
+		return (CRYPTO_ARGUMENTS_BAD);
+
+	/* check mechanism */
+	switch (mechanism->cm_type) {
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsaatomic(provider, session_id, mechanism, key,
+		    data, signature, KM_SLEEP, req, DCA_RSA_SIGN);
+		break;
+	case DSA_MECH_INFO_TYPE:
+		error = dca_dsaatomic(provider, session_id, mechanism, key,
+		    data, signature, KM_SLEEP, req, DCA_DSA_SIGN);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_sign_atomic: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_sign_atomic: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_sign_recover_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_sign_recover_init: started\n");
+
+	if (ctx_template != NULL)
+		return (CRYPTO_ARGUMENTS_BAD);
+
+	/* check mechanism */
+	switch (mechanism->cm_type) {
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_sign_recover_init: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_sign_recover_init: done, err = 0x%x", error);
+
+	if (error == CRYPTO_SUCCESS)
+		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
+		    &softc->dca_ctx_list_lock);
+
+	return (error);
+}
+
+static int
+dca_sign_recover(crypto_ctx_t *ctx, crypto_data_t *data,
+    crypto_data_t *signature, crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_sign_recover: started\n");
+
+	/* check mechanism */
+	switch (DCA_MECH_FROM_CTX(ctx)) {
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsastart(ctx, data, signature, req, DCA_RSA_SIGNR);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_sign_recover: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_sign_recover: done, err = 0x%x", error);
+
+	return (error);
+}
+
+static int
+dca_sign_recover_atomic(crypto_provider_handle_t provider,
+    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
+    crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc = (dca_t *)provider;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	instance = ddi_get_instance(softc->dca_dip);
+	DBG(softc, DENTRY, "dca_sign_recover_atomic: started\n");
+
+	if (ctx_template != NULL)
+		return (CRYPTO_ARGUMENTS_BAD);
+
+	/* check mechanism */
+	switch (mechanism->cm_type) {
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsaatomic(provider, session_id, mechanism, key,
+		    data, signature, KM_SLEEP, req, DCA_RSA_SIGNR);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_sign_recover_atomic: unexpected mech type"
+		    " 0x%llx\n", (unsigned long long)mechanism->cm_type);
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_sign_recover_atomic: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/*
+ * Verify entry points.
+ */
+
+/* ARGSUSED */
+static int
+dca_verify_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_verify_init: started\n");
+
+	if (ctx_template != NULL)
+		return (CRYPTO_ARGUMENTS_BAD);
+
+	/* check mechanism */
+	switch (mechanism->cm_type) {
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
+		break;
+	case DSA_MECH_INFO_TYPE:
+		error = dca_dsainit(ctx, mechanism, key, KM_SLEEP,
+		    DCA_DSA_VRFY);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_verify_init: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_verify_init: done, err = 0x%x", error);
+
+	if (error == CRYPTO_SUCCESS)
+		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
+		    &softc->dca_ctx_list_lock);
+
+	return (error);
+}
+
+static int
+dca_verify(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *signature,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_verify: started\n");
+
+	/* check mechanism */
+	switch (DCA_MECH_FROM_CTX(ctx)) {
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsastart(ctx, signature, data, req, DCA_RSA_VRFY);
+		break;
+	case DSA_MECH_INFO_TYPE:
+		error = dca_dsa_verify(ctx, data, signature, req);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_verify: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_verify: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_verify_update(crypto_ctx_t *ctx, crypto_data_t *data,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_MECHANISM_INVALID;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_verify_update: started\n");
+
+	cmn_err(CE_WARN, "dca_verify_update: unexpected mech type "
+	    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+
+	DBG(softc, DENTRY, "dca_verify_update: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_verify_final(crypto_ctx_t *ctx, crypto_data_t *signature,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_MECHANISM_INVALID;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_verify_final: started\n");
+
+	cmn_err(CE_WARN, "dca_verify_final: unexpected mech type "
+	    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+
+	DBG(softc, DENTRY, "dca_verify_final: done, err = 0x%x", error);
+
+	return (error);
+}
+
+static int
+dca_verify_atomic(crypto_provider_handle_t provider,
+    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
+    crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc = (dca_t *)provider;
+
+	DBG(softc, DENTRY, "dca_verify_atomic: started\n");
+
+	if (ctx_template != NULL)
+		return (CRYPTO_ARGUMENTS_BAD);
+
+	/* check mechanism */
+	switch (mechanism->cm_type) {
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsaatomic(provider, session_id, mechanism, key,
+		    signature, data, KM_SLEEP, req, DCA_RSA_VRFY);
+		break;
+	case DSA_MECH_INFO_TYPE:
+		error = dca_dsaatomic(provider, session_id, mechanism, key,
+		    data, signature, KM_SLEEP, req, DCA_DSA_VRFY);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_verify_atomic: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY, "dca_verify_atomic: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+dca_verify_recover_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
+    crypto_req_handle_t req)
+{
+	int error = CRYPTO_MECHANISM_INVALID;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_verify_recover_init: started\n");
+
+	if (ctx_template != NULL)
+		return (CRYPTO_ARGUMENTS_BAD);
+
+	/* check mechanism */
+	switch (mechanism->cm_type) {
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_verify_recover_init: unexpected mech type"
+		    " 0x%llx\n", (unsigned long long)mechanism->cm_type);
+	}
+
+	DBG(softc, DENTRY, "dca_verify_recover_init: done, err = 0x%x", error);
+
+	if (error == CRYPTO_SUCCESS)
+		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
+		    &softc->dca_ctx_list_lock);
+
+	return (error);
+}
+
+static int
+dca_verify_recover(crypto_ctx_t *ctx, crypto_data_t *signature,
+    crypto_data_t *data, crypto_req_handle_t req)
+{
+	int error = CRYPTO_MECHANISM_INVALID;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
+		return (CRYPTO_OPERATION_NOT_INITIALIZED);
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_verify_recover: started\n");
+
+	/* check mechanism */
+	switch (DCA_MECH_FROM_CTX(ctx)) {
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsastart(ctx, signature, data, req, DCA_RSA_VRFYR);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_verify_recover: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+	}
+
+	DBG(softc, DENTRY, "dca_verify_recover: done, err = 0x%x", error);
+
+	return (error);
+}
+
+static int
+dca_verify_recover_atomic(crypto_provider_handle_t provider,
+    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
+    crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
+{
+	int error = CRYPTO_MECHANISM_INVALID;
+	dca_t *softc = (dca_t *)provider;
+
+	DBG(softc, DENTRY, "dca_verify_recover_atomic: started\n");
+
+	if (ctx_template != NULL)
+		return (CRYPTO_ARGUMENTS_BAD);
+
+	/* check mechanism */
+	switch (mechanism->cm_type) {
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		error = dca_rsaatomic(provider, session_id, mechanism, key,
+		    signature, data, KM_SLEEP, req, DCA_RSA_VRFYR);
+		break;
+	default:
+		cmn_err(CE_WARN, "dca_verify_recover_atomic: unexpected mech "
+		    "type 0x%llx\n", (unsigned long long)mechanism->cm_type);
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	DBG(softc, DENTRY,
+	    "dca_verify_recover_atomic: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/*
+ * Random number entry points.
+ */
+
+/* ARGSUSED */
+static int
+dca_generate_random(crypto_provider_handle_t provider,
+    crypto_session_id_t session_id,
+    uchar_t *buf, size_t len, crypto_req_handle_t req)
+{
+	int error = CRYPTO_FAILED;
+	dca_t *softc = (dca_t *)provider;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	instance = ddi_get_instance(softc->dca_dip);
+	DBG(softc, DENTRY, "dca_generate_random: started");
+
+	error = dca_rng(softc, buf, len, req);
+
+	DBG(softc, DENTRY, "dca_generate_random: done, err = 0x%x", error);
+
+	return (error);
+}
+
+/*
+ * Context management entry points.
+ */
+
+int
+dca_free_context(crypto_ctx_t *ctx)
+{
+	int error = CRYPTO_SUCCESS;
+	dca_t *softc;
+	/* LINTED E_FUNC_SET_NOT_USED */
+	int instance;
+
+	/* extract softc and instance number from context */
+	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
+	DBG(softc, DENTRY, "dca_free_context: entered");
+
+	if (ctx->cc_provider_private == NULL)
+		return (error);
+
+	dca_rmlist2(ctx->cc_provider_private, &softc->dca_ctx_list_lock);
+
+	error = dca_free_context_low(ctx);
+
+	DBG(softc, DENTRY, "dca_free_context: done, err = 0x%x", error);
+
+	return (error);
+}
+
+static int
+dca_free_context_low(crypto_ctx_t *ctx)
+{
+	int error = CRYPTO_SUCCESS;
+
+	/* check mechanism */
+	switch (DCA_MECH_FROM_CTX(ctx)) {
+	case DES_CBC_MECH_INFO_TYPE:
+	case DES3_CBC_MECH_INFO_TYPE:
+		dca_3desctxfree(ctx);
+		break;
+	case RSA_PKCS_MECH_INFO_TYPE:
+	case RSA_X_509_MECH_INFO_TYPE:
+		dca_rsactxfree(ctx);
+		break;
+	case DSA_MECH_INFO_TYPE:
+		dca_dsactxfree(ctx);
+		break;
+	default:
+		/* Should never reach here */
+		cmn_err(CE_WARN, "dca_free_context_low: unexpected mech type "
+		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
+		error = CRYPTO_MECHANISM_INVALID;
+	}
+
+	return (error);
+}
+
+
+/* Free any unfreed private context. It is called in detach. */
+static void
+dca_free_context_list(dca_t *dca)
+{
+	dca_listnode_t	*node;
+	crypto_ctx_t	ctx;
+
+	(void) memset(&ctx, 0, sizeof (ctx));
+	ctx.cc_provider = dca;
+
+	while ((node = dca_delist2(&dca->dca_ctx_list,
+	    &dca->dca_ctx_list_lock)) != NULL) {
+		ctx.cc_provider_private = node;
+		(void) dca_free_context_low(&ctx);
+	}
+}
+
+static int
+ext_info_sym(crypto_provider_handle_t prov,
+    crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq)
+{
+	return (ext_info_base(prov, ext_info, cfreq, IDENT_SYM));
+}
+
+static int
+ext_info_asym(crypto_provider_handle_t prov,
+    crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq)
+{
+	int rv;
+
+	rv = ext_info_base(prov, ext_info, cfreq, IDENT_ASYM);
+	/* The asymmetric cipher slot supports random */
+	ext_info->ei_flags |= CRYPTO_EXTF_RNG;
+
+	return (rv);
+}
+
+/* ARGSUSED */
+static int
+ext_info_base(crypto_provider_handle_t prov,
+    crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq, char *id)
+{
+	dca_t   *dca = (dca_t *)prov;
+	int len;
+
+	/* Label */
+	(void) sprintf((char *)ext_info->ei_label, "%s/%d %s",
+	    ddi_driver_name(dca->dca_dip), ddi_get_instance(dca->dca_dip), id);
+	len = strlen((char *)ext_info->ei_label);
+	(void) memset(ext_info->ei_label + len, ' ',
+	    CRYPTO_EXT_SIZE_LABEL - len);
+
+	/* Manufacturer ID */
+	(void) sprintf((char *)ext_info->ei_manufacturerID, "%s",
+		DCA_MANUFACTURER_ID);
+	len = strlen((char *)ext_info->ei_manufacturerID);
+	(void) memset(ext_info->ei_manufacturerID + len, ' ',
+	    CRYPTO_EXT_SIZE_MANUF - len);
+
+	/* Model */
+	(void) sprintf((char *)ext_info->ei_model, dca->dca_model);
+
+	DBG(dca, DWARN, "kCF MODEL: %s", (char *)ext_info->ei_model);
+
+	len = strlen((char *)ext_info->ei_model);
+	(void) memset(ext_info->ei_model + len, ' ',
+		CRYPTO_EXT_SIZE_MODEL - len);
+
+	/* Serial Number. Blank for Deimos */
+	(void) memset(ext_info->ei_serial_number, ' ', CRYPTO_EXT_SIZE_SERIAL);
+
+	ext_info->ei_flags = CRYPTO_EXTF_WRITE_PROTECTED;
+
+	ext_info->ei_max_session_count = CRYPTO_UNAVAILABLE_INFO;
+	ext_info->ei_max_pin_len = CRYPTO_UNAVAILABLE_INFO;
+	ext_info->ei_min_pin_len = CRYPTO_UNAVAILABLE_INFO;
+	ext_info->ei_total_public_memory = CRYPTO_UNAVAILABLE_INFO;
+	ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO;
+	ext_info->ei_total_private_memory = CRYPTO_UNAVAILABLE_INFO;
+	ext_info->ei_free_private_memory = CRYPTO_UNAVAILABLE_INFO;
+	ext_info->ei_hardware_version.cv_major = 0;
+	ext_info->ei_hardware_version.cv_minor = 0;
+	ext_info->ei_firmware_version.cv_major = 0;
+	ext_info->ei_firmware_version.cv_minor = 0;
+
+	/* Time. No need to be supplied for token without a clock */
+	ext_info->ei_time[0] = '\000';
+
+	return (CRYPTO_SUCCESS);
+}
+
+static void
+dca_fma_init(dca_t *dca)
+{
+	ddi_iblock_cookie_t fm_ibc;
+	int fm_capabilities = DDI_FM_EREPORT_CAPABLE |
+		DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE |
+		DDI_FM_ERRCB_CAPABLE;
+
+	/* Read FMA capabilities from dca.conf file (if present) */
+	dca->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, dca->dca_dip,
+	    DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "fm-capable",
+	    fm_capabilities);
+
+	DBG(dca, DWARN, "dca->fm_capabilities = 0x%x", dca->fm_capabilities);
+
+	/* Only register with IO Fault Services if we have some capability */
+	if (dca->fm_capabilities) {
+		dca_regsattr.devacc_attr_access = DDI_FLAGERR_ACC;
+		dca_devattr.devacc_attr_access = DDI_FLAGERR_ACC;
+		dca_dmaattr.dma_attr_flags = DDI_DMA_FLAGERR;
+
+		/* Register capabilities with IO Fault Services */
+		ddi_fm_init(dca->dca_dip, &dca->fm_capabilities, &fm_ibc);
+		DBG(dca, DWARN, "fm_capable() =  0x%x",
+		    ddi_fm_capable(dca->dca_dip));
+
+		/*
+		 * Initialize pci ereport capabilities if ereport capable
+		 */
+		if (DDI_FM_EREPORT_CAP(dca->fm_capabilities))
+			pci_ereport_setup(dca->dca_dip);
+
+		/*
+		 * Initialize callback mutex and register error callback if
+		 * error callback capable.
+		 */
+		if (DDI_FM_ERRCB_CAP(dca->fm_capabilities)) {
+			ddi_fm_handler_register(dca->dca_dip, dca_fm_error_cb,
+			    (void *)dca);
+		}
+	} else {
+		/*
+		 * These fields have to be cleared of FMA if there are no
+		 * FMA capabilities at runtime.
+		 */
+		dca_regsattr.devacc_attr_access = DDI_DEFAULT_ACC;
+		dca_devattr.devacc_attr_access = DDI_DEFAULT_ACC;
+		dca_dmaattr.dma_attr_flags = 0;
+	}
+}
+
+
+static void
+dca_fma_fini(dca_t *dca)
+{
+	/* Only unregister FMA capabilities if we registered some */
+	if (dca->fm_capabilities) {
+
+		/*
+		 * Release any resources allocated by pci_ereport_setup()
+		 */
+		if (DDI_FM_EREPORT_CAP(dca->fm_capabilities)) {
+			pci_ereport_teardown(dca->dca_dip);
+		}
+
+		/*
+		 * Free callback mutex and un-register error callback if
+		 * error callback capable.
+		 */
+		if (DDI_FM_ERRCB_CAP(dca->fm_capabilities)) {
+			ddi_fm_handler_unregister(dca->dca_dip);
+		}
+
+		/* Unregister from IO Fault Services */
+		ddi_fm_fini(dca->dca_dip);
+		DBG(dca, DWARN, "fm_capable() = 0x%x",
+		    ddi_fm_capable(dca->dca_dip));
+	}
+}
+
+
+/*
+ * The IO fault service error handling callback function
+ */
+static int
+dca_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
+{
+	int		rv;
+	dca_t		*dca = (dca_t *)impl_data;
+	uint16_t	pci_status;
+	ddi_fm_error_t	dca_err;
+
+	rv = err->fme_status = DDI_FM_OK;
+	if (err->fme_flag == DDI_FM_ERR_EXPECTED) {
+		/*
+		 * dca never perfrom DDI_ACC_CAUTIOUS protected operations
+		 * but if it did. we would handle it here
+		 */
+		return (rv);
+	}
+
+	/*
+	 * See if there is a pci error as well
+	 * The updated pci_ereport_post function requires a reinitialized
+	 * ddi_fm_error_t structure with a zero ena field.
+	 */
+	bzero(&dca_err, sizeof (ddi_fm_error_t));
+	dca_err.fme_version = DDI_FME_VERSION;
+	dca_err.fme_flag = DDI_FM_ERR_UNEXPECTED;
+	pci_ereport_post(dip, &dca_err, &pci_status);
+	if (pci_status != 0) {
+		dca_failure(dca, DDI_DATAPATH_FAULT,
+		    DCA_FM_ECLASS_NONE, dca_ena(0), CRYPTO_DEVICE_ERROR,
+		    "fault PCI in FMA callback.");
+
+		rv = err->fme_status = DDI_FM_FATAL;
+		return (rv);
+	}
+
+	return (rv);
+}
+
+
+static int
+dca_check_acc_handle(dca_t *dca, ddi_acc_handle_t handle,
+    dca_fma_eclass_t eclass_index)
+{
+	ddi_fm_error_t	de;
+	int		version = 0;
+	uint16_t	pci_status;
+
+	if (DDI_FM_EREPORT_CAP(dca->fm_capabilities)) {
+		ddi_fm_acc_err_get(handle, &de, version);
+		if (de.fme_status != DDI_FM_OK) {
+			pci_ereport_post(dca->dca_dip, &de, &pci_status);
+			dca_failure(dca, DDI_DATAPATH_FAULT,
+			    eclass_index, fm_ena_increment(de.fme_ena),
+			    CRYPTO_DEVICE_ERROR, "");
+			return (DDI_FAILURE);
+		}
+	}
+
+	return (DDI_SUCCESS);
+}
+
+int
+dca_check_dma_handle(dca_t *dca, ddi_dma_handle_t handle,
+    dca_fma_eclass_t eclass_index)
+{
+	ddi_fm_error_t	de;
+	int		version = 0;
+	uint16_t	pci_status;
+
+	if (DDI_FM_EREPORT_CAP(dca->fm_capabilities)) {
+		ddi_fm_dma_err_get(handle, &de, version);
+		if (de.fme_status != DDI_FM_OK) {
+			pci_ereport_post(dca->dca_dip, &de, &pci_status);
+			dca_failure(dca, DDI_DATAPATH_FAULT,
+			    eclass_index, fm_ena_increment(de.fme_ena),
+			    CRYPTO_DEVICE_ERROR, "");
+			return (DDI_FAILURE);
+		}
+	}
+
+	return (DDI_SUCCESS);
+}
+
+static uint64_t
+dca_ena(uint64_t ena)
+{
+	if (ena == 0)
+		ena = fm_ena_generate(0, FM_ENA_FMT1);
+	else
+		ena = fm_ena_increment(ena);
+	return (ena);
+}
+
+static char *
+dca_fma_eclass_string(char *model, dca_fma_eclass_t index)
+{
+	if (strstr(model, "500"))
+		return (dca_fma_eclass_sca500[index]);
+	else
+		return (dca_fma_eclass_sca1000[index]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/crypto/io/dca.conf	Mon Nov 14 11:14:44 2005 -0800
@@ -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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+#
+ddi-forceattach=1; 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/crypto/io/dca_3des.c	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,747 @@
+
+/*
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Deimos - cryptographic acceleration based upon Broadcom 582x.
+ */
+
+#include <sys/types.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/kmem.h>
+#include <sys/note.h>
+#include <sys/crypto/common.h>
+#include <sys/crypto/spi.h>
+#include <sys/crypto/dca.h>
+
+/*
+ * 3DES implementation.
+ */
+
+static int dca_3desstart(dca_t *, uint32_t, dca_request_t *);
+static void dca_3desdone(dca_request_t *, int);
+
+
+int
+dca_3des(crypto_ctx_t *ctx, crypto_data_t *in,
+    crypto_data_t *out, crypto_req_handle_t req, int flags)
+{
+	int			len;
+	int			rv;
+	dca_request_t		*reqp = ctx->cc_provider_private;
+	dca_request_t		*des_ctx = ctx->cc_provider_private;
+	dca_t			*dca = ctx->cc_provider;
+	crypto_data_t		*nin = &reqp->dr_ctx.in_dup;
+
+	len = dca_length(in);
+	if (len % DESBLOCK) {
+		DBG(dca, DWARN, "input not an integral number of DES blocks");
+		(void) dca_free_context(ctx);
+		if (flags & DR_DECRYPT) {
+			return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
+		} else {
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+	}
+
+	/*
+	 * If cd_miscdata non-null then this contains the IV.
+	 */
+	if (in->cd_miscdata != NULL) {
+		uchar_t	*p = (uchar_t *)in->cd_miscdata;
+		des_ctx->dr_ctx.iv[0] = p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3];
+		des_ctx->dr_ctx.iv[1] = p[4]<<24 | p[5]<<16 | p[6]<<8 | p[7];
+	}
+
+	/*
+	 * In-place operations (input == out) are indicated by having a
+	 * NULL output. In this case set the output to point to the input.
+	 */
+	if (out == NULL) {
+		out = in;
+	}
+	if (len > dca_length(out)) {
+		DBG(dca, DWARN, "inadequate output space (need %d, got %d)",
+		    len, dca_length(out));
+		out->cd_length = len;
+		/* Do not free the context since the app will call again */
+		return (CRYPTO_BUFFER_TOO_SMALL);
+	}
+
+	if ((rv = dca_verifyio(in, out)) != CRYPTO_SUCCESS) {
+		(void) dca_free_context(ctx);
+		return (rv);
+	}
+
+	/* special handling for null-sized input buffers */
+	if (len == 0) {
+		out->cd_length = 0;
+		(void) dca_free_context(ctx);
+		return (CRYPTO_SUCCESS);
+	}
+
+	/*
+	 * Make a local copy of the input crypto_data_t structure. This
+	 * allows it to be manipulated locally and for dealing with in-place
+	 * data (ie in == out). Note that "nin" has been pre-allocated,
+	 * and only fields are copied, not actual data.
+	 */
+	if ((rv = dca_dupcrypto(in, nin)) != CRYPTO_SUCCESS) {
+		(void) dca_free_context(ctx);
+		return (rv);
+	}
+
+	/* Set output to zero ready to take the processed data */
+	out->cd_length = 0;
+
+	reqp->dr_kcf_req = req;
+	reqp->dr_in = nin;
+	reqp->dr_out = out;
+	reqp->dr_job_stat = DS_3DESJOBS;
+	reqp->dr_byte_stat = DS_3DESBYTES;
+
+	rv = dca_3desstart(dca, flags, reqp);
+
+	/* Context will be freed in the kCF callback function otherwise */
+	if (rv != CRYPTO_QUEUED && rv != CRYPTO_BUFFER_TOO_SMALL) {
+		(void) dca_free_context(ctx);
+	}
+	return (rv);
+}
+
+
+void
+dca_3desctxfree(void *arg)
+{
+	crypto_ctx_t	*ctx = (crypto_ctx_t *)arg;
+	dca_request_t	*des_ctx = ctx->cc_provider_private;
+
+	if (des_ctx == NULL)
+		return;
+
+	des_ctx->dr_ctx.atomic = 0;
+	des_ctx->dr_ctx.ctx_cm_type = 0;
+	ctx->cc_provider_private = NULL;
+
+	if (des_ctx->destroy)
+		dca_destroyreq(des_ctx);
+	else
+		/* Return it to the pool */
+		dca_freereq(des_ctx);
+}
+
+int
+dca_3desupdate(crypto_ctx_t *ctx, crypto_data_t *in,
+    crypto_data_t *out, crypto_req_handle_t req, int flags)
+{
+	int			len;
+	int			rawlen;
+	int			rv;
+	dca_request_t		*reqp = ctx->cc_provider_private;
+	dca_request_t		*des_ctx = ctx->cc_provider_private;
+	dca_t			*dca = ctx->cc_provider;
+	crypto_data_t		*nin = &reqp->dr_ctx.in_dup;
+
+	rawlen = dca_length(in) + des_ctx->dr_ctx.residlen;
+
+	len = ROUNDDOWN(rawlen, DESBLOCK);
+	/*
+	 * If cd_miscdata non-null then this contains the IV.
+	 */
+	if (in->cd_miscdata != NULL) {
+		uchar_t	*p = (uchar_t *)in->cd_miscdata;
+		des_ctx->dr_ctx.iv[0] = p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3];
+		des_ctx->dr_ctx.iv[1] = p[4]<<24 | p[5]<<16 | p[6]<<8 | p[7];
+	}
+
+	/*
+	 * In-place operations (in == out) are indicated by having a
+	 * NULL output. In this case set the output to point to the input.
+	 */
+	if (out == NULL) {
+		out = in;
+	}
+	if (len > dca_length(out)) {
+		DBG(dca, DWARN, "not enough output space (need %d, got %d)",
+		    len, dca_length(out));
+		out->cd_length = len;
+		/* Do not free the context since the app will call again */
+		return (CRYPTO_BUFFER_TOO_SMALL);
+	}
+
+	if ((rv = dca_verifyio(in, out)) != CRYPTO_SUCCESS) {
+		(void) dca_free_context(ctx);
+		return (rv);
+	}
+
+	reqp->dr_kcf_req = req;
+
+	/*
+	 * From here on out, we are committed.
+	 */
+
+	if (len == 0) {
+		/*
+		 * No blocks being encrypted, so we just accumulate the
+		 * input for the next pass and return.
+		 */
+		if ((rv = dca_getbufbytes(in, 0,
+		    (rawlen % DESBLOCK) - des_ctx->dr_ctx.residlen,
+		    des_ctx->dr_ctx.resid + des_ctx->dr_ctx.residlen)) !=
+		    CRYPTO_SUCCESS) {
+			DBG(dca, DWARN,
+	    "dca_3desupdate: dca_getbufbytes() failed for residual only pass");
+			dca_freereq(reqp);
+			return (rv);
+		}
+		des_ctx->dr_ctx.residlen = rawlen % DESBLOCK;
+
+		out->cd_length = 0;
+		/*
+		 * Do not free the context here since it will be done
+		 * in the final function
+		 */
+		return (CRYPTO_SUCCESS);
+	}
+
+	/*
+	 * Set up rbuf for previous residual data.
+	 */
+	if (des_ctx->dr_ctx.residlen) {
+		bcopy(des_ctx->dr_ctx.resid, des_ctx->dr_ctx.activeresid,
+		    des_ctx->dr_ctx.residlen);
+		des_ctx->dr_ctx.activeresidlen = des_ctx->dr_ctx.residlen;
+	}
+
+	/*
+	 * Locate and save residual data for next encrypt_update.
+	 */
+	if ((rv = dca_getbufbytes(in, len - des_ctx->dr_ctx.residlen,
+	    rawlen % DESBLOCK, des_ctx->dr_ctx.resid)) != CRYPTO_SUCCESS) {
+		DBG(dca, DWARN, "dca_3desupdate: dca_getbufbytes() failed");
+		(void) dca_free_context(ctx);
+		return (rv);
+	}
+
+	/* Calculate new residual length. */
+	des_ctx->dr_ctx.residlen = rawlen % DESBLOCK;
+
+	/*
+	 * Make a local copy of the input crypto_data_t structure. This
+	 * allows it to be manipulated locally and for dealing with in-place
+	 * data (ie in == out).
+	 */
+	if ((rv = dca_dupcrypto(in, nin)) != CRYPTO_SUCCESS) {
+		(void) dca_free_context(ctx);
+		return (rv);
+	}
+
+	/* Set output to zero ready to take the processed data */
+	out->cd_length = 0;
+
+	reqp->dr_in = nin;
+	reqp->dr_out = out;
+	reqp->dr_job_stat = DS_3DESJOBS;
+	reqp->dr_byte_stat = DS_3DESBYTES;
+
+	rv = dca_3desstart(dca, flags, reqp);
+
+	/*
+	 * As this is multi-part the context is cleared on success
+	 * (CRYPTO_QUEUED) in dca_3desfinal().
+	 */
+
+	if (rv != CRYPTO_QUEUED && rv != CRYPTO_BUFFER_TOO_SMALL) {
+		(void) dca_free_context(ctx);
+	}
+	return (rv);
+}
+
+int
+dca_3desfinal(crypto_ctx_t *ctx, crypto_data_t *out, int mode)
+{
+	dca_request_t	*des_ctx = ctx->cc_provider_private;
+	dca_t		*dca = ctx->cc_provider;
+	int		rv = CRYPTO_SUCCESS;
+
+	ASSERT(ctx->cc_provider_private != NULL);
+	/*
+	 * There must be no unprocessed ciphertext/plaintext.
+	 * This happens if the length of the last data is
+	 * not a multiple of the DES block length.
+	 */
+	if (des_ctx->dr_ctx.residlen != 0) {
+		DBG(dca, DWARN, "dca_3desfinal: invalid nonzero residual");
+		if (mode & DR_DECRYPT) {
+			rv = CRYPTO_ENCRYPTED_DATA_LEN_RANGE;
+		} else {
+			rv = CRYPTO_DATA_LEN_RANGE;
+		}
+	}
+	(void) dca_free_context(ctx);
+	out->cd_length = 0;
+	return (rv);
+}
+
+int
+dca_3desatomic(crypto_provider_handle_t provider,
+    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_data_t *input, crypto_data_t *output,
+    int kmflag, crypto_req_handle_t req, int mode)
+{
+	crypto_ctx_t	ctx;	/* on the stack */
+	int		rv;
+
+	ctx.cc_provider = provider;
+	ctx.cc_session = session_id;
+
+	/*
+	 * Input must be a multiple of the block size. This test only
+	 * works for non-padded mechanisms when the blocksize is 2^N.
+	 */
+	if ((dca_length(input) & (DESBLOCK - 1)) != 0) {
+		DBG(NULL, DWARN, "dca_3desatomic: input not multiple of BS");
+		if (mode & DR_DECRYPT) {
+			return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
+		} else {
+			return (CRYPTO_DATA_LEN_RANGE);
+		}
+	}
+
+	rv = dca_3desctxinit(&ctx, mechanism, key, kmflag, mode);
+	if (rv != CRYPTO_SUCCESS) {
+		DBG(NULL, DWARN, "dca_3desatomic: dca_3desctxinit() failed");
+		return (rv);
+	}
+
+	/*
+	 * Set the atomic flag so that the hardware callback function
+	 * will free the context.
+	 */
+	((dca_request_t *)ctx.cc_provider_private)->dr_ctx.atomic = 1;
+
+	rv = dca_3des(&ctx, input, output, req, mode);
+	if ((rv != CRYPTO_QUEUED) && (rv != CRYPTO_SUCCESS)) {
+		DBG(NULL, DWARN, "dca_3desatomic: dca_3des() failed");
+		output->cd_length = 0;
+	}
+
+	/*
+	 * The features of dca_3desfinal() are implemented within
+	 * dca_3desdone() due to the asynchronous nature of dca_3des().
+	 */
+
+	/*
+	 * The context will be freed in the hardware callback function if it
+	 * is queued
+	 */
+	if (rv != CRYPTO_QUEUED)
+		dca_3desctxfree(&ctx);
+
+	return (rv);
+}
+
+int
+dca_3desstart(dca_t *dca, uint32_t flags, dca_request_t *reqp)
+{
+	size_t		len;
+	crypto_data_t	*in = reqp->dr_in;
+	int		rv;
+	dca_request_t	*ctx = reqp;
+	uint32_t	iv[2];
+
+	/*
+	 * Preconditions:
+	 * 1) in and out point to the "right" buffers.
+	 * 2) in->b_bcount - in->b_resid == initial offset
+	 * 3) likewise for out
+	 * 4) there is enough space in the output
+	 * 5) we perform a block for block encrypt
+	 */
+	len = ctx->dr_ctx.activeresidlen + dca_length(in);
+	len = ROUNDDOWN(min(len, MAXPACKET), DESBLOCK);
+	reqp->dr_pkt_length = (uint16_t)len;
+
+	/* collect IVs for this pass */
+	iv[0] = ctx->dr_ctx.iv[0];
+	iv[1] = ctx->dr_ctx.iv[1];
+
+	/*
+	 * And also, for decrypt, collect the IV for the next pass.  For
+	 * decrypt, the IV must be collected BEFORE decryption, or else
+	 * we will lose it.  (For encrypt, we grab the IV AFTER encryption,
+	 * in dca_3desdone.
+	 */
+	if (flags & DR_DECRYPT) {
+		uchar_t		ivstore[DESBLOCK];
+		uchar_t		*ivp = ivstore;
+
+		/* get last 8 bytes of ciphertext for IV of next op */
+		/*
+		 * If we're processing only a DESBLOCKS worth of data
+		 * and there is active residual present then it will be
+		 * needed for the IV also.
+		 */
+		if ((len == DESBLOCK) && ctx->dr_ctx.activeresidlen) {
+			/* Bring the active residual into play */
+			bcopy(ctx->dr_ctx.activeresid, ivstore,
+			    ctx->dr_ctx.activeresidlen);
+			rv = dca_getbufbytes(in, 0,
+			    DESBLOCK - ctx->dr_ctx.activeresidlen,
+			    ivstore + ctx->dr_ctx.activeresidlen);
+		} else {
+			rv = dca_getbufbytes(in,
+			    len - DESBLOCK - ctx->dr_ctx.activeresidlen,
+			    DESBLOCK, ivstore);
+		}
+
+		if (rv != CRYPTO_SUCCESS) {
+			DBG(dca, DWARN,
+			    "dca_3desstart: dca_getbufbytes() failed");
+			return (rv);
+		}
+
+		/* store as a pair of native 32-bit values */
+		ctx->dr_ctx.iv[0] =
+		    ivp[0]<<24 | ivp[1]<<16 | ivp[2]<<8 | ivp[3];
+		ctx->dr_ctx.iv[1] =
+		    ivp[4]<<24 | ivp[5]<<16 | ivp[6]<<8 | ivp[7];
+	}
+
+	/* For now we force a pullup.  Add direct DMA later. */
+	reqp->dr_flags &= ~(DR_SCATTER | DR_GATHER);
+	if ((len < dca_mindma) || (ctx->dr_ctx.activeresidlen > 0) ||
+	    dca_sgcheck(dca, reqp->dr_in, DCA_SG_CONTIG) ||
+	    dca_sgcheck(dca, reqp->dr_out, DCA_SG_WALIGN)) {
+		reqp->dr_flags |= DR_SCATTER | DR_GATHER;
+	}
+
+	/* Try to do direct DMA. */
+	if (!(reqp->dr_flags & (DR_SCATTER | DR_GATHER))) {
+		if (dca_bindchains(reqp, len, len) == DDI_SUCCESS) {
+			reqp->dr_in->cd_offset += len;
+			reqp->dr_in->cd_length -= len;
+		} else {
+			DBG(dca, DWARN,
+			    "dca_3desstart: dca_bindchains() failed");
+			return (CRYPTO_DEVICE_ERROR);
+		}
+	}
+
+	/* gather the data into the device */
+	if (reqp->dr_flags & DR_GATHER) {
+		rv = dca_resid_gather(in, (char *)ctx->dr_ctx.activeresid,
+		    &ctx->dr_ctx.activeresidlen, reqp->dr_ibuf_kaddr, len);
+		if (rv != CRYPTO_SUCCESS) {
+			DBG(dca, DWARN,
+			    "dca_3desstart: dca_resid_gather() failed");
+			return (rv);
+		}
+		/*
+		 * Setup for scattering the result back out
+		 * The output buffer is a multi-entry chain for x86 and
+		 * a single entry chain for Sparc.
+		 * Use the actual length if the first entry is sufficient.
+		 */
+		(void) ddi_dma_sync(reqp->dr_ibuf_dmah, 0, len,
+			DDI_DMA_SYNC_FORDEV);
+		if (dca_check_dma_handle(dca, reqp->dr_ibuf_dmah,
+		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+			reqp->destroy = TRUE;
+			return (CRYPTO_DEVICE_ERROR);
+		}
+
+		reqp->dr_in_paddr = reqp->dr_ibuf_head.dc_buffer_paddr;
+		reqp->dr_in_next = reqp->dr_ibuf_head.dc_next_paddr;
+		if (len > reqp->dr_ibuf_head.dc_buffer_length)
+			reqp->dr_in_len = reqp->dr_ibuf_head.dc_buffer_length;
+		else
+			reqp->dr_in_len = len;
+	}
+	/*
+	 * Setup for scattering the result back out
+	 * The output buffer is a multi-entry chain for x86 and
+	 * a single entry chain for Sparc.
+	 * Use the actual length if the first entry is sufficient.
+	 */
+	if (reqp->dr_flags & DR_SCATTER) {
+		reqp->dr_out_paddr = reqp->dr_obuf_head.dc_buffer_paddr;
+		reqp->dr_out_next = reqp->dr_obuf_head.dc_next_paddr;
+		if (len > reqp->dr_obuf_head.dc_buffer_length)
+			reqp->dr_out_len = reqp->dr_obuf_head.dc_buffer_length;
+		else
+			reqp->dr_out_len = len;
+	}
+
+	reqp->dr_flags |= flags;
+	reqp->dr_callback = dca_3desdone;
+
+	/* write out the context structure */
+	PUTCTX32(reqp, CTX_3DESIVHI, iv[0]);
+	PUTCTX32(reqp, CTX_3DESIVLO, iv[1]);
+
+	/* schedule the work by doing a submit */
+	return (dca_start(dca, reqp, MCR1, 1));
+}
+
+void
+dca_3desdone(dca_request_t *reqp, int errno)
+{
+	crypto_data_t	*out = reqp->dr_out;
+	dca_request_t	*ctx = reqp;
+	ASSERT(ctx != NULL);
+
+	if (errno == CRYPTO_SUCCESS) {
+		size_t		off;
+		/*
+		 * Save the offset: this has to be done *before* dca_scatter
+		 * modifies the buffer.  We take the initial offset into the
+		 * first buf, and add that to the total packet size to find
+		 * the end of the packet.
+		 */
+		off = dca_length(out) + reqp->dr_pkt_length - DESBLOCK;
+
+		if (reqp->dr_flags & DR_SCATTER) {
+			(void) ddi_dma_sync(reqp->dr_obuf_dmah, 0,
+				reqp->dr_out_len, DDI_DMA_SYNC_FORKERNEL);
+			if (dca_check_dma_handle(reqp->dr_dca,
+			    reqp->dr_obuf_dmah, DCA_FM_ECLASS_NONE) !=
+			    DDI_SUCCESS) {
+				reqp->destroy = TRUE;
+				errno = CRYPTO_DEVICE_ERROR;
+				goto errout;
+			}
+
+			errno = dca_scatter(reqp->dr_obuf_kaddr,
+			    reqp->dr_out, reqp->dr_out_len, 0);
+			if (errno != CRYPTO_SUCCESS) {
+				DBG(NULL, DWARN,
+				    "dca_3desdone: dca_scatter() failed");
+				goto errout;
+			}
+
+		} else {
+			/* we've processed some more data */
+			out->cd_length += reqp->dr_pkt_length;
+		}
+
+
+		/*
+		 * For encryption only, we have to grab the IV for the
+		 * next pass AFTER encryption.
+		 */
+		if (reqp->dr_flags & DR_ENCRYPT) {
+			uchar_t		ivstore[DESBLOCK];
+			uchar_t		*iv = ivstore;
+
+			/* get last 8 bytes for IV of next op */
+			errno = dca_getbufbytes(out, off, DESBLOCK, iv);
+			if (errno != CRYPTO_SUCCESS) {
+				DBG(NULL, DWARN,
+				    "dca_3desdone: dca_getbufbytes() failed");
+				goto errout;
+			}
+			/* store as a pair of native 32-bit values */
+			ctx->dr_ctx.iv[0] =
+			    iv[0]<<24 | iv[1]<<16 | iv[2]<<8 | iv[3];
+			ctx->dr_ctx.iv[1] =
+			    iv[4]<<24 | iv[5]<<16 | iv[6]<<8 | iv[7];
+		}
+
+		/*
+		 * If there is more to do, then reschedule another
+		 * pass.
+		 */
+		if (dca_length(reqp->dr_in) >= 8) {
+			errno = dca_3desstart(reqp->dr_dca, reqp->dr_flags,
+			    reqp);
+			if (errno == CRYPTO_QUEUED) {
+				return;
+			}
+		}
+	}
+
+errout:
+
+	/*
+	 * If this is an atomic operation perform the final function
+	 * tasks (equivalent to to dca_3desfinal()).
+	 */
+	if (reqp->dr_ctx.atomic) {
+		if ((errno == CRYPTO_SUCCESS) && (ctx->dr_ctx.residlen != 0)) {
+			DBG(NULL, DWARN,
+			    "dca_3desdone: invalid nonzero residual");
+			if (reqp->dr_flags & DR_DECRYPT) {
+				errno = CRYPTO_ENCRYPTED_DATA_LEN_RANGE;
+			} else {
+				errno = CRYPTO_DATA_LEN_RANGE;
+			}
+		}
+	}
+
+	ASSERT(reqp->dr_kcf_req != NULL);
+	/* notify framework that request is completed */
+	crypto_op_notification(reqp->dr_kcf_req, errno);
+	DBG(NULL, DINTR,
+	    "dca_3desdone: returning %d to the kef via crypto_op_notification",
+	    errno);
+
+	/* This has to be done after notifing the framework */
+	if (reqp->dr_ctx.atomic) {
+		reqp->dr_context = NULL;
+		reqp->dr_ctx.atomic = 0;
+		reqp->dr_ctx.ctx_cm_type = 0;
+		if (reqp->destroy)
+			dca_destroyreq(reqp);
+		else
+			dca_freereq(reqp);
+	}
+}
+
+/* ARGSUSED */
+int
+dca_3desctxinit(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, int kmflag, int flags)
+{
+	dca_request_t	*des_ctx;
+	dca_t		*dca = ctx->cc_provider;
+	uchar_t		*param;
+	uchar_t		*value;
+	size_t		paramsz;
+	unsigned	len;
+	int		i, j;
+
+	paramsz = mechanism->cm_param_len;
+	param = (uchar_t *)mechanism->cm_param;
+	if ((paramsz != 0) && (paramsz != DES_IV_LEN)) {
+		DBG(NULL, DWARN,
+		    "dca_3desctxinit: parameter(IV) length not %d (%d)",
+		    DES_IV_LEN, paramsz);
+		return (CRYPTO_MECHANISM_PARAM_INVALID);
+	}
+
+	if ((des_ctx = dca_getreq(dca, MCR1, 1)) == NULL) {
+		dca_error(dca, "unable to allocate request for 3DES");
+		return (CRYPTO_HOST_MEMORY);
+	}
+	/*
+	 * Identify and store the IV as a pair of native 32-bit words.
+	 *
+	 * If cm_param == NULL then the IV comes from the cd_miscdata field
+	 * in the crypto_data structure.
+	 */
+	if (param != NULL) {
+		ASSERT(paramsz == DES_IV_LEN);
+		des_ctx->dr_ctx.iv[0] = param[0]<<24 | param[1]<<16 |
+		    param[2]<<8 | param[3];
+		des_ctx->dr_ctx.iv[1] = param[4]<<24 | param[5]<<16 |
+		    param[6]<<8 | param[7];
+	}
+	des_ctx->dr_ctx.residlen = 0;
+	des_ctx->dr_ctx.activeresidlen = 0;
+	des_ctx->dr_ctx.ctx_cm_type = mechanism->cm_type;
+	ctx->cc_provider_private = des_ctx;
+
+	if (key->ck_format != CRYPTO_KEY_RAW) {
+		DBG(NULL, DWARN,
+	"dca_3desctxinit: only raw crypto key type support with DES/3DES");
+		dca_3desctxfree(ctx);
+		return (CRYPTO_KEY_TYPE_INCONSISTENT);
+	}
+
+	len = key->ck_length;
+	value = (uchar_t *)key->ck_data;
+
+	if (flags & DR_TRIPLE) {
+		/* 3DES */
+		switch (len) {
+		case 192:
+			for (i = 0; i < 6; i++) {
+				des_ctx->dr_ctx.key[i] = 0;
+				for (j = 0; j < 4; j++) {
+					des_ctx->dr_ctx.key[i] <<= 8;
+					des_ctx->dr_ctx.key[i] |= *value;
+					value++;
+				}
+			}
+			break;
+
+		case 128:
+			for (i = 0; i < 4; i++) {
+				des_ctx->dr_ctx.key[i] = 0;
+				for (j = 0; j < 4; j++) {
+					des_ctx->dr_ctx.key[i] <<= 8;
+					des_ctx->dr_ctx.key[i] |= *value;
+					value++;
+				}
+			}
+			des_ctx->dr_ctx.key[4] = des_ctx->dr_ctx.key[0];
+			des_ctx->dr_ctx.key[5] = des_ctx->dr_ctx.key[1];
+			break;
+
+		default:
+			DBG(NULL, DWARN, "Incorrect 3DES keysize (%d)", len);
+			dca_3desctxfree(ctx);
+			return (CRYPTO_KEY_SIZE_RANGE);
+		}
+	} else {
+		/* single DES */
+		if (len != 64) {
+			DBG(NULL, DWARN, "Incorrect DES keysize (%d)", len);
+			dca_3desctxfree(ctx);
+			return (CRYPTO_KEY_SIZE_RANGE);
+		}
+		des_ctx->dr_ctx.key[0] =
+		    value[0]<<24 | value[1]<<16 | value[2]<<8 | value[3];
+		des_ctx->dr_ctx.key[1] =
+		    value[4]<<24 | value[5]<<16 | value[6]<<8 | value[7];
+		/* for single des just repeat des key */
+		des_ctx->dr_ctx.key[4] =
+		    des_ctx->dr_ctx.key[2] = des_ctx->dr_ctx.key[0];
+		des_ctx->dr_ctx.key[5] =
+		    des_ctx->dr_ctx.key[3] = des_ctx->dr_ctx.key[1];
+	}
+
+	/*
+	 * Setup the context here so that we do not need to setup it up
+	 * for every update
+	 */
+	PUTCTX16(des_ctx, CTX_LENGTH, CTX_3DES_LENGTH);
+	PUTCTX16(des_ctx, CTX_CMD, CMD_3DES);
+	PUTCTX32(des_ctx, CTX_3DESDIRECTION,
+	    flags & DR_ENCRYPT ? CTX_3DES_ENCRYPT : CTX_3DES_DECRYPT);
+	PUTCTX32(des_ctx, CTX_3DESKEY1HI, des_ctx->dr_ctx.key[0]);
+	PUTCTX32(des_ctx, CTX_3DESKEY1LO, des_ctx->dr_ctx.key[1]);
+	PUTCTX32(des_ctx, CTX_3DESKEY2HI, des_ctx->dr_ctx.key[2]);
+	PUTCTX32(des_ctx, CTX_3DESKEY2LO, des_ctx->dr_ctx.key[3]);
+	PUTCTX32(des_ctx, CTX_3DESKEY3HI, des_ctx->dr_ctx.key[4]);
+	PUTCTX32(des_ctx, CTX_3DESKEY3LO, des_ctx->dr_ctx.key[5]);
+
+	return (CRYPTO_SUCCESS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/crypto/io/dca_debug.c	Mon Nov 14 11:14:44 2005 -0800
@@ -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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Deimos - cryptographic acceleration based upon Broadcom 582x.
+ */
+
+#include <sys/types.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/cmn_err.h>
+#include <sys/varargs.h>
+#include <sys/crypto/dca.h>
+
+/*
+ * Debugging and messaging.
+ */
+#if DEBUG
+static int dca_debug = 0;
+
+void
+dca_dprintf(dca_t *dca, int level, const char *fmt, ...)
+{
+	va_list ap;
+	char	buf[256];
+
+	if (dca_debug & level) {
+		va_start(ap, fmt);
+		if (dca == NULL) {
+			(void) sprintf(buf, "%s\n", fmt);
+		} else {
+			(void) sprintf(buf, "%s/%d: %s\n",
+			    ddi_driver_name(dca->dca_dip),
+			    ddi_get_instance(dca->dca_dip), fmt);
+		}
+		vprintf(buf, ap);
+		va_end(ap);
+	}
+}
+#endif
+
+void
+dca_error(dca_t *dca, const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	dca_dipverror(dca->dca_dip, fmt, ap);
+	va_end(ap);
+}
+
+void
+dca_diperror(dev_info_t *dip, const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	dca_dipverror(dip, fmt, ap);
+	va_end(ap);
+}
+
+void
+dca_dipverror(dev_info_t *dip, const char *fmt, va_list ap)
+{
+	char	buf[256];
+	(void) sprintf(buf, "%s%d: %s", ddi_driver_name(dip),
+			ddi_get_instance(dip), fmt);
+	vcmn_err(CE_WARN, buf, ap);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/crypto/io/dca_dsa.c	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,582 @@
+/*
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Deimos - cryptographic acceleration based upon Broadcom 582x.
+ */
+
+#include <sys/types.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/kmem.h>
+#include <sys/crypto/common.h>
+#include <sys/crypto/spi.h>
+#include <sys/crypto/ioctl.h>
+#include <sys/crypto/dca.h>
+
+/*
+ * DSA implementation.
+ */
+
+static void dca_dsa_sign_done(dca_request_t *, int);
+static void dca_dsa_verify_done(dca_request_t *, int);
+
+
+int dca_dsa_sign(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *sig,
+    crypto_req_handle_t req);
+int dca_dsa_verify(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *sig,
+    crypto_req_handle_t req);
+int dca_dsainit(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, int kmflag, int mode);
+
+
+int
+dca_dsa_sign(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *sig,
+    crypto_req_handle_t req)
+{
+	dca_request_t	*reqp = ctx->cc_provider_private;
+	dca_t		*dca = ctx->cc_provider;
+	int		err;
+	int		rv = CRYPTO_QUEUED;
+	caddr_t		kaddr;
+	size_t		buflen;
+
+	buflen = dca_length(data);
+	if (buflen != SHA1LEN) {
+		DBG(dca, DWARN, "dca_dsa_sign: data length != %d", SHA1LEN);
+		rv = CRYPTO_DATA_LEN_RANGE;
+		goto errout;
+	}
+
+	/* Return length needed to store the output. */
+	if (dca_length(sig) < DSASIGLEN) {
+		DBG(dca, DWARN,
+		    "dca_dsa_sign: output buffer too short (%d < %d)",
+		    dca_length(sig), DSASIGLEN);
+		sig->cd_length = DSASIGLEN;
+		rv = CRYPTO_BUFFER_TOO_SMALL;
+		goto errout;
+	}
+
+	/*
+	 * Don't change the data values of the data crypto_data_t structure
+	 * yet. Only reset the sig cd_length to zero before writing to it.
+	 */
+
+	reqp->dr_job_stat = DS_DSASIGN;
+	reqp->dr_byte_stat = -1;
+	reqp->dr_in = data;
+	reqp->dr_out = sig;
+	reqp->dr_callback = dca_dsa_sign_done;
+
+	reqp->dr_kcf_req = req;
+	/* dca_gather() increments cd_offset & dec. cd_length by SHA1LEN. */
+	err = dca_gather(data, reqp->dr_ibuf_kaddr, SHA1LEN, 1);
+	if (err != CRYPTO_SUCCESS) {
+		DBG(dca, DWARN, "dca_dsa_sign: dca_gather() failed");
+		rv = err;
+		goto errout;
+	}
+
+
+	/* sync the input buffer */
+	(void) ddi_dma_sync(reqp->dr_ibuf_dmah, 0, SHA1LEN,
+		DDI_DMA_SYNC_FORDEV);
+	if (dca_check_dma_handle(dca, reqp->dr_ibuf_dmah,
+	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+		reqp->destroy = TRUE;
+		rv = CRYPTO_DEVICE_ERROR;
+		goto errout;
+	}
+
+	reqp->dr_in_paddr = reqp->dr_ibuf_paddr;
+	reqp->dr_in_next = 0;
+	reqp->dr_in_len = SHA1LEN;
+	reqp->dr_pkt_length = buflen;
+
+	/*
+	 * The output requires *two* buffers, r followed by s.
+	 */
+	kaddr = reqp->dr_ctx_kaddr + reqp->dr_offset;
+
+	/* r */
+	reqp->dr_out_paddr = reqp->dr_obuf_paddr;
+	reqp->dr_out_len = DSAPARTLEN;
+	reqp->dr_out_next = reqp->dr_ctx_paddr + reqp->dr_offset;
+
+	/* s */
+	PUTDESC32(reqp, kaddr, DESC_BUFADDR,
+	    reqp->dr_obuf_paddr + DSAPARTLEN);
+	PUTDESC32(reqp, kaddr, DESC_NEXT, 0);
+	PUTDESC16(reqp, kaddr, DESC_RSVD, 0);
+	PUTDESC16(reqp, kaddr, DESC_LENGTH, DSAPARTLEN);
+
+	/* schedule the work by doing a submit */
+	rv = dca_start(dca, reqp, MCR2, 1);
+
+errout:
+
+	if (rv != CRYPTO_QUEUED && rv != CRYPTO_BUFFER_TOO_SMALL)
+		(void) dca_free_context(ctx);
+
+	return (rv);
+}
+
+static void
+dca_dsa_sign_done(dca_request_t *reqp, int errno)
+{
+	if (errno == CRYPTO_SUCCESS) {
+		(void) ddi_dma_sync(reqp->dr_obuf_dmah, 0, DSASIGLEN,
+		    DDI_DMA_SYNC_FORKERNEL);
+		if (dca_check_dma_handle(reqp->dr_dca, reqp->dr_obuf_dmah,
+		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+			reqp->destroy = TRUE;
+			errno = CRYPTO_DEVICE_ERROR;
+			goto errout;
+		}
+		/*
+		 * Set the sig cd_length to zero so it's ready to take the
+		 * signature. Have already confirmed its size is adequate.
+		 */
+		reqp->dr_out->cd_length = 0;
+		errno = dca_scatter(reqp->dr_obuf_kaddr,
+		    reqp->dr_out, DSAPARTLEN, 1);
+		if (errno != CRYPTO_SUCCESS) {
+			DBG(reqp->dr_dca, DWARN,
+			    "dca_dsa_sign_done: dca_scatter() failed");
+			goto errout;
+		}
+		errno = dca_scatter(reqp->dr_obuf_kaddr+DSAPARTLEN,
+		    reqp->dr_out, DSAPARTLEN, 1);
+		if (errno != CRYPTO_SUCCESS) {
+			DBG(reqp->dr_dca, DWARN,
+			    "dca_dsa_sign_done: dca_scatter() failed");
+		}
+	}
+errout:
+	ASSERT(reqp->dr_kcf_req != NULL);
+
+	/* notify framework that request is completed */
+	crypto_op_notification(reqp->dr_kcf_req, errno);
+	DBG(reqp->dr_dca, DINTR,
+	    "dca_dsa_sign_done: rtn 0x%x to kef via crypto_op_notification",
+	    errno);
+
+	/*
+	 * For non-atomic operations, reqp will be freed in the kCF
+	 * callback function since it may be needed again if
+	 * CRYPTO_BUFFER_TOO_SMALL is returned to kCF
+	 */
+	if (reqp->dr_ctx.atomic) {
+		crypto_ctx_t ctx;
+		ctx.cc_provider_private = reqp;
+		dca_dsactxfree(&ctx);
+	}
+}
+
+int
+dca_dsa_verify(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *sig,
+    crypto_req_handle_t req)
+{
+	dca_request_t	*reqp = ctx->cc_provider_private;
+	dca_t		*dca = ctx->cc_provider;
+	int		err;
+	int		rv = CRYPTO_QUEUED;
+	caddr_t		kaddr;
+
+	/* Impossible for verify to be an in-place operation. */
+	if (sig == NULL) {
+		rv = CRYPTO_ARGUMENTS_BAD;
+		goto errout;
+	}
+
+	if (dca_length(data) != SHA1LEN) {
+		DBG(dca, DWARN, "dca_dsa_verify: input length != %d", SHA1LEN);
+		rv = CRYPTO_DATA_LEN_RANGE;
+		goto errout;
+	}
+
+	if (dca_length(sig) != DSASIGLEN) {
+		DBG(dca, DWARN, "dca_dsa_verify: signature length != %d",
+		    DSASIGLEN);
+		rv = CRYPTO_SIGNATURE_LEN_RANGE;
+		goto errout;
+	}
+
+	/* Don't change the data & sig values for verify. */
+
+	reqp->dr_job_stat = DS_DSAVERIFY;
+	reqp->dr_byte_stat = -1;
+
+	/*
+	 * Grab h, r and s.
+	 */
+	err = dca_gather(data, reqp->dr_ibuf_kaddr, SHA1LEN, 1);
+	if (err != CRYPTO_SUCCESS) {
+		DBG(dca, DWARN,
+		    "dca_dsa_vrfy: dca_gather() failed for h");
+		rv = err;
+		goto errout;
+	}
+	err = dca_gather(sig, reqp->dr_ibuf_kaddr+SHA1LEN, DSAPARTLEN, 1);
+	if (err != CRYPTO_SUCCESS) {
+		DBG(dca, DWARN,
+		    "dca_dsa_vrfy: dca_gather() failed for r");
+		rv = err;
+		goto errout;
+	}
+	err = dca_gather(sig, reqp->dr_ibuf_kaddr+SHA1LEN+DSAPARTLEN,
+	    DSAPARTLEN, 1);
+	if (err != CRYPTO_SUCCESS) {
+		DBG(dca, DWARN,
+		    "dca_dsa_vrfy: dca_gather() failed for s");
+		rv = err;
+		goto errout;
+	}
+	/*
+	 * As dca_gather() increments the cd_offset and decrements
+	 * the cd_length as it copies the data rewind the values ready for
+	 * the final compare.
+	 */
+	sig->cd_offset -= (DSAPARTLEN * 2);
+	sig->cd_length += (DSAPARTLEN * 2);
+	/* sync the input buffer */
+	(void) ddi_dma_sync(reqp->dr_ibuf_dmah, 0, SHA1LEN + DSAPARTLEN,
+	    DDI_DMA_SYNC_FORDEV);
+
+	if (dca_check_dma_handle(dca, reqp->dr_ibuf_dmah,
+	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+		reqp->destroy = TRUE;
+		rv = CRYPTO_DEVICE_ERROR;
+		goto errout;
+	}
+
+	reqp->dr_in = data;
+	reqp->dr_out = sig;
+	reqp->dr_kcf_req = req;
+	reqp->dr_flags |= DR_SCATTER | DR_GATHER;
+	reqp->dr_callback = dca_dsa_verify_done;
+
+	/*
+	 * Input requires three buffers.  m, followed by r, followed by s.
+	 * In order to deal with things cleanly, we reverse the signature
+	 * into the buffer and then fix up the pointers.
+	 */
+	reqp->dr_pkt_length = SHA1LEN;
+
+	reqp->dr_in_paddr = reqp->dr_ibuf_paddr;
+	reqp->dr_in_len = SHA1LEN;
+	reqp->dr_in_next = reqp->dr_ctx_paddr + reqp->dr_offset;
+
+	reqp->dr_out_paddr = reqp->dr_obuf_paddr;
+	reqp->dr_out_len = DSAPARTLEN;
+	reqp->dr_out_next = 0;
+
+	/* setup 1st chain for r */
+	kaddr = reqp->dr_ctx_kaddr + reqp->dr_offset;
+	PUTDESC32(reqp, kaddr, DESC_BUFADDR, reqp->dr_ibuf_paddr + SHA1LEN);
+	PUTDESC32(reqp, kaddr, DESC_NEXT,
+	    reqp->dr_ctx_paddr + reqp->dr_offset + DESC_SIZE);
+	PUTDESC16(reqp, kaddr, DESC_RSVD, 0);
+	PUTDESC16(reqp, kaddr, DESC_LENGTH, DSAPARTLEN);
+
+	/* and 2nd chain for s */
+	kaddr = reqp->dr_ctx_kaddr + reqp->dr_offset + DESC_SIZE;
+	PUTDESC32(reqp, kaddr, DESC_BUFADDR, reqp->dr_ibuf_paddr +
+	    SHA1LEN + DSAPARTLEN);
+	PUTDESC32(reqp, kaddr, DESC_NEXT, 0);
+	PUTDESC16(reqp, kaddr, DESC_RSVD, 0);
+	PUTDESC16(reqp, kaddr, DESC_LENGTH, DSAPARTLEN);
+
+	/* schedule the work by doing a submit */
+	rv = dca_start(dca, reqp, MCR2, 1);
+
+errout:
+	if (rv != CRYPTO_QUEUED && rv != CRYPTO_BUFFER_TOO_SMALL) {
+		(void) dca_free_context(ctx);
+	}
+	return (rv);
+}
+
+static void
+dca_dsa_verify_done(dca_request_t *reqp, int errno)
+{
+	if (errno == CRYPTO_SUCCESS) {
+		int		count = DSAPARTLEN;
+		crypto_data_t	*sig = reqp->dr_out;
+		caddr_t		daddr;
+
+		(void) ddi_dma_sync(reqp->dr_obuf_dmah, 0, count,
+		    DDI_DMA_SYNC_FORKERNEL);
+		if (dca_check_dma_handle(reqp->dr_dca, reqp->dr_obuf_dmah,
+		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+			reqp->destroy = TRUE;
+			errno = CRYPTO_DEVICE_ERROR;
+			goto errout;
+		}
+
+		/* Can only handle a contiguous data buffer currently. */
+		if (dca_sgcheck(reqp->dr_dca, sig, DCA_SG_CONTIG)) {
+			errno = CRYPTO_SIGNATURE_INVALID;
+			goto errout;
+		}
+
+		if ((daddr = dca_bufdaddr(sig)) == NULL) {
+			errno = CRYPTO_ARGUMENTS_BAD;
+			goto errout;
+		}
+
+		if (dca_bcmp_reverse(daddr, reqp->dr_obuf_kaddr,
+		    DSAPARTLEN) != 0) {
+			/* VERIFY FAILED */
+			errno = CRYPTO_SIGNATURE_INVALID;
+		}
+	}
+errout:
+	ASSERT(reqp->dr_kcf_req != NULL);
+
+	/* notify framework that request is completed */
+
+	crypto_op_notification(reqp->dr_kcf_req, errno);
+	DBG(reqp->dr_dca, DINTR,
+	    "dca_dsa_verify_done: rtn 0x%x to kef via crypto_op_notification",
+	    errno);
+
+	/*
+	 * For non-atomic operations, reqp will be freed in the kCF
+	 * callback function since it may be needed again if
+	 * CRYPTO_BUFFER_TOO_SMALL is returned to kCF
+	 */
+	if (reqp->dr_ctx.atomic) {
+		crypto_ctx_t ctx;
+		ctx.cc_provider_private = reqp;
+		dca_dsactxfree(&ctx);
+	}
+}
+
+/* ARGSUSED */
+int
+dca_dsainit(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, int kmflag, int mode)
+{
+	crypto_object_attribute_t	*attr;
+	unsigned			plen = 0, qlen = 0, glen = 0, xlen = 0;
+	uchar_t				*p, *q, *g, *x;
+	dca_request_t			*reqp = NULL;
+	dca_t				*dca = (dca_t *)ctx->cc_provider;
+	int				rv = CRYPTO_SUCCESS;
+	unsigned			pbits, padjlen;
+	uint16_t			ctxlen;
+	caddr_t				kaddr;
+
+	if ((reqp = dca_getreq(dca, MCR2, 1)) == NULL) {
+		dca_error(dca,
+		    "dca_dsainit: unable to allocate request for DSA");
+		rv = CRYPTO_HOST_MEMORY;
+		goto errout;
+	}
+
+	ctx->cc_provider_private = reqp;
+	reqp->dr_ctx.ctx_cm_type = mechanism->cm_type;
+
+	if ((attr = dca_get_key_attr(key)) == NULL) {
+		DBG(NULL, DWARN, "dca_dsainit: key attributes missing");
+		rv = CRYPTO_KEY_TYPE_INCONSISTENT;
+		goto errout;
+	}
+
+	/* Prime */
+	if (dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_PRIME,
+	    (void *) &p, &plen)) {
+		DBG(NULL, DWARN, "dca_dsainit: prime key value not present");
+		rv = CRYPTO_ARGUMENTS_BAD;
+		goto errout;
+	}
+
+	/* Subprime */
+	if (dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_SUBPRIME,
+	    (void *) &q, &qlen)) {
+		DBG(NULL, DWARN, "dca_dsainit: subprime key value not present");
+		rv = CRYPTO_ARGUMENTS_BAD;
+		goto errout;
+	}
+
+	/* Base */
+	if (dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_BASE,
+	    (void *) &g, &glen)) {
+		DBG(NULL, DWARN, "dca_dsainit: base key value not present");
+		rv = CRYPTO_ARGUMENTS_BAD;
+		goto errout;
+	}
+
+	/* Value */
+	if (dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_VALUE,
+	    (void *) &x, &xlen)) {
+		DBG(NULL, DWARN, "dca_dsainit: value key not present");
+		rv = CRYPTO_ARGUMENTS_BAD;
+		goto errout;
+	}
+
+	if (plen == 0 || qlen == 0 || glen == 0 || xlen == 0) {
+		rv = CRYPTO_ARGUMENTS_BAD;
+		goto errout;
+	}
+
+	if (plen > DSA_MAX_KEY_LEN) {
+		/* maximum 1Kbit key */
+		DBG(NULL, DWARN, "dca_dsainit: maximum 1Kbit key (%d)", plen);
+		rv = CRYPTO_KEY_SIZE_RANGE;
+		goto errout;
+	}
+
+	if (qlen > DSAPARTLEN) {
+		DBG(NULL, DWARN, "dca_dsainit: q is too long (%d)", qlen);
+		rv = CRYPTO_KEY_SIZE_RANGE;
+		goto errout;
+	}
+
+	if (mode == DCA_DSA_SIGN && xlen > DSAPARTLEN) {
+		DBG(NULL, DWARN,
+		    "dca_dsainit: private key is too long (%d)", xlen);
+		rv = CRYPTO_KEY_SIZE_RANGE;
+		goto errout;
+	}
+
+	/*
+	 * Setup the key partion of the request.
+	 */
+
+	pbits = dca_bitlen(p, plen);
+	padjlen = dca_padfull(pbits);
+
+	/* accounts for leading context words */
+	if (mode == DCA_DSA_SIGN) {
+		ctxlen = CTX_DSABIGNUMS + DSAPARTLEN + (padjlen * 2) +
+		    DSAPARTLEN;
+		PUTCTX16(reqp, CTX_CMD, CMD_DSASIGN);
+	} else {
+		ctxlen = CTX_DSABIGNUMS + DSAPARTLEN + (padjlen * 3);
+		PUTCTX16(reqp, CTX_CMD, CMD_DSAVERIFY);
+	}
+
+	PUTCTX16(reqp, CTX_LENGTH, ctxlen);
+	PUTCTX16(reqp, CTX_DSAMSGTYPE, CTX_DSAMSGTYPE_SHA1);
+	PUTCTX16(reqp, CTX_DSARSVD, 0);
+	if (mode == DCA_DSA_SIGN)
+		PUTCTX16(reqp, CTX_DSARNG, CTX_DSARNG_GEN);
+	else
+		PUTCTX16(reqp, CTX_DSARNG, 0);
+	PUTCTX16(reqp, CTX_DSAPLEN, pbits);
+
+	kaddr = reqp->dr_ctx_kaddr + CTX_DSABIGNUMS;
+
+	/* store the bignums */
+	dca_reverse(q, kaddr, qlen, DSAPARTLEN);
+	kaddr += DSAPARTLEN;
+
+	dca_reverse(p, kaddr, plen, padjlen);
+	kaddr += padjlen;
+
+	dca_reverse(g, kaddr, glen, padjlen);
+	kaddr += padjlen;
+
+	if (mode == DCA_DSA_SIGN) {
+		dca_reverse(x, kaddr, xlen, DSAPARTLEN);
+		kaddr += DSAPARTLEN;
+	} else {
+		dca_reverse(x, kaddr, xlen, padjlen);
+		kaddr += padjlen;
+	}
+
+	return (CRYPTO_SUCCESS);
+
+errout:
+
+	dca_dsactxfree(ctx);
+	return (rv);
+}
+
+void
+dca_dsactxfree(void *arg)
+{
+	crypto_ctx_t	*ctx = (crypto_ctx_t *)arg;
+	dca_request_t	*reqp = ctx->cc_provider_private;
+
+	if (reqp == NULL)
+		return;
+
+	reqp->dr_ctx.ctx_cm_type = 0;
+	reqp->dr_ctx.atomic = 0;
+	if (reqp->destroy)
+		dca_destroyreq(reqp);
+	else
+		dca_freereq(reqp);
+
+	ctx->cc_provider_private = NULL;
+}
+
+int
+dca_dsaatomic(crypto_provider_handle_t provider,
+    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_data_t *data, crypto_data_t *sig,
+    int kmflag, crypto_req_handle_t req, int mode)
+{
+	crypto_ctx_t	ctx;	/* on the stack */
+	int		rv;
+
+	ctx.cc_provider = provider;
+	ctx.cc_session = session_id;
+
+	rv = dca_dsainit(&ctx, mechanism, key, kmflag, mode);
+	if (rv != CRYPTO_SUCCESS) {
+		DBG(NULL, DWARN, "dca_dsaatomic: dca_dsainit() failed");
+		return (rv);
+	}
+
+	/*
+	 * Set the atomic flag so that the hardware callback function
+	 * will free the context.
+	 */
+	((dca_request_t *)ctx.cc_provider_private)->dr_ctx.atomic = 1;
+
+	if (mode == DCA_DSA_SIGN) {
+		rv = dca_dsa_sign(&ctx, data, sig, req);
+	} else {
+		ASSERT(mode == DCA_DSA_VRFY);
+		rv = dca_dsa_verify(&ctx, data, sig, req);
+	}
+
+	/*
+	 * The context will be freed in the hardware callback function if it
+	 * is queued
+	 */
+	if (rv != CRYPTO_QUEUED)
+		dca_dsactxfree(&ctx);
+
+	return (rv);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/crypto/io/dca_kstat.c	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,208 @@
+/*
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Deimos - cryptographic acceleration based upon Broadcom 582x.
+ */
+
+#include <sys/types.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/kstat.h>
+#include <sys/crypto/dca.h>
+
+/*
+ * Kernel statistics.
+ */
+static int dca_ksupdate(kstat_t *, int);
+
+/*
+ * Initialize Kstats.
+ */
+void
+dca_ksinit(dca_t *dca)
+{
+	char	buf[64];
+	int	instance;
+
+	if (ddi_getprop(DDI_DEV_T_ANY, dca->dca_dip,
+	    DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "nostats", 0) != 0) {
+		/*
+		 * sysadmin has explicity disabled stats to prevent
+		 * covert channel.
+		 */
+		return;
+	}
+
+	instance = ddi_get_instance(dca->dca_dip);
+
+	/*
+	 * Interrupt kstats.
+	 */
+	(void) sprintf(buf, "%sc%d", DRIVER, instance);
+	if ((dca->dca_intrstats = kstat_create(DRIVER, instance, buf,
+	    "controller", KSTAT_TYPE_INTR, 1, 0)) == NULL) {
+		dca_error(dca, "unable to create interrupt kstat");
+	} else {
+		kstat_install(dca->dca_intrstats);
+	}
+
+	/*
+	 * Named kstats.
+	 */
+	if ((dca->dca_ksp = kstat_create(DRIVER, instance, NULL, "misc",
+	    KSTAT_TYPE_NAMED, sizeof (dca_stat_t) / sizeof (kstat_named_t),
+	    KSTAT_FLAG_WRITABLE)) == NULL) {
+		dca_error(dca, "unable to create kstats");
+	} else {
+		dca_stat_t *dkp = (dca_stat_t *)dca->dca_ksp->ks_data;
+		kstat_named_init(&dkp->ds_status, "status", KSTAT_DATA_CHAR);
+		kstat_named_init(&dkp->ds_mcr[0].ds_submit, "mcr1submit",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_mcr[0].ds_flowctl, "mcr1flowctl",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_mcr[0].ds_lowater, "mcr1lowater",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_mcr[0].ds_hiwater, "mcr1hiwater",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_mcr[0].ds_maxreqs, "mcr1maxreqs",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_mcr[1].ds_submit, "mcr2submit",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_mcr[1].ds_flowctl, "mcr2flowctl",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_mcr[1].ds_lowater, "mcr2lowater",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_mcr[1].ds_hiwater, "mcr2hiwater",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_mcr[1].ds_maxreqs, "mcr2maxreqs",
+		    KSTAT_DATA_ULONGLONG);
+#ifdef	DS_RC4JOBS
+		/* rc4 */
+		kstat_named_init(&dkp->ds_algs[DS_RC4JOBS], "rc4jobs",
+		    KSTAT_DATA_ULONGLONG);
+#endif
+#ifdef	DS_RC4BYTES
+		kstat_named_init(&dkp->ds_algs[DS_RC4BYTES], "rc4bytes",
+		    KSTAT_DATA_ULONGLONG);
+#endif
+		/* 3des */
+		kstat_named_init(&dkp->ds_algs[DS_3DESJOBS], "3desjobs",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_algs[DS_3DESBYTES], "3desbytes",
+		    KSTAT_DATA_ULONGLONG);
+		/* rsa */
+		kstat_named_init(&dkp->ds_algs[DS_RSAPUBLIC], "rsapublic",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_algs[DS_RSAPRIVATE], "rsaprivate",
+		    KSTAT_DATA_ULONGLONG);
+		/* dsa */
+		kstat_named_init(&dkp->ds_algs[DS_DSASIGN], "dsasign",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_algs[DS_DSAVERIFY], "dsaverify",
+		    KSTAT_DATA_ULONGLONG);
+#ifdef	DS_DHPUBLIC
+		/* diffie-hellman */
+		kstat_named_init(&dkp->ds_algs[DS_DHPUBLIC], "dhpublic",
+		    KSTAT_DATA_ULONGLONG);
+#endif
+#ifdef	DS_DHSECRET
+		kstat_named_init(&dkp->ds_algs[DS_DHSECRET], "dhsecret",
+		    KSTAT_DATA_ULONGLONG);
+#endif
+		/* random number jobs */
+		kstat_named_init(&dkp->ds_algs[DS_RNGJOBS], "rngjobs",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_algs[DS_RNGBYTES], "rngbytes",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_algs[DS_RNGSHA1JOBS], "rngsha1jobs",
+		    KSTAT_DATA_ULONGLONG);
+		kstat_named_init(&dkp->ds_algs[DS_RNGSHA1BYTES],
+		    "rngsha1bytes", KSTAT_DATA_ULONGLONG);
+		dca->dca_ksp->ks_update = dca_ksupdate;
+		dca->dca_ksp->ks_private = dca;
+		kstat_install(dca->dca_ksp);
+	}
+}
+
+/*
+ * Update Kstats.
+ */
+int
+dca_ksupdate(kstat_t *ksp, int rw)
+{
+	dca_t		*dca;
+	dca_stat_t	*dkp;
+	int		i;
+
+	dca = (dca_t *)ksp->ks_private;
+	dkp = (dca_stat_t *)ksp->ks_data;
+
+	if (rw == KSTAT_WRITE) {
+		for (i = 0; i < DS_MAX; i++) {
+			dca->dca_stats[i] = dkp->ds_algs[i].value.ull;
+		}
+		for (i = MCR1; i <= MCR2; i++) {
+			WORKLIST(dca, i)->dwl_submit =
+			    dkp->ds_mcr[i - 1].ds_submit.value.ull;
+			WORKLIST(dca, i)->dwl_flowctl =
+			    dkp->ds_mcr[i - 1].ds_flowctl.value.ull;
+			/* hiwater, lowater, and maxreqs are read only */
+		}
+	} else {
+		/* handy status value */
+		if (dca->dca_flags & DCA_FAILED) {
+			/* device has failed */
+			(void) strcpy(dkp->ds_status.value.c, "fail");
+		} else if ((WORKLIST(dca, MCR1)->dwl_drain) ||
+		    (WORKLIST(dca, MCR2)->dwl_drain)) {
+			/* device is draining for DR */
+			(void) strcpy(dkp->ds_status.value.c, "drain");
+		} else {
+			/* everything looks good */
+			(void) strcpy(dkp->ds_status.value.c, "online");
+		}
+
+		for (i = 0; i < DS_MAX; i++) {
+			dkp->ds_algs[i].value.ull = dca->dca_stats[i];
+		}
+		for (i = MCR1; i <= MCR2; i++) {
+			dkp->ds_mcr[i - 1].ds_submit.value.ull =
+			    WORKLIST(dca, i)->dwl_submit;
+			dkp->ds_mcr[i - 1].ds_flowctl.value.ull =
+			    WORKLIST(dca, i)->dwl_flowctl;
+			dkp->ds_mcr[i - 1].ds_lowater.value.ull =
+			    WORKLIST(dca, i)->dwl_lowater;
+			dkp->ds_mcr[i - 1].ds_hiwater.value.ull =
+			    WORKLIST(dca, i)->dwl_hiwater;
+			dkp->ds_mcr[i - 1].ds_maxreqs.value.ull =
+			    WORKLIST(dca, i)->dwl_reqspermcr;
+		}
+	}
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/crypto/io/dca_rng.c	Mon Nov 14 11:14:44 2005 -0800
@@ -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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Deimos - cryptographic acceleration based upon Broadcom 582x.
+ */
+
+#include <sys/types.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/kmem.h>
+#include <sys/crypto/dca.h>
+#include <sys/atomic.h>
+
+/*
+ * Random number implementation.
+ */
+
+static int dca_rngstart(dca_t *, dca_request_t *);
+static void dca_rngdone(dca_request_t *, int);
+
+static void dca_random_done();
+int dca_random_buffer(dca_t *dca, caddr_t buf, int len);
+int dca_random_init();
+void dca_random_fini();
+
+int
+dca_rng(dca_t *dca, uchar_t *buf, size_t len, crypto_req_handle_t req)
+{
+	dca_request_t	*reqp;
+	int		rv;
+	crypto_data_t	*data;
+
+	if ((reqp = dca_getreq(dca, MCR2, 1)) == NULL) {
+		dca_error(dca, "unable to allocate request for RNG");
+		return (CRYPTO_HOST_MEMORY);
+	}
+
+	reqp->dr_kcf_req = req;
+
+	data = &reqp->dr_ctx.in_dup;
+	data->cd_format = CRYPTO_DATA_RAW;
+	data->cd_offset = 0;
+	data->cd_length = 0;
+	data->cd_raw.iov_base = (char *)buf;
+	data->cd_raw.iov_len = len;
+	reqp->dr_out = data;
+	reqp->dr_in = NULL;
+
+	rv = dca_rngstart(dca, reqp);
+	if (rv != CRYPTO_QUEUED) {
+		if (reqp->destroy)
+			dca_destroyreq(reqp);
+		else
+			dca_freereq(reqp);
+	}
+	return (rv);
+}
+
+int
+dca_rngstart(dca_t *dca, dca_request_t *reqp)
+{
+	uint16_t	cmd;
+	size_t		len;
+	uint16_t	chunk;
+	crypto_data_t	*out = reqp->dr_out;
+
+	if (dca->dca_flags & DCA_RNGSHA1) {
+		reqp->dr_job_stat = DS_RNGSHA1JOBS;
+		reqp->dr_byte_stat = DS_RNGSHA1BYTES;
+		cmd = CMD_RNGSHA1;
+	} else {
+		reqp->dr_job_stat = DS_RNGJOBS;
+		reqp->dr_byte_stat = DS_RNGBYTES;
+		cmd = CMD_RNGDIRECT;
+	}
+
+	len = out->cd_raw.iov_len - out->cd_length;
+	len = min(len, MAXPACKET & ~0xf);
+	chunk = ROUNDUP(len, sizeof (uint32_t));
+
+	if ((len < dca_mindma) ||
+	    dca_sgcheck(dca, reqp->dr_out, DCA_SG_WALIGN)) {
+		reqp->dr_flags |= DR_SCATTER;
+	}
+
+	/* Try to do direct DMA. */
+	if (!(reqp->dr_flags & DR_SCATTER)) {
+		if (dca_bindchains(reqp, 0, len) != DDI_SUCCESS) {
+			return (CRYPTO_DEVICE_ERROR);
+		}
+	}
+
+	reqp->dr_in_paddr = 0;
+	reqp->dr_in_next = 0;
+	reqp->dr_in_len = 0;
+
+	/*
+	 * Setup for scattering the result back out
+	 * Using the pre-mapped buffers to store random numbers. Since the
+	 * data buffer is a linked list, we need to transfer its head to MCR
+	 */
+	if (reqp->dr_flags & DR_SCATTER) {
+		reqp->dr_out_paddr = reqp->dr_obuf_head.dc_buffer_paddr;
+		reqp->dr_out_next = reqp->dr_obuf_head.dc_next_paddr;
+		if (chunk > reqp->dr_obuf_head.dc_buffer_length)
+			reqp->dr_out_len = reqp->dr_obuf_head.dc_buffer_length;
+		else
+			reqp->dr_out_len = chunk;
+	}
+	reqp->dr_param.dp_rng.dr_chunklen = len;
+	reqp->dr_pkt_length = (uint16_t)chunk;
+	reqp->dr_callback = dca_rngdone;
+
+	/* write out the context structure */
+	PUTCTX16(reqp, CTX_LENGTH, CTX_RNG_LENGTH);
+	PUTCTX16(reqp, CTX_CMD, cmd);
+
+	/* schedule the work by doing a submit */
+	return (dca_start(dca, reqp, MCR2, 1));
+}
+
+void
+dca_rngdone(dca_request_t *reqp, int errno)
+{
+	if (errno == CRYPTO_SUCCESS) {
+
+		if (reqp->dr_flags & DR_SCATTER) {
+			(void) ddi_dma_sync(reqp->dr_obuf_dmah, 0,
+				reqp->dr_out_len, DDI_DMA_SYNC_FORKERNEL);
+			if (dca_check_dma_handle(reqp->dr_dca,
+			    reqp->dr_obuf_dmah, DCA_FM_ECLASS_NONE) !=
+			    DDI_SUCCESS) {
+				reqp->destroy = TRUE;
+				errno = CRYPTO_DEVICE_ERROR;
+				goto errout;
+			}
+			errno = dca_scatter(reqp->dr_obuf_kaddr,
+			    reqp->dr_out, reqp->dr_param.dp_rng.dr_chunklen, 0);
+			if (errno != CRYPTO_SUCCESS) {
+				goto errout;
+			}
+		} else {
+			reqp->dr_out->cd_length +=
+			    reqp->dr_param.dp_rng.dr_chunklen;
+		}
+
+		/*
+		 * If there is more to do, then reschedule another
+		 * pass.
+		 */
+		if (reqp->dr_out->cd_length < reqp->dr_out->cd_raw.iov_len) {
+			errno = dca_rngstart(reqp->dr_dca, reqp);
+			if (errno == CRYPTO_QUEUED) {
+				return;
+			}
+		}
+	}
+
+errout:
+
+	if (reqp->dr_kcf_req) {
+		/* notify framework that request is completed */
+		crypto_op_notification(reqp->dr_kcf_req, errno);
+	} else {
+		/* For internal random number generation */
+		dca_random_done(reqp->dr_dca);
+	}
+
+	DBG(NULL, DINTR,
+	    "dca_rngdone: returning %d to the kef via crypto_op_notification",
+	    errno);
+	if (reqp->destroy)
+		dca_destroyreq(reqp);
+	else
+		dca_freereq(reqp);
+}
+
+/*
+ * This gives a 32k random bytes per buffer. The two buffers will switch back
+ * and forth. When a buffer is used up, a request will be submitted to refill
+ * this buffer before switching to the other one
+ */
+
+#define	RANDOM_BUFFER_SIZE		(1<<15)
+#define	DCA_RANDOM_MAX_WAIT		10000
+
+int
+dca_random_init(dca_t *dca)
+{
+	/* Mutex for the local random number pool */
+	mutex_init(&dca->dca_random_lock, NULL, MUTEX_DRIVER, NULL);
+
+	if ((dca->dca_buf1 = kmem_alloc(RANDOM_BUFFER_SIZE, KM_SLEEP)) ==
+	    NULL) {
+		mutex_destroy(&dca->dca_random_lock);
+		return (CRYPTO_FAILED);
+	}
+
+	if ((dca->dca_buf2 = kmem_alloc(RANDOM_BUFFER_SIZE, KM_SLEEP)) ==
+	    NULL) {
+		mutex_destroy(&dca->dca_random_lock);
+		kmem_free(dca->dca_buf1, RANDOM_BUFFER_SIZE);
+		return (CRYPTO_FAILED);
+	}
+
+	return (CRYPTO_SUCCESS);
+}
+
+void
+dca_random_fini(dca_t *dca)
+{
+	kmem_free(dca->dca_buf1, RANDOM_BUFFER_SIZE);
+	kmem_free(dca->dca_buf2, RANDOM_BUFFER_SIZE);
+	dca->dca_buf1 = dca->dca_buf2 = dca->dca_buf_ptr = NULL;
+	(void) mutex_destroy(&dca->dca_random_lock);
+}
+
+int
+dca_random_buffer(dca_t *dca, caddr_t buf, int len)
+{
+	int rv;
+	int i, j;
+	char *fill_buf;
+
+	mutex_enter(&dca->dca_random_lock);
+
+	if (dca->dca_buf_ptr == NULL) {
+		if (dca->dca_buf1 == NULL || dca->dca_buf2 == NULL) {
+			mutex_exit(&dca->dca_random_lock);
+			return (CRYPTO_FAILED);
+		}
+
+		/* Very first time. Let us fill the first buffer */
+		if (dca_rng(dca, (uchar_t *)dca->dca_buf1, RANDOM_BUFFER_SIZE,
+		    NULL) != CRYPTO_QUEUED) {
+			mutex_exit(&dca->dca_random_lock);
+			return (CRYPTO_FAILED);
+		}
+
+		atomic_or_32(&dca->dca_random_filling, 0x1);
+
+		/* Pretend we are using buffer2 and it is empty */
+		dca->dca_buf_ptr = dca->dca_buf2;
+		dca->dca_index = RANDOM_BUFFER_SIZE;
+	}
+
+	i = 0;
+	while (i < len) {
+		if (dca->dca_index >= RANDOM_BUFFER_SIZE) {
+			j = 0;
+			while (dca->dca_random_filling) {
+				/* Only wait here at the first time */
+				delay(drv_usectohz(100));
+				if (j++ >= DCA_RANDOM_MAX_WAIT)
+					break;
+			}
+			DBG(NULL, DENTRY, "dca_random_buffer: j: %d", j);
+			if (j > DCA_RANDOM_MAX_WAIT) {
+				mutex_exit(&dca->dca_random_lock);
+				return (CRYPTO_FAILED);
+			}
+
+			/* switch to the other buffer */
+			if (dca->dca_buf_ptr == dca->dca_buf1) {
+				dca->dca_buf_ptr = dca->dca_buf2;
+				fill_buf = dca->dca_buf1;
+			} else {
+				dca->dca_buf_ptr = dca->dca_buf1;
+				fill_buf = dca->dca_buf2;
+			}
+
+			atomic_or_32(&dca->dca_random_filling, 0x1);
+			dca->dca_index = 0;
+
+			if ((rv = dca_rng(dca, (uchar_t *)fill_buf,
+			    RANDOM_BUFFER_SIZE, NULL)) != CRYPTO_QUEUED) {
+				mutex_exit(&dca->dca_random_lock);
+				return (rv);
+			}
+		}
+
+		if (dca->dca_buf_ptr[dca->dca_index] != '\0')
+			buf[i++] = dca->dca_buf_ptr[dca->dca_index];
+
+		dca->dca_index++;
+	}
+
+	mutex_exit(&dca->dca_random_lock);
+
+	DBG(NULL, DENTRY, "dca_random_buffer: i: %d", i);
+	return (CRYPTO_SUCCESS);
+}
+
+static void
+dca_random_done(dca_t *dca)
+{
+	DBG(NULL, DENTRY, "dca_random_done");
+	atomic_and_32(&dca->dca_random_filling, 0x0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/crypto/io/dca_rsa.c	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,832 @@
+/*
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Deimos - cryptographic acceleration based upon Broadcom 582x.
+ */
+
+#include <sys/types.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/kmem.h>
+#include <sys/note.h>
+#include <sys/crypto/common.h>
+#include <sys/crypto/spi.h>
+#include <sys/crypto/ioctl.h>
+#include <sys/crypto/dca.h>
+
+
+static void dca_rsaverifydone(dca_request_t *, int);
+static void dca_rsadone(dca_request_t *, int);
+
+/* Exported function prototypes */
+int dca_rsastart(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
+    crypto_req_handle_t, int);
+int dca_rsainit(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *, int);
+void dca_rsactxfree(void *);
+int dca_rsaatomic(crypto_provider_handle_t, crypto_session_id_t,
+    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
+    int, crypto_req_handle_t, int);
+
+/* Local function prototypes */
+static int dca_pkcs1_padding(dca_t *dca, caddr_t buf, int flen, int tlen,
+    int private);
+static int dca_pkcs1_unpadding(char *buf, int *tlen, int flen, int mode);
+static int dca_x509_padding(caddr_t buf, int flen, int tlen);
+static int dca_x509_unpadding(char *buf, int tlen, int flen, int mode);
+static int decrypt_error_code(int mode, int decrypt, int verify, int def);
+
+
+int dca_rsastart(crypto_ctx_t *ctx, crypto_data_t *in, crypto_data_t *out,
+    crypto_req_handle_t req, int mode)
+{
+	dca_request_t		*reqp = ctx->cc_provider_private;
+	dca_t			*dca = ctx->cc_provider;
+	caddr_t			daddr;
+	int			rv = CRYPTO_QUEUED;
+	int			len;
+
+	/*
+	 * In-place operations (in == out) are indicated by having a
+	 * NULL output. In this case set the out to point to the in.
+	 * Note that this only works for CKM_RSA_X_509 without any padding
+	 */
+	if (!out) {
+		DBG(dca, DWARN, "Using inline since output buffer is NULL.");
+		out = in;
+	}
+
+	/* We don't support non-contiguous buffers for RSA */
+	if (dca_sgcheck(dca, in, DCA_SG_CONTIG) ||
+	    dca_sgcheck(dca, out, DCA_SG_CONTIG)) {
+		rv = CRYPTO_NOT_SUPPORTED;
+		goto errout;
+	}
+
+	len = dca_length(in);
+
+	/* Extracting the key attributes is now done in dca_rsainit(). */
+	if (mode == DCA_RSA_ENC || mode == DCA_RSA_SIGN ||
+	    mode == DCA_RSA_SIGNR) {
+		/*
+		 * Return length needed to store the output.
+		 * For sign, sign-recover, and encrypt, the output buffer
+		 * should not be smaller than modlen since PKCS or X_509
+		 * padding will be applied
+		 */
+		if (dca_length(out) < reqp->dr_ctx.modlen) {
+			DBG(dca, DWARN,
+			    "dca_rsastart: output buffer too short (%d < %d)",
+			    dca_length(out), reqp->dr_ctx.modlen);
+			out->cd_length = reqp->dr_ctx.modlen;
+			rv = CRYPTO_BUFFER_TOO_SMALL;
+			goto errout;
+		}
+	}
+	if (out != in && out->cd_length > reqp->dr_ctx.modlen)
+		out->cd_length = reqp->dr_ctx.modlen;
+
+	/* The input length should not be bigger than the modulus */
+	if (len > reqp->dr_ctx.modlen) {
+		rv = decrypt_error_code(mode, CRYPTO_ENCRYPTED_DATA_LEN_RANGE,
+		    CRYPTO_SIGNATURE_LEN_RANGE, CRYPTO_DATA_LEN_RANGE);
+		goto errout;
+	}
+
+	/*
+	 * For decryption, verify, and verifyRecover, the input length should
+	 * not be less than the modulus
+	 */
+	if (len < reqp->dr_ctx.modlen && (mode == DCA_RSA_DEC ||
+	    mode == DCA_RSA_VRFY || mode == DCA_RSA_VRFYR)) {
+		rv = decrypt_error_code(mode, CRYPTO_ENCRYPTED_DATA_LEN_RANGE,
+		    CRYPTO_SIGNATURE_LEN_RANGE, CRYPTO_DATA_LEN_RANGE);
+		goto errout;
+	}
+
+	/*
+	 * For decryption and verifyRecover, the output buffer should not
+	 * be less than the modulus
+	 */
+	if (out->cd_length < reqp->dr_ctx.modlen && (mode == DCA_RSA_DEC ||
+	    mode == DCA_RSA_VRFYR) &&
+	    reqp->dr_ctx.ctx_cm_type == RSA_X_509_MECH_INFO_TYPE) {
+		out->cd_length = reqp->dr_ctx.modlen;
+		rv = CRYPTO_BUFFER_TOO_SMALL;
+		goto errout;
+	}
+
+	/* For decrypt and verify, the input should not be less than output */
+	if (out && len < out->cd_length) {
+		if ((rv = decrypt_error_code(mode,
+		    CRYPTO_ENCRYPTED_DATA_LEN_RANGE,
+		    CRYPTO_SIGNATURE_LEN_RANGE, CRYPTO_SUCCESS)) !=
+		    CRYPTO_SUCCESS)
+			goto errout;
+	}
+
+	if ((daddr = dca_bufdaddr(in)) == NULL && len > 0) {
+		rv = CRYPTO_ARGUMENTS_BAD;
+		goto errout;
+	}
+
+	if (dca_numcmp(daddr, len, (char *)reqp->dr_ctx.mod,
+	    reqp->dr_ctx.modlen) > 0) {
+		DBG(dca, DWARN,
+		    "dca_rsastart: input larger (numerically) than modulus!");
+		rv = decrypt_error_code(mode, CRYPTO_ENCRYPTED_DATA_INVALID,
+		    CRYPTO_SIGNATURE_INVALID, CRYPTO_DATA_INVALID);
+		goto errout;
+	}
+
+	reqp->dr_byte_stat = -1;
+	reqp->dr_in = in;
+	reqp->dr_out = out;
+	reqp->dr_kcf_req = req;
+	if (mode == DCA_RSA_VRFY)
+		reqp->dr_callback = dca_rsaverifydone;
+	else
+		reqp->dr_callback = dca_rsadone;
+
+	dca_reverse(daddr, reqp->dr_ibuf_kaddr, len, reqp->dr_pkt_length);
+	if (mode == DCA_RSA_ENC || mode == DCA_RSA_SIGN ||
+	    mode == DCA_RSA_SIGNR) {
+		/*
+		 * Needs to pad appropriately for encrypt, sign, and
+		 * sign_recover
+		 */
+		if (reqp->dr_ctx.ctx_cm_type == RSA_PKCS_MECH_INFO_TYPE) {
+			if ((rv = dca_pkcs1_padding(dca, reqp->dr_ibuf_kaddr,
+			    len, reqp->dr_ctx.modlen, reqp->dr_ctx.pqfix)) !=
+			    CRYPTO_QUEUED)
+				goto errout;
+		} else if (reqp->dr_ctx.ctx_cm_type ==
+		    RSA_X_509_MECH_INFO_TYPE) {
+			if ((rv = dca_x509_padding(reqp->dr_ibuf_kaddr,
+			    len, reqp->dr_pkt_length)) != CRYPTO_QUEUED)
+				goto errout;
+		}
+	}
+	reqp->dr_ctx.mode = mode;
+
+	/*
+	 * Since the max RSA input size is 256 bytes (2048 bits), the firstx
+	 * page (at least 4096 bytes) in the pre-mapped buffer is large enough.
+	 * Therefore, we use this first page for RSA.
+	 */
+	reqp->dr_in_paddr = reqp->dr_ibuf_head.dc_buffer_paddr;
+	reqp->dr_in_next = 0;
+	reqp->dr_in_len = reqp->dr_pkt_length;
+	reqp->dr_out_paddr = reqp->dr_obuf_head.dc_buffer_paddr;
+	reqp->dr_out_next = 0;
+	reqp->dr_out_len = reqp->dr_pkt_length;
+
+	/* schedule the work by doing a submit */
+	rv = dca_start(dca, reqp, MCR2, 1);
+
+
+errout:
+	if (rv != CRYPTO_QUEUED && rv != CRYPTO_BUFFER_TOO_SMALL)
+		(void) dca_free_context(ctx);
+
+	return (rv);
+}
+
+void
+dca_rsadone(dca_request_t *reqp, int errno)
+{
+	if (errno == CRYPTO_SUCCESS) {
+		int	outsz = reqp->dr_out->cd_length;
+		caddr_t	daddr;
+
+		(void) ddi_dma_sync(reqp->dr_obuf_dmah, 0, reqp->dr_out_len,
+		    DDI_DMA_SYNC_FORKERNEL);
+		if (dca_check_dma_handle(reqp->dr_dca, reqp->dr_obuf_dmah,
+		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+			reqp->destroy = TRUE;
+			errno = CRYPTO_DEVICE_ERROR;
+			goto errout;
+		}
+
+		if (reqp->dr_ctx.mode == DCA_RSA_DEC ||
+		    reqp->dr_ctx.mode == DCA_RSA_VRFY ||
+		    reqp->dr_ctx.mode == DCA_RSA_VRFYR) {
+			/*
+			 * Needs to unpad appropriately for decrypt, verify,
+			 * and verify_recover
+			 */
+			if (reqp->dr_ctx.ctx_cm_type ==
+			    RSA_PKCS_MECH_INFO_TYPE) {
+				errno = dca_pkcs1_unpadding(
+				    reqp->dr_obuf_kaddr, &outsz,
+				    reqp->dr_ctx.modlen, reqp->dr_ctx.mode);
+
+				/* check for bad data errors */
+				if (errno != CRYPTO_SUCCESS &&
+				    errno != CRYPTO_BUFFER_TOO_SMALL) {
+					goto errout;
+				}
+				if (dca_bufdaddr(reqp->dr_out) == NULL) {
+					errno = CRYPTO_BUFFER_TOO_SMALL;
+				}
+				if (errno == CRYPTO_BUFFER_TOO_SMALL) {
+					reqp->dr_out->cd_length = outsz;
+					goto errout;
+				}
+				/* Reset the output data length */
+				reqp->dr_out->cd_length = outsz;
+			} else if (reqp->dr_ctx.ctx_cm_type ==
+			    RSA_X_509_MECH_INFO_TYPE) {
+				if ((errno = dca_x509_unpadding(
+				    reqp->dr_obuf_kaddr, outsz,
+				    reqp->dr_pkt_length, reqp->dr_ctx.mode)) !=
+				    CRYPTO_SUCCESS)
+					goto errout;
+			}
+		}
+
+		if ((daddr = dca_bufdaddr(reqp->dr_out)) == NULL) {
+			DBG(reqp->dr_dca, DINTR,
+			    "dca_rsadone: reqp->dr_out is bad");
+			errno = CRYPTO_ARGUMENTS_BAD;
+			goto errout;
+		}
+		/*
+		 * Note that there may be some number of null bytes
+		 * at the end of the source (result), but we don't care
+		 * about them -- they are place holders only and are
+		 * truncated here.
+		 */
+		dca_reverse(reqp->dr_obuf_kaddr, daddr, outsz, outsz);
+	}
+errout:
+	ASSERT(reqp->dr_kcf_req != NULL);
+
+	/* notify framework that request is completed */
+	crypto_op_notification(reqp->dr_kcf_req, errno);
+	DBG(reqp->dr_dca, DINTR,
+	    "dca_rsadone: returning 0x%x to the kef via crypto_op_notification",
+	    errno);
+
+	/*
+	 * For non-atomic operations, reqp will be freed in the kCF
+	 * callback function since it may be needed again if
+	 * CRYPTO_BUFFER_TOO_SMALL is returned to kCF
+	 */
+	if (reqp->dr_ctx.atomic) {
+		crypto_ctx_t ctx;
+		ctx.cc_provider_private = reqp;
+		dca_rsactxfree(&ctx);
+	}
+}
+
+void
+dca_rsaverifydone(dca_request_t *reqp, int errno)
+{
+	if (errno == CRYPTO_SUCCESS) {
+		char	scratch[RSA_MAX_KEY_LEN];
+		int	outsz = reqp->dr_out->cd_length;
+		caddr_t	daddr;
+
+		/*
+		 * ASSUMPTION: the signature length was already
+		 * checked on the way in, and it is a valid length.
+		 */
+		(void) ddi_dma_sync(reqp->dr_obuf_dmah, 0, outsz,
+		    DDI_DMA_SYNC_FORKERNEL);
+		if (dca_check_dma_handle(reqp->dr_dca, reqp->dr_obuf_dmah,
+		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
+			reqp->destroy = TRUE;
+			errno = CRYPTO_DEVICE_ERROR;
+			goto errout;
+		}
+
+		if (reqp->dr_ctx.mode == DCA_RSA_DEC ||
+		    reqp->dr_ctx.mode == DCA_RSA_VRFY ||
+		    reqp->dr_ctx.mode == DCA_RSA_VRFYR) {
+			/*
+			 * Needs to unpad appropriately for decrypt, verify,
+			 * and verify_recover
+			 */
+			if (reqp->dr_ctx.ctx_cm_type ==
+			    RSA_PKCS_MECH_INFO_TYPE) {
+				errno = dca_pkcs1_unpadding(
+				    reqp->dr_obuf_kaddr, &outsz,
+				    reqp->dr_ctx.modlen, reqp->dr_ctx.mode);
+
+				/* check for bad data errors */
+				if (errno != CRYPTO_SUCCESS &&
+				    errno != CRYPTO_BUFFER_TOO_SMALL) {
+					goto errout;
+				}
+				if (dca_bufdaddr(reqp->dr_out) == NULL) {
+					errno = CRYPTO_BUFFER_TOO_SMALL;
+				}
+				if (errno == CRYPTO_BUFFER_TOO_SMALL) {
+					reqp->dr_out->cd_length = outsz;
+					goto errout;
+				}
+				/* Reset the output data length */
+				reqp->dr_out->cd_length = outsz;
+			} else if (reqp->dr_ctx.ctx_cm_type ==
+			    RSA_X_509_MECH_INFO_TYPE) {
+				if ((errno = dca_x509_unpadding(
+				    reqp->dr_obuf_kaddr, outsz,
+				    reqp->dr_pkt_length, reqp->dr_ctx.mode)) !=
+				    CRYPTO_SUCCESS)
+					goto errout;
+			}
+		}
+
+		dca_reverse(reqp->dr_obuf_kaddr, scratch, outsz, outsz);
+
+		if ((daddr = dca_bufdaddr(reqp->dr_out)) == NULL) {
+			errno = CRYPTO_ARGUMENTS_BAD;
+			goto errout;
+		}
+		if (dca_numcmp(daddr, reqp->dr_out->cd_length, scratch,
+		    outsz) != 0) {
+			/* VERIFY FAILED */
+			errno = CRYPTO_SIGNATURE_INVALID;
+		}
+	}
+errout:
+	ASSERT(reqp->dr_kcf_req != NULL);
+
+	/* notify framework that request is completed */
+	crypto_op_notification(reqp->dr_kcf_req, errno);
+	DBG(reqp->dr_dca, DINTR,
+	    "dca_rsaverifydone: rtn 0x%x to the kef via crypto_op_notification",
+	    errno);
+
+	/*
+	 * For non-atomic operations, reqp will be freed in the kCF
+	 * callback function since it may be needed again if
+	 * CRYPTO_BUFFER_TOO_SMALL is returned to kCF
+	 */
+	if (reqp->dr_ctx.atomic) {
+		crypto_ctx_t ctx;
+		ctx.cc_provider_private = reqp;
+		dca_rsactxfree(&ctx);
+	}
+}
+
+/*
+ * Setup either a public or a private RSA key for subsequent uses
+ */
+int
+dca_rsainit(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, int kmflag)
+{
+	crypto_object_attribute_t	*attr;
+	unsigned			expname = 0;
+	void				*attrdata;
+	int rv;
+
+	uchar_t			*exp;
+	uchar_t			*p;
+	uchar_t			*q;
+	uchar_t			*dp;
+	uchar_t			*dq;
+	uchar_t			*pinv;
+
+	unsigned		explen = 0;
+	unsigned		plen = 0;
+	unsigned		qlen = 0;
+	unsigned		dplen = 0;
+	unsigned		dqlen = 0;
+	unsigned		pinvlen = 0;
+
+	unsigned		modbits, expbits, pbits, qbits;
+	unsigned		modfix, expfix, pqfix = 0;
+	uint16_t		ctxlen;
+	caddr_t			kaddr;
+	dca_request_t		*reqp = NULL;
+	dca_t			*dca = (dca_t *)ctx->cc_provider;
+
+	DBG(NULL, DENTRY, "dca_rsainit: start");
+
+	if ((reqp = dca_getreq(dca, MCR2, 1)) == NULL) {
+		DBG(NULL, DWARN,
+		    "dca_rsainit: unable to allocate request for RSA");
+		rv = CRYPTO_HOST_MEMORY;
+		goto errout;
+	}
+
+	reqp->dr_ctx.ctx_cm_type = mechanism->cm_type;
+	ctx->cc_provider_private = reqp;
+
+	/*
+	 * Key type can be either RAW, or REFERENCE, or ATTR_LIST (VALUE).
+	 * Only ATTR_LIST is supported on Deimos for RSA.
+	 */
+	if ((attr = dca_get_key_attr(key)) == NULL) {
+		DBG(NULL, DWARN, "dca_rsainit: key attributes missing");
+		rv = CRYPTO_KEY_TYPE_INCONSISTENT;
+		goto errout;
+	}
+
+	if (dca_find_attribute(attr, key->ck_count, CKA_PUBLIC_EXPONENT))
+		expname = CKA_PUBLIC_EXPONENT;
+
+	/*
+	 * RSA public key has only public exponent. RSA private key must have
+	 * private exponent. However, it may also have public exponent.
+	 * Thus, the existance of a private exponent indicates a private key.
+	 */
+	if (dca_find_attribute(attr, key->ck_count, CKA_PRIVATE_EXPONENT))
+		expname = CKA_PRIVATE_EXPONENT;
+
+	if (!expname) {
+		DBG(NULL, DWARN, "dca_rsainit: no exponent in key");
+		rv = CRYPTO_ARGUMENTS_BAD;
+		goto errout;
+	}
+
+	/* Modulus */
+	if ((rv = dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_MODULUS,
+	    &attrdata, &(reqp->dr_ctx.modlen))) != CRYPTO_SUCCESS) {
+		DBG(NULL, DWARN, "dca_rsainit: failed to retrieve modulus");
+		goto errout;
+	}
+	if ((reqp->dr_ctx.modlen == 0) ||
+	    (reqp->dr_ctx.modlen > RSA_MAX_KEY_LEN)) {
+		DBG(NULL, DWARN, "dca_rsainit: bad modulus size");
+		rv = CRYPTO_ARGUMENTS_BAD;
+		goto errout;
+	}
+	if ((reqp->dr_ctx.mod = kmem_alloc(reqp->dr_ctx.modlen, kmflag)) ==
+	    NULL) {
+		rv = CRYPTO_HOST_MEMORY;
+		goto errout;
+	}
+	bcopy(attrdata, reqp->dr_ctx.mod, reqp->dr_ctx.modlen);
+
+	/* Exponent */
+	if ((rv = dca_attr_lookup_uint8_array(attr, key->ck_count, expname,
+	    (void **) &exp, &explen)) != CRYPTO_SUCCESS) {
+		DBG(NULL, DWARN, "dca_rsainit: failed to retrieve exponent");
+		goto errout;
+	}
+	if ((explen == 0) || (explen > RSA_MAX_KEY_LEN)) {
+		DBG(NULL, DWARN, "dca_rsainit: bad exponent size");
+		rv = CRYPTO_ARGUMENTS_BAD;
+		goto errout;
+	}
+
+	/* Lookup private attributes */
+	if (expname == CKA_PRIVATE_EXPONENT) {
+		/* Prime 1 */
+		(void) dca_attr_lookup_uint8_array(attr, key->ck_count,
+		    CKA_PRIME_1, (void **)&q, &qlen);
+
+		/* Prime 2 */
+		(void) dca_attr_lookup_uint8_array(attr, key->ck_count,
+		    CKA_PRIME_2, (void **)&p, &plen);
+
+		/* Exponent 1 */
+		(void) dca_attr_lookup_uint8_array(attr, key->ck_count,
+		    CKA_EXPONENT_1, (void **)&dq, &dqlen);
+
+		/* Exponent 2 */
+		(void) dca_attr_lookup_uint8_array(attr, key->ck_count,
+		    CKA_EXPONENT_2, (void **)&dp, &dplen);
+
+		/* Coefficient */
+		(void) dca_attr_lookup_uint8_array(attr, key->ck_count,
+		    CKA_COEFFICIENT, (void **)&pinv, &pinvlen);
+	}
+
+	modbits = dca_bitlen(reqp->dr_ctx.mod, reqp->dr_ctx.modlen);
+	expbits = dca_bitlen(exp, explen);
+
+	if ((modfix = dca_padfull(modbits)) == 0) {
+		DBG(NULL, DWARN, "dca_rsainit: modulus too long");
+		rv = CRYPTO_KEY_SIZE_RANGE;
+		goto errout;
+	}
+	expfix =  ROUNDUP(explen, sizeof (uint32_t));
+
+	if (plen && qlen && dplen && dqlen && pinvlen) {
+		unsigned pfix, qfix;
+		qbits = dca_bitlen(q, qlen);
+		pbits = dca_bitlen(p, plen);
+		qfix = dca_padhalf(qbits);
+		pfix = dca_padhalf(pbits);
+		if (pfix & qfix)
+			pqfix = max(pfix, qfix);
+	}
+
+	if (pqfix) {
+		reqp->dr_job_stat = DS_RSAPRIVATE;
+		reqp->dr_pkt_length = 2 * pqfix;
+	} else {
+		reqp->dr_job_stat = DS_RSAPUBLIC;
+		reqp->dr_pkt_length = modfix;
+	}
+
+	if (pqfix) {
+		/*
+		 * NOTE: chip's notion of p vs. q is reversed from
+		 * PKCS#11.  We use the chip's notion in our variable
+		 * naming.
+		 */
+		ctxlen = 8 + pqfix * 5;
+
+		/* write out the context structure */
+		PUTCTX16(reqp, CTX_CMD, CMD_RSAPRIVATE);
+		PUTCTX16(reqp, CTX_LENGTH, ctxlen);
+		/* exponent and modulus length in bits!!! */
+		PUTCTX16(reqp, CTX_RSAQLEN, qbits);
+		PUTCTX16(reqp, CTX_RSAPLEN, pbits);
+
+		kaddr = reqp->dr_ctx_kaddr + CTX_RSABIGNUMS;
+
+		/* store the bignums */
+		dca_reverse(p, kaddr, plen, pqfix);
+		kaddr += pqfix;
+
+		dca_reverse(q, kaddr, qlen, pqfix);
+		kaddr += pqfix;
+
+		dca_reverse(dp, kaddr, dplen, pqfix);
+		kaddr += pqfix;
+
+		dca_reverse(dq, kaddr, dqlen, pqfix);
+		kaddr += pqfix;
+
+		dca_reverse(pinv, kaddr, pinvlen, pqfix);
+		kaddr += pqfix;
+	} else {
+		ctxlen = 8 + modfix + expfix;
+		/* write out the context structure */
+		PUTCTX16(reqp, CTX_CMD, CMD_RSAPUBLIC);
+		PUTCTX16(reqp, CTX_LENGTH, (uint16_t)ctxlen);
+		/* exponent and modulus length in bits!!! */
+		PUTCTX16(reqp, CTX_RSAEXPLEN, expbits);
+		PUTCTX16(reqp, CTX_RSAMODLEN, modbits);
+
+		kaddr = reqp->dr_ctx_kaddr + CTX_RSABIGNUMS;
+
+		/* store the bignums */
+		dca_reverse(reqp->dr_ctx.mod, kaddr, reqp->dr_ctx.modlen,
+		    modfix);
+		kaddr += modfix;
+
+		dca_reverse(exp, kaddr, explen, expfix);
+		kaddr += expfix;
+	}
+
+	reqp->dr_ctx.pqfix = pqfix;
+
+errout:
+	if (rv != CRYPTO_SUCCESS)
+		dca_rsactxfree(ctx);
+
+	return (rv);
+}
+
+void
+dca_rsactxfree(void *arg)
+{
+	crypto_ctx_t	*ctx = (crypto_ctx_t *)arg;
+	dca_request_t	*reqp = ctx->cc_provider_private;
+
+	if (reqp == NULL)
+		return;
+
+	if (reqp->dr_ctx.mod)
+		kmem_free(reqp->dr_ctx.mod, reqp->dr_ctx.modlen);
+
+	reqp->dr_ctx.mode = 0;
+	reqp->dr_ctx.ctx_cm_type = 0;
+	reqp->dr_ctx.mod = NULL;
+	reqp->dr_ctx.modlen = 0;
+	reqp->dr_ctx.pqfix = 0;
+	reqp->dr_ctx.atomic = 0;
+
+	if (reqp->destroy)
+		dca_destroyreq(reqp);
+	else
+		dca_freereq(reqp);
+
+	ctx->cc_provider_private = NULL;
+}
+
+int
+dca_rsaatomic(crypto_provider_handle_t provider,
+    crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
+    crypto_key_t *key, crypto_data_t *input, crypto_data_t *output,
+    int kmflag, crypto_req_handle_t req, int mode)
+{
+	crypto_ctx_t	ctx;	/* on the stack */
+	int		rv;
+
+	ctx.cc_provider = provider;
+	ctx.cc_session = session_id;
+
+	rv = dca_rsainit(&ctx, mechanism, key, kmflag);
+	if (rv != CRYPTO_SUCCESS) {
+		DBG(NULL, DWARN, "dca_rsaatomic: dca_rsainit() failed");
+		/* The content of ctx should have been freed already */
+		return (rv);
+	}
+
+	/*
+	 * Set the atomic flag so that the hardware callback function
+	 * will free the context.
+	 */
+	((dca_request_t *)ctx.cc_provider_private)->dr_ctx.atomic = 1;
+
+	rv = dca_rsastart(&ctx, input, output, req, mode);
+
+	/*
+	 * The context will be freed in the hardware callback function if it
+	 * is queued
+	 */
+	if (rv != CRYPTO_QUEUED)
+		dca_rsactxfree(&ctx);
+
+	return (rv);
+}
+
+
+/*
+ * For RSA_PKCS padding and unpadding:
+ * 1. The minimum padding is 11 bytes.
+ * 2. The first and the last bytes must 0.
+ * 3. The second byte is 1 for private and 2 for public keys.
+ * 4. Pad with 0xff for private and non-zero random for public keys.
+ */
+static int
+dca_pkcs1_padding(dca_t *dca, caddr_t buf, int flen, int tlen, int private)
+{
+	int i;
+
+	DBG(NULL, DENTRY,
+	    "dca_pkcs1_padding: tlen: %d, flen: %d: private: %d\n",
+	    tlen, flen, private);
+
+	if (flen > tlen - 11)
+		return (CRYPTO_DATA_LEN_RANGE);
+
+	if (private) {
+		/* Padding for private encrypt */
+		buf[flen] = '\0';
+		for (i = flen + 1; i < tlen - 2; i++) {
+			buf[i] = (unsigned char) 0xff;
+		}
+		buf[tlen - 2] = 1;
+		buf[tlen - 1] = 0;
+	} else {
+		/* Padding for public encrypt */
+		buf[flen] = '\0';
+
+		if (dca_random_buffer(dca, &buf[flen+1], tlen - flen - 3) !=
+		    CRYPTO_SUCCESS)
+			return (CRYPTO_RANDOM_NO_RNG);
+
+		buf[tlen - 2] = 2;
+		buf[tlen - 1] = 0;
+	}
+
+	return (CRYPTO_QUEUED);
+}
+
+static int
+dca_pkcs1_unpadding(char *buf, int *tlen, int flen, int mode)
+{
+	int i;
+	const unsigned char *p;
+	unsigned char type;
+
+	DBG(NULL, DENTRY, "dca_pkcs1_unpadding: tlen: %d, flen: %d\n",
+	    *tlen, flen);
+
+	p = (unsigned char *) buf + (flen-1);
+	if (*(p--) != 0)
+		return decrypt_error_code(mode, CRYPTO_ENCRYPTED_DATA_INVALID,
+		    CRYPTO_SIGNATURE_INVALID, CRYPTO_DATA_INVALID);
+
+	/* It is ok if the data length is 0 after removing the padding */
+	type = *(p--);
+	if (type == 01) {
+		for (i = flen - 3; i >= 0; i--) {
+			if (*p != 0xff) {
+				if (*p == '\0') {
+					p--;
+					break;
+				} else {
+					return decrypt_error_code(mode,
+					    CRYPTO_ENCRYPTED_DATA_INVALID,
+					    CRYPTO_SIGNATURE_INVALID,
+					    CRYPTO_DATA_INVALID);
+				}
+			}
+			p--;
+		}
+	} else if (type == 02) {
+		for (i = flen - 3; i >= 0; i--) {
+			if (*p == '\0') {
+				p--;
+				break;
+			}
+			p--;
+		}
+	} else {
+		return decrypt_error_code(mode, CRYPTO_ENCRYPTED_DATA_INVALID,
+		    CRYPTO_SIGNATURE_INVALID, CRYPTO_DATA_INVALID);
+	}
+
+	/* i < 0 means did not find the end of the padding */
+	if (i < 0)
+		return decrypt_error_code(mode, CRYPTO_ENCRYPTED_DATA_INVALID,
+		    CRYPTO_SIGNATURE_INVALID, CRYPTO_DATA_INVALID);
+
+	if (i > *tlen) {
+		*tlen = i;
+		return (CRYPTO_BUFFER_TOO_SMALL);
+	}
+
+	if (flen - i < 11)
+		return decrypt_error_code(mode,
+		    CRYPTO_ENCRYPTED_DATA_LEN_RANGE,
+		    CRYPTO_SIGNATURE_LEN_RANGE, CRYPTO_DATA_LEN_RANGE);
+
+	/* Return the unpadded length to the caller */
+	*tlen = i;
+
+	return (CRYPTO_SUCCESS);
+}
+
+/*
+ * For RSA_X_509 padding and unpadding, pad all 0s before actual data.
+ * Note that the data will be in reverse order.
+ */
+static int
+dca_x509_padding(caddr_t buf, int flen, int tlen)
+{
+	DBG(NULL, DENTRY, "dca_x509_padding: tlen: %d, flen: %d\n",
+	    tlen, flen);
+
+	bzero(buf+tlen, tlen - flen);
+
+	return (CRYPTO_QUEUED);
+}
+
+/* ARGSUSED */
+static int
+dca_x509_unpadding(char *buf, int tlen, int flen, int mode)
+{
+	int i;
+	const unsigned char *p;
+
+	DBG(NULL, DENTRY, "dca_x509_unpadding: tlen: %d, flen: %d\n",
+	    tlen, flen);
+
+	p = (unsigned char *) buf + flen;
+	for (i = tlen; i < flen; i++) {
+		if (*(--p) != 0)
+			return (CRYPTO_SIGNATURE_INVALID);
+	}
+
+	return (CRYPTO_SUCCESS);
+}
+
+static int decrypt_error_code(int mode, int decrypt, int verify, int def)
+{
+	switch (mode) {
+	case DCA_RSA_DEC:
+		return (decrypt);
+	case DCA_RSA_VRFY:
+	case DCA_RSA_VRFYR:
+		return (verify);
+	default:
+		return (def);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/crypto/dca.h	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,928 @@
+/*
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_CRYPTO_DCA_H
+#define	_SYS_CRYPTO_DCA_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/varargs.h>
+
+#include <sys/crypto/spi.h>
+
+/*
+ * Deimos - cryptographic acceleration based upon Broadcom 582x.
+ *
+ * Note: Everything in this file is private to the Deimos device
+ *	 driver!  Do not include this in any other file.
+ */
+
+#define	DRIVER			"dca"
+#define	DCA_MANUFACTURER_ID	"SUNWdca"
+
+#ifdef _KERNEL
+
+/*
+ * Tunables.
+ */
+#define	MCR1LOWATER	16	/* these numbers favor overall throughput */
+#define	MCR1HIWATER	24
+#define	MCR1MAXREQS	8
+#define	MCR2LOWATER	16
+#define	MCR2HIWATER	24
+#define	MCR2MAXREQS	4
+#define	MAXMCR		2	/* there are 2 mcrs */
+#define	MAXREQSPERMCR	16	/* there are 4 subunits serviced by MCR2 */
+#define	MAXFRAGS	6	/* Limit on the number of fragments */
+#define	MAXWORK		6	/* How many work structures to preallocate */
+
+/*
+ * These are constants.  Do not change them.
+ */
+#if defined(i386) || defined(__i386) || defined(__amd64)
+#define	MAXPACKET	0xefff	/* rootnex INT_MAX_BUF hack. */
+#else
+#define	MAXPACKET	0xffff	/* Max size of a packet or fragment */
+#endif
+#define	DESBLOCK	8	/* Size of a DES or 3DES block */
+#define	DSAPARTLEN	20	/* Size of fixed DSA parts (r, s, q, x, v) */
+#define	DSASIGLEN	40	/* Size of a DSA signature */
+#define	SHA1LEN		20	/* Size of a SHA1 hash */
+#define	SECOND		1000000	/* One second in usec */
+#define	MSEC		1000	/* One millisecond in usec */
+#define	DES_KEYSIZE	8
+#define	DES_IV_LEN	8
+#define	DES3_KEYSIZE	(3 * DES_KEYSIZE)
+
+/*
+ * Mechanism info structure passed to KCF during registration.
+ */
+
+#define	MD5_HMAC_BLOCK_SIZE	64	/* MD5-HMAC block size */
+#define	MD5_HMAC_MIN_KEY_LEN	1	/* MD5-HMAC min key length in bytes */
+#define	MD5_HMAC_MAX_KEY_LEN	64	/* MD5-HMAC max key length in bytes */
+
+#define	SHA1_HMAC_BLOCK_SIZE	64	/* SHA1-HMAC block size */
+#define	SHA1_HMAC_MIN_KEY_LEN	1	/* SHA1-HMAC min key length in bytes */
+#define	SHA1_HMAC_MAX_KEY_LEN	64	/* SHA1-HMAC max key length in bytes */
+
+#define	DES_KEY_LEN		8	/* DES key length in bytes */
+#define	DES3_KEY_LEN		24	/* 3DES key length in bytes */
+
+#define	DSA_MIN_KEY_LEN		64	/* DSA min key length in bytes */
+#define	DSA_MAX_KEY_LEN		128	/* DSA max key length in bytes */
+
+#define	RSA_MIN_KEY_LEN		32	/* RSA min key length in bytes */
+#define	RSA_MAX_KEY_LEN		256	/* RSA max key length in bytes */
+
+/*
+ * RSA implementation.
+ */
+
+#define	DCA_RSA_ENC	0
+#define	DCA_RSA_DEC	1
+#define	DCA_RSA_SIGN	2
+#define	DCA_RSA_VRFY	3
+#define	DCA_RSA_SIGNR	4
+#define	DCA_RSA_VRFYR	5
+
+/*
+ * DSA implementation.
+ */
+
+#define	DCA_DSA_SIGN	0
+#define	DCA_DSA_VRFY	1
+
+/*
+ * FMA eclass index definitions. Note that this enum must be consistent
+ * with the dca_fma_eclass_sca1000 and dca_fma_eclass_sca500 string arrays.
+ */
+typedef enum dca_fma_eclass {
+	DCA_FM_ECLASS_HW_DEVICE = 0,
+	DCA_FM_ECLASS_HW_TIMEOUT,
+	DCA_FM_ECLASS_NONE
+} dca_fma_eclass_t;
+
+/*
+ * Forward typedefs.
+ */
+typedef struct dca dca_t;
+typedef struct dca_chain dca_chain_t;
+typedef struct dca_listnode dca_listnode_t;
+typedef struct dca_worklist dca_worklist_t;
+typedef struct dca_work dca_work_t;
+typedef struct dca_request dca_request_t;
+typedef struct dca_stat dca_stat_t;
+typedef struct dca_cookie dca_cookie_t;
+typedef struct dca_device dca_device_t;
+
+/*
+ * This structure is used to identify a specific board.
+ */
+struct dca_device {
+	ushort_t		dd_vendor_id;
+	ushort_t		dd_device_id;
+	char			*dd_model;
+};
+
+/*
+ * Structure representing a node in a DMA chain.  (Broadcom calls
+ * these "Data Buffer Chain Entries".)
+ *
+ * note, this structure must be a multiple of sizeof (intptr_t)
+ */
+struct dca_chain {
+	/* the descriptor */
+	caddr_t			dc_desc_kaddr;
+	/* and the buffer to which it points */
+	size_t			dc_buffer_length;
+	ddi_dma_handle_t	dc_buffer_dmah;
+	caddr_t			dc_buffer_kaddr;
+	/* physical addresses */
+	uint32_t		dc_desc_paddr;
+	uint32_t		dc_buffer_paddr;
+	uint32_t		dc_next_paddr;
+};
+
+/*
+ * Linked-list linkage.
+ */
+struct dca_listnode {
+	dca_listnode_t		*dl_next;
+	dca_listnode_t		*dl_prev;
+	dca_listnode_t		*dl_next2;
+	dca_listnode_t		*dl_prev2;
+};
+
+typedef enum dca_mech_type {
+	DES_CBC_MECH_INFO_TYPE,		/* SUN_CKM_DES_CBC */
+	DES3_CBC_MECH_INFO_TYPE,	/* SUN_CKM_DES3_CBC */
+	DSA_MECH_INFO_TYPE,		/* SUN_CKM_DSA */
+	RSA_X_509_MECH_INFO_TYPE,	/* SUN_CKM_RSA_X_509 */
+	RSA_PKCS_MECH_INFO_TYPE		/* SUN_CKM_RSA_PKCS */
+} dca_mech_type_t;
+
+#define	SUN_CKM_DSA			"CKM_DSA"
+
+struct dca_rng {
+	uint32_t		dr_chunklen;
+};
+
+union dca_parameters {
+	struct dca_rng		dp_rng;
+};
+
+typedef struct dca_ctx {
+	/*
+	 * The following are context fields for Deimos 2.0.
+	 */
+	crypto_mech_type_t	ctx_cm_type;	/* Mechanism type */
+	int			mode;		/* Mode of operation */
+	int 			atomic;		/* Boolean */
+
+	/* Fields for RSA and DSA */
+	uchar_t			*mod;		/* RSA modulus */
+	unsigned		modlen;		/* RSA modulus length */
+	unsigned		pqfix;		/* RSA flag */
+
+	/* Fields for DES and 3DES */
+	uint32_t		iv[2];
+	uint32_t		key[6];
+	int			residlen;
+	uchar_t			resid[DESBLOCK];
+	int			activeresidlen;
+	uchar_t			activeresid[DESBLOCK];
+	crypto_data_t		in_dup;		/* input data duplicate */
+} dca_ctx_t;
+
+/*
+ * Work structure.  One of these per actual job submitted to an MCR.
+ * Contains everything we need to submit the job, and everything we
+ * need to notify caller and release resources when the completion
+ * interrupt comes.
+ */
+struct dca_request {
+	dca_listnode_t		dr_linkage;
+	uint16_t		dr_pkt_length;
+	crypto_req_handle_t	dr_kcf_req;
+	dca_t			*dr_dca;
+	dca_worklist_t		*dr_wlp;
+	/*
+	 * Consumer's I/O buffers.
+	 */
+	crypto_data_t		*dr_in;
+	crypto_data_t		*dr_out;
+	dca_ctx_t		dr_ctx;
+	/*
+	 * Chains and DMA structures.
+	 */
+	size_t			dr_dma_size;
+	uint32_t		dr_ctx_paddr;
+	caddr_t			dr_ctx_kaddr;
+	ddi_acc_handle_t	dr_ctx_acch;
+	ddi_dma_handle_t	dr_ctx_dmah;
+	/*
+	 * Scratch input buffer.
+	 */
+	ddi_acc_handle_t	dr_ibuf_acch;
+	ddi_dma_handle_t	dr_ibuf_dmah;
+	caddr_t			dr_ibuf_kaddr;
+	uint32_t		dr_ibuf_paddr;
+
+	/*
+	 * Scratch output buffer.
+	 */
+	ddi_acc_handle_t	dr_obuf_acch;
+	ddi_dma_handle_t	dr_obuf_dmah;
+	caddr_t			dr_obuf_kaddr;
+	uint32_t		dr_obuf_paddr;
+
+	/*
+	 * Values to program MCR with.
+	 */
+	uint32_t		dr_in_paddr;
+	uint32_t		dr_out_paddr;
+	uint32_t		dr_in_next;
+	uint32_t		dr_out_next;
+	uint16_t		dr_in_len;
+	uint16_t		dr_out_len;
+	/*
+	 * Callback.
+	 */
+	void			(*dr_callback)(dca_request_t *, int);
+	/*
+	 * Other stuff.
+	 */
+	uint32_t		dr_flags;
+	/*
+	 * Algorithm specific parameters.
+	 */
+	void			*dr_context;
+	union dca_parameters	dr_param;
+	/*
+	 * Statistics.
+	 */
+	int			dr_job_stat;
+	int			dr_byte_stat;
+
+	/* Pre-mapped input and output data buffer chain support */
+	dca_chain_t		dr_ibuf_head;
+	dca_chain_t		dr_obuf_head;
+
+	/*
+	 * User buffers are mapped to DMA handles dynamically. The physically
+	 * contigous blocks ( >= a page) are built into a data buffer chain.
+	 */
+	dca_chain_t		dr_chain_in_head;
+	ddi_dma_handle_t	dr_chain_in_dmah;
+
+	dca_chain_t		dr_chain_out_head;
+	ddi_dma_handle_t	dr_chain_out_dmah;
+
+	/* Offset in the context page for storing dynamic buffer chains */
+	int			dr_offset;
+
+	/* Destroy this request if true */
+	int			destroy;
+};
+
+/*
+ * Request flags (dca_request_t.dr_flags).
+ */
+#define	DR_INPLACE		0x002
+#define	DR_SCATTER		0x004
+#define	DR_GATHER		0x008
+#define	DR_NOCACHE		0x020
+#define	DR_ENCRYPT		0x040
+#define	DR_DECRYPT		0x080
+#define	DR_TRIPLE		0x100	/* triple DES vs. single DES */
+#define	DR_ATOMIC		0x200	/* for atomic operation */
+
+struct dca_work {
+	dca_listnode_t		dw_linkage;
+	dca_worklist_t		*dw_wlp;
+
+	/* DMA access to the MCR and context */
+	ddi_acc_handle_t	dw_mcr_acch;
+	ddi_dma_handle_t	dw_mcr_dmah;
+	caddr_t			dw_mcr_kaddr;
+	uint32_t		dw_mcr_paddr;
+
+	dca_request_t		*dw_reqs[MAXREQSPERMCR];
+	clock_t			dw_lbolt;
+};
+
+/*
+ * MCRs.
+ */
+#define	MCR1			0x1
+#define	MCR2			0x2
+
+struct dca_worklist {
+	dca_t			*dwl_dca;
+	crypto_kcf_provider_handle_t	dwl_prov;
+	char			dwl_name[16];
+	int			dwl_mcr;
+	kmutex_t		dwl_lock;
+	kmutex_t		dwl_freereqslock;
+	kcondvar_t		dwl_cv;
+	dca_listnode_t		dwl_freereqs;	/* available requests */
+	dca_listnode_t		dwl_waitq;	/* requests arrive here */
+	dca_listnode_t		dwl_freework;	/* available work structures */
+	dca_listnode_t		dwl_runq;	/* work structs sent to chip */
+	timeout_id_t		dwl_schedtid;
+	clock_t			dwl_lastsubmit;
+	int			dwl_count;
+	int			dwl_busy;
+	int			dwl_lowater;
+	int			dwl_hiwater;
+	int			dwl_reqspermcr;
+	int			dwl_drain;	/* for DR (suspend) */
+	/* Kstats */
+	u_longlong_t		dwl_submit;
+	u_longlong_t		dwl_flowctl;
+};
+
+/*
+ * Operations for MCR1 (bulk stuff).
+ */
+#define	CMD_IPSEC		0x0	/* IPsec packet processing */
+#define	CMD_SSLMAC		0x1	/* SSL HMAC processing */
+#define	CMD_TLSMAC		0x2	/* TLS HMAC processing */
+#define	CMD_3DES		0x3	/* SSL/TLS/raw 3DES processing */
+#define	CMD_RC4			0x4	/* ARCFOUR procesing */
+#define	CMD_PUREHASH		0x5	/* Pure MD5/SHA1 hash processing */
+
+/*
+ * Operations for MCR2 (key stuff).
+ */
+#define	CMD_DHPUBLIC		0x1	/* DH public key generation */
+#define	CMD_DHSHARED		0x2	/* DH shared secret generation */
+#define	CMD_RSAPUBLIC		0x3	/* RSA public key operation */
+#define	CMD_RSAPRIVATE		0x4	/* RSA private key operation (CRT) */
+#define	CMD_DSASIGN		0x5	/* DSA signing operation */
+#define	CMD_DSAVERIFY		0x6	/* DSA verification operation */
+#define	CMD_RNGDIRECT		0x41	/* Direct access to the RNG */
+#define	CMD_RNGSHA1		0x42	/* RNG output processed by SHA1 */
+#define	CMD_MODADD		0x43	/* Modular add */
+#define	CMD_MODSUB		0x44	/* Moduler subtract */
+#define	CMD_MODMUL		0x45	/* Modular multiply */
+#define	CMD_MODREM		0x46	/* Modular remainder */
+#define	CMD_MODEXP		0x47	/* Modular exponentiation */
+#define	CMD_MODINV		0x48	/* Modular inverse */
+
+/*
+ * Kstats.
+ */
+#define	DS_3DESJOBS		0
+#define	DS_3DESBYTES		1
+#define	DS_RSAPUBLIC		2
+#define	DS_RSAPRIVATE		3
+#define	DS_DSASIGN		4
+#define	DS_DSAVERIFY		5
+#define	DS_RNGJOBS		6
+#define	DS_RNGBYTES		7
+#define	DS_RNGSHA1JOBS		8
+#define	DS_RNGSHA1BYTES		9
+#define	DS_MAX			10
+
+#if 0
+/*
+ * note that when reenabling any of these stats, DS_MAX will need to
+ * be adjusted.
+ */
+#define	DS_RC4JOBS		11
+#define	DS_RC4BYTES		12
+#define	DS_DHPUBLIC		13
+#define	DS_DHSECRET		14
+#endif
+
+struct dca_stat {
+	kstat_named_t		ds_status;
+	kstat_named_t		ds_algs[DS_MAX];
+	struct {
+		kstat_named_t	ds_submit;
+		kstat_named_t	ds_flowctl;
+		kstat_named_t	ds_lowater;
+		kstat_named_t	ds_hiwater;
+		kstat_named_t	ds_maxreqs;
+	}			ds_mcr[MAXMCR];
+};
+
+/*
+ * Blocking structure for ioctls.
+ */
+struct dca_cookie {
+	kmutex_t		dc_mx;
+	kcondvar_t		dc_cv;
+	int			dc_outstanding;
+	int			dc_status;
+};
+
+/*
+ * Per instance structure.
+ */
+struct dca {
+	dev_info_t		*dca_dip;
+	kmutex_t		dca_intrlock;
+	caddr_t			dca_regs;
+	ddi_acc_handle_t	dca_regs_handle;
+	ddi_iblock_cookie_t	dca_icookie;
+	timeout_id_t		dca_jobtid;
+	ulong_t			dca_pagesize;
+	unsigned		dca_flags;	/* dev state flags */
+
+	/*
+	 * Work requests.
+	 */
+	dca_worklist_t		dca_worklist[MAXMCR];
+
+	/*
+	 * hardware model
+	 */
+	char			*dca_model;
+
+	/*
+	 * Kstats.  There is no standard for what standards
+	 * Cryptographic Providers should supply, so we're
+	 * making them up for now.
+	 */
+	kstat_t			*dca_ksp;
+	kstat_t			*dca_intrstats;
+	u_longlong_t		dca_stats[DS_MAX];
+
+	/* For the local random number pool used internally by the dca driver */
+	char 			*dca_buf1;
+	char 			*dca_buf2;
+	char 			*dca_buf_ptr;
+	int 			dca_index;
+	uint32_t 		dca_random_filling;
+	kmutex_t 		dca_random_lock;
+
+	/* FMA capabilities */
+	int			fm_capabilities;	/* FMA capabilities */
+
+	kmutex_t		dca_ctx_list_lock;
+	dca_listnode_t		dca_ctx_list;
+};
+
+/*
+ * Device flags (dca_t.dca_flags)
+ */
+#define	DCA_FAILED		0x1
+#define	DCA_POWERMGMT		0x4
+#define	DCA_RNGSHA1		0x8
+
+#define	KIOIP(dca)		KSTAT_INTR_PTR((dca)->dca_intrstats)
+
+/*
+ * Scatter/gather checks.
+ */
+typedef enum dca_sg_param {
+	DCA_SG_CONTIG = 1,
+	DCA_SG_WALIGN,
+	DCA_SG_PALIGN
+} dca_sg_param_t;
+
+#define	FALSE		0
+#define	TRUE		1
+
+/*
+ * PCI configuration registers.
+ */
+#define	PCI_VENID		0x00	/* vendor id, 16 bits */
+#define	PCI_DEVID		0x02	/* device id, 16 bits */
+#define	PCI_COMM		0x04	/* command, 16 bits */
+#define	PCI_STATUS		0x06	/* status, 16 bits */
+#define	PCI_REVID		0x08	/* revision id, 8 bits */
+#define	PCI_PROGCLASS		0x09	/* programming class, 8 bits */
+#define	PCI_SUBCLASS		0x0A	/* subclass, 8 bits */
+#define	PCI_CACHELINESZ		0x0C	/* cache line size, 8 bits */
+#define	PCI_LATTMR		0x0D	/* latency timer, 8 bits */
+#define	PCI_BIST		0x0F	/* builtin-self-test, 8 bits */
+#define	PCI_SUBVENID		0x2C	/* subsystem vendor id, 16 bits */
+#define	PCI_SUBSYSID		0x2E	/* subsystem id, 16 bits */
+#define	PCI_MINGNT		0x3E	/* min grant for burst, 8 bits */
+#define	PCI_MAXLAT		0x3F	/* maximum grant for burst, 8 bits */
+#define	PCI_TRDYTO		0x40	/* TRDY timeout, 8 bits */
+#define	PCI_RETRIES		0x41	/* retries bus will perform, 8 bits */
+
+/*
+ * PCI configuration register bit values.
+ */
+#define	PCICOMM_FBBE		0x0200	/* fast back-to-back enable */
+#define	PCICOMM_SEE		0x0100	/* system error enable */
+#define	PCICOMM_PEE		0x0040	/* parity error enable */
+#define	PCICOMM_MWIE		0x0010	/* memory write & invalidate enable */
+#define	PCICOMM_BME		0x0004	/* bus master enable */
+#define	PCICOMM_MAE		0x0002	/* memory access enable */
+
+#define	PCISTAT_PERR		0x8000	/* parity error detected */
+#define	PCISTAT_SERR		0x4000	/* system error detected */
+#define	PCISTAT_MABRT		0x2000	/* master abort detected */
+#define	PCISTAT_TABRT		0x1000	/* target abort detected */
+#define	PCISTAT_TABRTS		0x0800	/* target abort signaled */
+#define	PCISTAT_PARITY		0x0100	/* data parity error detected */
+
+#define	PCIREVID_DOMESTIC	0x01	/* domestic version */
+#define	PCIREVID_EXPORT		0xE1	/* export version */
+
+/* Note: 5820 errata: BIST feature does not work */
+#define	PCIBIST_CAP		0x80	/* BIST capable */
+#define	PCIBIST_START		0x40	/* start BIST test */
+#define	PCIBIST_ERRMASK		0x0F	/* mask of BIST error codes */
+
+/*
+ * Command and Status Registers.
+ */
+#define	CSR_MCR1		0x00	/* pointer to MCR1 (bulk) */
+#define	CSR_DMACTL		0x04	/* DMA control */
+#define	CSR_DMASTAT		0x08	/* DMA status */
+#define	CSR_DMAEA		0x0C	/* DMA error address */
+#define	CSR_MCR2		0x10	/* pointer to MCR2 (exponentiator) */
+
+/*
+ * Command and status register bits.
+ */
+#define	DMACTL_RESET		0x80000000U	/* reset the chip */
+#define	DMACTL_MCR2IE		0x40000000U	/* MCR2 interrupt enable */
+#define	DMACTL_MCR1IE		0x20000000U	/* MCR1 interrupt enable */
+#define	DMACTL_OFM		0x10000000U	/* output fragment mode */
+#define	DMACTL_BE32		0x08000000U	/* 32-bit big endian mode */
+#define	DMACTL_BE64		0x04000000U	/* 64-bit big endian mode */
+#define	DMACTL_EIE		0x02000000U	/* error interrupt enable */
+#define	DMACTL_RNGMASK		0x01800000U	/* RNG mode mask */
+#define	DMACTL_RNG1		0x00000000U	/* 1 RNG bit per cycle */
+#define	DMACTL_RNG4		0x00800000U	/* 1 RNG bit per 4 cycles */
+#define	DMACTL_RNG8		0x01000000U	/* 1 RNG bit per 8 cycles */
+#define	DMACTL_RNG16		0x01800000U	/* 1 RNG bit per 16 cycles */
+#define	DMACTL_MODNORM		0x00400000U	/* s/w modulus normalization */
+#define	DMACTL_FRAGMASK		0x0000FFFFU	/* output fragment size */
+
+#define	DMASTAT_MAIP		0x80000000U	/* master access in progress */
+#define	DMASTAT_MCR1FULL	0x40000000U	/* MCR1 is full */
+#define	DMASTAT_MCR1INT		0x20000000U	/* MCR1 interrupted */
+#define	DMASTAT_ERRINT		0x10000000U	/* error interrupted */
+#define	DMASTAT_MCR2FULL	0x08000000U	/* MCR2 is full */
+#define	DMASTAT_MCR2INT		0x04000000U	/* MCR2 interrupted */
+#define	DMASTAT_INTERRUPTS	0x34000000U	/* all interrupts */
+
+/*
+ * Offsets of things relative to an MCR.
+ */
+#define	MCR_COUNT	0	/* 16 bits */
+#define	MCR_FLAGS	2	/* 16 bits */
+#define	MCR_CTXADDR	4	/* 32 bits */
+
+/*
+ * Basis for size (should be optimized by constant folding):
+ *	4 bytes for flags and #packets.
+ *	for each packet:
+ *		2 descriptors (DESC_SIZE)
+ *		4 bytes for context address
+ *		4 bytes for packet length and reserved
+ */
+#define	MCR_SIZE	(4 + MAXREQSPERMCR * ((2 * DESC_SIZE) + 8))
+
+/*
+ * MCR flags.
+ */
+#define	MCRFLAG_FINISHED	0x0001		/* MCR processing complete */
+#define	MCRFLAG_ERROR		0x0002		/* set if an error occured */
+#define	MCRFLAG_ERRORMASK	0xff00		/* error code bits */
+
+/*
+ * Fields within a descriptor (data buffer chain).
+ */
+#define	DESC_BUFADDR	0	/* 32 bits */
+#define	DESC_NEXT	4	/* 32 bits */
+#define	DESC_LENGTH	8	/* 16 bits */
+#define	DESC_RSVD	10	/* 16 bits */
+#define	DESC_SIZE	16	/* ROUNDUP(12, 16) - descriptor size (bytes) */
+
+/*
+ * Offsets of fields within context structures, see Broadcom spec.
+ */
+#define	CTX_LENGTH		0	/* 16 bits */
+#define	CTX_CMD			2	/* 16 bits */
+#define	CTX_MAXLENGTH		768	/* max size of ctx, fits anything */
+
+#define	CTX_3DESDIRECTION	4	/* 16 bits */
+#define	CTX_3DESKEY1HI		8	/* 32 bits */
+#define	CTX_3DESKEY1LO		12	/* 32 bits */
+#define	CTX_3DESKEY2HI		16	/* 32 bits */
+#define	CTX_3DESKEY2LO		20	/* 32 bits */
+#define	CTX_3DESKEY3HI		24	/* 32 bits */
+#define	CTX_3DESKEY3LO		28	/* 32 bits */
+#define	CTX_3DESIVHI		32	/* 32 bits */
+#define	CTX_3DESIVLO		36	/* 32 bits */
+
+#define	CTX_IPSECFLAGS		4	/* 16 bits */
+#define	CTX_IPSECOFFSET		6	/* 16 bits */
+#define	CTX_IPSECKEY1HI		8	/* 32 bits */
+#define	CTX_IPSECKEY1LO		12	/* 32 bits */
+#define	CTX_IPSECKEY2HI		16	/* 32 bits */
+#define	CTX_IPSECKEY2LO		20	/* 32 bits */
+#define	CTX_IPSECKEY3HI		24	/* 32 bits */
+#define	CTX_IPSECKEY3LO		28	/* 32 bits */
+#define	CTX_IPSECIVHI		32	/* 32 bits */
+#define	CTX_IPSECIVLO		36	/* 32 bits */
+#define	CTX_IPSECHMACINNER1	40	/* 32 bits */
+#define	CTX_IPSECHMACINNER2	44	/* 32 bits */
+#define	CTX_IPSECHMACINNER3	48	/* 32 bits */
+#define	CTX_IPSECHMACINNER4	52	/* 32 bits */
+#define	CTX_IPSECHMACINNER5	56	/* 32 bits */
+#define	CTX_IPSECHMACOUTER1	60	/* 32 bits */
+#define	CTX_IPSECHMACOUTER2	64	/* 32 bits */
+#define	CTX_IPSECHMACOUTER3	68	/* 32 bits */
+#define	CTX_IPSECHMACOUTER4	72	/* 32 bits */
+#define	CTX_IPSECHMACOUTER5	76	/* 32 bits */
+
+#define	CTX_RSAEXPLEN		4	/* 16 bits */
+#define	CTX_RSAMODLEN		6	/* 16 bits */
+#define	CTX_RSABIGNUMS		8	/* variable length */
+#define	CTX_RSAQLEN		4	/* 16 bits */
+#define	CTX_RSAPLEN		6	/* 16 bits */
+
+#define	CTX_DSAMSGTYPE		4	/* 16 bits */
+#define	CTX_DSARSVD		6	/* 16 bits */
+#define	CTX_DSARNG		8	/* 16 bits */
+#define	CTX_DSAPLEN		10	/* 16 bits */
+#define	CTX_DSABIGNUMS		12	/* variable length */
+
+/*
+ * Values for specific operations.
+ */
+#define	CTX_RNG_LENGTH		64	/* context length for RNG (64 min) */
+#define	CTX_3DES_LENGTH		64	/* context length for 3DES (64 min) */
+#define	CTX_3DES_DECRYPT	0x4000	/* perform decryption */
+#define	CTX_3DES_ENCRYPT	0x0000	/* perform encryption */
+#define	CTX_IPSEC_LENGTH	80	/* context length for IPsec */
+#define	CTX_IPSEC_ENCRYPT	0x8000	/* perform encryption */
+#define	CTX_IPSEC_DECRYPT	0xc000	/* perform decryption */
+#define	CTX_IPSEC_HMAC_MD5	0x1000	/* HMAC-MD5 authentication */
+#define	CTX_IPSEC_HMAC_SHA1	0x2000	/* HMAC-MD5 authentication */
+#define	CTX_DSAMSGTYPE_SHA1	0	/* Message is SHA1 */
+#define	CTX_DSAMSGTYPE_TEXT	1	/* Generate SHA1 hash first */
+#define	CTX_DSARNG_GEN		1	/* Generate random k */
+#define	CTX_DSARNG_SUPPLY	0	/* Random k is supplied */
+
+/*
+ * Macros to access fields within the MCR.  Note that this includes the
+ * context fields as well, since the context is just offset from the
+ * base of the MCR.
+ */
+
+#define	PUTMCR32(work, reg, val)	\
+	ddi_put32(work->dw_mcr_acch,	\
+	(uint32_t *)(work->dw_mcr_kaddr + reg), val)
+
+#define	PUTMCR16(work, reg, val)	\
+	ddi_put16(work->dw_mcr_acch,	\
+	(uint16_t *)(work->dw_mcr_kaddr + reg), val)
+
+#define	GETMCR32(work, reg)	\
+	ddi_get32(work->dw_mcr_acch, (uint32_t *)(work->dw_mcr_kaddr + reg))
+
+#define	GETMCR16(work, reg)	\
+	ddi_get16(work->dw_mcr_acch, (uint16_t *)(work->dw_mcr_kaddr + reg))
+
+#define	PUTDESC32(req, dc_desc_kaddr, reg, val)	\
+	ddi_put32(req->dr_ctx_acch,	\
+	(uint32_t *)(dc_desc_kaddr + reg), val)
+
+#define	PUTDESC16(req, dc_desc_kaddr, reg, val)	\
+	ddi_put16(req->dr_ctx_acch,	\
+	(uint16_t *)(dc_desc_kaddr + reg), val)
+
+/* XXX: define the GET forms for descriptors only if needed */
+
+#define	PUTCTX32(req, reg, val)	\
+	ddi_put32(req->dr_ctx_acch,	\
+	(uint32_t *)(req->dr_ctx_kaddr + reg), val)
+
+#define	PUTCTX16(req, reg, val)	\
+	ddi_put16(req->dr_ctx_acch,	\
+	(uint16_t *)(req->dr_ctx_kaddr + reg), val)
+
+#define	CTXBCOPY(req, src, dst, count)	\
+	ddi_rep_put8(req->dr_ctx_acch, (uchar_t *)src, (uchar_t *)dst, count, \
+	DDI_DEV_AUTOINCR)
+
+/*
+ * Register access.
+ */
+#define	GETCSR(dca, reg)	\
+	ddi_get32(dca->dca_regs_handle, (uint_t *)(dca->dca_regs + reg))
+
+#define	PUTCSR(dca, reg, val)	\
+	ddi_put32(dca->dca_regs_handle, (uint_t *)(dca->dca_regs + reg), val)
+
+#define	SETBIT(dca, reg, val)	\
+	PUTCSR(dca, reg, GETCSR(dca, reg) | val)
+
+#define	CLRBIT(dca, reg, val)	\
+	PUTCSR(dca, reg, GETCSR(dca, reg) & ~val)
+
+/*
+ * Used to guarantee alignment.
+ */
+#define	ROUNDUP(a, n)	(((a) + ((n) - 1)) & ~((n) - 1))
+#define	ROUNDDOWN(a, n)	(((a) & ~((n) - 1)))
+#define	HIDBLWORD(x)	(((x) & 0xffffffff00000000ULL) >> 32)
+#define	LODBLWORD(x)	((x) & 0xffffffffULL)
+
+/*
+ * Driver hardening related.
+ */
+#define	CHECK_REGS(dca)	ddi_check_acc_handle(dca->dca_regs_handle)
+
+/*
+ * Other utility macros.
+ */
+#define	QEMPTY(q)	((q)->dl_next == (q))
+#define	BITS2BYTES(b)	((b) >> 3)
+#define	WORKLIST(dca, mcr)	(&((dca)->dca_worklist[mcr - 1]))
+
+/*
+ * Debug stuff.
+ */
+#ifdef	DEBUG
+#define	DWARN		0x0001
+#define	DPCI		0x0002
+#define	DINTR		0x0004
+#define	DSTART		0x0008
+#define	DRECLAIM	0x0010
+#define	DCHATTY		0x0020
+#define	DMOD		0x0040	/* _init/_fini/_info/attach/detach */
+#define	DENTRY		0x0080	/* crypto routine entry/exit points */
+
+void	dca_dprintf(dca_t *, int, const char *, ...);
+#define	DBG	dca_dprintf
+#else
+#define	DBG(dca, lvl, ...)
+#endif
+
+/*
+ * Some pkcs#11 defines as there are no pkcs#11 header files included.
+ */
+#define	CKO_PUBLIC_KEY		0x00000002
+#define	CKO_PRIVATE_KEY		0x00000003
+
+#define	CKA_CLASS		0x00000000
+#define	CKA_VALUE		0x00000011
+#define	CKA_KEY_TYPE		0x00000100
+#define	CKA_MODULUS		0x00000120
+#define	CKA_PUBLIC_EXPONENT	0x00000122
+#define	CKA_PRIVATE_EXPONENT	0x00000123
+#define	CKA_PRIME_1		0x00000124
+#define	CKA_PRIME_2		0x00000125
+#define	CKA_EXPONENT_1		0x00000126
+#define	CKA_EXPONENT_2		0x00000127
+#define	CKA_COEFFICIENT		0x00000128
+#define	CKA_PRIME		0x00000130
+#define	CKA_SUBPRIME		0x00000131
+#define	CKA_BASE		0x00000132
+/*
+ * Driver globals.
+ */
+extern int	dca_mindma;
+extern int	dca_hardening;
+
+/*
+ * Prototypes.
+ */
+
+/*
+ * dca_debug.c
+ */
+void	dca_error(dca_t *, const char *, ...);
+void	dca_diperror(dev_info_t *, const char *, ...);
+void	dca_dipverror(dev_info_t *, const char *, va_list);
+/*
+ * dca_3des.c
+ */
+int	dca_3desctxinit(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *,
+    int, int);
+int	dca_3des(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
+    crypto_req_handle_t, int);
+int	dca_3desupdate(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
+    crypto_req_handle_t, int);
+int	dca_3desfinal(crypto_ctx_t *, crypto_data_t *, int);
+int	dca_3desatomic(crypto_provider_handle_t, crypto_session_id_t,
+    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
+    int, crypto_req_handle_t, int);
+void	dca_3desctxfree(void *);
+
+/*
+ * dca_rsa.c
+ */
+int	dca_rsastart(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
+    crypto_req_handle_t, int);
+int	dca_rsainit(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *, int);
+void	dca_rsactxfree(void *);
+int	dca_rsaatomic(crypto_provider_handle_t, crypto_session_id_t,
+    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
+    int, crypto_req_handle_t, int);
+
+/*
+ * dca_dsa.c
+ */
+int	dca_dsa_sign(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
+    crypto_req_handle_t);
+int	dca_dsa_verify(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
+    crypto_req_handle_t);
+int	dca_dsainit(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *, int,
+    int);
+void	dca_dsactxfree(void *);
+int	dca_dsaatomic(crypto_provider_handle_t, crypto_session_id_t,
+    crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
+    int, crypto_req_handle_t, int);
+
+/*
+ * dca_rng.c
+ */
+int	dca_rng(dca_t *, uchar_t *, size_t len, crypto_req_handle_t);
+int	dca_random_buffer(dca_t *dca, caddr_t buf, int len);
+int	dca_random_init();
+void	dca_random_fini();
+
+/*
+ * dca_kstat.c
+ */
+void	dca_ksinit(dca_t *);
+/*
+ * dca.c
+ */
+void	dca_rmqueue(dca_listnode_t *);
+dca_request_t *dca_getreq(dca_t *, int, int);
+void	dca_freereq(dca_request_t *);
+int	dca_bindchains(dca_request_t *, size_t, size_t);
+int	dca_unbindchains(dca_request_t *);
+int	dca_start(dca_t *, dca_request_t *, int, int);
+void	dca_done(dca_request_t *, int);
+void	dca_destroyreq(dca_request_t *);
+int	dca_length(crypto_data_t *);
+int	dca_gather(crypto_data_t *, char *, int, int);
+int	dca_resid_gather(crypto_data_t *, char *, int *, char *, int);
+int	dca_scatter(const char *, crypto_data_t *, int, int);
+int	dca_bcmp_reverse(const void *s1, const void *s2, size_t n);
+int	dca_dupcrypto(crypto_data_t *, crypto_data_t *);
+int	dca_verifyio(crypto_data_t *, crypto_data_t *);
+int	dca_getbufbytes(crypto_data_t *, size_t, int, uchar_t *);
+int	dca_sgcheck(dca_t *, crypto_data_t *, dca_sg_param_t);
+crypto_object_attribute_t *
+	dca_get_key_attr(crypto_key_t *);
+int	dca_attr_lookup_uint32(crypto_object_attribute_t *, uint_t, uint64_t,
+	    uint32_t *);
+int	dca_attr_lookup_uint8_array(crypto_object_attribute_t *, uint_t,
+	    uint64_t, void **, unsigned int *);
+crypto_object_attribute_t *
+	dca_find_attribute(crypto_object_attribute_t *, uint_t, uint64_t);
+caddr_t	dca_bufdaddr(crypto_data_t *);
+void	dca_rcoalesce(dca_request_t *, int);
+void	dca_runcoalesce(dca_request_t *);
+int	dca_bitlen(unsigned char *, int);
+uint16_t dca_padhalf(int);
+uint16_t dca_padfull(int);
+void	dca_reverse(void *, void *, int, int);
+int	dca_numcmp(caddr_t, int, caddr_t, int);
+int dca_check_dma_handle(dca_t *dca, ddi_dma_handle_t handle,
+	dca_fma_eclass_t eclass_index);
+int dca_free_context(crypto_ctx_t *ctx);
+
+#endif	/* _KERNEL */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_CRYPTO_DCA_H */
--- a/usr/src/uts/intel/Makefile	Mon Nov 14 09:42:30 2005 -0800
+++ b/usr/src/uts/intel/Makefile	Mon Nov 14 11:14:44 2005 -0800
@@ -51,6 +51,11 @@
 DRV_KMODS	+= dprov
 
 #
+# dca is delivered in the SUNWdcar package.
+#
+DRV_KMODS	+= dca
+
+#
 # CRYPTO_EK_KMODS modules go in the encryption pack (SUNWcry*)
 # They need to be listed separately since they duplicate global symbols
 # causing the 2nd pass of lint on the kernel to complain. CRYPTO_EK_KMODS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/dca/Makefile	Mon Nov 14 11:14:44 2005 -0800
@@ -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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of the DCA kCF provider.
+#
+#	intel implementation architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= dca	
+OBJECTS		= $(DCA_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(DCA_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/crypto/io
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#	set signing mode
+ELFSIGN_MOD	= $(ELFSIGN_CRYPTO)
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY) $(SRC_CONFFILE)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
+
+# C99 mode is needed for dca
+CFLAGS += $(C99_ENABLE) 
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+$(ROOTLINK):	$(ROOT_CRYPTO_DIR) $(ROOTMODULE)
+	-$(RM) $@; ln $(ROOTMODULE) $@
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- a/usr/src/uts/sparc/Makefile	Mon Nov 14 09:42:30 2005 -0800
+++ b/usr/src/uts/sparc/Makefile	Mon Nov 14 11:14:44 2005 -0800
@@ -44,6 +44,7 @@
 LINT_XMODLIBS	= $(XMODS:e1000g=)
 LINT_LIBS	+= $(LINT_XMODLIBS:%=$(LINT_LIB_DIR)/llib-l%.ln)
 DRV_KMODS	+= dprov
+DRV_KMODS	+= dca
 #
 # CRYPTO_EK_KMODS modules go in the encryption pack (SUNWcry*)
 # They need to be listed separately since they duplicate global symbols
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/dca/Makefile	Mon Nov 14 11:14:44 2005 -0800
@@ -0,0 +1,95 @@
+#
+# 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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of the DCA kCF provider.
+#
+#	sparc implementation architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= dca
+OBJECTS		= $(DCA_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(DCA_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/crypto/io
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#	set signing mode
+ELFSIGN_MOD	= $(ELFSIGN_CRYPTO)
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY) $(SRC_CONFFILE)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
+
+#
+# lint pass one enforcement
+#
+CFLAGS += $(CCVERBOSE) 
+CFLAGS += $(C99_ENABLE) 
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+$(ROOTLINK):	$(ROOT_CRYPTO_DIR) $(ROOTMODULE)
+	-$(RM) $@; ln $(ROOTMODULE) $@
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ