xref: /illumos-gate/usr/src/uts/common/crypto/io/dca.c (revision 86ef0a63e1cfa5dc98606efef379365acca98063)
188f8b78aSgm89044 /*
288f8b78aSgm89044  * CDDL HEADER START
388f8b78aSgm89044  *
488f8b78aSgm89044  * The contents of this file are subject to the terms of the
588f8b78aSgm89044  * Common Development and Distribution License (the "License").
688f8b78aSgm89044  * You may not use this file except in compliance with the License.
788f8b78aSgm89044  *
888f8b78aSgm89044  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988f8b78aSgm89044  * or http://www.opensolaris.org/os/licensing.
1088f8b78aSgm89044  * See the License for the specific language governing permissions
1188f8b78aSgm89044  * and limitations under the License.
1288f8b78aSgm89044  *
1388f8b78aSgm89044  * When distributing Covered Code, include this CDDL HEADER in each
1488f8b78aSgm89044  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588f8b78aSgm89044  * If applicable, add the following below this CDDL HEADER, with the
1688f8b78aSgm89044  * fields enclosed by brackets "[]" replaced with your own identifying
1788f8b78aSgm89044  * information: Portions Copyright [yyyy] [name of copyright owner]
1888f8b78aSgm89044  *
1988f8b78aSgm89044  * CDDL HEADER END
2088f8b78aSgm89044  */
2188f8b78aSgm89044 
2288f8b78aSgm89044 /*
2395014fbbSDan OpenSolaris Anderson  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2488f8b78aSgm89044  * Use is subject to license terms.
2588f8b78aSgm89044  */
2688f8b78aSgm89044 
2788f8b78aSgm89044 
2888f8b78aSgm89044 /*
2988f8b78aSgm89044  * Deimos - cryptographic acceleration based upon Broadcom 582x.
3088f8b78aSgm89044  */
3188f8b78aSgm89044 
3288f8b78aSgm89044 #include <sys/types.h>
3388f8b78aSgm89044 #include <sys/modctl.h>
3488f8b78aSgm89044 #include <sys/conf.h>
3588f8b78aSgm89044 #include <sys/devops.h>
3688f8b78aSgm89044 #include <sys/ddi.h>
3788f8b78aSgm89044 #include <sys/sunddi.h>
3888f8b78aSgm89044 #include <sys/cmn_err.h>
3988f8b78aSgm89044 #include <sys/varargs.h>
4088f8b78aSgm89044 #include <sys/file.h>
4188f8b78aSgm89044 #include <sys/stat.h>
4288f8b78aSgm89044 #include <sys/kmem.h>
4388f8b78aSgm89044 #include <sys/ioccom.h>
4488f8b78aSgm89044 #include <sys/open.h>
4588f8b78aSgm89044 #include <sys/cred.h>
4688f8b78aSgm89044 #include <sys/kstat.h>
4788f8b78aSgm89044 #include <sys/strsun.h>
4888f8b78aSgm89044 #include <sys/note.h>
4988f8b78aSgm89044 #include <sys/crypto/common.h>
5088f8b78aSgm89044 #include <sys/crypto/spi.h>
5188f8b78aSgm89044 #include <sys/ddifm.h>
5288f8b78aSgm89044 #include <sys/fm/protocol.h>
5388f8b78aSgm89044 #include <sys/fm/util.h>
5488f8b78aSgm89044 #include <sys/fm/io/ddi.h>
5588f8b78aSgm89044 #include <sys/crypto/dca.h>
5688f8b78aSgm89044 
5788f8b78aSgm89044 /*
5888f8b78aSgm89044  * Core Deimos driver.
5988f8b78aSgm89044  */
6088f8b78aSgm89044 
6188f8b78aSgm89044 static void		dca_enlist2(dca_listnode_t *, dca_listnode_t *,
6288f8b78aSgm89044     kmutex_t *);
6388f8b78aSgm89044 static void		dca_rmlist2(dca_listnode_t *node, kmutex_t *);
6488f8b78aSgm89044 static dca_listnode_t	*dca_delist2(dca_listnode_t *q, kmutex_t *);
6588f8b78aSgm89044 static void		dca_free_context_list(dca_t *dca);
6688f8b78aSgm89044 static int		dca_free_context_low(crypto_ctx_t *ctx);
6788f8b78aSgm89044 static int		dca_attach(dev_info_t *, ddi_attach_cmd_t);
6888f8b78aSgm89044 static int		dca_detach(dev_info_t *, ddi_detach_cmd_t);
6988f8b78aSgm89044 static int		dca_suspend(dca_t *);
7088f8b78aSgm89044 static int		dca_resume(dca_t *);
7188f8b78aSgm89044 static int		dca_init(dca_t *);
7288f8b78aSgm89044 static int		dca_reset(dca_t *, int);
7388f8b78aSgm89044 static int		dca_initworklist(dca_t *, dca_worklist_t *);
7488f8b78aSgm89044 static void		dca_uninit(dca_t *);
7588f8b78aSgm89044 static void		dca_initq(dca_listnode_t *);
7688f8b78aSgm89044 static void		dca_enqueue(dca_listnode_t *, dca_listnode_t *);
7788f8b78aSgm89044 static dca_listnode_t	*dca_dequeue(dca_listnode_t *);
7888f8b78aSgm89044 static dca_listnode_t	*dca_unqueue(dca_listnode_t *);
7988f8b78aSgm89044 static dca_request_t	*dca_newreq(dca_t *);
8088f8b78aSgm89044 static dca_work_t	*dca_getwork(dca_t *, int);
8188f8b78aSgm89044 static void		dca_freework(dca_work_t *);
8288f8b78aSgm89044 static dca_work_t	*dca_newwork(dca_t *);
8388f8b78aSgm89044 static void		dca_destroywork(dca_work_t *);
8488f8b78aSgm89044 static void		dca_schedule(dca_t *, int);
8588f8b78aSgm89044 static void		dca_reclaim(dca_t *, int);
8688f8b78aSgm89044 static uint_t		dca_intr(char *);
8788f8b78aSgm89044 static void		dca_failure(dca_t *, ddi_fault_location_t,
8888f8b78aSgm89044 			    dca_fma_eclass_t index, uint64_t, int, char *, ...);
8988f8b78aSgm89044 static void		dca_jobtimeout(void *);
9088f8b78aSgm89044 static int		dca_drain(dca_t *);
9188f8b78aSgm89044 static void		dca_undrain(dca_t *);
9288f8b78aSgm89044 static void		dca_rejectjobs(dca_t *);
9388f8b78aSgm89044 
9488f8b78aSgm89044 #ifdef	SCHEDDELAY
9588f8b78aSgm89044 static void		dca_schedtimeout(void *);
9688f8b78aSgm89044 #endif
9788f8b78aSgm89044 
9888f8b78aSgm89044 /*
9988f8b78aSgm89044  * We want these inlined for performance.
10088f8b78aSgm89044  */
10188f8b78aSgm89044 #ifndef	DEBUG
10288f8b78aSgm89044 #pragma inline(dca_freereq, dca_getreq, dca_freework, dca_getwork)
10388f8b78aSgm89044 #pragma inline(dca_enqueue, dca_dequeue, dca_rmqueue, dca_done)
10488f8b78aSgm89044 #pragma inline(dca_reverse, dca_length)
10588f8b78aSgm89044 #endif
10688f8b78aSgm89044 
10788f8b78aSgm89044 /*
10888f8b78aSgm89044  * Device operations.
10988f8b78aSgm89044  */
11088f8b78aSgm89044 static struct dev_ops devops = {
11188f8b78aSgm89044 	DEVO_REV,		/* devo_rev */
11288f8b78aSgm89044 	0,			/* devo_refcnt */
11388f8b78aSgm89044 	nodev,			/* devo_getinfo */
11488f8b78aSgm89044 	nulldev,		/* devo_identify */
11588f8b78aSgm89044 	nulldev,		/* devo_probe */
11688f8b78aSgm89044 	dca_attach,		/* devo_attach */
11788f8b78aSgm89044 	dca_detach,		/* devo_detach */
11888f8b78aSgm89044 	nodev,			/* devo_reset */
11988f8b78aSgm89044 	NULL,			/* devo_cb_ops */
12088f8b78aSgm89044 	NULL,			/* devo_bus_ops */
12119397407SSherry Moore 	ddi_power,		/* devo_power */
12219397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
12388f8b78aSgm89044 };
12488f8b78aSgm89044 
12519397407SSherry Moore #define	IDENT		"PCI Crypto Accelerator"
12688f8b78aSgm89044 #define	IDENT_SYM	"Crypto Accel Sym 2.0"
12788f8b78aSgm89044 #define	IDENT_ASYM	"Crypto Accel Asym 2.0"
12888f8b78aSgm89044 
12988f8b78aSgm89044 /* Space-padded, will be filled in dynamically during registration */
13088f8b78aSgm89044 #define	IDENT3	"PCI Crypto Accelerator Mod 2.0"
13188f8b78aSgm89044 
13288f8b78aSgm89044 #define	VENDOR	"Sun Microsystems, Inc."
13388f8b78aSgm89044 
13488f8b78aSgm89044 #define	STALETIME	(30 * SECOND)
13588f8b78aSgm89044 
13688f8b78aSgm89044 #define	crypto_prov_notify	crypto_provider_notification
13788f8b78aSgm89044 		/* A 28 char function name doesn't leave much line space */
13888f8b78aSgm89044 
13988f8b78aSgm89044 /*
14088f8b78aSgm89044  * Module linkage.
14188f8b78aSgm89044  */
14288f8b78aSgm89044 static struct modldrv modldrv = {
14388f8b78aSgm89044 	&mod_driverops,		/* drv_modops */
14488f8b78aSgm89044 	IDENT,			/* drv_linkinfo */
14588f8b78aSgm89044 	&devops,		/* drv_dev_ops */
14688f8b78aSgm89044 };
14788f8b78aSgm89044 
14888f8b78aSgm89044 extern struct mod_ops mod_cryptoops;
14988f8b78aSgm89044 
15088f8b78aSgm89044 static struct modlcrypto modlcrypto = {
15188f8b78aSgm89044 	&mod_cryptoops,
15288f8b78aSgm89044 	IDENT3
15388f8b78aSgm89044 };
15488f8b78aSgm89044 
15588f8b78aSgm89044 static struct modlinkage modlinkage = {
15688f8b78aSgm89044 	MODREV_1,		/* ml_rev */
15788f8b78aSgm89044 	&modldrv,		/* ml_linkage */
15888f8b78aSgm89044 	&modlcrypto,
15988f8b78aSgm89044 	NULL
16088f8b78aSgm89044 };
16188f8b78aSgm89044 
16288f8b78aSgm89044 /*
16388f8b78aSgm89044  * CSPI information (entry points, provider info, etc.)
16488f8b78aSgm89044  */
16588f8b78aSgm89044 
16688f8b78aSgm89044 /* Mechanisms for the symmetric cipher provider */
16788f8b78aSgm89044 static crypto_mech_info_t dca_mech_info_tab1[] = {
16888f8b78aSgm89044 	/* DES-CBC */
16988f8b78aSgm89044 	{SUN_CKM_DES_CBC, DES_CBC_MECH_INFO_TYPE,
17088f8b78aSgm89044 	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT |
17188f8b78aSgm89044 	    CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC,
17288f8b78aSgm89044 	    DES_KEY_LEN, DES_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES},
17388f8b78aSgm89044 	/* 3DES-CBC */
17488f8b78aSgm89044 	{SUN_CKM_DES3_CBC, DES3_CBC_MECH_INFO_TYPE,
17588f8b78aSgm89044 	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT |
17688f8b78aSgm89044 	    CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC,
177436935a1SVladimir Kotal 	    DES3_MIN_KEY_LEN, DES3_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES}
17888f8b78aSgm89044 };
17988f8b78aSgm89044 
18088f8b78aSgm89044 /* Mechanisms for the asymmetric cipher provider */
18188f8b78aSgm89044 static crypto_mech_info_t dca_mech_info_tab2[] = {
18288f8b78aSgm89044 	/* DSA */
18388f8b78aSgm89044 	{SUN_CKM_DSA, DSA_MECH_INFO_TYPE,
18488f8b78aSgm89044 	    CRYPTO_FG_SIGN | CRYPTO_FG_VERIFY |
18588f8b78aSgm89044 	    CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_VERIFY_ATOMIC,
18695014fbbSDan OpenSolaris Anderson 	    CRYPTO_BYTES2BITS(DSA_MIN_KEY_LEN),
18795014fbbSDan OpenSolaris Anderson 	    CRYPTO_BYTES2BITS(DSA_MAX_KEY_LEN),
18888f8b78aSgm89044 	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
18988f8b78aSgm89044 
19088f8b78aSgm89044 	/* RSA */
19188f8b78aSgm89044 	{SUN_CKM_RSA_X_509, RSA_X_509_MECH_INFO_TYPE,
19288f8b78aSgm89044 	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_SIGN |
19388f8b78aSgm89044 	    CRYPTO_FG_SIGN_RECOVER | CRYPTO_FG_VERIFY |
19488f8b78aSgm89044 	    CRYPTO_FG_VERIFY_RECOVER |
19588f8b78aSgm89044 	    CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC |
19688f8b78aSgm89044 	    CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_SIGN_RECOVER_ATOMIC |
19788f8b78aSgm89044 	    CRYPTO_FG_VERIFY_ATOMIC | CRYPTO_FG_VERIFY_RECOVER_ATOMIC,
19895014fbbSDan OpenSolaris Anderson 	    CRYPTO_BYTES2BITS(RSA_MIN_KEY_LEN),
19995014fbbSDan OpenSolaris Anderson 	    CRYPTO_BYTES2BITS(RSA_MAX_KEY_LEN),
20088f8b78aSgm89044 	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
20188f8b78aSgm89044 	{SUN_CKM_RSA_PKCS, RSA_PKCS_MECH_INFO_TYPE,
20288f8b78aSgm89044 	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_SIGN |
20388f8b78aSgm89044 	    CRYPTO_FG_SIGN_RECOVER | CRYPTO_FG_VERIFY |
20488f8b78aSgm89044 	    CRYPTO_FG_VERIFY_RECOVER |
20588f8b78aSgm89044 	    CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC |
20688f8b78aSgm89044 	    CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_SIGN_RECOVER_ATOMIC |
20788f8b78aSgm89044 	    CRYPTO_FG_VERIFY_ATOMIC | CRYPTO_FG_VERIFY_RECOVER_ATOMIC,
20895014fbbSDan OpenSolaris Anderson 	    CRYPTO_BYTES2BITS(RSA_MIN_KEY_LEN),
20995014fbbSDan OpenSolaris Anderson 	    CRYPTO_BYTES2BITS(RSA_MAX_KEY_LEN),
21088f8b78aSgm89044 	    CRYPTO_KEYSIZE_UNIT_IN_BITS}
21188f8b78aSgm89044 };
21288f8b78aSgm89044 
21388f8b78aSgm89044 static void dca_provider_status(crypto_provider_handle_t, uint_t *);
21488f8b78aSgm89044 
21588f8b78aSgm89044 static crypto_control_ops_t dca_control_ops = {
21688f8b78aSgm89044 	dca_provider_status
21788f8b78aSgm89044 };
21888f8b78aSgm89044 
21988f8b78aSgm89044 static int dca_encrypt_init(crypto_ctx_t *, crypto_mechanism_t *,
22088f8b78aSgm89044     crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
22188f8b78aSgm89044 static int dca_encrypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
22288f8b78aSgm89044     crypto_req_handle_t);
22388f8b78aSgm89044 static int dca_encrypt_update(crypto_ctx_t *, crypto_data_t *,
22488f8b78aSgm89044     crypto_data_t *, crypto_req_handle_t);
22588f8b78aSgm89044 static int dca_encrypt_final(crypto_ctx_t *, crypto_data_t *,
22688f8b78aSgm89044     crypto_req_handle_t);
22788f8b78aSgm89044 static int dca_encrypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
22888f8b78aSgm89044     crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
22988f8b78aSgm89044     crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
23088f8b78aSgm89044 
23188f8b78aSgm89044 static int dca_decrypt_init(crypto_ctx_t *, crypto_mechanism_t *,
23288f8b78aSgm89044     crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
23388f8b78aSgm89044 static int dca_decrypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
23488f8b78aSgm89044     crypto_req_handle_t);
23588f8b78aSgm89044 static int dca_decrypt_update(crypto_ctx_t *, crypto_data_t *,
23688f8b78aSgm89044     crypto_data_t *, crypto_req_handle_t);
23788f8b78aSgm89044 static int dca_decrypt_final(crypto_ctx_t *, crypto_data_t *,
23888f8b78aSgm89044     crypto_req_handle_t);
23988f8b78aSgm89044 static int dca_decrypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
24088f8b78aSgm89044     crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
24188f8b78aSgm89044     crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
24288f8b78aSgm89044 
24388f8b78aSgm89044 static crypto_cipher_ops_t dca_cipher_ops = {
24488f8b78aSgm89044 	dca_encrypt_init,
24588f8b78aSgm89044 	dca_encrypt,
24688f8b78aSgm89044 	dca_encrypt_update,
24788f8b78aSgm89044 	dca_encrypt_final,
24888f8b78aSgm89044 	dca_encrypt_atomic,
24988f8b78aSgm89044 	dca_decrypt_init,
25088f8b78aSgm89044 	dca_decrypt,
25188f8b78aSgm89044 	dca_decrypt_update,
25288f8b78aSgm89044 	dca_decrypt_final,
25388f8b78aSgm89044 	dca_decrypt_atomic
25488f8b78aSgm89044 };
25588f8b78aSgm89044 
25688f8b78aSgm89044 static int dca_sign_init(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *,
25788f8b78aSgm89044     crypto_spi_ctx_template_t, crypto_req_handle_t);
25888f8b78aSgm89044 static int dca_sign(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
25988f8b78aSgm89044     crypto_req_handle_t);
26088f8b78aSgm89044 static int dca_sign_update(crypto_ctx_t *, crypto_data_t *,
26188f8b78aSgm89044     crypto_req_handle_t);
26288f8b78aSgm89044 static int dca_sign_final(crypto_ctx_t *, crypto_data_t *,
26388f8b78aSgm89044     crypto_req_handle_t);
26488f8b78aSgm89044 static int dca_sign_atomic(crypto_provider_handle_t, crypto_session_id_t,
26588f8b78aSgm89044     crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
26688f8b78aSgm89044     crypto_spi_ctx_template_t, crypto_req_handle_t);
26788f8b78aSgm89044 static int dca_sign_recover_init(crypto_ctx_t *, crypto_mechanism_t *,
26888f8b78aSgm89044     crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
26988f8b78aSgm89044 static int dca_sign_recover(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
27088f8b78aSgm89044     crypto_req_handle_t);
27188f8b78aSgm89044 static int dca_sign_recover_atomic(crypto_provider_handle_t,
27288f8b78aSgm89044     crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
27388f8b78aSgm89044     crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
27488f8b78aSgm89044 
27588f8b78aSgm89044 static crypto_sign_ops_t dca_sign_ops = {
27688f8b78aSgm89044 	dca_sign_init,
27788f8b78aSgm89044 	dca_sign,
27888f8b78aSgm89044 	dca_sign_update,
27988f8b78aSgm89044 	dca_sign_final,
28088f8b78aSgm89044 	dca_sign_atomic,
28188f8b78aSgm89044 	dca_sign_recover_init,
28288f8b78aSgm89044 	dca_sign_recover,
28388f8b78aSgm89044 	dca_sign_recover_atomic
28488f8b78aSgm89044 };
28588f8b78aSgm89044 
28688f8b78aSgm89044 static int dca_verify_init(crypto_ctx_t *, crypto_mechanism_t *,
28788f8b78aSgm89044     crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
28888f8b78aSgm89044 static int dca_verify(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
28988f8b78aSgm89044     crypto_req_handle_t);
29088f8b78aSgm89044 static int dca_verify_update(crypto_ctx_t *, crypto_data_t *,
29188f8b78aSgm89044     crypto_req_handle_t);
29288f8b78aSgm89044 static int dca_verify_final(crypto_ctx_t *, crypto_data_t *,
29388f8b78aSgm89044     crypto_req_handle_t);
29488f8b78aSgm89044 static int dca_verify_atomic(crypto_provider_handle_t, crypto_session_id_t,
29588f8b78aSgm89044     crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
29688f8b78aSgm89044     crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
29788f8b78aSgm89044 static int dca_verify_recover_init(crypto_ctx_t *, crypto_mechanism_t *,
29888f8b78aSgm89044     crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
29988f8b78aSgm89044 static int dca_verify_recover(crypto_ctx_t *, crypto_data_t *,
30088f8b78aSgm89044     crypto_data_t *, crypto_req_handle_t);
30188f8b78aSgm89044 static int dca_verify_recover_atomic(crypto_provider_handle_t,
30288f8b78aSgm89044     crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
30388f8b78aSgm89044     crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
30488f8b78aSgm89044 
30588f8b78aSgm89044 static crypto_verify_ops_t dca_verify_ops = {
30688f8b78aSgm89044 	dca_verify_init,
30788f8b78aSgm89044 	dca_verify,
30888f8b78aSgm89044 	dca_verify_update,
30988f8b78aSgm89044 	dca_verify_final,
31088f8b78aSgm89044 	dca_verify_atomic,
31188f8b78aSgm89044 	dca_verify_recover_init,
31288f8b78aSgm89044 	dca_verify_recover,
31388f8b78aSgm89044 	dca_verify_recover_atomic
31488f8b78aSgm89044 };
31588f8b78aSgm89044 
31688f8b78aSgm89044 static int dca_generate_random(crypto_provider_handle_t, crypto_session_id_t,
31788f8b78aSgm89044     uchar_t *, size_t, crypto_req_handle_t);
31888f8b78aSgm89044 
31988f8b78aSgm89044 static crypto_random_number_ops_t dca_random_number_ops = {
32088f8b78aSgm89044 	NULL,
32188f8b78aSgm89044 	dca_generate_random
32288f8b78aSgm89044 };
32388f8b78aSgm89044 
32488f8b78aSgm89044 static int ext_info_sym(crypto_provider_handle_t prov,
32588f8b78aSgm89044     crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq);
32688f8b78aSgm89044 static int ext_info_asym(crypto_provider_handle_t prov,
32788f8b78aSgm89044     crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq);
32888f8b78aSgm89044 static int ext_info_base(crypto_provider_handle_t prov,
32988f8b78aSgm89044     crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq, char *id);
33088f8b78aSgm89044 
33188f8b78aSgm89044 static crypto_provider_management_ops_t dca_provmanage_ops_1 = {
33288f8b78aSgm89044 	ext_info_sym,		/* ext_info */
33388f8b78aSgm89044 	NULL,			/* init_token */
33488f8b78aSgm89044 	NULL,			/* init_pin */
33588f8b78aSgm89044 	NULL			/* set_pin */
33688f8b78aSgm89044 };
33788f8b78aSgm89044 
33888f8b78aSgm89044 static crypto_provider_management_ops_t dca_provmanage_ops_2 = {
33988f8b78aSgm89044 	ext_info_asym,		/* ext_info */
34088f8b78aSgm89044 	NULL,			/* init_token */
34188f8b78aSgm89044 	NULL,			/* init_pin */
34288f8b78aSgm89044 	NULL			/* set_pin */
34388f8b78aSgm89044 };
34488f8b78aSgm89044 
34588f8b78aSgm89044 int dca_free_context(crypto_ctx_t *);
34688f8b78aSgm89044 
34788f8b78aSgm89044 static crypto_ctx_ops_t dca_ctx_ops = {
34888f8b78aSgm89044 	NULL,
34988f8b78aSgm89044 	dca_free_context
35088f8b78aSgm89044 };
35188f8b78aSgm89044 
35288f8b78aSgm89044 /* Operations for the symmetric cipher provider */
35388f8b78aSgm89044 static crypto_ops_t dca_crypto_ops1 = {
35488f8b78aSgm89044 	&dca_control_ops,
35588f8b78aSgm89044 	NULL,				/* digest_ops */
35688f8b78aSgm89044 	&dca_cipher_ops,
35788f8b78aSgm89044 	NULL,				/* mac_ops */
35888f8b78aSgm89044 	NULL,				/* sign_ops */
35988f8b78aSgm89044 	NULL,				/* verify_ops */
36088f8b78aSgm89044 	NULL,				/* dual_ops */
36188f8b78aSgm89044 	NULL,				/* cipher_mac_ops */
36288f8b78aSgm89044 	NULL,				/* random_number_ops */
36388f8b78aSgm89044 	NULL,				/* session_ops */
36488f8b78aSgm89044 	NULL,				/* object_ops */
36588f8b78aSgm89044 	NULL,				/* key_ops */
36688f8b78aSgm89044 	&dca_provmanage_ops_1,		/* management_ops */
36788f8b78aSgm89044 	&dca_ctx_ops
36888f8b78aSgm89044 };
36988f8b78aSgm89044 
37088f8b78aSgm89044 /* Operations for the asymmetric cipher provider */
37188f8b78aSgm89044 static crypto_ops_t dca_crypto_ops2 = {
37288f8b78aSgm89044 	&dca_control_ops,
37388f8b78aSgm89044 	NULL,				/* digest_ops */
37488f8b78aSgm89044 	&dca_cipher_ops,
37588f8b78aSgm89044 	NULL,				/* mac_ops */
37688f8b78aSgm89044 	&dca_sign_ops,
37788f8b78aSgm89044 	&dca_verify_ops,
37888f8b78aSgm89044 	NULL,				/* dual_ops */
37988f8b78aSgm89044 	NULL,				/* cipher_mac_ops */
38088f8b78aSgm89044 	&dca_random_number_ops,
38188f8b78aSgm89044 	NULL,				/* session_ops */
38288f8b78aSgm89044 	NULL,				/* object_ops */
38388f8b78aSgm89044 	NULL,				/* key_ops */
38488f8b78aSgm89044 	&dca_provmanage_ops_2,		/* management_ops */
38588f8b78aSgm89044 	&dca_ctx_ops
38688f8b78aSgm89044 };
38788f8b78aSgm89044 
38888f8b78aSgm89044 /* Provider information for the symmetric cipher provider */
38988f8b78aSgm89044 static crypto_provider_info_t dca_prov_info1 = {
39088f8b78aSgm89044 	CRYPTO_SPI_VERSION_1,
39188f8b78aSgm89044 	NULL,				/* pi_provider_description */
39288f8b78aSgm89044 	CRYPTO_HW_PROVIDER,
39388f8b78aSgm89044 	NULL,				/* pi_provider_dev */
39488f8b78aSgm89044 	NULL,				/* pi_provider_handle */
39588f8b78aSgm89044 	&dca_crypto_ops1,
39688f8b78aSgm89044 	sizeof (dca_mech_info_tab1)/sizeof (crypto_mech_info_t),
39788f8b78aSgm89044 	dca_mech_info_tab1,
39888f8b78aSgm89044 	0,				/* pi_logical_provider_count */
39988f8b78aSgm89044 	NULL				/* pi_logical_providers */
40088f8b78aSgm89044 };
40188f8b78aSgm89044 
40288f8b78aSgm89044 /* Provider information for the asymmetric cipher provider */
40388f8b78aSgm89044 static crypto_provider_info_t dca_prov_info2 = {
40488f8b78aSgm89044 	CRYPTO_SPI_VERSION_1,
40588f8b78aSgm89044 	NULL,				/* pi_provider_description */
40688f8b78aSgm89044 	CRYPTO_HW_PROVIDER,
40788f8b78aSgm89044 	NULL,				/* pi_provider_dev */
40888f8b78aSgm89044 	NULL,				/* pi_provider_handle */
40988f8b78aSgm89044 	&dca_crypto_ops2,
41088f8b78aSgm89044 	sizeof (dca_mech_info_tab2)/sizeof (crypto_mech_info_t),
41188f8b78aSgm89044 	dca_mech_info_tab2,
41288f8b78aSgm89044 	0,				/* pi_logical_provider_count */
41388f8b78aSgm89044 	NULL				/* pi_logical_providers */
41488f8b78aSgm89044 };
41588f8b78aSgm89044 
41688f8b78aSgm89044 /* Convenience macros */
4173ba94426SRichard Lowe #define	DCA_SOFTC_FROM_CTX(ctx)	((dca_t *)(ctx)->cc_provider)
41888f8b78aSgm89044 #define	DCA_MECH_FROM_CTX(ctx) \
41988f8b78aSgm89044 	(((dca_request_t *)(ctx)->cc_provider_private)->dr_ctx.ctx_cm_type)
42088f8b78aSgm89044 
42188f8b78aSgm89044 static int dca_bindchains_one(dca_request_t *reqp, size_t cnt, int dr_offset,
42288f8b78aSgm89044     caddr_t kaddr, ddi_dma_handle_t handle, uint_t flags,
42388f8b78aSgm89044     dca_chain_t *head, int *n_chain);
42488f8b78aSgm89044 static uint64_t dca_ena(uint64_t ena);
42588f8b78aSgm89044 static caddr_t dca_bufdaddr_out(crypto_data_t *data);
42688f8b78aSgm89044 static char *dca_fma_eclass_string(char *model, dca_fma_eclass_t index);
42788f8b78aSgm89044 static int dca_check_acc_handle(dca_t *dca, ddi_acc_handle_t handle,
42888f8b78aSgm89044     dca_fma_eclass_t eclass_index);
42988f8b78aSgm89044 
43088f8b78aSgm89044 static void dca_fma_init(dca_t *dca);
43188f8b78aSgm89044 static void dca_fma_fini(dca_t *dca);
43288f8b78aSgm89044 static int dca_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err,
43388f8b78aSgm89044     const void *impl_data);
43488f8b78aSgm89044 
43588f8b78aSgm89044 
43688f8b78aSgm89044 static dca_device_t dca_devices[] = {
43788f8b78aSgm89044 	/* Broadcom vanilla variants */
43888f8b78aSgm89044 	{	0x14e4, 0x5820, "Broadcom 5820" },
43988f8b78aSgm89044 	{	0x14e4, 0x5821, "Broadcom 5821" },
44088f8b78aSgm89044 	{	0x14e4, 0x5822, "Broadcom 5822" },
44188f8b78aSgm89044 	{	0x14e4, 0x5825, "Broadcom 5825" },
44288f8b78aSgm89044 	/* Sun specific OEMd variants */
44388f8b78aSgm89044 	{	0x108e, 0x5454, "SCA" },
44488f8b78aSgm89044 	{	0x108e, 0x5455, "SCA 1000" },
44588f8b78aSgm89044 	{	0x108e, 0x5457, "SCA 500" },
44688f8b78aSgm89044 	/* subsysid should be 0x5457, but got 0x1 from HW. Assume both here. */
44788f8b78aSgm89044 	{	0x108e, 0x1, "SCA 500" },
44888f8b78aSgm89044 };
44988f8b78aSgm89044 
45088f8b78aSgm89044 /*
45188f8b78aSgm89044  * Device attributes.
45288f8b78aSgm89044  */
45388f8b78aSgm89044 static struct ddi_device_acc_attr dca_regsattr = {
454837c1ac4SStephen Hanson 	DDI_DEVICE_ATTR_V1,
45588f8b78aSgm89044 	DDI_STRUCTURE_LE_ACC,
45688f8b78aSgm89044 	DDI_STRICTORDER_ACC,
45788f8b78aSgm89044 	DDI_FLAGERR_ACC
45888f8b78aSgm89044 };
45988f8b78aSgm89044 
46088f8b78aSgm89044 static struct ddi_device_acc_attr dca_devattr = {
46188f8b78aSgm89044 	DDI_DEVICE_ATTR_V0,
46288f8b78aSgm89044 	DDI_STRUCTURE_LE_ACC,
463837c1ac4SStephen Hanson 	DDI_STRICTORDER_ACC
46488f8b78aSgm89044 };
46588f8b78aSgm89044 
46688f8b78aSgm89044 static struct ddi_device_acc_attr dca_bufattr = {
46788f8b78aSgm89044 	DDI_DEVICE_ATTR_V0,
46888f8b78aSgm89044 	DDI_NEVERSWAP_ACC,
469837c1ac4SStephen Hanson 	DDI_STRICTORDER_ACC
47088f8b78aSgm89044 };
47188f8b78aSgm89044 
47288f8b78aSgm89044 static struct ddi_dma_attr dca_dmaattr = {
47388f8b78aSgm89044 	DMA_ATTR_V0,		/* dma_attr_version */
47488f8b78aSgm89044 	0x0,			/* dma_attr_addr_lo */
47588f8b78aSgm89044 	0xffffffffUL,		/* dma_attr_addr_hi */
47688f8b78aSgm89044 	0x00ffffffUL,		/* dma_attr_count_max */
47788f8b78aSgm89044 	0x40,			/* dma_attr_align */
47888f8b78aSgm89044 	0x40,			/* dma_attr_burstsizes */
47988f8b78aSgm89044 	0x1,			/* dma_attr_minxfer */
48088f8b78aSgm89044 	0x00ffffffUL,		/* dma_attr_maxxfer */
48188f8b78aSgm89044 	0xffffffffUL,		/* dma_attr_seg */
482*86ef0a63SRichard Lowe #if defined(__x86)
48388f8b78aSgm89044 	512,			/* dma_attr_sgllen */
48488f8b78aSgm89044 #else
48588f8b78aSgm89044 	1,			/* dma_attr_sgllen */
48688f8b78aSgm89044 #endif
48788f8b78aSgm89044 	1,			/* dma_attr_granular */
48888f8b78aSgm89044 	DDI_DMA_FLAGERR		/* dma_attr_flags */
48988f8b78aSgm89044 };
49088f8b78aSgm89044 
49188f8b78aSgm89044 static void	*dca_state = NULL;
49288f8b78aSgm89044 int	dca_mindma = 2500;
49388f8b78aSgm89044 
49488f8b78aSgm89044 /*
49588f8b78aSgm89044  * FMA eclass string definitions. Note that these string arrays must be
49688f8b78aSgm89044  * consistent with the dca_fma_eclass_t enum.
49788f8b78aSgm89044  */
49888f8b78aSgm89044 static char *dca_fma_eclass_sca1000[] = {
49988f8b78aSgm89044 	"sca1000.hw.device",
50088f8b78aSgm89044 	"sca1000.hw.timeout",
50188f8b78aSgm89044 	"sca1000.none"
50288f8b78aSgm89044 };
50388f8b78aSgm89044 
50488f8b78aSgm89044 static char *dca_fma_eclass_sca500[] = {
50588f8b78aSgm89044 	"sca500.hw.device",
50688f8b78aSgm89044 	"sca500.hw.timeout",
50788f8b78aSgm89044 	"sca500.none"
50888f8b78aSgm89044 };
50988f8b78aSgm89044 
51088f8b78aSgm89044 /*
51188f8b78aSgm89044  * DDI entry points.
51288f8b78aSgm89044  */
51388f8b78aSgm89044 int
_init(void)51488f8b78aSgm89044 _init(void)
51588f8b78aSgm89044 {
51688f8b78aSgm89044 	int rv;
51788f8b78aSgm89044 
51888f8b78aSgm89044 	DBG(NULL, DMOD, "dca: in _init");
51988f8b78aSgm89044 
52088f8b78aSgm89044 	if ((rv = ddi_soft_state_init(&dca_state, sizeof (dca_t), 1)) != 0) {
52188f8b78aSgm89044 		/* this should *never* happen! */
52288f8b78aSgm89044 		return (rv);
52388f8b78aSgm89044 	}
52488f8b78aSgm89044 
52588f8b78aSgm89044 	if ((rv = mod_install(&modlinkage)) != 0) {
52688f8b78aSgm89044 		/* cleanup here */
52788f8b78aSgm89044 		ddi_soft_state_fini(&dca_state);
52888f8b78aSgm89044 		return (rv);
52988f8b78aSgm89044 	}
53088f8b78aSgm89044 
53188f8b78aSgm89044 	return (0);
53288f8b78aSgm89044 }
53388f8b78aSgm89044 
53488f8b78aSgm89044 int
_fini(void)53588f8b78aSgm89044 _fini(void)
53688f8b78aSgm89044 {
53788f8b78aSgm89044 	int rv;
53888f8b78aSgm89044 
53988f8b78aSgm89044 	DBG(NULL, DMOD, "dca: in _fini");
54088f8b78aSgm89044 
54188f8b78aSgm89044 	if ((rv = mod_remove(&modlinkage)) == 0) {
54288f8b78aSgm89044 		/* cleanup here */
54388f8b78aSgm89044 		ddi_soft_state_fini(&dca_state);
54488f8b78aSgm89044 	}
54588f8b78aSgm89044 	return (rv);
54688f8b78aSgm89044 }
54788f8b78aSgm89044 
54888f8b78aSgm89044 int
_info(struct modinfo * modinfop)54988f8b78aSgm89044 _info(struct modinfo *modinfop)
55088f8b78aSgm89044 {
55188f8b78aSgm89044 	DBG(NULL, DMOD, "dca: in _info");
55288f8b78aSgm89044 
55388f8b78aSgm89044 	return (mod_info(&modlinkage, modinfop));
55488f8b78aSgm89044 }
55588f8b78aSgm89044 
55688f8b78aSgm89044 int
dca_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)55788f8b78aSgm89044 dca_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
55888f8b78aSgm89044 {
55988f8b78aSgm89044 	ddi_acc_handle_t	pci;
56088f8b78aSgm89044 	int			instance;
56188f8b78aSgm89044 	ddi_iblock_cookie_t	ibc;
56288f8b78aSgm89044 	int			intr_added = 0;
56388f8b78aSgm89044 	dca_t			*dca;
56488f8b78aSgm89044 	ushort_t		venid;
56588f8b78aSgm89044 	ushort_t		devid;
56688f8b78aSgm89044 	ushort_t		revid;
56788f8b78aSgm89044 	ushort_t		subsysid;
56888f8b78aSgm89044 	ushort_t		subvenid;
56988f8b78aSgm89044 	int			i;
57088f8b78aSgm89044 	int			ret;
57188f8b78aSgm89044 	char			ID[64];
57288f8b78aSgm89044 	static char		*unknowndev = "Unknown device";
57388f8b78aSgm89044 
57488f8b78aSgm89044 #if DEBUG
57588f8b78aSgm89044 	/* these are only used for debugging */
57688f8b78aSgm89044 	ushort_t		pcicomm;
57788f8b78aSgm89044 	ushort_t		pcistat;
57888f8b78aSgm89044 	uchar_t			cachelinesz;
57988f8b78aSgm89044 	uchar_t			mingnt;
58088f8b78aSgm89044 	uchar_t			maxlat;
58188f8b78aSgm89044 	uchar_t			lattmr;
58288f8b78aSgm89044 #endif
58388f8b78aSgm89044 
58488f8b78aSgm89044 	instance = ddi_get_instance(dip);
58588f8b78aSgm89044 
58688f8b78aSgm89044 	DBG(NULL, DMOD, "dca: in dca_attach() for %d", instance);
58788f8b78aSgm89044 
58888f8b78aSgm89044 	switch (cmd) {
58988f8b78aSgm89044 	case DDI_RESUME:
59088f8b78aSgm89044 		if ((dca = (dca_t *)ddi_get_driver_private(dip)) == NULL) {
59188f8b78aSgm89044 			dca_diperror(dip, "no soft state in detach");
59288f8b78aSgm89044 			return (DDI_FAILURE);
59388f8b78aSgm89044 		}
59488f8b78aSgm89044 		/* assumption: we won't be DDI_DETACHed until we return */
59588f8b78aSgm89044 		return (dca_resume(dca));
59688f8b78aSgm89044 	case DDI_ATTACH:
59788f8b78aSgm89044 		break;
59888f8b78aSgm89044 	default:
59988f8b78aSgm89044 		return (DDI_FAILURE);
60088f8b78aSgm89044 	}
60188f8b78aSgm89044 
60288f8b78aSgm89044 	if (ddi_slaveonly(dip) == DDI_SUCCESS) {
60388f8b78aSgm89044 		dca_diperror(dip, "slot does not support PCI bus-master");
60488f8b78aSgm89044 		return (DDI_FAILURE);
60588f8b78aSgm89044 	}
60688f8b78aSgm89044 
60788f8b78aSgm89044 	if (ddi_intr_hilevel(dip, 0) != 0) {
60888f8b78aSgm89044 		dca_diperror(dip, "hilevel interrupts not supported");
60988f8b78aSgm89044 		return (DDI_FAILURE);
61088f8b78aSgm89044 	}
61188f8b78aSgm89044 
61288f8b78aSgm89044 	if (pci_config_setup(dip, &pci) != DDI_SUCCESS) {
61388f8b78aSgm89044 		dca_diperror(dip, "unable to setup PCI config handle");
61488f8b78aSgm89044 		return (DDI_FAILURE);
61588f8b78aSgm89044 	}
61688f8b78aSgm89044 
61788f8b78aSgm89044 	/* common PCI attributes */
61888f8b78aSgm89044 	venid = pci_config_get16(pci, PCI_VENID);
61988f8b78aSgm89044 	devid = pci_config_get16(pci, PCI_DEVID);
62088f8b78aSgm89044 	revid = pci_config_get8(pci, PCI_REVID);
62188f8b78aSgm89044 	subvenid = pci_config_get16(pci, PCI_SUBVENID);
62288f8b78aSgm89044 	subsysid = pci_config_get16(pci, PCI_SUBSYSID);
62388f8b78aSgm89044 
62488f8b78aSgm89044 	/*
62588f8b78aSgm89044 	 * Broadcom-specific timings.
62688f8b78aSgm89044 	 * We disable these timers/counters since they can cause
62788f8b78aSgm89044 	 * incorrect false failures when the bus is just a little
62888f8b78aSgm89044 	 * bit slow, or busy.
62988f8b78aSgm89044 	 */
63088f8b78aSgm89044 	pci_config_put8(pci, PCI_TRDYTO, 0);
63188f8b78aSgm89044 	pci_config_put8(pci, PCI_RETRIES, 0);
63288f8b78aSgm89044 
63388f8b78aSgm89044 	/* initialize PCI access settings */
63488f8b78aSgm89044 	pci_config_put16(pci, PCI_COMM, PCICOMM_SEE |
63588f8b78aSgm89044 	    PCICOMM_PEE | PCICOMM_BME | PCICOMM_MAE);
63688f8b78aSgm89044 
63788f8b78aSgm89044 	/* set up our PCI latency timer */
63888f8b78aSgm89044 	pci_config_put8(pci, PCI_LATTMR, 0x40);
63988f8b78aSgm89044 
64088f8b78aSgm89044 #if DEBUG
64188f8b78aSgm89044 	/* read registers (for debugging) */
64288f8b78aSgm89044 	pcicomm = pci_config_get16(pci, PCI_COMM);
64388f8b78aSgm89044 	pcistat = pci_config_get16(pci, PCI_STATUS);
64488f8b78aSgm89044 	cachelinesz = pci_config_get8(pci, PCI_CACHELINESZ);
64588f8b78aSgm89044 	mingnt = pci_config_get8(pci, PCI_MINGNT);
64688f8b78aSgm89044 	maxlat = pci_config_get8(pci, PCI_MAXLAT);
64788f8b78aSgm89044 	lattmr = pci_config_get8(pci, PCI_LATTMR);
64888f8b78aSgm89044 #endif
64988f8b78aSgm89044 
65088f8b78aSgm89044 	pci_config_teardown(&pci);
65188f8b78aSgm89044 
65288f8b78aSgm89044 	if (ddi_get_iblock_cookie(dip, 0, &ibc) != DDI_SUCCESS) {
65388f8b78aSgm89044 		dca_diperror(dip, "unable to get iblock cookie");
65488f8b78aSgm89044 		return (DDI_FAILURE);
65588f8b78aSgm89044 	}
65688f8b78aSgm89044 
65788f8b78aSgm89044 	if (ddi_soft_state_zalloc(dca_state, instance) != DDI_SUCCESS) {
65888f8b78aSgm89044 		dca_diperror(dip, "unable to allocate soft state");
65988f8b78aSgm89044 		return (DDI_FAILURE);
66088f8b78aSgm89044 	}
66188f8b78aSgm89044 
66288f8b78aSgm89044 	dca = ddi_get_soft_state(dca_state, instance);
66388f8b78aSgm89044 	ASSERT(dca != NULL);
66488f8b78aSgm89044 	dca->dca_dip = dip;
6655c63ad82SToomas Soome 	WORKLIST(dca, MCR1)->dwl_prov = 0;
6665c63ad82SToomas Soome 	WORKLIST(dca, MCR2)->dwl_prov = 0;
66788f8b78aSgm89044 	/* figure pagesize */
66888f8b78aSgm89044 	dca->dca_pagesize = ddi_ptob(dip, 1);
66988f8b78aSgm89044 
67088f8b78aSgm89044 	/*
67188f8b78aSgm89044 	 * Search for the device in our supported devices table.  This
67288f8b78aSgm89044 	 * is here for two reasons.  First, we want to ensure that
67388f8b78aSgm89044 	 * only Sun-qualified (and presumably Sun-labeled) devices can
67488f8b78aSgm89044 	 * be used with this driver.  Second, some devices have
67588f8b78aSgm89044 	 * specific differences.  E.g. the 5821 has support for a
67688f8b78aSgm89044 	 * special mode of RC4, deeper queues, power management, and
67788f8b78aSgm89044 	 * other changes.  Also, the export versions of some of these
67888f8b78aSgm89044 	 * chips don't support RC4 or 3DES, so we catch that here.
67988f8b78aSgm89044 	 *
68088f8b78aSgm89044 	 * Note that we only look at the upper nibble of the device
68188f8b78aSgm89044 	 * id, which is used to distinguish export vs. domestic
68288f8b78aSgm89044 	 * versions of the chip.  (The lower nibble is used for
68388f8b78aSgm89044 	 * stepping information.)
68488f8b78aSgm89044 	 */
68588f8b78aSgm89044 	for (i = 0; i < (sizeof (dca_devices) / sizeof (dca_device_t)); i++) {
68688f8b78aSgm89044 		/*
68788f8b78aSgm89044 		 * Try to match the subsystem information first.
68888f8b78aSgm89044 		 */
68988f8b78aSgm89044 		if (subvenid && (subvenid == dca_devices[i].dd_vendor_id) &&
69088f8b78aSgm89044 		    subsysid && (subsysid == dca_devices[i].dd_device_id)) {
69188f8b78aSgm89044 			dca->dca_model = dca_devices[i].dd_model;
6923383b6ddSqs148142 			dca->dca_devid = dca_devices[i].dd_device_id;
69388f8b78aSgm89044 			break;
69488f8b78aSgm89044 		}
69588f8b78aSgm89044 		/*
69688f8b78aSgm89044 		 * Failing that, try the generic vendor and device id.
69788f8b78aSgm89044 		 * Even if we find a match, we keep searching anyway,
69888f8b78aSgm89044 		 * since we would prefer to find a match based on the
69988f8b78aSgm89044 		 * subsystem ids.
70088f8b78aSgm89044 		 */
70188f8b78aSgm89044 		if ((venid == dca_devices[i].dd_vendor_id) &&
70288f8b78aSgm89044 		    (devid == dca_devices[i].dd_device_id)) {
70388f8b78aSgm89044 			dca->dca_model = dca_devices[i].dd_model;
7043383b6ddSqs148142 			dca->dca_devid = dca_devices[i].dd_device_id;
70588f8b78aSgm89044 		}
70688f8b78aSgm89044 	}
70788f8b78aSgm89044 	/* try and handle an unrecognized device */
70888f8b78aSgm89044 	if (dca->dca_model == NULL) {
70988f8b78aSgm89044 		dca->dca_model = unknowndev;
71088f8b78aSgm89044 		dca_error(dca, "device not recognized, not supported");
71188f8b78aSgm89044 		DBG(dca, DPCI, "i=%d venid=%x devid=%x rev=%d",
71288f8b78aSgm89044 		    i, venid, devid, revid);
71388f8b78aSgm89044 	}
71488f8b78aSgm89044 
71588f8b78aSgm89044 	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "description",
71688f8b78aSgm89044 	    dca->dca_model) != DDI_SUCCESS) {
71788f8b78aSgm89044 		dca_error(dca, "unable to create description property");
71888f8b78aSgm89044 		return (DDI_FAILURE);
71988f8b78aSgm89044 	}
72088f8b78aSgm89044 
72188f8b78aSgm89044 	DBG(dca, DPCI, "PCI command=0x%x status=%x cachelinesz=%x",
72288f8b78aSgm89044 	    pcicomm, pcistat, cachelinesz);
72388f8b78aSgm89044 	DBG(dca, DPCI, "mingnt=0x%x maxlat=0x%x lattmr=0x%x",
72488f8b78aSgm89044 	    mingnt, maxlat, lattmr);
72588f8b78aSgm89044 
72688f8b78aSgm89044 	/*
72788f8b78aSgm89044 	 * initialize locks, etc.
72888f8b78aSgm89044 	 */
72988f8b78aSgm89044 	(void) mutex_init(&dca->dca_intrlock, NULL, MUTEX_DRIVER, ibc);
73088f8b78aSgm89044 
73188f8b78aSgm89044 	/* use RNGSHA1 by default */
73288f8b78aSgm89044 	if (ddi_getprop(DDI_DEV_T_ANY, dip,
73388f8b78aSgm89044 	    DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "rngdirect", 0) == 0) {
73488f8b78aSgm89044 		dca->dca_flags |= DCA_RNGSHA1;
73588f8b78aSgm89044 	}
73688f8b78aSgm89044 
73788f8b78aSgm89044 	/* initialize FMA */
73888f8b78aSgm89044 	dca_fma_init(dca);
73988f8b78aSgm89044 
74088f8b78aSgm89044 	/* initialize some key data structures */
74188f8b78aSgm89044 	if (dca_init(dca) != DDI_SUCCESS) {
74288f8b78aSgm89044 		goto failed;
74388f8b78aSgm89044 	}
74488f8b78aSgm89044 
74588f8b78aSgm89044 	/* initialize kstats */
74688f8b78aSgm89044 	dca_ksinit(dca);
74788f8b78aSgm89044 
74888f8b78aSgm89044 	/* setup access to registers */
74988f8b78aSgm89044 	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&dca->dca_regs,
75088f8b78aSgm89044 	    0, 0, &dca_regsattr, &dca->dca_regs_handle) != DDI_SUCCESS) {
75188f8b78aSgm89044 		dca_error(dca, "unable to map registers");
75288f8b78aSgm89044 		goto failed;
75388f8b78aSgm89044 	}
75488f8b78aSgm89044 
75588f8b78aSgm89044 	DBG(dca, DCHATTY, "MCR1 = %x", GETCSR(dca, CSR_MCR1));
75688f8b78aSgm89044 	DBG(dca, DCHATTY, "CONTROL = %x", GETCSR(dca, CSR_DMACTL));
75788f8b78aSgm89044 	DBG(dca, DCHATTY, "STATUS = %x", GETCSR(dca, CSR_DMASTAT));
75888f8b78aSgm89044 	DBG(dca, DCHATTY, "DMAEA = %x", GETCSR(dca, CSR_DMAEA));
75988f8b78aSgm89044 	DBG(dca, DCHATTY, "MCR2 = %x", GETCSR(dca, CSR_MCR2));
76088f8b78aSgm89044 
76188f8b78aSgm89044 	/* reset the chip */
76288f8b78aSgm89044 	if (dca_reset(dca, 0) < 0) {
76388f8b78aSgm89044 		goto failed;
76488f8b78aSgm89044 	}
76588f8b78aSgm89044 
76688f8b78aSgm89044 	/* initialize the chip */
76788f8b78aSgm89044 	PUTCSR(dca, CSR_DMACTL, DMACTL_BE32 | DMACTL_BE64);
76888f8b78aSgm89044 	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
76988f8b78aSgm89044 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
77088f8b78aSgm89044 		goto failed;
77188f8b78aSgm89044 	}
77288f8b78aSgm89044 
77388f8b78aSgm89044 	/* add the interrupt */
77488f8b78aSgm89044 	if (ddi_add_intr(dip, 0, &dca->dca_icookie, NULL, dca_intr,
77588f8b78aSgm89044 	    (void *)dca) != DDI_SUCCESS) {
77688f8b78aSgm89044 		DBG(dca, DWARN, "ddi_add_intr failed");
77788f8b78aSgm89044 		goto failed;
77888f8b78aSgm89044 	} else {
77988f8b78aSgm89044 		intr_added = 1;
78088f8b78aSgm89044 	}
78188f8b78aSgm89044 
78288f8b78aSgm89044 	/* enable interrupts on the device */
78388f8b78aSgm89044 	/*
78488f8b78aSgm89044 	 * XXX: Note, 5820A1 errata indicates that this may clobber
78588f8b78aSgm89044 	 * bits 24 and 23, which affect the speed of the RNG.  Since
78688f8b78aSgm89044 	 * we always want to run in full-speed mode, this should be
78788f8b78aSgm89044 	 * harmless.
78888f8b78aSgm89044 	 */
7893383b6ddSqs148142 	if (dca->dca_devid == 0x5825) {
7903383b6ddSqs148142 		/* for 5825 - increase the DMA read size */
7913383b6ddSqs148142 		SETBIT(dca, CSR_DMACTL,
7923383b6ddSqs148142 		    DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE | DMACTL_RD256);
7933383b6ddSqs148142 	} else {
7943383b6ddSqs148142 		SETBIT(dca, CSR_DMACTL,
7953383b6ddSqs148142 		    DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE);
7963383b6ddSqs148142 	}
79788f8b78aSgm89044 	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
79888f8b78aSgm89044 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
79988f8b78aSgm89044 		goto failed;
80088f8b78aSgm89044 	}
80188f8b78aSgm89044 
80288f8b78aSgm89044 	/* register MCR1 with the crypto framework */
80388f8b78aSgm89044 	/* Be careful not to exceed 32 chars */
80488f8b78aSgm89044 	(void) sprintf(ID, "%s/%d %s",
80588f8b78aSgm89044 	    ddi_driver_name(dip), ddi_get_instance(dip), IDENT_SYM);
80688f8b78aSgm89044 	dca_prov_info1.pi_provider_description = ID;
80788f8b78aSgm89044 	dca_prov_info1.pi_provider_dev.pd_hw = dip;
80888f8b78aSgm89044 	dca_prov_info1.pi_provider_handle = dca;
80988f8b78aSgm89044 	if ((ret = crypto_register_provider(&dca_prov_info1,
81088f8b78aSgm89044 	    &WORKLIST(dca, MCR1)->dwl_prov)) != CRYPTO_SUCCESS) {
81188f8b78aSgm89044 		cmn_err(CE_WARN,
81288f8b78aSgm89044 		    "crypto_register_provider() failed (%d) for MCR1", ret);
81388f8b78aSgm89044 		goto failed;
81488f8b78aSgm89044 	}
81588f8b78aSgm89044 
81688f8b78aSgm89044 	/* register MCR2 with the crypto framework */
81788f8b78aSgm89044 	/* Be careful not to exceed 32 chars */
81888f8b78aSgm89044 	(void) sprintf(ID, "%s/%d %s",
81988f8b78aSgm89044 	    ddi_driver_name(dip), ddi_get_instance(dip), IDENT_ASYM);
82088f8b78aSgm89044 	dca_prov_info2.pi_provider_description = ID;
82188f8b78aSgm89044 	dca_prov_info2.pi_provider_dev.pd_hw = dip;
82288f8b78aSgm89044 	dca_prov_info2.pi_provider_handle = dca;
82388f8b78aSgm89044 	if ((ret = crypto_register_provider(&dca_prov_info2,
82488f8b78aSgm89044 	    &WORKLIST(dca, MCR2)->dwl_prov)) != CRYPTO_SUCCESS) {
82588f8b78aSgm89044 		cmn_err(CE_WARN,
82688f8b78aSgm89044 		    "crypto_register_provider() failed (%d) for MCR2", ret);
82788f8b78aSgm89044 		goto failed;
82888f8b78aSgm89044 	}
82988f8b78aSgm89044 
83088f8b78aSgm89044 	crypto_prov_notify(WORKLIST(dca, MCR1)->dwl_prov,
83188f8b78aSgm89044 	    CRYPTO_PROVIDER_READY);
83288f8b78aSgm89044 	crypto_prov_notify(WORKLIST(dca, MCR2)->dwl_prov,
83388f8b78aSgm89044 	    CRYPTO_PROVIDER_READY);
83488f8b78aSgm89044 
83588f8b78aSgm89044 	/* Initialize the local random number pool for this instance */
83688f8b78aSgm89044 	if ((ret = dca_random_init(dca)) != CRYPTO_SUCCESS) {
83788f8b78aSgm89044 		goto failed;
83888f8b78aSgm89044 	}
83988f8b78aSgm89044 
84088f8b78aSgm89044 	mutex_enter(&dca->dca_intrlock);
84188f8b78aSgm89044 	dca->dca_jobtid = timeout(dca_jobtimeout, (void *)dca,
84288f8b78aSgm89044 	    drv_usectohz(SECOND));
84388f8b78aSgm89044 	mutex_exit(&dca->dca_intrlock);
84488f8b78aSgm89044 
84588f8b78aSgm89044 	ddi_set_driver_private(dip, (caddr_t)dca);
84688f8b78aSgm89044 
84788f8b78aSgm89044 	ddi_report_dev(dip);
84888f8b78aSgm89044 
84988f8b78aSgm89044 	if (ddi_get_devstate(dca->dca_dip) != DDI_DEVSTATE_UP) {
85088f8b78aSgm89044 		ddi_fm_service_impact(dca->dca_dip, DDI_SERVICE_RESTORED);
85188f8b78aSgm89044 	}
85288f8b78aSgm89044 
85388f8b78aSgm89044 	return (DDI_SUCCESS);
85488f8b78aSgm89044 
85588f8b78aSgm89044 failed:
85688f8b78aSgm89044 	/* unregister from the crypto framework */
8575c63ad82SToomas Soome 	if (WORKLIST(dca, MCR1)->dwl_prov != 0) {
858d8dd9913Sgm89044 		(void) crypto_unregister_provider(
859d8dd9913Sgm89044 		    WORKLIST(dca, MCR1)->dwl_prov);
86088f8b78aSgm89044 	}
8615c63ad82SToomas Soome 	if (WORKLIST(dca, MCR2)->dwl_prov != 0) {
862d8dd9913Sgm89044 		(void) crypto_unregister_provider(
863d8dd9913Sgm89044 		    WORKLIST(dca, MCR2)->dwl_prov);
86488f8b78aSgm89044 	}
86588f8b78aSgm89044 	if (intr_added) {
86688f8b78aSgm89044 		CLRBIT(dca, CSR_DMACTL,
86788f8b78aSgm89044 		    DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE);
86888f8b78aSgm89044 		/* unregister intr handler */
86988f8b78aSgm89044 		ddi_remove_intr(dip, 0, dca->dca_icookie);
87088f8b78aSgm89044 	}
87188f8b78aSgm89044 	if (dca->dca_regs_handle) {
87288f8b78aSgm89044 		ddi_regs_map_free(&dca->dca_regs_handle);
87388f8b78aSgm89044 	}
87488f8b78aSgm89044 	if (dca->dca_intrstats) {
87588f8b78aSgm89044 		kstat_delete(dca->dca_intrstats);
87688f8b78aSgm89044 	}
87788f8b78aSgm89044 	if (dca->dca_ksp) {
87888f8b78aSgm89044 		kstat_delete(dca->dca_ksp);
87988f8b78aSgm89044 	}
88088f8b78aSgm89044 	dca_uninit(dca);
88188f8b78aSgm89044 
88288f8b78aSgm89044 	/* finalize FMA */
88388f8b78aSgm89044 	dca_fma_fini(dca);
88488f8b78aSgm89044 
88588f8b78aSgm89044 	mutex_destroy(&dca->dca_intrlock);
88688f8b78aSgm89044 	ddi_soft_state_free(dca_state, instance);
88788f8b78aSgm89044 	return (DDI_FAILURE);
88888f8b78aSgm89044 
88988f8b78aSgm89044 }
89088f8b78aSgm89044 
89188f8b78aSgm89044 int
dca_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)89288f8b78aSgm89044 dca_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
89388f8b78aSgm89044 {
89488f8b78aSgm89044 	int		instance;
89588f8b78aSgm89044 	dca_t		*dca;
89688f8b78aSgm89044 	timeout_id_t	tid;
89788f8b78aSgm89044 
89888f8b78aSgm89044 	instance = ddi_get_instance(dip);
89988f8b78aSgm89044 
90088f8b78aSgm89044 	DBG(NULL, DMOD, "dca: in dca_detach() for %d", instance);
90188f8b78aSgm89044 
90288f8b78aSgm89044 	switch (cmd) {
90388f8b78aSgm89044 	case DDI_SUSPEND:
90488f8b78aSgm89044 		if ((dca = (dca_t *)ddi_get_driver_private(dip)) == NULL) {
90588f8b78aSgm89044 			dca_diperror(dip, "no soft state in detach");
90688f8b78aSgm89044 			return (DDI_FAILURE);
90788f8b78aSgm89044 		}
90888f8b78aSgm89044 		/* assumption: we won't be DDI_DETACHed until we return */
90988f8b78aSgm89044 		return (dca_suspend(dca));
91088f8b78aSgm89044 
91188f8b78aSgm89044 	case DDI_DETACH:
91288f8b78aSgm89044 		break;
91388f8b78aSgm89044 	default:
91488f8b78aSgm89044 		return (DDI_FAILURE);
91588f8b78aSgm89044 	}
91688f8b78aSgm89044 
91788f8b78aSgm89044 	if ((dca = (dca_t *)ddi_get_driver_private(dip)) == NULL) {
91888f8b78aSgm89044 		dca_diperror(dip, "no soft state in detach");
91988f8b78aSgm89044 		return (DDI_FAILURE);
92088f8b78aSgm89044 	}
92188f8b78aSgm89044 
92288f8b78aSgm89044 	/*
92388f8b78aSgm89044 	 * Unregister from kCF.
92488f8b78aSgm89044 	 * This needs to be done at the beginning of detach.
92588f8b78aSgm89044 	 */
9265c63ad82SToomas Soome 	if (WORKLIST(dca, MCR1)->dwl_prov != 0) {
927d8dd9913Sgm89044 		if (crypto_unregister_provider(
928d8dd9913Sgm89044 		    WORKLIST(dca, MCR1)->dwl_prov) != CRYPTO_SUCCESS) {
92988f8b78aSgm89044 			dca_error(dca, "unable to unregister MCR1 from kcf");
93088f8b78aSgm89044 			return (DDI_FAILURE);
93188f8b78aSgm89044 		}
93288f8b78aSgm89044 	}
93388f8b78aSgm89044 
9345c63ad82SToomas Soome 	if (WORKLIST(dca, MCR2)->dwl_prov != 0) {
935d8dd9913Sgm89044 		if (crypto_unregister_provider(
936d8dd9913Sgm89044 		    WORKLIST(dca, MCR2)->dwl_prov) != CRYPTO_SUCCESS) {
93788f8b78aSgm89044 			dca_error(dca, "unable to unregister MCR2 from kcf");
93888f8b78aSgm89044 			return (DDI_FAILURE);
93988f8b78aSgm89044 		}
94088f8b78aSgm89044 	}
94188f8b78aSgm89044 
94288f8b78aSgm89044 	/*
94388f8b78aSgm89044 	 * Cleanup the private context list. Once the
94488f8b78aSgm89044 	 * crypto_unregister_provider returns, it is safe to do so.
94588f8b78aSgm89044 	 */
94688f8b78aSgm89044 	dca_free_context_list(dca);
94788f8b78aSgm89044 
94888f8b78aSgm89044 	/* Cleanup the local random number pool */
94988f8b78aSgm89044 	dca_random_fini(dca);
95088f8b78aSgm89044 
95188f8b78aSgm89044 	/* send any jobs in the waitq back to kCF */
95288f8b78aSgm89044 	dca_rejectjobs(dca);
95388f8b78aSgm89044 
95488f8b78aSgm89044 	/* untimeout the timeouts */
95588f8b78aSgm89044 	mutex_enter(&dca->dca_intrlock);
95688f8b78aSgm89044 	tid = dca->dca_jobtid;
95788f8b78aSgm89044 	dca->dca_jobtid = 0;
95888f8b78aSgm89044 	mutex_exit(&dca->dca_intrlock);
95988f8b78aSgm89044 	if (tid) {
96088f8b78aSgm89044 		(void) untimeout(tid);
96188f8b78aSgm89044 	}
96288f8b78aSgm89044 
96388f8b78aSgm89044 	/* disable device interrupts */
96488f8b78aSgm89044 	CLRBIT(dca, CSR_DMACTL, DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE);
96588f8b78aSgm89044 
96688f8b78aSgm89044 	/* unregister interrupt handlers */
96788f8b78aSgm89044 	ddi_remove_intr(dip, 0, dca->dca_icookie);
96888f8b78aSgm89044 
96988f8b78aSgm89044 	/* release our regs handle */
97088f8b78aSgm89044 	ddi_regs_map_free(&dca->dca_regs_handle);
97188f8b78aSgm89044 
97288f8b78aSgm89044 	/* toss out kstats */
97388f8b78aSgm89044 	if (dca->dca_intrstats) {
97488f8b78aSgm89044 		kstat_delete(dca->dca_intrstats);
97588f8b78aSgm89044 	}
97688f8b78aSgm89044 	if (dca->dca_ksp) {
97788f8b78aSgm89044 		kstat_delete(dca->dca_ksp);
97888f8b78aSgm89044 	}
97988f8b78aSgm89044 
98088f8b78aSgm89044 	mutex_destroy(&dca->dca_intrlock);
98188f8b78aSgm89044 	dca_uninit(dca);
98288f8b78aSgm89044 
98388f8b78aSgm89044 	/* finalize FMA */
98488f8b78aSgm89044 	dca_fma_fini(dca);
98588f8b78aSgm89044 
98688f8b78aSgm89044 	ddi_soft_state_free(dca_state, instance);
98788f8b78aSgm89044 
98888f8b78aSgm89044 	return (DDI_SUCCESS);
98988f8b78aSgm89044 }
99088f8b78aSgm89044 
99188f8b78aSgm89044 int
dca_resume(dca_t * dca)99288f8b78aSgm89044 dca_resume(dca_t *dca)
99388f8b78aSgm89044 {
99488f8b78aSgm89044 	ddi_acc_handle_t	pci;
99588f8b78aSgm89044 
99688f8b78aSgm89044 	if (pci_config_setup(dca->dca_dip, &pci) != DDI_SUCCESS) {
99788f8b78aSgm89044 		dca_error(dca, "unable to setup PCI config handle");
99888f8b78aSgm89044 		return (DDI_FAILURE);
99988f8b78aSgm89044 	}
100088f8b78aSgm89044 
100188f8b78aSgm89044 	/*
100288f8b78aSgm89044 	 * Reprogram registers in PCI configuration space.
100388f8b78aSgm89044 	 */
100488f8b78aSgm89044 
100588f8b78aSgm89044 	/* Broadcom-specific timers -- we disable them. */
100688f8b78aSgm89044 	pci_config_put8(pci, PCI_TRDYTO, 0);
100788f8b78aSgm89044 	pci_config_put8(pci, PCI_RETRIES, 0);
100888f8b78aSgm89044 
100988f8b78aSgm89044 	/* initialize PCI access settings */
101088f8b78aSgm89044 	pci_config_put16(pci, PCI_COMM, PCICOMM_SEE |
101188f8b78aSgm89044 	    PCICOMM_PEE | PCICOMM_BME | PCICOMM_MAE);
101288f8b78aSgm89044 
101388f8b78aSgm89044 	/* set up our PCI latency timer */
101488f8b78aSgm89044 	pci_config_put8(pci, PCI_LATTMR, 0x40);
101588f8b78aSgm89044 
101688f8b78aSgm89044 	pci_config_teardown(&pci);
101788f8b78aSgm89044 
101888f8b78aSgm89044 	if (dca_reset(dca, 0) < 0) {
101988f8b78aSgm89044 		dca_error(dca, "unable to reset device during resume");
102088f8b78aSgm89044 		return (DDI_FAILURE);
102188f8b78aSgm89044 	}
102288f8b78aSgm89044 
102388f8b78aSgm89044 	/*
102488f8b78aSgm89044 	 * Now restore the card-specific CSRs.
102588f8b78aSgm89044 	 */
102688f8b78aSgm89044 
102788f8b78aSgm89044 	/* restore endianness settings */
102888f8b78aSgm89044 	PUTCSR(dca, CSR_DMACTL, DMACTL_BE32 | DMACTL_BE64);
102988f8b78aSgm89044 	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
103088f8b78aSgm89044 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
103188f8b78aSgm89044 		return (DDI_FAILURE);
103288f8b78aSgm89044 
103388f8b78aSgm89044 	/* restore interrupt enables */
10343383b6ddSqs148142 	if (dca->dca_devid == 0x5825) {
10353383b6ddSqs148142 		/* for 5825 set 256 byte read size to improve performance */
10363383b6ddSqs148142 		SETBIT(dca, CSR_DMACTL,
10373383b6ddSqs148142 		    DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE | DMACTL_RD256);
10383383b6ddSqs148142 	} else {
10393383b6ddSqs148142 		SETBIT(dca, CSR_DMACTL,
10403383b6ddSqs148142 		    DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE);
10413383b6ddSqs148142 	}
104288f8b78aSgm89044 	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
104388f8b78aSgm89044 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
104488f8b78aSgm89044 		return (DDI_FAILURE);
104588f8b78aSgm89044 
104688f8b78aSgm89044 	/* resume scheduling jobs on the device */
104788f8b78aSgm89044 	dca_undrain(dca);
104888f8b78aSgm89044 
104988f8b78aSgm89044 	return (DDI_SUCCESS);
105088f8b78aSgm89044 }
105188f8b78aSgm89044 
105288f8b78aSgm89044 int
dca_suspend(dca_t * dca)105388f8b78aSgm89044 dca_suspend(dca_t *dca)
105488f8b78aSgm89044 {
105588f8b78aSgm89044 	if ((dca_drain(dca)) != 0) {
105688f8b78aSgm89044 		return (DDI_FAILURE);
105788f8b78aSgm89044 	}
105888f8b78aSgm89044 	if (dca_reset(dca, 0) < 0) {
105988f8b78aSgm89044 		dca_error(dca, "unable to reset device during suspend");
106088f8b78aSgm89044 		return (DDI_FAILURE);
106188f8b78aSgm89044 	}
106288f8b78aSgm89044 	return (DDI_SUCCESS);
106388f8b78aSgm89044 }
106488f8b78aSgm89044 
106588f8b78aSgm89044 /*
106688f8b78aSgm89044  * Hardware access stuff.
106788f8b78aSgm89044  */
106888f8b78aSgm89044 int
dca_reset(dca_t * dca,int failreset)106988f8b78aSgm89044 dca_reset(dca_t *dca, int failreset)
107088f8b78aSgm89044 {
107188f8b78aSgm89044 	int i;
107288f8b78aSgm89044 
107388f8b78aSgm89044 	if (dca->dca_regs_handle == NULL) {
107488f8b78aSgm89044 		return (-1);
107588f8b78aSgm89044 	}
107688f8b78aSgm89044 
107788f8b78aSgm89044 	PUTCSR(dca, CSR_DMACTL, DMACTL_RESET);
107888f8b78aSgm89044 	if (!failreset) {
107988f8b78aSgm89044 		if (dca_check_acc_handle(dca, dca->dca_regs_handle,
108088f8b78aSgm89044 		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
108188f8b78aSgm89044 			return (-1);
108288f8b78aSgm89044 	}
108388f8b78aSgm89044 
108488f8b78aSgm89044 	/* now wait for a reset */
108588f8b78aSgm89044 	for (i = 1; i < 100; i++) {
108688f8b78aSgm89044 		uint32_t	dmactl;
108788f8b78aSgm89044 		drv_usecwait(100);
108888f8b78aSgm89044 		dmactl = GETCSR(dca, CSR_DMACTL);
108988f8b78aSgm89044 		if (!failreset) {
109088f8b78aSgm89044 			if (dca_check_acc_handle(dca, dca->dca_regs_handle,
109188f8b78aSgm89044 			    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
109288f8b78aSgm89044 				return (-1);
109388f8b78aSgm89044 		}
109488f8b78aSgm89044 		if ((dmactl & DMACTL_RESET) == 0) {
109588f8b78aSgm89044 			DBG(dca, DCHATTY, "reset in %d usec", i * 100);
109688f8b78aSgm89044 			return (0);
109788f8b78aSgm89044 		}
109888f8b78aSgm89044 	}
109988f8b78aSgm89044 	if (!failreset) {
110088f8b78aSgm89044 		dca_failure(dca, DDI_DEVICE_FAULT,
110188f8b78aSgm89044 		    DCA_FM_ECLASS_NONE, dca_ena(0), CRYPTO_DEVICE_ERROR,
110288f8b78aSgm89044 		    "timeout waiting for reset after %d usec", i * 100);
110388f8b78aSgm89044 	}
110488f8b78aSgm89044 	return (-1);
110588f8b78aSgm89044 }
110688f8b78aSgm89044 
110788f8b78aSgm89044 int
dca_initworklist(dca_t * dca,dca_worklist_t * wlp)110888f8b78aSgm89044 dca_initworklist(dca_t *dca, dca_worklist_t *wlp)
110988f8b78aSgm89044 {
111088f8b78aSgm89044 	int	i;
111188f8b78aSgm89044 	int	reqprealloc = wlp->dwl_hiwater + (MAXWORK * MAXREQSPERMCR);
111288f8b78aSgm89044 
111388f8b78aSgm89044 	/*
111488f8b78aSgm89044 	 * Set up work queue.
111588f8b78aSgm89044 	 */
111688f8b78aSgm89044 	mutex_init(&wlp->dwl_lock, NULL, MUTEX_DRIVER, dca->dca_icookie);
111788f8b78aSgm89044 	mutex_init(&wlp->dwl_freereqslock, NULL, MUTEX_DRIVER,
111888f8b78aSgm89044 	    dca->dca_icookie);
11193383b6ddSqs148142 	mutex_init(&wlp->dwl_freelock, NULL, MUTEX_DRIVER, dca->dca_icookie);
112088f8b78aSgm89044 	cv_init(&wlp->dwl_cv, NULL, CV_DRIVER, NULL);
112188f8b78aSgm89044 
112288f8b78aSgm89044 	mutex_enter(&wlp->dwl_lock);
112388f8b78aSgm89044 
112488f8b78aSgm89044 	dca_initq(&wlp->dwl_freereqs);
112588f8b78aSgm89044 	dca_initq(&wlp->dwl_waitq);
112688f8b78aSgm89044 	dca_initq(&wlp->dwl_freework);
112788f8b78aSgm89044 	dca_initq(&wlp->dwl_runq);
112888f8b78aSgm89044 
112988f8b78aSgm89044 	for (i = 0; i < MAXWORK; i++) {
113088f8b78aSgm89044 		dca_work_t		*workp;
113188f8b78aSgm89044 
113288f8b78aSgm89044 		if ((workp = dca_newwork(dca)) == NULL) {
113388f8b78aSgm89044 			dca_error(dca, "unable to allocate work");
113488f8b78aSgm89044 			mutex_exit(&wlp->dwl_lock);
113588f8b78aSgm89044 			return (DDI_FAILURE);
113688f8b78aSgm89044 		}
113788f8b78aSgm89044 		workp->dw_wlp = wlp;
113888f8b78aSgm89044 		dca_freework(workp);
113988f8b78aSgm89044 	}
114088f8b78aSgm89044 	mutex_exit(&wlp->dwl_lock);
114188f8b78aSgm89044 
114288f8b78aSgm89044 	for (i = 0; i < reqprealloc; i++) {
114388f8b78aSgm89044 		dca_request_t *reqp;
114488f8b78aSgm89044 
114588f8b78aSgm89044 		if ((reqp = dca_newreq(dca)) == NULL) {
114688f8b78aSgm89044 			dca_error(dca, "unable to allocate request");
114788f8b78aSgm89044 			return (DDI_FAILURE);
114888f8b78aSgm89044 		}
114988f8b78aSgm89044 		reqp->dr_dca = dca;
115088f8b78aSgm89044 		reqp->dr_wlp = wlp;
115188f8b78aSgm89044 		dca_freereq(reqp);
115288f8b78aSgm89044 	}
115388f8b78aSgm89044 	return (DDI_SUCCESS);
115488f8b78aSgm89044 }
115588f8b78aSgm89044 
115688f8b78aSgm89044 int
dca_init(dca_t * dca)115788f8b78aSgm89044 dca_init(dca_t *dca)
115888f8b78aSgm89044 {
115988f8b78aSgm89044 	dca_worklist_t		*wlp;
116088f8b78aSgm89044 
116188f8b78aSgm89044 	/* Initialize the private context list and the corresponding lock. */
116288f8b78aSgm89044 	mutex_init(&dca->dca_ctx_list_lock, NULL, MUTEX_DRIVER, NULL);
116388f8b78aSgm89044 	dca_initq(&dca->dca_ctx_list);
116488f8b78aSgm89044 
116588f8b78aSgm89044 	/*
116688f8b78aSgm89044 	 * MCR1 algorithms.
116788f8b78aSgm89044 	 */
116888f8b78aSgm89044 	wlp = WORKLIST(dca, MCR1);
116988f8b78aSgm89044 	(void) sprintf(wlp->dwl_name, "dca%d:mcr1",
117088f8b78aSgm89044 	    ddi_get_instance(dca->dca_dip));
117188f8b78aSgm89044 	wlp->dwl_lowater = ddi_getprop(DDI_DEV_T_ANY,
117288f8b78aSgm89044 	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
117388f8b78aSgm89044 	    "mcr1_lowater", MCR1LOWATER);
117488f8b78aSgm89044 	wlp->dwl_hiwater = ddi_getprop(DDI_DEV_T_ANY,
117588f8b78aSgm89044 	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
117688f8b78aSgm89044 	    "mcr1_hiwater", MCR1HIWATER);
117788f8b78aSgm89044 	wlp->dwl_reqspermcr = min(ddi_getprop(DDI_DEV_T_ANY,
117888f8b78aSgm89044 	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
117988f8b78aSgm89044 	    "mcr1_maxreqs", MCR1MAXREQS), MAXREQSPERMCR);
118088f8b78aSgm89044 	wlp->dwl_dca = dca;
118188f8b78aSgm89044 	wlp->dwl_mcr = MCR1;
118288f8b78aSgm89044 	if (dca_initworklist(dca, wlp) != DDI_SUCCESS) {
118388f8b78aSgm89044 		return (DDI_FAILURE);
118488f8b78aSgm89044 	}
118588f8b78aSgm89044 
118688f8b78aSgm89044 	/*
118788f8b78aSgm89044 	 * MCR2 algorithms.
118888f8b78aSgm89044 	 */
118988f8b78aSgm89044 	wlp = WORKLIST(dca, MCR2);
119088f8b78aSgm89044 	(void) sprintf(wlp->dwl_name, "dca%d:mcr2",
119188f8b78aSgm89044 	    ddi_get_instance(dca->dca_dip));
119288f8b78aSgm89044 	wlp->dwl_lowater = ddi_getprop(DDI_DEV_T_ANY,
119388f8b78aSgm89044 	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
119488f8b78aSgm89044 	    "mcr2_lowater", MCR2LOWATER);
119588f8b78aSgm89044 	wlp->dwl_hiwater = ddi_getprop(DDI_DEV_T_ANY,
119688f8b78aSgm89044 	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
119788f8b78aSgm89044 	    "mcr2_hiwater", MCR2HIWATER);
119888f8b78aSgm89044 	wlp->dwl_reqspermcr = min(ddi_getprop(DDI_DEV_T_ANY,
119988f8b78aSgm89044 	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
120088f8b78aSgm89044 	    "mcr2_maxreqs", MCR2MAXREQS), MAXREQSPERMCR);
120188f8b78aSgm89044 	wlp->dwl_dca = dca;
120288f8b78aSgm89044 	wlp->dwl_mcr = MCR2;
120388f8b78aSgm89044 	if (dca_initworklist(dca, wlp) != DDI_SUCCESS) {
120488f8b78aSgm89044 		return (DDI_FAILURE);
120588f8b78aSgm89044 	}
120688f8b78aSgm89044 	return (DDI_SUCCESS);
120788f8b78aSgm89044 }
120888f8b78aSgm89044 
120988f8b78aSgm89044 /*
121088f8b78aSgm89044  * Uninitialize worklists.  This routine should only be called when no
121188f8b78aSgm89044  * active jobs (hence DMA mappings) exist.  One way to ensure this is
121288f8b78aSgm89044  * to unregister from kCF before calling this routine.  (This is done
121388f8b78aSgm89044  * e.g. in detach(9e).)
121488f8b78aSgm89044  */
121588f8b78aSgm89044 void
dca_uninit(dca_t * dca)121688f8b78aSgm89044 dca_uninit(dca_t *dca)
121788f8b78aSgm89044 {
121888f8b78aSgm89044 	int	mcr;
121988f8b78aSgm89044 
122088f8b78aSgm89044 	mutex_destroy(&dca->dca_ctx_list_lock);
122188f8b78aSgm89044 
122288f8b78aSgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
122388f8b78aSgm89044 		dca_worklist_t	*wlp = WORKLIST(dca, mcr);
122488f8b78aSgm89044 		dca_work_t	*workp;
122588f8b78aSgm89044 		dca_request_t	*reqp;
122688f8b78aSgm89044 
122788f8b78aSgm89044 		if (dca->dca_regs_handle == NULL) {
122888f8b78aSgm89044 			continue;
122988f8b78aSgm89044 		}
123088f8b78aSgm89044 
123188f8b78aSgm89044 		mutex_enter(&wlp->dwl_lock);
123288f8b78aSgm89044 		while ((workp = dca_getwork(dca, mcr)) != NULL) {
123388f8b78aSgm89044 			dca_destroywork(workp);
123488f8b78aSgm89044 		}
123588f8b78aSgm89044 		mutex_exit(&wlp->dwl_lock);
123688f8b78aSgm89044 		while ((reqp = dca_getreq(dca, mcr, 0)) != NULL) {
123788f8b78aSgm89044 			dca_destroyreq(reqp);
123888f8b78aSgm89044 		}
123988f8b78aSgm89044 
124088f8b78aSgm89044 		mutex_destroy(&wlp->dwl_lock);
124188f8b78aSgm89044 		mutex_destroy(&wlp->dwl_freereqslock);
12423383b6ddSqs148142 		mutex_destroy(&wlp->dwl_freelock);
124388f8b78aSgm89044 		cv_destroy(&wlp->dwl_cv);
12445c63ad82SToomas Soome 		wlp->dwl_prov = 0;
124588f8b78aSgm89044 	}
124688f8b78aSgm89044 }
124788f8b78aSgm89044 
124888f8b78aSgm89044 static void
dca_enlist2(dca_listnode_t * q,dca_listnode_t * node,kmutex_t * lock)124988f8b78aSgm89044 dca_enlist2(dca_listnode_t *q, dca_listnode_t *node, kmutex_t *lock)
125088f8b78aSgm89044 {
125188f8b78aSgm89044 	if (!q || !node)
125288f8b78aSgm89044 		return;
125388f8b78aSgm89044 
125488f8b78aSgm89044 	mutex_enter(lock);
125588f8b78aSgm89044 	node->dl_next2 = q;
125688f8b78aSgm89044 	node->dl_prev2 = q->dl_prev2;
125788f8b78aSgm89044 	node->dl_next2->dl_prev2 = node;
125888f8b78aSgm89044 	node->dl_prev2->dl_next2 = node;
125988f8b78aSgm89044 	mutex_exit(lock);
126088f8b78aSgm89044 }
126188f8b78aSgm89044 
126288f8b78aSgm89044 static void
dca_rmlist2(dca_listnode_t * node,kmutex_t * lock)126388f8b78aSgm89044 dca_rmlist2(dca_listnode_t *node, kmutex_t *lock)
126488f8b78aSgm89044 {
126588f8b78aSgm89044 	if (!node)
126688f8b78aSgm89044 		return;
126788f8b78aSgm89044 
126888f8b78aSgm89044 	mutex_enter(lock);
126988f8b78aSgm89044 	node->dl_next2->dl_prev2 = node->dl_prev2;
127088f8b78aSgm89044 	node->dl_prev2->dl_next2 = node->dl_next2;
127188f8b78aSgm89044 	node->dl_next2 = NULL;
127288f8b78aSgm89044 	node->dl_prev2 = NULL;
127388f8b78aSgm89044 	mutex_exit(lock);
127488f8b78aSgm89044 }
127588f8b78aSgm89044 
127688f8b78aSgm89044 static dca_listnode_t *
dca_delist2(dca_listnode_t * q,kmutex_t * lock)127788f8b78aSgm89044 dca_delist2(dca_listnode_t *q, kmutex_t *lock)
127888f8b78aSgm89044 {
127988f8b78aSgm89044 	dca_listnode_t *node;
128088f8b78aSgm89044 
128188f8b78aSgm89044 	mutex_enter(lock);
128288f8b78aSgm89044 	if ((node = q->dl_next2) == q) {
128388f8b78aSgm89044 		mutex_exit(lock);
128488f8b78aSgm89044 		return (NULL);
128588f8b78aSgm89044 	}
128688f8b78aSgm89044 
128788f8b78aSgm89044 	node->dl_next2->dl_prev2 = node->dl_prev2;
128888f8b78aSgm89044 	node->dl_prev2->dl_next2 = node->dl_next2;
128988f8b78aSgm89044 	node->dl_next2 = NULL;
129088f8b78aSgm89044 	node->dl_prev2 = NULL;
129188f8b78aSgm89044 	mutex_exit(lock);
129288f8b78aSgm89044 
129388f8b78aSgm89044 	return (node);
129488f8b78aSgm89044 }
129588f8b78aSgm89044 
129688f8b78aSgm89044 void
dca_initq(dca_listnode_t * q)129788f8b78aSgm89044 dca_initq(dca_listnode_t *q)
129888f8b78aSgm89044 {
129988f8b78aSgm89044 	q->dl_next = q;
130088f8b78aSgm89044 	q->dl_prev = q;
130188f8b78aSgm89044 	q->dl_next2 = q;
130288f8b78aSgm89044 	q->dl_prev2 = q;
130388f8b78aSgm89044 }
130488f8b78aSgm89044 
130588f8b78aSgm89044 void
dca_enqueue(dca_listnode_t * q,dca_listnode_t * node)130688f8b78aSgm89044 dca_enqueue(dca_listnode_t *q, dca_listnode_t *node)
130788f8b78aSgm89044 {
130888f8b78aSgm89044 	/*
130988f8b78aSgm89044 	 * Enqueue submits at the "tail" of the list, i.e. just
131088f8b78aSgm89044 	 * behind the sentinel.
131188f8b78aSgm89044 	 */
131288f8b78aSgm89044 	node->dl_next = q;
131388f8b78aSgm89044 	node->dl_prev = q->dl_prev;
131488f8b78aSgm89044 	node->dl_next->dl_prev = node;
131588f8b78aSgm89044 	node->dl_prev->dl_next = node;
131688f8b78aSgm89044 }
131788f8b78aSgm89044 
131888f8b78aSgm89044 void
dca_rmqueue(dca_listnode_t * node)131988f8b78aSgm89044 dca_rmqueue(dca_listnode_t *node)
132088f8b78aSgm89044 {
132188f8b78aSgm89044 	node->dl_next->dl_prev = node->dl_prev;
132288f8b78aSgm89044 	node->dl_prev->dl_next = node->dl_next;
132388f8b78aSgm89044 	node->dl_next = NULL;
132488f8b78aSgm89044 	node->dl_prev = NULL;
132588f8b78aSgm89044 }
132688f8b78aSgm89044 
132788f8b78aSgm89044 dca_listnode_t *
dca_dequeue(dca_listnode_t * q)132888f8b78aSgm89044 dca_dequeue(dca_listnode_t *q)
132988f8b78aSgm89044 {
133088f8b78aSgm89044 	dca_listnode_t *node;
133188f8b78aSgm89044 	/*
133288f8b78aSgm89044 	 * Dequeue takes from the "head" of the list, i.e. just after
133388f8b78aSgm89044 	 * the sentinel.
133488f8b78aSgm89044 	 */
133588f8b78aSgm89044 	if ((node = q->dl_next) == q) {
133688f8b78aSgm89044 		/* queue is empty */
133788f8b78aSgm89044 		return (NULL);
133888f8b78aSgm89044 	}
133988f8b78aSgm89044 	dca_rmqueue(node);
134088f8b78aSgm89044 	return (node);
134188f8b78aSgm89044 }
134288f8b78aSgm89044 
134388f8b78aSgm89044 /* this is the opposite of dequeue, it takes things off in LIFO order */
134488f8b78aSgm89044 dca_listnode_t *
dca_unqueue(dca_listnode_t * q)134588f8b78aSgm89044 dca_unqueue(dca_listnode_t *q)
134688f8b78aSgm89044 {
134788f8b78aSgm89044 	dca_listnode_t *node;
134888f8b78aSgm89044 	/*
134988f8b78aSgm89044 	 * unqueue takes from the "tail" of the list, i.e. just before
135088f8b78aSgm89044 	 * the sentinel.
135188f8b78aSgm89044 	 */
1352d8dd9913Sgm89044 	if ((node = q->dl_prev) == q) {
135388f8b78aSgm89044 		/* queue is empty */
135488f8b78aSgm89044 		return (NULL);
135588f8b78aSgm89044 	}
135688f8b78aSgm89044 	dca_rmqueue(node);
135788f8b78aSgm89044 	return (node);
135888f8b78aSgm89044 }
135988f8b78aSgm89044 
136088f8b78aSgm89044 dca_listnode_t *
dca_peekqueue(dca_listnode_t * q)136188f8b78aSgm89044 dca_peekqueue(dca_listnode_t *q)
136288f8b78aSgm89044 {
136388f8b78aSgm89044 	dca_listnode_t *node;
136488f8b78aSgm89044 
136588f8b78aSgm89044 	if ((node = q->dl_next) == q) {
136688f8b78aSgm89044 		return (NULL);
136788f8b78aSgm89044 	} else {
136888f8b78aSgm89044 		return (node);
136988f8b78aSgm89044 	}
137088f8b78aSgm89044 }
137188f8b78aSgm89044 
137288f8b78aSgm89044 /*
137388f8b78aSgm89044  * Interrupt service routine.
137488f8b78aSgm89044  */
137588f8b78aSgm89044 uint_t
dca_intr(char * arg)137688f8b78aSgm89044 dca_intr(char *arg)
137788f8b78aSgm89044 {
137888f8b78aSgm89044 	dca_t		*dca = (dca_t *)arg;
137988f8b78aSgm89044 	uint32_t	status;
138088f8b78aSgm89044 
138188f8b78aSgm89044 	mutex_enter(&dca->dca_intrlock);
138288f8b78aSgm89044 	status = GETCSR(dca, CSR_DMASTAT);
138388f8b78aSgm89044 	PUTCSR(dca, CSR_DMASTAT, status & DMASTAT_INTERRUPTS);
138488f8b78aSgm89044 	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
138588f8b78aSgm89044 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
138688f8b78aSgm89044 		mutex_exit(&dca->dca_intrlock);
138788f8b78aSgm89044 		return ((uint_t)DDI_FAILURE);
138888f8b78aSgm89044 	}
138988f8b78aSgm89044 
139088f8b78aSgm89044 	DBG(dca, DINTR, "interrupted, status = 0x%x!", status);
139188f8b78aSgm89044 
139288f8b78aSgm89044 	if ((status & DMASTAT_INTERRUPTS) == 0) {
139388f8b78aSgm89044 		/* increment spurious interrupt kstat */
139488f8b78aSgm89044 		if (dca->dca_intrstats) {
139588f8b78aSgm89044 			KIOIP(dca)->intrs[KSTAT_INTR_SPURIOUS]++;
139688f8b78aSgm89044 		}
139788f8b78aSgm89044 		mutex_exit(&dca->dca_intrlock);
139888f8b78aSgm89044 		return (DDI_INTR_UNCLAIMED);
139988f8b78aSgm89044 	}
140088f8b78aSgm89044 
140188f8b78aSgm89044 	if (dca->dca_intrstats) {
140288f8b78aSgm89044 		KIOIP(dca)->intrs[KSTAT_INTR_HARD]++;
140388f8b78aSgm89044 	}
140488f8b78aSgm89044 	if (status & DMASTAT_MCR1INT) {
140588f8b78aSgm89044 		DBG(dca, DINTR, "MCR1 interrupted");
140688f8b78aSgm89044 		mutex_enter(&(WORKLIST(dca, MCR1)->dwl_lock));
140788f8b78aSgm89044 		dca_schedule(dca, MCR1);
140888f8b78aSgm89044 		dca_reclaim(dca, MCR1);
140988f8b78aSgm89044 		mutex_exit(&(WORKLIST(dca, MCR1)->dwl_lock));
141088f8b78aSgm89044 	}
141188f8b78aSgm89044 
141288f8b78aSgm89044 	if (status & DMASTAT_MCR2INT) {
141388f8b78aSgm89044 		DBG(dca, DINTR, "MCR2 interrupted");
141488f8b78aSgm89044 		mutex_enter(&(WORKLIST(dca, MCR2)->dwl_lock));
141588f8b78aSgm89044 		dca_schedule(dca, MCR2);
141688f8b78aSgm89044 		dca_reclaim(dca, MCR2);
141788f8b78aSgm89044 		mutex_exit(&(WORKLIST(dca, MCR2)->dwl_lock));
141888f8b78aSgm89044 	}
141988f8b78aSgm89044 
142088f8b78aSgm89044 	if (status & DMASTAT_ERRINT) {
142188f8b78aSgm89044 		uint32_t	erraddr;
142288f8b78aSgm89044 		erraddr = GETCSR(dca, CSR_DMAEA);
142388f8b78aSgm89044 		mutex_exit(&dca->dca_intrlock);
142488f8b78aSgm89044 
142588f8b78aSgm89044 		/*
142688f8b78aSgm89044 		 * bit 1 of the error address indicates failure during
142788f8b78aSgm89044 		 * read if set, during write otherwise.
142888f8b78aSgm89044 		 */
142988f8b78aSgm89044 		dca_failure(dca, DDI_DEVICE_FAULT,
143088f8b78aSgm89044 		    DCA_FM_ECLASS_HW_DEVICE, dca_ena(0), CRYPTO_DEVICE_ERROR,
143188f8b78aSgm89044 		    "DMA master access error %s address 0x%x",
143288f8b78aSgm89044 		    erraddr & 0x1 ? "reading" : "writing", erraddr & ~1);
143388f8b78aSgm89044 		return (DDI_INTR_CLAIMED);
143488f8b78aSgm89044 	}
143588f8b78aSgm89044 
143688f8b78aSgm89044 	mutex_exit(&dca->dca_intrlock);
143788f8b78aSgm89044 
143888f8b78aSgm89044 	return (DDI_INTR_CLAIMED);
143988f8b78aSgm89044 }
144088f8b78aSgm89044 
144188f8b78aSgm89044 /*
144288f8b78aSgm89044  * Reverse a string of bytes from s1 into s2.  The reversal happens
144388f8b78aSgm89044  * from the tail of s1.  If len1 < len2, then null bytes will be
144488f8b78aSgm89044  * padded to the end of s2.  If len2 < len1, then (presumably null)
144588f8b78aSgm89044  * bytes will be dropped from the start of s1.
144688f8b78aSgm89044  *
144788f8b78aSgm89044  * The rationale here is that when s1 (source) is shorter, then we
144888f8b78aSgm89044  * are reversing from big-endian ordering, into device ordering, and
144988f8b78aSgm89044  * want to add some extra nulls to the tail (MSB) side of the device.
145088f8b78aSgm89044  *
145188f8b78aSgm89044  * Similarly, when s2 (dest) is shorter, then we are truncating what
145288f8b78aSgm89044  * are presumably null MSB bits from the device.
145388f8b78aSgm89044  *
145488f8b78aSgm89044  * There is an expectation when reversing from the device back into
145588f8b78aSgm89044  * big-endian, that the number of bytes to reverse and the target size
145688f8b78aSgm89044  * will match, and no truncation or padding occurs.
145788f8b78aSgm89044  */
145888f8b78aSgm89044 void
dca_reverse(void * s1,void * s2,int len1,int len2)145988f8b78aSgm89044 dca_reverse(void *s1, void *s2, int len1, int len2)
146088f8b78aSgm89044 {
146188f8b78aSgm89044 	caddr_t	src, dst;
146288f8b78aSgm89044 
146388f8b78aSgm89044 	if (len1 == 0) {
146488f8b78aSgm89044 		if (len2) {
146588f8b78aSgm89044 			bzero(s2, len2);
146688f8b78aSgm89044 		}
146788f8b78aSgm89044 		return;
146888f8b78aSgm89044 	}
146988f8b78aSgm89044 	src = (caddr_t)s1 + len1 - 1;
147088f8b78aSgm89044 	dst = s2;
147188f8b78aSgm89044 	while ((src >= (caddr_t)s1) && (len2)) {
147288f8b78aSgm89044 		*dst++ = *src--;
147388f8b78aSgm89044 		len2--;
147488f8b78aSgm89044 	}
147588f8b78aSgm89044 	while (len2 > 0) {
147688f8b78aSgm89044 		*dst++ = 0;
147788f8b78aSgm89044 		len2--;
147888f8b78aSgm89044 	}
147988f8b78aSgm89044 }
148088f8b78aSgm89044 
148188f8b78aSgm89044 uint16_t
dca_padfull(int num)148288f8b78aSgm89044 dca_padfull(int num)
148388f8b78aSgm89044 {
148488f8b78aSgm89044 	if (num <= 512) {
148588f8b78aSgm89044 		return (BITS2BYTES(512));
148688f8b78aSgm89044 	}
148788f8b78aSgm89044 	if (num <= 768) {
148888f8b78aSgm89044 		return (BITS2BYTES(768));
148988f8b78aSgm89044 	}
149088f8b78aSgm89044 	if (num <= 1024) {
149188f8b78aSgm89044 		return (BITS2BYTES(1024));
149288f8b78aSgm89044 	}
149388f8b78aSgm89044 	if (num <= 1536) {
149488f8b78aSgm89044 		return (BITS2BYTES(1536));
149588f8b78aSgm89044 	}
149688f8b78aSgm89044 	if (num <= 2048) {
149788f8b78aSgm89044 		return (BITS2BYTES(2048));
149888f8b78aSgm89044 	}
149988f8b78aSgm89044 	return (0);
150088f8b78aSgm89044 }
150188f8b78aSgm89044 
150288f8b78aSgm89044 uint16_t
dca_padhalf(int num)150388f8b78aSgm89044 dca_padhalf(int num)
150488f8b78aSgm89044 {
150588f8b78aSgm89044 	if (num <= 256) {
150688f8b78aSgm89044 		return (BITS2BYTES(256));
150788f8b78aSgm89044 	}
150888f8b78aSgm89044 	if (num <= 384) {
150988f8b78aSgm89044 		return (BITS2BYTES(384));
151088f8b78aSgm89044 	}
151188f8b78aSgm89044 	if (num <= 512) {
151288f8b78aSgm89044 		return (BITS2BYTES(512));
151388f8b78aSgm89044 	}
151488f8b78aSgm89044 	if (num <= 768) {
151588f8b78aSgm89044 		return (BITS2BYTES(768));
151688f8b78aSgm89044 	}
151788f8b78aSgm89044 	if (num <= 1024) {
151888f8b78aSgm89044 		return (BITS2BYTES(1024));
151988f8b78aSgm89044 	}
152088f8b78aSgm89044 	return (0);
152188f8b78aSgm89044 }
152288f8b78aSgm89044 
152388f8b78aSgm89044 dca_work_t *
dca_newwork(dca_t * dca)152488f8b78aSgm89044 dca_newwork(dca_t *dca)
152588f8b78aSgm89044 {
152688f8b78aSgm89044 	dca_work_t		*workp;
152788f8b78aSgm89044 	size_t			size;
152888f8b78aSgm89044 	ddi_dma_cookie_t	c;
152988f8b78aSgm89044 	unsigned		nc;
153088f8b78aSgm89044 	int			rv;
153188f8b78aSgm89044 
153288f8b78aSgm89044 	workp = kmem_zalloc(sizeof (dca_work_t), KM_SLEEP);
153388f8b78aSgm89044 
153488f8b78aSgm89044 	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
153588f8b78aSgm89044 	    DDI_DMA_SLEEP, NULL, &workp->dw_mcr_dmah);
153688f8b78aSgm89044 	if (rv != 0) {
153788f8b78aSgm89044 		dca_error(dca, "unable to alloc MCR DMA handle");
153888f8b78aSgm89044 		dca_destroywork(workp);
153988f8b78aSgm89044 		return (NULL);
154088f8b78aSgm89044 	}
154188f8b78aSgm89044 
154288f8b78aSgm89044 	rv = ddi_dma_mem_alloc(workp->dw_mcr_dmah,
154388f8b78aSgm89044 	    ROUNDUP(MCR_SIZE, dca->dca_pagesize),
154488f8b78aSgm89044 	    &dca_devattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
154588f8b78aSgm89044 	    &workp->dw_mcr_kaddr, &size, &workp->dw_mcr_acch);
154688f8b78aSgm89044 	if (rv != 0) {
154788f8b78aSgm89044 		dca_error(dca, "unable to alloc MCR DMA memory");
154888f8b78aSgm89044 		dca_destroywork(workp);
154988f8b78aSgm89044 		return (NULL);
155088f8b78aSgm89044 	}
155188f8b78aSgm89044 
155288f8b78aSgm89044 	rv = ddi_dma_addr_bind_handle(workp->dw_mcr_dmah, NULL,
155388f8b78aSgm89044 	    workp->dw_mcr_kaddr, size, DDI_DMA_CONSISTENT | DDI_DMA_RDWR,
155488f8b78aSgm89044 	    DDI_DMA_SLEEP, NULL, &c, &nc);
155588f8b78aSgm89044 	if (rv != DDI_DMA_MAPPED) {
155688f8b78aSgm89044 		dca_error(dca, "unable to map MCR DMA memory");
155788f8b78aSgm89044 		dca_destroywork(workp);
155888f8b78aSgm89044 		return (NULL);
155988f8b78aSgm89044 	}
156088f8b78aSgm89044 
156188f8b78aSgm89044 	workp->dw_mcr_paddr = c.dmac_address;
156288f8b78aSgm89044 	return (workp);
156388f8b78aSgm89044 }
156488f8b78aSgm89044 
156588f8b78aSgm89044 void
dca_destroywork(dca_work_t * workp)156688f8b78aSgm89044 dca_destroywork(dca_work_t *workp)
156788f8b78aSgm89044 {
156888f8b78aSgm89044 	if (workp->dw_mcr_paddr) {
156988f8b78aSgm89044 		(void) ddi_dma_unbind_handle(workp->dw_mcr_dmah);
157088f8b78aSgm89044 	}
157188f8b78aSgm89044 	if (workp->dw_mcr_acch) {
157288f8b78aSgm89044 		ddi_dma_mem_free(&workp->dw_mcr_acch);
157388f8b78aSgm89044 	}
157488f8b78aSgm89044 	if (workp->dw_mcr_dmah) {
157588f8b78aSgm89044 		ddi_dma_free_handle(&workp->dw_mcr_dmah);
157688f8b78aSgm89044 	}
157788f8b78aSgm89044 	kmem_free(workp, sizeof (dca_work_t));
157888f8b78aSgm89044 }
157988f8b78aSgm89044 
158088f8b78aSgm89044 dca_request_t *
dca_newreq(dca_t * dca)158188f8b78aSgm89044 dca_newreq(dca_t *dca)
158288f8b78aSgm89044 {
158388f8b78aSgm89044 	dca_request_t		*reqp;
158488f8b78aSgm89044 	size_t			size;
158588f8b78aSgm89044 	ddi_dma_cookie_t	c;
158688f8b78aSgm89044 	unsigned		nc;
158788f8b78aSgm89044 	int			rv;
158888f8b78aSgm89044 	int			n_chain = 0;
158988f8b78aSgm89044 
159088f8b78aSgm89044 	size = (DESC_SIZE * MAXFRAGS) + CTX_MAXLENGTH;
159188f8b78aSgm89044 
159288f8b78aSgm89044 	reqp = kmem_zalloc(sizeof (dca_request_t), KM_SLEEP);
159388f8b78aSgm89044 
159488f8b78aSgm89044 	reqp->dr_dca = dca;
159588f8b78aSgm89044 
159688f8b78aSgm89044 	/*
159788f8b78aSgm89044 	 * Setup the DMA region for the context and descriptors.
159888f8b78aSgm89044 	 */
159988f8b78aSgm89044 	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr, DDI_DMA_SLEEP,
160088f8b78aSgm89044 	    NULL, &reqp->dr_ctx_dmah);
160188f8b78aSgm89044 	if (rv != DDI_SUCCESS) {
160288f8b78aSgm89044 		dca_error(dca, "failure allocating request DMA handle");
160388f8b78aSgm89044 		dca_destroyreq(reqp);
160488f8b78aSgm89044 		return (NULL);
160588f8b78aSgm89044 	}
160688f8b78aSgm89044 
160788f8b78aSgm89044 	/* for driver hardening, allocate in whole pages */
160888f8b78aSgm89044 	rv = ddi_dma_mem_alloc(reqp->dr_ctx_dmah,
160988f8b78aSgm89044 	    ROUNDUP(size, dca->dca_pagesize), &dca_devattr, DDI_DMA_CONSISTENT,
161088f8b78aSgm89044 	    DDI_DMA_SLEEP, NULL, &reqp->dr_ctx_kaddr, &size,
161188f8b78aSgm89044 	    &reqp->dr_ctx_acch);
161288f8b78aSgm89044 	if (rv != DDI_SUCCESS) {
161388f8b78aSgm89044 		dca_error(dca, "unable to alloc request DMA memory");
161488f8b78aSgm89044 		dca_destroyreq(reqp);
161588f8b78aSgm89044 		return (NULL);
161688f8b78aSgm89044 	}
161788f8b78aSgm89044 
161888f8b78aSgm89044 	rv = ddi_dma_addr_bind_handle(reqp->dr_ctx_dmah, NULL,
161988f8b78aSgm89044 	    reqp->dr_ctx_kaddr, size, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
162088f8b78aSgm89044 	    DDI_DMA_SLEEP, 0, &c, &nc);
162188f8b78aSgm89044 	if (rv != DDI_DMA_MAPPED) {
162288f8b78aSgm89044 		dca_error(dca, "failed binding request DMA handle");
162388f8b78aSgm89044 		dca_destroyreq(reqp);
162488f8b78aSgm89044 		return (NULL);
162588f8b78aSgm89044 	}
162688f8b78aSgm89044 	reqp->dr_ctx_paddr = c.dmac_address;
162788f8b78aSgm89044 
162888f8b78aSgm89044 	reqp->dr_dma_size = size;
162988f8b78aSgm89044 
163088f8b78aSgm89044 	/*
163188f8b78aSgm89044 	 * Set up the dma for our scratch/shared buffers.
163288f8b78aSgm89044 	 */
163388f8b78aSgm89044 	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
163488f8b78aSgm89044 	    DDI_DMA_SLEEP, NULL, &reqp->dr_ibuf_dmah);
163588f8b78aSgm89044 	if (rv != DDI_SUCCESS) {
163688f8b78aSgm89044 		dca_error(dca, "failure allocating ibuf DMA handle");
163788f8b78aSgm89044 		dca_destroyreq(reqp);
163888f8b78aSgm89044 		return (NULL);
163988f8b78aSgm89044 	}
164088f8b78aSgm89044 	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
164188f8b78aSgm89044 	    DDI_DMA_SLEEP, NULL, &reqp->dr_obuf_dmah);
164288f8b78aSgm89044 	if (rv != DDI_SUCCESS) {
164388f8b78aSgm89044 		dca_error(dca, "failure allocating obuf DMA handle");
164488f8b78aSgm89044 		dca_destroyreq(reqp);
164588f8b78aSgm89044 		return (NULL);
164688f8b78aSgm89044 	}
164788f8b78aSgm89044 
164888f8b78aSgm89044 	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
164988f8b78aSgm89044 	    DDI_DMA_SLEEP, NULL, &reqp->dr_chain_in_dmah);
165088f8b78aSgm89044 	if (rv != DDI_SUCCESS) {
165188f8b78aSgm89044 		dca_error(dca, "failure allocating chain_in DMA handle");
165288f8b78aSgm89044 		dca_destroyreq(reqp);
165388f8b78aSgm89044 		return (NULL);
165488f8b78aSgm89044 	}
165588f8b78aSgm89044 
165688f8b78aSgm89044 	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
165788f8b78aSgm89044 	    DDI_DMA_SLEEP, NULL, &reqp->dr_chain_out_dmah);
165888f8b78aSgm89044 	if (rv != DDI_SUCCESS) {
165988f8b78aSgm89044 		dca_error(dca, "failure allocating chain_out DMA handle");
166088f8b78aSgm89044 		dca_destroyreq(reqp);
166188f8b78aSgm89044 		return (NULL);
166288f8b78aSgm89044 	}
166388f8b78aSgm89044 
166488f8b78aSgm89044 	/*
166588f8b78aSgm89044 	 * for driver hardening, allocate in whole pages.
166688f8b78aSgm89044 	 */
166788f8b78aSgm89044 	size = ROUNDUP(MAXPACKET, dca->dca_pagesize);
166888f8b78aSgm89044 	/*
166995014fbbSDan OpenSolaris Anderson 	 * We could kmem_alloc for Sparc too. However, it gives worse
167095014fbbSDan OpenSolaris Anderson 	 * performance when transferring more than one page data. For example,
167195014fbbSDan OpenSolaris Anderson 	 * using 4 threads and 12032 byte data and 3DES on 900MHZ Sparc system,
167288f8b78aSgm89044 	 * kmem_alloc uses 80% CPU and ddi_dma_mem_alloc uses 50% CPU for
167388f8b78aSgm89044 	 * the same throughput.
167488f8b78aSgm89044 	 */
167588f8b78aSgm89044 	rv = ddi_dma_mem_alloc(reqp->dr_ibuf_dmah,
167688f8b78aSgm89044 	    size, &dca_bufattr,
167788f8b78aSgm89044 	    DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &reqp->dr_ibuf_kaddr,
167888f8b78aSgm89044 	    &size, &reqp->dr_ibuf_acch);
167988f8b78aSgm89044 	if (rv != DDI_SUCCESS) {
168088f8b78aSgm89044 		dca_error(dca, "unable to alloc request DMA memory");
168188f8b78aSgm89044 		dca_destroyreq(reqp);
168288f8b78aSgm89044 		return (NULL);
168388f8b78aSgm89044 	}
168488f8b78aSgm89044 
168588f8b78aSgm89044 	rv = ddi_dma_mem_alloc(reqp->dr_obuf_dmah,
168688f8b78aSgm89044 	    size, &dca_bufattr,
168788f8b78aSgm89044 	    DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &reqp->dr_obuf_kaddr,
168888f8b78aSgm89044 	    &size, &reqp->dr_obuf_acch);
168988f8b78aSgm89044 	if (rv != DDI_SUCCESS) {
169088f8b78aSgm89044 		dca_error(dca, "unable to alloc request DMA memory");
169188f8b78aSgm89044 		dca_destroyreq(reqp);
169288f8b78aSgm89044 		return (NULL);
169388f8b78aSgm89044 	}
169488f8b78aSgm89044 
169588f8b78aSgm89044 	/* Skip the used portion in the context page */
169688f8b78aSgm89044 	reqp->dr_offset = CTX_MAXLENGTH;
169788f8b78aSgm89044 	if ((rv = dca_bindchains_one(reqp, size, reqp->dr_offset,
169888f8b78aSgm89044 	    reqp->dr_ibuf_kaddr, reqp->dr_ibuf_dmah,
169988f8b78aSgm89044 	    DDI_DMA_WRITE | DDI_DMA_STREAMING,
170088f8b78aSgm89044 	    &reqp->dr_ibuf_head, &n_chain)) != DDI_SUCCESS) {
170188f8b78aSgm89044 		(void) dca_destroyreq(reqp);
170288f8b78aSgm89044 		return (NULL);
170388f8b78aSgm89044 	}
170488f8b78aSgm89044 	reqp->dr_ibuf_paddr = reqp->dr_ibuf_head.dc_buffer_paddr;
170588f8b78aSgm89044 	/* Skip the space used by the input buffer */
170688f8b78aSgm89044 	reqp->dr_offset += DESC_SIZE * n_chain;
170788f8b78aSgm89044 
170888f8b78aSgm89044 	if ((rv = dca_bindchains_one(reqp, size, reqp->dr_offset,
170988f8b78aSgm89044 	    reqp->dr_obuf_kaddr, reqp->dr_obuf_dmah,
171088f8b78aSgm89044 	    DDI_DMA_READ | DDI_DMA_STREAMING,
171188f8b78aSgm89044 	    &reqp->dr_obuf_head, &n_chain)) != DDI_SUCCESS) {
171288f8b78aSgm89044 		(void) dca_destroyreq(reqp);
171388f8b78aSgm89044 		return (NULL);
171488f8b78aSgm89044 	}
171588f8b78aSgm89044 	reqp->dr_obuf_paddr = reqp->dr_obuf_head.dc_buffer_paddr;
171688f8b78aSgm89044 	/* Skip the space used by the output buffer */
171788f8b78aSgm89044 	reqp->dr_offset += DESC_SIZE * n_chain;
171888f8b78aSgm89044 
171988f8b78aSgm89044 	DBG(dca, DCHATTY, "CTX is 0x%p, phys 0x%x, len %d",
172088f8b78aSgm89044 	    reqp->dr_ctx_kaddr, reqp->dr_ctx_paddr, CTX_MAXLENGTH);
172188f8b78aSgm89044 	return (reqp);
172288f8b78aSgm89044 }
172388f8b78aSgm89044 
172488f8b78aSgm89044 void
dca_destroyreq(dca_request_t * reqp)172588f8b78aSgm89044 dca_destroyreq(dca_request_t *reqp)
172688f8b78aSgm89044 {
172788f8b78aSgm89044 
172888f8b78aSgm89044 	/*
172988f8b78aSgm89044 	 * Clean up DMA for the context structure.
173088f8b78aSgm89044 	 */
173188f8b78aSgm89044 	if (reqp->dr_ctx_paddr) {
173288f8b78aSgm89044 		(void) ddi_dma_unbind_handle(reqp->dr_ctx_dmah);
173388f8b78aSgm89044 	}
173488f8b78aSgm89044 
173588f8b78aSgm89044 	if (reqp->dr_ctx_acch) {
173688f8b78aSgm89044 		ddi_dma_mem_free(&reqp->dr_ctx_acch);
173788f8b78aSgm89044 	}
173888f8b78aSgm89044 
173988f8b78aSgm89044 	if (reqp->dr_ctx_dmah) {
174088f8b78aSgm89044 		ddi_dma_free_handle(&reqp->dr_ctx_dmah);
174188f8b78aSgm89044 	}
174288f8b78aSgm89044 
174388f8b78aSgm89044 	/*
174488f8b78aSgm89044 	 * Clean up DMA for the scratch buffer.
174588f8b78aSgm89044 	 */
174688f8b78aSgm89044 	if (reqp->dr_ibuf_paddr) {
174788f8b78aSgm89044 		(void) ddi_dma_unbind_handle(reqp->dr_ibuf_dmah);
174888f8b78aSgm89044 	}
174988f8b78aSgm89044 	if (reqp->dr_obuf_paddr) {
175088f8b78aSgm89044 		(void) ddi_dma_unbind_handle(reqp->dr_obuf_dmah);
175188f8b78aSgm89044 	}
175288f8b78aSgm89044 
175388f8b78aSgm89044 	if (reqp->dr_ibuf_acch) {
175488f8b78aSgm89044 		ddi_dma_mem_free(&reqp->dr_ibuf_acch);
175588f8b78aSgm89044 	}
175688f8b78aSgm89044 	if (reqp->dr_obuf_acch) {
175788f8b78aSgm89044 		ddi_dma_mem_free(&reqp->dr_obuf_acch);
175888f8b78aSgm89044 	}
175988f8b78aSgm89044 
176088f8b78aSgm89044 	if (reqp->dr_ibuf_dmah) {
176188f8b78aSgm89044 		ddi_dma_free_handle(&reqp->dr_ibuf_dmah);
176288f8b78aSgm89044 	}
176388f8b78aSgm89044 	if (reqp->dr_obuf_dmah) {
176488f8b78aSgm89044 		ddi_dma_free_handle(&reqp->dr_obuf_dmah);
176588f8b78aSgm89044 	}
176688f8b78aSgm89044 	/*
176788f8b78aSgm89044 	 * These two DMA handles should have been unbinded in
176888f8b78aSgm89044 	 * dca_unbindchains() function
176988f8b78aSgm89044 	 */
177088f8b78aSgm89044 	if (reqp->dr_chain_in_dmah) {
177188f8b78aSgm89044 		ddi_dma_free_handle(&reqp->dr_chain_in_dmah);
177288f8b78aSgm89044 	}
177388f8b78aSgm89044 	if (reqp->dr_chain_out_dmah) {
177488f8b78aSgm89044 		ddi_dma_free_handle(&reqp->dr_chain_out_dmah);
177588f8b78aSgm89044 	}
177688f8b78aSgm89044 
177788f8b78aSgm89044 	kmem_free(reqp, sizeof (dca_request_t));
177888f8b78aSgm89044 }
177988f8b78aSgm89044 
178088f8b78aSgm89044 dca_work_t *
dca_getwork(dca_t * dca,int mcr)178188f8b78aSgm89044 dca_getwork(dca_t *dca, int mcr)
178288f8b78aSgm89044 {
178388f8b78aSgm89044 	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
178488f8b78aSgm89044 	dca_work_t	*workp;
178588f8b78aSgm89044 
17863383b6ddSqs148142 	mutex_enter(&wlp->dwl_freelock);
178788f8b78aSgm89044 	workp = (dca_work_t *)dca_dequeue(&wlp->dwl_freework);
17883383b6ddSqs148142 	mutex_exit(&wlp->dwl_freelock);
178988f8b78aSgm89044 	if (workp) {
179088f8b78aSgm89044 		int	nreqs;
179188f8b78aSgm89044 		bzero(workp->dw_mcr_kaddr, 8);
179288f8b78aSgm89044 
179388f8b78aSgm89044 		/* clear out old requests */
179488f8b78aSgm89044 		for (nreqs = 0; nreqs < MAXREQSPERMCR; nreqs++) {
179588f8b78aSgm89044 			workp->dw_reqs[nreqs] = NULL;
179688f8b78aSgm89044 		}
179788f8b78aSgm89044 	}
179888f8b78aSgm89044 	return (workp);
179988f8b78aSgm89044 }
180088f8b78aSgm89044 
180188f8b78aSgm89044 void
dca_freework(dca_work_t * workp)180288f8b78aSgm89044 dca_freework(dca_work_t *workp)
180388f8b78aSgm89044 {
18043383b6ddSqs148142 	mutex_enter(&workp->dw_wlp->dwl_freelock);
180588f8b78aSgm89044 	dca_enqueue(&workp->dw_wlp->dwl_freework, (dca_listnode_t *)workp);
18063383b6ddSqs148142 	mutex_exit(&workp->dw_wlp->dwl_freelock);
180788f8b78aSgm89044 }
180888f8b78aSgm89044 
180988f8b78aSgm89044 dca_request_t *
dca_getreq(dca_t * dca,int mcr,int tryhard)181088f8b78aSgm89044 dca_getreq(dca_t *dca, int mcr, int tryhard)
181188f8b78aSgm89044 {
181288f8b78aSgm89044 	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
181388f8b78aSgm89044 	dca_request_t	*reqp;
181488f8b78aSgm89044 
181588f8b78aSgm89044 	mutex_enter(&wlp->dwl_freereqslock);
181688f8b78aSgm89044 	reqp = (dca_request_t *)dca_dequeue(&wlp->dwl_freereqs);
181788f8b78aSgm89044 	mutex_exit(&wlp->dwl_freereqslock);
181888f8b78aSgm89044 	if (reqp) {
181988f8b78aSgm89044 		reqp->dr_flags = 0;
182088f8b78aSgm89044 		reqp->dr_callback = NULL;
182188f8b78aSgm89044 	} else if (tryhard) {
182288f8b78aSgm89044 		/*
182388f8b78aSgm89044 		 * failed to get a free one, try an allocation, the hard way.
182488f8b78aSgm89044 		 * XXX: Kstat desired here.
182588f8b78aSgm89044 		 */
182688f8b78aSgm89044 		if ((reqp = dca_newreq(dca)) != NULL) {
182788f8b78aSgm89044 			reqp->dr_wlp = wlp;
182888f8b78aSgm89044 			reqp->dr_dca = dca;
182988f8b78aSgm89044 			reqp->dr_flags = 0;
183088f8b78aSgm89044 			reqp->dr_callback = NULL;
183188f8b78aSgm89044 		}
183288f8b78aSgm89044 	}
183388f8b78aSgm89044 	return (reqp);
183488f8b78aSgm89044 }
183588f8b78aSgm89044 
183688f8b78aSgm89044 void
dca_freereq(dca_request_t * reqp)183788f8b78aSgm89044 dca_freereq(dca_request_t *reqp)
183888f8b78aSgm89044 {
183988f8b78aSgm89044 	reqp->dr_kcf_req = NULL;
184088f8b78aSgm89044 	if (!(reqp->dr_flags & DR_NOCACHE)) {
184188f8b78aSgm89044 		mutex_enter(&reqp->dr_wlp->dwl_freereqslock);
184288f8b78aSgm89044 		dca_enqueue(&reqp->dr_wlp->dwl_freereqs,
184388f8b78aSgm89044 		    (dca_listnode_t *)reqp);
184488f8b78aSgm89044 		mutex_exit(&reqp->dr_wlp->dwl_freereqslock);
184588f8b78aSgm89044 	}
184688f8b78aSgm89044 }
184788f8b78aSgm89044 
184888f8b78aSgm89044 /*
184988f8b78aSgm89044  * Binds user buffers to DMA handles dynamically. On Sparc, a user buffer
185095014fbbSDan OpenSolaris Anderson  * is mapped to a single physical address. On x86, a user buffer is mapped
185195014fbbSDan OpenSolaris Anderson  * to multiple physical addresses. These physical addresses are chained
185295014fbbSDan OpenSolaris Anderson  * using the method specified in Broadcom BCM5820 specification.
185388f8b78aSgm89044  */
185488f8b78aSgm89044 int
dca_bindchains(dca_request_t * reqp,size_t incnt,size_t outcnt)185588f8b78aSgm89044 dca_bindchains(dca_request_t *reqp, size_t incnt, size_t outcnt)
185688f8b78aSgm89044 {
185788f8b78aSgm89044 	int			rv;
185888f8b78aSgm89044 	caddr_t			kaddr;
185988f8b78aSgm89044 	uint_t			flags;
186088f8b78aSgm89044 	int			n_chain = 0;
186188f8b78aSgm89044 
186288f8b78aSgm89044 	if (reqp->dr_flags & DR_INPLACE) {
186388f8b78aSgm89044 		flags = DDI_DMA_RDWR | DDI_DMA_CONSISTENT;
186488f8b78aSgm89044 	} else {
186588f8b78aSgm89044 		flags = DDI_DMA_WRITE | DDI_DMA_STREAMING;
186688f8b78aSgm89044 	}
186788f8b78aSgm89044 
186888f8b78aSgm89044 	/* first the input */
186988f8b78aSgm89044 	if (incnt) {
187088f8b78aSgm89044 		if ((kaddr = dca_bufdaddr(reqp->dr_in)) == NULL) {
187188f8b78aSgm89044 			DBG(NULL, DWARN, "unrecognised crypto data format");
187288f8b78aSgm89044 			return (DDI_FAILURE);
187388f8b78aSgm89044 		}
187488f8b78aSgm89044 		if ((rv = dca_bindchains_one(reqp, incnt, reqp->dr_offset,
187588f8b78aSgm89044 		    kaddr, reqp->dr_chain_in_dmah, flags,
187688f8b78aSgm89044 		    &reqp->dr_chain_in_head, &n_chain)) != DDI_SUCCESS) {
187788f8b78aSgm89044 			(void) dca_unbindchains(reqp);
187888f8b78aSgm89044 			return (rv);
187988f8b78aSgm89044 		}
188088f8b78aSgm89044 
188188f8b78aSgm89044 		/*
188288f8b78aSgm89044 		 * The offset and length are altered by the calling routine
188388f8b78aSgm89044 		 * reqp->dr_in->cd_offset += incnt;
188488f8b78aSgm89044 		 * reqp->dr_in->cd_length -= incnt;
188588f8b78aSgm89044 		 */
188688f8b78aSgm89044 		/* Save the first one in the chain for MCR */
188788f8b78aSgm89044 		reqp->dr_in_paddr = reqp->dr_chain_in_head.dc_buffer_paddr;
188888f8b78aSgm89044 		reqp->dr_in_next = reqp->dr_chain_in_head.dc_next_paddr;
188988f8b78aSgm89044 		reqp->dr_in_len = reqp->dr_chain_in_head.dc_buffer_length;
189088f8b78aSgm89044 	} else {
18915c63ad82SToomas Soome 		reqp->dr_in_paddr = 0;
189288f8b78aSgm89044 		reqp->dr_in_next = 0;
189388f8b78aSgm89044 		reqp->dr_in_len = 0;
189488f8b78aSgm89044 	}
189588f8b78aSgm89044 
189688f8b78aSgm89044 	if (reqp->dr_flags & DR_INPLACE) {
189788f8b78aSgm89044 		reqp->dr_out_paddr = reqp->dr_in_paddr;
189888f8b78aSgm89044 		reqp->dr_out_len = reqp->dr_in_len;
189988f8b78aSgm89044 		reqp->dr_out_next = reqp->dr_in_next;
190088f8b78aSgm89044 		return (DDI_SUCCESS);
190188f8b78aSgm89044 	}
190288f8b78aSgm89044 
190388f8b78aSgm89044 	/* then the output */
190488f8b78aSgm89044 	if (outcnt) {
190588f8b78aSgm89044 		flags = DDI_DMA_READ | DDI_DMA_STREAMING;
190688f8b78aSgm89044 		if ((kaddr = dca_bufdaddr_out(reqp->dr_out)) == NULL) {
190788f8b78aSgm89044 			DBG(NULL, DWARN, "unrecognised crypto data format");
190888f8b78aSgm89044 			(void) dca_unbindchains(reqp);
190988f8b78aSgm89044 			return (DDI_FAILURE);
191088f8b78aSgm89044 		}
191188f8b78aSgm89044 		rv = dca_bindchains_one(reqp, outcnt, reqp->dr_offset +
191288f8b78aSgm89044 		    n_chain * DESC_SIZE, kaddr, reqp->dr_chain_out_dmah,
191388f8b78aSgm89044 		    flags, &reqp->dr_chain_out_head, &n_chain);
191488f8b78aSgm89044 		if (rv != DDI_SUCCESS) {
191588f8b78aSgm89044 			(void) dca_unbindchains(reqp);
191688f8b78aSgm89044 			return (DDI_FAILURE);
191788f8b78aSgm89044 		}
191888f8b78aSgm89044 
191988f8b78aSgm89044 		/* Save the first one in the chain for MCR */
192088f8b78aSgm89044 		reqp->dr_out_paddr = reqp->dr_chain_out_head.dc_buffer_paddr;
192188f8b78aSgm89044 		reqp->dr_out_next = reqp->dr_chain_out_head.dc_next_paddr;
192288f8b78aSgm89044 		reqp->dr_out_len = reqp->dr_chain_out_head.dc_buffer_length;
192388f8b78aSgm89044 	} else {
19245c63ad82SToomas Soome 		reqp->dr_out_paddr = 0;
192588f8b78aSgm89044 		reqp->dr_out_next = 0;
192688f8b78aSgm89044 		reqp->dr_out_len = 0;
192788f8b78aSgm89044 	}
192888f8b78aSgm89044 
192988f8b78aSgm89044 	return (DDI_SUCCESS);
193088f8b78aSgm89044 }
193188f8b78aSgm89044 
193288f8b78aSgm89044 /*
193388f8b78aSgm89044  * Unbind the user buffers from the DMA handles.
193488f8b78aSgm89044  */
193588f8b78aSgm89044 int
dca_unbindchains(dca_request_t * reqp)193688f8b78aSgm89044 dca_unbindchains(dca_request_t *reqp)
193788f8b78aSgm89044 {
193888f8b78aSgm89044 	int rv = DDI_SUCCESS;
193988f8b78aSgm89044 	int rv1 = DDI_SUCCESS;
194088f8b78aSgm89044 
194188f8b78aSgm89044 	/* Clear the input chain */
19425c63ad82SToomas Soome 	if (reqp->dr_chain_in_head.dc_buffer_paddr != 0) {
194388f8b78aSgm89044 		(void) ddi_dma_unbind_handle(reqp->dr_chain_in_dmah);
194488f8b78aSgm89044 		reqp->dr_chain_in_head.dc_buffer_paddr = 0;
194588f8b78aSgm89044 	}
194688f8b78aSgm89044 
1947d8dd9913Sgm89044 	if (reqp->dr_flags & DR_INPLACE) {
1948d8dd9913Sgm89044 		return (rv);
1949d8dd9913Sgm89044 	}
1950d8dd9913Sgm89044 
195188f8b78aSgm89044 	/* Clear the output chain */
19525c63ad82SToomas Soome 	if (reqp->dr_chain_out_head.dc_buffer_paddr != 0) {
195388f8b78aSgm89044 		(void) ddi_dma_unbind_handle(reqp->dr_chain_out_dmah);
195488f8b78aSgm89044 		reqp->dr_chain_out_head.dc_buffer_paddr = 0;
195588f8b78aSgm89044 	}
195688f8b78aSgm89044 
195788f8b78aSgm89044 	return ((rv != DDI_SUCCESS)? rv : rv1);
195888f8b78aSgm89044 }
195988f8b78aSgm89044 
196088f8b78aSgm89044 /*
196188f8b78aSgm89044  * Build either input chain or output chain. It is single-item chain for Sparc,
196288f8b78aSgm89044  * and possible mutiple-item chain for x86.
196388f8b78aSgm89044  */
196488f8b78aSgm89044 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)196588f8b78aSgm89044 dca_bindchains_one(dca_request_t *reqp, size_t cnt, int dr_offset,
196688f8b78aSgm89044     caddr_t kaddr, ddi_dma_handle_t handle, uint_t flags,
196788f8b78aSgm89044     dca_chain_t *head, int *n_chain)
196888f8b78aSgm89044 {
196988f8b78aSgm89044 	ddi_dma_cookie_t	c;
197088f8b78aSgm89044 	uint_t			nc;
197188f8b78aSgm89044 	int			rv;
197288f8b78aSgm89044 	caddr_t			chain_kaddr_pre;
197388f8b78aSgm89044 	caddr_t			chain_kaddr;
197488f8b78aSgm89044 	uint32_t		chain_paddr;
197588f8b78aSgm89044 	int			i;
197688f8b78aSgm89044 
197788f8b78aSgm89044 	/* Advance past the context structure to the starting address */
197888f8b78aSgm89044 	chain_paddr = reqp->dr_ctx_paddr + dr_offset;
197988f8b78aSgm89044 	chain_kaddr = reqp->dr_ctx_kaddr + dr_offset;
198088f8b78aSgm89044 
198188f8b78aSgm89044 	/*
198288f8b78aSgm89044 	 * Bind the kernel address to the DMA handle. On x86, the actual
198388f8b78aSgm89044 	 * buffer is mapped into multiple physical addresses. On Sparc,
198488f8b78aSgm89044 	 * the actual buffer is mapped into a single address.
198588f8b78aSgm89044 	 */
198688f8b78aSgm89044 	rv = ddi_dma_addr_bind_handle(handle,
198788f8b78aSgm89044 	    NULL, kaddr, cnt, flags, DDI_DMA_DONTWAIT, NULL, &c, &nc);
198888f8b78aSgm89044 	if (rv != DDI_DMA_MAPPED) {
198988f8b78aSgm89044 		return (DDI_FAILURE);
199088f8b78aSgm89044 	}
199188f8b78aSgm89044 
199288f8b78aSgm89044 	(void) ddi_dma_sync(handle, 0, cnt, DDI_DMA_SYNC_FORDEV);
199388f8b78aSgm89044 	if ((rv = dca_check_dma_handle(reqp->dr_dca, handle,
199488f8b78aSgm89044 	    DCA_FM_ECLASS_NONE)) != DDI_SUCCESS) {
199588f8b78aSgm89044 		reqp->destroy = TRUE;
199688f8b78aSgm89044 		return (rv);
199788f8b78aSgm89044 	}
199888f8b78aSgm89044 
199988f8b78aSgm89044 	*n_chain = nc;
200088f8b78aSgm89044 
200188f8b78aSgm89044 	/* Setup the data buffer chain for DMA transfer */
200288f8b78aSgm89044 	chain_kaddr_pre = NULL;
200388f8b78aSgm89044 	head->dc_buffer_paddr = 0;
200488f8b78aSgm89044 	head->dc_next_paddr = 0;
200588f8b78aSgm89044 	head->dc_buffer_length = 0;
200688f8b78aSgm89044 	for (i = 0; i < nc; i++) {
200788f8b78aSgm89044 		/* PIO */
200888f8b78aSgm89044 		PUTDESC32(reqp, chain_kaddr, DESC_BUFADDR, c.dmac_address);
200988f8b78aSgm89044 		PUTDESC16(reqp, chain_kaddr, DESC_RSVD, 0);
201088f8b78aSgm89044 		PUTDESC16(reqp, chain_kaddr, DESC_LENGTH, c.dmac_size);
201188f8b78aSgm89044 
201288f8b78aSgm89044 		/* Remember the head of the chain */
201388f8b78aSgm89044 		if (head->dc_buffer_paddr == 0) {
201488f8b78aSgm89044 			head->dc_buffer_paddr = c.dmac_address;
201588f8b78aSgm89044 			head->dc_buffer_length = c.dmac_size;
201688f8b78aSgm89044 		}
201788f8b78aSgm89044 
201888f8b78aSgm89044 		/* Link to the previous one if one exists */
201988f8b78aSgm89044 		if (chain_kaddr_pre) {
202088f8b78aSgm89044 			PUTDESC32(reqp, chain_kaddr_pre, DESC_NEXT,
202188f8b78aSgm89044 			    chain_paddr);
202288f8b78aSgm89044 			if (head->dc_next_paddr == 0)
202388f8b78aSgm89044 				head->dc_next_paddr = chain_paddr;
202488f8b78aSgm89044 		}
202588f8b78aSgm89044 		chain_kaddr_pre = chain_kaddr;
202688f8b78aSgm89044 
202788f8b78aSgm89044 		/* Maintain pointers */
202888f8b78aSgm89044 		chain_paddr += DESC_SIZE;
202988f8b78aSgm89044 		chain_kaddr += DESC_SIZE;
203088f8b78aSgm89044 
203188f8b78aSgm89044 		/* Retrieve the next cookie if there is one */
203288f8b78aSgm89044 		if (i < nc-1)
203388f8b78aSgm89044 			ddi_dma_nextcookie(handle, &c);
203488f8b78aSgm89044 	}
203588f8b78aSgm89044 
203688f8b78aSgm89044 	/* Set the next pointer in the last entry to NULL */
203788f8b78aSgm89044 	PUTDESC32(reqp, chain_kaddr_pre, DESC_NEXT, 0);
203888f8b78aSgm89044 
203988f8b78aSgm89044 	return (DDI_SUCCESS);
204088f8b78aSgm89044 }
204188f8b78aSgm89044 
204288f8b78aSgm89044 /*
204388f8b78aSgm89044  * Schedule some work.
204488f8b78aSgm89044  */
204588f8b78aSgm89044 int
dca_start(dca_t * dca,dca_request_t * reqp,int mcr,int dosched)204688f8b78aSgm89044 dca_start(dca_t *dca, dca_request_t *reqp, int mcr, int dosched)
204788f8b78aSgm89044 {
204888f8b78aSgm89044 	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
204988f8b78aSgm89044 
205088f8b78aSgm89044 	mutex_enter(&wlp->dwl_lock);
205188f8b78aSgm89044 
205288f8b78aSgm89044 	DBG(dca, DCHATTY, "req=%p, in=%p, out=%p, ctx=%p, ibuf=%p, obuf=%p",
205388f8b78aSgm89044 	    reqp, reqp->dr_in, reqp->dr_out, reqp->dr_ctx_kaddr,
205488f8b78aSgm89044 	    reqp->dr_ibuf_kaddr, reqp->dr_obuf_kaddr);
205588f8b78aSgm89044 	DBG(dca, DCHATTY, "ctx paddr = %x, ibuf paddr = %x, obuf paddr = %x",
205688f8b78aSgm89044 	    reqp->dr_ctx_paddr, reqp->dr_ibuf_paddr, reqp->dr_obuf_paddr);
205788f8b78aSgm89044 	/* sync out the entire context and descriptor chains */
205888f8b78aSgm89044 	(void) ddi_dma_sync(reqp->dr_ctx_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
205988f8b78aSgm89044 	if (dca_check_dma_handle(dca, reqp->dr_ctx_dmah,
206088f8b78aSgm89044 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
206188f8b78aSgm89044 		reqp->destroy = TRUE;
206288f8b78aSgm89044 		mutex_exit(&wlp->dwl_lock);
206388f8b78aSgm89044 		return (CRYPTO_DEVICE_ERROR);
206488f8b78aSgm89044 	}
206588f8b78aSgm89044 
206688f8b78aSgm89044 	dca_enqueue(&wlp->dwl_waitq, (dca_listnode_t *)reqp);
206788f8b78aSgm89044 	wlp->dwl_count++;
206888f8b78aSgm89044 	wlp->dwl_lastsubmit = ddi_get_lbolt();
206988f8b78aSgm89044 	reqp->dr_wlp = wlp;
207088f8b78aSgm89044 
207188f8b78aSgm89044 	if ((wlp->dwl_count == wlp->dwl_hiwater) && (wlp->dwl_busy == 0)) {
207288f8b78aSgm89044 		/* we are fully loaded now, let kCF know */
207388f8b78aSgm89044 
207488f8b78aSgm89044 		wlp->dwl_flowctl++;
207588f8b78aSgm89044 		wlp->dwl_busy = 1;
207688f8b78aSgm89044 
207788f8b78aSgm89044 		crypto_prov_notify(wlp->dwl_prov, CRYPTO_PROVIDER_BUSY);
207888f8b78aSgm89044 	}
207988f8b78aSgm89044 
208088f8b78aSgm89044 	if (dosched) {
208188f8b78aSgm89044 #ifdef	SCHEDDELAY
208288f8b78aSgm89044 		/* possibly wait for more work to arrive */
208388f8b78aSgm89044 		if (wlp->dwl_count >= wlp->dwl_reqspermcr) {
208488f8b78aSgm89044 			dca_schedule(dca, mcr);
208588f8b78aSgm89044 		} else if (!wlp->dwl_schedtid) {
208688f8b78aSgm89044 			/* wait 1 msec for more work before doing it */
208788f8b78aSgm89044 			wlp->dwl_schedtid = timeout(dca_schedtimeout,
208888f8b78aSgm89044 			    (void *)wlp, drv_usectohz(MSEC));
208988f8b78aSgm89044 		}
209088f8b78aSgm89044 #else
209188f8b78aSgm89044 		dca_schedule(dca, mcr);
209288f8b78aSgm89044 #endif
209388f8b78aSgm89044 	}
209488f8b78aSgm89044 	mutex_exit(&wlp->dwl_lock);
209588f8b78aSgm89044 
209688f8b78aSgm89044 	return (CRYPTO_QUEUED);
209788f8b78aSgm89044 }
209888f8b78aSgm89044 
209988f8b78aSgm89044 void
dca_schedule(dca_t * dca,int mcr)210088f8b78aSgm89044 dca_schedule(dca_t *dca, int mcr)
210188f8b78aSgm89044 {
210288f8b78aSgm89044 	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
210388f8b78aSgm89044 	int		csr;
210488f8b78aSgm89044 	int		full;
210588f8b78aSgm89044 	uint32_t	status;
210688f8b78aSgm89044 
210788f8b78aSgm89044 	ASSERT(mutex_owned(&wlp->dwl_lock));
210888f8b78aSgm89044 	/*
210988f8b78aSgm89044 	 * If the card is draining or has an outstanding failure,
211088f8b78aSgm89044 	 * don't schedule any more work on it right now
211188f8b78aSgm89044 	 */
211288f8b78aSgm89044 	if (wlp->dwl_drain || (dca->dca_flags & DCA_FAILED)) {
211388f8b78aSgm89044 		return;
211488f8b78aSgm89044 	}
211588f8b78aSgm89044 
211688f8b78aSgm89044 	if (mcr == MCR2) {
211788f8b78aSgm89044 		csr = CSR_MCR2;
211888f8b78aSgm89044 		full = DMASTAT_MCR2FULL;
211988f8b78aSgm89044 	} else {
212088f8b78aSgm89044 		csr = CSR_MCR1;
212188f8b78aSgm89044 		full = DMASTAT_MCR1FULL;
212288f8b78aSgm89044 	}
212388f8b78aSgm89044 
212488f8b78aSgm89044 	for (;;) {
212588f8b78aSgm89044 		dca_work_t	*workp;
212688f8b78aSgm89044 		uint32_t	offset;
212788f8b78aSgm89044 		int		nreqs;
212888f8b78aSgm89044 
212988f8b78aSgm89044 		status = GETCSR(dca, CSR_DMASTAT);
213088f8b78aSgm89044 		if (dca_check_acc_handle(dca, dca->dca_regs_handle,
213188f8b78aSgm89044 		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
213288f8b78aSgm89044 			return;
213388f8b78aSgm89044 
213488f8b78aSgm89044 		if ((status & full) != 0)
213588f8b78aSgm89044 			break;
213688f8b78aSgm89044 
213788f8b78aSgm89044 #ifdef	SCHEDDELAY
213888f8b78aSgm89044 		/* if there isn't enough to do, don't bother now */
213988f8b78aSgm89044 		if ((wlp->dwl_count < wlp->dwl_reqspermcr) &&
214088f8b78aSgm89044 		    (ddi_get_lbolt() < (wlp->dwl_lastsubmit +
214188f8b78aSgm89044 		    drv_usectohz(MSEC)))) {
214288f8b78aSgm89044 			/* wait a bit longer... */
214388f8b78aSgm89044 			if (wlp->dwl_schedtid == 0) {
214488f8b78aSgm89044 				wlp->dwl_schedtid = timeout(dca_schedtimeout,
214588f8b78aSgm89044 				    (void *)wlp, drv_usectohz(MSEC));
214688f8b78aSgm89044 			}
214788f8b78aSgm89044 			return;
214888f8b78aSgm89044 		}
214988f8b78aSgm89044 #endif
215088f8b78aSgm89044 
215188f8b78aSgm89044 		/* grab a work structure */
215288f8b78aSgm89044 		workp = dca_getwork(dca, mcr);
215388f8b78aSgm89044 
215488f8b78aSgm89044 		if (workp == NULL) {
215588f8b78aSgm89044 			/*
215688f8b78aSgm89044 			 * There must be work ready to be reclaimed,
215788f8b78aSgm89044 			 * in this case, since the chip can only hold
215888f8b78aSgm89044 			 * less work outstanding than there are total.
215988f8b78aSgm89044 			 */
216088f8b78aSgm89044 			dca_reclaim(dca, mcr);
216188f8b78aSgm89044 			continue;
216288f8b78aSgm89044 		}
216388f8b78aSgm89044 
216488f8b78aSgm89044 		nreqs = 0;
216588f8b78aSgm89044 		offset = MCR_CTXADDR;
216688f8b78aSgm89044 
216788f8b78aSgm89044 		while (nreqs < wlp->dwl_reqspermcr) {
216888f8b78aSgm89044 			dca_request_t	*reqp;
216988f8b78aSgm89044 
217088f8b78aSgm89044 			reqp = (dca_request_t *)dca_dequeue(&wlp->dwl_waitq);
217188f8b78aSgm89044 			if (reqp == NULL) {
217288f8b78aSgm89044 				/* nothing left to process */
217388f8b78aSgm89044 				break;
217488f8b78aSgm89044 			}
217588f8b78aSgm89044 			/*
217688f8b78aSgm89044 			 * Update flow control.
217788f8b78aSgm89044 			 */
217888f8b78aSgm89044 			wlp->dwl_count--;
217988f8b78aSgm89044 			if ((wlp->dwl_count == wlp->dwl_lowater) &&
218088f8b78aSgm89044 			    (wlp->dwl_busy))  {
218188f8b78aSgm89044 				wlp->dwl_busy = 0;
218288f8b78aSgm89044 				crypto_prov_notify(wlp->dwl_prov,
218388f8b78aSgm89044 				    CRYPTO_PROVIDER_READY);
218488f8b78aSgm89044 			}
218588f8b78aSgm89044 
218688f8b78aSgm89044 			/*
218788f8b78aSgm89044 			 * Context address.
218888f8b78aSgm89044 			 */
218988f8b78aSgm89044 			PUTMCR32(workp, offset, reqp->dr_ctx_paddr);
219088f8b78aSgm89044 			offset += 4;
219188f8b78aSgm89044 
219288f8b78aSgm89044 			/*
219388f8b78aSgm89044 			 * Input chain.
219488f8b78aSgm89044 			 */
219588f8b78aSgm89044 			/* input buffer address */
219688f8b78aSgm89044 			PUTMCR32(workp, offset, reqp->dr_in_paddr);
219788f8b78aSgm89044 			offset += 4;
219888f8b78aSgm89044 			/* next input buffer entry */
219988f8b78aSgm89044 			PUTMCR32(workp, offset, reqp->dr_in_next);
220088f8b78aSgm89044 			offset += 4;
220188f8b78aSgm89044 			/* input buffer length */
220288f8b78aSgm89044 			PUTMCR16(workp, offset, reqp->dr_in_len);
220388f8b78aSgm89044 			offset += 2;
220488f8b78aSgm89044 			/* zero the reserved field */
220588f8b78aSgm89044 			PUTMCR16(workp, offset, 0);
220688f8b78aSgm89044 			offset += 2;
220788f8b78aSgm89044 
220888f8b78aSgm89044 			/*
220988f8b78aSgm89044 			 * Overall length.
221088f8b78aSgm89044 			 */
221188f8b78aSgm89044 			/* reserved field */
221288f8b78aSgm89044 			PUTMCR16(workp, offset, 0);
221388f8b78aSgm89044 			offset += 2;
221488f8b78aSgm89044 			/* total packet length */
221588f8b78aSgm89044 			PUTMCR16(workp, offset, reqp->dr_pkt_length);
221688f8b78aSgm89044 			offset += 2;
221788f8b78aSgm89044 
221888f8b78aSgm89044 			/*
221988f8b78aSgm89044 			 * Output chain.
222088f8b78aSgm89044 			 */
222188f8b78aSgm89044 			/* output buffer address */
222288f8b78aSgm89044 			PUTMCR32(workp, offset, reqp->dr_out_paddr);
222388f8b78aSgm89044 			offset += 4;
222488f8b78aSgm89044 			/* next output buffer entry */
222588f8b78aSgm89044 			PUTMCR32(workp, offset, reqp->dr_out_next);
222688f8b78aSgm89044 			offset += 4;
222788f8b78aSgm89044 			/* output buffer length */
222888f8b78aSgm89044 			PUTMCR16(workp, offset, reqp->dr_out_len);
222988f8b78aSgm89044 			offset += 2;
223088f8b78aSgm89044 			/* zero the reserved field */
223188f8b78aSgm89044 			PUTMCR16(workp, offset, 0);
223288f8b78aSgm89044 			offset += 2;
223388f8b78aSgm89044 
223488f8b78aSgm89044 			/*
223588f8b78aSgm89044 			 * Note submission.
223688f8b78aSgm89044 			 */
223788f8b78aSgm89044 			workp->dw_reqs[nreqs] = reqp;
223888f8b78aSgm89044 			nreqs++;
223988f8b78aSgm89044 		}
224088f8b78aSgm89044 
224188f8b78aSgm89044 		if (nreqs == 0) {
224288f8b78aSgm89044 			/* nothing in the queue! */
224388f8b78aSgm89044 			dca_freework(workp);
224488f8b78aSgm89044 			return;
224588f8b78aSgm89044 		}
224688f8b78aSgm89044 
224788f8b78aSgm89044 		wlp->dwl_submit++;
224888f8b78aSgm89044 
224988f8b78aSgm89044 		PUTMCR16(workp, MCR_FLAGS, 0);
225088f8b78aSgm89044 		PUTMCR16(workp, MCR_COUNT, nreqs);
225188f8b78aSgm89044 
225288f8b78aSgm89044 		DBG(dca, DCHATTY,
225388f8b78aSgm89044 		    "posting work (phys %x, virt 0x%p) (%d reqs) to MCR%d",
225488f8b78aSgm89044 		    workp->dw_mcr_paddr, workp->dw_mcr_kaddr,
225588f8b78aSgm89044 		    nreqs, mcr);
225688f8b78aSgm89044 
225788f8b78aSgm89044 		workp->dw_lbolt = ddi_get_lbolt();
225888f8b78aSgm89044 		/* Make sure MCR is synced out to device. */
225988f8b78aSgm89044 		(void) ddi_dma_sync(workp->dw_mcr_dmah, 0, 0,
226088f8b78aSgm89044 		    DDI_DMA_SYNC_FORDEV);
226188f8b78aSgm89044 		if (dca_check_dma_handle(dca, workp->dw_mcr_dmah,
226288f8b78aSgm89044 		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
226388f8b78aSgm89044 			dca_destroywork(workp);
226488f8b78aSgm89044 			return;
226588f8b78aSgm89044 		}
226688f8b78aSgm89044 
226788f8b78aSgm89044 		PUTCSR(dca, csr, workp->dw_mcr_paddr);
226888f8b78aSgm89044 		if (dca_check_acc_handle(dca, dca->dca_regs_handle,
226988f8b78aSgm89044 		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
227088f8b78aSgm89044 			dca_destroywork(workp);
227188f8b78aSgm89044 			return;
227288f8b78aSgm89044 		} else {
227388f8b78aSgm89044 			dca_enqueue(&wlp->dwl_runq, (dca_listnode_t *)workp);
227488f8b78aSgm89044 		}
227588f8b78aSgm89044 
227688f8b78aSgm89044 		DBG(dca, DCHATTY, "posted");
227788f8b78aSgm89044 	}
227888f8b78aSgm89044 }
227988f8b78aSgm89044 
228088f8b78aSgm89044 /*
228188f8b78aSgm89044  * Reclaim completed work, called in interrupt context.
228288f8b78aSgm89044  */
228388f8b78aSgm89044 void
dca_reclaim(dca_t * dca,int mcr)228488f8b78aSgm89044 dca_reclaim(dca_t *dca, int mcr)
228588f8b78aSgm89044 {
228688f8b78aSgm89044 	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
228788f8b78aSgm89044 	dca_work_t	*workp;
228888f8b78aSgm89044 	ushort_t	flags;
228988f8b78aSgm89044 	int		nreclaimed = 0;
229088f8b78aSgm89044 	int		i;
229188f8b78aSgm89044 
229288f8b78aSgm89044 	DBG(dca, DRECLAIM, "worklist = 0x%p (MCR%d)", wlp, mcr);
229388f8b78aSgm89044 	ASSERT(mutex_owned(&wlp->dwl_lock));
229488f8b78aSgm89044 	/*
229588f8b78aSgm89044 	 * For each MCR in the submitted (runq), we check to see if
229688f8b78aSgm89044 	 * it has been processed.  If so, then we note each individual
229788f8b78aSgm89044 	 * job in the MCR, and and do the completion processing for
229888f8b78aSgm89044 	 * each of such job.
229988f8b78aSgm89044 	 */
230088f8b78aSgm89044 	for (;;) {
230188f8b78aSgm89044 
230288f8b78aSgm89044 		workp = (dca_work_t *)dca_peekqueue(&wlp->dwl_runq);
230388f8b78aSgm89044 		if (workp == NULL) {
230488f8b78aSgm89044 			break;
230588f8b78aSgm89044 		}
230688f8b78aSgm89044 
230788f8b78aSgm89044 		/* only sync the MCR flags, since that's all we need */
230888f8b78aSgm89044 		(void) ddi_dma_sync(workp->dw_mcr_dmah, 0, 4,
230988f8b78aSgm89044 		    DDI_DMA_SYNC_FORKERNEL);
231088f8b78aSgm89044 		if (dca_check_dma_handle(dca, workp->dw_mcr_dmah,
231188f8b78aSgm89044 		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
231288f8b78aSgm89044 			dca_rmqueue((dca_listnode_t *)workp);
231388f8b78aSgm89044 			dca_destroywork(workp);
231488f8b78aSgm89044 			return;
231588f8b78aSgm89044 		}
231688f8b78aSgm89044 
231788f8b78aSgm89044 		flags = GETMCR16(workp, MCR_FLAGS);
231888f8b78aSgm89044 		if ((flags & MCRFLAG_FINISHED) == 0) {
231988f8b78aSgm89044 			/* chip is still working on it */
232088f8b78aSgm89044 			DBG(dca, DRECLAIM,
232188f8b78aSgm89044 			    "chip still working on it (MCR%d)", mcr);
232288f8b78aSgm89044 			break;
232388f8b78aSgm89044 		}
232488f8b78aSgm89044 
232588f8b78aSgm89044 		/* its really for us, so remove it from the queue */
232688f8b78aSgm89044 		dca_rmqueue((dca_listnode_t *)workp);
232788f8b78aSgm89044 
232888f8b78aSgm89044 		/* if we were draining, signal on the cv */
232988f8b78aSgm89044 		if (wlp->dwl_drain && QEMPTY(&wlp->dwl_runq)) {
233088f8b78aSgm89044 			cv_signal(&wlp->dwl_cv);
233188f8b78aSgm89044 		}
233288f8b78aSgm89044 
233388f8b78aSgm89044 		/* update statistics, done under the lock */
233488f8b78aSgm89044 		for (i = 0; i < wlp->dwl_reqspermcr; i++) {
233588f8b78aSgm89044 			dca_request_t *reqp = workp->dw_reqs[i];
233688f8b78aSgm89044 			if (reqp == NULL) {
233788f8b78aSgm89044 				continue;
233888f8b78aSgm89044 			}
233988f8b78aSgm89044 			if (reqp->dr_byte_stat >= 0) {
234088f8b78aSgm89044 				dca->dca_stats[reqp->dr_byte_stat] +=
234188f8b78aSgm89044 				    reqp->dr_pkt_length;
234288f8b78aSgm89044 			}
234388f8b78aSgm89044 			if (reqp->dr_job_stat >= 0) {
234488f8b78aSgm89044 				dca->dca_stats[reqp->dr_job_stat]++;
234588f8b78aSgm89044 			}
234688f8b78aSgm89044 		}
234788f8b78aSgm89044 		mutex_exit(&wlp->dwl_lock);
234888f8b78aSgm89044 
234988f8b78aSgm89044 		for (i = 0; i < wlp->dwl_reqspermcr; i++) {
235088f8b78aSgm89044 			dca_request_t *reqp = workp->dw_reqs[i];
235188f8b78aSgm89044 
235288f8b78aSgm89044 			if (reqp == NULL) {
235388f8b78aSgm89044 				continue;
235488f8b78aSgm89044 			}
235588f8b78aSgm89044 
235688f8b78aSgm89044 			/* Do the callback. */
235788f8b78aSgm89044 			workp->dw_reqs[i] = NULL;
235888f8b78aSgm89044 			dca_done(reqp, CRYPTO_SUCCESS);
235988f8b78aSgm89044 
236088f8b78aSgm89044 			nreclaimed++;
236188f8b78aSgm89044 		}
236288f8b78aSgm89044 
236388f8b78aSgm89044 		/* now we can release the work */
236488f8b78aSgm89044 		dca_freework(workp);
23653383b6ddSqs148142 
23663383b6ddSqs148142 		mutex_enter(&wlp->dwl_lock);
236788f8b78aSgm89044 	}
236888f8b78aSgm89044 	DBG(dca, DRECLAIM, "reclaimed %d cmds", nreclaimed);
236988f8b78aSgm89044 }
237088f8b78aSgm89044 
237188f8b78aSgm89044 int
dca_length(crypto_data_t * cdata)237288f8b78aSgm89044 dca_length(crypto_data_t *cdata)
237388f8b78aSgm89044 {
237488f8b78aSgm89044 	return (cdata->cd_length);
237588f8b78aSgm89044 }
237688f8b78aSgm89044 
237788f8b78aSgm89044 /*
237888f8b78aSgm89044  * This is the callback function called from the interrupt when a kCF job
237988f8b78aSgm89044  * completes.  It does some driver-specific things, and then calls the
238088f8b78aSgm89044  * kCF-provided callback.  Finally, it cleans up the state for the work
238188f8b78aSgm89044  * request and drops the reference count to allow for DR.
238288f8b78aSgm89044  */
238388f8b78aSgm89044 void
dca_done(dca_request_t * reqp,int err)238488f8b78aSgm89044 dca_done(dca_request_t *reqp, int err)
238588f8b78aSgm89044 {
238688f8b78aSgm89044 	uint64_t	ena = 0;
238788f8b78aSgm89044 
238888f8b78aSgm89044 	/* unbind any chains we were using */
238988f8b78aSgm89044 	if (dca_unbindchains(reqp) != DDI_SUCCESS) {
239088f8b78aSgm89044 		/* DMA failure */
239188f8b78aSgm89044 		ena = dca_ena(ena);
239288f8b78aSgm89044 		dca_failure(reqp->dr_dca, DDI_DATAPATH_FAULT,
239388f8b78aSgm89044 		    DCA_FM_ECLASS_NONE, ena, CRYPTO_DEVICE_ERROR,
239488f8b78aSgm89044 		    "fault on buffer DMA handle");
239588f8b78aSgm89044 		if (err == CRYPTO_SUCCESS) {
239688f8b78aSgm89044 			err = CRYPTO_DEVICE_ERROR;
239788f8b78aSgm89044 		}
239888f8b78aSgm89044 	}
239988f8b78aSgm89044 
240088f8b78aSgm89044 	if (reqp->dr_callback != NULL) {
240188f8b78aSgm89044 		reqp->dr_callback(reqp, err);
240288f8b78aSgm89044 	} else {
240388f8b78aSgm89044 		dca_freereq(reqp);
240488f8b78aSgm89044 	}
240588f8b78aSgm89044 }
240688f8b78aSgm89044 
240788f8b78aSgm89044 /*
240888f8b78aSgm89044  * Call this when a failure is detected.  It will reset the chip,
240988f8b78aSgm89044  * log a message, alert kCF, and mark jobs in the runq as failed.
241088f8b78aSgm89044  */
241188f8b78aSgm89044 /* ARGSUSED */
241288f8b78aSgm89044 void
dca_failure(dca_t * dca,ddi_fault_location_t loc,dca_fma_eclass_t index,uint64_t ena,int errno,char * mess,...)241388f8b78aSgm89044 dca_failure(dca_t *dca, ddi_fault_location_t loc, dca_fma_eclass_t index,
241488f8b78aSgm89044     uint64_t ena, int errno, char *mess, ...)
241588f8b78aSgm89044 {
241688f8b78aSgm89044 	va_list	ap;
241788f8b78aSgm89044 	char	buf[256];
241888f8b78aSgm89044 	int	mcr;
241988f8b78aSgm89044 	char	*eclass;
242088f8b78aSgm89044 	int	have_mutex;
242188f8b78aSgm89044 
242288f8b78aSgm89044 	va_start(ap, mess);
242388f8b78aSgm89044 	(void) vsprintf(buf, mess, ap);
242488f8b78aSgm89044 	va_end(ap);
242588f8b78aSgm89044 
242688f8b78aSgm89044 	eclass = dca_fma_eclass_string(dca->dca_model, index);
242788f8b78aSgm89044 
242888f8b78aSgm89044 	if (DDI_FM_EREPORT_CAP(dca->fm_capabilities) &&
242988f8b78aSgm89044 	    index != DCA_FM_ECLASS_NONE) {
243088f8b78aSgm89044 		ddi_fm_ereport_post(dca->dca_dip, eclass, ena,
243188f8b78aSgm89044 		    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8,
243288f8b78aSgm89044 		    FM_EREPORT_VERS0, NULL);
243388f8b78aSgm89044 
243488f8b78aSgm89044 		/* Report the impact of the failure to the DDI. */
243588f8b78aSgm89044 		ddi_fm_service_impact(dca->dca_dip, DDI_SERVICE_LOST);
243688f8b78aSgm89044 	} else {
243788f8b78aSgm89044 		/* Just log the error string to the message log */
243888f8b78aSgm89044 		dca_error(dca, buf);
243988f8b78aSgm89044 	}
244088f8b78aSgm89044 
244188f8b78aSgm89044 	/*
244288f8b78aSgm89044 	 * Indicate a failure (keeps schedule from running).
244388f8b78aSgm89044 	 */
244488f8b78aSgm89044 	dca->dca_flags |= DCA_FAILED;
244588f8b78aSgm89044 
244688f8b78aSgm89044 	/*
244788f8b78aSgm89044 	 * Reset the chip.  This should also have as a side effect, the
244888f8b78aSgm89044 	 * disabling of all interrupts from the device.
244988f8b78aSgm89044 	 */
245088f8b78aSgm89044 	(void) dca_reset(dca, 1);
245188f8b78aSgm89044 
245288f8b78aSgm89044 	/*
245388f8b78aSgm89044 	 * Report the failure to kCF.
245488f8b78aSgm89044 	 */
245588f8b78aSgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
245688f8b78aSgm89044 		if (WORKLIST(dca, mcr)->dwl_prov) {
245788f8b78aSgm89044 			crypto_prov_notify(WORKLIST(dca, mcr)->dwl_prov,
245888f8b78aSgm89044 			    CRYPTO_PROVIDER_FAILED);
245988f8b78aSgm89044 		}
246088f8b78aSgm89044 	}
246188f8b78aSgm89044 
246288f8b78aSgm89044 	/*
246388f8b78aSgm89044 	 * Return jobs not sent to hardware back to kCF.
246488f8b78aSgm89044 	 */
246588f8b78aSgm89044 	dca_rejectjobs(dca);
246688f8b78aSgm89044 
246788f8b78aSgm89044 	/*
246888f8b78aSgm89044 	 * From this point on, no new work should be arriving, and the
246988f8b78aSgm89044 	 * chip should not be doing any active DMA.
247088f8b78aSgm89044 	 */
247188f8b78aSgm89044 
247288f8b78aSgm89044 	/*
247388f8b78aSgm89044 	 * Now find all the work submitted to the device and fail
247488f8b78aSgm89044 	 * them.
247588f8b78aSgm89044 	 */
247688f8b78aSgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
247788f8b78aSgm89044 		dca_worklist_t	*wlp;
247888f8b78aSgm89044 		int		i;
247988f8b78aSgm89044 
248088f8b78aSgm89044 		wlp = WORKLIST(dca, mcr);
248188f8b78aSgm89044 
248288f8b78aSgm89044 		if (wlp == NULL || wlp->dwl_waitq.dl_prev == NULL) {
248388f8b78aSgm89044 			continue;
248488f8b78aSgm89044 		}
248588f8b78aSgm89044 		for (;;) {
248688f8b78aSgm89044 			dca_work_t	*workp;
248788f8b78aSgm89044 
248888f8b78aSgm89044 			have_mutex = mutex_tryenter(&wlp->dwl_lock);
248988f8b78aSgm89044 			workp = (dca_work_t *)dca_dequeue(&wlp->dwl_runq);
249088f8b78aSgm89044 			if (workp == NULL) {
249188f8b78aSgm89044 				if (have_mutex)
249288f8b78aSgm89044 					mutex_exit(&wlp->dwl_lock);
249388f8b78aSgm89044 				break;
249488f8b78aSgm89044 			}
249588f8b78aSgm89044 			mutex_exit(&wlp->dwl_lock);
249688f8b78aSgm89044 
249788f8b78aSgm89044 			/*
249888f8b78aSgm89044 			 * Free up requests
249988f8b78aSgm89044 			 */
250088f8b78aSgm89044 			for (i = 0; i < wlp->dwl_reqspermcr; i++) {
250188f8b78aSgm89044 				dca_request_t *reqp = workp->dw_reqs[i];
250288f8b78aSgm89044 				if (reqp) {
250388f8b78aSgm89044 					dca_done(reqp, errno);
250488f8b78aSgm89044 					workp->dw_reqs[i] = NULL;
250588f8b78aSgm89044 				}
250688f8b78aSgm89044 			}
250788f8b78aSgm89044 
250888f8b78aSgm89044 			mutex_enter(&wlp->dwl_lock);
250988f8b78aSgm89044 			/*
251088f8b78aSgm89044 			 * If waiting to drain, signal on the waiter.
251188f8b78aSgm89044 			 */
251288f8b78aSgm89044 			if (wlp->dwl_drain && QEMPTY(&wlp->dwl_runq)) {
251388f8b78aSgm89044 				cv_signal(&wlp->dwl_cv);
251488f8b78aSgm89044 			}
251588f8b78aSgm89044 
251688f8b78aSgm89044 			/*
251788f8b78aSgm89044 			 * Return the work and request structures to
251888f8b78aSgm89044 			 * the free pool.
251988f8b78aSgm89044 			 */
252088f8b78aSgm89044 			dca_freework(workp);
252188f8b78aSgm89044 			if (have_mutex)
252288f8b78aSgm89044 				mutex_exit(&wlp->dwl_lock);
252388f8b78aSgm89044 		}
252488f8b78aSgm89044 	}
252588f8b78aSgm89044 
252688f8b78aSgm89044 }
252788f8b78aSgm89044 
252888f8b78aSgm89044 #ifdef	SCHEDDELAY
252988f8b78aSgm89044 /*
253088f8b78aSgm89044  * Reschedule worklist as needed.
253188f8b78aSgm89044  */
253288f8b78aSgm89044 void
dca_schedtimeout(void * arg)253388f8b78aSgm89044 dca_schedtimeout(void *arg)
253488f8b78aSgm89044 {
253588f8b78aSgm89044 	dca_worklist_t	*wlp = (dca_worklist_t *)arg;
253688f8b78aSgm89044 	mutex_enter(&wlp->dwl_lock);
253788f8b78aSgm89044 	wlp->dwl_schedtid = 0;
253888f8b78aSgm89044 	dca_schedule(wlp->dwl_dca, wlp->dwl_mcr);
253988f8b78aSgm89044 	mutex_exit(&wlp->dwl_lock);
254088f8b78aSgm89044 }
254188f8b78aSgm89044 #endif
254288f8b78aSgm89044 
254388f8b78aSgm89044 /*
254488f8b78aSgm89044  * Check for stalled jobs.
254588f8b78aSgm89044  */
254688f8b78aSgm89044 void
dca_jobtimeout(void * arg)254788f8b78aSgm89044 dca_jobtimeout(void *arg)
254888f8b78aSgm89044 {
254988f8b78aSgm89044 	int		mcr;
255088f8b78aSgm89044 	dca_t		*dca = (dca_t *)arg;
255188f8b78aSgm89044 	int		hung = 0;
255288f8b78aSgm89044 
255388f8b78aSgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
255488f8b78aSgm89044 		dca_worklist_t	*wlp = WORKLIST(dca, mcr);
255588f8b78aSgm89044 		dca_work_t	*workp;
255688f8b78aSgm89044 		clock_t		when;
255788f8b78aSgm89044 
255888f8b78aSgm89044 		mutex_enter(&wlp->dwl_lock);
255988f8b78aSgm89044 		when = ddi_get_lbolt();
256088f8b78aSgm89044 
256188f8b78aSgm89044 		workp = (dca_work_t *)dca_peekqueue(&wlp->dwl_runq);
256288f8b78aSgm89044 		if (workp == NULL) {
256388f8b78aSgm89044 			/* nothing sitting in the queue */
256488f8b78aSgm89044 			mutex_exit(&wlp->dwl_lock);
256588f8b78aSgm89044 			continue;
256688f8b78aSgm89044 		}
256788f8b78aSgm89044 
256888f8b78aSgm89044 		if ((when - workp->dw_lbolt) < drv_usectohz(STALETIME)) {
256988f8b78aSgm89044 			/* request has been queued for less than STALETIME */
257088f8b78aSgm89044 			mutex_exit(&wlp->dwl_lock);
257188f8b78aSgm89044 			continue;
257288f8b78aSgm89044 		}
257388f8b78aSgm89044 
257488f8b78aSgm89044 		/* job has been sitting around for over 1 second, badness */
257588f8b78aSgm89044 		DBG(dca, DWARN, "stale job (0x%p) found in MCR%d!", workp,
257688f8b78aSgm89044 		    mcr);
257788f8b78aSgm89044 
257888f8b78aSgm89044 		/* put it back in the queue, until we reset the chip */
257988f8b78aSgm89044 		hung++;
258088f8b78aSgm89044 		mutex_exit(&wlp->dwl_lock);
258188f8b78aSgm89044 	}
258288f8b78aSgm89044 
258388f8b78aSgm89044 	if (hung) {
258488f8b78aSgm89044 		dca_failure(dca, DDI_DEVICE_FAULT,
258588f8b78aSgm89044 		    DCA_FM_ECLASS_HW_TIMEOUT, dca_ena(0), CRYPTO_DEVICE_ERROR,
258688f8b78aSgm89044 		    "timeout processing job.)");
258788f8b78aSgm89044 	}
258888f8b78aSgm89044 
258988f8b78aSgm89044 	/* reschedule ourself */
259088f8b78aSgm89044 	mutex_enter(&dca->dca_intrlock);
259188f8b78aSgm89044 	if (dca->dca_jobtid == 0) {
259288f8b78aSgm89044 		/* timeout has been canceled, prior to DR */
259388f8b78aSgm89044 		mutex_exit(&dca->dca_intrlock);
259488f8b78aSgm89044 		return;
259588f8b78aSgm89044 	}
259688f8b78aSgm89044 
259788f8b78aSgm89044 	/* check again in 1 second */
259888f8b78aSgm89044 	dca->dca_jobtid = timeout(dca_jobtimeout, arg,
259988f8b78aSgm89044 	    drv_usectohz(SECOND));
260088f8b78aSgm89044 	mutex_exit(&dca->dca_intrlock);
260188f8b78aSgm89044 }
260288f8b78aSgm89044 
260388f8b78aSgm89044 /*
260488f8b78aSgm89044  * This returns all jobs back to kCF.  It assumes that processing
260588f8b78aSgm89044  * on the worklist has halted.
260688f8b78aSgm89044  */
260788f8b78aSgm89044 void
dca_rejectjobs(dca_t * dca)260888f8b78aSgm89044 dca_rejectjobs(dca_t *dca)
260988f8b78aSgm89044 {
261088f8b78aSgm89044 	int mcr;
261188f8b78aSgm89044 	int have_mutex;
261288f8b78aSgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
261388f8b78aSgm89044 		dca_worklist_t	*wlp = WORKLIST(dca, mcr);
261488f8b78aSgm89044 		dca_request_t	*reqp;
261588f8b78aSgm89044 
261688f8b78aSgm89044 		if (wlp == NULL || wlp->dwl_waitq.dl_prev == NULL) {
261788f8b78aSgm89044 			continue;
261888f8b78aSgm89044 		}
261988f8b78aSgm89044 		have_mutex = mutex_tryenter(&wlp->dwl_lock);
262088f8b78aSgm89044 		for (;;) {
262188f8b78aSgm89044 			reqp = (dca_request_t *)dca_unqueue(&wlp->dwl_waitq);
262288f8b78aSgm89044 			if (reqp == NULL) {
262388f8b78aSgm89044 				break;
262488f8b78aSgm89044 			}
262588f8b78aSgm89044 			/* update flow control */
262688f8b78aSgm89044 			wlp->dwl_count--;
262788f8b78aSgm89044 			if ((wlp->dwl_count == wlp->dwl_lowater) &&
262888f8b78aSgm89044 			    (wlp->dwl_busy))  {
262988f8b78aSgm89044 				wlp->dwl_busy = 0;
263088f8b78aSgm89044 				crypto_prov_notify(wlp->dwl_prov,
263188f8b78aSgm89044 				    CRYPTO_PROVIDER_READY);
263288f8b78aSgm89044 			}
263388f8b78aSgm89044 			mutex_exit(&wlp->dwl_lock);
263488f8b78aSgm89044 
263588f8b78aSgm89044 			(void) dca_unbindchains(reqp);
263688f8b78aSgm89044 			reqp->dr_callback(reqp, EAGAIN);
263788f8b78aSgm89044 			mutex_enter(&wlp->dwl_lock);
263888f8b78aSgm89044 		}
263988f8b78aSgm89044 		if (have_mutex)
264088f8b78aSgm89044 			mutex_exit(&wlp->dwl_lock);
264188f8b78aSgm89044 	}
264288f8b78aSgm89044 }
264388f8b78aSgm89044 
264488f8b78aSgm89044 int
dca_drain(dca_t * dca)264588f8b78aSgm89044 dca_drain(dca_t *dca)
264688f8b78aSgm89044 {
264788f8b78aSgm89044 	int mcr;
264888f8b78aSgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
264988f8b78aSgm89044 #ifdef	SCHEDDELAY
265088f8b78aSgm89044 		timeout_id_t	tid;
265188f8b78aSgm89044 #endif
265288f8b78aSgm89044 		dca_worklist_t *wlp = WORKLIST(dca, mcr);
265388f8b78aSgm89044 
265488f8b78aSgm89044 		mutex_enter(&wlp->dwl_lock);
265588f8b78aSgm89044 		wlp->dwl_drain = 1;
265688f8b78aSgm89044 
265788f8b78aSgm89044 		/* give it up to a second to drain from the chip */
265888f8b78aSgm89044 		if (!QEMPTY(&wlp->dwl_runq)) {
2659d3d50737SRafael Vanoni 			(void) cv_reltimedwait(&wlp->dwl_cv, &wlp->dwl_lock,
2660d3d50737SRafael Vanoni 			    drv_usectohz(STALETIME), TR_CLOCK_TICK);
266188f8b78aSgm89044 
266288f8b78aSgm89044 			if (!QEMPTY(&wlp->dwl_runq)) {
266388f8b78aSgm89044 				dca_error(dca, "unable to drain device");
266488f8b78aSgm89044 				mutex_exit(&wlp->dwl_lock);
266588f8b78aSgm89044 				dca_undrain(dca);
266688f8b78aSgm89044 				return (EBUSY);
266788f8b78aSgm89044 			}
266888f8b78aSgm89044 		}
266988f8b78aSgm89044 
267088f8b78aSgm89044 #ifdef	SCHEDDELAY
267188f8b78aSgm89044 		tid = wlp->dwl_schedtid;
267288f8b78aSgm89044 		mutex_exit(&wlp->dwl_lock);
267388f8b78aSgm89044 
267488f8b78aSgm89044 		/*
267588f8b78aSgm89044 		 * untimeout outside the lock -- this is safe because we
267688f8b78aSgm89044 		 * have set the drain flag, so dca_schedule() will not
267788f8b78aSgm89044 		 * reschedule another timeout
267888f8b78aSgm89044 		 */
267988f8b78aSgm89044 		if (tid) {
268088f8b78aSgm89044 			untimeout(tid);
268188f8b78aSgm89044 		}
268288f8b78aSgm89044 #else
268388f8b78aSgm89044 		mutex_exit(&wlp->dwl_lock);
268488f8b78aSgm89044 #endif
268588f8b78aSgm89044 	}
268688f8b78aSgm89044 	return (0);
268788f8b78aSgm89044 }
268888f8b78aSgm89044 
268988f8b78aSgm89044 void
dca_undrain(dca_t * dca)269088f8b78aSgm89044 dca_undrain(dca_t *dca)
269188f8b78aSgm89044 {
269288f8b78aSgm89044 	int	mcr;
269388f8b78aSgm89044 
269488f8b78aSgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
269588f8b78aSgm89044 		dca_worklist_t	*wlp = WORKLIST(dca, mcr);
269688f8b78aSgm89044 		mutex_enter(&wlp->dwl_lock);
269788f8b78aSgm89044 		wlp->dwl_drain = 0;
269888f8b78aSgm89044 		dca_schedule(dca, mcr);
269988f8b78aSgm89044 		mutex_exit(&wlp->dwl_lock);
270088f8b78aSgm89044 	}
270188f8b78aSgm89044 }
270288f8b78aSgm89044 
270388f8b78aSgm89044 /*
270488f8b78aSgm89044  * Duplicate the crypto_data_t structure, but point to the original
270588f8b78aSgm89044  * buffers.
270688f8b78aSgm89044  */
270788f8b78aSgm89044 int
dca_dupcrypto(crypto_data_t * input,crypto_data_t * ninput)270888f8b78aSgm89044 dca_dupcrypto(crypto_data_t *input, crypto_data_t *ninput)
270988f8b78aSgm89044 {
271088f8b78aSgm89044 	ninput->cd_format = input->cd_format;
271188f8b78aSgm89044 	ninput->cd_offset = input->cd_offset;
271288f8b78aSgm89044 	ninput->cd_length = input->cd_length;
271388f8b78aSgm89044 	ninput->cd_miscdata = input->cd_miscdata;
271488f8b78aSgm89044 
271588f8b78aSgm89044 	switch (input->cd_format) {
271688f8b78aSgm89044 	case CRYPTO_DATA_RAW:
271788f8b78aSgm89044 		ninput->cd_raw.iov_base = input->cd_raw.iov_base;
271888f8b78aSgm89044 		ninput->cd_raw.iov_len = input->cd_raw.iov_len;
271988f8b78aSgm89044 		break;
272088f8b78aSgm89044 
272188f8b78aSgm89044 	case CRYPTO_DATA_UIO:
272288f8b78aSgm89044 		ninput->cd_uio = input->cd_uio;
272388f8b78aSgm89044 		break;
272488f8b78aSgm89044 
272588f8b78aSgm89044 	case CRYPTO_DATA_MBLK:
272688f8b78aSgm89044 		ninput->cd_mp = input->cd_mp;
272788f8b78aSgm89044 		break;
272888f8b78aSgm89044 
272988f8b78aSgm89044 	default:
273088f8b78aSgm89044 		DBG(NULL, DWARN,
273188f8b78aSgm89044 		    "dca_dupcrypto: unrecognised crypto data format");
273288f8b78aSgm89044 		return (CRYPTO_FAILED);
273388f8b78aSgm89044 	}
273488f8b78aSgm89044 
273588f8b78aSgm89044 	return (CRYPTO_SUCCESS);
273688f8b78aSgm89044 }
273788f8b78aSgm89044 
273888f8b78aSgm89044 /*
273988f8b78aSgm89044  * Performs validation checks on the input and output data structures.
274088f8b78aSgm89044  */
274188f8b78aSgm89044 int
dca_verifyio(crypto_data_t * input,crypto_data_t * output)274288f8b78aSgm89044 dca_verifyio(crypto_data_t *input, crypto_data_t *output)
274388f8b78aSgm89044 {
274488f8b78aSgm89044 	int	rv = CRYPTO_SUCCESS;
274588f8b78aSgm89044 
274688f8b78aSgm89044 	switch (input->cd_format) {
274788f8b78aSgm89044 	case CRYPTO_DATA_RAW:
274888f8b78aSgm89044 		break;
274988f8b78aSgm89044 
275088f8b78aSgm89044 	case CRYPTO_DATA_UIO:
275188f8b78aSgm89044 		/* we support only kernel buffer */
275288f8b78aSgm89044 		if (input->cd_uio->uio_segflg != UIO_SYSSPACE) {
275388f8b78aSgm89044 			DBG(NULL, DWARN, "non kernel input uio buffer");
275488f8b78aSgm89044 			rv = CRYPTO_ARGUMENTS_BAD;
275588f8b78aSgm89044 		}
275688f8b78aSgm89044 		break;
275788f8b78aSgm89044 
275888f8b78aSgm89044 	case CRYPTO_DATA_MBLK:
275988f8b78aSgm89044 		break;
276088f8b78aSgm89044 
276188f8b78aSgm89044 	default:
276288f8b78aSgm89044 		DBG(NULL, DWARN, "unrecognised input crypto data format");
276388f8b78aSgm89044 		rv = CRYPTO_ARGUMENTS_BAD;
276488f8b78aSgm89044 	}
276588f8b78aSgm89044 
276688f8b78aSgm89044 	switch (output->cd_format) {
276788f8b78aSgm89044 	case CRYPTO_DATA_RAW:
276888f8b78aSgm89044 		break;
276988f8b78aSgm89044 
277088f8b78aSgm89044 	case CRYPTO_DATA_UIO:
277188f8b78aSgm89044 		/* we support only kernel buffer */
277288f8b78aSgm89044 		if (output->cd_uio->uio_segflg != UIO_SYSSPACE) {
277388f8b78aSgm89044 			DBG(NULL, DWARN, "non kernel output uio buffer");
277488f8b78aSgm89044 			rv = CRYPTO_ARGUMENTS_BAD;
277588f8b78aSgm89044 		}
277688f8b78aSgm89044 		break;
277788f8b78aSgm89044 
277888f8b78aSgm89044 	case CRYPTO_DATA_MBLK:
277988f8b78aSgm89044 		break;
278088f8b78aSgm89044 
278188f8b78aSgm89044 	default:
278288f8b78aSgm89044 		DBG(NULL, DWARN, "unrecognised output crypto data format");
278388f8b78aSgm89044 		rv = CRYPTO_ARGUMENTS_BAD;
278488f8b78aSgm89044 	}
278588f8b78aSgm89044 
278688f8b78aSgm89044 	return (rv);
278788f8b78aSgm89044 }
278888f8b78aSgm89044 
278988f8b78aSgm89044 /*
279088f8b78aSgm89044  * data: source crypto_data_t struct
279188f8b78aSgm89044  * off:	offset into the source before commencing copy
279288f8b78aSgm89044  * count: the amount of data to copy
279388f8b78aSgm89044  * dest: destination buffer
279488f8b78aSgm89044  */
279588f8b78aSgm89044 int
dca_getbufbytes(crypto_data_t * data,size_t off,int count,uchar_t * dest)279688f8b78aSgm89044 dca_getbufbytes(crypto_data_t *data, size_t off, int count, uchar_t *dest)
279788f8b78aSgm89044 {
279888f8b78aSgm89044 	int rv = CRYPTO_SUCCESS;
279988f8b78aSgm89044 	uio_t *uiop;
280088f8b78aSgm89044 	uint_t vec_idx;
280188f8b78aSgm89044 	size_t cur_len;
280288f8b78aSgm89044 	mblk_t *mp;
280388f8b78aSgm89044 
280488f8b78aSgm89044 	if (count == 0) {
280588f8b78aSgm89044 		/* We don't want anything so we're done. */
280688f8b78aSgm89044 		return (rv);
280788f8b78aSgm89044 	}
280888f8b78aSgm89044 
280988f8b78aSgm89044 	/*
281088f8b78aSgm89044 	 * Sanity check that we haven't specified a length greater than the
281188f8b78aSgm89044 	 * offset adjusted size of the buffer.
281288f8b78aSgm89044 	 */
281388f8b78aSgm89044 	if (count > (data->cd_length - off)) {
281488f8b78aSgm89044 		return (CRYPTO_DATA_LEN_RANGE);
281588f8b78aSgm89044 	}
281688f8b78aSgm89044 
281788f8b78aSgm89044 	/* Add the internal crypto_data offset to the requested offset. */
281888f8b78aSgm89044 	off += data->cd_offset;
281988f8b78aSgm89044 
282088f8b78aSgm89044 	switch (data->cd_format) {
282188f8b78aSgm89044 	case CRYPTO_DATA_RAW:
282288f8b78aSgm89044 		bcopy(data->cd_raw.iov_base + off, dest, count);
282388f8b78aSgm89044 		break;
282488f8b78aSgm89044 
282588f8b78aSgm89044 	case CRYPTO_DATA_UIO:
282688f8b78aSgm89044 		/*
282788f8b78aSgm89044 		 * Jump to the first iovec containing data to be
282888f8b78aSgm89044 		 * processed.
282988f8b78aSgm89044 		 */
283088f8b78aSgm89044 		uiop = data->cd_uio;
283188f8b78aSgm89044 		for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
283288f8b78aSgm89044 		    off >= uiop->uio_iov[vec_idx].iov_len;
2833d8dd9913Sgm89044 		    off -= uiop->uio_iov[vec_idx++].iov_len)
2834d8dd9913Sgm89044 			;
283588f8b78aSgm89044 		if (vec_idx == uiop->uio_iovcnt) {
283688f8b78aSgm89044 			/*
283788f8b78aSgm89044 			 * The caller specified an offset that is larger than
283888f8b78aSgm89044 			 * the total size of the buffers it provided.
283988f8b78aSgm89044 			 */
284088f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
284188f8b78aSgm89044 		}
284288f8b78aSgm89044 
284388f8b78aSgm89044 		/*
284488f8b78aSgm89044 		 * Now process the iovecs.
284588f8b78aSgm89044 		 */
284688f8b78aSgm89044 		while (vec_idx < uiop->uio_iovcnt && count > 0) {
284788f8b78aSgm89044 			cur_len = min(uiop->uio_iov[vec_idx].iov_len -
284888f8b78aSgm89044 			    off, count);
284988f8b78aSgm89044 			bcopy(uiop->uio_iov[vec_idx].iov_base + off, dest,
285088f8b78aSgm89044 			    cur_len);
285188f8b78aSgm89044 			count -= cur_len;
285288f8b78aSgm89044 			dest += cur_len;
285388f8b78aSgm89044 			vec_idx++;
285488f8b78aSgm89044 			off = 0;
285588f8b78aSgm89044 		}
285688f8b78aSgm89044 
285788f8b78aSgm89044 		if (vec_idx == uiop->uio_iovcnt && count > 0) {
285888f8b78aSgm89044 			/*
285988f8b78aSgm89044 			 * The end of the specified iovec's was reached but
286088f8b78aSgm89044 			 * the length requested could not be processed
286188f8b78aSgm89044 			 * (requested to digest more data than it provided).
286288f8b78aSgm89044 			 */
286388f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
286488f8b78aSgm89044 		}
286588f8b78aSgm89044 		break;
286688f8b78aSgm89044 
286788f8b78aSgm89044 	case CRYPTO_DATA_MBLK:
286888f8b78aSgm89044 		/*
286988f8b78aSgm89044 		 * Jump to the first mblk_t containing data to be processed.
287088f8b78aSgm89044 		 */
287188f8b78aSgm89044 		for (mp = data->cd_mp; mp != NULL && off >= MBLKL(mp);
2872d8dd9913Sgm89044 		    off -= MBLKL(mp), mp = mp->b_cont)
2873d8dd9913Sgm89044 			;
287488f8b78aSgm89044 		if (mp == NULL) {
287588f8b78aSgm89044 			/*
287688f8b78aSgm89044 			 * The caller specified an offset that is larger than
287788f8b78aSgm89044 			 * the total size of the buffers it provided.
287888f8b78aSgm89044 			 */
287988f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
288088f8b78aSgm89044 		}
288188f8b78aSgm89044 
288288f8b78aSgm89044 		/*
288388f8b78aSgm89044 		 * Now do the processing on the mblk chain.
288488f8b78aSgm89044 		 */
288588f8b78aSgm89044 		while (mp != NULL && count > 0) {
288688f8b78aSgm89044 			cur_len = min(MBLKL(mp) - off, count);
288788f8b78aSgm89044 			bcopy((char *)(mp->b_rptr + off), dest, cur_len);
288888f8b78aSgm89044 			count -= cur_len;
288988f8b78aSgm89044 			dest += cur_len;
289088f8b78aSgm89044 			mp = mp->b_cont;
289188f8b78aSgm89044 			off = 0;
289288f8b78aSgm89044 		}
289388f8b78aSgm89044 
289488f8b78aSgm89044 		if (mp == NULL && count > 0) {
289588f8b78aSgm89044 			/*
289688f8b78aSgm89044 			 * The end of the mblk was reached but the length
289788f8b78aSgm89044 			 * requested could not be processed, (requested to
289888f8b78aSgm89044 			 * digest more data than it provided).
289988f8b78aSgm89044 			 */
290088f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
290188f8b78aSgm89044 		}
290288f8b78aSgm89044 		break;
290388f8b78aSgm89044 
290488f8b78aSgm89044 	default:
290588f8b78aSgm89044 		DBG(NULL, DWARN, "unrecognised crypto data format");
290688f8b78aSgm89044 		rv = CRYPTO_ARGUMENTS_BAD;
290788f8b78aSgm89044 	}
290888f8b78aSgm89044 	return (rv);
290988f8b78aSgm89044 }
291088f8b78aSgm89044 
291188f8b78aSgm89044 
291288f8b78aSgm89044 /*
291388f8b78aSgm89044  * Performs the input, output or hard scatter/gather checks on the specified
291488f8b78aSgm89044  * crypto_data_t struct. Returns true if the data is scatter/gather in nature
291588f8b78aSgm89044  * ie fails the test.
291688f8b78aSgm89044  */
291788f8b78aSgm89044 int
dca_sgcheck(dca_t * dca,crypto_data_t * data,dca_sg_param_t val)291888f8b78aSgm89044 dca_sgcheck(dca_t *dca, crypto_data_t *data, dca_sg_param_t val)
291988f8b78aSgm89044 {
292088f8b78aSgm89044 	uio_t *uiop;
292188f8b78aSgm89044 	mblk_t *mp;
292288f8b78aSgm89044 	int rv = FALSE;
292388f8b78aSgm89044 
292488f8b78aSgm89044 	switch (val) {
292588f8b78aSgm89044 	case DCA_SG_CONTIG:
292688f8b78aSgm89044 		/*
292788f8b78aSgm89044 		 * Check for a contiguous data buffer.
292888f8b78aSgm89044 		 */
292988f8b78aSgm89044 		switch (data->cd_format) {
293088f8b78aSgm89044 		case CRYPTO_DATA_RAW:
293188f8b78aSgm89044 			/* Contiguous in nature */
293288f8b78aSgm89044 			break;
293388f8b78aSgm89044 
293488f8b78aSgm89044 		case CRYPTO_DATA_UIO:
293588f8b78aSgm89044 			if (data->cd_uio->uio_iovcnt > 1)
293688f8b78aSgm89044 				rv = TRUE;
293788f8b78aSgm89044 			break;
293888f8b78aSgm89044 
293988f8b78aSgm89044 		case CRYPTO_DATA_MBLK:
294088f8b78aSgm89044 			mp = data->cd_mp;
294188f8b78aSgm89044 			if (mp->b_cont != NULL)
294288f8b78aSgm89044 				rv = TRUE;
294388f8b78aSgm89044 			break;
294488f8b78aSgm89044 
294588f8b78aSgm89044 		default:
294688f8b78aSgm89044 			DBG(NULL, DWARN, "unrecognised crypto data format");
294788f8b78aSgm89044 		}
294888f8b78aSgm89044 		break;
294988f8b78aSgm89044 
295088f8b78aSgm89044 	case DCA_SG_WALIGN:
295188f8b78aSgm89044 		/*
295288f8b78aSgm89044 		 * Check for a contiguous data buffer that is 32-bit word
295388f8b78aSgm89044 		 * aligned and is of word multiples in size.
295488f8b78aSgm89044 		 */
295588f8b78aSgm89044 		switch (data->cd_format) {
295688f8b78aSgm89044 		case CRYPTO_DATA_RAW:
295788f8b78aSgm89044 			if ((data->cd_raw.iov_len % sizeof (uint32_t)) ||
295888f8b78aSgm89044 			    ((uintptr_t)data->cd_raw.iov_base %
295988f8b78aSgm89044 			    sizeof (uint32_t))) {
296088f8b78aSgm89044 				rv = TRUE;
296188f8b78aSgm89044 			}
296288f8b78aSgm89044 			break;
296388f8b78aSgm89044 
296488f8b78aSgm89044 		case CRYPTO_DATA_UIO:
296588f8b78aSgm89044 			uiop = data->cd_uio;
296688f8b78aSgm89044 			if (uiop->uio_iovcnt > 1) {
296788f8b78aSgm89044 				return (TRUE);
296888f8b78aSgm89044 			}
296988f8b78aSgm89044 			/* So there is only one iovec */
297088f8b78aSgm89044 			if ((uiop->uio_iov[0].iov_len % sizeof (uint32_t)) ||
297188f8b78aSgm89044 			    ((uintptr_t)uiop->uio_iov[0].iov_base %
297288f8b78aSgm89044 			    sizeof (uint32_t))) {
297388f8b78aSgm89044 				rv = TRUE;
297488f8b78aSgm89044 			}
297588f8b78aSgm89044 			break;
297688f8b78aSgm89044 
297788f8b78aSgm89044 		case CRYPTO_DATA_MBLK:
297888f8b78aSgm89044 			mp = data->cd_mp;
297988f8b78aSgm89044 			if (mp->b_cont != NULL) {
298088f8b78aSgm89044 				return (TRUE);
298188f8b78aSgm89044 			}
298288f8b78aSgm89044 			/* So there is only one mblk in the chain */
298388f8b78aSgm89044 			if ((MBLKL(mp) % sizeof (uint32_t)) ||
298488f8b78aSgm89044 			    ((uintptr_t)mp->b_rptr % sizeof (uint32_t))) {
298588f8b78aSgm89044 				rv = TRUE;
298688f8b78aSgm89044 			}
298788f8b78aSgm89044 			break;
298888f8b78aSgm89044 
298988f8b78aSgm89044 		default:
299088f8b78aSgm89044 			DBG(NULL, DWARN, "unrecognised crypto data format");
299188f8b78aSgm89044 		}
299288f8b78aSgm89044 		break;
299388f8b78aSgm89044 
299488f8b78aSgm89044 	case DCA_SG_PALIGN:
299588f8b78aSgm89044 		/*
299688f8b78aSgm89044 		 * Check that the data buffer is page aligned and is of
299788f8b78aSgm89044 		 * page multiples in size.
299888f8b78aSgm89044 		 */
299988f8b78aSgm89044 		switch (data->cd_format) {
300088f8b78aSgm89044 		case CRYPTO_DATA_RAW:
300188f8b78aSgm89044 			if ((data->cd_length % dca->dca_pagesize) ||
300288f8b78aSgm89044 			    ((uintptr_t)data->cd_raw.iov_base %
300388f8b78aSgm89044 			    dca->dca_pagesize)) {
300488f8b78aSgm89044 				rv = TRUE;
300588f8b78aSgm89044 			}
300688f8b78aSgm89044 			break;
300788f8b78aSgm89044 
300888f8b78aSgm89044 		case CRYPTO_DATA_UIO:
300988f8b78aSgm89044 			uiop = data->cd_uio;
301088f8b78aSgm89044 			if ((uiop->uio_iov[0].iov_len % dca->dca_pagesize) ||
301188f8b78aSgm89044 			    ((uintptr_t)uiop->uio_iov[0].iov_base %
301288f8b78aSgm89044 			    dca->dca_pagesize)) {
301388f8b78aSgm89044 				rv = TRUE;
301488f8b78aSgm89044 			}
301588f8b78aSgm89044 			break;
301688f8b78aSgm89044 
301788f8b78aSgm89044 		case CRYPTO_DATA_MBLK:
301888f8b78aSgm89044 			mp = data->cd_mp;
301988f8b78aSgm89044 			if ((MBLKL(mp) % dca->dca_pagesize) ||
302088f8b78aSgm89044 			    ((uintptr_t)mp->b_rptr % dca->dca_pagesize)) {
302188f8b78aSgm89044 				rv = TRUE;
302288f8b78aSgm89044 			}
302388f8b78aSgm89044 			break;
302488f8b78aSgm89044 
302588f8b78aSgm89044 		default:
302688f8b78aSgm89044 			DBG(NULL, DWARN, "unrecognised crypto data format");
302788f8b78aSgm89044 		}
302888f8b78aSgm89044 		break;
302988f8b78aSgm89044 
303088f8b78aSgm89044 	default:
303188f8b78aSgm89044 		DBG(NULL, DWARN, "unrecognised scatter/gather param type");
303288f8b78aSgm89044 	}
303388f8b78aSgm89044 
303488f8b78aSgm89044 	return (rv);
303588f8b78aSgm89044 }
303688f8b78aSgm89044 
303788f8b78aSgm89044 /*
303888f8b78aSgm89044  * Increments the cd_offset and decrements the cd_length as the data is
303988f8b78aSgm89044  * gathered from the crypto_data_t struct.
304088f8b78aSgm89044  * The data is reverse-copied into the dest buffer if the flag is true.
304188f8b78aSgm89044  */
304288f8b78aSgm89044 int
dca_gather(crypto_data_t * in,char * dest,int count,int reverse)304388f8b78aSgm89044 dca_gather(crypto_data_t *in, char *dest, int count, int reverse)
304488f8b78aSgm89044 {
304588f8b78aSgm89044 	int	rv = CRYPTO_SUCCESS;
304688f8b78aSgm89044 	uint_t	vec_idx;
304788f8b78aSgm89044 	uio_t	*uiop;
304888f8b78aSgm89044 	off_t	off = in->cd_offset;
304988f8b78aSgm89044 	size_t	cur_len;
305088f8b78aSgm89044 	mblk_t	*mp;
305188f8b78aSgm89044 
305288f8b78aSgm89044 	switch (in->cd_format) {
305388f8b78aSgm89044 	case CRYPTO_DATA_RAW:
305488f8b78aSgm89044 		if (count > in->cd_length) {
305588f8b78aSgm89044 			/*
305688f8b78aSgm89044 			 * The caller specified a length greater than the
305788f8b78aSgm89044 			 * size of the buffer.
305888f8b78aSgm89044 			 */
305988f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
306088f8b78aSgm89044 		}
306188f8b78aSgm89044 		if (reverse)
306288f8b78aSgm89044 			dca_reverse(in->cd_raw.iov_base + off, dest, count,
306388f8b78aSgm89044 			    count);
306488f8b78aSgm89044 		else
306588f8b78aSgm89044 			bcopy(in->cd_raw.iov_base + in->cd_offset, dest, count);
306688f8b78aSgm89044 		in->cd_offset += count;
306788f8b78aSgm89044 		in->cd_length -= count;
306888f8b78aSgm89044 		break;
306988f8b78aSgm89044 
307088f8b78aSgm89044 	case CRYPTO_DATA_UIO:
307188f8b78aSgm89044 		/*
307288f8b78aSgm89044 		 * Jump to the first iovec containing data to be processed.
307388f8b78aSgm89044 		 */
307488f8b78aSgm89044 		uiop = in->cd_uio;
307588f8b78aSgm89044 		for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
307688f8b78aSgm89044 		    off >= uiop->uio_iov[vec_idx].iov_len;
3077d8dd9913Sgm89044 		    off -= uiop->uio_iov[vec_idx++].iov_len)
3078d8dd9913Sgm89044 			;
307988f8b78aSgm89044 		if (vec_idx == uiop->uio_iovcnt) {
308088f8b78aSgm89044 			/*
308188f8b78aSgm89044 			 * The caller specified an offset that is larger than
308288f8b78aSgm89044 			 * the total size of the buffers it provided.
308388f8b78aSgm89044 			 */
308488f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
308588f8b78aSgm89044 		}
308688f8b78aSgm89044 
308788f8b78aSgm89044 		/*
308888f8b78aSgm89044 		 * Now process the iovecs.
308988f8b78aSgm89044 		 */
309088f8b78aSgm89044 		while (vec_idx < uiop->uio_iovcnt && count > 0) {
309188f8b78aSgm89044 			cur_len = min(uiop->uio_iov[vec_idx].iov_len -
309288f8b78aSgm89044 			    off, count);
309388f8b78aSgm89044 			count -= cur_len;
309488f8b78aSgm89044 			if (reverse) {
309588f8b78aSgm89044 				/* Fill the dest buffer from the end */
309688f8b78aSgm89044 				dca_reverse(uiop->uio_iov[vec_idx].iov_base +
309788f8b78aSgm89044 				    off, dest+count, cur_len, cur_len);
309888f8b78aSgm89044 			} else {
309988f8b78aSgm89044 				bcopy(uiop->uio_iov[vec_idx].iov_base + off,
310088f8b78aSgm89044 				    dest, cur_len);
310188f8b78aSgm89044 				dest += cur_len;
310288f8b78aSgm89044 			}
310388f8b78aSgm89044 			in->cd_offset += cur_len;
310488f8b78aSgm89044 			in->cd_length -= cur_len;
310588f8b78aSgm89044 			vec_idx++;
310688f8b78aSgm89044 			off = 0;
310788f8b78aSgm89044 		}
310888f8b78aSgm89044 
310988f8b78aSgm89044 		if (vec_idx == uiop->uio_iovcnt && count > 0) {
311088f8b78aSgm89044 			/*
311188f8b78aSgm89044 			 * The end of the specified iovec's was reached but
311288f8b78aSgm89044 			 * the length requested could not be processed
311388f8b78aSgm89044 			 * (requested to digest more data than it provided).
311488f8b78aSgm89044 			 */
311588f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
311688f8b78aSgm89044 		}
311788f8b78aSgm89044 		break;
311888f8b78aSgm89044 
311988f8b78aSgm89044 	case CRYPTO_DATA_MBLK:
312088f8b78aSgm89044 		/*
312188f8b78aSgm89044 		 * Jump to the first mblk_t containing data to be processed.
312288f8b78aSgm89044 		 */
312388f8b78aSgm89044 		for (mp = in->cd_mp; mp != NULL && off >= MBLKL(mp);
3124d8dd9913Sgm89044 		    off -= MBLKL(mp), mp = mp->b_cont)
3125d8dd9913Sgm89044 			;
312688f8b78aSgm89044 		if (mp == NULL) {
312788f8b78aSgm89044 			/*
312888f8b78aSgm89044 			 * The caller specified an offset that is larger than
312988f8b78aSgm89044 			 * the total size of the buffers it provided.
313088f8b78aSgm89044 			 */
313188f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
313288f8b78aSgm89044 		}
313388f8b78aSgm89044 
313488f8b78aSgm89044 		/*
313588f8b78aSgm89044 		 * Now do the processing on the mblk chain.
313688f8b78aSgm89044 		 */
313788f8b78aSgm89044 		while (mp != NULL && count > 0) {
313888f8b78aSgm89044 			cur_len = min(MBLKL(mp) - off, count);
313988f8b78aSgm89044 			count -= cur_len;
314088f8b78aSgm89044 			if (reverse) {
314188f8b78aSgm89044 				/* Fill the dest buffer from the end */
314288f8b78aSgm89044 				dca_reverse((char *)(mp->b_rptr + off),
314388f8b78aSgm89044 				    dest+count, cur_len, cur_len);
314488f8b78aSgm89044 			} else {
314588f8b78aSgm89044 				bcopy((char *)(mp->b_rptr + off), dest,
314688f8b78aSgm89044 				    cur_len);
314788f8b78aSgm89044 				dest += cur_len;
314888f8b78aSgm89044 			}
314988f8b78aSgm89044 			in->cd_offset += cur_len;
315088f8b78aSgm89044 			in->cd_length -= cur_len;
315188f8b78aSgm89044 			mp = mp->b_cont;
315288f8b78aSgm89044 			off = 0;
315388f8b78aSgm89044 		}
315488f8b78aSgm89044 
315588f8b78aSgm89044 		if (mp == NULL && count > 0) {
315688f8b78aSgm89044 			/*
315788f8b78aSgm89044 			 * The end of the mblk was reached but the length
315888f8b78aSgm89044 			 * requested could not be processed, (requested to
315988f8b78aSgm89044 			 * digest more data than it provided).
316088f8b78aSgm89044 			 */
316188f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
316288f8b78aSgm89044 		}
316388f8b78aSgm89044 		break;
316488f8b78aSgm89044 
316588f8b78aSgm89044 	default:
316688f8b78aSgm89044 		DBG(NULL, DWARN, "dca_gather: unrecognised crypto data format");
316788f8b78aSgm89044 		rv = CRYPTO_ARGUMENTS_BAD;
316888f8b78aSgm89044 	}
316988f8b78aSgm89044 	return (rv);
317088f8b78aSgm89044 }
317188f8b78aSgm89044 
317288f8b78aSgm89044 /*
317388f8b78aSgm89044  * Increments the cd_offset and decrements the cd_length as the data is
317488f8b78aSgm89044  * gathered from the crypto_data_t struct.
317588f8b78aSgm89044  */
317688f8b78aSgm89044 int
dca_resid_gather(crypto_data_t * in,char * resid,int * residlen,char * dest,int count)317788f8b78aSgm89044 dca_resid_gather(crypto_data_t *in, char *resid, int *residlen, char *dest,
317888f8b78aSgm89044     int count)
317988f8b78aSgm89044 {
318088f8b78aSgm89044 	int	rv = CRYPTO_SUCCESS;
318188f8b78aSgm89044 	caddr_t	baddr;
318288f8b78aSgm89044 	uint_t	vec_idx;
318388f8b78aSgm89044 	uio_t	*uiop;
318488f8b78aSgm89044 	off_t	off = in->cd_offset;
318588f8b78aSgm89044 	size_t	cur_len;
318688f8b78aSgm89044 	mblk_t	*mp;
318788f8b78aSgm89044 
318888f8b78aSgm89044 	/* Process the residual first */
318988f8b78aSgm89044 	if (*residlen > 0) {
319088f8b78aSgm89044 		uint_t	num = min(count, *residlen);
319188f8b78aSgm89044 		bcopy(resid, dest, num);
319288f8b78aSgm89044 		*residlen -= num;
319388f8b78aSgm89044 		if (*residlen > 0) {
319488f8b78aSgm89044 			/*
319588f8b78aSgm89044 			 * Requested amount 'count' is less than what's in
319688f8b78aSgm89044 			 * the residual, so shuffle any remaining resid to
319788f8b78aSgm89044 			 * the front.
319888f8b78aSgm89044 			 */
319988f8b78aSgm89044 			baddr = resid + num;
320088f8b78aSgm89044 			bcopy(baddr, resid, *residlen);
320188f8b78aSgm89044 		}
320288f8b78aSgm89044 		dest += num;
320388f8b78aSgm89044 		count -= num;
320488f8b78aSgm89044 	}
320588f8b78aSgm89044 
320688f8b78aSgm89044 	/* Now process what's in the crypto_data_t structs */
320788f8b78aSgm89044 	switch (in->cd_format) {
320888f8b78aSgm89044 	case CRYPTO_DATA_RAW:
320988f8b78aSgm89044 		if (count > in->cd_length) {
321088f8b78aSgm89044 			/*
321188f8b78aSgm89044 			 * The caller specified a length greater than the
321288f8b78aSgm89044 			 * size of the buffer.
321388f8b78aSgm89044 			 */
321488f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
321588f8b78aSgm89044 		}
321688f8b78aSgm89044 		bcopy(in->cd_raw.iov_base + in->cd_offset, dest, count);
321788f8b78aSgm89044 		in->cd_offset += count;
321888f8b78aSgm89044 		in->cd_length -= count;
321988f8b78aSgm89044 		break;
322088f8b78aSgm89044 
322188f8b78aSgm89044 	case CRYPTO_DATA_UIO:
322288f8b78aSgm89044 		/*
322388f8b78aSgm89044 		 * Jump to the first iovec containing data to be processed.
322488f8b78aSgm89044 		 */
322588f8b78aSgm89044 		uiop = in->cd_uio;
322688f8b78aSgm89044 		for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
322788f8b78aSgm89044 		    off >= uiop->uio_iov[vec_idx].iov_len;
3228d8dd9913Sgm89044 		    off -= uiop->uio_iov[vec_idx++].iov_len)
3229d8dd9913Sgm89044 			;
323088f8b78aSgm89044 		if (vec_idx == uiop->uio_iovcnt) {
323188f8b78aSgm89044 			/*
323288f8b78aSgm89044 			 * The caller specified an offset that is larger than
323388f8b78aSgm89044 			 * the total size of the buffers it provided.
323488f8b78aSgm89044 			 */
323588f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
323688f8b78aSgm89044 		}
323788f8b78aSgm89044 
323888f8b78aSgm89044 		/*
323988f8b78aSgm89044 		 * Now process the iovecs.
324088f8b78aSgm89044 		 */
324188f8b78aSgm89044 		while (vec_idx < uiop->uio_iovcnt && count > 0) {
324288f8b78aSgm89044 			cur_len = min(uiop->uio_iov[vec_idx].iov_len -
324388f8b78aSgm89044 			    off, count);
324488f8b78aSgm89044 			bcopy(uiop->uio_iov[vec_idx].iov_base + off, dest,
324588f8b78aSgm89044 			    cur_len);
324688f8b78aSgm89044 			count -= cur_len;
324788f8b78aSgm89044 			dest += cur_len;
324888f8b78aSgm89044 			in->cd_offset += cur_len;
324988f8b78aSgm89044 			in->cd_length -= cur_len;
325088f8b78aSgm89044 			vec_idx++;
325188f8b78aSgm89044 			off = 0;
325288f8b78aSgm89044 		}
325388f8b78aSgm89044 
325488f8b78aSgm89044 		if (vec_idx == uiop->uio_iovcnt && count > 0) {
325588f8b78aSgm89044 			/*
325688f8b78aSgm89044 			 * The end of the specified iovec's was reached but
325788f8b78aSgm89044 			 * the length requested could not be processed
325888f8b78aSgm89044 			 * (requested to digest more data than it provided).
325988f8b78aSgm89044 			 */
326088f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
326188f8b78aSgm89044 		}
326288f8b78aSgm89044 		break;
326388f8b78aSgm89044 
326488f8b78aSgm89044 	case CRYPTO_DATA_MBLK:
326588f8b78aSgm89044 		/*
326688f8b78aSgm89044 		 * Jump to the first mblk_t containing data to be processed.
326788f8b78aSgm89044 		 */
326888f8b78aSgm89044 		for (mp = in->cd_mp; mp != NULL && off >= MBLKL(mp);
3269d8dd9913Sgm89044 		    off -= MBLKL(mp), mp = mp->b_cont)
3270d8dd9913Sgm89044 			;
327188f8b78aSgm89044 		if (mp == NULL) {
327288f8b78aSgm89044 			/*
327388f8b78aSgm89044 			 * The caller specified an offset that is larger than
327488f8b78aSgm89044 			 * the total size of the buffers it provided.
327588f8b78aSgm89044 			 */
327688f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
327788f8b78aSgm89044 		}
327888f8b78aSgm89044 
327988f8b78aSgm89044 		/*
328088f8b78aSgm89044 		 * Now do the processing on the mblk chain.
328188f8b78aSgm89044 		 */
328288f8b78aSgm89044 		while (mp != NULL && count > 0) {
328388f8b78aSgm89044 			cur_len = min(MBLKL(mp) - off, count);
328488f8b78aSgm89044 			bcopy((char *)(mp->b_rptr + off), dest, cur_len);
328588f8b78aSgm89044 			count -= cur_len;
328688f8b78aSgm89044 			dest += cur_len;
328788f8b78aSgm89044 			in->cd_offset += cur_len;
328888f8b78aSgm89044 			in->cd_length -= cur_len;
328988f8b78aSgm89044 			mp = mp->b_cont;
329088f8b78aSgm89044 			off = 0;
329188f8b78aSgm89044 		}
329288f8b78aSgm89044 
329388f8b78aSgm89044 		if (mp == NULL && count > 0) {
329488f8b78aSgm89044 			/*
329588f8b78aSgm89044 			 * The end of the mblk was reached but the length
329688f8b78aSgm89044 			 * requested could not be processed, (requested to
329788f8b78aSgm89044 			 * digest more data than it provided).
329888f8b78aSgm89044 			 */
329988f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
330088f8b78aSgm89044 		}
330188f8b78aSgm89044 		break;
330288f8b78aSgm89044 
330388f8b78aSgm89044 	default:
330488f8b78aSgm89044 		DBG(NULL, DWARN,
330588f8b78aSgm89044 		    "dca_resid_gather: unrecognised crypto data format");
330688f8b78aSgm89044 		rv = CRYPTO_ARGUMENTS_BAD;
330788f8b78aSgm89044 	}
330888f8b78aSgm89044 	return (rv);
330988f8b78aSgm89044 }
331088f8b78aSgm89044 
331188f8b78aSgm89044 /*
331288f8b78aSgm89044  * Appends the data to the crypto_data_t struct increasing cd_length.
331388f8b78aSgm89044  * cd_offset is left unchanged.
331488f8b78aSgm89044  * Data is reverse-copied if the flag is TRUE.
331588f8b78aSgm89044  */
331688f8b78aSgm89044 int
dca_scatter(const char * src,crypto_data_t * out,int count,int reverse)331788f8b78aSgm89044 dca_scatter(const char *src, crypto_data_t *out, int count, int reverse)
331888f8b78aSgm89044 {
331988f8b78aSgm89044 	int	rv = CRYPTO_SUCCESS;
332088f8b78aSgm89044 	off_t	offset = out->cd_offset + out->cd_length;
332188f8b78aSgm89044 	uint_t	vec_idx;
332288f8b78aSgm89044 	uio_t	*uiop;
332388f8b78aSgm89044 	size_t	cur_len;
332488f8b78aSgm89044 	mblk_t	*mp;
332588f8b78aSgm89044 
332688f8b78aSgm89044 	switch (out->cd_format) {
332788f8b78aSgm89044 	case CRYPTO_DATA_RAW:
332888f8b78aSgm89044 		if (out->cd_raw.iov_len - offset < count) {
332988f8b78aSgm89044 			/* Trying to write out more than space available. */
333088f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
333188f8b78aSgm89044 		}
333288f8b78aSgm89044 		if (reverse)
333388f8b78aSgm89044 			dca_reverse((void*) src, out->cd_raw.iov_base + offset,
333488f8b78aSgm89044 			    count, count);
333588f8b78aSgm89044 		else
333688f8b78aSgm89044 			bcopy(src, out->cd_raw.iov_base + offset, count);
333788f8b78aSgm89044 		out->cd_length += count;
333888f8b78aSgm89044 		break;
333988f8b78aSgm89044 
334088f8b78aSgm89044 	case CRYPTO_DATA_UIO:
334188f8b78aSgm89044 		/*
334288f8b78aSgm89044 		 * Jump to the first iovec that can be written to.
334388f8b78aSgm89044 		 */
334488f8b78aSgm89044 		uiop = out->cd_uio;
334588f8b78aSgm89044 		for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
334688f8b78aSgm89044 		    offset >= uiop->uio_iov[vec_idx].iov_len;
3347d8dd9913Sgm89044 		    offset -= uiop->uio_iov[vec_idx++].iov_len)
3348d8dd9913Sgm89044 			;
334988f8b78aSgm89044 		if (vec_idx == uiop->uio_iovcnt) {
335088f8b78aSgm89044 			/*
335188f8b78aSgm89044 			 * The caller specified an offset that is larger than
335288f8b78aSgm89044 			 * the total size of the buffers it provided.
335388f8b78aSgm89044 			 */
335488f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
335588f8b78aSgm89044 		}
335688f8b78aSgm89044 
335788f8b78aSgm89044 		/*
335888f8b78aSgm89044 		 * Now process the iovecs.
335988f8b78aSgm89044 		 */
336088f8b78aSgm89044 		while (vec_idx < uiop->uio_iovcnt && count > 0) {
336188f8b78aSgm89044 			cur_len = min(uiop->uio_iov[vec_idx].iov_len -
336288f8b78aSgm89044 			    offset, count);
336388f8b78aSgm89044 			count -= cur_len;
336488f8b78aSgm89044 			if (reverse) {
336588f8b78aSgm89044 				dca_reverse((void*) (src+count),
336688f8b78aSgm89044 				    uiop->uio_iov[vec_idx].iov_base +
336788f8b78aSgm89044 				    offset, cur_len, cur_len);
336888f8b78aSgm89044 			} else {
336988f8b78aSgm89044 				bcopy(src, uiop->uio_iov[vec_idx].iov_base +
337088f8b78aSgm89044 				    offset, cur_len);
337188f8b78aSgm89044 				src += cur_len;
337288f8b78aSgm89044 			}
337388f8b78aSgm89044 			out->cd_length += cur_len;
337488f8b78aSgm89044 			vec_idx++;
337588f8b78aSgm89044 			offset = 0;
337688f8b78aSgm89044 		}
337788f8b78aSgm89044 
337888f8b78aSgm89044 		if (vec_idx == uiop->uio_iovcnt && count > 0) {
337988f8b78aSgm89044 			/*
338088f8b78aSgm89044 			 * The end of the specified iovec's was reached but
338188f8b78aSgm89044 			 * the length requested could not be processed
338288f8b78aSgm89044 			 * (requested to write more data than space provided).
338388f8b78aSgm89044 			 */
338488f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
338588f8b78aSgm89044 		}
338688f8b78aSgm89044 		break;
338788f8b78aSgm89044 
338888f8b78aSgm89044 	case CRYPTO_DATA_MBLK:
338988f8b78aSgm89044 		/*
339088f8b78aSgm89044 		 * Jump to the first mblk_t that can be written to.
339188f8b78aSgm89044 		 */
339288f8b78aSgm89044 		for (mp = out->cd_mp; mp != NULL && offset >= MBLKL(mp);
3393d8dd9913Sgm89044 		    offset -= MBLKL(mp), mp = mp->b_cont)
3394d8dd9913Sgm89044 			;
339588f8b78aSgm89044 		if (mp == NULL) {
339688f8b78aSgm89044 			/*
339788f8b78aSgm89044 			 * The caller specified an offset that is larger than
339888f8b78aSgm89044 			 * the total size of the buffers it provided.
339988f8b78aSgm89044 			 */
340088f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
340188f8b78aSgm89044 		}
340288f8b78aSgm89044 
340388f8b78aSgm89044 		/*
340488f8b78aSgm89044 		 * Now do the processing on the mblk chain.
340588f8b78aSgm89044 		 */
340688f8b78aSgm89044 		while (mp != NULL && count > 0) {
340788f8b78aSgm89044 			cur_len = min(MBLKL(mp) - offset, count);
340888f8b78aSgm89044 			count -= cur_len;
340988f8b78aSgm89044 			if (reverse) {
341088f8b78aSgm89044 				dca_reverse((void*) (src+count),
341188f8b78aSgm89044 				    (char *)(mp->b_rptr + offset), cur_len,
341288f8b78aSgm89044 				    cur_len);
341388f8b78aSgm89044 			} else {
341488f8b78aSgm89044 				bcopy(src, (char *)(mp->b_rptr + offset),
341588f8b78aSgm89044 				    cur_len);
341688f8b78aSgm89044 				src += cur_len;
341788f8b78aSgm89044 			}
341888f8b78aSgm89044 			out->cd_length += cur_len;
341988f8b78aSgm89044 			mp = mp->b_cont;
342088f8b78aSgm89044 			offset = 0;
342188f8b78aSgm89044 		}
342288f8b78aSgm89044 
342388f8b78aSgm89044 		if (mp == NULL && count > 0) {
342488f8b78aSgm89044 			/*
342588f8b78aSgm89044 			 * The end of the mblk was reached but the length
342688f8b78aSgm89044 			 * requested could not be processed, (requested to
342788f8b78aSgm89044 			 * digest more data than it provided).
342888f8b78aSgm89044 			 */
342988f8b78aSgm89044 			return (CRYPTO_DATA_LEN_RANGE);
343088f8b78aSgm89044 		}
343188f8b78aSgm89044 		break;
343288f8b78aSgm89044 
343388f8b78aSgm89044 	default:
343488f8b78aSgm89044 		DBG(NULL, DWARN, "unrecognised crypto data format");
343588f8b78aSgm89044 		rv = CRYPTO_ARGUMENTS_BAD;
343688f8b78aSgm89044 	}
343788f8b78aSgm89044 	return (rv);
343888f8b78aSgm89044 }
343988f8b78aSgm89044 
344088f8b78aSgm89044 /*
344188f8b78aSgm89044  * Compare two byte arrays in reverse order.
344288f8b78aSgm89044  * Return 0 if they are identical, 1 otherwise.
344388f8b78aSgm89044  */
344488f8b78aSgm89044 int
dca_bcmp_reverse(const void * s1,const void * s2,size_t n)344588f8b78aSgm89044 dca_bcmp_reverse(const void *s1, const void *s2, size_t n)
344688f8b78aSgm89044 {
344788f8b78aSgm89044 	int i;
344888f8b78aSgm89044 	caddr_t src, dst;
344988f8b78aSgm89044 
345088f8b78aSgm89044 	if (!n)
345188f8b78aSgm89044 		return (0);
345288f8b78aSgm89044 
345388f8b78aSgm89044 	src = ((caddr_t)s1) + n - 1;
345488f8b78aSgm89044 	dst = (caddr_t)s2;
345588f8b78aSgm89044 	for (i = 0; i < n; i++) {
345688f8b78aSgm89044 		if (*src != *dst)
345788f8b78aSgm89044 			return (1);
345888f8b78aSgm89044 		src--;
345988f8b78aSgm89044 		dst++;
346088f8b78aSgm89044 	}
346188f8b78aSgm89044 
346288f8b78aSgm89044 	return (0);
346388f8b78aSgm89044 }
346488f8b78aSgm89044 
346588f8b78aSgm89044 
346688f8b78aSgm89044 /*
346788f8b78aSgm89044  * This calculates the size of a bignum in bits, specifically not counting
346888f8b78aSgm89044  * leading zero bits.  This size calculation must be done *before* any
346988f8b78aSgm89044  * endian reversal takes place (i.e. the numbers are in absolute big-endian
347088f8b78aSgm89044  * order.)
347188f8b78aSgm89044  */
347288f8b78aSgm89044 int
dca_bitlen(unsigned char * bignum,int bytelen)347388f8b78aSgm89044 dca_bitlen(unsigned char *bignum, int bytelen)
347488f8b78aSgm89044 {
347588f8b78aSgm89044 	unsigned char	msbyte;
347688f8b78aSgm89044 	int		i, j;
347788f8b78aSgm89044 
347888f8b78aSgm89044 	for (i = 0; i < bytelen - 1; i++) {
347988f8b78aSgm89044 		if (bignum[i] != 0) {
348088f8b78aSgm89044 			break;
348188f8b78aSgm89044 		}
348288f8b78aSgm89044 	}
348388f8b78aSgm89044 	msbyte = bignum[i];
348488f8b78aSgm89044 	for (j = 8; j > 1; j--) {
348588f8b78aSgm89044 		if (msbyte & 0x80) {
348688f8b78aSgm89044 			break;
348788f8b78aSgm89044 		}
348888f8b78aSgm89044 		msbyte <<= 1;
348988f8b78aSgm89044 	}
349088f8b78aSgm89044 	return ((8 * (bytelen - i - 1)) + j);
349188f8b78aSgm89044 }
349288f8b78aSgm89044 
349388f8b78aSgm89044 /*
349488f8b78aSgm89044  * This compares to bignums (in big-endian order).  It ignores leading
349588f8b78aSgm89044  * null bytes.  The result semantics follow bcmp, mempcmp, strcmp, etc.
349688f8b78aSgm89044  */
349788f8b78aSgm89044 int
dca_numcmp(caddr_t n1,int n1len,caddr_t n2,int n2len)349888f8b78aSgm89044 dca_numcmp(caddr_t n1, int n1len, caddr_t n2, int n2len)
349988f8b78aSgm89044 {
350088f8b78aSgm89044 	while ((n1len > 1) && (*n1 == 0)) {
350188f8b78aSgm89044 		n1len--;
350288f8b78aSgm89044 		n1++;
350388f8b78aSgm89044 	}
350488f8b78aSgm89044 	while ((n2len > 1) && (*n2 == 0)) {
350588f8b78aSgm89044 		n2len--;
350688f8b78aSgm89044 		n2++;
350788f8b78aSgm89044 	}
350888f8b78aSgm89044 	if (n1len != n2len) {
350988f8b78aSgm89044 		return (n1len - n2len);
351088f8b78aSgm89044 	}
351188f8b78aSgm89044 	while ((n1len > 1) && (*n1 == *n2)) {
351288f8b78aSgm89044 		n1++;
351388f8b78aSgm89044 		n2++;
351488f8b78aSgm89044 		n1len--;
351588f8b78aSgm89044 	}
351688f8b78aSgm89044 	return ((int)(*(uchar_t *)n1) - (int)(*(uchar_t *)n2));
351788f8b78aSgm89044 }
351888f8b78aSgm89044 
351988f8b78aSgm89044 /*
352088f8b78aSgm89044  * Return array of key attributes.
352188f8b78aSgm89044  */
352288f8b78aSgm89044 crypto_object_attribute_t *
dca_get_key_attr(crypto_key_t * key)352388f8b78aSgm89044 dca_get_key_attr(crypto_key_t *key)
352488f8b78aSgm89044 {
352588f8b78aSgm89044 	if ((key->ck_format != CRYPTO_KEY_ATTR_LIST) ||
352688f8b78aSgm89044 	    (key->ck_count == 0)) {
352788f8b78aSgm89044 		return (NULL);
352888f8b78aSgm89044 	}
352988f8b78aSgm89044 
353088f8b78aSgm89044 	return (key->ck_attrs);
353188f8b78aSgm89044 }
353288f8b78aSgm89044 
353388f8b78aSgm89044 /*
353488f8b78aSgm89044  * If attribute type exists valp points to it's 32-bit value.
353588f8b78aSgm89044  */
353688f8b78aSgm89044 int
dca_attr_lookup_uint32(crypto_object_attribute_t * attrp,uint_t atnum,uint64_t atype,uint32_t * valp)353788f8b78aSgm89044 dca_attr_lookup_uint32(crypto_object_attribute_t *attrp, uint_t atnum,
353888f8b78aSgm89044     uint64_t atype, uint32_t *valp)
353988f8b78aSgm89044 {
354088f8b78aSgm89044 	crypto_object_attribute_t	*bap;
354188f8b78aSgm89044 
354288f8b78aSgm89044 	bap = dca_find_attribute(attrp, atnum, atype);
354388f8b78aSgm89044 	if (bap == NULL) {
354488f8b78aSgm89044 		return (CRYPTO_ATTRIBUTE_TYPE_INVALID);
354588f8b78aSgm89044 	}
354688f8b78aSgm89044 
354788f8b78aSgm89044 	*valp = *bap->oa_value;
354888f8b78aSgm89044 
354988f8b78aSgm89044 	return (CRYPTO_SUCCESS);
355088f8b78aSgm89044 }
355188f8b78aSgm89044 
355288f8b78aSgm89044 /*
355388f8b78aSgm89044  * If attribute type exists data contains the start address of the value,
355488f8b78aSgm89044  * and numelems contains it's length.
355588f8b78aSgm89044  */
355688f8b78aSgm89044 int
dca_attr_lookup_uint8_array(crypto_object_attribute_t * attrp,uint_t atnum,uint64_t atype,void ** data,unsigned int * numelems)355788f8b78aSgm89044 dca_attr_lookup_uint8_array(crypto_object_attribute_t *attrp, uint_t atnum,
355888f8b78aSgm89044     uint64_t atype, void **data, unsigned int *numelems)
355988f8b78aSgm89044 {
356088f8b78aSgm89044 	crypto_object_attribute_t	*bap;
356188f8b78aSgm89044 
356288f8b78aSgm89044 	bap = dca_find_attribute(attrp, atnum, atype);
356388f8b78aSgm89044 	if (bap == NULL) {
356488f8b78aSgm89044 		return (CRYPTO_ATTRIBUTE_TYPE_INVALID);
356588f8b78aSgm89044 	}
356688f8b78aSgm89044 
356788f8b78aSgm89044 	*data = bap->oa_value;
356888f8b78aSgm89044 	*numelems = bap->oa_value_len;
356988f8b78aSgm89044 
357088f8b78aSgm89044 	return (CRYPTO_SUCCESS);
357188f8b78aSgm89044 }
357288f8b78aSgm89044 
357388f8b78aSgm89044 /*
357488f8b78aSgm89044  * Finds entry of specified name. If it is not found dca_find_attribute returns
357588f8b78aSgm89044  * NULL.
357688f8b78aSgm89044  */
357788f8b78aSgm89044 crypto_object_attribute_t *
dca_find_attribute(crypto_object_attribute_t * attrp,uint_t atnum,uint64_t atype)357888f8b78aSgm89044 dca_find_attribute(crypto_object_attribute_t *attrp, uint_t atnum,
357988f8b78aSgm89044     uint64_t atype)
358088f8b78aSgm89044 {
358188f8b78aSgm89044 	while (atnum) {
358288f8b78aSgm89044 		if (attrp->oa_type == atype)
358388f8b78aSgm89044 			return (attrp);
358488f8b78aSgm89044 		atnum--;
358588f8b78aSgm89044 		attrp++;
358688f8b78aSgm89044 	}
358788f8b78aSgm89044 	return (NULL);
358888f8b78aSgm89044 }
358988f8b78aSgm89044 
359088f8b78aSgm89044 /*
359188f8b78aSgm89044  * Return the address of the first data buffer. If the data format is
359288f8b78aSgm89044  * unrecognised return NULL.
359388f8b78aSgm89044  */
359488f8b78aSgm89044 caddr_t
dca_bufdaddr(crypto_data_t * data)359588f8b78aSgm89044 dca_bufdaddr(crypto_data_t *data)
359688f8b78aSgm89044 {
359788f8b78aSgm89044 	switch (data->cd_format) {
359888f8b78aSgm89044 	case CRYPTO_DATA_RAW:
359988f8b78aSgm89044 		return (data->cd_raw.iov_base + data->cd_offset);
360088f8b78aSgm89044 	case CRYPTO_DATA_UIO:
360188f8b78aSgm89044 		return (data->cd_uio->uio_iov[0].iov_base + data->cd_offset);
360288f8b78aSgm89044 	case CRYPTO_DATA_MBLK:
360388f8b78aSgm89044 		return ((char *)data->cd_mp->b_rptr + data->cd_offset);
360488f8b78aSgm89044 	default:
360588f8b78aSgm89044 		DBG(NULL, DWARN,
360688f8b78aSgm89044 		    "dca_bufdaddr: unrecognised crypto data format");
360788f8b78aSgm89044 		return (NULL);
360888f8b78aSgm89044 	}
360988f8b78aSgm89044 }
361088f8b78aSgm89044 
361188f8b78aSgm89044 static caddr_t
dca_bufdaddr_out(crypto_data_t * data)361288f8b78aSgm89044 dca_bufdaddr_out(crypto_data_t *data)
361388f8b78aSgm89044 {
361488f8b78aSgm89044 	size_t offset = data->cd_offset + data->cd_length;
361588f8b78aSgm89044 
361688f8b78aSgm89044 	switch (data->cd_format) {
361788f8b78aSgm89044 	case CRYPTO_DATA_RAW:
361888f8b78aSgm89044 		return (data->cd_raw.iov_base + offset);
361988f8b78aSgm89044 	case CRYPTO_DATA_UIO:
362088f8b78aSgm89044 		return (data->cd_uio->uio_iov[0].iov_base + offset);
362188f8b78aSgm89044 	case CRYPTO_DATA_MBLK:
362288f8b78aSgm89044 		return ((char *)data->cd_mp->b_rptr + offset);
362388f8b78aSgm89044 	default:
362488f8b78aSgm89044 		DBG(NULL, DWARN,
362588f8b78aSgm89044 		    "dca_bufdaddr_out: unrecognised crypto data format");
362688f8b78aSgm89044 		return (NULL);
362788f8b78aSgm89044 	}
362888f8b78aSgm89044 }
362988f8b78aSgm89044 
363088f8b78aSgm89044 /*
363188f8b78aSgm89044  * Control entry points.
363288f8b78aSgm89044  */
363388f8b78aSgm89044 
363488f8b78aSgm89044 /* ARGSUSED */
363588f8b78aSgm89044 static void
dca_provider_status(crypto_provider_handle_t provider,uint_t * status)363688f8b78aSgm89044 dca_provider_status(crypto_provider_handle_t provider, uint_t *status)
363788f8b78aSgm89044 {
363888f8b78aSgm89044 	*status = CRYPTO_PROVIDER_READY;
363988f8b78aSgm89044 }
364088f8b78aSgm89044 
364188f8b78aSgm89044 /*
364288f8b78aSgm89044  * Cipher (encrypt/decrypt) entry points.
364388f8b78aSgm89044  */
364488f8b78aSgm89044 
364588f8b78aSgm89044 /* ARGSUSED */
364688f8b78aSgm89044 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)364788f8b78aSgm89044 dca_encrypt_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
364888f8b78aSgm89044     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
364988f8b78aSgm89044     crypto_req_handle_t req)
365088f8b78aSgm89044 {
365188f8b78aSgm89044 	int error = CRYPTO_FAILED;
365288f8b78aSgm89044 	dca_t *softc;
365388f8b78aSgm89044 
36543ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
365588f8b78aSgm89044 	DBG(softc, DENTRY, "dca_encrypt_init: started");
365688f8b78aSgm89044 
365788f8b78aSgm89044 	/* check mechanism */
365888f8b78aSgm89044 	switch (mechanism->cm_type) {
365988f8b78aSgm89044 	case DES_CBC_MECH_INFO_TYPE:
366088f8b78aSgm89044 		error = dca_3desctxinit(ctx, mechanism, key, KM_SLEEP,
366188f8b78aSgm89044 		    DR_ENCRYPT);
366288f8b78aSgm89044 		break;
366388f8b78aSgm89044 	case DES3_CBC_MECH_INFO_TYPE:
366488f8b78aSgm89044 		error = dca_3desctxinit(ctx, mechanism, key, KM_SLEEP,
366588f8b78aSgm89044 		    DR_ENCRYPT | DR_TRIPLE);
366688f8b78aSgm89044 		break;
366788f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
366888f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
366988f8b78aSgm89044 		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
367088f8b78aSgm89044 		break;
367188f8b78aSgm89044 	default:
367288f8b78aSgm89044 		cmn_err(CE_WARN, "dca_encrypt_init: unexpected mech type "
367388f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
367488f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
367588f8b78aSgm89044 	}
367688f8b78aSgm89044 
367788f8b78aSgm89044 	DBG(softc, DENTRY, "dca_encrypt_init: done, err = 0x%x", error);
367888f8b78aSgm89044 
367988f8b78aSgm89044 	if (error == CRYPTO_SUCCESS)
368088f8b78aSgm89044 		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
368188f8b78aSgm89044 		    &softc->dca_ctx_list_lock);
368288f8b78aSgm89044 
368388f8b78aSgm89044 	return (error);
368488f8b78aSgm89044 }
368588f8b78aSgm89044 
368688f8b78aSgm89044 /* ARGSUSED */
368788f8b78aSgm89044 static int
dca_encrypt(crypto_ctx_t * ctx,crypto_data_t * plaintext,crypto_data_t * ciphertext,crypto_req_handle_t req)368888f8b78aSgm89044 dca_encrypt(crypto_ctx_t *ctx, crypto_data_t *plaintext,
368988f8b78aSgm89044     crypto_data_t *ciphertext, crypto_req_handle_t req)
369088f8b78aSgm89044 {
369188f8b78aSgm89044 	int error = CRYPTO_FAILED;
369288f8b78aSgm89044 	dca_t *softc;
369388f8b78aSgm89044 
369488f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
369588f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
369688f8b78aSgm89044 
36973ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
369888f8b78aSgm89044 	DBG(softc, DENTRY, "dca_encrypt: started");
369988f8b78aSgm89044 
3700d8dd9913Sgm89044 	/* handle inplace ops */
3701d8dd9913Sgm89044 	if (!ciphertext) {
3702d8dd9913Sgm89044 		dca_request_t *reqp = ctx->cc_provider_private;
3703d8dd9913Sgm89044 		reqp->dr_flags |= DR_INPLACE;
3704d8dd9913Sgm89044 		ciphertext = plaintext;
3705d8dd9913Sgm89044 	}
3706d8dd9913Sgm89044 
370788f8b78aSgm89044 	/* check mechanism */
370888f8b78aSgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
370988f8b78aSgm89044 	case DES_CBC_MECH_INFO_TYPE:
371088f8b78aSgm89044 		error = dca_3des(ctx, plaintext, ciphertext, req, DR_ENCRYPT);
371188f8b78aSgm89044 		break;
371288f8b78aSgm89044 	case DES3_CBC_MECH_INFO_TYPE:
371388f8b78aSgm89044 		error = dca_3des(ctx, plaintext, ciphertext, req,
371488f8b78aSgm89044 		    DR_ENCRYPT | DR_TRIPLE);
371588f8b78aSgm89044 		break;
371688f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
371788f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
371888f8b78aSgm89044 		error = dca_rsastart(ctx, plaintext, ciphertext, req,
371988f8b78aSgm89044 		    DCA_RSA_ENC);
372088f8b78aSgm89044 		break;
372188f8b78aSgm89044 	default:
372288f8b78aSgm89044 		/* Should never reach here */
372388f8b78aSgm89044 		cmn_err(CE_WARN, "dca_encrypt: unexpected mech type "
372488f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
372588f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
372688f8b78aSgm89044 	}
372788f8b78aSgm89044 
372888f8b78aSgm89044 	if ((error != CRYPTO_QUEUED) && (error != CRYPTO_SUCCESS) &&
372988f8b78aSgm89044 	    (error != CRYPTO_BUFFER_TOO_SMALL)) {
373088f8b78aSgm89044 		ciphertext->cd_length = 0;
373188f8b78aSgm89044 	}
373288f8b78aSgm89044 
373388f8b78aSgm89044 	DBG(softc, DENTRY, "dca_encrypt: done, err = 0x%x", error);
373488f8b78aSgm89044 
373588f8b78aSgm89044 	return (error);
373688f8b78aSgm89044 }
373788f8b78aSgm89044 
373888f8b78aSgm89044 /* ARGSUSED */
373988f8b78aSgm89044 static int
dca_encrypt_update(crypto_ctx_t * ctx,crypto_data_t * plaintext,crypto_data_t * ciphertext,crypto_req_handle_t req)374088f8b78aSgm89044 dca_encrypt_update(crypto_ctx_t *ctx, crypto_data_t *plaintext,
374188f8b78aSgm89044     crypto_data_t *ciphertext, crypto_req_handle_t req)
374288f8b78aSgm89044 {
374388f8b78aSgm89044 	int error = CRYPTO_FAILED;
374488f8b78aSgm89044 	dca_t *softc;
374588f8b78aSgm89044 
374688f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
374788f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
374888f8b78aSgm89044 
37493ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
375088f8b78aSgm89044 	DBG(softc, DENTRY, "dca_encrypt_update: started");
375188f8b78aSgm89044 
3752d8dd9913Sgm89044 	/* handle inplace ops */
3753d8dd9913Sgm89044 	if (!ciphertext) {
3754d8dd9913Sgm89044 		dca_request_t *reqp = ctx->cc_provider_private;
3755d8dd9913Sgm89044 		reqp->dr_flags |= DR_INPLACE;
3756d8dd9913Sgm89044 		ciphertext = plaintext;
3757d8dd9913Sgm89044 	}
3758d8dd9913Sgm89044 
375988f8b78aSgm89044 	/* check mechanism */
376088f8b78aSgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
376188f8b78aSgm89044 	case DES_CBC_MECH_INFO_TYPE:
376288f8b78aSgm89044 		error = dca_3desupdate(ctx, plaintext, ciphertext, req,
376388f8b78aSgm89044 		    DR_ENCRYPT);
376488f8b78aSgm89044 		break;
376588f8b78aSgm89044 	case DES3_CBC_MECH_INFO_TYPE:
376688f8b78aSgm89044 		error = dca_3desupdate(ctx, plaintext, ciphertext, req,
376788f8b78aSgm89044 		    DR_ENCRYPT | DR_TRIPLE);
376888f8b78aSgm89044 		break;
376988f8b78aSgm89044 	default:
377088f8b78aSgm89044 		/* Should never reach here */
377188f8b78aSgm89044 		cmn_err(CE_WARN, "dca_encrypt_update: unexpected mech type "
377288f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
377388f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
377488f8b78aSgm89044 	}
377588f8b78aSgm89044 
377688f8b78aSgm89044 	DBG(softc, DENTRY, "dca_encrypt_update: done, err = 0x%x", error);
377788f8b78aSgm89044 
377888f8b78aSgm89044 	return (error);
377988f8b78aSgm89044 }
378088f8b78aSgm89044 
378188f8b78aSgm89044 /* ARGSUSED */
378288f8b78aSgm89044 static int
dca_encrypt_final(crypto_ctx_t * ctx,crypto_data_t * ciphertext,crypto_req_handle_t req)378388f8b78aSgm89044 dca_encrypt_final(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
378488f8b78aSgm89044     crypto_req_handle_t req)
378588f8b78aSgm89044 {
378688f8b78aSgm89044 	int error = CRYPTO_FAILED;
378788f8b78aSgm89044 	dca_t *softc;
378888f8b78aSgm89044 
378988f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
379088f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
379188f8b78aSgm89044 
37923ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
379388f8b78aSgm89044 	DBG(softc, DENTRY, "dca_encrypt_final: started");
379488f8b78aSgm89044 
379588f8b78aSgm89044 	/* check mechanism */
379688f8b78aSgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
379788f8b78aSgm89044 	case DES_CBC_MECH_INFO_TYPE:
379888f8b78aSgm89044 		error = dca_3desfinal(ctx, ciphertext, DR_ENCRYPT);
379988f8b78aSgm89044 		break;
380088f8b78aSgm89044 	case DES3_CBC_MECH_INFO_TYPE:
380188f8b78aSgm89044 		error = dca_3desfinal(ctx, ciphertext, DR_ENCRYPT | DR_TRIPLE);
380288f8b78aSgm89044 		break;
380388f8b78aSgm89044 	default:
380488f8b78aSgm89044 		/* Should never reach here */
380588f8b78aSgm89044 		cmn_err(CE_WARN, "dca_encrypt_final: unexpected mech type "
380688f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
380788f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
380888f8b78aSgm89044 	}
380988f8b78aSgm89044 
381088f8b78aSgm89044 	DBG(softc, DENTRY, "dca_encrypt_final: done, err = 0x%x", error);
381188f8b78aSgm89044 
381288f8b78aSgm89044 	return (error);
381388f8b78aSgm89044 }
381488f8b78aSgm89044 
381588f8b78aSgm89044 /* ARGSUSED */
381688f8b78aSgm89044 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)381788f8b78aSgm89044 dca_encrypt_atomic(crypto_provider_handle_t provider,
381888f8b78aSgm89044     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
381988f8b78aSgm89044     crypto_key_t *key, crypto_data_t *plaintext, crypto_data_t *ciphertext,
382088f8b78aSgm89044     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
382188f8b78aSgm89044 {
382288f8b78aSgm89044 	int error = CRYPTO_FAILED;
382388f8b78aSgm89044 	dca_t *softc = (dca_t *)provider;
382488f8b78aSgm89044 
382588f8b78aSgm89044 	DBG(softc, DENTRY, "dca_encrypt_atomic: started");
382688f8b78aSgm89044 
382788f8b78aSgm89044 	if (ctx_template != NULL)
382888f8b78aSgm89044 		return (CRYPTO_ARGUMENTS_BAD);
382988f8b78aSgm89044 
3830d8dd9913Sgm89044 	/* handle inplace ops */
3831d8dd9913Sgm89044 	if (!ciphertext) {
3832d8dd9913Sgm89044 		ciphertext = plaintext;
3833d8dd9913Sgm89044 	}
3834d8dd9913Sgm89044 
383588f8b78aSgm89044 	/* check mechanism */
383688f8b78aSgm89044 	switch (mechanism->cm_type) {
383788f8b78aSgm89044 	case DES_CBC_MECH_INFO_TYPE:
383888f8b78aSgm89044 		error = dca_3desatomic(provider, session_id, mechanism, key,
383988f8b78aSgm89044 		    plaintext, ciphertext, KM_SLEEP, req,
384088f8b78aSgm89044 		    DR_ENCRYPT | DR_ATOMIC);
384188f8b78aSgm89044 		break;
384288f8b78aSgm89044 	case DES3_CBC_MECH_INFO_TYPE:
384388f8b78aSgm89044 		error = dca_3desatomic(provider, session_id, mechanism, key,
384488f8b78aSgm89044 		    plaintext, ciphertext, KM_SLEEP, req,
384588f8b78aSgm89044 		    DR_ENCRYPT | DR_TRIPLE | DR_ATOMIC);
384688f8b78aSgm89044 		break;
384788f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
384888f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
384988f8b78aSgm89044 		error = dca_rsaatomic(provider, session_id, mechanism, key,
385088f8b78aSgm89044 		    plaintext, ciphertext, KM_SLEEP, req, DCA_RSA_ENC);
385188f8b78aSgm89044 		break;
385288f8b78aSgm89044 	default:
385388f8b78aSgm89044 		cmn_err(CE_WARN, "dca_encrypt_atomic: unexpected mech type "
385488f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
385588f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
385688f8b78aSgm89044 	}
385788f8b78aSgm89044 
385888f8b78aSgm89044 	if ((error != CRYPTO_QUEUED) && (error != CRYPTO_SUCCESS)) {
385988f8b78aSgm89044 		ciphertext->cd_length = 0;
386088f8b78aSgm89044 	}
386188f8b78aSgm89044 
386288f8b78aSgm89044 	DBG(softc, DENTRY, "dca_encrypt_atomic: done, err = 0x%x", error);
386388f8b78aSgm89044 
386488f8b78aSgm89044 	return (error);
386588f8b78aSgm89044 }
386688f8b78aSgm89044 
386788f8b78aSgm89044 /* ARGSUSED */
386888f8b78aSgm89044 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)386988f8b78aSgm89044 dca_decrypt_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
387088f8b78aSgm89044     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
387188f8b78aSgm89044     crypto_req_handle_t req)
387288f8b78aSgm89044 {
387388f8b78aSgm89044 	int error = CRYPTO_FAILED;
387488f8b78aSgm89044 	dca_t *softc;
387588f8b78aSgm89044 
38763ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
387788f8b78aSgm89044 	DBG(softc, DENTRY, "dca_decrypt_init: started");
387888f8b78aSgm89044 
387988f8b78aSgm89044 	/* check mechanism */
388088f8b78aSgm89044 	switch (mechanism->cm_type) {
388188f8b78aSgm89044 	case DES_CBC_MECH_INFO_TYPE:
388288f8b78aSgm89044 		error = dca_3desctxinit(ctx, mechanism, key, KM_SLEEP,
388388f8b78aSgm89044 		    DR_DECRYPT);
388488f8b78aSgm89044 		break;
388588f8b78aSgm89044 	case DES3_CBC_MECH_INFO_TYPE:
388688f8b78aSgm89044 		error = dca_3desctxinit(ctx, mechanism, key, KM_SLEEP,
388788f8b78aSgm89044 		    DR_DECRYPT | DR_TRIPLE);
388888f8b78aSgm89044 		break;
388988f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
389088f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
389188f8b78aSgm89044 		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
389288f8b78aSgm89044 		break;
389388f8b78aSgm89044 	default:
389488f8b78aSgm89044 		cmn_err(CE_WARN, "dca_decrypt_init: unexpected mech type "
389588f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
389688f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
389788f8b78aSgm89044 	}
389888f8b78aSgm89044 
389988f8b78aSgm89044 	DBG(softc, DENTRY, "dca_decrypt_init: done, err = 0x%x", error);
390088f8b78aSgm89044 
390188f8b78aSgm89044 	if (error == CRYPTO_SUCCESS)
390288f8b78aSgm89044 		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
390388f8b78aSgm89044 		    &softc->dca_ctx_list_lock);
390488f8b78aSgm89044 
390588f8b78aSgm89044 	return (error);
390688f8b78aSgm89044 }
390788f8b78aSgm89044 
390888f8b78aSgm89044 /* ARGSUSED */
390988f8b78aSgm89044 static int
dca_decrypt(crypto_ctx_t * ctx,crypto_data_t * ciphertext,crypto_data_t * plaintext,crypto_req_handle_t req)391088f8b78aSgm89044 dca_decrypt(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
391188f8b78aSgm89044     crypto_data_t *plaintext, crypto_req_handle_t req)
391288f8b78aSgm89044 {
391388f8b78aSgm89044 	int error = CRYPTO_FAILED;
391488f8b78aSgm89044 	dca_t *softc;
391588f8b78aSgm89044 
391688f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
391788f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
391888f8b78aSgm89044 
39193ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
392088f8b78aSgm89044 	DBG(softc, DENTRY, "dca_decrypt: started");
392188f8b78aSgm89044 
3922d8dd9913Sgm89044 	/* handle inplace ops */
3923d8dd9913Sgm89044 	if (!plaintext) {
3924d8dd9913Sgm89044 		dca_request_t *reqp = ctx->cc_provider_private;
3925d8dd9913Sgm89044 		reqp->dr_flags |= DR_INPLACE;
3926d8dd9913Sgm89044 		plaintext = ciphertext;
3927d8dd9913Sgm89044 	}
3928d8dd9913Sgm89044 
392988f8b78aSgm89044 	/* check mechanism */
393088f8b78aSgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
393188f8b78aSgm89044 	case DES_CBC_MECH_INFO_TYPE:
393288f8b78aSgm89044 		error = dca_3des(ctx, ciphertext, plaintext, req, DR_DECRYPT);
393388f8b78aSgm89044 		break;
393488f8b78aSgm89044 	case DES3_CBC_MECH_INFO_TYPE:
393588f8b78aSgm89044 		error = dca_3des(ctx, ciphertext, plaintext, req,
393688f8b78aSgm89044 		    DR_DECRYPT | DR_TRIPLE);
393788f8b78aSgm89044 		break;
393888f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
393988f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
394088f8b78aSgm89044 		error = dca_rsastart(ctx, ciphertext, plaintext, req,
394188f8b78aSgm89044 		    DCA_RSA_DEC);
394288f8b78aSgm89044 		break;
394388f8b78aSgm89044 	default:
394488f8b78aSgm89044 		/* Should never reach here */
394588f8b78aSgm89044 		cmn_err(CE_WARN, "dca_decrypt: unexpected mech type "
394688f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
394788f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
394888f8b78aSgm89044 	}
394988f8b78aSgm89044 
395088f8b78aSgm89044 	if ((error != CRYPTO_QUEUED) && (error != CRYPTO_SUCCESS) &&
395188f8b78aSgm89044 	    (error != CRYPTO_BUFFER_TOO_SMALL)) {
395288f8b78aSgm89044 		if (plaintext)
395388f8b78aSgm89044 			plaintext->cd_length = 0;
395488f8b78aSgm89044 	}
395588f8b78aSgm89044 
395688f8b78aSgm89044 	DBG(softc, DENTRY, "dca_decrypt: done, err = 0x%x", error);
395788f8b78aSgm89044 
395888f8b78aSgm89044 	return (error);
395988f8b78aSgm89044 }
396088f8b78aSgm89044 
396188f8b78aSgm89044 /* ARGSUSED */
396288f8b78aSgm89044 static int
dca_decrypt_update(crypto_ctx_t * ctx,crypto_data_t * ciphertext,crypto_data_t * plaintext,crypto_req_handle_t req)396388f8b78aSgm89044 dca_decrypt_update(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
396488f8b78aSgm89044     crypto_data_t *plaintext, crypto_req_handle_t req)
396588f8b78aSgm89044 {
396688f8b78aSgm89044 	int error = CRYPTO_FAILED;
396788f8b78aSgm89044 	dca_t *softc;
396888f8b78aSgm89044 
396988f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
397088f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
397188f8b78aSgm89044 
39723ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
397388f8b78aSgm89044 	DBG(softc, DENTRY, "dca_decrypt_update: started");
397488f8b78aSgm89044 
3975d8dd9913Sgm89044 	/* handle inplace ops */
3976d8dd9913Sgm89044 	if (!plaintext) {
3977d8dd9913Sgm89044 		dca_request_t *reqp = ctx->cc_provider_private;
3978d8dd9913Sgm89044 		reqp->dr_flags |= DR_INPLACE;
3979d8dd9913Sgm89044 		plaintext = ciphertext;
3980d8dd9913Sgm89044 	}
3981d8dd9913Sgm89044 
398288f8b78aSgm89044 	/* check mechanism */
398388f8b78aSgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
398488f8b78aSgm89044 	case DES_CBC_MECH_INFO_TYPE:
398588f8b78aSgm89044 		error = dca_3desupdate(ctx, ciphertext, plaintext, req,
398688f8b78aSgm89044 		    DR_DECRYPT);
398788f8b78aSgm89044 		break;
398888f8b78aSgm89044 	case DES3_CBC_MECH_INFO_TYPE:
398988f8b78aSgm89044 		error = dca_3desupdate(ctx, ciphertext, plaintext, req,
399088f8b78aSgm89044 		    DR_DECRYPT | DR_TRIPLE);
399188f8b78aSgm89044 		break;
399288f8b78aSgm89044 	default:
399388f8b78aSgm89044 		/* Should never reach here */
399488f8b78aSgm89044 		cmn_err(CE_WARN, "dca_decrypt_update: unexpected mech type "
399588f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
399688f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
399788f8b78aSgm89044 	}
399888f8b78aSgm89044 
399988f8b78aSgm89044 	DBG(softc, DENTRY, "dca_decrypt_update: done, err = 0x%x", error);
400088f8b78aSgm89044 
400188f8b78aSgm89044 	return (error);
400288f8b78aSgm89044 }
400388f8b78aSgm89044 
400488f8b78aSgm89044 /* ARGSUSED */
400588f8b78aSgm89044 static int
dca_decrypt_final(crypto_ctx_t * ctx,crypto_data_t * plaintext,crypto_req_handle_t req)400688f8b78aSgm89044 dca_decrypt_final(crypto_ctx_t *ctx, crypto_data_t *plaintext,
400788f8b78aSgm89044     crypto_req_handle_t req)
400888f8b78aSgm89044 {
400988f8b78aSgm89044 	int error = CRYPTO_FAILED;
401088f8b78aSgm89044 	dca_t *softc;
401188f8b78aSgm89044 
401288f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
401388f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
401488f8b78aSgm89044 
40153ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
401688f8b78aSgm89044 	DBG(softc, DENTRY, "dca_decrypt_final: started");
401788f8b78aSgm89044 
401888f8b78aSgm89044 	/* check mechanism */
401988f8b78aSgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
402088f8b78aSgm89044 	case DES_CBC_MECH_INFO_TYPE:
402188f8b78aSgm89044 		error = dca_3desfinal(ctx, plaintext, DR_DECRYPT);
402288f8b78aSgm89044 		break;
402388f8b78aSgm89044 	case DES3_CBC_MECH_INFO_TYPE:
402488f8b78aSgm89044 		error = dca_3desfinal(ctx, plaintext, DR_DECRYPT | DR_TRIPLE);
402588f8b78aSgm89044 		break;
402688f8b78aSgm89044 	default:
402788f8b78aSgm89044 		/* Should never reach here */
402888f8b78aSgm89044 		cmn_err(CE_WARN, "dca_decrypt_final: unexpected mech type "
402988f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
403088f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
403188f8b78aSgm89044 	}
403288f8b78aSgm89044 
403388f8b78aSgm89044 	DBG(softc, DENTRY, "dca_decrypt_final: done, err = 0x%x", error);
403488f8b78aSgm89044 
403588f8b78aSgm89044 	return (error);
403688f8b78aSgm89044 }
403788f8b78aSgm89044 
403888f8b78aSgm89044 /* ARGSUSED */
403988f8b78aSgm89044 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)404088f8b78aSgm89044 dca_decrypt_atomic(crypto_provider_handle_t provider,
404188f8b78aSgm89044     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
404288f8b78aSgm89044     crypto_key_t *key, crypto_data_t *ciphertext, crypto_data_t *plaintext,
404388f8b78aSgm89044     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
404488f8b78aSgm89044 {
404588f8b78aSgm89044 	int error = CRYPTO_FAILED;
404688f8b78aSgm89044 	dca_t *softc = (dca_t *)provider;
404788f8b78aSgm89044 
404888f8b78aSgm89044 	DBG(softc, DENTRY, "dca_decrypt_atomic: started");
404988f8b78aSgm89044 
405088f8b78aSgm89044 	if (ctx_template != NULL)
405188f8b78aSgm89044 		return (CRYPTO_ARGUMENTS_BAD);
405288f8b78aSgm89044 
4053d8dd9913Sgm89044 	/* handle inplace ops */
4054d8dd9913Sgm89044 	if (!plaintext) {
4055d8dd9913Sgm89044 		plaintext = ciphertext;
4056d8dd9913Sgm89044 	}
4057d8dd9913Sgm89044 
405888f8b78aSgm89044 	/* check mechanism */
405988f8b78aSgm89044 	switch (mechanism->cm_type) {
406088f8b78aSgm89044 	case DES_CBC_MECH_INFO_TYPE:
406188f8b78aSgm89044 		error = dca_3desatomic(provider, session_id, mechanism, key,
406288f8b78aSgm89044 		    ciphertext, plaintext, KM_SLEEP, req,
406388f8b78aSgm89044 		    DR_DECRYPT | DR_ATOMIC);
406488f8b78aSgm89044 		break;
406588f8b78aSgm89044 	case DES3_CBC_MECH_INFO_TYPE:
406688f8b78aSgm89044 		error = dca_3desatomic(provider, session_id, mechanism, key,
406788f8b78aSgm89044 		    ciphertext, plaintext, KM_SLEEP, req,
406888f8b78aSgm89044 		    DR_DECRYPT | DR_TRIPLE | DR_ATOMIC);
406988f8b78aSgm89044 		break;
407088f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
407188f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
407288f8b78aSgm89044 		error = dca_rsaatomic(provider, session_id, mechanism, key,
407388f8b78aSgm89044 		    ciphertext, plaintext, KM_SLEEP, req, DCA_RSA_DEC);
407488f8b78aSgm89044 		break;
407588f8b78aSgm89044 	default:
407688f8b78aSgm89044 		cmn_err(CE_WARN, "dca_decrypt_atomic: unexpected mech type "
407788f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
407888f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
407988f8b78aSgm89044 	}
408088f8b78aSgm89044 
408188f8b78aSgm89044 	if ((error != CRYPTO_QUEUED) && (error != CRYPTO_SUCCESS)) {
408288f8b78aSgm89044 		plaintext->cd_length = 0;
408388f8b78aSgm89044 	}
408488f8b78aSgm89044 
408588f8b78aSgm89044 	DBG(softc, DENTRY, "dca_decrypt_atomic: done, err = 0x%x", error);
408688f8b78aSgm89044 
408788f8b78aSgm89044 	return (error);
408888f8b78aSgm89044 }
408988f8b78aSgm89044 
409088f8b78aSgm89044 /*
409188f8b78aSgm89044  * Sign entry points.
409288f8b78aSgm89044  */
409388f8b78aSgm89044 
409488f8b78aSgm89044 /* ARGSUSED */
409588f8b78aSgm89044 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)409688f8b78aSgm89044 dca_sign_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
409788f8b78aSgm89044     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
409888f8b78aSgm89044     crypto_req_handle_t req)
409988f8b78aSgm89044 {
410088f8b78aSgm89044 	int error = CRYPTO_FAILED;
410188f8b78aSgm89044 	dca_t *softc;
410288f8b78aSgm89044 
41033ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
410488f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_init: started\n");
410588f8b78aSgm89044 
410688f8b78aSgm89044 	if (ctx_template != NULL)
410788f8b78aSgm89044 		return (CRYPTO_ARGUMENTS_BAD);
410888f8b78aSgm89044 
410988f8b78aSgm89044 	/* check mechanism */
411088f8b78aSgm89044 	switch (mechanism->cm_type) {
411188f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
411288f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
411388f8b78aSgm89044 		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
411488f8b78aSgm89044 		break;
411588f8b78aSgm89044 	case DSA_MECH_INFO_TYPE:
411688f8b78aSgm89044 		error = dca_dsainit(ctx, mechanism, key, KM_SLEEP,
411788f8b78aSgm89044 		    DCA_DSA_SIGN);
411888f8b78aSgm89044 		break;
411988f8b78aSgm89044 	default:
412088f8b78aSgm89044 		cmn_err(CE_WARN, "dca_sign_init: unexpected mech type "
412188f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
412288f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
412388f8b78aSgm89044 	}
412488f8b78aSgm89044 
412588f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_init: done, err = 0x%x", error);
412688f8b78aSgm89044 
412788f8b78aSgm89044 	if (error == CRYPTO_SUCCESS)
412888f8b78aSgm89044 		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
412988f8b78aSgm89044 		    &softc->dca_ctx_list_lock);
413088f8b78aSgm89044 
413188f8b78aSgm89044 	return (error);
413288f8b78aSgm89044 }
413388f8b78aSgm89044 
413488f8b78aSgm89044 static int
dca_sign(crypto_ctx_t * ctx,crypto_data_t * data,crypto_data_t * signature,crypto_req_handle_t req)413588f8b78aSgm89044 dca_sign(crypto_ctx_t *ctx, crypto_data_t *data,
413688f8b78aSgm89044     crypto_data_t *signature, crypto_req_handle_t req)
413788f8b78aSgm89044 {
413888f8b78aSgm89044 	int error = CRYPTO_FAILED;
413988f8b78aSgm89044 	dca_t *softc;
414088f8b78aSgm89044 
414188f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
414288f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
414388f8b78aSgm89044 
41443ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
414588f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign: started\n");
414688f8b78aSgm89044 
414788f8b78aSgm89044 	/* check mechanism */
414888f8b78aSgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
414988f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
415088f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
415188f8b78aSgm89044 		error = dca_rsastart(ctx, data, signature, req, DCA_RSA_SIGN);
415288f8b78aSgm89044 		break;
415388f8b78aSgm89044 	case DSA_MECH_INFO_TYPE:
415488f8b78aSgm89044 		error = dca_dsa_sign(ctx, data, signature, req);
415588f8b78aSgm89044 		break;
415688f8b78aSgm89044 	default:
415788f8b78aSgm89044 		cmn_err(CE_WARN, "dca_sign: unexpected mech type "
415888f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
415988f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
416088f8b78aSgm89044 	}
416188f8b78aSgm89044 
416288f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign: done, err = 0x%x", error);
416388f8b78aSgm89044 
416488f8b78aSgm89044 	return (error);
416588f8b78aSgm89044 }
416688f8b78aSgm89044 
416788f8b78aSgm89044 /* ARGSUSED */
416888f8b78aSgm89044 static int
dca_sign_update(crypto_ctx_t * ctx,crypto_data_t * data,crypto_req_handle_t req)416988f8b78aSgm89044 dca_sign_update(crypto_ctx_t *ctx, crypto_data_t *data,
417088f8b78aSgm89044     crypto_req_handle_t req)
417188f8b78aSgm89044 {
417288f8b78aSgm89044 	int error = CRYPTO_MECHANISM_INVALID;
417388f8b78aSgm89044 	dca_t *softc;
417488f8b78aSgm89044 
417588f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
417688f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
417788f8b78aSgm89044 
41783ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
417988f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_update: started\n");
418088f8b78aSgm89044 
418188f8b78aSgm89044 	cmn_err(CE_WARN, "dca_sign_update: unexpected mech type "
418288f8b78aSgm89044 	    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
418388f8b78aSgm89044 
418488f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_update: done, err = 0x%x", error);
418588f8b78aSgm89044 
418688f8b78aSgm89044 	return (error);
418788f8b78aSgm89044 }
418888f8b78aSgm89044 
418988f8b78aSgm89044 /* ARGSUSED */
419088f8b78aSgm89044 static int
dca_sign_final(crypto_ctx_t * ctx,crypto_data_t * signature,crypto_req_handle_t req)419188f8b78aSgm89044 dca_sign_final(crypto_ctx_t *ctx, crypto_data_t *signature,
419288f8b78aSgm89044     crypto_req_handle_t req)
419388f8b78aSgm89044 {
419488f8b78aSgm89044 	int error = CRYPTO_MECHANISM_INVALID;
419588f8b78aSgm89044 	dca_t *softc;
419688f8b78aSgm89044 
419788f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
419888f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
419988f8b78aSgm89044 
42003ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
420188f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_final: started\n");
420288f8b78aSgm89044 
420388f8b78aSgm89044 	cmn_err(CE_WARN, "dca_sign_final: unexpected mech type "
420488f8b78aSgm89044 	    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
420588f8b78aSgm89044 
420688f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_final: done, err = 0x%x", error);
420788f8b78aSgm89044 
420888f8b78aSgm89044 	return (error);
420988f8b78aSgm89044 }
421088f8b78aSgm89044 
421188f8b78aSgm89044 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)421288f8b78aSgm89044 dca_sign_atomic(crypto_provider_handle_t provider,
421388f8b78aSgm89044     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
421488f8b78aSgm89044     crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
421588f8b78aSgm89044     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
421688f8b78aSgm89044 {
421788f8b78aSgm89044 	int error = CRYPTO_FAILED;
421888f8b78aSgm89044 	dca_t *softc = (dca_t *)provider;
421988f8b78aSgm89044 
422088f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_atomic: started\n");
422188f8b78aSgm89044 
422288f8b78aSgm89044 	if (ctx_template != NULL)
422388f8b78aSgm89044 		return (CRYPTO_ARGUMENTS_BAD);
422488f8b78aSgm89044 
422588f8b78aSgm89044 	/* check mechanism */
422688f8b78aSgm89044 	switch (mechanism->cm_type) {
422788f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
422888f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
422988f8b78aSgm89044 		error = dca_rsaatomic(provider, session_id, mechanism, key,
423088f8b78aSgm89044 		    data, signature, KM_SLEEP, req, DCA_RSA_SIGN);
423188f8b78aSgm89044 		break;
423288f8b78aSgm89044 	case DSA_MECH_INFO_TYPE:
423388f8b78aSgm89044 		error = dca_dsaatomic(provider, session_id, mechanism, key,
423488f8b78aSgm89044 		    data, signature, KM_SLEEP, req, DCA_DSA_SIGN);
423588f8b78aSgm89044 		break;
423688f8b78aSgm89044 	default:
423788f8b78aSgm89044 		cmn_err(CE_WARN, "dca_sign_atomic: unexpected mech type "
423888f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
423988f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
424088f8b78aSgm89044 	}
424188f8b78aSgm89044 
424288f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_atomic: done, err = 0x%x", error);
424388f8b78aSgm89044 
424488f8b78aSgm89044 	return (error);
424588f8b78aSgm89044 }
424688f8b78aSgm89044 
424788f8b78aSgm89044 /* ARGSUSED */
424888f8b78aSgm89044 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)424988f8b78aSgm89044 dca_sign_recover_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
425088f8b78aSgm89044     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
425188f8b78aSgm89044     crypto_req_handle_t req)
425288f8b78aSgm89044 {
425388f8b78aSgm89044 	int error = CRYPTO_FAILED;
425488f8b78aSgm89044 	dca_t *softc;
425588f8b78aSgm89044 
42563ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
425788f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_recover_init: started\n");
425888f8b78aSgm89044 
425988f8b78aSgm89044 	if (ctx_template != NULL)
426088f8b78aSgm89044 		return (CRYPTO_ARGUMENTS_BAD);
426188f8b78aSgm89044 
426288f8b78aSgm89044 	/* check mechanism */
426388f8b78aSgm89044 	switch (mechanism->cm_type) {
426488f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
426588f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
426688f8b78aSgm89044 		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
426788f8b78aSgm89044 		break;
426888f8b78aSgm89044 	default:
426988f8b78aSgm89044 		cmn_err(CE_WARN, "dca_sign_recover_init: unexpected mech type "
427088f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
427188f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
427288f8b78aSgm89044 	}
427388f8b78aSgm89044 
427488f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_recover_init: done, err = 0x%x", error);
427588f8b78aSgm89044 
427688f8b78aSgm89044 	if (error == CRYPTO_SUCCESS)
427788f8b78aSgm89044 		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
427888f8b78aSgm89044 		    &softc->dca_ctx_list_lock);
427988f8b78aSgm89044 
428088f8b78aSgm89044 	return (error);
428188f8b78aSgm89044 }
428288f8b78aSgm89044 
428388f8b78aSgm89044 static int
dca_sign_recover(crypto_ctx_t * ctx,crypto_data_t * data,crypto_data_t * signature,crypto_req_handle_t req)428488f8b78aSgm89044 dca_sign_recover(crypto_ctx_t *ctx, crypto_data_t *data,
428588f8b78aSgm89044     crypto_data_t *signature, crypto_req_handle_t req)
428688f8b78aSgm89044 {
428788f8b78aSgm89044 	int error = CRYPTO_FAILED;
428888f8b78aSgm89044 	dca_t *softc;
428988f8b78aSgm89044 
429088f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
429188f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
429288f8b78aSgm89044 
42933ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
429488f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_recover: started\n");
429588f8b78aSgm89044 
429688f8b78aSgm89044 	/* check mechanism */
429788f8b78aSgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
429888f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
429988f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
430088f8b78aSgm89044 		error = dca_rsastart(ctx, data, signature, req, DCA_RSA_SIGNR);
430188f8b78aSgm89044 		break;
430288f8b78aSgm89044 	default:
430388f8b78aSgm89044 		cmn_err(CE_WARN, "dca_sign_recover: unexpected mech type "
430488f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
430588f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
430688f8b78aSgm89044 	}
430788f8b78aSgm89044 
430888f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_recover: done, err = 0x%x", error);
430988f8b78aSgm89044 
431088f8b78aSgm89044 	return (error);
431188f8b78aSgm89044 }
431288f8b78aSgm89044 
431388f8b78aSgm89044 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)431488f8b78aSgm89044 dca_sign_recover_atomic(crypto_provider_handle_t provider,
431588f8b78aSgm89044     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
431688f8b78aSgm89044     crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
431788f8b78aSgm89044     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
431888f8b78aSgm89044 {
431988f8b78aSgm89044 	int error = CRYPTO_FAILED;
432088f8b78aSgm89044 	dca_t *softc = (dca_t *)provider;
432188f8b78aSgm89044 
432288f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_recover_atomic: started\n");
432388f8b78aSgm89044 
432488f8b78aSgm89044 	if (ctx_template != NULL)
432588f8b78aSgm89044 		return (CRYPTO_ARGUMENTS_BAD);
432688f8b78aSgm89044 
432788f8b78aSgm89044 	/* check mechanism */
432888f8b78aSgm89044 	switch (mechanism->cm_type) {
432988f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
433088f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
433188f8b78aSgm89044 		error = dca_rsaatomic(provider, session_id, mechanism, key,
433288f8b78aSgm89044 		    data, signature, KM_SLEEP, req, DCA_RSA_SIGNR);
433388f8b78aSgm89044 		break;
433488f8b78aSgm89044 	default:
433588f8b78aSgm89044 		cmn_err(CE_WARN, "dca_sign_recover_atomic: unexpected mech type"
433688f8b78aSgm89044 		    " 0x%llx\n", (unsigned long long)mechanism->cm_type);
433788f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
433888f8b78aSgm89044 	}
433988f8b78aSgm89044 
434088f8b78aSgm89044 	DBG(softc, DENTRY, "dca_sign_recover_atomic: done, err = 0x%x", error);
434188f8b78aSgm89044 
434288f8b78aSgm89044 	return (error);
434388f8b78aSgm89044 }
434488f8b78aSgm89044 
434588f8b78aSgm89044 /*
434688f8b78aSgm89044  * Verify entry points.
434788f8b78aSgm89044  */
434888f8b78aSgm89044 
434988f8b78aSgm89044 /* ARGSUSED */
435088f8b78aSgm89044 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)435188f8b78aSgm89044 dca_verify_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
435288f8b78aSgm89044     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
435388f8b78aSgm89044     crypto_req_handle_t req)
435488f8b78aSgm89044 {
435588f8b78aSgm89044 	int error = CRYPTO_FAILED;
435688f8b78aSgm89044 	dca_t *softc;
435788f8b78aSgm89044 
43583ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
435988f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_init: started\n");
436088f8b78aSgm89044 
436188f8b78aSgm89044 	if (ctx_template != NULL)
436288f8b78aSgm89044 		return (CRYPTO_ARGUMENTS_BAD);
436388f8b78aSgm89044 
436488f8b78aSgm89044 	/* check mechanism */
436588f8b78aSgm89044 	switch (mechanism->cm_type) {
436688f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
436788f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
436888f8b78aSgm89044 		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
436988f8b78aSgm89044 		break;
437088f8b78aSgm89044 	case DSA_MECH_INFO_TYPE:
437188f8b78aSgm89044 		error = dca_dsainit(ctx, mechanism, key, KM_SLEEP,
437288f8b78aSgm89044 		    DCA_DSA_VRFY);
437388f8b78aSgm89044 		break;
437488f8b78aSgm89044 	default:
437588f8b78aSgm89044 		cmn_err(CE_WARN, "dca_verify_init: unexpected mech type "
437688f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
437788f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
437888f8b78aSgm89044 	}
437988f8b78aSgm89044 
438088f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_init: done, err = 0x%x", error);
438188f8b78aSgm89044 
438288f8b78aSgm89044 	if (error == CRYPTO_SUCCESS)
438388f8b78aSgm89044 		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
438488f8b78aSgm89044 		    &softc->dca_ctx_list_lock);
438588f8b78aSgm89044 
438688f8b78aSgm89044 	return (error);
438788f8b78aSgm89044 }
438888f8b78aSgm89044 
438988f8b78aSgm89044 static int
dca_verify(crypto_ctx_t * ctx,crypto_data_t * data,crypto_data_t * signature,crypto_req_handle_t req)439088f8b78aSgm89044 dca_verify(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *signature,
439188f8b78aSgm89044     crypto_req_handle_t req)
439288f8b78aSgm89044 {
439388f8b78aSgm89044 	int error = CRYPTO_FAILED;
439488f8b78aSgm89044 	dca_t *softc;
439588f8b78aSgm89044 
439688f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
439788f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
439888f8b78aSgm89044 
43993ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
440088f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify: started\n");
440188f8b78aSgm89044 
440288f8b78aSgm89044 	/* check mechanism */
440388f8b78aSgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
440488f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
440588f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
440688f8b78aSgm89044 		error = dca_rsastart(ctx, signature, data, req, DCA_RSA_VRFY);
440788f8b78aSgm89044 		break;
440888f8b78aSgm89044 	case DSA_MECH_INFO_TYPE:
440988f8b78aSgm89044 		error = dca_dsa_verify(ctx, data, signature, req);
441088f8b78aSgm89044 		break;
441188f8b78aSgm89044 	default:
441288f8b78aSgm89044 		cmn_err(CE_WARN, "dca_verify: unexpected mech type "
441388f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
441488f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
441588f8b78aSgm89044 	}
441688f8b78aSgm89044 
441788f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify: done, err = 0x%x", error);
441888f8b78aSgm89044 
441988f8b78aSgm89044 	return (error);
442088f8b78aSgm89044 }
442188f8b78aSgm89044 
442288f8b78aSgm89044 /* ARGSUSED */
442388f8b78aSgm89044 static int
dca_verify_update(crypto_ctx_t * ctx,crypto_data_t * data,crypto_req_handle_t req)442488f8b78aSgm89044 dca_verify_update(crypto_ctx_t *ctx, crypto_data_t *data,
442588f8b78aSgm89044     crypto_req_handle_t req)
442688f8b78aSgm89044 {
442788f8b78aSgm89044 	int error = CRYPTO_MECHANISM_INVALID;
442888f8b78aSgm89044 	dca_t *softc;
442988f8b78aSgm89044 
443088f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
443188f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
443288f8b78aSgm89044 
44333ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
443488f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_update: started\n");
443588f8b78aSgm89044 
443688f8b78aSgm89044 	cmn_err(CE_WARN, "dca_verify_update: unexpected mech type "
443788f8b78aSgm89044 	    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
443888f8b78aSgm89044 
443988f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_update: done, err = 0x%x", error);
444088f8b78aSgm89044 
444188f8b78aSgm89044 	return (error);
444288f8b78aSgm89044 }
444388f8b78aSgm89044 
444488f8b78aSgm89044 /* ARGSUSED */
444588f8b78aSgm89044 static int
dca_verify_final(crypto_ctx_t * ctx,crypto_data_t * signature,crypto_req_handle_t req)444688f8b78aSgm89044 dca_verify_final(crypto_ctx_t *ctx, crypto_data_t *signature,
444788f8b78aSgm89044     crypto_req_handle_t req)
444888f8b78aSgm89044 {
444988f8b78aSgm89044 	int error = CRYPTO_MECHANISM_INVALID;
445088f8b78aSgm89044 	dca_t *softc;
445188f8b78aSgm89044 
445288f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
445388f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
445488f8b78aSgm89044 
44553ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
445688f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_final: started\n");
445788f8b78aSgm89044 
445888f8b78aSgm89044 	cmn_err(CE_WARN, "dca_verify_final: unexpected mech type "
445988f8b78aSgm89044 	    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
446088f8b78aSgm89044 
446188f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_final: done, err = 0x%x", error);
446288f8b78aSgm89044 
446388f8b78aSgm89044 	return (error);
446488f8b78aSgm89044 }
446588f8b78aSgm89044 
446688f8b78aSgm89044 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)446788f8b78aSgm89044 dca_verify_atomic(crypto_provider_handle_t provider,
446888f8b78aSgm89044     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
446988f8b78aSgm89044     crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
447088f8b78aSgm89044     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
447188f8b78aSgm89044 {
447288f8b78aSgm89044 	int error = CRYPTO_FAILED;
447388f8b78aSgm89044 	dca_t *softc = (dca_t *)provider;
447488f8b78aSgm89044 
447588f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_atomic: started\n");
447688f8b78aSgm89044 
447788f8b78aSgm89044 	if (ctx_template != NULL)
447888f8b78aSgm89044 		return (CRYPTO_ARGUMENTS_BAD);
447988f8b78aSgm89044 
448088f8b78aSgm89044 	/* check mechanism */
448188f8b78aSgm89044 	switch (mechanism->cm_type) {
448288f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
448388f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
448488f8b78aSgm89044 		error = dca_rsaatomic(provider, session_id, mechanism, key,
448588f8b78aSgm89044 		    signature, data, KM_SLEEP, req, DCA_RSA_VRFY);
448688f8b78aSgm89044 		break;
448788f8b78aSgm89044 	case DSA_MECH_INFO_TYPE:
448888f8b78aSgm89044 		error = dca_dsaatomic(provider, session_id, mechanism, key,
448988f8b78aSgm89044 		    data, signature, KM_SLEEP, req, DCA_DSA_VRFY);
449088f8b78aSgm89044 		break;
449188f8b78aSgm89044 	default:
449288f8b78aSgm89044 		cmn_err(CE_WARN, "dca_verify_atomic: unexpected mech type "
449388f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
449488f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
449588f8b78aSgm89044 	}
449688f8b78aSgm89044 
449788f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_atomic: done, err = 0x%x", error);
449888f8b78aSgm89044 
449988f8b78aSgm89044 	return (error);
450088f8b78aSgm89044 }
450188f8b78aSgm89044 
450288f8b78aSgm89044 /* ARGSUSED */
450388f8b78aSgm89044 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)450488f8b78aSgm89044 dca_verify_recover_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
450588f8b78aSgm89044     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
450688f8b78aSgm89044     crypto_req_handle_t req)
450788f8b78aSgm89044 {
450888f8b78aSgm89044 	int error = CRYPTO_MECHANISM_INVALID;
450988f8b78aSgm89044 	dca_t *softc;
451088f8b78aSgm89044 
45113ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
451288f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_recover_init: started\n");
451388f8b78aSgm89044 
451488f8b78aSgm89044 	if (ctx_template != NULL)
451588f8b78aSgm89044 		return (CRYPTO_ARGUMENTS_BAD);
451688f8b78aSgm89044 
451788f8b78aSgm89044 	/* check mechanism */
451888f8b78aSgm89044 	switch (mechanism->cm_type) {
451988f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
452088f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
452188f8b78aSgm89044 		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
452288f8b78aSgm89044 		break;
452388f8b78aSgm89044 	default:
452488f8b78aSgm89044 		cmn_err(CE_WARN, "dca_verify_recover_init: unexpected mech type"
452588f8b78aSgm89044 		    " 0x%llx\n", (unsigned long long)mechanism->cm_type);
452688f8b78aSgm89044 	}
452788f8b78aSgm89044 
452888f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_recover_init: done, err = 0x%x", error);
452988f8b78aSgm89044 
453088f8b78aSgm89044 	if (error == CRYPTO_SUCCESS)
453188f8b78aSgm89044 		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
453288f8b78aSgm89044 		    &softc->dca_ctx_list_lock);
453388f8b78aSgm89044 
453488f8b78aSgm89044 	return (error);
453588f8b78aSgm89044 }
453688f8b78aSgm89044 
453788f8b78aSgm89044 static int
dca_verify_recover(crypto_ctx_t * ctx,crypto_data_t * signature,crypto_data_t * data,crypto_req_handle_t req)453888f8b78aSgm89044 dca_verify_recover(crypto_ctx_t *ctx, crypto_data_t *signature,
453988f8b78aSgm89044     crypto_data_t *data, crypto_req_handle_t req)
454088f8b78aSgm89044 {
454188f8b78aSgm89044 	int error = CRYPTO_MECHANISM_INVALID;
454288f8b78aSgm89044 	dca_t *softc;
454388f8b78aSgm89044 
454488f8b78aSgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
454588f8b78aSgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
454688f8b78aSgm89044 
45473ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
454888f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_recover: started\n");
454988f8b78aSgm89044 
455088f8b78aSgm89044 	/* check mechanism */
455188f8b78aSgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
455288f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
455388f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
455488f8b78aSgm89044 		error = dca_rsastart(ctx, signature, data, req, DCA_RSA_VRFYR);
455588f8b78aSgm89044 		break;
455688f8b78aSgm89044 	default:
455788f8b78aSgm89044 		cmn_err(CE_WARN, "dca_verify_recover: unexpected mech type "
455888f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
455988f8b78aSgm89044 	}
456088f8b78aSgm89044 
456188f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_recover: done, err = 0x%x", error);
456288f8b78aSgm89044 
456388f8b78aSgm89044 	return (error);
456488f8b78aSgm89044 }
456588f8b78aSgm89044 
456688f8b78aSgm89044 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)456788f8b78aSgm89044 dca_verify_recover_atomic(crypto_provider_handle_t provider,
456888f8b78aSgm89044     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
456988f8b78aSgm89044     crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
457088f8b78aSgm89044     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
457188f8b78aSgm89044 {
457288f8b78aSgm89044 	int error = CRYPTO_MECHANISM_INVALID;
457388f8b78aSgm89044 	dca_t *softc = (dca_t *)provider;
457488f8b78aSgm89044 
457588f8b78aSgm89044 	DBG(softc, DENTRY, "dca_verify_recover_atomic: started\n");
457688f8b78aSgm89044 
457788f8b78aSgm89044 	if (ctx_template != NULL)
457888f8b78aSgm89044 		return (CRYPTO_ARGUMENTS_BAD);
457988f8b78aSgm89044 
458088f8b78aSgm89044 	/* check mechanism */
458188f8b78aSgm89044 	switch (mechanism->cm_type) {
458288f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
458388f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
458488f8b78aSgm89044 		error = dca_rsaatomic(provider, session_id, mechanism, key,
458588f8b78aSgm89044 		    signature, data, KM_SLEEP, req, DCA_RSA_VRFYR);
458688f8b78aSgm89044 		break;
458788f8b78aSgm89044 	default:
458888f8b78aSgm89044 		cmn_err(CE_WARN, "dca_verify_recover_atomic: unexpected mech "
458988f8b78aSgm89044 		    "type 0x%llx\n", (unsigned long long)mechanism->cm_type);
459088f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
459188f8b78aSgm89044 	}
459288f8b78aSgm89044 
459388f8b78aSgm89044 	DBG(softc, DENTRY,
459488f8b78aSgm89044 	    "dca_verify_recover_atomic: done, err = 0x%x", error);
459588f8b78aSgm89044 
459688f8b78aSgm89044 	return (error);
459788f8b78aSgm89044 }
459888f8b78aSgm89044 
459988f8b78aSgm89044 /*
460088f8b78aSgm89044  * Random number entry points.
460188f8b78aSgm89044  */
460288f8b78aSgm89044 
460388f8b78aSgm89044 /* ARGSUSED */
460488f8b78aSgm89044 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)460588f8b78aSgm89044 dca_generate_random(crypto_provider_handle_t provider,
460688f8b78aSgm89044     crypto_session_id_t session_id,
460788f8b78aSgm89044     uchar_t *buf, size_t len, crypto_req_handle_t req)
460888f8b78aSgm89044 {
460988f8b78aSgm89044 	int error = CRYPTO_FAILED;
461088f8b78aSgm89044 	dca_t *softc = (dca_t *)provider;
461188f8b78aSgm89044 
461288f8b78aSgm89044 	DBG(softc, DENTRY, "dca_generate_random: started");
461388f8b78aSgm89044 
461488f8b78aSgm89044 	error = dca_rng(softc, buf, len, req);
461588f8b78aSgm89044 
461688f8b78aSgm89044 	DBG(softc, DENTRY, "dca_generate_random: done, err = 0x%x", error);
461788f8b78aSgm89044 
461888f8b78aSgm89044 	return (error);
461988f8b78aSgm89044 }
462088f8b78aSgm89044 
462188f8b78aSgm89044 /*
462288f8b78aSgm89044  * Context management entry points.
462388f8b78aSgm89044  */
462488f8b78aSgm89044 
462588f8b78aSgm89044 int
dca_free_context(crypto_ctx_t * ctx)462688f8b78aSgm89044 dca_free_context(crypto_ctx_t *ctx)
462788f8b78aSgm89044 {
462888f8b78aSgm89044 	int error = CRYPTO_SUCCESS;
462988f8b78aSgm89044 	dca_t *softc;
463088f8b78aSgm89044 
46313ba94426SRichard Lowe 	softc = DCA_SOFTC_FROM_CTX(ctx);
463288f8b78aSgm89044 	DBG(softc, DENTRY, "dca_free_context: entered");
463388f8b78aSgm89044 
463488f8b78aSgm89044 	if (ctx->cc_provider_private == NULL)
463588f8b78aSgm89044 		return (error);
463688f8b78aSgm89044 
463788f8b78aSgm89044 	dca_rmlist2(ctx->cc_provider_private, &softc->dca_ctx_list_lock);
463888f8b78aSgm89044 
463988f8b78aSgm89044 	error = dca_free_context_low(ctx);
464088f8b78aSgm89044 
464188f8b78aSgm89044 	DBG(softc, DENTRY, "dca_free_context: done, err = 0x%x", error);
464288f8b78aSgm89044 
464388f8b78aSgm89044 	return (error);
464488f8b78aSgm89044 }
464588f8b78aSgm89044 
464688f8b78aSgm89044 static int
dca_free_context_low(crypto_ctx_t * ctx)464788f8b78aSgm89044 dca_free_context_low(crypto_ctx_t *ctx)
464888f8b78aSgm89044 {
464988f8b78aSgm89044 	int error = CRYPTO_SUCCESS;
465088f8b78aSgm89044 
465188f8b78aSgm89044 	/* check mechanism */
465288f8b78aSgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
465388f8b78aSgm89044 	case DES_CBC_MECH_INFO_TYPE:
465488f8b78aSgm89044 	case DES3_CBC_MECH_INFO_TYPE:
465588f8b78aSgm89044 		dca_3desctxfree(ctx);
465688f8b78aSgm89044 		break;
465788f8b78aSgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
465888f8b78aSgm89044 	case RSA_X_509_MECH_INFO_TYPE:
465988f8b78aSgm89044 		dca_rsactxfree(ctx);
466088f8b78aSgm89044 		break;
466188f8b78aSgm89044 	case DSA_MECH_INFO_TYPE:
466288f8b78aSgm89044 		dca_dsactxfree(ctx);
466388f8b78aSgm89044 		break;
466488f8b78aSgm89044 	default:
466588f8b78aSgm89044 		/* Should never reach here */
466688f8b78aSgm89044 		cmn_err(CE_WARN, "dca_free_context_low: unexpected mech type "
466788f8b78aSgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
466888f8b78aSgm89044 		error = CRYPTO_MECHANISM_INVALID;
466988f8b78aSgm89044 	}
467088f8b78aSgm89044 
467188f8b78aSgm89044 	return (error);
467288f8b78aSgm89044 }
467388f8b78aSgm89044 
467488f8b78aSgm89044 
467588f8b78aSgm89044 /* Free any unfreed private context. It is called in detach. */
467688f8b78aSgm89044 static void
dca_free_context_list(dca_t * dca)467788f8b78aSgm89044 dca_free_context_list(dca_t *dca)
467888f8b78aSgm89044 {
467988f8b78aSgm89044 	dca_listnode_t	*node;
468088f8b78aSgm89044 	crypto_ctx_t	ctx;
468188f8b78aSgm89044 
468288f8b78aSgm89044 	(void) memset(&ctx, 0, sizeof (ctx));
468388f8b78aSgm89044 	ctx.cc_provider = dca;
468488f8b78aSgm89044 
468588f8b78aSgm89044 	while ((node = dca_delist2(&dca->dca_ctx_list,
468688f8b78aSgm89044 	    &dca->dca_ctx_list_lock)) != NULL) {
468788f8b78aSgm89044 		ctx.cc_provider_private = node;
468888f8b78aSgm89044 		(void) dca_free_context_low(&ctx);
468988f8b78aSgm89044 	}
469088f8b78aSgm89044 }
469188f8b78aSgm89044 
469288f8b78aSgm89044 static int
ext_info_sym(crypto_provider_handle_t prov,crypto_provider_ext_info_t * ext_info,crypto_req_handle_t cfreq)469388f8b78aSgm89044 ext_info_sym(crypto_provider_handle_t prov,
469488f8b78aSgm89044     crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq)
469588f8b78aSgm89044 {
469688f8b78aSgm89044 	return (ext_info_base(prov, ext_info, cfreq, IDENT_SYM));
469788f8b78aSgm89044 }
469888f8b78aSgm89044 
469988f8b78aSgm89044 static int
ext_info_asym(crypto_provider_handle_t prov,crypto_provider_ext_info_t * ext_info,crypto_req_handle_t cfreq)470088f8b78aSgm89044 ext_info_asym(crypto_provider_handle_t prov,
470188f8b78aSgm89044     crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq)
470288f8b78aSgm89044 {
470388f8b78aSgm89044 	int rv;
470488f8b78aSgm89044 
470588f8b78aSgm89044 	rv = ext_info_base(prov, ext_info, cfreq, IDENT_ASYM);
470688f8b78aSgm89044 	/* The asymmetric cipher slot supports random */
470788f8b78aSgm89044 	ext_info->ei_flags |= CRYPTO_EXTF_RNG;
470888f8b78aSgm89044 
470988f8b78aSgm89044 	return (rv);
471088f8b78aSgm89044 }
471188f8b78aSgm89044 
471288f8b78aSgm89044 /* ARGSUSED */
471388f8b78aSgm89044 static int
ext_info_base(crypto_provider_handle_t prov,crypto_provider_ext_info_t * ext_info,crypto_req_handle_t cfreq,char * id)471488f8b78aSgm89044 ext_info_base(crypto_provider_handle_t prov,
471588f8b78aSgm89044     crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq, char *id)
471688f8b78aSgm89044 {
471788f8b78aSgm89044 	dca_t   *dca = (dca_t *)prov;
471888f8b78aSgm89044 	int len;
471988f8b78aSgm89044 
472088f8b78aSgm89044 	/* Label */
472188f8b78aSgm89044 	(void) sprintf((char *)ext_info->ei_label, "%s/%d %s",
472288f8b78aSgm89044 	    ddi_driver_name(dca->dca_dip), ddi_get_instance(dca->dca_dip), id);
472388f8b78aSgm89044 	len = strlen((char *)ext_info->ei_label);
472488f8b78aSgm89044 	(void) memset(ext_info->ei_label + len, ' ',
472588f8b78aSgm89044 	    CRYPTO_EXT_SIZE_LABEL - len);
472688f8b78aSgm89044 
472788f8b78aSgm89044 	/* Manufacturer ID */
472888f8b78aSgm89044 	(void) sprintf((char *)ext_info->ei_manufacturerID, "%s",
472988f8b78aSgm89044 	    DCA_MANUFACTURER_ID);
473088f8b78aSgm89044 	len = strlen((char *)ext_info->ei_manufacturerID);
473188f8b78aSgm89044 	(void) memset(ext_info->ei_manufacturerID + len, ' ',
473288f8b78aSgm89044 	    CRYPTO_EXT_SIZE_MANUF - len);
473388f8b78aSgm89044 
473488f8b78aSgm89044 	/* Model */
473588f8b78aSgm89044 	(void) sprintf((char *)ext_info->ei_model, dca->dca_model);
473688f8b78aSgm89044 
473788f8b78aSgm89044 	DBG(dca, DWARN, "kCF MODEL: %s", (char *)ext_info->ei_model);
473888f8b78aSgm89044 
473988f8b78aSgm89044 	len = strlen((char *)ext_info->ei_model);
474088f8b78aSgm89044 	(void) memset(ext_info->ei_model + len, ' ',
474188f8b78aSgm89044 	    CRYPTO_EXT_SIZE_MODEL - len);
474288f8b78aSgm89044 
474388f8b78aSgm89044 	/* Serial Number. Blank for Deimos */
474488f8b78aSgm89044 	(void) memset(ext_info->ei_serial_number, ' ', CRYPTO_EXT_SIZE_SERIAL);
474588f8b78aSgm89044 
474688f8b78aSgm89044 	ext_info->ei_flags = CRYPTO_EXTF_WRITE_PROTECTED;
474788f8b78aSgm89044 
474888f8b78aSgm89044 	ext_info->ei_max_session_count = CRYPTO_UNAVAILABLE_INFO;
474988f8b78aSgm89044 	ext_info->ei_max_pin_len = CRYPTO_UNAVAILABLE_INFO;
475088f8b78aSgm89044 	ext_info->ei_min_pin_len = CRYPTO_UNAVAILABLE_INFO;
475188f8b78aSgm89044 	ext_info->ei_total_public_memory = CRYPTO_UNAVAILABLE_INFO;
475288f8b78aSgm89044 	ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO;
475388f8b78aSgm89044 	ext_info->ei_total_private_memory = CRYPTO_UNAVAILABLE_INFO;
475488f8b78aSgm89044 	ext_info->ei_free_private_memory = CRYPTO_UNAVAILABLE_INFO;
475588f8b78aSgm89044 	ext_info->ei_hardware_version.cv_major = 0;
475688f8b78aSgm89044 	ext_info->ei_hardware_version.cv_minor = 0;
475788f8b78aSgm89044 	ext_info->ei_firmware_version.cv_major = 0;
475888f8b78aSgm89044 	ext_info->ei_firmware_version.cv_minor = 0;
475988f8b78aSgm89044 
476088f8b78aSgm89044 	/* Time. No need to be supplied for token without a clock */
476188f8b78aSgm89044 	ext_info->ei_time[0] = '\000';
476288f8b78aSgm89044 
476388f8b78aSgm89044 	return (CRYPTO_SUCCESS);
476488f8b78aSgm89044 }
476588f8b78aSgm89044 
476688f8b78aSgm89044 static void
dca_fma_init(dca_t * dca)476788f8b78aSgm89044 dca_fma_init(dca_t *dca)
476888f8b78aSgm89044 {
476988f8b78aSgm89044 	ddi_iblock_cookie_t fm_ibc;
477088f8b78aSgm89044 	int fm_capabilities = DDI_FM_EREPORT_CAPABLE |
477188f8b78aSgm89044 	    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE |
477288f8b78aSgm89044 	    DDI_FM_ERRCB_CAPABLE;
477388f8b78aSgm89044 
477488f8b78aSgm89044 	/* Read FMA capabilities from dca.conf file (if present) */
477588f8b78aSgm89044 	dca->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, dca->dca_dip,
477688f8b78aSgm89044 	    DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "fm-capable",
477788f8b78aSgm89044 	    fm_capabilities);
477888f8b78aSgm89044 
477988f8b78aSgm89044 	DBG(dca, DWARN, "dca->fm_capabilities = 0x%x", dca->fm_capabilities);
478088f8b78aSgm89044 
478188f8b78aSgm89044 	/* Only register with IO Fault Services if we have some capability */
478288f8b78aSgm89044 	if (dca->fm_capabilities) {
478388f8b78aSgm89044 		dca_regsattr.devacc_attr_access = DDI_FLAGERR_ACC;
478488f8b78aSgm89044 		dca_dmaattr.dma_attr_flags = DDI_DMA_FLAGERR;
478588f8b78aSgm89044 
478688f8b78aSgm89044 		/* Register capabilities with IO Fault Services */
478788f8b78aSgm89044 		ddi_fm_init(dca->dca_dip, &dca->fm_capabilities, &fm_ibc);
478888f8b78aSgm89044 		DBG(dca, DWARN, "fm_capable() =  0x%x",
478988f8b78aSgm89044 		    ddi_fm_capable(dca->dca_dip));
479088f8b78aSgm89044 
479188f8b78aSgm89044 		/*
479288f8b78aSgm89044 		 * Initialize pci ereport capabilities if ereport capable
479388f8b78aSgm89044 		 */
479400d0963fSdilpreet 		if (DDI_FM_EREPORT_CAP(dca->fm_capabilities) ||
479500d0963fSdilpreet 		    DDI_FM_ERRCB_CAP(dca->fm_capabilities))
479688f8b78aSgm89044 			pci_ereport_setup(dca->dca_dip);
479788f8b78aSgm89044 
479888f8b78aSgm89044 		/*
479988f8b78aSgm89044 		 * Initialize callback mutex and register error callback if
480088f8b78aSgm89044 		 * error callback capable.
480188f8b78aSgm89044 		 */
480288f8b78aSgm89044 		if (DDI_FM_ERRCB_CAP(dca->fm_capabilities)) {
480388f8b78aSgm89044 			ddi_fm_handler_register(dca->dca_dip, dca_fm_error_cb,
480488f8b78aSgm89044 			    (void *)dca);
480588f8b78aSgm89044 		}
480688f8b78aSgm89044 	} else {
480788f8b78aSgm89044 		/*
480888f8b78aSgm89044 		 * These fields have to be cleared of FMA if there are no
480988f8b78aSgm89044 		 * FMA capabilities at runtime.
481088f8b78aSgm89044 		 */
481188f8b78aSgm89044 		dca_regsattr.devacc_attr_access = DDI_DEFAULT_ACC;
481288f8b78aSgm89044 		dca_dmaattr.dma_attr_flags = 0;
481388f8b78aSgm89044 	}
481488f8b78aSgm89044 }
481588f8b78aSgm89044 
481688f8b78aSgm89044 
481788f8b78aSgm89044 static void
dca_fma_fini(dca_t * dca)481888f8b78aSgm89044 dca_fma_fini(dca_t *dca)
481988f8b78aSgm89044 {
482088f8b78aSgm89044 	/* Only unregister FMA capabilities if we registered some */
482188f8b78aSgm89044 	if (dca->fm_capabilities) {
482288f8b78aSgm89044 
482388f8b78aSgm89044 		/*
482488f8b78aSgm89044 		 * Release any resources allocated by pci_ereport_setup()
482588f8b78aSgm89044 		 */
482600d0963fSdilpreet 		if (DDI_FM_EREPORT_CAP(dca->fm_capabilities) ||
482700d0963fSdilpreet 		    DDI_FM_ERRCB_CAP(dca->fm_capabilities)) {
482888f8b78aSgm89044 			pci_ereport_teardown(dca->dca_dip);
482988f8b78aSgm89044 		}
483088f8b78aSgm89044 
483188f8b78aSgm89044 		/*
483288f8b78aSgm89044 		 * Free callback mutex and un-register error callback if
483388f8b78aSgm89044 		 * error callback capable.
483488f8b78aSgm89044 		 */
483588f8b78aSgm89044 		if (DDI_FM_ERRCB_CAP(dca->fm_capabilities)) {
483688f8b78aSgm89044 			ddi_fm_handler_unregister(dca->dca_dip);
483788f8b78aSgm89044 		}
483888f8b78aSgm89044 
483988f8b78aSgm89044 		/* Unregister from IO Fault Services */
484088f8b78aSgm89044 		ddi_fm_fini(dca->dca_dip);
484188f8b78aSgm89044 		DBG(dca, DWARN, "fm_capable() = 0x%x",
484288f8b78aSgm89044 		    ddi_fm_capable(dca->dca_dip));
484388f8b78aSgm89044 	}
484488f8b78aSgm89044 }
484588f8b78aSgm89044 
484688f8b78aSgm89044 
484788f8b78aSgm89044 /*
484888f8b78aSgm89044  * The IO fault service error handling callback function
484988f8b78aSgm89044  */
485000d0963fSdilpreet /*ARGSUSED*/
485188f8b78aSgm89044 static int
dca_fm_error_cb(dev_info_t * dip,ddi_fm_error_t * err,const void * impl_data)485288f8b78aSgm89044 dca_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
485388f8b78aSgm89044 {
485488f8b78aSgm89044 	dca_t		*dca = (dca_t *)impl_data;
485588f8b78aSgm89044 
485600d0963fSdilpreet 	pci_ereport_post(dip, err, NULL);
485700d0963fSdilpreet 	if (err->fme_status == DDI_FM_FATAL) {
485888f8b78aSgm89044 		dca_failure(dca, DDI_DATAPATH_FAULT,
485988f8b78aSgm89044 		    DCA_FM_ECLASS_NONE, dca_ena(0), CRYPTO_DEVICE_ERROR,
486088f8b78aSgm89044 		    "fault PCI in FMA callback.");
486188f8b78aSgm89044 	}
486200d0963fSdilpreet 	return (err->fme_status);
486388f8b78aSgm89044 }
486488f8b78aSgm89044 
486588f8b78aSgm89044 
486688f8b78aSgm89044 static int
dca_check_acc_handle(dca_t * dca,ddi_acc_handle_t handle,dca_fma_eclass_t eclass_index)486788f8b78aSgm89044 dca_check_acc_handle(dca_t *dca, ddi_acc_handle_t handle,
486888f8b78aSgm89044     dca_fma_eclass_t eclass_index)
486988f8b78aSgm89044 {
487088f8b78aSgm89044 	ddi_fm_error_t	de;
487188f8b78aSgm89044 	int		version = 0;
487288f8b78aSgm89044 
487388f8b78aSgm89044 	ddi_fm_acc_err_get(handle, &de, version);
487488f8b78aSgm89044 	if (de.fme_status != DDI_FM_OK) {
487588f8b78aSgm89044 		dca_failure(dca, DDI_DATAPATH_FAULT,
487688f8b78aSgm89044 		    eclass_index, fm_ena_increment(de.fme_ena),
487788f8b78aSgm89044 		    CRYPTO_DEVICE_ERROR, "");
487888f8b78aSgm89044 		return (DDI_FAILURE);
487988f8b78aSgm89044 	}
488088f8b78aSgm89044 
488188f8b78aSgm89044 	return (DDI_SUCCESS);
488288f8b78aSgm89044 }
488388f8b78aSgm89044 
488488f8b78aSgm89044 int
dca_check_dma_handle(dca_t * dca,ddi_dma_handle_t handle,dca_fma_eclass_t eclass_index)488588f8b78aSgm89044 dca_check_dma_handle(dca_t *dca, ddi_dma_handle_t handle,
488688f8b78aSgm89044     dca_fma_eclass_t eclass_index)
488788f8b78aSgm89044 {
488888f8b78aSgm89044 	ddi_fm_error_t	de;
488988f8b78aSgm89044 	int		version = 0;
489088f8b78aSgm89044 
489188f8b78aSgm89044 	ddi_fm_dma_err_get(handle, &de, version);
489288f8b78aSgm89044 	if (de.fme_status != DDI_FM_OK) {
489388f8b78aSgm89044 		dca_failure(dca, DDI_DATAPATH_FAULT,
489488f8b78aSgm89044 		    eclass_index, fm_ena_increment(de.fme_ena),
489588f8b78aSgm89044 		    CRYPTO_DEVICE_ERROR, "");
489688f8b78aSgm89044 		return (DDI_FAILURE);
489788f8b78aSgm89044 	}
489888f8b78aSgm89044 	return (DDI_SUCCESS);
489988f8b78aSgm89044 }
490088f8b78aSgm89044 
490188f8b78aSgm89044 static uint64_t
dca_ena(uint64_t ena)490288f8b78aSgm89044 dca_ena(uint64_t ena)
490388f8b78aSgm89044 {
490488f8b78aSgm89044 	if (ena == 0)
490588f8b78aSgm89044 		ena = fm_ena_generate(0, FM_ENA_FMT1);
490688f8b78aSgm89044 	else
490788f8b78aSgm89044 		ena = fm_ena_increment(ena);
490888f8b78aSgm89044 	return (ena);
490988f8b78aSgm89044 }
491088f8b78aSgm89044 
491188f8b78aSgm89044 static char *
dca_fma_eclass_string(char * model,dca_fma_eclass_t index)491288f8b78aSgm89044 dca_fma_eclass_string(char *model, dca_fma_eclass_t index)
491388f8b78aSgm89044 {
491488f8b78aSgm89044 	if (strstr(model, "500"))
491588f8b78aSgm89044 		return (dca_fma_eclass_sca500[index]);
491688f8b78aSgm89044 	else
491788f8b78aSgm89044 		return (dca_fma_eclass_sca1000[index]);
491888f8b78aSgm89044 }
4919