xref: /onnv-gate/usr/src/uts/common/crypto/io/dca.c (revision 5063:c7cb857a0196)
1906Sgm89044 /*
2906Sgm89044  * CDDL HEADER START
3906Sgm89044  *
4906Sgm89044  * The contents of this file are subject to the terms of the
5906Sgm89044  * Common Development and Distribution License (the "License").
6906Sgm89044  * You may not use this file except in compliance with the License.
7906Sgm89044  *
8906Sgm89044  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9906Sgm89044  * or http://www.opensolaris.org/os/licensing.
10906Sgm89044  * See the License for the specific language governing permissions
11906Sgm89044  * and limitations under the License.
12906Sgm89044  *
13906Sgm89044  * When distributing Covered Code, include this CDDL HEADER in each
14906Sgm89044  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15906Sgm89044  * If applicable, add the following below this CDDL HEADER, with the
16906Sgm89044  * fields enclosed by brackets "[]" replaced with your own identifying
17906Sgm89044  * information: Portions Copyright [yyyy] [name of copyright owner]
18906Sgm89044  *
19906Sgm89044  * CDDL HEADER END
20906Sgm89044  */
21906Sgm89044 
22906Sgm89044 /*
23*5063Sgm89044  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24906Sgm89044  * Use is subject to license terms.
25906Sgm89044  */
26906Sgm89044 
27906Sgm89044 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28906Sgm89044 
29906Sgm89044 /*
30906Sgm89044  * Deimos - cryptographic acceleration based upon Broadcom 582x.
31906Sgm89044  */
32906Sgm89044 
33906Sgm89044 #include <sys/types.h>
34906Sgm89044 #include <sys/modctl.h>
35906Sgm89044 #include <sys/conf.h>
36906Sgm89044 #include <sys/devops.h>
37906Sgm89044 #include <sys/ddi.h>
38906Sgm89044 #include <sys/sunddi.h>
39906Sgm89044 #include <sys/cmn_err.h>
40906Sgm89044 #include <sys/varargs.h>
41906Sgm89044 #include <sys/file.h>
42906Sgm89044 #include <sys/stat.h>
43906Sgm89044 #include <sys/kmem.h>
44906Sgm89044 #include <sys/ioccom.h>
45906Sgm89044 #include <sys/open.h>
46906Sgm89044 #include <sys/cred.h>
47906Sgm89044 #include <sys/kstat.h>
48906Sgm89044 #include <sys/strsun.h>
49906Sgm89044 #include <sys/note.h>
50906Sgm89044 #include <sys/crypto/common.h>
51906Sgm89044 #include <sys/crypto/spi.h>
52906Sgm89044 #include <sys/ddifm.h>
53906Sgm89044 #include <sys/fm/protocol.h>
54906Sgm89044 #include <sys/fm/util.h>
55906Sgm89044 #include <sys/fm/io/ddi.h>
56906Sgm89044 #include <sys/crypto/dca.h>
57906Sgm89044 
58906Sgm89044 /*
59906Sgm89044  * Core Deimos driver.
60906Sgm89044  */
61906Sgm89044 
62906Sgm89044 static void		dca_enlist2(dca_listnode_t *, dca_listnode_t *,
63906Sgm89044     kmutex_t *);
64906Sgm89044 static void		dca_rmlist2(dca_listnode_t *node, kmutex_t *);
65906Sgm89044 static dca_listnode_t	*dca_delist2(dca_listnode_t *q, kmutex_t *);
66906Sgm89044 static void		dca_free_context_list(dca_t *dca);
67906Sgm89044 static int		dca_free_context_low(crypto_ctx_t *ctx);
68906Sgm89044 static int		dca_attach(dev_info_t *, ddi_attach_cmd_t);
69906Sgm89044 static int		dca_detach(dev_info_t *, ddi_detach_cmd_t);
70906Sgm89044 static int		dca_suspend(dca_t *);
71906Sgm89044 static int		dca_resume(dca_t *);
72906Sgm89044 static int		dca_init(dca_t *);
73906Sgm89044 static int		dca_reset(dca_t *, int);
74906Sgm89044 static int		dca_initworklist(dca_t *, dca_worklist_t *);
75906Sgm89044 static void		dca_uninit(dca_t *);
76906Sgm89044 static void		dca_initq(dca_listnode_t *);
77906Sgm89044 static void		dca_enqueue(dca_listnode_t *, dca_listnode_t *);
78906Sgm89044 static dca_listnode_t	*dca_dequeue(dca_listnode_t *);
79906Sgm89044 static dca_listnode_t	*dca_unqueue(dca_listnode_t *);
80906Sgm89044 static dca_request_t	*dca_newreq(dca_t *);
81906Sgm89044 static dca_work_t	*dca_getwork(dca_t *, int);
82906Sgm89044 static void		dca_freework(dca_work_t *);
83906Sgm89044 static dca_work_t	*dca_newwork(dca_t *);
84906Sgm89044 static void		dca_destroywork(dca_work_t *);
85906Sgm89044 static void		dca_schedule(dca_t *, int);
86906Sgm89044 static void		dca_reclaim(dca_t *, int);
87906Sgm89044 static uint_t		dca_intr(char *);
88906Sgm89044 static void		dca_failure(dca_t *, ddi_fault_location_t,
89906Sgm89044 			    dca_fma_eclass_t index, uint64_t, int, char *, ...);
90906Sgm89044 static void		dca_jobtimeout(void *);
91906Sgm89044 static int		dca_drain(dca_t *);
92906Sgm89044 static void		dca_undrain(dca_t *);
93906Sgm89044 static void		dca_rejectjobs(dca_t *);
94906Sgm89044 
95906Sgm89044 #ifdef	SCHEDDELAY
96906Sgm89044 static void		dca_schedtimeout(void *);
97906Sgm89044 #endif
98906Sgm89044 
99906Sgm89044 /*
100906Sgm89044  * We want these inlined for performance.
101906Sgm89044  */
102906Sgm89044 #ifndef	DEBUG
103906Sgm89044 #pragma inline(dca_freereq, dca_getreq, dca_freework, dca_getwork)
104906Sgm89044 #pragma inline(dca_enqueue, dca_dequeue, dca_rmqueue, dca_done)
105906Sgm89044 #pragma inline(dca_reverse, dca_length)
106906Sgm89044 #endif
107906Sgm89044 
108906Sgm89044 /*
109906Sgm89044  * Device operations.
110906Sgm89044  */
111906Sgm89044 static struct dev_ops devops = {
112906Sgm89044 	DEVO_REV,		/* devo_rev */
113906Sgm89044 	0,			/* devo_refcnt */
114906Sgm89044 	nodev,			/* devo_getinfo */
115906Sgm89044 	nulldev,		/* devo_identify */
116906Sgm89044 	nulldev,		/* devo_probe */
117906Sgm89044 	dca_attach,		/* devo_attach */
118906Sgm89044 	dca_detach,		/* devo_detach */
119906Sgm89044 	nodev,			/* devo_reset */
120906Sgm89044 	NULL,			/* devo_cb_ops */
121906Sgm89044 	NULL,			/* devo_bus_ops */
122906Sgm89044 	ddi_power		/* devo_power */
123906Sgm89044 };
124906Sgm89044 
125906Sgm89044 #define	IDENT		"PCI Crypto Accelerator 2.0"
126906Sgm89044 #define	IDENT_SYM	"Crypto Accel Sym 2.0"
127906Sgm89044 #define	IDENT_ASYM	"Crypto Accel Asym 2.0"
128906Sgm89044 
129906Sgm89044 /* Space-padded, will be filled in dynamically during registration */
130906Sgm89044 #define	IDENT3	"PCI Crypto Accelerator Mod 2.0"
131906Sgm89044 
132906Sgm89044 #define	VENDOR	"Sun Microsystems, Inc."
133906Sgm89044 
134906Sgm89044 #define	STALETIME	(30 * SECOND)
135906Sgm89044 
136906Sgm89044 #define	crypto_prov_notify	crypto_provider_notification
137906Sgm89044 		/* A 28 char function name doesn't leave much line space */
138906Sgm89044 
139906Sgm89044 /*
140906Sgm89044  * Module linkage.
141906Sgm89044  */
142906Sgm89044 static struct modldrv modldrv = {
143906Sgm89044 	&mod_driverops,		/* drv_modops */
144906Sgm89044 	IDENT,			/* drv_linkinfo */
145906Sgm89044 	&devops,		/* drv_dev_ops */
146906Sgm89044 };
147906Sgm89044 
148906Sgm89044 extern struct mod_ops mod_cryptoops;
149906Sgm89044 
150906Sgm89044 static struct modlcrypto modlcrypto = {
151906Sgm89044 	&mod_cryptoops,
152906Sgm89044 	IDENT3
153906Sgm89044 };
154906Sgm89044 
155906Sgm89044 static struct modlinkage modlinkage = {
156906Sgm89044 	MODREV_1,		/* ml_rev */
157906Sgm89044 	&modldrv,		/* ml_linkage */
158906Sgm89044 	&modlcrypto,
159906Sgm89044 	NULL
160906Sgm89044 };
161906Sgm89044 
162906Sgm89044 /*
163906Sgm89044  * CSPI information (entry points, provider info, etc.)
164906Sgm89044  */
165906Sgm89044 
166906Sgm89044 /* Mechanisms for the symmetric cipher provider */
167906Sgm89044 static crypto_mech_info_t dca_mech_info_tab1[] = {
168906Sgm89044 	/* DES-CBC */
169906Sgm89044 	{SUN_CKM_DES_CBC, DES_CBC_MECH_INFO_TYPE,
170906Sgm89044 	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT |
171906Sgm89044 	    CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC,
172906Sgm89044 	    DES_KEY_LEN, DES_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES},
173906Sgm89044 	/* 3DES-CBC */
174906Sgm89044 	{SUN_CKM_DES3_CBC, DES3_CBC_MECH_INFO_TYPE,
175906Sgm89044 	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT |
176906Sgm89044 	    CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC,
177906Sgm89044 	    DES3_KEY_LEN, DES3_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BYTES}
178906Sgm89044 };
179906Sgm89044 
180906Sgm89044 /* Mechanisms for the asymmetric cipher provider */
181906Sgm89044 static crypto_mech_info_t dca_mech_info_tab2[] = {
182906Sgm89044 	/* DSA */
183906Sgm89044 	{SUN_CKM_DSA, DSA_MECH_INFO_TYPE,
184906Sgm89044 	    CRYPTO_FG_SIGN | CRYPTO_FG_VERIFY |
185906Sgm89044 	    CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_VERIFY_ATOMIC,
186906Sgm89044 	    DSA_MIN_KEY_LEN * 8, DSA_MAX_KEY_LEN * 8,
187906Sgm89044 	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
188906Sgm89044 
189906Sgm89044 	/* RSA */
190906Sgm89044 	{SUN_CKM_RSA_X_509, RSA_X_509_MECH_INFO_TYPE,
191906Sgm89044 	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_SIGN |
192906Sgm89044 	    CRYPTO_FG_SIGN_RECOVER | CRYPTO_FG_VERIFY |
193906Sgm89044 	    CRYPTO_FG_VERIFY_RECOVER |
194906Sgm89044 	    CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC |
195906Sgm89044 	    CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_SIGN_RECOVER_ATOMIC |
196906Sgm89044 	    CRYPTO_FG_VERIFY_ATOMIC | CRYPTO_FG_VERIFY_RECOVER_ATOMIC,
197906Sgm89044 	    RSA_MIN_KEY_LEN * 8, RSA_MAX_KEY_LEN * 8,
198906Sgm89044 	    CRYPTO_KEYSIZE_UNIT_IN_BITS},
199906Sgm89044 	{SUN_CKM_RSA_PKCS, RSA_PKCS_MECH_INFO_TYPE,
200906Sgm89044 	    CRYPTO_FG_ENCRYPT | CRYPTO_FG_DECRYPT | CRYPTO_FG_SIGN |
201906Sgm89044 	    CRYPTO_FG_SIGN_RECOVER | CRYPTO_FG_VERIFY |
202906Sgm89044 	    CRYPTO_FG_VERIFY_RECOVER |
203906Sgm89044 	    CRYPTO_FG_ENCRYPT_ATOMIC | CRYPTO_FG_DECRYPT_ATOMIC |
204906Sgm89044 	    CRYPTO_FG_SIGN_ATOMIC | CRYPTO_FG_SIGN_RECOVER_ATOMIC |
205906Sgm89044 	    CRYPTO_FG_VERIFY_ATOMIC | CRYPTO_FG_VERIFY_RECOVER_ATOMIC,
206906Sgm89044 	    RSA_MIN_KEY_LEN * 8, RSA_MAX_KEY_LEN * 8,
207906Sgm89044 	    CRYPTO_KEYSIZE_UNIT_IN_BITS}
208906Sgm89044 };
209906Sgm89044 
210906Sgm89044 static void dca_provider_status(crypto_provider_handle_t, uint_t *);
211906Sgm89044 
212906Sgm89044 static crypto_control_ops_t dca_control_ops = {
213906Sgm89044 	dca_provider_status
214906Sgm89044 };
215906Sgm89044 
216906Sgm89044 static int dca_encrypt_init(crypto_ctx_t *, crypto_mechanism_t *,
217906Sgm89044     crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
218906Sgm89044 static int dca_encrypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
219906Sgm89044     crypto_req_handle_t);
220906Sgm89044 static int dca_encrypt_update(crypto_ctx_t *, crypto_data_t *,
221906Sgm89044     crypto_data_t *, crypto_req_handle_t);
222906Sgm89044 static int dca_encrypt_final(crypto_ctx_t *, crypto_data_t *,
223906Sgm89044     crypto_req_handle_t);
224906Sgm89044 static int dca_encrypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
225906Sgm89044     crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
226906Sgm89044     crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
227906Sgm89044 
228906Sgm89044 static int dca_decrypt_init(crypto_ctx_t *, crypto_mechanism_t *,
229906Sgm89044     crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
230906Sgm89044 static int dca_decrypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
231906Sgm89044     crypto_req_handle_t);
232906Sgm89044 static int dca_decrypt_update(crypto_ctx_t *, crypto_data_t *,
233906Sgm89044     crypto_data_t *, crypto_req_handle_t);
234906Sgm89044 static int dca_decrypt_final(crypto_ctx_t *, crypto_data_t *,
235906Sgm89044     crypto_req_handle_t);
236906Sgm89044 static int dca_decrypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
237906Sgm89044     crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
238906Sgm89044     crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
239906Sgm89044 
240906Sgm89044 static crypto_cipher_ops_t dca_cipher_ops = {
241906Sgm89044 	dca_encrypt_init,
242906Sgm89044 	dca_encrypt,
243906Sgm89044 	dca_encrypt_update,
244906Sgm89044 	dca_encrypt_final,
245906Sgm89044 	dca_encrypt_atomic,
246906Sgm89044 	dca_decrypt_init,
247906Sgm89044 	dca_decrypt,
248906Sgm89044 	dca_decrypt_update,
249906Sgm89044 	dca_decrypt_final,
250906Sgm89044 	dca_decrypt_atomic
251906Sgm89044 };
252906Sgm89044 
253906Sgm89044 static int dca_sign_init(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *,
254906Sgm89044     crypto_spi_ctx_template_t, crypto_req_handle_t);
255906Sgm89044 static int dca_sign(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
256906Sgm89044     crypto_req_handle_t);
257906Sgm89044 static int dca_sign_update(crypto_ctx_t *, crypto_data_t *,
258906Sgm89044     crypto_req_handle_t);
259906Sgm89044 static int dca_sign_final(crypto_ctx_t *, crypto_data_t *,
260906Sgm89044     crypto_req_handle_t);
261906Sgm89044 static int dca_sign_atomic(crypto_provider_handle_t, crypto_session_id_t,
262906Sgm89044     crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
263906Sgm89044     crypto_spi_ctx_template_t, crypto_req_handle_t);
264906Sgm89044 static int dca_sign_recover_init(crypto_ctx_t *, crypto_mechanism_t *,
265906Sgm89044     crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
266906Sgm89044 static int dca_sign_recover(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
267906Sgm89044     crypto_req_handle_t);
268906Sgm89044 static int dca_sign_recover_atomic(crypto_provider_handle_t,
269906Sgm89044     crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
270906Sgm89044     crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
271906Sgm89044 
272906Sgm89044 static crypto_sign_ops_t dca_sign_ops = {
273906Sgm89044 	dca_sign_init,
274906Sgm89044 	dca_sign,
275906Sgm89044 	dca_sign_update,
276906Sgm89044 	dca_sign_final,
277906Sgm89044 	dca_sign_atomic,
278906Sgm89044 	dca_sign_recover_init,
279906Sgm89044 	dca_sign_recover,
280906Sgm89044 	dca_sign_recover_atomic
281906Sgm89044 };
282906Sgm89044 
283906Sgm89044 static int dca_verify_init(crypto_ctx_t *, crypto_mechanism_t *,
284906Sgm89044     crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
285906Sgm89044 static int dca_verify(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
286906Sgm89044     crypto_req_handle_t);
287906Sgm89044 static int dca_verify_update(crypto_ctx_t *, crypto_data_t *,
288906Sgm89044     crypto_req_handle_t);
289906Sgm89044 static int dca_verify_final(crypto_ctx_t *, crypto_data_t *,
290906Sgm89044     crypto_req_handle_t);
291906Sgm89044 static int dca_verify_atomic(crypto_provider_handle_t, crypto_session_id_t,
292906Sgm89044     crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
293906Sgm89044     crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
294906Sgm89044 static int dca_verify_recover_init(crypto_ctx_t *, crypto_mechanism_t *,
295906Sgm89044     crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
296906Sgm89044 static int dca_verify_recover(crypto_ctx_t *, crypto_data_t *,
297906Sgm89044     crypto_data_t *, crypto_req_handle_t);
298906Sgm89044 static int dca_verify_recover_atomic(crypto_provider_handle_t,
299906Sgm89044     crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
300906Sgm89044     crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
301906Sgm89044 
302906Sgm89044 static crypto_verify_ops_t dca_verify_ops = {
303906Sgm89044 	dca_verify_init,
304906Sgm89044 	dca_verify,
305906Sgm89044 	dca_verify_update,
306906Sgm89044 	dca_verify_final,
307906Sgm89044 	dca_verify_atomic,
308906Sgm89044 	dca_verify_recover_init,
309906Sgm89044 	dca_verify_recover,
310906Sgm89044 	dca_verify_recover_atomic
311906Sgm89044 };
312906Sgm89044 
313906Sgm89044 static int dca_generate_random(crypto_provider_handle_t, crypto_session_id_t,
314906Sgm89044     uchar_t *, size_t, crypto_req_handle_t);
315906Sgm89044 
316906Sgm89044 static crypto_random_number_ops_t dca_random_number_ops = {
317906Sgm89044 	NULL,
318906Sgm89044 	dca_generate_random
319906Sgm89044 };
320906Sgm89044 
321906Sgm89044 static int ext_info_sym(crypto_provider_handle_t prov,
322906Sgm89044     crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq);
323906Sgm89044 static int ext_info_asym(crypto_provider_handle_t prov,
324906Sgm89044     crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq);
325906Sgm89044 static int ext_info_base(crypto_provider_handle_t prov,
326906Sgm89044     crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq, char *id);
327906Sgm89044 
328906Sgm89044 static crypto_provider_management_ops_t dca_provmanage_ops_1 = {
329906Sgm89044 	ext_info_sym,		/* ext_info */
330906Sgm89044 	NULL,			/* init_token */
331906Sgm89044 	NULL,			/* init_pin */
332906Sgm89044 	NULL			/* set_pin */
333906Sgm89044 };
334906Sgm89044 
335906Sgm89044 static crypto_provider_management_ops_t dca_provmanage_ops_2 = {
336906Sgm89044 	ext_info_asym,		/* ext_info */
337906Sgm89044 	NULL,			/* init_token */
338906Sgm89044 	NULL,			/* init_pin */
339906Sgm89044 	NULL			/* set_pin */
340906Sgm89044 };
341906Sgm89044 
342906Sgm89044 int dca_free_context(crypto_ctx_t *);
343906Sgm89044 
344906Sgm89044 static crypto_ctx_ops_t dca_ctx_ops = {
345906Sgm89044 	NULL,
346906Sgm89044 	dca_free_context
347906Sgm89044 };
348906Sgm89044 
349906Sgm89044 /* Operations for the symmetric cipher provider */
350906Sgm89044 static crypto_ops_t dca_crypto_ops1 = {
351906Sgm89044 	&dca_control_ops,
352906Sgm89044 	NULL,				/* digest_ops */
353906Sgm89044 	&dca_cipher_ops,
354906Sgm89044 	NULL,				/* mac_ops */
355906Sgm89044 	NULL,				/* sign_ops */
356906Sgm89044 	NULL,				/* verify_ops */
357906Sgm89044 	NULL,				/* dual_ops */
358906Sgm89044 	NULL,				/* cipher_mac_ops */
359906Sgm89044 	NULL,				/* random_number_ops */
360906Sgm89044 	NULL,				/* session_ops */
361906Sgm89044 	NULL,				/* object_ops */
362906Sgm89044 	NULL,				/* key_ops */
363906Sgm89044 	&dca_provmanage_ops_1,		/* management_ops */
364906Sgm89044 	&dca_ctx_ops
365906Sgm89044 };
366906Sgm89044 
367906Sgm89044 /* Operations for the asymmetric cipher provider */
368906Sgm89044 static crypto_ops_t dca_crypto_ops2 = {
369906Sgm89044 	&dca_control_ops,
370906Sgm89044 	NULL,				/* digest_ops */
371906Sgm89044 	&dca_cipher_ops,
372906Sgm89044 	NULL,				/* mac_ops */
373906Sgm89044 	&dca_sign_ops,
374906Sgm89044 	&dca_verify_ops,
375906Sgm89044 	NULL,				/* dual_ops */
376906Sgm89044 	NULL,				/* cipher_mac_ops */
377906Sgm89044 	&dca_random_number_ops,
378906Sgm89044 	NULL,				/* session_ops */
379906Sgm89044 	NULL,				/* object_ops */
380906Sgm89044 	NULL,				/* key_ops */
381906Sgm89044 	&dca_provmanage_ops_2,		/* management_ops */
382906Sgm89044 	&dca_ctx_ops
383906Sgm89044 };
384906Sgm89044 
385906Sgm89044 /* Provider information for the symmetric cipher provider */
386906Sgm89044 static crypto_provider_info_t dca_prov_info1 = {
387906Sgm89044 	CRYPTO_SPI_VERSION_1,
388906Sgm89044 	NULL,				/* pi_provider_description */
389906Sgm89044 	CRYPTO_HW_PROVIDER,
390906Sgm89044 	NULL,				/* pi_provider_dev */
391906Sgm89044 	NULL,				/* pi_provider_handle */
392906Sgm89044 	&dca_crypto_ops1,
393906Sgm89044 	sizeof (dca_mech_info_tab1)/sizeof (crypto_mech_info_t),
394906Sgm89044 	dca_mech_info_tab1,
395906Sgm89044 	0,				/* pi_logical_provider_count */
396906Sgm89044 	NULL				/* pi_logical_providers */
397906Sgm89044 };
398906Sgm89044 
399906Sgm89044 /* Provider information for the asymmetric cipher provider */
400906Sgm89044 static crypto_provider_info_t dca_prov_info2 = {
401906Sgm89044 	CRYPTO_SPI_VERSION_1,
402906Sgm89044 	NULL,				/* pi_provider_description */
403906Sgm89044 	CRYPTO_HW_PROVIDER,
404906Sgm89044 	NULL,				/* pi_provider_dev */
405906Sgm89044 	NULL,				/* pi_provider_handle */
406906Sgm89044 	&dca_crypto_ops2,
407906Sgm89044 	sizeof (dca_mech_info_tab2)/sizeof (crypto_mech_info_t),
408906Sgm89044 	dca_mech_info_tab2,
409906Sgm89044 	0,				/* pi_logical_provider_count */
410906Sgm89044 	NULL				/* pi_logical_providers */
411906Sgm89044 };
412906Sgm89044 
413906Sgm89044 /* Convenience macros */
414906Sgm89044 /* Retrieve the softc and instance number from a SPI crypto context */
415906Sgm89044 #define	DCA_SOFTC_FROM_CTX(ctx, softc, instance) {		\
416906Sgm89044 	(softc) = (dca_t *)(ctx)->cc_provider;			\
417906Sgm89044 	(instance) = ddi_get_instance((softc)->dca_dip);	\
418906Sgm89044 }
419906Sgm89044 
420906Sgm89044 #define	DCA_MECH_FROM_CTX(ctx) \
421906Sgm89044 	(((dca_request_t *)(ctx)->cc_provider_private)->dr_ctx.ctx_cm_type)
422906Sgm89044 
423906Sgm89044 static int dca_bindchains_one(dca_request_t *reqp, size_t cnt, int dr_offset,
424906Sgm89044     caddr_t kaddr, ddi_dma_handle_t handle, uint_t flags,
425906Sgm89044     dca_chain_t *head, int *n_chain);
426906Sgm89044 static uint64_t dca_ena(uint64_t ena);
427906Sgm89044 static caddr_t dca_bufdaddr_out(crypto_data_t *data);
428906Sgm89044 static char *dca_fma_eclass_string(char *model, dca_fma_eclass_t index);
429906Sgm89044 static int dca_check_acc_handle(dca_t *dca, ddi_acc_handle_t handle,
430906Sgm89044     dca_fma_eclass_t eclass_index);
431906Sgm89044 
432906Sgm89044 static void dca_fma_init(dca_t *dca);
433906Sgm89044 static void dca_fma_fini(dca_t *dca);
434906Sgm89044 static int dca_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err,
435906Sgm89044     const void *impl_data);
436906Sgm89044 
437906Sgm89044 
438906Sgm89044 static dca_device_t dca_devices[] = {
439906Sgm89044 	/* Broadcom vanilla variants */
440906Sgm89044 	{	0x14e4, 0x5820, "Broadcom 5820" },
441906Sgm89044 	{	0x14e4, 0x5821, "Broadcom 5821" },
442906Sgm89044 	{	0x14e4, 0x5822, "Broadcom 5822" },
443906Sgm89044 	{	0x14e4, 0x5825, "Broadcom 5825" },
444906Sgm89044 	/* Sun specific OEMd variants */
445906Sgm89044 	{	0x108e, 0x5454, "SCA" },
446906Sgm89044 	{	0x108e, 0x5455, "SCA 1000" },
447906Sgm89044 	{	0x108e, 0x5457, "SCA 500" },
448906Sgm89044 	/* subsysid should be 0x5457, but got 0x1 from HW. Assume both here. */
449906Sgm89044 	{	0x108e, 0x1, "SCA 500" },
450906Sgm89044 };
451906Sgm89044 
452906Sgm89044 /*
453906Sgm89044  * Device attributes.
454906Sgm89044  */
455906Sgm89044 static struct ddi_device_acc_attr dca_regsattr = {
456906Sgm89044 	DDI_DEVICE_ATTR_V0,
457906Sgm89044 	DDI_STRUCTURE_LE_ACC,
458906Sgm89044 	DDI_STRICTORDER_ACC,
459906Sgm89044 	DDI_FLAGERR_ACC
460906Sgm89044 };
461906Sgm89044 
462906Sgm89044 static struct ddi_device_acc_attr dca_devattr = {
463906Sgm89044 	DDI_DEVICE_ATTR_V0,
464906Sgm89044 	DDI_STRUCTURE_LE_ACC,
465906Sgm89044 	DDI_STRICTORDER_ACC,
466906Sgm89044 	DDI_FLAGERR_ACC
467906Sgm89044 };
468906Sgm89044 
469906Sgm89044 #if !defined(i386) && !defined(__i386)
470906Sgm89044 static struct ddi_device_acc_attr dca_bufattr = {
471906Sgm89044 	DDI_DEVICE_ATTR_V0,
472906Sgm89044 	DDI_NEVERSWAP_ACC,
473906Sgm89044 	DDI_STRICTORDER_ACC,
474906Sgm89044 	DDI_FLAGERR_ACC
475906Sgm89044 };
476906Sgm89044 #endif
477906Sgm89044 
478906Sgm89044 static struct ddi_dma_attr dca_dmaattr = {
479906Sgm89044 	DMA_ATTR_V0,		/* dma_attr_version */
480906Sgm89044 	0x0,			/* dma_attr_addr_lo */
481906Sgm89044 	0xffffffffUL,		/* dma_attr_addr_hi */
482906Sgm89044 	0x00ffffffUL,		/* dma_attr_count_max */
483906Sgm89044 	0x40,			/* dma_attr_align */
484906Sgm89044 	0x40,			/* dma_attr_burstsizes */
485906Sgm89044 	0x1,			/* dma_attr_minxfer */
486906Sgm89044 	0x00ffffffUL,		/* dma_attr_maxxfer */
487906Sgm89044 	0xffffffffUL,		/* dma_attr_seg */
488906Sgm89044 #if defined(i386) || defined(__i386) || defined(__amd64)
489906Sgm89044 	512,			/* dma_attr_sgllen */
490906Sgm89044 #else
491906Sgm89044 	1,			/* dma_attr_sgllen */
492906Sgm89044 #endif
493906Sgm89044 	1,			/* dma_attr_granular */
494906Sgm89044 	DDI_DMA_FLAGERR		/* dma_attr_flags */
495906Sgm89044 };
496906Sgm89044 
497906Sgm89044 static void	*dca_state = NULL;
498906Sgm89044 int	dca_mindma = 2500;
499906Sgm89044 
500906Sgm89044 /*
501906Sgm89044  * FMA eclass string definitions. Note that these string arrays must be
502906Sgm89044  * consistent with the dca_fma_eclass_t enum.
503906Sgm89044  */
504906Sgm89044 static char *dca_fma_eclass_sca1000[] = {
505906Sgm89044 	"sca1000.hw.device",
506906Sgm89044 	"sca1000.hw.timeout",
507906Sgm89044 	"sca1000.none"
508906Sgm89044 };
509906Sgm89044 
510906Sgm89044 static char *dca_fma_eclass_sca500[] = {
511906Sgm89044 	"sca500.hw.device",
512906Sgm89044 	"sca500.hw.timeout",
513906Sgm89044 	"sca500.none"
514906Sgm89044 };
515906Sgm89044 
516906Sgm89044 /*
517906Sgm89044  * DDI entry points.
518906Sgm89044  */
519906Sgm89044 int
520906Sgm89044 _init(void)
521906Sgm89044 {
522906Sgm89044 	int rv;
523906Sgm89044 
524906Sgm89044 	DBG(NULL, DMOD, "dca: in _init");
525906Sgm89044 
526906Sgm89044 	if ((rv = ddi_soft_state_init(&dca_state, sizeof (dca_t), 1)) != 0) {
527906Sgm89044 		/* this should *never* happen! */
528906Sgm89044 		return (rv);
529906Sgm89044 	}
530906Sgm89044 
531906Sgm89044 	if ((rv = mod_install(&modlinkage)) != 0) {
532906Sgm89044 		/* cleanup here */
533906Sgm89044 		ddi_soft_state_fini(&dca_state);
534906Sgm89044 		return (rv);
535906Sgm89044 	}
536906Sgm89044 
537906Sgm89044 	return (0);
538906Sgm89044 }
539906Sgm89044 
540906Sgm89044 int
541906Sgm89044 _fini(void)
542906Sgm89044 {
543906Sgm89044 	int rv;
544906Sgm89044 
545906Sgm89044 	DBG(NULL, DMOD, "dca: in _fini");
546906Sgm89044 
547906Sgm89044 	if ((rv = mod_remove(&modlinkage)) == 0) {
548906Sgm89044 		/* cleanup here */
549906Sgm89044 		ddi_soft_state_fini(&dca_state);
550906Sgm89044 	}
551906Sgm89044 	return (rv);
552906Sgm89044 }
553906Sgm89044 
554906Sgm89044 int
555906Sgm89044 _info(struct modinfo *modinfop)
556906Sgm89044 {
557906Sgm89044 	DBG(NULL, DMOD, "dca: in _info");
558906Sgm89044 
559906Sgm89044 	return (mod_info(&modlinkage, modinfop));
560906Sgm89044 }
561906Sgm89044 
562906Sgm89044 int
563906Sgm89044 dca_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
564906Sgm89044 {
565906Sgm89044 	ddi_acc_handle_t	pci;
566906Sgm89044 	int			instance;
567906Sgm89044 	ddi_iblock_cookie_t	ibc;
568906Sgm89044 	int			intr_added = 0;
569906Sgm89044 	dca_t			*dca;
570906Sgm89044 	ushort_t		venid;
571906Sgm89044 	ushort_t		devid;
572906Sgm89044 	ushort_t		revid;
573906Sgm89044 	ushort_t		subsysid;
574906Sgm89044 	ushort_t		subvenid;
575906Sgm89044 	int			i;
576906Sgm89044 	int			ret;
577906Sgm89044 	char			ID[64];
578906Sgm89044 	static char		*unknowndev = "Unknown device";
579906Sgm89044 
580906Sgm89044 #if DEBUG
581906Sgm89044 	/* these are only used for debugging */
582906Sgm89044 	ushort_t		pcicomm;
583906Sgm89044 	ushort_t		pcistat;
584906Sgm89044 	uchar_t			cachelinesz;
585906Sgm89044 	uchar_t			mingnt;
586906Sgm89044 	uchar_t			maxlat;
587906Sgm89044 	uchar_t			lattmr;
588906Sgm89044 #endif
589906Sgm89044 
590906Sgm89044 	instance = ddi_get_instance(dip);
591906Sgm89044 
592906Sgm89044 	DBG(NULL, DMOD, "dca: in dca_attach() for %d", instance);
593906Sgm89044 
594906Sgm89044 	switch (cmd) {
595906Sgm89044 	case DDI_RESUME:
596906Sgm89044 		if ((dca = (dca_t *)ddi_get_driver_private(dip)) == NULL) {
597906Sgm89044 			dca_diperror(dip, "no soft state in detach");
598906Sgm89044 			return (DDI_FAILURE);
599906Sgm89044 		}
600906Sgm89044 		/* assumption: we won't be DDI_DETACHed until we return */
601906Sgm89044 		return (dca_resume(dca));
602906Sgm89044 	case DDI_ATTACH:
603906Sgm89044 		break;
604906Sgm89044 	default:
605906Sgm89044 		return (DDI_FAILURE);
606906Sgm89044 	}
607906Sgm89044 
608906Sgm89044 	if (ddi_slaveonly(dip) == DDI_SUCCESS) {
609906Sgm89044 		dca_diperror(dip, "slot does not support PCI bus-master");
610906Sgm89044 		return (DDI_FAILURE);
611906Sgm89044 	}
612906Sgm89044 
613906Sgm89044 	if (ddi_intr_hilevel(dip, 0) != 0) {
614906Sgm89044 		dca_diperror(dip, "hilevel interrupts not supported");
615906Sgm89044 		return (DDI_FAILURE);
616906Sgm89044 	}
617906Sgm89044 
618906Sgm89044 	if (pci_config_setup(dip, &pci) != DDI_SUCCESS) {
619906Sgm89044 		dca_diperror(dip, "unable to setup PCI config handle");
620906Sgm89044 		return (DDI_FAILURE);
621906Sgm89044 	}
622906Sgm89044 
623906Sgm89044 	/* common PCI attributes */
624906Sgm89044 	venid = pci_config_get16(pci, PCI_VENID);
625906Sgm89044 	devid = pci_config_get16(pci, PCI_DEVID);
626906Sgm89044 	revid = pci_config_get8(pci, PCI_REVID);
627906Sgm89044 	subvenid = pci_config_get16(pci, PCI_SUBVENID);
628906Sgm89044 	subsysid = pci_config_get16(pci, PCI_SUBSYSID);
629906Sgm89044 
630906Sgm89044 	/*
631906Sgm89044 	 * Broadcom-specific timings.
632906Sgm89044 	 * We disable these timers/counters since they can cause
633906Sgm89044 	 * incorrect false failures when the bus is just a little
634906Sgm89044 	 * bit slow, or busy.
635906Sgm89044 	 */
636906Sgm89044 	pci_config_put8(pci, PCI_TRDYTO, 0);
637906Sgm89044 	pci_config_put8(pci, PCI_RETRIES, 0);
638906Sgm89044 
639906Sgm89044 	/* initialize PCI access settings */
640906Sgm89044 	pci_config_put16(pci, PCI_COMM, PCICOMM_SEE |
641906Sgm89044 	    PCICOMM_PEE | PCICOMM_BME | PCICOMM_MAE);
642906Sgm89044 
643906Sgm89044 	/* set up our PCI latency timer */
644906Sgm89044 	pci_config_put8(pci, PCI_LATTMR, 0x40);
645906Sgm89044 
646906Sgm89044 #if DEBUG
647906Sgm89044 	/* read registers (for debugging) */
648906Sgm89044 	pcicomm = pci_config_get16(pci, PCI_COMM);
649906Sgm89044 	pcistat = pci_config_get16(pci, PCI_STATUS);
650906Sgm89044 	cachelinesz = pci_config_get8(pci, PCI_CACHELINESZ);
651906Sgm89044 	mingnt = pci_config_get8(pci, PCI_MINGNT);
652906Sgm89044 	maxlat = pci_config_get8(pci, PCI_MAXLAT);
653906Sgm89044 	lattmr = pci_config_get8(pci, PCI_LATTMR);
654906Sgm89044 #endif
655906Sgm89044 
656906Sgm89044 	pci_config_teardown(&pci);
657906Sgm89044 
658906Sgm89044 	if (ddi_get_iblock_cookie(dip, 0, &ibc) != DDI_SUCCESS) {
659906Sgm89044 		dca_diperror(dip, "unable to get iblock cookie");
660906Sgm89044 		return (DDI_FAILURE);
661906Sgm89044 	}
662906Sgm89044 
663906Sgm89044 	if (ddi_soft_state_zalloc(dca_state, instance) != DDI_SUCCESS) {
664906Sgm89044 		dca_diperror(dip, "unable to allocate soft state");
665906Sgm89044 		return (DDI_FAILURE);
666906Sgm89044 	}
667906Sgm89044 
668906Sgm89044 	dca = ddi_get_soft_state(dca_state, instance);
669906Sgm89044 	ASSERT(dca != NULL);
670906Sgm89044 	dca->dca_dip = dip;
671906Sgm89044 	WORKLIST(dca, MCR1)->dwl_prov = NULL;
672906Sgm89044 	WORKLIST(dca, MCR2)->dwl_prov = NULL;
673906Sgm89044 	/* figure pagesize */
674906Sgm89044 	dca->dca_pagesize = ddi_ptob(dip, 1);
675906Sgm89044 
676906Sgm89044 	/*
677906Sgm89044 	 * Search for the device in our supported devices table.  This
678906Sgm89044 	 * is here for two reasons.  First, we want to ensure that
679906Sgm89044 	 * only Sun-qualified (and presumably Sun-labeled) devices can
680906Sgm89044 	 * be used with this driver.  Second, some devices have
681906Sgm89044 	 * specific differences.  E.g. the 5821 has support for a
682906Sgm89044 	 * special mode of RC4, deeper queues, power management, and
683906Sgm89044 	 * other changes.  Also, the export versions of some of these
684906Sgm89044 	 * chips don't support RC4 or 3DES, so we catch that here.
685906Sgm89044 	 *
686906Sgm89044 	 * Note that we only look at the upper nibble of the device
687906Sgm89044 	 * id, which is used to distinguish export vs. domestic
688906Sgm89044 	 * versions of the chip.  (The lower nibble is used for
689906Sgm89044 	 * stepping information.)
690906Sgm89044 	 */
691906Sgm89044 	for (i = 0; i < (sizeof (dca_devices) / sizeof (dca_device_t)); i++) {
692906Sgm89044 		/*
693906Sgm89044 		 * Try to match the subsystem information first.
694906Sgm89044 		 */
695906Sgm89044 		if (subvenid && (subvenid == dca_devices[i].dd_vendor_id) &&
696906Sgm89044 		    subsysid && (subsysid == dca_devices[i].dd_device_id)) {
697906Sgm89044 			dca->dca_model = dca_devices[i].dd_model;
6983124Sqs148142 			dca->dca_devid = dca_devices[i].dd_device_id;
699906Sgm89044 			break;
700906Sgm89044 		}
701906Sgm89044 		/*
702906Sgm89044 		 * Failing that, try the generic vendor and device id.
703906Sgm89044 		 * Even if we find a match, we keep searching anyway,
704906Sgm89044 		 * since we would prefer to find a match based on the
705906Sgm89044 		 * subsystem ids.
706906Sgm89044 		 */
707906Sgm89044 		if ((venid == dca_devices[i].dd_vendor_id) &&
708906Sgm89044 		    (devid == dca_devices[i].dd_device_id)) {
709906Sgm89044 			dca->dca_model = dca_devices[i].dd_model;
7103124Sqs148142 			dca->dca_devid = dca_devices[i].dd_device_id;
711906Sgm89044 		}
712906Sgm89044 	}
713906Sgm89044 	/* try and handle an unrecognized device */
714906Sgm89044 	if (dca->dca_model == NULL) {
715906Sgm89044 		dca->dca_model = unknowndev;
716906Sgm89044 		dca_error(dca, "device not recognized, not supported");
717906Sgm89044 		DBG(dca, DPCI, "i=%d venid=%x devid=%x rev=%d",
718906Sgm89044 		    i, venid, devid, revid);
719906Sgm89044 	}
720906Sgm89044 
721906Sgm89044 	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "description",
722906Sgm89044 	    dca->dca_model) != DDI_SUCCESS) {
723906Sgm89044 		dca_error(dca, "unable to create description property");
724906Sgm89044 		return (DDI_FAILURE);
725906Sgm89044 	}
726906Sgm89044 
727906Sgm89044 	DBG(dca, DPCI, "PCI command=0x%x status=%x cachelinesz=%x",
728906Sgm89044 	    pcicomm, pcistat, cachelinesz);
729906Sgm89044 	DBG(dca, DPCI, "mingnt=0x%x maxlat=0x%x lattmr=0x%x",
730906Sgm89044 	    mingnt, maxlat, lattmr);
731906Sgm89044 
732906Sgm89044 	/*
733906Sgm89044 	 * initialize locks, etc.
734906Sgm89044 	 */
735906Sgm89044 	(void) mutex_init(&dca->dca_intrlock, NULL, MUTEX_DRIVER, ibc);
736906Sgm89044 
737906Sgm89044 	/* use RNGSHA1 by default */
738906Sgm89044 	if (ddi_getprop(DDI_DEV_T_ANY, dip,
739906Sgm89044 	    DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "rngdirect", 0) == 0) {
740906Sgm89044 		dca->dca_flags |= DCA_RNGSHA1;
741906Sgm89044 	}
742906Sgm89044 
743906Sgm89044 	/* initialize FMA */
744906Sgm89044 	dca_fma_init(dca);
745906Sgm89044 
746906Sgm89044 	/* initialize some key data structures */
747906Sgm89044 	if (dca_init(dca) != DDI_SUCCESS) {
748906Sgm89044 		goto failed;
749906Sgm89044 	}
750906Sgm89044 
751906Sgm89044 	/* initialize kstats */
752906Sgm89044 	dca_ksinit(dca);
753906Sgm89044 
754906Sgm89044 	/* setup access to registers */
755906Sgm89044 	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&dca->dca_regs,
756906Sgm89044 	    0, 0, &dca_regsattr, &dca->dca_regs_handle) != DDI_SUCCESS) {
757906Sgm89044 		dca_error(dca, "unable to map registers");
758906Sgm89044 		goto failed;
759906Sgm89044 	}
760906Sgm89044 
761906Sgm89044 	DBG(dca, DCHATTY, "MCR1 = %x", GETCSR(dca, CSR_MCR1));
762906Sgm89044 	DBG(dca, DCHATTY, "CONTROL = %x", GETCSR(dca, CSR_DMACTL));
763906Sgm89044 	DBG(dca, DCHATTY, "STATUS = %x", GETCSR(dca, CSR_DMASTAT));
764906Sgm89044 	DBG(dca, DCHATTY, "DMAEA = %x", GETCSR(dca, CSR_DMAEA));
765906Sgm89044 	DBG(dca, DCHATTY, "MCR2 = %x", GETCSR(dca, CSR_MCR2));
766906Sgm89044 
767906Sgm89044 	/* reset the chip */
768906Sgm89044 	if (dca_reset(dca, 0) < 0) {
769906Sgm89044 		goto failed;
770906Sgm89044 	}
771906Sgm89044 
772906Sgm89044 	/* initialize the chip */
773906Sgm89044 	PUTCSR(dca, CSR_DMACTL, DMACTL_BE32 | DMACTL_BE64);
774906Sgm89044 	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
775906Sgm89044 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
776906Sgm89044 		goto failed;
777906Sgm89044 	}
778906Sgm89044 
779906Sgm89044 	/* add the interrupt */
780906Sgm89044 	if (ddi_add_intr(dip, 0, &dca->dca_icookie, NULL, dca_intr,
781906Sgm89044 	    (void *)dca) != DDI_SUCCESS) {
782906Sgm89044 		DBG(dca, DWARN, "ddi_add_intr failed");
783906Sgm89044 		goto failed;
784906Sgm89044 	} else {
785906Sgm89044 		intr_added = 1;
786906Sgm89044 	}
787906Sgm89044 
788906Sgm89044 	/* enable interrupts on the device */
789906Sgm89044 	/*
790906Sgm89044 	 * XXX: Note, 5820A1 errata indicates that this may clobber
791906Sgm89044 	 * bits 24 and 23, which affect the speed of the RNG.  Since
792906Sgm89044 	 * we always want to run in full-speed mode, this should be
793906Sgm89044 	 * harmless.
794906Sgm89044 	 */
7953124Sqs148142 	if (dca->dca_devid == 0x5825) {
7963124Sqs148142 		/* for 5825 - increase the DMA read size */
7973124Sqs148142 		SETBIT(dca, CSR_DMACTL,
7983124Sqs148142 		    DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE | DMACTL_RD256);
7993124Sqs148142 	} else {
8003124Sqs148142 		SETBIT(dca, CSR_DMACTL,
8013124Sqs148142 		    DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE);
8023124Sqs148142 	}
803906Sgm89044 	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
804906Sgm89044 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
805906Sgm89044 		goto failed;
806906Sgm89044 	}
807906Sgm89044 
808906Sgm89044 	/* register MCR1 with the crypto framework */
809906Sgm89044 	/* Be careful not to exceed 32 chars */
810906Sgm89044 	(void) sprintf(ID, "%s/%d %s",
811906Sgm89044 	    ddi_driver_name(dip), ddi_get_instance(dip), IDENT_SYM);
812906Sgm89044 	dca_prov_info1.pi_provider_description = ID;
813906Sgm89044 	dca_prov_info1.pi_provider_dev.pd_hw = dip;
814906Sgm89044 	dca_prov_info1.pi_provider_handle = dca;
815906Sgm89044 	if ((ret = crypto_register_provider(&dca_prov_info1,
816906Sgm89044 	    &WORKLIST(dca, MCR1)->dwl_prov)) != CRYPTO_SUCCESS) {
817906Sgm89044 		cmn_err(CE_WARN,
818906Sgm89044 		    "crypto_register_provider() failed (%d) for MCR1", ret);
819906Sgm89044 		goto failed;
820906Sgm89044 	}
821906Sgm89044 
822906Sgm89044 	/* register MCR2 with the crypto framework */
823906Sgm89044 	/* Be careful not to exceed 32 chars */
824906Sgm89044 	(void) sprintf(ID, "%s/%d %s",
825906Sgm89044 	    ddi_driver_name(dip), ddi_get_instance(dip), IDENT_ASYM);
826906Sgm89044 	dca_prov_info2.pi_provider_description = ID;
827906Sgm89044 	dca_prov_info2.pi_provider_dev.pd_hw = dip;
828906Sgm89044 	dca_prov_info2.pi_provider_handle = dca;
829906Sgm89044 	if ((ret = crypto_register_provider(&dca_prov_info2,
830906Sgm89044 	    &WORKLIST(dca, MCR2)->dwl_prov)) != CRYPTO_SUCCESS) {
831906Sgm89044 		cmn_err(CE_WARN,
832906Sgm89044 		    "crypto_register_provider() failed (%d) for MCR2", ret);
833906Sgm89044 		goto failed;
834906Sgm89044 	}
835906Sgm89044 
836906Sgm89044 	crypto_prov_notify(WORKLIST(dca, MCR1)->dwl_prov,
837*5063Sgm89044 	    CRYPTO_PROVIDER_READY);
838906Sgm89044 	crypto_prov_notify(WORKLIST(dca, MCR2)->dwl_prov,
839*5063Sgm89044 	    CRYPTO_PROVIDER_READY);
840906Sgm89044 
841906Sgm89044 	/* Initialize the local random number pool for this instance */
842906Sgm89044 	if ((ret = dca_random_init(dca)) != CRYPTO_SUCCESS) {
843906Sgm89044 		goto failed;
844906Sgm89044 	}
845906Sgm89044 
846906Sgm89044 	mutex_enter(&dca->dca_intrlock);
847906Sgm89044 	dca->dca_jobtid = timeout(dca_jobtimeout, (void *)dca,
848906Sgm89044 	    drv_usectohz(SECOND));
849906Sgm89044 	mutex_exit(&dca->dca_intrlock);
850906Sgm89044 
851906Sgm89044 	ddi_set_driver_private(dip, (caddr_t)dca);
852906Sgm89044 
853906Sgm89044 	ddi_report_dev(dip);
854906Sgm89044 
855906Sgm89044 	if (ddi_get_devstate(dca->dca_dip) != DDI_DEVSTATE_UP) {
856906Sgm89044 		ddi_fm_service_impact(dca->dca_dip, DDI_SERVICE_RESTORED);
857906Sgm89044 	}
858906Sgm89044 
859906Sgm89044 	return (DDI_SUCCESS);
860906Sgm89044 
861906Sgm89044 failed:
862906Sgm89044 	/* unregister from the crypto framework */
863906Sgm89044 	if (WORKLIST(dca, MCR1)->dwl_prov != NULL) {
864*5063Sgm89044 		(void) crypto_unregister_provider(
865*5063Sgm89044 		    WORKLIST(dca, MCR1)->dwl_prov);
866906Sgm89044 	}
867906Sgm89044 	if (WORKLIST(dca, MCR2)->dwl_prov != NULL) {
868*5063Sgm89044 		(void) crypto_unregister_provider(
869*5063Sgm89044 		    WORKLIST(dca, MCR2)->dwl_prov);
870906Sgm89044 	}
871906Sgm89044 	if (intr_added) {
872906Sgm89044 		CLRBIT(dca, CSR_DMACTL,
873906Sgm89044 		    DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE);
874906Sgm89044 		/* unregister intr handler */
875906Sgm89044 		ddi_remove_intr(dip, 0, dca->dca_icookie);
876906Sgm89044 	}
877906Sgm89044 	if (dca->dca_regs_handle) {
878906Sgm89044 		ddi_regs_map_free(&dca->dca_regs_handle);
879906Sgm89044 	}
880906Sgm89044 	if (dca->dca_intrstats) {
881906Sgm89044 		kstat_delete(dca->dca_intrstats);
882906Sgm89044 	}
883906Sgm89044 	if (dca->dca_ksp) {
884906Sgm89044 		kstat_delete(dca->dca_ksp);
885906Sgm89044 	}
886906Sgm89044 	dca_uninit(dca);
887906Sgm89044 
888906Sgm89044 	/* finalize FMA */
889906Sgm89044 	dca_fma_fini(dca);
890906Sgm89044 
891906Sgm89044 	mutex_destroy(&dca->dca_intrlock);
892906Sgm89044 	ddi_soft_state_free(dca_state, instance);
893906Sgm89044 	return (DDI_FAILURE);
894906Sgm89044 
895906Sgm89044 }
896906Sgm89044 
897906Sgm89044 int
898906Sgm89044 dca_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
899906Sgm89044 {
900906Sgm89044 	int		instance;
901906Sgm89044 	dca_t		*dca;
902906Sgm89044 	timeout_id_t	tid;
903906Sgm89044 
904906Sgm89044 	instance = ddi_get_instance(dip);
905906Sgm89044 
906906Sgm89044 	DBG(NULL, DMOD, "dca: in dca_detach() for %d", instance);
907906Sgm89044 
908906Sgm89044 	switch (cmd) {
909906Sgm89044 	case DDI_SUSPEND:
910906Sgm89044 		if ((dca = (dca_t *)ddi_get_driver_private(dip)) == NULL) {
911906Sgm89044 			dca_diperror(dip, "no soft state in detach");
912906Sgm89044 			return (DDI_FAILURE);
913906Sgm89044 		}
914906Sgm89044 		/* assumption: we won't be DDI_DETACHed until we return */
915906Sgm89044 		return (dca_suspend(dca));
916906Sgm89044 
917906Sgm89044 	case DDI_DETACH:
918906Sgm89044 		break;
919906Sgm89044 	default:
920906Sgm89044 		return (DDI_FAILURE);
921906Sgm89044 	}
922906Sgm89044 
923906Sgm89044 	if ((dca = (dca_t *)ddi_get_driver_private(dip)) == NULL) {
924906Sgm89044 		dca_diperror(dip, "no soft state in detach");
925906Sgm89044 		return (DDI_FAILURE);
926906Sgm89044 	}
927906Sgm89044 
928906Sgm89044 	/*
929906Sgm89044 	 * Unregister from kCF.
930906Sgm89044 	 * This needs to be done at the beginning of detach.
931906Sgm89044 	 */
932906Sgm89044 	if (WORKLIST(dca, MCR1)->dwl_prov != NULL) {
933*5063Sgm89044 		if (crypto_unregister_provider(
934*5063Sgm89044 		    WORKLIST(dca, MCR1)->dwl_prov) != CRYPTO_SUCCESS) {
935*5063Sgm89044 			dca_error(dca, "unable to unregister MCR1 from kcf");
936*5063Sgm89044 			return (DDI_FAILURE);
937*5063Sgm89044 		}
938906Sgm89044 	}
939906Sgm89044 
940906Sgm89044 	if (WORKLIST(dca, MCR2)->dwl_prov != NULL) {
941*5063Sgm89044 		if (crypto_unregister_provider(
942*5063Sgm89044 		    WORKLIST(dca, MCR2)->dwl_prov) != CRYPTO_SUCCESS) {
943*5063Sgm89044 			dca_error(dca, "unable to unregister MCR2 from kcf");
944*5063Sgm89044 			return (DDI_FAILURE);
945*5063Sgm89044 		}
946906Sgm89044 	}
947906Sgm89044 
948906Sgm89044 	/*
949906Sgm89044 	 * Cleanup the private context list. Once the
950906Sgm89044 	 * crypto_unregister_provider returns, it is safe to do so.
951906Sgm89044 	 */
952906Sgm89044 	dca_free_context_list(dca);
953906Sgm89044 
954906Sgm89044 	/* Cleanup the local random number pool */
955906Sgm89044 	dca_random_fini(dca);
956906Sgm89044 
957906Sgm89044 	/* send any jobs in the waitq back to kCF */
958906Sgm89044 	dca_rejectjobs(dca);
959906Sgm89044 
960906Sgm89044 	/* untimeout the timeouts */
961906Sgm89044 	mutex_enter(&dca->dca_intrlock);
962906Sgm89044 	tid = dca->dca_jobtid;
963906Sgm89044 	dca->dca_jobtid = 0;
964906Sgm89044 	mutex_exit(&dca->dca_intrlock);
965906Sgm89044 	if (tid) {
966906Sgm89044 		(void) untimeout(tid);
967906Sgm89044 	}
968906Sgm89044 
969906Sgm89044 	/* disable device interrupts */
970906Sgm89044 	CLRBIT(dca, CSR_DMACTL, DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE);
971906Sgm89044 
972906Sgm89044 	/* unregister interrupt handlers */
973906Sgm89044 	ddi_remove_intr(dip, 0, dca->dca_icookie);
974906Sgm89044 
975906Sgm89044 	/* release our regs handle */
976906Sgm89044 	ddi_regs_map_free(&dca->dca_regs_handle);
977906Sgm89044 
978906Sgm89044 	/* toss out kstats */
979906Sgm89044 	if (dca->dca_intrstats) {
980906Sgm89044 		kstat_delete(dca->dca_intrstats);
981906Sgm89044 	}
982906Sgm89044 	if (dca->dca_ksp) {
983906Sgm89044 		kstat_delete(dca->dca_ksp);
984906Sgm89044 	}
985906Sgm89044 
986906Sgm89044 	mutex_destroy(&dca->dca_intrlock);
987906Sgm89044 	dca_uninit(dca);
988906Sgm89044 
989906Sgm89044 	/* finalize FMA */
990906Sgm89044 	dca_fma_fini(dca);
991906Sgm89044 
992906Sgm89044 	ddi_soft_state_free(dca_state, instance);
993906Sgm89044 
994906Sgm89044 	return (DDI_SUCCESS);
995906Sgm89044 }
996906Sgm89044 
997906Sgm89044 int
998906Sgm89044 dca_resume(dca_t *dca)
999906Sgm89044 {
1000906Sgm89044 	ddi_acc_handle_t	pci;
1001906Sgm89044 
1002906Sgm89044 	if (pci_config_setup(dca->dca_dip, &pci) != DDI_SUCCESS) {
1003906Sgm89044 		dca_error(dca, "unable to setup PCI config handle");
1004906Sgm89044 		return (DDI_FAILURE);
1005906Sgm89044 	}
1006906Sgm89044 
1007906Sgm89044 	/*
1008906Sgm89044 	 * Reprogram registers in PCI configuration space.
1009906Sgm89044 	 */
1010906Sgm89044 
1011906Sgm89044 	/* Broadcom-specific timers -- we disable them. */
1012906Sgm89044 	pci_config_put8(pci, PCI_TRDYTO, 0);
1013906Sgm89044 	pci_config_put8(pci, PCI_RETRIES, 0);
1014906Sgm89044 
1015906Sgm89044 	/* initialize PCI access settings */
1016906Sgm89044 	pci_config_put16(pci, PCI_COMM, PCICOMM_SEE |
1017906Sgm89044 	    PCICOMM_PEE | PCICOMM_BME | PCICOMM_MAE);
1018906Sgm89044 
1019906Sgm89044 	/* set up our PCI latency timer */
1020906Sgm89044 	pci_config_put8(pci, PCI_LATTMR, 0x40);
1021906Sgm89044 
1022906Sgm89044 	pci_config_teardown(&pci);
1023906Sgm89044 
1024906Sgm89044 	if (dca_reset(dca, 0) < 0) {
1025906Sgm89044 		dca_error(dca, "unable to reset device during resume");
1026906Sgm89044 		return (DDI_FAILURE);
1027906Sgm89044 	}
1028906Sgm89044 
1029906Sgm89044 	/*
1030906Sgm89044 	 * Now restore the card-specific CSRs.
1031906Sgm89044 	 */
1032906Sgm89044 
1033906Sgm89044 	/* restore endianness settings */
1034906Sgm89044 	PUTCSR(dca, CSR_DMACTL, DMACTL_BE32 | DMACTL_BE64);
1035906Sgm89044 	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
1036906Sgm89044 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
1037906Sgm89044 		return (DDI_FAILURE);
1038906Sgm89044 
1039906Sgm89044 	/* restore interrupt enables */
10403124Sqs148142 	if (dca->dca_devid == 0x5825) {
10413124Sqs148142 		/* for 5825 set 256 byte read size to improve performance */
10423124Sqs148142 		SETBIT(dca, CSR_DMACTL,
10433124Sqs148142 		    DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE | DMACTL_RD256);
10443124Sqs148142 	} else {
10453124Sqs148142 		SETBIT(dca, CSR_DMACTL,
10463124Sqs148142 		    DMACTL_MCR1IE | DMACTL_MCR2IE | DMACTL_EIE);
10473124Sqs148142 	}
1048906Sgm89044 	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
1049906Sgm89044 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
1050906Sgm89044 		return (DDI_FAILURE);
1051906Sgm89044 
1052906Sgm89044 	/* resume scheduling jobs on the device */
1053906Sgm89044 	dca_undrain(dca);
1054906Sgm89044 
1055906Sgm89044 	return (DDI_SUCCESS);
1056906Sgm89044 }
1057906Sgm89044 
1058906Sgm89044 int
1059906Sgm89044 dca_suspend(dca_t *dca)
1060906Sgm89044 {
1061906Sgm89044 	if ((dca_drain(dca)) != 0) {
1062906Sgm89044 		return (DDI_FAILURE);
1063906Sgm89044 	}
1064906Sgm89044 	if (dca_reset(dca, 0) < 0) {
1065906Sgm89044 		dca_error(dca, "unable to reset device during suspend");
1066906Sgm89044 		return (DDI_FAILURE);
1067906Sgm89044 	}
1068906Sgm89044 	return (DDI_SUCCESS);
1069906Sgm89044 }
1070906Sgm89044 
1071906Sgm89044 /*
1072906Sgm89044  * Hardware access stuff.
1073906Sgm89044  */
1074906Sgm89044 int
1075906Sgm89044 dca_reset(dca_t *dca, int failreset)
1076906Sgm89044 {
1077906Sgm89044 	int i;
1078906Sgm89044 
1079906Sgm89044 	if (dca->dca_regs_handle == NULL) {
1080906Sgm89044 		return (-1);
1081906Sgm89044 	}
1082906Sgm89044 
1083906Sgm89044 	PUTCSR(dca, CSR_DMACTL, DMACTL_RESET);
1084906Sgm89044 	if (!failreset) {
1085906Sgm89044 		if (dca_check_acc_handle(dca, dca->dca_regs_handle,
1086906Sgm89044 		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
1087906Sgm89044 			return (-1);
1088906Sgm89044 	}
1089906Sgm89044 
1090906Sgm89044 	/* now wait for a reset */
1091906Sgm89044 	for (i = 1; i < 100; i++) {
1092906Sgm89044 		uint32_t	dmactl;
1093906Sgm89044 		drv_usecwait(100);
1094906Sgm89044 		dmactl = GETCSR(dca, CSR_DMACTL);
1095906Sgm89044 		if (!failreset) {
1096906Sgm89044 			if (dca_check_acc_handle(dca, dca->dca_regs_handle,
1097906Sgm89044 			    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
1098906Sgm89044 				return (-1);
1099906Sgm89044 		}
1100906Sgm89044 		if ((dmactl & DMACTL_RESET) == 0) {
1101906Sgm89044 			DBG(dca, DCHATTY, "reset in %d usec", i * 100);
1102906Sgm89044 			return (0);
1103906Sgm89044 		}
1104906Sgm89044 	}
1105906Sgm89044 	if (!failreset) {
1106906Sgm89044 		dca_failure(dca, DDI_DEVICE_FAULT,
1107906Sgm89044 		    DCA_FM_ECLASS_NONE, dca_ena(0), CRYPTO_DEVICE_ERROR,
1108906Sgm89044 		    "timeout waiting for reset after %d usec", i * 100);
1109906Sgm89044 	}
1110906Sgm89044 	return (-1);
1111906Sgm89044 }
1112906Sgm89044 
1113906Sgm89044 int
1114906Sgm89044 dca_initworklist(dca_t *dca, dca_worklist_t *wlp)
1115906Sgm89044 {
1116906Sgm89044 	int	i;
1117906Sgm89044 	int	reqprealloc = wlp->dwl_hiwater + (MAXWORK * MAXREQSPERMCR);
1118906Sgm89044 
1119906Sgm89044 	/*
1120906Sgm89044 	 * Set up work queue.
1121906Sgm89044 	 */
1122906Sgm89044 	mutex_init(&wlp->dwl_lock, NULL, MUTEX_DRIVER, dca->dca_icookie);
1123906Sgm89044 	mutex_init(&wlp->dwl_freereqslock, NULL, MUTEX_DRIVER,
1124906Sgm89044 	    dca->dca_icookie);
11253124Sqs148142 	mutex_init(&wlp->dwl_freelock, NULL, MUTEX_DRIVER, dca->dca_icookie);
1126906Sgm89044 	cv_init(&wlp->dwl_cv, NULL, CV_DRIVER, NULL);
1127906Sgm89044 
1128906Sgm89044 	mutex_enter(&wlp->dwl_lock);
1129906Sgm89044 
1130906Sgm89044 	dca_initq(&wlp->dwl_freereqs);
1131906Sgm89044 	dca_initq(&wlp->dwl_waitq);
1132906Sgm89044 	dca_initq(&wlp->dwl_freework);
1133906Sgm89044 	dca_initq(&wlp->dwl_runq);
1134906Sgm89044 
1135906Sgm89044 	for (i = 0; i < MAXWORK; i++) {
1136906Sgm89044 		dca_work_t		*workp;
1137906Sgm89044 
1138906Sgm89044 		if ((workp = dca_newwork(dca)) == NULL) {
1139906Sgm89044 			dca_error(dca, "unable to allocate work");
1140906Sgm89044 			mutex_exit(&wlp->dwl_lock);
1141906Sgm89044 			return (DDI_FAILURE);
1142906Sgm89044 		}
1143906Sgm89044 		workp->dw_wlp = wlp;
1144906Sgm89044 		dca_freework(workp);
1145906Sgm89044 	}
1146906Sgm89044 	mutex_exit(&wlp->dwl_lock);
1147906Sgm89044 
1148906Sgm89044 	for (i = 0; i < reqprealloc; i++) {
1149906Sgm89044 		dca_request_t *reqp;
1150906Sgm89044 
1151906Sgm89044 		if ((reqp = dca_newreq(dca)) == NULL) {
1152906Sgm89044 			dca_error(dca, "unable to allocate request");
1153906Sgm89044 			return (DDI_FAILURE);
1154906Sgm89044 		}
1155906Sgm89044 		reqp->dr_dca = dca;
1156906Sgm89044 		reqp->dr_wlp = wlp;
1157906Sgm89044 		dca_freereq(reqp);
1158906Sgm89044 	}
1159906Sgm89044 	return (DDI_SUCCESS);
1160906Sgm89044 }
1161906Sgm89044 
1162906Sgm89044 int
1163906Sgm89044 dca_init(dca_t *dca)
1164906Sgm89044 {
1165906Sgm89044 	dca_worklist_t		*wlp;
1166906Sgm89044 
1167906Sgm89044 	/* Initialize the private context list and the corresponding lock. */
1168906Sgm89044 	mutex_init(&dca->dca_ctx_list_lock, NULL, MUTEX_DRIVER, NULL);
1169906Sgm89044 	dca_initq(&dca->dca_ctx_list);
1170906Sgm89044 
1171906Sgm89044 	/*
1172906Sgm89044 	 * MCR1 algorithms.
1173906Sgm89044 	 */
1174906Sgm89044 	wlp = WORKLIST(dca, MCR1);
1175906Sgm89044 	(void) sprintf(wlp->dwl_name, "dca%d:mcr1",
1176*5063Sgm89044 	    ddi_get_instance(dca->dca_dip));
1177906Sgm89044 	wlp->dwl_lowater = ddi_getprop(DDI_DEV_T_ANY,
1178906Sgm89044 	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
1179906Sgm89044 	    "mcr1_lowater", MCR1LOWATER);
1180906Sgm89044 	wlp->dwl_hiwater = ddi_getprop(DDI_DEV_T_ANY,
1181906Sgm89044 	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
1182906Sgm89044 	    "mcr1_hiwater", MCR1HIWATER);
1183906Sgm89044 	wlp->dwl_reqspermcr = min(ddi_getprop(DDI_DEV_T_ANY,
1184906Sgm89044 	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
1185906Sgm89044 	    "mcr1_maxreqs", MCR1MAXREQS), MAXREQSPERMCR);
1186906Sgm89044 	wlp->dwl_dca = dca;
1187906Sgm89044 	wlp->dwl_mcr = MCR1;
1188906Sgm89044 	if (dca_initworklist(dca, wlp) != DDI_SUCCESS) {
1189906Sgm89044 		return (DDI_FAILURE);
1190906Sgm89044 	}
1191906Sgm89044 
1192906Sgm89044 	/*
1193906Sgm89044 	 * MCR2 algorithms.
1194906Sgm89044 	 */
1195906Sgm89044 	wlp = WORKLIST(dca, MCR2);
1196906Sgm89044 	(void) sprintf(wlp->dwl_name, "dca%d:mcr2",
1197*5063Sgm89044 	    ddi_get_instance(dca->dca_dip));
1198906Sgm89044 	wlp->dwl_lowater = ddi_getprop(DDI_DEV_T_ANY,
1199906Sgm89044 	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
1200906Sgm89044 	    "mcr2_lowater", MCR2LOWATER);
1201906Sgm89044 	wlp->dwl_hiwater = ddi_getprop(DDI_DEV_T_ANY,
1202906Sgm89044 	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
1203906Sgm89044 	    "mcr2_hiwater", MCR2HIWATER);
1204906Sgm89044 	wlp->dwl_reqspermcr = min(ddi_getprop(DDI_DEV_T_ANY,
1205906Sgm89044 	    dca->dca_dip, DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
1206906Sgm89044 	    "mcr2_maxreqs", MCR2MAXREQS), MAXREQSPERMCR);
1207906Sgm89044 	wlp->dwl_dca = dca;
1208906Sgm89044 	wlp->dwl_mcr = MCR2;
1209906Sgm89044 	if (dca_initworklist(dca, wlp) != DDI_SUCCESS) {
1210906Sgm89044 		return (DDI_FAILURE);
1211906Sgm89044 	}
1212906Sgm89044 	return (DDI_SUCCESS);
1213906Sgm89044 }
1214906Sgm89044 
1215906Sgm89044 /*
1216906Sgm89044  * Uninitialize worklists.  This routine should only be called when no
1217906Sgm89044  * active jobs (hence DMA mappings) exist.  One way to ensure this is
1218906Sgm89044  * to unregister from kCF before calling this routine.  (This is done
1219906Sgm89044  * e.g. in detach(9e).)
1220906Sgm89044  */
1221906Sgm89044 void
1222906Sgm89044 dca_uninit(dca_t *dca)
1223906Sgm89044 {
1224906Sgm89044 	int	mcr;
1225906Sgm89044 
1226906Sgm89044 	mutex_destroy(&dca->dca_ctx_list_lock);
1227906Sgm89044 
1228906Sgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
1229906Sgm89044 		dca_worklist_t	*wlp = WORKLIST(dca, mcr);
1230906Sgm89044 		dca_work_t	*workp;
1231906Sgm89044 		dca_request_t	*reqp;
1232906Sgm89044 
1233906Sgm89044 		if (dca->dca_regs_handle == NULL) {
1234906Sgm89044 			continue;
1235906Sgm89044 		}
1236906Sgm89044 
1237906Sgm89044 		mutex_enter(&wlp->dwl_lock);
1238906Sgm89044 		while ((workp = dca_getwork(dca, mcr)) != NULL) {
1239906Sgm89044 			dca_destroywork(workp);
1240906Sgm89044 		}
1241906Sgm89044 		mutex_exit(&wlp->dwl_lock);
1242906Sgm89044 		while ((reqp = dca_getreq(dca, mcr, 0)) != NULL) {
1243906Sgm89044 			dca_destroyreq(reqp);
1244906Sgm89044 		}
1245906Sgm89044 
1246906Sgm89044 		mutex_destroy(&wlp->dwl_lock);
1247906Sgm89044 		mutex_destroy(&wlp->dwl_freereqslock);
12483124Sqs148142 		mutex_destroy(&wlp->dwl_freelock);
1249906Sgm89044 		cv_destroy(&wlp->dwl_cv);
1250906Sgm89044 		wlp->dwl_prov = NULL;
1251906Sgm89044 	}
1252906Sgm89044 }
1253906Sgm89044 
1254906Sgm89044 static void
1255906Sgm89044 dca_enlist2(dca_listnode_t *q, dca_listnode_t *node, kmutex_t *lock)
1256906Sgm89044 {
1257906Sgm89044 	if (!q || !node)
1258906Sgm89044 		return;
1259906Sgm89044 
1260906Sgm89044 	mutex_enter(lock);
1261906Sgm89044 	node->dl_next2 = q;
1262906Sgm89044 	node->dl_prev2 = q->dl_prev2;
1263906Sgm89044 	node->dl_next2->dl_prev2 = node;
1264906Sgm89044 	node->dl_prev2->dl_next2 = node;
1265906Sgm89044 	mutex_exit(lock);
1266906Sgm89044 }
1267906Sgm89044 
1268906Sgm89044 static void
1269906Sgm89044 dca_rmlist2(dca_listnode_t *node, kmutex_t *lock)
1270906Sgm89044 {
1271906Sgm89044 	if (!node)
1272906Sgm89044 		return;
1273906Sgm89044 
1274906Sgm89044 	mutex_enter(lock);
1275906Sgm89044 	node->dl_next2->dl_prev2 = node->dl_prev2;
1276906Sgm89044 	node->dl_prev2->dl_next2 = node->dl_next2;
1277906Sgm89044 	node->dl_next2 = NULL;
1278906Sgm89044 	node->dl_prev2 = NULL;
1279906Sgm89044 	mutex_exit(lock);
1280906Sgm89044 }
1281906Sgm89044 
1282906Sgm89044 static dca_listnode_t *
1283906Sgm89044 dca_delist2(dca_listnode_t *q, kmutex_t *lock)
1284906Sgm89044 {
1285906Sgm89044 	dca_listnode_t *node;
1286906Sgm89044 
1287906Sgm89044 	mutex_enter(lock);
1288906Sgm89044 	if ((node = q->dl_next2) == q) {
1289906Sgm89044 		mutex_exit(lock);
1290906Sgm89044 		return (NULL);
1291906Sgm89044 	}
1292906Sgm89044 
1293906Sgm89044 	node->dl_next2->dl_prev2 = node->dl_prev2;
1294906Sgm89044 	node->dl_prev2->dl_next2 = node->dl_next2;
1295906Sgm89044 	node->dl_next2 = NULL;
1296906Sgm89044 	node->dl_prev2 = NULL;
1297906Sgm89044 	mutex_exit(lock);
1298906Sgm89044 
1299906Sgm89044 	return (node);
1300906Sgm89044 }
1301906Sgm89044 
1302906Sgm89044 void
1303906Sgm89044 dca_initq(dca_listnode_t *q)
1304906Sgm89044 {
1305906Sgm89044 	q->dl_next = q;
1306906Sgm89044 	q->dl_prev = q;
1307906Sgm89044 	q->dl_next2 = q;
1308906Sgm89044 	q->dl_prev2 = q;
1309906Sgm89044 }
1310906Sgm89044 
1311906Sgm89044 void
1312906Sgm89044 dca_enqueue(dca_listnode_t *q, dca_listnode_t *node)
1313906Sgm89044 {
1314906Sgm89044 	/*
1315906Sgm89044 	 * Enqueue submits at the "tail" of the list, i.e. just
1316906Sgm89044 	 * behind the sentinel.
1317906Sgm89044 	 */
1318906Sgm89044 	node->dl_next = q;
1319906Sgm89044 	node->dl_prev = q->dl_prev;
1320906Sgm89044 	node->dl_next->dl_prev = node;
1321906Sgm89044 	node->dl_prev->dl_next = node;
1322906Sgm89044 }
1323906Sgm89044 
1324906Sgm89044 void
1325906Sgm89044 dca_rmqueue(dca_listnode_t *node)
1326906Sgm89044 {
1327906Sgm89044 	node->dl_next->dl_prev = node->dl_prev;
1328906Sgm89044 	node->dl_prev->dl_next = node->dl_next;
1329906Sgm89044 	node->dl_next = NULL;
1330906Sgm89044 	node->dl_prev = NULL;
1331906Sgm89044 }
1332906Sgm89044 
1333906Sgm89044 dca_listnode_t *
1334906Sgm89044 dca_dequeue(dca_listnode_t *q)
1335906Sgm89044 {
1336906Sgm89044 	dca_listnode_t *node;
1337906Sgm89044 	/*
1338906Sgm89044 	 * Dequeue takes from the "head" of the list, i.e. just after
1339906Sgm89044 	 * the sentinel.
1340906Sgm89044 	 */
1341906Sgm89044 	if ((node = q->dl_next) == q) {
1342906Sgm89044 		/* queue is empty */
1343906Sgm89044 		return (NULL);
1344906Sgm89044 	}
1345906Sgm89044 	dca_rmqueue(node);
1346906Sgm89044 	return (node);
1347906Sgm89044 }
1348906Sgm89044 
1349906Sgm89044 /* this is the opposite of dequeue, it takes things off in LIFO order */
1350906Sgm89044 dca_listnode_t *
1351906Sgm89044 dca_unqueue(dca_listnode_t *q)
1352906Sgm89044 {
1353906Sgm89044 	dca_listnode_t *node;
1354906Sgm89044 	/*
1355906Sgm89044 	 * unqueue takes from the "tail" of the list, i.e. just before
1356906Sgm89044 	 * the sentinel.
1357906Sgm89044 	 */
1358*5063Sgm89044 	if ((node = q->dl_prev) == q) {
1359906Sgm89044 		/* queue is empty */
1360906Sgm89044 		return (NULL);
1361906Sgm89044 	}
1362906Sgm89044 	dca_rmqueue(node);
1363906Sgm89044 	return (node);
1364906Sgm89044 }
1365906Sgm89044 
1366906Sgm89044 dca_listnode_t *
1367906Sgm89044 dca_peekqueue(dca_listnode_t *q)
1368906Sgm89044 {
1369906Sgm89044 	dca_listnode_t *node;
1370906Sgm89044 
1371906Sgm89044 	if ((node = q->dl_next) == q) {
1372906Sgm89044 		return (NULL);
1373906Sgm89044 	} else {
1374906Sgm89044 		return (node);
1375906Sgm89044 	}
1376906Sgm89044 }
1377906Sgm89044 
1378906Sgm89044 /*
1379906Sgm89044  * Interrupt service routine.
1380906Sgm89044  */
1381906Sgm89044 uint_t
1382906Sgm89044 dca_intr(char *arg)
1383906Sgm89044 {
1384906Sgm89044 	dca_t		*dca = (dca_t *)arg;
1385906Sgm89044 	uint32_t	status;
1386906Sgm89044 
1387906Sgm89044 	mutex_enter(&dca->dca_intrlock);
1388906Sgm89044 	status = GETCSR(dca, CSR_DMASTAT);
1389906Sgm89044 	PUTCSR(dca, CSR_DMASTAT, status & DMASTAT_INTERRUPTS);
1390906Sgm89044 	if (dca_check_acc_handle(dca, dca->dca_regs_handle,
1391906Sgm89044 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
1392906Sgm89044 		mutex_exit(&dca->dca_intrlock);
1393906Sgm89044 		return ((uint_t)DDI_FAILURE);
1394906Sgm89044 	}
1395906Sgm89044 
1396906Sgm89044 	DBG(dca, DINTR, "interrupted, status = 0x%x!", status);
1397906Sgm89044 
1398906Sgm89044 	if ((status & DMASTAT_INTERRUPTS) == 0) {
1399906Sgm89044 		/* increment spurious interrupt kstat */
1400906Sgm89044 		if (dca->dca_intrstats) {
1401906Sgm89044 			KIOIP(dca)->intrs[KSTAT_INTR_SPURIOUS]++;
1402906Sgm89044 		}
1403906Sgm89044 		mutex_exit(&dca->dca_intrlock);
1404906Sgm89044 		return (DDI_INTR_UNCLAIMED);
1405906Sgm89044 	}
1406906Sgm89044 
1407906Sgm89044 	if (dca->dca_intrstats) {
1408906Sgm89044 		KIOIP(dca)->intrs[KSTAT_INTR_HARD]++;
1409906Sgm89044 	}
1410906Sgm89044 	if (status & DMASTAT_MCR1INT) {
1411906Sgm89044 		DBG(dca, DINTR, "MCR1 interrupted");
1412906Sgm89044 		mutex_enter(&(WORKLIST(dca, MCR1)->dwl_lock));
1413906Sgm89044 		dca_schedule(dca, MCR1);
1414906Sgm89044 		dca_reclaim(dca, MCR1);
1415906Sgm89044 		mutex_exit(&(WORKLIST(dca, MCR1)->dwl_lock));
1416906Sgm89044 	}
1417906Sgm89044 
1418906Sgm89044 	if (status & DMASTAT_MCR2INT) {
1419906Sgm89044 		DBG(dca, DINTR, "MCR2 interrupted");
1420906Sgm89044 		mutex_enter(&(WORKLIST(dca, MCR2)->dwl_lock));
1421906Sgm89044 		dca_schedule(dca, MCR2);
1422906Sgm89044 		dca_reclaim(dca, MCR2);
1423906Sgm89044 		mutex_exit(&(WORKLIST(dca, MCR2)->dwl_lock));
1424906Sgm89044 	}
1425906Sgm89044 
1426906Sgm89044 	if (status & DMASTAT_ERRINT) {
1427906Sgm89044 		uint32_t	erraddr;
1428906Sgm89044 		erraddr = GETCSR(dca, CSR_DMAEA);
1429906Sgm89044 		mutex_exit(&dca->dca_intrlock);
1430906Sgm89044 
1431906Sgm89044 		/*
1432906Sgm89044 		 * bit 1 of the error address indicates failure during
1433906Sgm89044 		 * read if set, during write otherwise.
1434906Sgm89044 		 */
1435906Sgm89044 		dca_failure(dca, DDI_DEVICE_FAULT,
1436906Sgm89044 		    DCA_FM_ECLASS_HW_DEVICE, dca_ena(0), CRYPTO_DEVICE_ERROR,
1437906Sgm89044 		    "DMA master access error %s address 0x%x",
1438906Sgm89044 		    erraddr & 0x1 ? "reading" : "writing", erraddr & ~1);
1439906Sgm89044 		return (DDI_INTR_CLAIMED);
1440906Sgm89044 	}
1441906Sgm89044 
1442906Sgm89044 	mutex_exit(&dca->dca_intrlock);
1443906Sgm89044 
1444906Sgm89044 	return (DDI_INTR_CLAIMED);
1445906Sgm89044 }
1446906Sgm89044 
1447906Sgm89044 /*
1448906Sgm89044  * Reverse a string of bytes from s1 into s2.  The reversal happens
1449906Sgm89044  * from the tail of s1.  If len1 < len2, then null bytes will be
1450906Sgm89044  * padded to the end of s2.  If len2 < len1, then (presumably null)
1451906Sgm89044  * bytes will be dropped from the start of s1.
1452906Sgm89044  *
1453906Sgm89044  * The rationale here is that when s1 (source) is shorter, then we
1454906Sgm89044  * are reversing from big-endian ordering, into device ordering, and
1455906Sgm89044  * want to add some extra nulls to the tail (MSB) side of the device.
1456906Sgm89044  *
1457906Sgm89044  * Similarly, when s2 (dest) is shorter, then we are truncating what
1458906Sgm89044  * are presumably null MSB bits from the device.
1459906Sgm89044  *
1460906Sgm89044  * There is an expectation when reversing from the device back into
1461906Sgm89044  * big-endian, that the number of bytes to reverse and the target size
1462906Sgm89044  * will match, and no truncation or padding occurs.
1463906Sgm89044  */
1464906Sgm89044 void
1465906Sgm89044 dca_reverse(void *s1, void *s2, int len1, int len2)
1466906Sgm89044 {
1467906Sgm89044 	caddr_t	src, dst;
1468906Sgm89044 
1469906Sgm89044 	if (len1 == 0) {
1470906Sgm89044 		if (len2) {
1471906Sgm89044 			bzero(s2, len2);
1472906Sgm89044 		}
1473906Sgm89044 		return;
1474906Sgm89044 	}
1475906Sgm89044 	src = (caddr_t)s1 + len1 - 1;
1476906Sgm89044 	dst = s2;
1477906Sgm89044 	while ((src >= (caddr_t)s1) && (len2)) {
1478906Sgm89044 		*dst++ = *src--;
1479906Sgm89044 		len2--;
1480906Sgm89044 	}
1481906Sgm89044 	while (len2 > 0) {
1482906Sgm89044 		*dst++ = 0;
1483906Sgm89044 		len2--;
1484906Sgm89044 	}
1485906Sgm89044 }
1486906Sgm89044 
1487906Sgm89044 uint16_t
1488906Sgm89044 dca_padfull(int num)
1489906Sgm89044 {
1490906Sgm89044 	if (num <= 512) {
1491906Sgm89044 		return (BITS2BYTES(512));
1492906Sgm89044 	}
1493906Sgm89044 	if (num <= 768) {
1494906Sgm89044 		return (BITS2BYTES(768));
1495906Sgm89044 	}
1496906Sgm89044 	if (num <= 1024) {
1497906Sgm89044 		return (BITS2BYTES(1024));
1498906Sgm89044 	}
1499906Sgm89044 	if (num <= 1536) {
1500906Sgm89044 		return (BITS2BYTES(1536));
1501906Sgm89044 	}
1502906Sgm89044 	if (num <= 2048) {
1503906Sgm89044 		return (BITS2BYTES(2048));
1504906Sgm89044 	}
1505906Sgm89044 	return (0);
1506906Sgm89044 }
1507906Sgm89044 
1508906Sgm89044 uint16_t
1509906Sgm89044 dca_padhalf(int num)
1510906Sgm89044 {
1511906Sgm89044 	if (num <= 256) {
1512906Sgm89044 		return (BITS2BYTES(256));
1513906Sgm89044 	}
1514906Sgm89044 	if (num <= 384) {
1515906Sgm89044 		return (BITS2BYTES(384));
1516906Sgm89044 	}
1517906Sgm89044 	if (num <= 512) {
1518906Sgm89044 		return (BITS2BYTES(512));
1519906Sgm89044 	}
1520906Sgm89044 	if (num <= 768) {
1521906Sgm89044 		return (BITS2BYTES(768));
1522906Sgm89044 	}
1523906Sgm89044 	if (num <= 1024) {
1524906Sgm89044 		return (BITS2BYTES(1024));
1525906Sgm89044 	}
1526906Sgm89044 	return (0);
1527906Sgm89044 }
1528906Sgm89044 
1529906Sgm89044 dca_work_t *
1530906Sgm89044 dca_newwork(dca_t *dca)
1531906Sgm89044 {
1532906Sgm89044 	dca_work_t		*workp;
1533906Sgm89044 	size_t			size;
1534906Sgm89044 	ddi_dma_cookie_t	c;
1535906Sgm89044 	unsigned		nc;
1536906Sgm89044 	int			rv;
1537906Sgm89044 
1538906Sgm89044 	workp = kmem_zalloc(sizeof (dca_work_t), KM_SLEEP);
1539906Sgm89044 
1540906Sgm89044 	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
1541906Sgm89044 	    DDI_DMA_SLEEP, NULL, &workp->dw_mcr_dmah);
1542906Sgm89044 	if (rv != 0) {
1543906Sgm89044 		dca_error(dca, "unable to alloc MCR DMA handle");
1544906Sgm89044 		dca_destroywork(workp);
1545906Sgm89044 		return (NULL);
1546906Sgm89044 	}
1547906Sgm89044 
1548906Sgm89044 	rv = ddi_dma_mem_alloc(workp->dw_mcr_dmah,
1549906Sgm89044 	    ROUNDUP(MCR_SIZE, dca->dca_pagesize),
1550906Sgm89044 	    &dca_devattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
1551906Sgm89044 	    &workp->dw_mcr_kaddr, &size, &workp->dw_mcr_acch);
1552906Sgm89044 	if (rv != 0) {
1553906Sgm89044 		dca_error(dca, "unable to alloc MCR DMA memory");
1554906Sgm89044 		dca_destroywork(workp);
1555906Sgm89044 		return (NULL);
1556906Sgm89044 	}
1557906Sgm89044 
1558906Sgm89044 	rv = ddi_dma_addr_bind_handle(workp->dw_mcr_dmah, NULL,
1559906Sgm89044 	    workp->dw_mcr_kaddr, size, DDI_DMA_CONSISTENT | DDI_DMA_RDWR,
1560906Sgm89044 	    DDI_DMA_SLEEP, NULL, &c, &nc);
1561906Sgm89044 	if (rv != DDI_DMA_MAPPED) {
1562906Sgm89044 		dca_error(dca, "unable to map MCR DMA memory");
1563906Sgm89044 		dca_destroywork(workp);
1564906Sgm89044 		return (NULL);
1565906Sgm89044 	}
1566906Sgm89044 
1567906Sgm89044 	workp->dw_mcr_paddr = c.dmac_address;
1568906Sgm89044 	return (workp);
1569906Sgm89044 }
1570906Sgm89044 
1571906Sgm89044 void
1572906Sgm89044 dca_destroywork(dca_work_t *workp)
1573906Sgm89044 {
1574906Sgm89044 	if (workp->dw_mcr_paddr) {
1575906Sgm89044 		(void) ddi_dma_unbind_handle(workp->dw_mcr_dmah);
1576906Sgm89044 	}
1577906Sgm89044 	if (workp->dw_mcr_acch) {
1578906Sgm89044 		ddi_dma_mem_free(&workp->dw_mcr_acch);
1579906Sgm89044 	}
1580906Sgm89044 	if (workp->dw_mcr_dmah) {
1581906Sgm89044 		ddi_dma_free_handle(&workp->dw_mcr_dmah);
1582906Sgm89044 	}
1583906Sgm89044 	kmem_free(workp, sizeof (dca_work_t));
1584906Sgm89044 }
1585906Sgm89044 
1586906Sgm89044 dca_request_t *
1587906Sgm89044 dca_newreq(dca_t *dca)
1588906Sgm89044 {
1589906Sgm89044 	dca_request_t		*reqp;
1590906Sgm89044 	size_t			size;
1591906Sgm89044 	ddi_dma_cookie_t	c;
1592906Sgm89044 	unsigned		nc;
1593906Sgm89044 	int			rv;
1594906Sgm89044 	int			n_chain = 0;
1595906Sgm89044 
1596906Sgm89044 	size = (DESC_SIZE * MAXFRAGS) + CTX_MAXLENGTH;
1597906Sgm89044 
1598906Sgm89044 	reqp = kmem_zalloc(sizeof (dca_request_t), KM_SLEEP);
1599906Sgm89044 
1600906Sgm89044 	reqp->dr_dca = dca;
1601906Sgm89044 
1602906Sgm89044 	/*
1603906Sgm89044 	 * Setup the DMA region for the context and descriptors.
1604906Sgm89044 	 */
1605906Sgm89044 	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr, DDI_DMA_SLEEP,
1606906Sgm89044 	    NULL, &reqp->dr_ctx_dmah);
1607906Sgm89044 	if (rv != DDI_SUCCESS) {
1608906Sgm89044 		dca_error(dca, "failure allocating request DMA handle");
1609906Sgm89044 		dca_destroyreq(reqp);
1610906Sgm89044 		return (NULL);
1611906Sgm89044 	}
1612906Sgm89044 
1613906Sgm89044 	/* for driver hardening, allocate in whole pages */
1614906Sgm89044 	rv = ddi_dma_mem_alloc(reqp->dr_ctx_dmah,
1615906Sgm89044 	    ROUNDUP(size, dca->dca_pagesize), &dca_devattr, DDI_DMA_CONSISTENT,
1616906Sgm89044 	    DDI_DMA_SLEEP, NULL, &reqp->dr_ctx_kaddr, &size,
1617906Sgm89044 	    &reqp->dr_ctx_acch);
1618906Sgm89044 	if (rv != DDI_SUCCESS) {
1619906Sgm89044 		dca_error(dca, "unable to alloc request DMA memory");
1620906Sgm89044 		dca_destroyreq(reqp);
1621906Sgm89044 		return (NULL);
1622906Sgm89044 	}
1623906Sgm89044 
1624906Sgm89044 	rv = ddi_dma_addr_bind_handle(reqp->dr_ctx_dmah, NULL,
1625906Sgm89044 	    reqp->dr_ctx_kaddr, size, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
1626906Sgm89044 	    DDI_DMA_SLEEP, 0, &c, &nc);
1627906Sgm89044 	if (rv != DDI_DMA_MAPPED) {
1628906Sgm89044 		dca_error(dca, "failed binding request DMA handle");
1629906Sgm89044 		dca_destroyreq(reqp);
1630906Sgm89044 		return (NULL);
1631906Sgm89044 	}
1632906Sgm89044 	reqp->dr_ctx_paddr = c.dmac_address;
1633906Sgm89044 
1634906Sgm89044 	reqp->dr_dma_size = size;
1635906Sgm89044 
1636906Sgm89044 	/*
1637906Sgm89044 	 * Set up the dma for our scratch/shared buffers.
1638906Sgm89044 	 */
1639906Sgm89044 	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
1640906Sgm89044 	    DDI_DMA_SLEEP, NULL, &reqp->dr_ibuf_dmah);
1641906Sgm89044 	if (rv != DDI_SUCCESS) {
1642906Sgm89044 		dca_error(dca, "failure allocating ibuf DMA handle");
1643906Sgm89044 		dca_destroyreq(reqp);
1644906Sgm89044 		return (NULL);
1645906Sgm89044 	}
1646906Sgm89044 	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
1647906Sgm89044 	    DDI_DMA_SLEEP, NULL, &reqp->dr_obuf_dmah);
1648906Sgm89044 	if (rv != DDI_SUCCESS) {
1649906Sgm89044 		dca_error(dca, "failure allocating obuf DMA handle");
1650906Sgm89044 		dca_destroyreq(reqp);
1651906Sgm89044 		return (NULL);
1652906Sgm89044 	}
1653906Sgm89044 
1654906Sgm89044 	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
1655906Sgm89044 	    DDI_DMA_SLEEP, NULL, &reqp->dr_chain_in_dmah);
1656906Sgm89044 	if (rv != DDI_SUCCESS) {
1657906Sgm89044 		dca_error(dca, "failure allocating chain_in DMA handle");
1658906Sgm89044 		dca_destroyreq(reqp);
1659906Sgm89044 		return (NULL);
1660906Sgm89044 	}
1661906Sgm89044 
1662906Sgm89044 	rv = ddi_dma_alloc_handle(dca->dca_dip, &dca_dmaattr,
1663906Sgm89044 	    DDI_DMA_SLEEP, NULL, &reqp->dr_chain_out_dmah);
1664906Sgm89044 	if (rv != DDI_SUCCESS) {
1665906Sgm89044 		dca_error(dca, "failure allocating chain_out DMA handle");
1666906Sgm89044 		dca_destroyreq(reqp);
1667906Sgm89044 		return (NULL);
1668906Sgm89044 	}
1669906Sgm89044 
1670906Sgm89044 	/*
1671906Sgm89044 	 * for driver hardening, allocate in whole pages.
1672906Sgm89044 	 */
1673906Sgm89044 	size = ROUNDUP(MAXPACKET, dca->dca_pagesize);
1674906Sgm89044 #if defined(i386) || defined(__i386)
1675906Sgm89044 	/*
1676906Sgm89044 	 * Use kmem_alloc instead of ddi_dma_mem_alloc here since the latter
1677906Sgm89044 	 * may fail on x86 platform if a physically contigous memory chunk
1678906Sgm89044 	 * cannot be found. From initial testing, we did not see performance
1679906Sgm89044 	 * degration as seen on Sparc.
1680906Sgm89044 	 */
1681906Sgm89044 	if ((reqp->dr_ibuf_kaddr = kmem_alloc(size, KM_SLEEP)) == NULL) {
1682906Sgm89044 		dca_error(dca, "unable to alloc request ibuf memory");
1683906Sgm89044 		dca_destroyreq(reqp);
1684906Sgm89044 		return (NULL);
1685906Sgm89044 	}
1686906Sgm89044 	if ((reqp->dr_obuf_kaddr = kmem_alloc(size, KM_SLEEP)) == NULL) {
1687906Sgm89044 		dca_error(dca, "unable to alloc request obuf memory");
1688906Sgm89044 		dca_destroyreq(reqp);
1689906Sgm89044 		return (NULL);
1690906Sgm89044 	}
1691906Sgm89044 #else
1692906Sgm89044 	/*
1693906Sgm89044 	 * We could kmem_alloc for sparc too. However, it gives worse
1694906Sgm89044 	 * performance when transfering more than one page data. For example,
1695906Sgm89044 	 * using 4 threads and 12032 byte data and 3DES on 900MHZ sparc system,
1696906Sgm89044 	 * kmem_alloc uses 80% CPU and ddi_dma_mem_alloc uses 50% CPU for
1697906Sgm89044 	 * the same throughput.
1698906Sgm89044 	 */
1699906Sgm89044 	rv = ddi_dma_mem_alloc(reqp->dr_ibuf_dmah,
1700906Sgm89044 	    size, &dca_bufattr,
1701906Sgm89044 	    DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &reqp->dr_ibuf_kaddr,
1702906Sgm89044 	    &size, &reqp->dr_ibuf_acch);
1703906Sgm89044 	if (rv != DDI_SUCCESS) {
1704906Sgm89044 		dca_error(dca, "unable to alloc request DMA memory");
1705906Sgm89044 		dca_destroyreq(reqp);
1706906Sgm89044 		return (NULL);
1707906Sgm89044 	}
1708906Sgm89044 
1709906Sgm89044 	rv = ddi_dma_mem_alloc(reqp->dr_obuf_dmah,
1710906Sgm89044 	    size, &dca_bufattr,
1711906Sgm89044 	    DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &reqp->dr_obuf_kaddr,
1712906Sgm89044 	    &size, &reqp->dr_obuf_acch);
1713906Sgm89044 	if (rv != DDI_SUCCESS) {
1714906Sgm89044 		dca_error(dca, "unable to alloc request DMA memory");
1715906Sgm89044 		dca_destroyreq(reqp);
1716906Sgm89044 		return (NULL);
1717906Sgm89044 	}
1718906Sgm89044 #endif
1719906Sgm89044 
1720906Sgm89044 	/* Skip the used portion in the context page */
1721906Sgm89044 	reqp->dr_offset = CTX_MAXLENGTH;
1722906Sgm89044 	if ((rv = dca_bindchains_one(reqp, size, reqp->dr_offset,
1723906Sgm89044 	    reqp->dr_ibuf_kaddr, reqp->dr_ibuf_dmah,
1724906Sgm89044 	    DDI_DMA_WRITE | DDI_DMA_STREAMING,
1725906Sgm89044 	    &reqp->dr_ibuf_head, &n_chain)) != DDI_SUCCESS) {
1726906Sgm89044 		(void) dca_destroyreq(reqp);
1727906Sgm89044 		return (NULL);
1728906Sgm89044 	}
1729906Sgm89044 	reqp->dr_ibuf_paddr = reqp->dr_ibuf_head.dc_buffer_paddr;
1730906Sgm89044 	/* Skip the space used by the input buffer */
1731906Sgm89044 	reqp->dr_offset += DESC_SIZE * n_chain;
1732906Sgm89044 
1733906Sgm89044 	if ((rv = dca_bindchains_one(reqp, size, reqp->dr_offset,
1734906Sgm89044 	    reqp->dr_obuf_kaddr, reqp->dr_obuf_dmah,
1735906Sgm89044 	    DDI_DMA_READ | DDI_DMA_STREAMING,
1736906Sgm89044 	    &reqp->dr_obuf_head, &n_chain)) != DDI_SUCCESS) {
1737906Sgm89044 		(void) dca_destroyreq(reqp);
1738906Sgm89044 		return (NULL);
1739906Sgm89044 	}
1740906Sgm89044 	reqp->dr_obuf_paddr = reqp->dr_obuf_head.dc_buffer_paddr;
1741906Sgm89044 	/* Skip the space used by the output buffer */
1742906Sgm89044 	reqp->dr_offset += DESC_SIZE * n_chain;
1743906Sgm89044 
1744906Sgm89044 	DBG(dca, DCHATTY, "CTX is 0x%p, phys 0x%x, len %d",
1745906Sgm89044 	    reqp->dr_ctx_kaddr, reqp->dr_ctx_paddr, CTX_MAXLENGTH);
1746906Sgm89044 	return (reqp);
1747906Sgm89044 }
1748906Sgm89044 
1749906Sgm89044 void
1750906Sgm89044 dca_destroyreq(dca_request_t *reqp)
1751906Sgm89044 {
1752906Sgm89044 #if defined(i386) || defined(__i386)
1753906Sgm89044 	dca_t		*dca = reqp->dr_dca;
1754906Sgm89044 	size_t		size = ROUNDUP(MAXPACKET, dca->dca_pagesize);
1755906Sgm89044 #endif
1756906Sgm89044 
1757906Sgm89044 	/*
1758906Sgm89044 	 * Clean up DMA for the context structure.
1759906Sgm89044 	 */
1760906Sgm89044 	if (reqp->dr_ctx_paddr) {
1761906Sgm89044 		(void) ddi_dma_unbind_handle(reqp->dr_ctx_dmah);
1762906Sgm89044 	}
1763906Sgm89044 
1764906Sgm89044 	if (reqp->dr_ctx_acch) {
1765906Sgm89044 		ddi_dma_mem_free(&reqp->dr_ctx_acch);
1766906Sgm89044 	}
1767906Sgm89044 
1768906Sgm89044 	if (reqp->dr_ctx_dmah) {
1769906Sgm89044 		ddi_dma_free_handle(&reqp->dr_ctx_dmah);
1770906Sgm89044 	}
1771906Sgm89044 
1772906Sgm89044 	/*
1773906Sgm89044 	 * Clean up DMA for the scratch buffer.
1774906Sgm89044 	 */
1775906Sgm89044 #if defined(i386) || defined(__i386)
1776906Sgm89044 	if (reqp->dr_ibuf_dmah) {
1777906Sgm89044 		(void) ddi_dma_unbind_handle(reqp->dr_ibuf_dmah);
1778906Sgm89044 		ddi_dma_free_handle(&reqp->dr_ibuf_dmah);
1779906Sgm89044 	}
1780906Sgm89044 	if (reqp->dr_obuf_dmah) {
1781906Sgm89044 		(void) ddi_dma_unbind_handle(reqp->dr_obuf_dmah);
1782906Sgm89044 		ddi_dma_free_handle(&reqp->dr_obuf_dmah);
1783906Sgm89044 	}
1784906Sgm89044 
1785906Sgm89044 	kmem_free(reqp->dr_ibuf_kaddr, size);
1786906Sgm89044 	kmem_free(reqp->dr_obuf_kaddr, size);
1787906Sgm89044 #else
1788906Sgm89044 	if (reqp->dr_ibuf_paddr) {
1789906Sgm89044 		(void) ddi_dma_unbind_handle(reqp->dr_ibuf_dmah);
1790906Sgm89044 	}
1791906Sgm89044 	if (reqp->dr_obuf_paddr) {
1792906Sgm89044 		(void) ddi_dma_unbind_handle(reqp->dr_obuf_dmah);
1793906Sgm89044 	}
1794906Sgm89044 
1795906Sgm89044 	if (reqp->dr_ibuf_acch) {
1796906Sgm89044 		ddi_dma_mem_free(&reqp->dr_ibuf_acch);
1797906Sgm89044 	}
1798906Sgm89044 	if (reqp->dr_obuf_acch) {
1799906Sgm89044 		ddi_dma_mem_free(&reqp->dr_obuf_acch);
1800906Sgm89044 	}
1801906Sgm89044 
1802906Sgm89044 	if (reqp->dr_ibuf_dmah) {
1803906Sgm89044 		ddi_dma_free_handle(&reqp->dr_ibuf_dmah);
1804906Sgm89044 	}
1805906Sgm89044 	if (reqp->dr_obuf_dmah) {
1806906Sgm89044 		ddi_dma_free_handle(&reqp->dr_obuf_dmah);
1807906Sgm89044 	}
1808906Sgm89044 #endif
1809906Sgm89044 	/*
1810906Sgm89044 	 * These two DMA handles should have been unbinded in
1811906Sgm89044 	 * dca_unbindchains() function
1812906Sgm89044 	 */
1813906Sgm89044 	if (reqp->dr_chain_in_dmah) {
1814906Sgm89044 		ddi_dma_free_handle(&reqp->dr_chain_in_dmah);
1815906Sgm89044 	}
1816906Sgm89044 	if (reqp->dr_chain_out_dmah) {
1817906Sgm89044 		ddi_dma_free_handle(&reqp->dr_chain_out_dmah);
1818906Sgm89044 	}
1819906Sgm89044 
1820906Sgm89044 	kmem_free(reqp, sizeof (dca_request_t));
1821906Sgm89044 }
1822906Sgm89044 
1823906Sgm89044 dca_work_t *
1824906Sgm89044 dca_getwork(dca_t *dca, int mcr)
1825906Sgm89044 {
1826906Sgm89044 	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
1827906Sgm89044 	dca_work_t	*workp;
1828906Sgm89044 
18293124Sqs148142 	mutex_enter(&wlp->dwl_freelock);
1830906Sgm89044 	workp = (dca_work_t *)dca_dequeue(&wlp->dwl_freework);
18313124Sqs148142 	mutex_exit(&wlp->dwl_freelock);
1832906Sgm89044 	if (workp) {
1833906Sgm89044 		int	nreqs;
1834906Sgm89044 		bzero(workp->dw_mcr_kaddr, 8);
1835906Sgm89044 
1836906Sgm89044 		/* clear out old requests */
1837906Sgm89044 		for (nreqs = 0; nreqs < MAXREQSPERMCR; nreqs++) {
1838906Sgm89044 			workp->dw_reqs[nreqs] = NULL;
1839906Sgm89044 		}
1840906Sgm89044 	}
1841906Sgm89044 	return (workp);
1842906Sgm89044 }
1843906Sgm89044 
1844906Sgm89044 void
1845906Sgm89044 dca_freework(dca_work_t *workp)
1846906Sgm89044 {
18473124Sqs148142 	mutex_enter(&workp->dw_wlp->dwl_freelock);
1848906Sgm89044 	dca_enqueue(&workp->dw_wlp->dwl_freework, (dca_listnode_t *)workp);
18493124Sqs148142 	mutex_exit(&workp->dw_wlp->dwl_freelock);
1850906Sgm89044 }
1851906Sgm89044 
1852906Sgm89044 dca_request_t *
1853906Sgm89044 dca_getreq(dca_t *dca, int mcr, int tryhard)
1854906Sgm89044 {
1855906Sgm89044 	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
1856906Sgm89044 	dca_request_t	*reqp;
1857906Sgm89044 
1858906Sgm89044 	mutex_enter(&wlp->dwl_freereqslock);
1859906Sgm89044 	reqp = (dca_request_t *)dca_dequeue(&wlp->dwl_freereqs);
1860906Sgm89044 	mutex_exit(&wlp->dwl_freereqslock);
1861906Sgm89044 	if (reqp) {
1862906Sgm89044 		reqp->dr_flags = 0;
1863906Sgm89044 		reqp->dr_callback = NULL;
1864906Sgm89044 	} else if (tryhard) {
1865906Sgm89044 		/*
1866906Sgm89044 		 * failed to get a free one, try an allocation, the hard way.
1867906Sgm89044 		 * XXX: Kstat desired here.
1868906Sgm89044 		 */
1869906Sgm89044 		if ((reqp = dca_newreq(dca)) != NULL) {
1870906Sgm89044 			reqp->dr_wlp = wlp;
1871906Sgm89044 			reqp->dr_dca = dca;
1872906Sgm89044 			reqp->dr_flags = 0;
1873906Sgm89044 			reqp->dr_callback = NULL;
1874906Sgm89044 		}
1875906Sgm89044 	}
1876906Sgm89044 	return (reqp);
1877906Sgm89044 }
1878906Sgm89044 
1879906Sgm89044 void
1880906Sgm89044 dca_freereq(dca_request_t *reqp)
1881906Sgm89044 {
1882906Sgm89044 	reqp->dr_kcf_req = NULL;
1883906Sgm89044 	if (!(reqp->dr_flags & DR_NOCACHE)) {
1884906Sgm89044 		mutex_enter(&reqp->dr_wlp->dwl_freereqslock);
1885906Sgm89044 		dca_enqueue(&reqp->dr_wlp->dwl_freereqs,
1886906Sgm89044 		    (dca_listnode_t *)reqp);
1887906Sgm89044 		mutex_exit(&reqp->dr_wlp->dwl_freereqslock);
1888906Sgm89044 	}
1889906Sgm89044 }
1890906Sgm89044 
1891906Sgm89044 /*
1892906Sgm89044  * Binds user buffers to DMA handles dynamically. On Sparc, a user buffer
1893906Sgm89044  * is mapped to a single physicall address. On x86, a user buffer is mapped
1894906Sgm89044  * to multiple physically addresses. These phsyical addresses are chained
1895906Sgm89044  * using the method specified in Broadcom BCM5820 specification
1896906Sgm89044  */
1897906Sgm89044 int
1898906Sgm89044 dca_bindchains(dca_request_t *reqp, size_t incnt, size_t outcnt)
1899906Sgm89044 {
1900906Sgm89044 	int			rv;
1901906Sgm89044 	caddr_t			kaddr;
1902906Sgm89044 	uint_t			flags;
1903906Sgm89044 	int			n_chain = 0;
1904906Sgm89044 
1905906Sgm89044 	if (reqp->dr_flags & DR_INPLACE) {
1906906Sgm89044 		flags = DDI_DMA_RDWR | DDI_DMA_CONSISTENT;
1907906Sgm89044 	} else {
1908906Sgm89044 		flags = DDI_DMA_WRITE | DDI_DMA_STREAMING;
1909906Sgm89044 	}
1910906Sgm89044 
1911906Sgm89044 	/* first the input */
1912906Sgm89044 	if (incnt) {
1913906Sgm89044 		if ((kaddr = dca_bufdaddr(reqp->dr_in)) == NULL) {
1914906Sgm89044 			DBG(NULL, DWARN, "unrecognised crypto data format");
1915906Sgm89044 			return (DDI_FAILURE);
1916906Sgm89044 		}
1917906Sgm89044 		if ((rv = dca_bindchains_one(reqp, incnt, reqp->dr_offset,
1918906Sgm89044 		    kaddr, reqp->dr_chain_in_dmah, flags,
1919906Sgm89044 		    &reqp->dr_chain_in_head, &n_chain)) != DDI_SUCCESS) {
1920906Sgm89044 			(void) dca_unbindchains(reqp);
1921906Sgm89044 			return (rv);
1922906Sgm89044 		}
1923906Sgm89044 
1924906Sgm89044 		/*
1925906Sgm89044 		 * The offset and length are altered by the calling routine
1926906Sgm89044 		 * reqp->dr_in->cd_offset += incnt;
1927906Sgm89044 		 * reqp->dr_in->cd_length -= incnt;
1928906Sgm89044 		 */
1929906Sgm89044 		/* Save the first one in the chain for MCR */
1930906Sgm89044 		reqp->dr_in_paddr = reqp->dr_chain_in_head.dc_buffer_paddr;
1931906Sgm89044 		reqp->dr_in_next = reqp->dr_chain_in_head.dc_next_paddr;
1932906Sgm89044 		reqp->dr_in_len = reqp->dr_chain_in_head.dc_buffer_length;
1933906Sgm89044 	} else {
1934906Sgm89044 		reqp->dr_in_paddr = NULL;
1935906Sgm89044 		reqp->dr_in_next = 0;
1936906Sgm89044 		reqp->dr_in_len = 0;
1937906Sgm89044 	}
1938906Sgm89044 
1939906Sgm89044 	if (reqp->dr_flags & DR_INPLACE) {
1940906Sgm89044 		reqp->dr_out_paddr = reqp->dr_in_paddr;
1941906Sgm89044 		reqp->dr_out_len = reqp->dr_in_len;
1942906Sgm89044 		reqp->dr_out_next = reqp->dr_in_next;
1943906Sgm89044 		return (DDI_SUCCESS);
1944906Sgm89044 	}
1945906Sgm89044 
1946906Sgm89044 	/* then the output */
1947906Sgm89044 	if (outcnt) {
1948906Sgm89044 		flags = DDI_DMA_READ | DDI_DMA_STREAMING;
1949906Sgm89044 		if ((kaddr = dca_bufdaddr_out(reqp->dr_out)) == NULL) {
1950906Sgm89044 			DBG(NULL, DWARN, "unrecognised crypto data format");
1951906Sgm89044 			(void) dca_unbindchains(reqp);
1952906Sgm89044 			return (DDI_FAILURE);
1953906Sgm89044 		}
1954906Sgm89044 		rv = dca_bindchains_one(reqp, outcnt, reqp->dr_offset +
1955906Sgm89044 		    n_chain * DESC_SIZE, kaddr, reqp->dr_chain_out_dmah,
1956906Sgm89044 		    flags, &reqp->dr_chain_out_head, &n_chain);
1957906Sgm89044 		if (rv != DDI_SUCCESS) {
1958906Sgm89044 			(void) dca_unbindchains(reqp);
1959906Sgm89044 			return (DDI_FAILURE);
1960906Sgm89044 		}
1961906Sgm89044 
1962906Sgm89044 		/* Save the first one in the chain for MCR */
1963906Sgm89044 		reqp->dr_out_paddr = reqp->dr_chain_out_head.dc_buffer_paddr;
1964906Sgm89044 		reqp->dr_out_next = reqp->dr_chain_out_head.dc_next_paddr;
1965906Sgm89044 		reqp->dr_out_len = reqp->dr_chain_out_head.dc_buffer_length;
1966906Sgm89044 	} else {
1967906Sgm89044 		reqp->dr_out_paddr = NULL;
1968906Sgm89044 		reqp->dr_out_next = 0;
1969906Sgm89044 		reqp->dr_out_len = 0;
1970906Sgm89044 	}
1971906Sgm89044 
1972906Sgm89044 	return (DDI_SUCCESS);
1973906Sgm89044 }
1974906Sgm89044 
1975906Sgm89044 /*
1976906Sgm89044  * Unbind the user buffers from the DMA handles.
1977906Sgm89044  */
1978906Sgm89044 int
1979906Sgm89044 dca_unbindchains(dca_request_t *reqp)
1980906Sgm89044 {
1981906Sgm89044 	int rv = DDI_SUCCESS;
1982906Sgm89044 	int rv1 = DDI_SUCCESS;
1983906Sgm89044 
1984906Sgm89044 	/* Clear the input chain */
1985906Sgm89044 	if (reqp->dr_chain_in_head.dc_buffer_paddr != NULL) {
1986906Sgm89044 		(void) ddi_dma_unbind_handle(reqp->dr_chain_in_dmah);
1987906Sgm89044 		reqp->dr_chain_in_head.dc_buffer_paddr = 0;
1988906Sgm89044 	}
1989906Sgm89044 
1990*5063Sgm89044 	if (reqp->dr_flags & DR_INPLACE) {
1991*5063Sgm89044 		return (rv);
1992*5063Sgm89044 	}
1993*5063Sgm89044 
1994906Sgm89044 	/* Clear the output chain */
1995906Sgm89044 	if (reqp->dr_chain_out_head.dc_buffer_paddr != NULL) {
1996906Sgm89044 		(void) ddi_dma_unbind_handle(reqp->dr_chain_out_dmah);
1997906Sgm89044 		reqp->dr_chain_out_head.dc_buffer_paddr = 0;
1998906Sgm89044 	}
1999906Sgm89044 
2000906Sgm89044 	return ((rv != DDI_SUCCESS)? rv : rv1);
2001906Sgm89044 }
2002906Sgm89044 
2003906Sgm89044 /*
2004906Sgm89044  * Build either input chain or output chain. It is single-item chain for Sparc,
2005906Sgm89044  * and possible mutiple-item chain for x86.
2006906Sgm89044  */
2007906Sgm89044 static int
2008906Sgm89044 dca_bindchains_one(dca_request_t *reqp, size_t cnt, int dr_offset,
2009906Sgm89044     caddr_t kaddr, ddi_dma_handle_t handle, uint_t flags,
2010906Sgm89044     dca_chain_t *head, int *n_chain)
2011906Sgm89044 {
2012906Sgm89044 	ddi_dma_cookie_t	c;
2013906Sgm89044 	uint_t			nc;
2014906Sgm89044 	int			rv;
2015906Sgm89044 	caddr_t			chain_kaddr_pre;
2016906Sgm89044 	caddr_t			chain_kaddr;
2017906Sgm89044 	uint32_t		chain_paddr;
2018906Sgm89044 	int 			i;
2019906Sgm89044 
2020906Sgm89044 	/* Advance past the context structure to the starting address */
2021906Sgm89044 	chain_paddr = reqp->dr_ctx_paddr + dr_offset;
2022906Sgm89044 	chain_kaddr = reqp->dr_ctx_kaddr + dr_offset;
2023906Sgm89044 
2024906Sgm89044 	/*
2025906Sgm89044 	 * Bind the kernel address to the DMA handle. On x86, the actual
2026906Sgm89044 	 * buffer is mapped into multiple physical addresses. On Sparc,
2027906Sgm89044 	 * the actual buffer is mapped into a single address.
2028906Sgm89044 	 */
2029906Sgm89044 	rv = ddi_dma_addr_bind_handle(handle,
2030906Sgm89044 	    NULL, kaddr, cnt, flags, DDI_DMA_DONTWAIT, NULL, &c, &nc);
2031906Sgm89044 	if (rv != DDI_DMA_MAPPED) {
2032906Sgm89044 		return (DDI_FAILURE);
2033906Sgm89044 	}
2034906Sgm89044 
2035906Sgm89044 	(void) ddi_dma_sync(handle, 0, cnt, DDI_DMA_SYNC_FORDEV);
2036906Sgm89044 	if ((rv = dca_check_dma_handle(reqp->dr_dca, handle,
2037906Sgm89044 	    DCA_FM_ECLASS_NONE)) != DDI_SUCCESS) {
2038906Sgm89044 		reqp->destroy = TRUE;
2039906Sgm89044 		return (rv);
2040906Sgm89044 	}
2041906Sgm89044 
2042906Sgm89044 	*n_chain = nc;
2043906Sgm89044 
2044906Sgm89044 	/* Setup the data buffer chain for DMA transfer */
2045906Sgm89044 	chain_kaddr_pre = NULL;
2046906Sgm89044 	head->dc_buffer_paddr = 0;
2047906Sgm89044 	head->dc_next_paddr = 0;
2048906Sgm89044 	head->dc_buffer_length = 0;
2049906Sgm89044 	for (i = 0; i < nc; i++) {
2050906Sgm89044 		/* PIO */
2051906Sgm89044 		PUTDESC32(reqp, chain_kaddr, DESC_BUFADDR, c.dmac_address);
2052906Sgm89044 		PUTDESC16(reqp, chain_kaddr, DESC_RSVD, 0);
2053906Sgm89044 		PUTDESC16(reqp, chain_kaddr, DESC_LENGTH, c.dmac_size);
2054906Sgm89044 
2055906Sgm89044 		/* Remember the head of the chain */
2056906Sgm89044 		if (head->dc_buffer_paddr == 0) {
2057906Sgm89044 			head->dc_buffer_paddr = c.dmac_address;
2058906Sgm89044 			head->dc_buffer_length = c.dmac_size;
2059906Sgm89044 		}
2060906Sgm89044 
2061906Sgm89044 		/* Link to the previous one if one exists */
2062906Sgm89044 		if (chain_kaddr_pre) {
2063906Sgm89044 			PUTDESC32(reqp, chain_kaddr_pre, DESC_NEXT,
2064906Sgm89044 			    chain_paddr);
2065906Sgm89044 			if (head->dc_next_paddr == 0)
2066906Sgm89044 				head->dc_next_paddr = chain_paddr;
2067906Sgm89044 		}
2068906Sgm89044 		chain_kaddr_pre = chain_kaddr;
2069906Sgm89044 
2070906Sgm89044 		/* Maintain pointers */
2071906Sgm89044 		chain_paddr += DESC_SIZE;
2072906Sgm89044 		chain_kaddr += DESC_SIZE;
2073906Sgm89044 
2074906Sgm89044 		/* Retrieve the next cookie if there is one */
2075906Sgm89044 		if (i < nc-1)
2076906Sgm89044 			ddi_dma_nextcookie(handle, &c);
2077906Sgm89044 	}
2078906Sgm89044 
2079906Sgm89044 	/* Set the next pointer in the last entry to NULL */
2080906Sgm89044 	PUTDESC32(reqp, chain_kaddr_pre, DESC_NEXT, 0);
2081906Sgm89044 
2082906Sgm89044 	return (DDI_SUCCESS);
2083906Sgm89044 }
2084906Sgm89044 
2085906Sgm89044 /*
2086906Sgm89044  * Schedule some work.
2087906Sgm89044  */
2088906Sgm89044 int
2089906Sgm89044 dca_start(dca_t *dca, dca_request_t *reqp, int mcr, int dosched)
2090906Sgm89044 {
2091906Sgm89044 	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
2092906Sgm89044 
2093906Sgm89044 	mutex_enter(&wlp->dwl_lock);
2094906Sgm89044 
2095906Sgm89044 	DBG(dca, DCHATTY, "req=%p, in=%p, out=%p, ctx=%p, ibuf=%p, obuf=%p",
2096906Sgm89044 	    reqp, reqp->dr_in, reqp->dr_out, reqp->dr_ctx_kaddr,
2097906Sgm89044 	    reqp->dr_ibuf_kaddr, reqp->dr_obuf_kaddr);
2098906Sgm89044 	DBG(dca, DCHATTY, "ctx paddr = %x, ibuf paddr = %x, obuf paddr = %x",
2099906Sgm89044 	    reqp->dr_ctx_paddr, reqp->dr_ibuf_paddr, reqp->dr_obuf_paddr);
2100906Sgm89044 	/* sync out the entire context and descriptor chains */
2101906Sgm89044 	(void) ddi_dma_sync(reqp->dr_ctx_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
2102906Sgm89044 	if (dca_check_dma_handle(dca, reqp->dr_ctx_dmah,
2103906Sgm89044 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
2104906Sgm89044 		reqp->destroy = TRUE;
2105906Sgm89044 		mutex_exit(&wlp->dwl_lock);
2106906Sgm89044 		return (CRYPTO_DEVICE_ERROR);
2107906Sgm89044 	}
2108906Sgm89044 
2109906Sgm89044 	dca_enqueue(&wlp->dwl_waitq, (dca_listnode_t *)reqp);
2110906Sgm89044 	wlp->dwl_count++;
2111906Sgm89044 	wlp->dwl_lastsubmit = ddi_get_lbolt();
2112906Sgm89044 	reqp->dr_wlp = wlp;
2113906Sgm89044 
2114906Sgm89044 	if ((wlp->dwl_count == wlp->dwl_hiwater) && (wlp->dwl_busy == 0)) {
2115906Sgm89044 		/* we are fully loaded now, let kCF know */
2116906Sgm89044 
2117906Sgm89044 		wlp->dwl_flowctl++;
2118906Sgm89044 		wlp->dwl_busy = 1;
2119906Sgm89044 
2120906Sgm89044 		crypto_prov_notify(wlp->dwl_prov, CRYPTO_PROVIDER_BUSY);
2121906Sgm89044 	}
2122906Sgm89044 
2123906Sgm89044 	if (dosched) {
2124906Sgm89044 #ifdef	SCHEDDELAY
2125906Sgm89044 		/* possibly wait for more work to arrive */
2126906Sgm89044 		if (wlp->dwl_count >= wlp->dwl_reqspermcr) {
2127906Sgm89044 			dca_schedule(dca, mcr);
2128906Sgm89044 		} else if (!wlp->dwl_schedtid) {
2129906Sgm89044 			/* wait 1 msec for more work before doing it */
2130906Sgm89044 			wlp->dwl_schedtid = timeout(dca_schedtimeout,
2131906Sgm89044 			    (void *)wlp, drv_usectohz(MSEC));
2132906Sgm89044 		}
2133906Sgm89044 #else
2134906Sgm89044 		dca_schedule(dca, mcr);
2135906Sgm89044 #endif
2136906Sgm89044 	}
2137906Sgm89044 	mutex_exit(&wlp->dwl_lock);
2138906Sgm89044 
2139906Sgm89044 	return (CRYPTO_QUEUED);
2140906Sgm89044 }
2141906Sgm89044 
2142906Sgm89044 void
2143906Sgm89044 dca_schedule(dca_t *dca, int mcr)
2144906Sgm89044 {
2145906Sgm89044 	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
2146906Sgm89044 	int		csr;
2147906Sgm89044 	int		full;
2148906Sgm89044 	uint32_t	status;
2149906Sgm89044 
2150906Sgm89044 	ASSERT(mutex_owned(&wlp->dwl_lock));
2151906Sgm89044 	/*
2152906Sgm89044 	 * If the card is draining or has an outstanding failure,
2153906Sgm89044 	 * don't schedule any more work on it right now
2154906Sgm89044 	 */
2155906Sgm89044 	if (wlp->dwl_drain || (dca->dca_flags & DCA_FAILED)) {
2156906Sgm89044 		return;
2157906Sgm89044 	}
2158906Sgm89044 
2159906Sgm89044 	if (mcr == MCR2) {
2160906Sgm89044 		csr = CSR_MCR2;
2161906Sgm89044 		full = DMASTAT_MCR2FULL;
2162906Sgm89044 	} else {
2163906Sgm89044 		csr = CSR_MCR1;
2164906Sgm89044 		full = DMASTAT_MCR1FULL;
2165906Sgm89044 	}
2166906Sgm89044 
2167906Sgm89044 	for (;;) {
2168906Sgm89044 		dca_work_t	*workp;
2169906Sgm89044 		uint32_t	offset;
2170906Sgm89044 		int		nreqs;
2171906Sgm89044 
2172906Sgm89044 		status = GETCSR(dca, CSR_DMASTAT);
2173906Sgm89044 		if (dca_check_acc_handle(dca, dca->dca_regs_handle,
2174906Sgm89044 		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS)
2175906Sgm89044 			return;
2176906Sgm89044 
2177906Sgm89044 		if ((status & full) != 0)
2178906Sgm89044 			break;
2179906Sgm89044 
2180906Sgm89044 #ifdef	SCHEDDELAY
2181906Sgm89044 		/* if there isn't enough to do, don't bother now */
2182906Sgm89044 		if ((wlp->dwl_count < wlp->dwl_reqspermcr) &&
2183906Sgm89044 		    (ddi_get_lbolt() < (wlp->dwl_lastsubmit +
2184*5063Sgm89044 		    drv_usectohz(MSEC)))) {
2185906Sgm89044 			/* wait a bit longer... */
2186906Sgm89044 			if (wlp->dwl_schedtid == 0) {
2187906Sgm89044 				wlp->dwl_schedtid = timeout(dca_schedtimeout,
2188906Sgm89044 				    (void *)wlp, drv_usectohz(MSEC));
2189906Sgm89044 			}
2190906Sgm89044 			return;
2191906Sgm89044 		}
2192906Sgm89044 #endif
2193906Sgm89044 
2194906Sgm89044 		/* grab a work structure */
2195906Sgm89044 		workp = dca_getwork(dca, mcr);
2196906Sgm89044 
2197906Sgm89044 		if (workp == NULL) {
2198906Sgm89044 			/*
2199906Sgm89044 			 * There must be work ready to be reclaimed,
2200906Sgm89044 			 * in this case, since the chip can only hold
2201906Sgm89044 			 * less work outstanding than there are total.
2202906Sgm89044 			 */
2203906Sgm89044 			dca_reclaim(dca, mcr);
2204906Sgm89044 			continue;
2205906Sgm89044 		}
2206906Sgm89044 
2207906Sgm89044 		nreqs = 0;
2208906Sgm89044 		offset = MCR_CTXADDR;
2209906Sgm89044 
2210906Sgm89044 		while (nreqs < wlp->dwl_reqspermcr) {
2211906Sgm89044 			dca_request_t	*reqp;
2212906Sgm89044 
2213906Sgm89044 			reqp = (dca_request_t *)dca_dequeue(&wlp->dwl_waitq);
2214906Sgm89044 			if (reqp == NULL) {
2215906Sgm89044 				/* nothing left to process */
2216906Sgm89044 				break;
2217906Sgm89044 			}
2218906Sgm89044 			/*
2219906Sgm89044 			 * Update flow control.
2220906Sgm89044 			 */
2221906Sgm89044 			wlp->dwl_count--;
2222906Sgm89044 			if ((wlp->dwl_count == wlp->dwl_lowater) &&
2223906Sgm89044 			    (wlp->dwl_busy))  {
2224906Sgm89044 				wlp->dwl_busy = 0;
2225906Sgm89044 				crypto_prov_notify(wlp->dwl_prov,
2226906Sgm89044 				    CRYPTO_PROVIDER_READY);
2227906Sgm89044 			}
2228906Sgm89044 
2229906Sgm89044 			/*
2230906Sgm89044 			 * Context address.
2231906Sgm89044 			 */
2232906Sgm89044 			PUTMCR32(workp, offset, reqp->dr_ctx_paddr);
2233906Sgm89044 			offset += 4;
2234906Sgm89044 
2235906Sgm89044 			/*
2236906Sgm89044 			 * Input chain.
2237906Sgm89044 			 */
2238906Sgm89044 			/* input buffer address */
2239906Sgm89044 			PUTMCR32(workp, offset, reqp->dr_in_paddr);
2240906Sgm89044 			offset += 4;
2241906Sgm89044 			/* next input buffer entry */
2242906Sgm89044 			PUTMCR32(workp, offset, reqp->dr_in_next);
2243906Sgm89044 			offset += 4;
2244906Sgm89044 			/* input buffer length */
2245906Sgm89044 			PUTMCR16(workp, offset, reqp->dr_in_len);
2246906Sgm89044 			offset += 2;
2247906Sgm89044 			/* zero the reserved field */
2248906Sgm89044 			PUTMCR16(workp, offset, 0);
2249906Sgm89044 			offset += 2;
2250906Sgm89044 
2251906Sgm89044 			/*
2252906Sgm89044 			 * Overall length.
2253906Sgm89044 			 */
2254906Sgm89044 			/* reserved field */
2255906Sgm89044 			PUTMCR16(workp, offset, 0);
2256906Sgm89044 			offset += 2;
2257906Sgm89044 			/* total packet length */
2258906Sgm89044 			PUTMCR16(workp, offset, reqp->dr_pkt_length);
2259906Sgm89044 			offset += 2;
2260906Sgm89044 
2261906Sgm89044 			/*
2262906Sgm89044 			 * Output chain.
2263906Sgm89044 			 */
2264906Sgm89044 			/* output buffer address */
2265906Sgm89044 			PUTMCR32(workp, offset, reqp->dr_out_paddr);
2266906Sgm89044 			offset += 4;
2267906Sgm89044 			/* next output buffer entry */
2268906Sgm89044 			PUTMCR32(workp, offset, reqp->dr_out_next);
2269906Sgm89044 			offset += 4;
2270906Sgm89044 			/* output buffer length */
2271906Sgm89044 			PUTMCR16(workp, offset, reqp->dr_out_len);
2272906Sgm89044 			offset += 2;
2273906Sgm89044 			/* zero the reserved field */
2274906Sgm89044 			PUTMCR16(workp, offset, 0);
2275906Sgm89044 			offset += 2;
2276906Sgm89044 
2277906Sgm89044 			/*
2278906Sgm89044 			 * Note submission.
2279906Sgm89044 			 */
2280906Sgm89044 			workp->dw_reqs[nreqs] = reqp;
2281906Sgm89044 			nreqs++;
2282906Sgm89044 		}
2283906Sgm89044 
2284906Sgm89044 		if (nreqs == 0) {
2285906Sgm89044 			/* nothing in the queue! */
2286906Sgm89044 			dca_freework(workp);
2287906Sgm89044 			return;
2288906Sgm89044 		}
2289906Sgm89044 
2290906Sgm89044 		wlp->dwl_submit++;
2291906Sgm89044 
2292906Sgm89044 		PUTMCR16(workp, MCR_FLAGS, 0);
2293906Sgm89044 		PUTMCR16(workp, MCR_COUNT, nreqs);
2294906Sgm89044 
2295906Sgm89044 		DBG(dca, DCHATTY,
2296906Sgm89044 		    "posting work (phys %x, virt 0x%p) (%d reqs) to MCR%d",
2297906Sgm89044 		    workp->dw_mcr_paddr, workp->dw_mcr_kaddr,
2298906Sgm89044 		    nreqs, mcr);
2299906Sgm89044 
2300906Sgm89044 		workp->dw_lbolt = ddi_get_lbolt();
2301906Sgm89044 		/* Make sure MCR is synced out to device. */
2302906Sgm89044 		(void) ddi_dma_sync(workp->dw_mcr_dmah, 0, 0,
2303*5063Sgm89044 		    DDI_DMA_SYNC_FORDEV);
2304906Sgm89044 		if (dca_check_dma_handle(dca, workp->dw_mcr_dmah,
2305906Sgm89044 		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
2306906Sgm89044 			dca_destroywork(workp);
2307906Sgm89044 			return;
2308906Sgm89044 		}
2309906Sgm89044 
2310906Sgm89044 		PUTCSR(dca, csr, workp->dw_mcr_paddr);
2311906Sgm89044 		if (dca_check_acc_handle(dca, dca->dca_regs_handle,
2312906Sgm89044 		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
2313906Sgm89044 			dca_destroywork(workp);
2314906Sgm89044 			return;
2315906Sgm89044 		} else {
2316906Sgm89044 			dca_enqueue(&wlp->dwl_runq, (dca_listnode_t *)workp);
2317906Sgm89044 		}
2318906Sgm89044 
2319906Sgm89044 		DBG(dca, DCHATTY, "posted");
2320906Sgm89044 	}
2321906Sgm89044 }
2322906Sgm89044 
2323906Sgm89044 /*
2324906Sgm89044  * Reclaim completed work, called in interrupt context.
2325906Sgm89044  */
2326906Sgm89044 void
2327906Sgm89044 dca_reclaim(dca_t *dca, int mcr)
2328906Sgm89044 {
2329906Sgm89044 	dca_worklist_t	*wlp = WORKLIST(dca, mcr);
2330906Sgm89044 	dca_work_t	*workp;
2331906Sgm89044 	ushort_t	flags;
2332906Sgm89044 	int		nreclaimed = 0;
2333906Sgm89044 	int		i;
2334906Sgm89044 
2335906Sgm89044 	DBG(dca, DRECLAIM, "worklist = 0x%p (MCR%d)", wlp, mcr);
2336906Sgm89044 	ASSERT(mutex_owned(&wlp->dwl_lock));
2337906Sgm89044 	/*
2338906Sgm89044 	 * For each MCR in the submitted (runq), we check to see if
2339906Sgm89044 	 * it has been processed.  If so, then we note each individual
2340906Sgm89044 	 * job in the MCR, and and do the completion processing for
2341906Sgm89044 	 * each of such job.
2342906Sgm89044 	 */
2343906Sgm89044 	for (;;) {
2344906Sgm89044 
2345906Sgm89044 		workp = (dca_work_t *)dca_peekqueue(&wlp->dwl_runq);
2346906Sgm89044 		if (workp == NULL) {
2347906Sgm89044 			break;
2348906Sgm89044 		}
2349906Sgm89044 
2350906Sgm89044 		/* only sync the MCR flags, since that's all we need */
2351906Sgm89044 		(void) ddi_dma_sync(workp->dw_mcr_dmah, 0, 4,
2352*5063Sgm89044 		    DDI_DMA_SYNC_FORKERNEL);
2353906Sgm89044 		if (dca_check_dma_handle(dca, workp->dw_mcr_dmah,
2354906Sgm89044 		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
2355906Sgm89044 			dca_rmqueue((dca_listnode_t *)workp);
2356906Sgm89044 			dca_destroywork(workp);
2357906Sgm89044 			return;
2358906Sgm89044 		}
2359906Sgm89044 
2360906Sgm89044 		flags = GETMCR16(workp, MCR_FLAGS);
2361906Sgm89044 		if ((flags & MCRFLAG_FINISHED) == 0) {
2362906Sgm89044 			/* chip is still working on it */
2363906Sgm89044 			DBG(dca, DRECLAIM,
2364906Sgm89044 			    "chip still working on it (MCR%d)", mcr);
2365906Sgm89044 			break;
2366906Sgm89044 		}
2367906Sgm89044 
2368906Sgm89044 		/* its really for us, so remove it from the queue */
2369906Sgm89044 		dca_rmqueue((dca_listnode_t *)workp);
2370906Sgm89044 
2371906Sgm89044 		/* if we were draining, signal on the cv */
2372906Sgm89044 		if (wlp->dwl_drain && QEMPTY(&wlp->dwl_runq)) {
2373906Sgm89044 			cv_signal(&wlp->dwl_cv);
2374906Sgm89044 		}
2375906Sgm89044 
2376906Sgm89044 		/* update statistics, done under the lock */
2377906Sgm89044 		for (i = 0; i < wlp->dwl_reqspermcr; i++) {
2378906Sgm89044 			dca_request_t *reqp = workp->dw_reqs[i];
2379906Sgm89044 			if (reqp == NULL) {
2380906Sgm89044 				continue;
2381906Sgm89044 			}
2382906Sgm89044 			if (reqp->dr_byte_stat >= 0) {
2383906Sgm89044 				dca->dca_stats[reqp->dr_byte_stat] +=
2384906Sgm89044 				    reqp->dr_pkt_length;
2385906Sgm89044 			}
2386906Sgm89044 			if (reqp->dr_job_stat >= 0) {
2387906Sgm89044 				dca->dca_stats[reqp->dr_job_stat]++;
2388906Sgm89044 			}
2389906Sgm89044 		}
2390906Sgm89044 		mutex_exit(&wlp->dwl_lock);
2391906Sgm89044 
2392906Sgm89044 		for (i = 0; i < wlp->dwl_reqspermcr; i++) {
2393906Sgm89044 			dca_request_t *reqp = workp->dw_reqs[i];
2394906Sgm89044 
2395906Sgm89044 			if (reqp == NULL) {
2396906Sgm89044 				continue;
2397906Sgm89044 			}
2398906Sgm89044 
2399906Sgm89044 			/* Do the callback. */
2400906Sgm89044 			workp->dw_reqs[i] = NULL;
2401906Sgm89044 			dca_done(reqp, CRYPTO_SUCCESS);
2402906Sgm89044 
2403906Sgm89044 			nreclaimed++;
2404906Sgm89044 		}
2405906Sgm89044 
2406906Sgm89044 		/* now we can release the work */
2407906Sgm89044 		dca_freework(workp);
24083124Sqs148142 
24093124Sqs148142 		mutex_enter(&wlp->dwl_lock);
2410906Sgm89044 	}
2411906Sgm89044 	DBG(dca, DRECLAIM, "reclaimed %d cmds", nreclaimed);
2412906Sgm89044 }
2413906Sgm89044 
2414906Sgm89044 int
2415906Sgm89044 dca_length(crypto_data_t *cdata)
2416906Sgm89044 {
2417906Sgm89044 	return (cdata->cd_length);
2418906Sgm89044 }
2419906Sgm89044 
2420906Sgm89044 /*
2421906Sgm89044  * This is the callback function called from the interrupt when a kCF job
2422906Sgm89044  * completes.  It does some driver-specific things, and then calls the
2423906Sgm89044  * kCF-provided callback.  Finally, it cleans up the state for the work
2424906Sgm89044  * request and drops the reference count to allow for DR.
2425906Sgm89044  */
2426906Sgm89044 void
2427906Sgm89044 dca_done(dca_request_t *reqp, int err)
2428906Sgm89044 {
2429906Sgm89044 	uint64_t	ena = 0;
2430906Sgm89044 
2431906Sgm89044 	/* unbind any chains we were using */
2432906Sgm89044 	if (dca_unbindchains(reqp) != DDI_SUCCESS) {
2433906Sgm89044 		/* DMA failure */
2434906Sgm89044 		ena = dca_ena(ena);
2435906Sgm89044 		dca_failure(reqp->dr_dca, DDI_DATAPATH_FAULT,
2436906Sgm89044 		    DCA_FM_ECLASS_NONE, ena, CRYPTO_DEVICE_ERROR,
2437906Sgm89044 		    "fault on buffer DMA handle");
2438906Sgm89044 		if (err == CRYPTO_SUCCESS) {
2439906Sgm89044 			err = CRYPTO_DEVICE_ERROR;
2440906Sgm89044 		}
2441906Sgm89044 	}
2442906Sgm89044 
2443906Sgm89044 	if (reqp->dr_callback != NULL) {
2444906Sgm89044 		reqp->dr_callback(reqp, err);
2445906Sgm89044 	} else {
2446906Sgm89044 		dca_freereq(reqp);
2447906Sgm89044 	}
2448906Sgm89044 }
2449906Sgm89044 
2450906Sgm89044 /*
2451906Sgm89044  * Call this when a failure is detected.  It will reset the chip,
2452906Sgm89044  * log a message, alert kCF, and mark jobs in the runq as failed.
2453906Sgm89044  */
2454906Sgm89044 /* ARGSUSED */
2455906Sgm89044 void
2456906Sgm89044 dca_failure(dca_t *dca, ddi_fault_location_t loc, dca_fma_eclass_t index,
2457906Sgm89044     uint64_t ena, int errno, char *mess, ...)
2458906Sgm89044 {
2459906Sgm89044 	va_list	ap;
2460906Sgm89044 	char	buf[256];
2461906Sgm89044 	int	mcr;
2462906Sgm89044 	char	*eclass;
2463906Sgm89044 	int	have_mutex;
2464906Sgm89044 
2465906Sgm89044 	va_start(ap, mess);
2466906Sgm89044 	(void) vsprintf(buf, mess, ap);
2467906Sgm89044 	va_end(ap);
2468906Sgm89044 
2469906Sgm89044 	eclass = dca_fma_eclass_string(dca->dca_model, index);
2470906Sgm89044 
2471906Sgm89044 	if (DDI_FM_EREPORT_CAP(dca->fm_capabilities) &&
2472906Sgm89044 	    index != DCA_FM_ECLASS_NONE) {
2473906Sgm89044 		ddi_fm_ereport_post(dca->dca_dip, eclass, ena,
2474906Sgm89044 		    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8,
2475906Sgm89044 		    FM_EREPORT_VERS0, NULL);
2476906Sgm89044 
2477906Sgm89044 		/* Report the impact of the failure to the DDI. */
2478906Sgm89044 		ddi_fm_service_impact(dca->dca_dip, DDI_SERVICE_LOST);
2479906Sgm89044 	} else {
2480906Sgm89044 		/* Just log the error string to the message log */
2481906Sgm89044 		dca_error(dca, buf);
2482906Sgm89044 	}
2483906Sgm89044 
2484906Sgm89044 	/*
2485906Sgm89044 	 * Indicate a failure (keeps schedule from running).
2486906Sgm89044 	 */
2487906Sgm89044 	dca->dca_flags |= DCA_FAILED;
2488906Sgm89044 
2489906Sgm89044 	/*
2490906Sgm89044 	 * Reset the chip.  This should also have as a side effect, the
2491906Sgm89044 	 * disabling of all interrupts from the device.
2492906Sgm89044 	 */
2493906Sgm89044 	(void) dca_reset(dca, 1);
2494906Sgm89044 
2495906Sgm89044 	/*
2496906Sgm89044 	 * Report the failure to kCF.
2497906Sgm89044 	 */
2498906Sgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
2499906Sgm89044 		if (WORKLIST(dca, mcr)->dwl_prov) {
2500906Sgm89044 			crypto_prov_notify(WORKLIST(dca, mcr)->dwl_prov,
2501906Sgm89044 			    CRYPTO_PROVIDER_FAILED);
2502906Sgm89044 		}
2503906Sgm89044 	}
2504906Sgm89044 
2505906Sgm89044 	/*
2506906Sgm89044 	 * Return jobs not sent to hardware back to kCF.
2507906Sgm89044 	 */
2508906Sgm89044 	dca_rejectjobs(dca);
2509906Sgm89044 
2510906Sgm89044 	/*
2511906Sgm89044 	 * From this point on, no new work should be arriving, and the
2512906Sgm89044 	 * chip should not be doing any active DMA.
2513906Sgm89044 	 */
2514906Sgm89044 
2515906Sgm89044 	/*
2516906Sgm89044 	 * Now find all the work submitted to the device and fail
2517906Sgm89044 	 * them.
2518906Sgm89044 	 */
2519906Sgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
2520906Sgm89044 		dca_worklist_t	*wlp;
2521906Sgm89044 		int		i;
2522906Sgm89044 
2523906Sgm89044 		wlp = WORKLIST(dca, mcr);
2524906Sgm89044 
2525906Sgm89044 		if (wlp == NULL || wlp->dwl_waitq.dl_prev == NULL) {
2526906Sgm89044 			continue;
2527906Sgm89044 		}
2528906Sgm89044 		for (;;) {
2529906Sgm89044 			dca_work_t	*workp;
2530906Sgm89044 
2531906Sgm89044 			have_mutex = mutex_tryenter(&wlp->dwl_lock);
2532906Sgm89044 			workp = (dca_work_t *)dca_dequeue(&wlp->dwl_runq);
2533906Sgm89044 			if (workp == NULL) {
2534906Sgm89044 				if (have_mutex)
2535906Sgm89044 					mutex_exit(&wlp->dwl_lock);
2536906Sgm89044 				break;
2537906Sgm89044 			}
2538906Sgm89044 			mutex_exit(&wlp->dwl_lock);
2539906Sgm89044 
2540906Sgm89044 			/*
2541906Sgm89044 			 * Free up requests
2542906Sgm89044 			 */
2543906Sgm89044 			for (i = 0; i < wlp->dwl_reqspermcr; i++) {
2544906Sgm89044 				dca_request_t *reqp = workp->dw_reqs[i];
2545906Sgm89044 				if (reqp) {
2546*5063Sgm89044 					dca_done(reqp, errno);
2547906Sgm89044 					workp->dw_reqs[i] = NULL;
2548906Sgm89044 				}
2549906Sgm89044 			}
2550906Sgm89044 
2551906Sgm89044 			mutex_enter(&wlp->dwl_lock);
2552906Sgm89044 			/*
2553906Sgm89044 			 * If waiting to drain, signal on the waiter.
2554906Sgm89044 			 */
2555906Sgm89044 			if (wlp->dwl_drain && QEMPTY(&wlp->dwl_runq)) {
2556906Sgm89044 				cv_signal(&wlp->dwl_cv);
2557906Sgm89044 			}
2558906Sgm89044 
2559906Sgm89044 			/*
2560906Sgm89044 			 * Return the work and request structures to
2561906Sgm89044 			 * the free pool.
2562906Sgm89044 			 */
2563906Sgm89044 			dca_freework(workp);
2564906Sgm89044 			if (have_mutex)
2565906Sgm89044 				mutex_exit(&wlp->dwl_lock);
2566906Sgm89044 		}
2567906Sgm89044 	}
2568906Sgm89044 
2569906Sgm89044 }
2570906Sgm89044 
2571906Sgm89044 #ifdef	SCHEDDELAY
2572906Sgm89044 /*
2573906Sgm89044  * Reschedule worklist as needed.
2574906Sgm89044  */
2575906Sgm89044 void
2576906Sgm89044 dca_schedtimeout(void *arg)
2577906Sgm89044 {
2578906Sgm89044 	dca_worklist_t	*wlp = (dca_worklist_t *)arg;
2579906Sgm89044 	mutex_enter(&wlp->dwl_lock);
2580906Sgm89044 	wlp->dwl_schedtid = 0;
2581906Sgm89044 	dca_schedule(wlp->dwl_dca, wlp->dwl_mcr);
2582906Sgm89044 	mutex_exit(&wlp->dwl_lock);
2583906Sgm89044 }
2584906Sgm89044 #endif
2585906Sgm89044 
2586906Sgm89044 /*
2587906Sgm89044  * Check for stalled jobs.
2588906Sgm89044  */
2589906Sgm89044 void
2590906Sgm89044 dca_jobtimeout(void *arg)
2591906Sgm89044 {
2592906Sgm89044 	int		mcr;
2593906Sgm89044 	dca_t		*dca = (dca_t *)arg;
2594906Sgm89044 	int		hung = 0;
2595906Sgm89044 
2596906Sgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
2597906Sgm89044 		dca_worklist_t	*wlp = WORKLIST(dca, mcr);
2598906Sgm89044 		dca_work_t	*workp;
2599906Sgm89044 		clock_t		when;
2600906Sgm89044 
2601906Sgm89044 		mutex_enter(&wlp->dwl_lock);
2602906Sgm89044 		when = ddi_get_lbolt();
2603906Sgm89044 
2604906Sgm89044 		workp = (dca_work_t *)dca_peekqueue(&wlp->dwl_runq);
2605906Sgm89044 		if (workp == NULL) {
2606906Sgm89044 			/* nothing sitting in the queue */
2607906Sgm89044 			mutex_exit(&wlp->dwl_lock);
2608906Sgm89044 			continue;
2609906Sgm89044 		}
2610906Sgm89044 
2611906Sgm89044 		if ((when - workp->dw_lbolt) < drv_usectohz(STALETIME)) {
2612906Sgm89044 			/* request has been queued for less than STALETIME */
2613906Sgm89044 			mutex_exit(&wlp->dwl_lock);
2614906Sgm89044 			continue;
2615906Sgm89044 		}
2616906Sgm89044 
2617906Sgm89044 		/* job has been sitting around for over 1 second, badness */
2618906Sgm89044 		DBG(dca, DWARN, "stale job (0x%p) found in MCR%d!", workp,
2619906Sgm89044 		    mcr);
2620906Sgm89044 
2621906Sgm89044 		/* put it back in the queue, until we reset the chip */
2622906Sgm89044 		hung++;
2623906Sgm89044 		mutex_exit(&wlp->dwl_lock);
2624906Sgm89044 	}
2625906Sgm89044 
2626906Sgm89044 	if (hung) {
2627906Sgm89044 		dca_failure(dca, DDI_DEVICE_FAULT,
2628906Sgm89044 		    DCA_FM_ECLASS_HW_TIMEOUT, dca_ena(0), CRYPTO_DEVICE_ERROR,
2629906Sgm89044 		    "timeout processing job.)");
2630906Sgm89044 	}
2631906Sgm89044 
2632906Sgm89044 	/* reschedule ourself */
2633906Sgm89044 	mutex_enter(&dca->dca_intrlock);
2634906Sgm89044 	if (dca->dca_jobtid == 0) {
2635906Sgm89044 		/* timeout has been canceled, prior to DR */
2636906Sgm89044 		mutex_exit(&dca->dca_intrlock);
2637906Sgm89044 		return;
2638906Sgm89044 	}
2639906Sgm89044 
2640906Sgm89044 	/* check again in 1 second */
2641906Sgm89044 	dca->dca_jobtid = timeout(dca_jobtimeout, arg,
2642906Sgm89044 	    drv_usectohz(SECOND));
2643906Sgm89044 	mutex_exit(&dca->dca_intrlock);
2644906Sgm89044 }
2645906Sgm89044 
2646906Sgm89044 /*
2647906Sgm89044  * This returns all jobs back to kCF.  It assumes that processing
2648906Sgm89044  * on the worklist has halted.
2649906Sgm89044  */
2650906Sgm89044 void
2651906Sgm89044 dca_rejectjobs(dca_t *dca)
2652906Sgm89044 {
2653906Sgm89044 	int mcr;
2654906Sgm89044 	int have_mutex;
2655906Sgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
2656906Sgm89044 		dca_worklist_t	*wlp = WORKLIST(dca, mcr);
2657906Sgm89044 		dca_request_t	*reqp;
2658906Sgm89044 
2659906Sgm89044 		if (wlp == NULL || wlp->dwl_waitq.dl_prev == NULL) {
2660906Sgm89044 			continue;
2661906Sgm89044 		}
2662906Sgm89044 		have_mutex = mutex_tryenter(&wlp->dwl_lock);
2663906Sgm89044 		for (;;) {
2664906Sgm89044 			reqp = (dca_request_t *)dca_unqueue(&wlp->dwl_waitq);
2665906Sgm89044 			if (reqp == NULL) {
2666906Sgm89044 				break;
2667906Sgm89044 			}
2668906Sgm89044 			/* update flow control */
2669906Sgm89044 			wlp->dwl_count--;
2670906Sgm89044 			if ((wlp->dwl_count == wlp->dwl_lowater) &&
2671906Sgm89044 			    (wlp->dwl_busy))  {
2672906Sgm89044 				wlp->dwl_busy = 0;
2673906Sgm89044 				crypto_prov_notify(wlp->dwl_prov,
2674906Sgm89044 				    CRYPTO_PROVIDER_READY);
2675906Sgm89044 			}
2676906Sgm89044 			mutex_exit(&wlp->dwl_lock);
2677906Sgm89044 
2678906Sgm89044 			(void) dca_unbindchains(reqp);
2679906Sgm89044 			reqp->dr_callback(reqp, EAGAIN);
2680906Sgm89044 			mutex_enter(&wlp->dwl_lock);
2681906Sgm89044 		}
2682906Sgm89044 		if (have_mutex)
2683906Sgm89044 			mutex_exit(&wlp->dwl_lock);
2684906Sgm89044 	}
2685906Sgm89044 }
2686906Sgm89044 
2687906Sgm89044 int
2688906Sgm89044 dca_drain(dca_t *dca)
2689906Sgm89044 {
2690906Sgm89044 	int mcr;
2691906Sgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
2692906Sgm89044 #ifdef	SCHEDDELAY
2693906Sgm89044 		timeout_id_t	tid;
2694906Sgm89044 #endif
2695906Sgm89044 		dca_worklist_t *wlp = WORKLIST(dca, mcr);
2696906Sgm89044 
2697906Sgm89044 		mutex_enter(&wlp->dwl_lock);
2698906Sgm89044 		wlp->dwl_drain = 1;
2699906Sgm89044 
2700906Sgm89044 		/* give it up to a second to drain from the chip */
2701906Sgm89044 		if (!QEMPTY(&wlp->dwl_runq)) {
2702906Sgm89044 			(void) cv_timedwait(&wlp->dwl_cv, &wlp->dwl_lock,
2703906Sgm89044 			    ddi_get_time() + drv_usectohz(STALETIME));
2704906Sgm89044 
2705906Sgm89044 			if (!QEMPTY(&wlp->dwl_runq)) {
2706906Sgm89044 				dca_error(dca, "unable to drain device");
2707906Sgm89044 				mutex_exit(&wlp->dwl_lock);
2708906Sgm89044 				dca_undrain(dca);
2709906Sgm89044 				return (EBUSY);
2710906Sgm89044 			}
2711906Sgm89044 		}
2712906Sgm89044 
2713906Sgm89044 #ifdef	SCHEDDELAY
2714906Sgm89044 		tid = wlp->dwl_schedtid;
2715906Sgm89044 		mutex_exit(&wlp->dwl_lock);
2716906Sgm89044 
2717906Sgm89044 		/*
2718906Sgm89044 		 * untimeout outside the lock -- this is safe because we
2719906Sgm89044 		 * have set the drain flag, so dca_schedule() will not
2720906Sgm89044 		 * reschedule another timeout
2721906Sgm89044 		 */
2722906Sgm89044 		if (tid) {
2723906Sgm89044 			untimeout(tid);
2724906Sgm89044 		}
2725906Sgm89044 #else
2726906Sgm89044 		mutex_exit(&wlp->dwl_lock);
2727906Sgm89044 #endif
2728906Sgm89044 	}
2729906Sgm89044 	return (0);
2730906Sgm89044 }
2731906Sgm89044 
2732906Sgm89044 void
2733906Sgm89044 dca_undrain(dca_t *dca)
2734906Sgm89044 {
2735906Sgm89044 	int	mcr;
2736906Sgm89044 
2737906Sgm89044 	for (mcr = MCR1; mcr <= MCR2; mcr++) {
2738906Sgm89044 		dca_worklist_t	*wlp = WORKLIST(dca, mcr);
2739906Sgm89044 		mutex_enter(&wlp->dwl_lock);
2740906Sgm89044 		wlp->dwl_drain = 0;
2741906Sgm89044 		dca_schedule(dca, mcr);
2742906Sgm89044 		mutex_exit(&wlp->dwl_lock);
2743906Sgm89044 	}
2744906Sgm89044 }
2745906Sgm89044 
2746906Sgm89044 /*
2747906Sgm89044  * Duplicate the crypto_data_t structure, but point to the original
2748906Sgm89044  * buffers.
2749906Sgm89044  */
2750906Sgm89044 int
2751906Sgm89044 dca_dupcrypto(crypto_data_t *input, crypto_data_t *ninput)
2752906Sgm89044 {
2753906Sgm89044 	ninput->cd_format = input->cd_format;
2754906Sgm89044 	ninput->cd_offset = input->cd_offset;
2755906Sgm89044 	ninput->cd_length = input->cd_length;
2756906Sgm89044 	ninput->cd_miscdata = input->cd_miscdata;
2757906Sgm89044 
2758906Sgm89044 	switch (input->cd_format) {
2759906Sgm89044 	case CRYPTO_DATA_RAW:
2760906Sgm89044 		ninput->cd_raw.iov_base = input->cd_raw.iov_base;
2761906Sgm89044 		ninput->cd_raw.iov_len = input->cd_raw.iov_len;
2762906Sgm89044 		break;
2763906Sgm89044 
2764906Sgm89044 	case CRYPTO_DATA_UIO:
2765906Sgm89044 		ninput->cd_uio = input->cd_uio;
2766906Sgm89044 		break;
2767906Sgm89044 
2768906Sgm89044 	case CRYPTO_DATA_MBLK:
2769906Sgm89044 		ninput->cd_mp = input->cd_mp;
2770906Sgm89044 		break;
2771906Sgm89044 
2772906Sgm89044 	default:
2773906Sgm89044 		DBG(NULL, DWARN,
2774906Sgm89044 		    "dca_dupcrypto: unrecognised crypto data format");
2775906Sgm89044 		return (CRYPTO_FAILED);
2776906Sgm89044 	}
2777906Sgm89044 
2778906Sgm89044 	return (CRYPTO_SUCCESS);
2779906Sgm89044 }
2780906Sgm89044 
2781906Sgm89044 /*
2782906Sgm89044  * Performs validation checks on the input and output data structures.
2783906Sgm89044  */
2784906Sgm89044 int
2785906Sgm89044 dca_verifyio(crypto_data_t *input, crypto_data_t *output)
2786906Sgm89044 {
2787906Sgm89044 	int	rv = CRYPTO_SUCCESS;
2788906Sgm89044 
2789906Sgm89044 	switch (input->cd_format) {
2790906Sgm89044 	case CRYPTO_DATA_RAW:
2791906Sgm89044 		break;
2792906Sgm89044 
2793906Sgm89044 	case CRYPTO_DATA_UIO:
2794906Sgm89044 		/* we support only kernel buffer */
2795906Sgm89044 		if (input->cd_uio->uio_segflg != UIO_SYSSPACE) {
2796906Sgm89044 			DBG(NULL, DWARN, "non kernel input uio buffer");
2797906Sgm89044 			rv = CRYPTO_ARGUMENTS_BAD;
2798906Sgm89044 		}
2799906Sgm89044 		break;
2800906Sgm89044 
2801906Sgm89044 	case CRYPTO_DATA_MBLK:
2802906Sgm89044 		break;
2803906Sgm89044 
2804906Sgm89044 	default:
2805906Sgm89044 		DBG(NULL, DWARN, "unrecognised input crypto data format");
2806906Sgm89044 		rv = CRYPTO_ARGUMENTS_BAD;
2807906Sgm89044 	}
2808906Sgm89044 
2809906Sgm89044 	switch (output->cd_format) {
2810906Sgm89044 	case CRYPTO_DATA_RAW:
2811906Sgm89044 		break;
2812906Sgm89044 
2813906Sgm89044 	case CRYPTO_DATA_UIO:
2814906Sgm89044 		/* we support only kernel buffer */
2815906Sgm89044 		if (output->cd_uio->uio_segflg != UIO_SYSSPACE) {
2816906Sgm89044 			DBG(NULL, DWARN, "non kernel output uio buffer");
2817906Sgm89044 			rv = CRYPTO_ARGUMENTS_BAD;
2818906Sgm89044 		}
2819906Sgm89044 		break;
2820906Sgm89044 
2821906Sgm89044 	case CRYPTO_DATA_MBLK:
2822906Sgm89044 		break;
2823906Sgm89044 
2824906Sgm89044 	default:
2825906Sgm89044 		DBG(NULL, DWARN, "unrecognised output crypto data format");
2826906Sgm89044 		rv = CRYPTO_ARGUMENTS_BAD;
2827906Sgm89044 	}
2828906Sgm89044 
2829906Sgm89044 	return (rv);
2830906Sgm89044 }
2831906Sgm89044 
2832906Sgm89044 /*
2833906Sgm89044  * data: source crypto_data_t struct
2834906Sgm89044  * off:	offset into the source before commencing copy
2835906Sgm89044  * count: the amount of data to copy
2836906Sgm89044  * dest: destination buffer
2837906Sgm89044  */
2838906Sgm89044 int
2839906Sgm89044 dca_getbufbytes(crypto_data_t *data, size_t off, int count, uchar_t *dest)
2840906Sgm89044 {
2841906Sgm89044 	int rv = CRYPTO_SUCCESS;
2842906Sgm89044 	uio_t *uiop;
2843906Sgm89044 	uint_t vec_idx;
2844906Sgm89044 	size_t cur_len;
2845906Sgm89044 	mblk_t *mp;
2846906Sgm89044 
2847906Sgm89044 	if (count == 0) {
2848906Sgm89044 		/* We don't want anything so we're done. */
2849906Sgm89044 		return (rv);
2850906Sgm89044 	}
2851906Sgm89044 
2852906Sgm89044 	/*
2853906Sgm89044 	 * Sanity check that we haven't specified a length greater than the
2854906Sgm89044 	 * offset adjusted size of the buffer.
2855906Sgm89044 	 */
2856906Sgm89044 	if (count > (data->cd_length - off)) {
2857906Sgm89044 		return (CRYPTO_DATA_LEN_RANGE);
2858906Sgm89044 	}
2859906Sgm89044 
2860906Sgm89044 	/* Add the internal crypto_data offset to the requested offset. */
2861906Sgm89044 	off += data->cd_offset;
2862906Sgm89044 
2863906Sgm89044 	switch (data->cd_format) {
2864906Sgm89044 	case CRYPTO_DATA_RAW:
2865906Sgm89044 		bcopy(data->cd_raw.iov_base + off, dest, count);
2866906Sgm89044 		break;
2867906Sgm89044 
2868906Sgm89044 	case CRYPTO_DATA_UIO:
2869906Sgm89044 		/*
2870906Sgm89044 		 * Jump to the first iovec containing data to be
2871906Sgm89044 		 * processed.
2872906Sgm89044 		 */
2873906Sgm89044 		uiop = data->cd_uio;
2874906Sgm89044 		for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
2875906Sgm89044 		    off >= uiop->uio_iov[vec_idx].iov_len;
2876*5063Sgm89044 		    off -= uiop->uio_iov[vec_idx++].iov_len)
2877*5063Sgm89044 			;
2878906Sgm89044 		if (vec_idx == uiop->uio_iovcnt) {
2879906Sgm89044 			/*
2880906Sgm89044 			 * The caller specified an offset that is larger than
2881906Sgm89044 			 * the total size of the buffers it provided.
2882906Sgm89044 			 */
2883906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
2884906Sgm89044 		}
2885906Sgm89044 
2886906Sgm89044 		/*
2887906Sgm89044 		 * Now process the iovecs.
2888906Sgm89044 		 */
2889906Sgm89044 		while (vec_idx < uiop->uio_iovcnt && count > 0) {
2890906Sgm89044 			cur_len = min(uiop->uio_iov[vec_idx].iov_len -
2891906Sgm89044 			    off, count);
2892906Sgm89044 			bcopy(uiop->uio_iov[vec_idx].iov_base + off, dest,
2893906Sgm89044 			    cur_len);
2894906Sgm89044 			count -= cur_len;
2895906Sgm89044 			dest += cur_len;
2896906Sgm89044 			vec_idx++;
2897906Sgm89044 			off = 0;
2898906Sgm89044 		}
2899906Sgm89044 
2900906Sgm89044 		if (vec_idx == uiop->uio_iovcnt && count > 0) {
2901906Sgm89044 			/*
2902906Sgm89044 			 * The end of the specified iovec's was reached but
2903906Sgm89044 			 * the length requested could not be processed
2904906Sgm89044 			 * (requested to digest more data than it provided).
2905906Sgm89044 			 */
2906906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
2907906Sgm89044 		}
2908906Sgm89044 		break;
2909906Sgm89044 
2910906Sgm89044 	case CRYPTO_DATA_MBLK:
2911906Sgm89044 		/*
2912906Sgm89044 		 * Jump to the first mblk_t containing data to be processed.
2913906Sgm89044 		 */
2914906Sgm89044 		for (mp = data->cd_mp; mp != NULL && off >= MBLKL(mp);
2915*5063Sgm89044 		    off -= MBLKL(mp), mp = mp->b_cont)
2916*5063Sgm89044 			;
2917906Sgm89044 		if (mp == NULL) {
2918906Sgm89044 			/*
2919906Sgm89044 			 * The caller specified an offset that is larger than
2920906Sgm89044 			 * the total size of the buffers it provided.
2921906Sgm89044 			 */
2922906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
2923906Sgm89044 		}
2924906Sgm89044 
2925906Sgm89044 		/*
2926906Sgm89044 		 * Now do the processing on the mblk chain.
2927906Sgm89044 		 */
2928906Sgm89044 		while (mp != NULL && count > 0) {
2929906Sgm89044 			cur_len = min(MBLKL(mp) - off, count);
2930906Sgm89044 			bcopy((char *)(mp->b_rptr + off), dest, cur_len);
2931906Sgm89044 			count -= cur_len;
2932906Sgm89044 			dest += cur_len;
2933906Sgm89044 			mp = mp->b_cont;
2934906Sgm89044 			off = 0;
2935906Sgm89044 		}
2936906Sgm89044 
2937906Sgm89044 		if (mp == NULL && count > 0) {
2938906Sgm89044 			/*
2939906Sgm89044 			 * The end of the mblk was reached but the length
2940906Sgm89044 			 * requested could not be processed, (requested to
2941906Sgm89044 			 * digest more data than it provided).
2942906Sgm89044 			 */
2943906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
2944906Sgm89044 		}
2945906Sgm89044 		break;
2946906Sgm89044 
2947906Sgm89044 	default:
2948906Sgm89044 		DBG(NULL, DWARN, "unrecognised crypto data format");
2949906Sgm89044 		rv = CRYPTO_ARGUMENTS_BAD;
2950906Sgm89044 	}
2951906Sgm89044 	return (rv);
2952906Sgm89044 }
2953906Sgm89044 
2954906Sgm89044 
2955906Sgm89044 /*
2956906Sgm89044  * Performs the input, output or hard scatter/gather checks on the specified
2957906Sgm89044  * crypto_data_t struct. Returns true if the data is scatter/gather in nature
2958906Sgm89044  * ie fails the test.
2959906Sgm89044  */
2960906Sgm89044 int
2961906Sgm89044 dca_sgcheck(dca_t *dca, crypto_data_t *data, dca_sg_param_t val)
2962906Sgm89044 {
2963906Sgm89044 	uio_t *uiop;
2964906Sgm89044 	mblk_t *mp;
2965906Sgm89044 	int rv = FALSE;
2966906Sgm89044 
2967906Sgm89044 	switch (val) {
2968906Sgm89044 	case DCA_SG_CONTIG:
2969906Sgm89044 		/*
2970906Sgm89044 		 * Check for a contiguous data buffer.
2971906Sgm89044 		 */
2972906Sgm89044 		switch (data->cd_format) {
2973906Sgm89044 		case CRYPTO_DATA_RAW:
2974906Sgm89044 			/* Contiguous in nature */
2975906Sgm89044 			break;
2976906Sgm89044 
2977906Sgm89044 		case CRYPTO_DATA_UIO:
2978906Sgm89044 			if (data->cd_uio->uio_iovcnt > 1)
2979906Sgm89044 				rv = TRUE;
2980906Sgm89044 			break;
2981906Sgm89044 
2982906Sgm89044 		case CRYPTO_DATA_MBLK:
2983906Sgm89044 			mp = data->cd_mp;
2984906Sgm89044 			if (mp->b_cont != NULL)
2985906Sgm89044 				rv = TRUE;
2986906Sgm89044 			break;
2987906Sgm89044 
2988906Sgm89044 		default:
2989906Sgm89044 			DBG(NULL, DWARN, "unrecognised crypto data format");
2990906Sgm89044 		}
2991906Sgm89044 		break;
2992906Sgm89044 
2993906Sgm89044 	case DCA_SG_WALIGN:
2994906Sgm89044 		/*
2995906Sgm89044 		 * Check for a contiguous data buffer that is 32-bit word
2996906Sgm89044 		 * aligned and is of word multiples in size.
2997906Sgm89044 		 */
2998906Sgm89044 		switch (data->cd_format) {
2999906Sgm89044 		case CRYPTO_DATA_RAW:
3000906Sgm89044 			if ((data->cd_raw.iov_len % sizeof (uint32_t)) ||
3001906Sgm89044 			    ((uintptr_t)data->cd_raw.iov_base %
3002906Sgm89044 			    sizeof (uint32_t))) {
3003906Sgm89044 				rv = TRUE;
3004906Sgm89044 			}
3005906Sgm89044 			break;
3006906Sgm89044 
3007906Sgm89044 		case CRYPTO_DATA_UIO:
3008906Sgm89044 			uiop = data->cd_uio;
3009906Sgm89044 			if (uiop->uio_iovcnt > 1) {
3010906Sgm89044 				return (TRUE);
3011906Sgm89044 			}
3012906Sgm89044 			/* So there is only one iovec */
3013906Sgm89044 			if ((uiop->uio_iov[0].iov_len % sizeof (uint32_t)) ||
3014906Sgm89044 			    ((uintptr_t)uiop->uio_iov[0].iov_base %
3015906Sgm89044 			    sizeof (uint32_t))) {
3016906Sgm89044 				rv = TRUE;
3017906Sgm89044 			}
3018906Sgm89044 			break;
3019906Sgm89044 
3020906Sgm89044 		case CRYPTO_DATA_MBLK:
3021906Sgm89044 			mp = data->cd_mp;
3022906Sgm89044 			if (mp->b_cont != NULL) {
3023906Sgm89044 				return (TRUE);
3024906Sgm89044 			}
3025906Sgm89044 			/* So there is only one mblk in the chain */
3026906Sgm89044 			if ((MBLKL(mp) % sizeof (uint32_t)) ||
3027906Sgm89044 			    ((uintptr_t)mp->b_rptr % sizeof (uint32_t))) {
3028906Sgm89044 				rv = TRUE;
3029906Sgm89044 			}
3030906Sgm89044 			break;
3031906Sgm89044 
3032906Sgm89044 		default:
3033906Sgm89044 			DBG(NULL, DWARN, "unrecognised crypto data format");
3034906Sgm89044 		}
3035906Sgm89044 		break;
3036906Sgm89044 
3037906Sgm89044 	case DCA_SG_PALIGN:
3038906Sgm89044 		/*
3039906Sgm89044 		 * Check that the data buffer is page aligned and is of
3040906Sgm89044 		 * page multiples in size.
3041906Sgm89044 		 */
3042906Sgm89044 		switch (data->cd_format) {
3043906Sgm89044 		case CRYPTO_DATA_RAW:
3044906Sgm89044 			if ((data->cd_length % dca->dca_pagesize) ||
3045906Sgm89044 			    ((uintptr_t)data->cd_raw.iov_base %
3046906Sgm89044 			    dca->dca_pagesize)) {
3047906Sgm89044 				rv = TRUE;
3048906Sgm89044 			}
3049906Sgm89044 			break;
3050906Sgm89044 
3051906Sgm89044 		case CRYPTO_DATA_UIO:
3052906Sgm89044 			uiop = data->cd_uio;
3053906Sgm89044 			if ((uiop->uio_iov[0].iov_len % dca->dca_pagesize) ||
3054906Sgm89044 			    ((uintptr_t)uiop->uio_iov[0].iov_base %
3055906Sgm89044 			    dca->dca_pagesize)) {
3056906Sgm89044 				rv = TRUE;
3057906Sgm89044 			}
3058906Sgm89044 			break;
3059906Sgm89044 
3060906Sgm89044 		case CRYPTO_DATA_MBLK:
3061906Sgm89044 			mp = data->cd_mp;
3062906Sgm89044 			if ((MBLKL(mp) % dca->dca_pagesize) ||
3063906Sgm89044 			    ((uintptr_t)mp->b_rptr % dca->dca_pagesize)) {
3064906Sgm89044 				rv = TRUE;
3065906Sgm89044 			}
3066906Sgm89044 			break;
3067906Sgm89044 
3068906Sgm89044 		default:
3069906Sgm89044 			DBG(NULL, DWARN, "unrecognised crypto data format");
3070906Sgm89044 		}
3071906Sgm89044 		break;
3072906Sgm89044 
3073906Sgm89044 	default:
3074906Sgm89044 		DBG(NULL, DWARN, "unrecognised scatter/gather param type");
3075906Sgm89044 	}
3076906Sgm89044 
3077906Sgm89044 	return (rv);
3078906Sgm89044 }
3079906Sgm89044 
3080906Sgm89044 /*
3081906Sgm89044  * Increments the cd_offset and decrements the cd_length as the data is
3082906Sgm89044  * gathered from the crypto_data_t struct.
3083906Sgm89044  * The data is reverse-copied into the dest buffer if the flag is true.
3084906Sgm89044  */
3085906Sgm89044 int
3086906Sgm89044 dca_gather(crypto_data_t *in, char *dest, int count, int reverse)
3087906Sgm89044 {
3088906Sgm89044 	int	rv = CRYPTO_SUCCESS;
3089906Sgm89044 	uint_t	vec_idx;
3090906Sgm89044 	uio_t	*uiop;
3091906Sgm89044 	off_t	off = in->cd_offset;
3092906Sgm89044 	size_t	cur_len;
3093906Sgm89044 	mblk_t	*mp;
3094906Sgm89044 
3095906Sgm89044 	switch (in->cd_format) {
3096906Sgm89044 	case CRYPTO_DATA_RAW:
3097906Sgm89044 		if (count > in->cd_length) {
3098906Sgm89044 			/*
3099906Sgm89044 			 * The caller specified a length greater than the
3100906Sgm89044 			 * size of the buffer.
3101906Sgm89044 			 */
3102906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3103906Sgm89044 		}
3104906Sgm89044 		if (reverse)
3105906Sgm89044 			dca_reverse(in->cd_raw.iov_base + off, dest, count,
3106906Sgm89044 			    count);
3107906Sgm89044 		else
3108906Sgm89044 			bcopy(in->cd_raw.iov_base + in->cd_offset, dest, count);
3109906Sgm89044 		in->cd_offset += count;
3110906Sgm89044 		in->cd_length -= count;
3111906Sgm89044 		break;
3112906Sgm89044 
3113906Sgm89044 	case CRYPTO_DATA_UIO:
3114906Sgm89044 		/*
3115906Sgm89044 		 * Jump to the first iovec containing data to be processed.
3116906Sgm89044 		 */
3117906Sgm89044 		uiop = in->cd_uio;
3118906Sgm89044 		for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
3119906Sgm89044 		    off >= uiop->uio_iov[vec_idx].iov_len;
3120*5063Sgm89044 		    off -= uiop->uio_iov[vec_idx++].iov_len)
3121*5063Sgm89044 			;
3122906Sgm89044 		if (vec_idx == uiop->uio_iovcnt) {
3123906Sgm89044 			/*
3124906Sgm89044 			 * The caller specified an offset that is larger than
3125906Sgm89044 			 * the total size of the buffers it provided.
3126906Sgm89044 			 */
3127906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3128906Sgm89044 		}
3129906Sgm89044 
3130906Sgm89044 		/*
3131906Sgm89044 		 * Now process the iovecs.
3132906Sgm89044 		 */
3133906Sgm89044 		while (vec_idx < uiop->uio_iovcnt && count > 0) {
3134906Sgm89044 			cur_len = min(uiop->uio_iov[vec_idx].iov_len -
3135906Sgm89044 			    off, count);
3136906Sgm89044 			count -= cur_len;
3137906Sgm89044 			if (reverse) {
3138906Sgm89044 				/* Fill the dest buffer from the end */
3139906Sgm89044 				dca_reverse(uiop->uio_iov[vec_idx].iov_base +
3140906Sgm89044 				    off, dest+count, cur_len, cur_len);
3141906Sgm89044 			} else {
3142906Sgm89044 				bcopy(uiop->uio_iov[vec_idx].iov_base + off,
3143906Sgm89044 				    dest, cur_len);
3144906Sgm89044 				dest += cur_len;
3145906Sgm89044 			}
3146906Sgm89044 			in->cd_offset += cur_len;
3147906Sgm89044 			in->cd_length -= cur_len;
3148906Sgm89044 			vec_idx++;
3149906Sgm89044 			off = 0;
3150906Sgm89044 		}
3151906Sgm89044 
3152906Sgm89044 		if (vec_idx == uiop->uio_iovcnt && count > 0) {
3153906Sgm89044 			/*
3154906Sgm89044 			 * The end of the specified iovec's was reached but
3155906Sgm89044 			 * the length requested could not be processed
3156906Sgm89044 			 * (requested to digest more data than it provided).
3157906Sgm89044 			 */
3158906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3159906Sgm89044 		}
3160906Sgm89044 		break;
3161906Sgm89044 
3162906Sgm89044 	case CRYPTO_DATA_MBLK:
3163906Sgm89044 		/*
3164906Sgm89044 		 * Jump to the first mblk_t containing data to be processed.
3165906Sgm89044 		 */
3166906Sgm89044 		for (mp = in->cd_mp; mp != NULL && off >= MBLKL(mp);
3167*5063Sgm89044 		    off -= MBLKL(mp), mp = mp->b_cont)
3168*5063Sgm89044 			;
3169906Sgm89044 		if (mp == NULL) {
3170906Sgm89044 			/*
3171906Sgm89044 			 * The caller specified an offset that is larger than
3172906Sgm89044 			 * the total size of the buffers it provided.
3173906Sgm89044 			 */
3174906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3175906Sgm89044 		}
3176906Sgm89044 
3177906Sgm89044 		/*
3178906Sgm89044 		 * Now do the processing on the mblk chain.
3179906Sgm89044 		 */
3180906Sgm89044 		while (mp != NULL && count > 0) {
3181906Sgm89044 			cur_len = min(MBLKL(mp) - off, count);
3182906Sgm89044 			count -= cur_len;
3183906Sgm89044 			if (reverse) {
3184906Sgm89044 				/* Fill the dest buffer from the end */
3185906Sgm89044 				dca_reverse((char *)(mp->b_rptr + off),
3186906Sgm89044 				    dest+count, cur_len, cur_len);
3187906Sgm89044 			} else {
3188906Sgm89044 				bcopy((char *)(mp->b_rptr + off), dest,
3189906Sgm89044 				    cur_len);
3190906Sgm89044 				dest += cur_len;
3191906Sgm89044 			}
3192906Sgm89044 			in->cd_offset += cur_len;
3193906Sgm89044 			in->cd_length -= cur_len;
3194906Sgm89044 			mp = mp->b_cont;
3195906Sgm89044 			off = 0;
3196906Sgm89044 		}
3197906Sgm89044 
3198906Sgm89044 		if (mp == NULL && count > 0) {
3199906Sgm89044 			/*
3200906Sgm89044 			 * The end of the mblk was reached but the length
3201906Sgm89044 			 * requested could not be processed, (requested to
3202906Sgm89044 			 * digest more data than it provided).
3203906Sgm89044 			 */
3204906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3205906Sgm89044 		}
3206906Sgm89044 		break;
3207906Sgm89044 
3208906Sgm89044 	default:
3209906Sgm89044 		DBG(NULL, DWARN, "dca_gather: unrecognised crypto data format");
3210906Sgm89044 		rv = CRYPTO_ARGUMENTS_BAD;
3211906Sgm89044 	}
3212906Sgm89044 	return (rv);
3213906Sgm89044 }
3214906Sgm89044 
3215906Sgm89044 /*
3216906Sgm89044  * Increments the cd_offset and decrements the cd_length as the data is
3217906Sgm89044  * gathered from the crypto_data_t struct.
3218906Sgm89044  */
3219906Sgm89044 int
3220906Sgm89044 dca_resid_gather(crypto_data_t *in, char *resid, int *residlen, char *dest,
3221906Sgm89044     int count)
3222906Sgm89044 {
3223906Sgm89044 	int	rv = CRYPTO_SUCCESS;
3224906Sgm89044 	caddr_t	baddr;
3225906Sgm89044 	uint_t	vec_idx;
3226906Sgm89044 	uio_t	*uiop;
3227906Sgm89044 	off_t	off = in->cd_offset;
3228906Sgm89044 	size_t	cur_len;
3229906Sgm89044 	mblk_t	*mp;
3230906Sgm89044 
3231906Sgm89044 	/* Process the residual first */
3232906Sgm89044 	if (*residlen > 0) {
3233906Sgm89044 		uint_t	num = min(count, *residlen);
3234906Sgm89044 		bcopy(resid, dest, num);
3235906Sgm89044 		*residlen -= num;
3236906Sgm89044 		if (*residlen > 0) {
3237906Sgm89044 			/*
3238906Sgm89044 			 * Requested amount 'count' is less than what's in
3239906Sgm89044 			 * the residual, so shuffle any remaining resid to
3240906Sgm89044 			 * the front.
3241906Sgm89044 			 */
3242906Sgm89044 			baddr = resid + num;
3243906Sgm89044 			bcopy(baddr, resid, *residlen);
3244906Sgm89044 		}
3245906Sgm89044 		dest += num;
3246906Sgm89044 		count -= num;
3247906Sgm89044 	}
3248906Sgm89044 
3249906Sgm89044 	/* Now process what's in the crypto_data_t structs */
3250906Sgm89044 	switch (in->cd_format) {
3251906Sgm89044 	case CRYPTO_DATA_RAW:
3252906Sgm89044 		if (count > in->cd_length) {
3253906Sgm89044 			/*
3254906Sgm89044 			 * The caller specified a length greater than the
3255906Sgm89044 			 * size of the buffer.
3256906Sgm89044 			 */
3257906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3258906Sgm89044 		}
3259906Sgm89044 		bcopy(in->cd_raw.iov_base + in->cd_offset, dest, count);
3260906Sgm89044 		in->cd_offset += count;
3261906Sgm89044 		in->cd_length -= count;
3262906Sgm89044 		break;
3263906Sgm89044 
3264906Sgm89044 	case CRYPTO_DATA_UIO:
3265906Sgm89044 		/*
3266906Sgm89044 		 * Jump to the first iovec containing data to be processed.
3267906Sgm89044 		 */
3268906Sgm89044 		uiop = in->cd_uio;
3269906Sgm89044 		for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
3270906Sgm89044 		    off >= uiop->uio_iov[vec_idx].iov_len;
3271*5063Sgm89044 		    off -= uiop->uio_iov[vec_idx++].iov_len)
3272*5063Sgm89044 			;
3273906Sgm89044 		if (vec_idx == uiop->uio_iovcnt) {
3274906Sgm89044 			/*
3275906Sgm89044 			 * The caller specified an offset that is larger than
3276906Sgm89044 			 * the total size of the buffers it provided.
3277906Sgm89044 			 */
3278906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3279906Sgm89044 		}
3280906Sgm89044 
3281906Sgm89044 		/*
3282906Sgm89044 		 * Now process the iovecs.
3283906Sgm89044 		 */
3284906Sgm89044 		while (vec_idx < uiop->uio_iovcnt && count > 0) {
3285906Sgm89044 			cur_len = min(uiop->uio_iov[vec_idx].iov_len -
3286906Sgm89044 			    off, count);
3287906Sgm89044 			bcopy(uiop->uio_iov[vec_idx].iov_base + off, dest,
3288906Sgm89044 			    cur_len);
3289906Sgm89044 			count -= cur_len;
3290906Sgm89044 			dest += cur_len;
3291906Sgm89044 			in->cd_offset += cur_len;
3292906Sgm89044 			in->cd_length -= cur_len;
3293906Sgm89044 			vec_idx++;
3294906Sgm89044 			off = 0;
3295906Sgm89044 		}
3296906Sgm89044 
3297906Sgm89044 		if (vec_idx == uiop->uio_iovcnt && count > 0) {
3298906Sgm89044 			/*
3299906Sgm89044 			 * The end of the specified iovec's was reached but
3300906Sgm89044 			 * the length requested could not be processed
3301906Sgm89044 			 * (requested to digest more data than it provided).
3302906Sgm89044 			 */
3303906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3304906Sgm89044 		}
3305906Sgm89044 		break;
3306906Sgm89044 
3307906Sgm89044 	case CRYPTO_DATA_MBLK:
3308906Sgm89044 		/*
3309906Sgm89044 		 * Jump to the first mblk_t containing data to be processed.
3310906Sgm89044 		 */
3311906Sgm89044 		for (mp = in->cd_mp; mp != NULL && off >= MBLKL(mp);
3312*5063Sgm89044 		    off -= MBLKL(mp), mp = mp->b_cont)
3313*5063Sgm89044 			;
3314906Sgm89044 		if (mp == NULL) {
3315906Sgm89044 			/*
3316906Sgm89044 			 * The caller specified an offset that is larger than
3317906Sgm89044 			 * the total size of the buffers it provided.
3318906Sgm89044 			 */
3319906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3320906Sgm89044 		}
3321906Sgm89044 
3322906Sgm89044 		/*
3323906Sgm89044 		 * Now do the processing on the mblk chain.
3324906Sgm89044 		 */
3325906Sgm89044 		while (mp != NULL && count > 0) {
3326906Sgm89044 			cur_len = min(MBLKL(mp) - off, count);
3327906Sgm89044 			bcopy((char *)(mp->b_rptr + off), dest, cur_len);
3328906Sgm89044 			count -= cur_len;
3329906Sgm89044 			dest += cur_len;
3330906Sgm89044 			in->cd_offset += cur_len;
3331906Sgm89044 			in->cd_length -= cur_len;
3332906Sgm89044 			mp = mp->b_cont;
3333906Sgm89044 			off = 0;
3334906Sgm89044 		}
3335906Sgm89044 
3336906Sgm89044 		if (mp == NULL && count > 0) {
3337906Sgm89044 			/*
3338906Sgm89044 			 * The end of the mblk was reached but the length
3339906Sgm89044 			 * requested could not be processed, (requested to
3340906Sgm89044 			 * digest more data than it provided).
3341906Sgm89044 			 */
3342906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3343906Sgm89044 		}
3344906Sgm89044 		break;
3345906Sgm89044 
3346906Sgm89044 	default:
3347906Sgm89044 		DBG(NULL, DWARN,
3348906Sgm89044 		    "dca_resid_gather: unrecognised crypto data format");
3349906Sgm89044 		rv = CRYPTO_ARGUMENTS_BAD;
3350906Sgm89044 	}
3351906Sgm89044 	return (rv);
3352906Sgm89044 }
3353906Sgm89044 
3354906Sgm89044 /*
3355906Sgm89044  * Appends the data to the crypto_data_t struct increasing cd_length.
3356906Sgm89044  * cd_offset is left unchanged.
3357906Sgm89044  * Data is reverse-copied if the flag is TRUE.
3358906Sgm89044  */
3359906Sgm89044 int
3360906Sgm89044 dca_scatter(const char *src, crypto_data_t *out, int count, int reverse)
3361906Sgm89044 {
3362906Sgm89044 	int	rv = CRYPTO_SUCCESS;
3363906Sgm89044 	off_t	offset = out->cd_offset + out->cd_length;
3364906Sgm89044 	uint_t	vec_idx;
3365906Sgm89044 	uio_t	*uiop;
3366906Sgm89044 	size_t	cur_len;
3367906Sgm89044 	mblk_t	*mp;
3368906Sgm89044 
3369906Sgm89044 	switch (out->cd_format) {
3370906Sgm89044 	case CRYPTO_DATA_RAW:
3371906Sgm89044 		if (out->cd_raw.iov_len - offset < count) {
3372906Sgm89044 			/* Trying to write out more than space available. */
3373906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3374906Sgm89044 		}
3375906Sgm89044 		if (reverse)
3376906Sgm89044 			dca_reverse((void*) src, out->cd_raw.iov_base + offset,
3377906Sgm89044 			    count, count);
3378906Sgm89044 		else
3379906Sgm89044 			bcopy(src, out->cd_raw.iov_base + offset, count);
3380906Sgm89044 		out->cd_length += count;
3381906Sgm89044 		break;
3382906Sgm89044 
3383906Sgm89044 	case CRYPTO_DATA_UIO:
3384906Sgm89044 		/*
3385906Sgm89044 		 * Jump to the first iovec that can be written to.
3386906Sgm89044 		 */
3387906Sgm89044 		uiop = out->cd_uio;
3388906Sgm89044 		for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
3389906Sgm89044 		    offset >= uiop->uio_iov[vec_idx].iov_len;
3390*5063Sgm89044 		    offset -= uiop->uio_iov[vec_idx++].iov_len)
3391*5063Sgm89044 			;
3392906Sgm89044 		if (vec_idx == uiop->uio_iovcnt) {
3393906Sgm89044 			/*
3394906Sgm89044 			 * The caller specified an offset that is larger than
3395906Sgm89044 			 * the total size of the buffers it provided.
3396906Sgm89044 			 */
3397906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3398906Sgm89044 		}
3399906Sgm89044 
3400906Sgm89044 		/*
3401906Sgm89044 		 * Now process the iovecs.
3402906Sgm89044 		 */
3403906Sgm89044 		while (vec_idx < uiop->uio_iovcnt && count > 0) {
3404906Sgm89044 			cur_len = min(uiop->uio_iov[vec_idx].iov_len -
3405906Sgm89044 			    offset, count);
3406906Sgm89044 			count -= cur_len;
3407906Sgm89044 			if (reverse) {
3408906Sgm89044 				dca_reverse((void*) (src+count),
3409906Sgm89044 				    uiop->uio_iov[vec_idx].iov_base +
3410906Sgm89044 				    offset, cur_len, cur_len);
3411906Sgm89044 			} else {
3412906Sgm89044 				bcopy(src, uiop->uio_iov[vec_idx].iov_base +
3413906Sgm89044 				    offset, cur_len);
3414906Sgm89044 				src += cur_len;
3415906Sgm89044 			}
3416906Sgm89044 			out->cd_length += cur_len;
3417906Sgm89044 			vec_idx++;
3418906Sgm89044 			offset = 0;
3419906Sgm89044 		}
3420906Sgm89044 
3421906Sgm89044 		if (vec_idx == uiop->uio_iovcnt && count > 0) {
3422906Sgm89044 			/*
3423906Sgm89044 			 * The end of the specified iovec's was reached but
3424906Sgm89044 			 * the length requested could not be processed
3425906Sgm89044 			 * (requested to write more data than space provided).
3426906Sgm89044 			 */
3427906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3428906Sgm89044 		}
3429906Sgm89044 		break;
3430906Sgm89044 
3431906Sgm89044 	case CRYPTO_DATA_MBLK:
3432906Sgm89044 		/*
3433906Sgm89044 		 * Jump to the first mblk_t that can be written to.
3434906Sgm89044 		 */
3435906Sgm89044 		for (mp = out->cd_mp; mp != NULL && offset >= MBLKL(mp);
3436*5063Sgm89044 		    offset -= MBLKL(mp), mp = mp->b_cont)
3437*5063Sgm89044 			;
3438906Sgm89044 		if (mp == NULL) {
3439906Sgm89044 			/*
3440906Sgm89044 			 * The caller specified an offset that is larger than
3441906Sgm89044 			 * the total size of the buffers it provided.
3442906Sgm89044 			 */
3443906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3444906Sgm89044 		}
3445906Sgm89044 
3446906Sgm89044 		/*
3447906Sgm89044 		 * Now do the processing on the mblk chain.
3448906Sgm89044 		 */
3449906Sgm89044 		while (mp != NULL && count > 0) {
3450906Sgm89044 			cur_len = min(MBLKL(mp) - offset, count);
3451906Sgm89044 			count -= cur_len;
3452906Sgm89044 			if (reverse) {
3453906Sgm89044 				dca_reverse((void*) (src+count),
3454906Sgm89044 				    (char *)(mp->b_rptr + offset), cur_len,
3455906Sgm89044 				    cur_len);
3456906Sgm89044 			} else {
3457906Sgm89044 				bcopy(src, (char *)(mp->b_rptr + offset),
3458906Sgm89044 				    cur_len);
3459906Sgm89044 				src += cur_len;
3460906Sgm89044 			}
3461906Sgm89044 			out->cd_length += cur_len;
3462906Sgm89044 			mp = mp->b_cont;
3463906Sgm89044 			offset = 0;
3464906Sgm89044 		}
3465906Sgm89044 
3466906Sgm89044 		if (mp == NULL && count > 0) {
3467906Sgm89044 			/*
3468906Sgm89044 			 * The end of the mblk was reached but the length
3469906Sgm89044 			 * requested could not be processed, (requested to
3470906Sgm89044 			 * digest more data than it provided).
3471906Sgm89044 			 */
3472906Sgm89044 			return (CRYPTO_DATA_LEN_RANGE);
3473906Sgm89044 		}
3474906Sgm89044 		break;
3475906Sgm89044 
3476906Sgm89044 	default:
3477906Sgm89044 		DBG(NULL, DWARN, "unrecognised crypto data format");
3478906Sgm89044 		rv = CRYPTO_ARGUMENTS_BAD;
3479906Sgm89044 	}
3480906Sgm89044 	return (rv);
3481906Sgm89044 }
3482906Sgm89044 
3483906Sgm89044 /*
3484906Sgm89044  * Compare two byte arrays in reverse order.
3485906Sgm89044  * Return 0 if they are identical, 1 otherwise.
3486906Sgm89044  */
3487906Sgm89044 int
3488906Sgm89044 dca_bcmp_reverse(const void *s1, const void *s2, size_t n)
3489906Sgm89044 {
3490906Sgm89044 	int i;
3491906Sgm89044 	caddr_t src, dst;
3492906Sgm89044 
3493906Sgm89044 	if (!n)
3494906Sgm89044 		return (0);
3495906Sgm89044 
3496906Sgm89044 	src = ((caddr_t)s1) + n - 1;
3497906Sgm89044 	dst = (caddr_t)s2;
3498906Sgm89044 	for (i = 0; i < n; i++) {
3499906Sgm89044 		if (*src != *dst)
3500906Sgm89044 			return (1);
3501906Sgm89044 		src--;
3502906Sgm89044 		dst++;
3503906Sgm89044 	}
3504906Sgm89044 
3505906Sgm89044 	return (0);
3506906Sgm89044 }
3507906Sgm89044 
3508906Sgm89044 
3509906Sgm89044 /*
3510906Sgm89044  * This calculates the size of a bignum in bits, specifically not counting
3511906Sgm89044  * leading zero bits.  This size calculation must be done *before* any
3512906Sgm89044  * endian reversal takes place (i.e. the numbers are in absolute big-endian
3513906Sgm89044  * order.)
3514906Sgm89044  */
3515906Sgm89044 int
3516906Sgm89044 dca_bitlen(unsigned char *bignum, int bytelen)
3517906Sgm89044 {
3518906Sgm89044 	unsigned char	msbyte;
3519906Sgm89044 	int		i, j;
3520906Sgm89044 
3521906Sgm89044 	for (i = 0; i < bytelen - 1; i++) {
3522906Sgm89044 		if (bignum[i] != 0) {
3523906Sgm89044 			break;
3524906Sgm89044 		}
3525906Sgm89044 	}
3526906Sgm89044 	msbyte = bignum[i];
3527906Sgm89044 	for (j = 8; j > 1; j--) {
3528906Sgm89044 		if (msbyte & 0x80) {
3529906Sgm89044 			break;
3530906Sgm89044 		}
3531906Sgm89044 		msbyte <<= 1;
3532906Sgm89044 	}
3533906Sgm89044 	return ((8 * (bytelen - i - 1)) + j);
3534906Sgm89044 }
3535906Sgm89044 
3536906Sgm89044 /*
3537906Sgm89044  * This compares to bignums (in big-endian order).  It ignores leading
3538906Sgm89044  * null bytes.  The result semantics follow bcmp, mempcmp, strcmp, etc.
3539906Sgm89044  */
3540906Sgm89044 int
3541906Sgm89044 dca_numcmp(caddr_t n1, int n1len, caddr_t n2, int n2len)
3542906Sgm89044 {
3543906Sgm89044 	while ((n1len > 1) && (*n1 == 0)) {
3544906Sgm89044 		n1len--;
3545906Sgm89044 		n1++;
3546906Sgm89044 	}
3547906Sgm89044 	while ((n2len > 1) && (*n2 == 0)) {
3548906Sgm89044 		n2len--;
3549906Sgm89044 		n2++;
3550906Sgm89044 	}
3551906Sgm89044 	if (n1len != n2len) {
3552906Sgm89044 		return (n1len - n2len);
3553906Sgm89044 	}
3554906Sgm89044 	while ((n1len > 1) && (*n1 == *n2)) {
3555906Sgm89044 		n1++;
3556906Sgm89044 		n2++;
3557906Sgm89044 		n1len--;
3558906Sgm89044 	}
3559906Sgm89044 	return ((int)(*(uchar_t *)n1) - (int)(*(uchar_t *)n2));
3560906Sgm89044 }
3561906Sgm89044 
3562906Sgm89044 /*
3563906Sgm89044  * Return array of key attributes.
3564906Sgm89044  */
3565906Sgm89044 crypto_object_attribute_t *
3566906Sgm89044 dca_get_key_attr(crypto_key_t *key)
3567906Sgm89044 {
3568906Sgm89044 	if ((key->ck_format != CRYPTO_KEY_ATTR_LIST) ||
3569906Sgm89044 	    (key->ck_count == 0)) {
3570906Sgm89044 		return (NULL);
3571906Sgm89044 	}
3572906Sgm89044 
3573906Sgm89044 	return (key->ck_attrs);
3574906Sgm89044 }
3575906Sgm89044 
3576906Sgm89044 /*
3577906Sgm89044  * If attribute type exists valp points to it's 32-bit value.
3578906Sgm89044  */
3579906Sgm89044 int
3580906Sgm89044 dca_attr_lookup_uint32(crypto_object_attribute_t *attrp, uint_t atnum,
3581906Sgm89044     uint64_t atype, uint32_t *valp)
3582906Sgm89044 {
3583906Sgm89044 	crypto_object_attribute_t	*bap;
3584906Sgm89044 
3585906Sgm89044 	bap = dca_find_attribute(attrp, atnum, atype);
3586906Sgm89044 	if (bap == NULL) {
3587906Sgm89044 		return (CRYPTO_ATTRIBUTE_TYPE_INVALID);
3588906Sgm89044 	}
3589906Sgm89044 
3590906Sgm89044 	*valp = *bap->oa_value;
3591906Sgm89044 
3592906Sgm89044 	return (CRYPTO_SUCCESS);
3593906Sgm89044 }
3594906Sgm89044 
3595906Sgm89044 /*
3596906Sgm89044  * If attribute type exists data contains the start address of the value,
3597906Sgm89044  * and numelems contains it's length.
3598906Sgm89044  */
3599906Sgm89044 int
3600906Sgm89044 dca_attr_lookup_uint8_array(crypto_object_attribute_t *attrp, uint_t atnum,
3601906Sgm89044     uint64_t atype, void **data, unsigned int *numelems)
3602906Sgm89044 {
3603906Sgm89044 	crypto_object_attribute_t	*bap;
3604906Sgm89044 
3605906Sgm89044 	bap = dca_find_attribute(attrp, atnum, atype);
3606906Sgm89044 	if (bap == NULL) {
3607906Sgm89044 		return (CRYPTO_ATTRIBUTE_TYPE_INVALID);
3608906Sgm89044 	}
3609906Sgm89044 
3610906Sgm89044 	*data = bap->oa_value;
3611906Sgm89044 	*numelems = bap->oa_value_len;
3612906Sgm89044 
3613906Sgm89044 	return (CRYPTO_SUCCESS);
3614906Sgm89044 }
3615906Sgm89044 
3616906Sgm89044 /*
3617906Sgm89044  * Finds entry of specified name. If it is not found dca_find_attribute returns
3618906Sgm89044  * NULL.
3619906Sgm89044  */
3620906Sgm89044 crypto_object_attribute_t *
3621906Sgm89044 dca_find_attribute(crypto_object_attribute_t *attrp, uint_t atnum,
3622906Sgm89044     uint64_t atype)
3623906Sgm89044 {
3624906Sgm89044 	while (atnum) {
3625906Sgm89044 		if (attrp->oa_type == atype)
3626906Sgm89044 			return (attrp);
3627906Sgm89044 		atnum--;
3628906Sgm89044 		attrp++;
3629906Sgm89044 	}
3630906Sgm89044 	return (NULL);
3631906Sgm89044 }
3632906Sgm89044 
3633906Sgm89044 /*
3634906Sgm89044  * Return the address of the first data buffer. If the data format is
3635906Sgm89044  * unrecognised return NULL.
3636906Sgm89044  */
3637906Sgm89044 caddr_t
3638906Sgm89044 dca_bufdaddr(crypto_data_t *data)
3639906Sgm89044 {
3640906Sgm89044 	switch (data->cd_format) {
3641906Sgm89044 	case CRYPTO_DATA_RAW:
3642906Sgm89044 		return (data->cd_raw.iov_base + data->cd_offset);
3643906Sgm89044 	case CRYPTO_DATA_UIO:
3644906Sgm89044 		return (data->cd_uio->uio_iov[0].iov_base + data->cd_offset);
3645906Sgm89044 	case CRYPTO_DATA_MBLK:
3646906Sgm89044 		return ((char *)data->cd_mp->b_rptr + data->cd_offset);
3647906Sgm89044 	default:
3648906Sgm89044 		DBG(NULL, DWARN,
3649906Sgm89044 		    "dca_bufdaddr: unrecognised crypto data format");
3650906Sgm89044 		return (NULL);
3651906Sgm89044 	}
3652906Sgm89044 }
3653906Sgm89044 
3654906Sgm89044 static caddr_t
3655906Sgm89044 dca_bufdaddr_out(crypto_data_t *data)
3656906Sgm89044 {
3657906Sgm89044 	size_t offset = data->cd_offset + data->cd_length;
3658906Sgm89044 
3659906Sgm89044 	switch (data->cd_format) {
3660906Sgm89044 	case CRYPTO_DATA_RAW:
3661906Sgm89044 		return (data->cd_raw.iov_base + offset);
3662906Sgm89044 	case CRYPTO_DATA_UIO:
3663906Sgm89044 		return (data->cd_uio->uio_iov[0].iov_base + offset);
3664906Sgm89044 	case CRYPTO_DATA_MBLK:
3665906Sgm89044 		return ((char *)data->cd_mp->b_rptr + offset);
3666906Sgm89044 	default:
3667906Sgm89044 		DBG(NULL, DWARN,
3668906Sgm89044 		    "dca_bufdaddr_out: unrecognised crypto data format");
3669906Sgm89044 		return (NULL);
3670906Sgm89044 	}
3671906Sgm89044 }
3672906Sgm89044 
3673906Sgm89044 /*
3674906Sgm89044  * Control entry points.
3675906Sgm89044  */
3676906Sgm89044 
3677906Sgm89044 /* ARGSUSED */
3678906Sgm89044 static void
3679906Sgm89044 dca_provider_status(crypto_provider_handle_t provider, uint_t *status)
3680906Sgm89044 {
3681906Sgm89044 	*status = CRYPTO_PROVIDER_READY;
3682906Sgm89044 }
3683906Sgm89044 
3684906Sgm89044 /*
3685906Sgm89044  * Cipher (encrypt/decrypt) entry points.
3686906Sgm89044  */
3687906Sgm89044 
3688906Sgm89044 /* ARGSUSED */
3689906Sgm89044 static int
3690906Sgm89044 dca_encrypt_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
3691906Sgm89044     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
3692906Sgm89044     crypto_req_handle_t req)
3693906Sgm89044 {
3694906Sgm89044 	int error = CRYPTO_FAILED;
3695906Sgm89044 	dca_t *softc;
3696906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
3697906Sgm89044 	int instance;
3698906Sgm89044 
3699906Sgm89044 	/* extract softc and instance number from context */
3700906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
3701906Sgm89044 	DBG(softc, DENTRY, "dca_encrypt_init: started");
3702906Sgm89044 
3703906Sgm89044 	/* check mechanism */
3704906Sgm89044 	switch (mechanism->cm_type) {
3705906Sgm89044 	case DES_CBC_MECH_INFO_TYPE:
3706906Sgm89044 		error = dca_3desctxinit(ctx, mechanism, key, KM_SLEEP,
3707906Sgm89044 		    DR_ENCRYPT);
3708906Sgm89044 		break;
3709906Sgm89044 	case DES3_CBC_MECH_INFO_TYPE:
3710906Sgm89044 		error = dca_3desctxinit(ctx, mechanism, key, KM_SLEEP,
3711906Sgm89044 		    DR_ENCRYPT | DR_TRIPLE);
3712906Sgm89044 		break;
3713906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
3714906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
3715906Sgm89044 		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
3716906Sgm89044 		break;
3717906Sgm89044 	default:
3718906Sgm89044 		cmn_err(CE_WARN, "dca_encrypt_init: unexpected mech type "
3719906Sgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
3720906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
3721906Sgm89044 	}
3722906Sgm89044 
3723906Sgm89044 	DBG(softc, DENTRY, "dca_encrypt_init: done, err = 0x%x", error);
3724906Sgm89044 
3725906Sgm89044 	if (error == CRYPTO_SUCCESS)
3726906Sgm89044 		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
3727906Sgm89044 		    &softc->dca_ctx_list_lock);
3728906Sgm89044 
3729906Sgm89044 	return (error);
3730906Sgm89044 }
3731906Sgm89044 
3732906Sgm89044 /* ARGSUSED */
3733906Sgm89044 static int
3734906Sgm89044 dca_encrypt(crypto_ctx_t *ctx, crypto_data_t *plaintext,
3735906Sgm89044     crypto_data_t *ciphertext, crypto_req_handle_t req)
3736906Sgm89044 {
3737906Sgm89044 	int error = CRYPTO_FAILED;
3738906Sgm89044 	dca_t *softc;
3739906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
3740906Sgm89044 	int instance;
3741906Sgm89044 
3742906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
3743906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
3744906Sgm89044 
3745906Sgm89044 	/* extract softc and instance number from context */
3746906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
3747906Sgm89044 	DBG(softc, DENTRY, "dca_encrypt: started");
3748906Sgm89044 
3749*5063Sgm89044 	/* handle inplace ops */
3750*5063Sgm89044 	if (!ciphertext) {
3751*5063Sgm89044 		dca_request_t *reqp = ctx->cc_provider_private;
3752*5063Sgm89044 		reqp->dr_flags |= DR_INPLACE;
3753*5063Sgm89044 		ciphertext = plaintext;
3754*5063Sgm89044 	}
3755*5063Sgm89044 
3756906Sgm89044 	/* check mechanism */
3757906Sgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
3758906Sgm89044 	case DES_CBC_MECH_INFO_TYPE:
3759906Sgm89044 		error = dca_3des(ctx, plaintext, ciphertext, req, DR_ENCRYPT);
3760906Sgm89044 		break;
3761906Sgm89044 	case DES3_CBC_MECH_INFO_TYPE:
3762906Sgm89044 		error = dca_3des(ctx, plaintext, ciphertext, req,
3763906Sgm89044 		    DR_ENCRYPT | DR_TRIPLE);
3764906Sgm89044 		break;
3765906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
3766906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
3767906Sgm89044 		error = dca_rsastart(ctx, plaintext, ciphertext, req,
3768906Sgm89044 		    DCA_RSA_ENC);
3769906Sgm89044 		break;
3770906Sgm89044 	default:
3771906Sgm89044 		/* Should never reach here */
3772906Sgm89044 		cmn_err(CE_WARN, "dca_encrypt: unexpected mech type "
3773906Sgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
3774906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
3775906Sgm89044 	}
3776906Sgm89044 
3777906Sgm89044 	if ((error != CRYPTO_QUEUED) && (error != CRYPTO_SUCCESS) &&
3778906Sgm89044 	    (error != CRYPTO_BUFFER_TOO_SMALL)) {
3779906Sgm89044 		ciphertext->cd_length = 0;
3780906Sgm89044 	}
3781906Sgm89044 
3782906Sgm89044 	DBG(softc, DENTRY, "dca_encrypt: done, err = 0x%x", error);
3783906Sgm89044 
3784906Sgm89044 	return (error);
3785906Sgm89044 }
3786906Sgm89044 
3787906Sgm89044 /* ARGSUSED */
3788906Sgm89044 static int
3789906Sgm89044 dca_encrypt_update(crypto_ctx_t *ctx, crypto_data_t *plaintext,
3790906Sgm89044     crypto_data_t *ciphertext, crypto_req_handle_t req)
3791906Sgm89044 {
3792906Sgm89044 	int error = CRYPTO_FAILED;
3793906Sgm89044 	dca_t *softc;
3794906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
3795906Sgm89044 	int instance;
3796906Sgm89044 
3797906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
3798906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
3799906Sgm89044 
3800906Sgm89044 	/* extract softc and instance number from context */
3801906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
3802906Sgm89044 	DBG(softc, DENTRY, "dca_encrypt_update: started");
3803906Sgm89044 
3804*5063Sgm89044 	/* handle inplace ops */
3805*5063Sgm89044 	if (!ciphertext) {
3806*5063Sgm89044 		dca_request_t *reqp = ctx->cc_provider_private;
3807*5063Sgm89044 		reqp->dr_flags |= DR_INPLACE;
3808*5063Sgm89044 		ciphertext = plaintext;
3809*5063Sgm89044 	}
3810*5063Sgm89044 
3811906Sgm89044 	/* check mechanism */
3812906Sgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
3813906Sgm89044 	case DES_CBC_MECH_INFO_TYPE:
3814906Sgm89044 		error = dca_3desupdate(ctx, plaintext, ciphertext, req,
3815906Sgm89044 		    DR_ENCRYPT);
3816906Sgm89044 		break;
3817906Sgm89044 	case DES3_CBC_MECH_INFO_TYPE:
3818906Sgm89044 		error = dca_3desupdate(ctx, plaintext, ciphertext, req,
3819906Sgm89044 		    DR_ENCRYPT | DR_TRIPLE);
3820906Sgm89044 		break;
3821906Sgm89044 	default:
3822906Sgm89044 		/* Should never reach here */
3823906Sgm89044 		cmn_err(CE_WARN, "dca_encrypt_update: unexpected mech type "
3824906Sgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
3825906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
3826906Sgm89044 	}
3827906Sgm89044 
3828906Sgm89044 	DBG(softc, DENTRY, "dca_encrypt_update: done, err = 0x%x", error);
3829906Sgm89044 
3830906Sgm89044 	return (error);
3831906Sgm89044 }
3832906Sgm89044 
3833906Sgm89044 /* ARGSUSED */
3834906Sgm89044 static int
3835906Sgm89044 dca_encrypt_final(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
3836906Sgm89044     crypto_req_handle_t req)
3837906Sgm89044 {
3838906Sgm89044 	int error = CRYPTO_FAILED;
3839906Sgm89044 	dca_t *softc;
3840906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
3841906Sgm89044 	int instance;
3842906Sgm89044 
3843906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
3844906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
3845906Sgm89044 
3846906Sgm89044 	/* extract softc and instance number from context */
3847906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
3848906Sgm89044 	DBG(softc, DENTRY, "dca_encrypt_final: started");
3849906Sgm89044 
3850906Sgm89044 	/* check mechanism */
3851906Sgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
3852906Sgm89044 	case DES_CBC_MECH_INFO_TYPE:
3853906Sgm89044 		error = dca_3desfinal(ctx, ciphertext, DR_ENCRYPT);
3854906Sgm89044 		break;
3855906Sgm89044 	case DES3_CBC_MECH_INFO_TYPE:
3856906Sgm89044 		error = dca_3desfinal(ctx, ciphertext, DR_ENCRYPT | DR_TRIPLE);
3857906Sgm89044 		break;
3858906Sgm89044 	default:
3859906Sgm89044 		/* Should never reach here */
3860906Sgm89044 		cmn_err(CE_WARN, "dca_encrypt_final: unexpected mech type "
3861906Sgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
3862906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
3863906Sgm89044 	}
3864906Sgm89044 
3865906Sgm89044 	DBG(softc, DENTRY, "dca_encrypt_final: done, err = 0x%x", error);
3866906Sgm89044 
3867906Sgm89044 	return (error);
3868906Sgm89044 }
3869906Sgm89044 
3870906Sgm89044 /* ARGSUSED */
3871906Sgm89044 static int
3872906Sgm89044 dca_encrypt_atomic(crypto_provider_handle_t provider,
3873906Sgm89044     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
3874906Sgm89044     crypto_key_t *key, crypto_data_t *plaintext, crypto_data_t *ciphertext,
3875906Sgm89044     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
3876906Sgm89044 {
3877906Sgm89044 	int error = CRYPTO_FAILED;
3878906Sgm89044 	dca_t *softc = (dca_t *)provider;
3879906Sgm89044 
3880906Sgm89044 	DBG(softc, DENTRY, "dca_encrypt_atomic: started");
3881906Sgm89044 
3882906Sgm89044 	if (ctx_template != NULL)
3883906Sgm89044 		return (CRYPTO_ARGUMENTS_BAD);
3884906Sgm89044 
3885*5063Sgm89044 	/* handle inplace ops */
3886*5063Sgm89044 	if (!ciphertext) {
3887*5063Sgm89044 		ciphertext = plaintext;
3888*5063Sgm89044 	}
3889*5063Sgm89044 
3890906Sgm89044 	/* check mechanism */
3891906Sgm89044 	switch (mechanism->cm_type) {
3892906Sgm89044 	case DES_CBC_MECH_INFO_TYPE:
3893906Sgm89044 		error = dca_3desatomic(provider, session_id, mechanism, key,
3894906Sgm89044 		    plaintext, ciphertext, KM_SLEEP, req,
3895906Sgm89044 		    DR_ENCRYPT | DR_ATOMIC);
3896906Sgm89044 		break;
3897906Sgm89044 	case DES3_CBC_MECH_INFO_TYPE:
3898906Sgm89044 		error = dca_3desatomic(provider, session_id, mechanism, key,
3899906Sgm89044 		    plaintext, ciphertext, KM_SLEEP, req,
3900906Sgm89044 		    DR_ENCRYPT | DR_TRIPLE | DR_ATOMIC);
3901906Sgm89044 		break;
3902906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
3903906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
3904906Sgm89044 		error = dca_rsaatomic(provider, session_id, mechanism, key,
3905906Sgm89044 		    plaintext, ciphertext, KM_SLEEP, req, DCA_RSA_ENC);
3906906Sgm89044 		break;
3907906Sgm89044 	default:
3908906Sgm89044 		cmn_err(CE_WARN, "dca_encrypt_atomic: unexpected mech type "
3909906Sgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
3910906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
3911906Sgm89044 	}
3912906Sgm89044 
3913906Sgm89044 	if ((error != CRYPTO_QUEUED) && (error != CRYPTO_SUCCESS)) {
3914906Sgm89044 		ciphertext->cd_length = 0;
3915906Sgm89044 	}
3916906Sgm89044 
3917906Sgm89044 	DBG(softc, DENTRY, "dca_encrypt_atomic: done, err = 0x%x", error);
3918906Sgm89044 
3919906Sgm89044 	return (error);
3920906Sgm89044 }
3921906Sgm89044 
3922906Sgm89044 /* ARGSUSED */
3923906Sgm89044 static int
3924906Sgm89044 dca_decrypt_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
3925906Sgm89044     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
3926906Sgm89044     crypto_req_handle_t req)
3927906Sgm89044 {
3928906Sgm89044 	int error = CRYPTO_FAILED;
3929906Sgm89044 	dca_t *softc;
3930906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
3931906Sgm89044 	int instance;
3932906Sgm89044 
3933906Sgm89044 	/* extract softc and instance number from context */
3934906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
3935906Sgm89044 	DBG(softc, DENTRY, "dca_decrypt_init: started");
3936906Sgm89044 
3937906Sgm89044 	/* check mechanism */
3938906Sgm89044 	switch (mechanism->cm_type) {
3939906Sgm89044 	case DES_CBC_MECH_INFO_TYPE:
3940906Sgm89044 		error = dca_3desctxinit(ctx, mechanism, key, KM_SLEEP,
3941906Sgm89044 		    DR_DECRYPT);
3942906Sgm89044 		break;
3943906Sgm89044 	case DES3_CBC_MECH_INFO_TYPE:
3944906Sgm89044 		error = dca_3desctxinit(ctx, mechanism, key, KM_SLEEP,
3945906Sgm89044 		    DR_DECRYPT | DR_TRIPLE);
3946906Sgm89044 		break;
3947906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
3948906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
3949906Sgm89044 		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
3950906Sgm89044 		break;
3951906Sgm89044 	default:
3952906Sgm89044 		cmn_err(CE_WARN, "dca_decrypt_init: unexpected mech type "
3953906Sgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
3954906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
3955906Sgm89044 	}
3956906Sgm89044 
3957906Sgm89044 	DBG(softc, DENTRY, "dca_decrypt_init: done, err = 0x%x", error);
3958906Sgm89044 
3959906Sgm89044 	if (error == CRYPTO_SUCCESS)
3960906Sgm89044 		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
3961906Sgm89044 		    &softc->dca_ctx_list_lock);
3962906Sgm89044 
3963906Sgm89044 	return (error);
3964906Sgm89044 }
3965906Sgm89044 
3966906Sgm89044 /* ARGSUSED */
3967906Sgm89044 static int
3968906Sgm89044 dca_decrypt(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
3969906Sgm89044     crypto_data_t *plaintext, crypto_req_handle_t req)
3970906Sgm89044 {
3971906Sgm89044 	int error = CRYPTO_FAILED;
3972906Sgm89044 	dca_t *softc;
3973906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
3974906Sgm89044 	int instance;
3975906Sgm89044 
3976906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
3977906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
3978906Sgm89044 
3979906Sgm89044 	/* extract softc and instance number from context */
3980906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
3981906Sgm89044 	DBG(softc, DENTRY, "dca_decrypt: started");
3982906Sgm89044 
3983*5063Sgm89044 	/* handle inplace ops */
3984*5063Sgm89044 	if (!plaintext) {
3985*5063Sgm89044 		dca_request_t *reqp = ctx->cc_provider_private;
3986*5063Sgm89044 		reqp->dr_flags |= DR_INPLACE;
3987*5063Sgm89044 		plaintext = ciphertext;
3988*5063Sgm89044 	}
3989*5063Sgm89044 
3990906Sgm89044 	/* check mechanism */
3991906Sgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
3992906Sgm89044 	case DES_CBC_MECH_INFO_TYPE:
3993906Sgm89044 		error = dca_3des(ctx, ciphertext, plaintext, req, DR_DECRYPT);
3994906Sgm89044 		break;
3995906Sgm89044 	case DES3_CBC_MECH_INFO_TYPE:
3996906Sgm89044 		error = dca_3des(ctx, ciphertext, plaintext, req,
3997906Sgm89044 		    DR_DECRYPT | DR_TRIPLE);
3998906Sgm89044 		break;
3999906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4000906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4001906Sgm89044 		error = dca_rsastart(ctx, ciphertext, plaintext, req,
4002906Sgm89044 		    DCA_RSA_DEC);
4003906Sgm89044 		break;
4004906Sgm89044 	default:
4005906Sgm89044 		/* Should never reach here */
4006906Sgm89044 		cmn_err(CE_WARN, "dca_decrypt: unexpected mech type "
4007906Sgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
4008906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4009906Sgm89044 	}
4010906Sgm89044 
4011906Sgm89044 	if ((error != CRYPTO_QUEUED) && (error != CRYPTO_SUCCESS) &&
4012906Sgm89044 	    (error != CRYPTO_BUFFER_TOO_SMALL)) {
4013906Sgm89044 		if (plaintext)
4014906Sgm89044 			plaintext->cd_length = 0;
4015906Sgm89044 	}
4016906Sgm89044 
4017906Sgm89044 	DBG(softc, DENTRY, "dca_decrypt: done, err = 0x%x", error);
4018906Sgm89044 
4019906Sgm89044 	return (error);
4020906Sgm89044 }
4021906Sgm89044 
4022906Sgm89044 /* ARGSUSED */
4023906Sgm89044 static int
4024906Sgm89044 dca_decrypt_update(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
4025906Sgm89044     crypto_data_t *plaintext, crypto_req_handle_t req)
4026906Sgm89044 {
4027906Sgm89044 	int error = CRYPTO_FAILED;
4028906Sgm89044 	dca_t *softc;
4029906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4030906Sgm89044 	int instance;
4031906Sgm89044 
4032906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
4033906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
4034906Sgm89044 
4035906Sgm89044 	/* extract softc and instance number from context */
4036906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4037906Sgm89044 	DBG(softc, DENTRY, "dca_decrypt_update: started");
4038906Sgm89044 
4039*5063Sgm89044 	/* handle inplace ops */
4040*5063Sgm89044 	if (!plaintext) {
4041*5063Sgm89044 		dca_request_t *reqp = ctx->cc_provider_private;
4042*5063Sgm89044 		reqp->dr_flags |= DR_INPLACE;
4043*5063Sgm89044 		plaintext = ciphertext;
4044*5063Sgm89044 	}
4045*5063Sgm89044 
4046906Sgm89044 	/* check mechanism */
4047906Sgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
4048906Sgm89044 	case DES_CBC_MECH_INFO_TYPE:
4049906Sgm89044 		error = dca_3desupdate(ctx, ciphertext, plaintext, req,
4050906Sgm89044 		    DR_DECRYPT);
4051906Sgm89044 		break;
4052906Sgm89044 	case DES3_CBC_MECH_INFO_TYPE:
4053906Sgm89044 		error = dca_3desupdate(ctx, ciphertext, plaintext, req,
4054906Sgm89044 		    DR_DECRYPT | DR_TRIPLE);
4055906Sgm89044 		break;
4056906Sgm89044 	default:
4057906Sgm89044 		/* Should never reach here */
4058906Sgm89044 		cmn_err(CE_WARN, "dca_decrypt_update: unexpected mech type "
4059906Sgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
4060906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4061906Sgm89044 	}
4062906Sgm89044 
4063906Sgm89044 	DBG(softc, DENTRY, "dca_decrypt_update: done, err = 0x%x", error);
4064906Sgm89044 
4065906Sgm89044 	return (error);
4066906Sgm89044 }
4067906Sgm89044 
4068906Sgm89044 /* ARGSUSED */
4069906Sgm89044 static int
4070906Sgm89044 dca_decrypt_final(crypto_ctx_t *ctx, crypto_data_t *plaintext,
4071906Sgm89044     crypto_req_handle_t req)
4072906Sgm89044 {
4073906Sgm89044 	int error = CRYPTO_FAILED;
4074906Sgm89044 	dca_t *softc;
4075906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4076906Sgm89044 	int instance;
4077906Sgm89044 
4078906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
4079906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
4080906Sgm89044 
4081906Sgm89044 	/* extract softc and instance number from context */
4082906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4083906Sgm89044 	DBG(softc, DENTRY, "dca_decrypt_final: started");
4084906Sgm89044 
4085906Sgm89044 	/* check mechanism */
4086906Sgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
4087906Sgm89044 	case DES_CBC_MECH_INFO_TYPE:
4088906Sgm89044 		error = dca_3desfinal(ctx, plaintext, DR_DECRYPT);
4089906Sgm89044 		break;
4090906Sgm89044 	case DES3_CBC_MECH_INFO_TYPE:
4091906Sgm89044 		error = dca_3desfinal(ctx, plaintext, DR_DECRYPT | DR_TRIPLE);
4092906Sgm89044 		break;
4093906Sgm89044 	default:
4094906Sgm89044 		/* Should never reach here */
4095906Sgm89044 		cmn_err(CE_WARN, "dca_decrypt_final: unexpected mech type "
4096906Sgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
4097906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4098906Sgm89044 	}
4099906Sgm89044 
4100906Sgm89044 	DBG(softc, DENTRY, "dca_decrypt_final: done, err = 0x%x", error);
4101906Sgm89044 
4102906Sgm89044 	return (error);
4103906Sgm89044 }
4104906Sgm89044 
4105906Sgm89044 /* ARGSUSED */
4106906Sgm89044 static int
4107906Sgm89044 dca_decrypt_atomic(crypto_provider_handle_t provider,
4108906Sgm89044     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
4109906Sgm89044     crypto_key_t *key, crypto_data_t *ciphertext, crypto_data_t *plaintext,
4110906Sgm89044     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
4111906Sgm89044 {
4112906Sgm89044 	int error = CRYPTO_FAILED;
4113906Sgm89044 	dca_t *softc = (dca_t *)provider;
4114906Sgm89044 
4115906Sgm89044 	DBG(softc, DENTRY, "dca_decrypt_atomic: started");
4116906Sgm89044 
4117906Sgm89044 	if (ctx_template != NULL)
4118906Sgm89044 		return (CRYPTO_ARGUMENTS_BAD);
4119906Sgm89044 
4120*5063Sgm89044 	/* handle inplace ops */
4121*5063Sgm89044 	if (!plaintext) {
4122*5063Sgm89044 		plaintext = ciphertext;
4123*5063Sgm89044 	}
4124*5063Sgm89044 
4125906Sgm89044 	/* check mechanism */
4126906Sgm89044 	switch (mechanism->cm_type) {
4127906Sgm89044 	case DES_CBC_MECH_INFO_TYPE:
4128906Sgm89044 		error = dca_3desatomic(provider, session_id, mechanism, key,
4129906Sgm89044 		    ciphertext, plaintext, KM_SLEEP, req,
4130906Sgm89044 		    DR_DECRYPT | DR_ATOMIC);
4131906Sgm89044 		break;
4132906Sgm89044 	case DES3_CBC_MECH_INFO_TYPE:
4133906Sgm89044 		error = dca_3desatomic(provider, session_id, mechanism, key,
4134906Sgm89044 		    ciphertext, plaintext, KM_SLEEP, req,
4135906Sgm89044 		    DR_DECRYPT | DR_TRIPLE | DR_ATOMIC);
4136906Sgm89044 		break;
4137906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4138906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4139906Sgm89044 		error = dca_rsaatomic(provider, session_id, mechanism, key,
4140906Sgm89044 		    ciphertext, plaintext, KM_SLEEP, req, DCA_RSA_DEC);
4141906Sgm89044 		break;
4142906Sgm89044 	default:
4143906Sgm89044 		cmn_err(CE_WARN, "dca_decrypt_atomic: unexpected mech type "
4144906Sgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
4145906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4146906Sgm89044 	}
4147906Sgm89044 
4148906Sgm89044 	if ((error != CRYPTO_QUEUED) && (error != CRYPTO_SUCCESS)) {
4149906Sgm89044 		plaintext->cd_length = 0;
4150906Sgm89044 	}
4151906Sgm89044 
4152906Sgm89044 	DBG(softc, DENTRY, "dca_decrypt_atomic: done, err = 0x%x", error);
4153906Sgm89044 
4154906Sgm89044 	return (error);
4155906Sgm89044 }
4156906Sgm89044 
4157906Sgm89044 /*
4158906Sgm89044  * Sign entry points.
4159906Sgm89044  */
4160906Sgm89044 
4161906Sgm89044 /* ARGSUSED */
4162906Sgm89044 static int
4163906Sgm89044 dca_sign_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
4164906Sgm89044     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
4165906Sgm89044     crypto_req_handle_t req)
4166906Sgm89044 {
4167906Sgm89044 	int error = CRYPTO_FAILED;
4168906Sgm89044 	dca_t *softc;
4169906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4170906Sgm89044 	int instance;
4171906Sgm89044 
4172906Sgm89044 	/* extract softc and instance number from context */
4173906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4174906Sgm89044 	DBG(softc, DENTRY, "dca_sign_init: started\n");
4175906Sgm89044 
4176906Sgm89044 	if (ctx_template != NULL)
4177906Sgm89044 		return (CRYPTO_ARGUMENTS_BAD);
4178906Sgm89044 
4179906Sgm89044 	/* check mechanism */
4180906Sgm89044 	switch (mechanism->cm_type) {
4181906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4182906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4183906Sgm89044 		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
4184906Sgm89044 		break;
4185906Sgm89044 	case DSA_MECH_INFO_TYPE:
4186906Sgm89044 		error = dca_dsainit(ctx, mechanism, key, KM_SLEEP,
4187906Sgm89044 		    DCA_DSA_SIGN);
4188906Sgm89044 		break;
4189906Sgm89044 	default:
4190906Sgm89044 		cmn_err(CE_WARN, "dca_sign_init: unexpected mech type "
4191906Sgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
4192906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4193906Sgm89044 	}
4194906Sgm89044 
4195906Sgm89044 	DBG(softc, DENTRY, "dca_sign_init: done, err = 0x%x", error);
4196906Sgm89044 
4197906Sgm89044 	if (error == CRYPTO_SUCCESS)
4198906Sgm89044 		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
4199906Sgm89044 		    &softc->dca_ctx_list_lock);
4200906Sgm89044 
4201906Sgm89044 	return (error);
4202906Sgm89044 }
4203906Sgm89044 
4204906Sgm89044 static int
4205906Sgm89044 dca_sign(crypto_ctx_t *ctx, crypto_data_t *data,
4206906Sgm89044     crypto_data_t *signature, crypto_req_handle_t req)
4207906Sgm89044 {
4208906Sgm89044 	int error = CRYPTO_FAILED;
4209906Sgm89044 	dca_t *softc;
4210906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4211906Sgm89044 	int instance;
4212906Sgm89044 
4213906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
4214906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
4215906Sgm89044 
4216906Sgm89044 	/* extract softc and instance number from context */
4217906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4218906Sgm89044 	DBG(softc, DENTRY, "dca_sign: started\n");
4219906Sgm89044 
4220906Sgm89044 	/* check mechanism */
4221906Sgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
4222906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4223906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4224906Sgm89044 		error = dca_rsastart(ctx, data, signature, req, DCA_RSA_SIGN);
4225906Sgm89044 		break;
4226906Sgm89044 	case DSA_MECH_INFO_TYPE:
4227906Sgm89044 		error = dca_dsa_sign(ctx, data, signature, req);
4228906Sgm89044 		break;
4229906Sgm89044 	default:
4230906Sgm89044 		cmn_err(CE_WARN, "dca_sign: unexpected mech type "
4231906Sgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
4232906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4233906Sgm89044 	}
4234906Sgm89044 
4235906Sgm89044 	DBG(softc, DENTRY, "dca_sign: done, err = 0x%x", error);
4236906Sgm89044 
4237906Sgm89044 	return (error);
4238906Sgm89044 }
4239906Sgm89044 
4240906Sgm89044 /* ARGSUSED */
4241906Sgm89044 static int
4242906Sgm89044 dca_sign_update(crypto_ctx_t *ctx, crypto_data_t *data,
4243906Sgm89044     crypto_req_handle_t req)
4244906Sgm89044 {
4245906Sgm89044 	int error = CRYPTO_MECHANISM_INVALID;
4246906Sgm89044 	dca_t *softc;
4247906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4248906Sgm89044 	int instance;
4249906Sgm89044 
4250906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
4251906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
4252906Sgm89044 
4253906Sgm89044 	/* extract softc and instance number from context */
4254906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4255906Sgm89044 	DBG(softc, DENTRY, "dca_sign_update: started\n");
4256906Sgm89044 
4257906Sgm89044 	cmn_err(CE_WARN, "dca_sign_update: unexpected mech type "
4258906Sgm89044 	    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
4259906Sgm89044 
4260906Sgm89044 	DBG(softc, DENTRY, "dca_sign_update: done, err = 0x%x", error);
4261906Sgm89044 
4262906Sgm89044 	return (error);
4263906Sgm89044 }
4264906Sgm89044 
4265906Sgm89044 /* ARGSUSED */
4266906Sgm89044 static int
4267906Sgm89044 dca_sign_final(crypto_ctx_t *ctx, crypto_data_t *signature,
4268906Sgm89044     crypto_req_handle_t req)
4269906Sgm89044 {
4270906Sgm89044 	int error = CRYPTO_MECHANISM_INVALID;
4271906Sgm89044 	dca_t *softc;
4272906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4273906Sgm89044 	int instance;
4274906Sgm89044 
4275906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
4276906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
4277906Sgm89044 
4278906Sgm89044 	/* extract softc and instance number from context */
4279906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4280906Sgm89044 	DBG(softc, DENTRY, "dca_sign_final: started\n");
4281906Sgm89044 
4282906Sgm89044 	cmn_err(CE_WARN, "dca_sign_final: unexpected mech type "
4283906Sgm89044 	    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
4284906Sgm89044 
4285906Sgm89044 	DBG(softc, DENTRY, "dca_sign_final: done, err = 0x%x", error);
4286906Sgm89044 
4287906Sgm89044 	return (error);
4288906Sgm89044 }
4289906Sgm89044 
4290906Sgm89044 static int
4291906Sgm89044 dca_sign_atomic(crypto_provider_handle_t provider,
4292906Sgm89044     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
4293906Sgm89044     crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
4294906Sgm89044     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
4295906Sgm89044 {
4296906Sgm89044 	int error = CRYPTO_FAILED;
4297906Sgm89044 	dca_t *softc = (dca_t *)provider;
4298906Sgm89044 
4299906Sgm89044 	DBG(softc, DENTRY, "dca_sign_atomic: started\n");
4300906Sgm89044 
4301906Sgm89044 	if (ctx_template != NULL)
4302906Sgm89044 		return (CRYPTO_ARGUMENTS_BAD);
4303906Sgm89044 
4304906Sgm89044 	/* check mechanism */
4305906Sgm89044 	switch (mechanism->cm_type) {
4306906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4307906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4308906Sgm89044 		error = dca_rsaatomic(provider, session_id, mechanism, key,
4309906Sgm89044 		    data, signature, KM_SLEEP, req, DCA_RSA_SIGN);
4310906Sgm89044 		break;
4311906Sgm89044 	case DSA_MECH_INFO_TYPE:
4312906Sgm89044 		error = dca_dsaatomic(provider, session_id, mechanism, key,
4313906Sgm89044 		    data, signature, KM_SLEEP, req, DCA_DSA_SIGN);
4314906Sgm89044 		break;
4315906Sgm89044 	default:
4316906Sgm89044 		cmn_err(CE_WARN, "dca_sign_atomic: unexpected mech type "
4317906Sgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
4318906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4319906Sgm89044 	}
4320906Sgm89044 
4321906Sgm89044 	DBG(softc, DENTRY, "dca_sign_atomic: done, err = 0x%x", error);
4322906Sgm89044 
4323906Sgm89044 	return (error);
4324906Sgm89044 }
4325906Sgm89044 
4326906Sgm89044 /* ARGSUSED */
4327906Sgm89044 static int
4328906Sgm89044 dca_sign_recover_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
4329906Sgm89044     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
4330906Sgm89044     crypto_req_handle_t req)
4331906Sgm89044 {
4332906Sgm89044 	int error = CRYPTO_FAILED;
4333906Sgm89044 	dca_t *softc;
4334906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4335906Sgm89044 	int instance;
4336906Sgm89044 
4337906Sgm89044 	/* extract softc and instance number from context */
4338906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4339906Sgm89044 	DBG(softc, DENTRY, "dca_sign_recover_init: started\n");
4340906Sgm89044 
4341906Sgm89044 	if (ctx_template != NULL)
4342906Sgm89044 		return (CRYPTO_ARGUMENTS_BAD);
4343906Sgm89044 
4344906Sgm89044 	/* check mechanism */
4345906Sgm89044 	switch (mechanism->cm_type) {
4346906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4347906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4348906Sgm89044 		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
4349906Sgm89044 		break;
4350906Sgm89044 	default:
4351906Sgm89044 		cmn_err(CE_WARN, "dca_sign_recover_init: unexpected mech type "
4352906Sgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
4353906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4354906Sgm89044 	}
4355906Sgm89044 
4356906Sgm89044 	DBG(softc, DENTRY, "dca_sign_recover_init: done, err = 0x%x", error);
4357906Sgm89044 
4358906Sgm89044 	if (error == CRYPTO_SUCCESS)
4359906Sgm89044 		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
4360906Sgm89044 		    &softc->dca_ctx_list_lock);
4361906Sgm89044 
4362906Sgm89044 	return (error);
4363906Sgm89044 }
4364906Sgm89044 
4365906Sgm89044 static int
4366906Sgm89044 dca_sign_recover(crypto_ctx_t *ctx, crypto_data_t *data,
4367906Sgm89044     crypto_data_t *signature, crypto_req_handle_t req)
4368906Sgm89044 {
4369906Sgm89044 	int error = CRYPTO_FAILED;
4370906Sgm89044 	dca_t *softc;
4371906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4372906Sgm89044 	int instance;
4373906Sgm89044 
4374906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
4375906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
4376906Sgm89044 
4377906Sgm89044 	/* extract softc and instance number from context */
4378906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4379906Sgm89044 	DBG(softc, DENTRY, "dca_sign_recover: started\n");
4380906Sgm89044 
4381906Sgm89044 	/* check mechanism */
4382906Sgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
4383906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4384906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4385906Sgm89044 		error = dca_rsastart(ctx, data, signature, req, DCA_RSA_SIGNR);
4386906Sgm89044 		break;
4387906Sgm89044 	default:
4388906Sgm89044 		cmn_err(CE_WARN, "dca_sign_recover: unexpected mech type "
4389906Sgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
4390906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4391906Sgm89044 	}
4392906Sgm89044 
4393906Sgm89044 	DBG(softc, DENTRY, "dca_sign_recover: done, err = 0x%x", error);
4394906Sgm89044 
4395906Sgm89044 	return (error);
4396906Sgm89044 }
4397906Sgm89044 
4398906Sgm89044 static int
4399906Sgm89044 dca_sign_recover_atomic(crypto_provider_handle_t provider,
4400906Sgm89044     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
4401906Sgm89044     crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
4402906Sgm89044     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
4403906Sgm89044 {
4404906Sgm89044 	int error = CRYPTO_FAILED;
4405906Sgm89044 	dca_t *softc = (dca_t *)provider;
4406906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4407906Sgm89044 	int instance;
4408906Sgm89044 
4409906Sgm89044 	instance = ddi_get_instance(softc->dca_dip);
4410906Sgm89044 	DBG(softc, DENTRY, "dca_sign_recover_atomic: started\n");
4411906Sgm89044 
4412906Sgm89044 	if (ctx_template != NULL)
4413906Sgm89044 		return (CRYPTO_ARGUMENTS_BAD);
4414906Sgm89044 
4415906Sgm89044 	/* check mechanism */
4416906Sgm89044 	switch (mechanism->cm_type) {
4417906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4418906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4419906Sgm89044 		error = dca_rsaatomic(provider, session_id, mechanism, key,
4420906Sgm89044 		    data, signature, KM_SLEEP, req, DCA_RSA_SIGNR);
4421906Sgm89044 		break;
4422906Sgm89044 	default:
4423906Sgm89044 		cmn_err(CE_WARN, "dca_sign_recover_atomic: unexpected mech type"
4424906Sgm89044 		    " 0x%llx\n", (unsigned long long)mechanism->cm_type);
4425906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4426906Sgm89044 	}
4427906Sgm89044 
4428906Sgm89044 	DBG(softc, DENTRY, "dca_sign_recover_atomic: done, err = 0x%x", error);
4429906Sgm89044 
4430906Sgm89044 	return (error);
4431906Sgm89044 }
4432906Sgm89044 
4433906Sgm89044 /*
4434906Sgm89044  * Verify entry points.
4435906Sgm89044  */
4436906Sgm89044 
4437906Sgm89044 /* ARGSUSED */
4438906Sgm89044 static int
4439906Sgm89044 dca_verify_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
4440906Sgm89044     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
4441906Sgm89044     crypto_req_handle_t req)
4442906Sgm89044 {
4443906Sgm89044 	int error = CRYPTO_FAILED;
4444906Sgm89044 	dca_t *softc;
4445906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4446906Sgm89044 	int instance;
4447906Sgm89044 
4448906Sgm89044 	/* extract softc and instance number from context */
4449906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4450906Sgm89044 	DBG(softc, DENTRY, "dca_verify_init: started\n");
4451906Sgm89044 
4452906Sgm89044 	if (ctx_template != NULL)
4453906Sgm89044 		return (CRYPTO_ARGUMENTS_BAD);
4454906Sgm89044 
4455906Sgm89044 	/* check mechanism */
4456906Sgm89044 	switch (mechanism->cm_type) {
4457906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4458906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4459906Sgm89044 		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
4460906Sgm89044 		break;
4461906Sgm89044 	case DSA_MECH_INFO_TYPE:
4462906Sgm89044 		error = dca_dsainit(ctx, mechanism, key, KM_SLEEP,
4463906Sgm89044 		    DCA_DSA_VRFY);
4464906Sgm89044 		break;
4465906Sgm89044 	default:
4466906Sgm89044 		cmn_err(CE_WARN, "dca_verify_init: unexpected mech type "
4467906Sgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
4468906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4469906Sgm89044 	}
4470906Sgm89044 
4471906Sgm89044 	DBG(softc, DENTRY, "dca_verify_init: done, err = 0x%x", error);
4472906Sgm89044 
4473906Sgm89044 	if (error == CRYPTO_SUCCESS)
4474906Sgm89044 		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
4475906Sgm89044 		    &softc->dca_ctx_list_lock);
4476906Sgm89044 
4477906Sgm89044 	return (error);
4478906Sgm89044 }
4479906Sgm89044 
4480906Sgm89044 static int
4481906Sgm89044 dca_verify(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *signature,
4482906Sgm89044     crypto_req_handle_t req)
4483906Sgm89044 {
4484906Sgm89044 	int error = CRYPTO_FAILED;
4485906Sgm89044 	dca_t *softc;
4486906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4487906Sgm89044 	int instance;
4488906Sgm89044 
4489906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
4490906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
4491906Sgm89044 
4492906Sgm89044 	/* extract softc and instance number from context */
4493906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4494906Sgm89044 	DBG(softc, DENTRY, "dca_verify: started\n");
4495906Sgm89044 
4496906Sgm89044 	/* check mechanism */
4497906Sgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
4498906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4499906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4500906Sgm89044 		error = dca_rsastart(ctx, signature, data, req, DCA_RSA_VRFY);
4501906Sgm89044 		break;
4502906Sgm89044 	case DSA_MECH_INFO_TYPE:
4503906Sgm89044 		error = dca_dsa_verify(ctx, data, signature, req);
4504906Sgm89044 		break;
4505906Sgm89044 	default:
4506906Sgm89044 		cmn_err(CE_WARN, "dca_verify: unexpected mech type "
4507906Sgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
4508906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4509906Sgm89044 	}
4510906Sgm89044 
4511906Sgm89044 	DBG(softc, DENTRY, "dca_verify: done, err = 0x%x", error);
4512906Sgm89044 
4513906Sgm89044 	return (error);
4514906Sgm89044 }
4515906Sgm89044 
4516906Sgm89044 /* ARGSUSED */
4517906Sgm89044 static int
4518906Sgm89044 dca_verify_update(crypto_ctx_t *ctx, crypto_data_t *data,
4519906Sgm89044     crypto_req_handle_t req)
4520906Sgm89044 {
4521906Sgm89044 	int error = CRYPTO_MECHANISM_INVALID;
4522906Sgm89044 	dca_t *softc;
4523906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4524906Sgm89044 	int instance;
4525906Sgm89044 
4526906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
4527906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
4528906Sgm89044 
4529906Sgm89044 	/* extract softc and instance number from context */
4530906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4531906Sgm89044 	DBG(softc, DENTRY, "dca_verify_update: started\n");
4532906Sgm89044 
4533906Sgm89044 	cmn_err(CE_WARN, "dca_verify_update: unexpected mech type "
4534906Sgm89044 	    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
4535906Sgm89044 
4536906Sgm89044 	DBG(softc, DENTRY, "dca_verify_update: done, err = 0x%x", error);
4537906Sgm89044 
4538906Sgm89044 	return (error);
4539906Sgm89044 }
4540906Sgm89044 
4541906Sgm89044 /* ARGSUSED */
4542906Sgm89044 static int
4543906Sgm89044 dca_verify_final(crypto_ctx_t *ctx, crypto_data_t *signature,
4544906Sgm89044     crypto_req_handle_t req)
4545906Sgm89044 {
4546906Sgm89044 	int error = CRYPTO_MECHANISM_INVALID;
4547906Sgm89044 	dca_t *softc;
4548906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4549906Sgm89044 	int instance;
4550906Sgm89044 
4551906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
4552906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
4553906Sgm89044 
4554906Sgm89044 	/* extract softc and instance number from context */
4555906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4556906Sgm89044 	DBG(softc, DENTRY, "dca_verify_final: started\n");
4557906Sgm89044 
4558906Sgm89044 	cmn_err(CE_WARN, "dca_verify_final: unexpected mech type "
4559906Sgm89044 	    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
4560906Sgm89044 
4561906Sgm89044 	DBG(softc, DENTRY, "dca_verify_final: done, err = 0x%x", error);
4562906Sgm89044 
4563906Sgm89044 	return (error);
4564906Sgm89044 }
4565906Sgm89044 
4566906Sgm89044 static int
4567906Sgm89044 dca_verify_atomic(crypto_provider_handle_t provider,
4568906Sgm89044     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
4569906Sgm89044     crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
4570906Sgm89044     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
4571906Sgm89044 {
4572906Sgm89044 	int error = CRYPTO_FAILED;
4573906Sgm89044 	dca_t *softc = (dca_t *)provider;
4574906Sgm89044 
4575906Sgm89044 	DBG(softc, DENTRY, "dca_verify_atomic: started\n");
4576906Sgm89044 
4577906Sgm89044 	if (ctx_template != NULL)
4578906Sgm89044 		return (CRYPTO_ARGUMENTS_BAD);
4579906Sgm89044 
4580906Sgm89044 	/* check mechanism */
4581906Sgm89044 	switch (mechanism->cm_type) {
4582906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4583906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4584906Sgm89044 		error = dca_rsaatomic(provider, session_id, mechanism, key,
4585906Sgm89044 		    signature, data, KM_SLEEP, req, DCA_RSA_VRFY);
4586906Sgm89044 		break;
4587906Sgm89044 	case DSA_MECH_INFO_TYPE:
4588906Sgm89044 		error = dca_dsaatomic(provider, session_id, mechanism, key,
4589906Sgm89044 		    data, signature, KM_SLEEP, req, DCA_DSA_VRFY);
4590906Sgm89044 		break;
4591906Sgm89044 	default:
4592906Sgm89044 		cmn_err(CE_WARN, "dca_verify_atomic: unexpected mech type "
4593906Sgm89044 		    "0x%llx\n", (unsigned long long)mechanism->cm_type);
4594906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4595906Sgm89044 	}
4596906Sgm89044 
4597906Sgm89044 	DBG(softc, DENTRY, "dca_verify_atomic: done, err = 0x%x", error);
4598906Sgm89044 
4599906Sgm89044 	return (error);
4600906Sgm89044 }
4601906Sgm89044 
4602906Sgm89044 /* ARGSUSED */
4603906Sgm89044 static int
4604906Sgm89044 dca_verify_recover_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
4605906Sgm89044     crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
4606906Sgm89044     crypto_req_handle_t req)
4607906Sgm89044 {
4608906Sgm89044 	int error = CRYPTO_MECHANISM_INVALID;
4609906Sgm89044 	dca_t *softc;
4610906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4611906Sgm89044 	int instance;
4612906Sgm89044 
4613906Sgm89044 	/* extract softc and instance number from context */
4614906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4615906Sgm89044 	DBG(softc, DENTRY, "dca_verify_recover_init: started\n");
4616906Sgm89044 
4617906Sgm89044 	if (ctx_template != NULL)
4618906Sgm89044 		return (CRYPTO_ARGUMENTS_BAD);
4619906Sgm89044 
4620906Sgm89044 	/* check mechanism */
4621906Sgm89044 	switch (mechanism->cm_type) {
4622906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4623906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4624906Sgm89044 		error = dca_rsainit(ctx, mechanism, key, KM_SLEEP);
4625906Sgm89044 		break;
4626906Sgm89044 	default:
4627906Sgm89044 		cmn_err(CE_WARN, "dca_verify_recover_init: unexpected mech type"
4628906Sgm89044 		    " 0x%llx\n", (unsigned long long)mechanism->cm_type);
4629906Sgm89044 	}
4630906Sgm89044 
4631906Sgm89044 	DBG(softc, DENTRY, "dca_verify_recover_init: done, err = 0x%x", error);
4632906Sgm89044 
4633906Sgm89044 	if (error == CRYPTO_SUCCESS)
4634906Sgm89044 		dca_enlist2(&softc->dca_ctx_list, ctx->cc_provider_private,
4635906Sgm89044 		    &softc->dca_ctx_list_lock);
4636906Sgm89044 
4637906Sgm89044 	return (error);
4638906Sgm89044 }
4639906Sgm89044 
4640906Sgm89044 static int
4641906Sgm89044 dca_verify_recover(crypto_ctx_t *ctx, crypto_data_t *signature,
4642906Sgm89044     crypto_data_t *data, crypto_req_handle_t req)
4643906Sgm89044 {
4644906Sgm89044 	int error = CRYPTO_MECHANISM_INVALID;
4645906Sgm89044 	dca_t *softc;
4646906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4647906Sgm89044 	int instance;
4648906Sgm89044 
4649906Sgm89044 	if (!ctx || !ctx->cc_provider || !ctx->cc_provider_private)
4650906Sgm89044 		return (CRYPTO_OPERATION_NOT_INITIALIZED);
4651906Sgm89044 
4652906Sgm89044 	/* extract softc and instance number from context */
4653906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4654906Sgm89044 	DBG(softc, DENTRY, "dca_verify_recover: started\n");
4655906Sgm89044 
4656906Sgm89044 	/* check mechanism */
4657906Sgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
4658906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4659906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4660906Sgm89044 		error = dca_rsastart(ctx, signature, data, req, DCA_RSA_VRFYR);
4661906Sgm89044 		break;
4662906Sgm89044 	default:
4663906Sgm89044 		cmn_err(CE_WARN, "dca_verify_recover: unexpected mech type "
4664906Sgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
4665906Sgm89044 	}
4666906Sgm89044 
4667906Sgm89044 	DBG(softc, DENTRY, "dca_verify_recover: done, err = 0x%x", error);
4668906Sgm89044 
4669906Sgm89044 	return (error);
4670906Sgm89044 }
4671906Sgm89044 
4672906Sgm89044 static int
4673906Sgm89044 dca_verify_recover_atomic(crypto_provider_handle_t provider,
4674906Sgm89044     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
4675906Sgm89044     crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
4676906Sgm89044     crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
4677906Sgm89044 {
4678906Sgm89044 	int error = CRYPTO_MECHANISM_INVALID;
4679906Sgm89044 	dca_t *softc = (dca_t *)provider;
4680906Sgm89044 
4681906Sgm89044 	DBG(softc, DENTRY, "dca_verify_recover_atomic: started\n");
4682906Sgm89044 
4683906Sgm89044 	if (ctx_template != NULL)
4684906Sgm89044 		return (CRYPTO_ARGUMENTS_BAD);
4685906Sgm89044 
4686906Sgm89044 	/* check mechanism */
4687906Sgm89044 	switch (mechanism->cm_type) {
4688906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4689906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4690906Sgm89044 		error = dca_rsaatomic(provider, session_id, mechanism, key,
4691906Sgm89044 		    signature, data, KM_SLEEP, req, DCA_RSA_VRFYR);
4692906Sgm89044 		break;
4693906Sgm89044 	default:
4694906Sgm89044 		cmn_err(CE_WARN, "dca_verify_recover_atomic: unexpected mech "
4695906Sgm89044 		    "type 0x%llx\n", (unsigned long long)mechanism->cm_type);
4696906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4697906Sgm89044 	}
4698906Sgm89044 
4699906Sgm89044 	DBG(softc, DENTRY,
4700906Sgm89044 	    "dca_verify_recover_atomic: done, err = 0x%x", error);
4701906Sgm89044 
4702906Sgm89044 	return (error);
4703906Sgm89044 }
4704906Sgm89044 
4705906Sgm89044 /*
4706906Sgm89044  * Random number entry points.
4707906Sgm89044  */
4708906Sgm89044 
4709906Sgm89044 /* ARGSUSED */
4710906Sgm89044 static int
4711906Sgm89044 dca_generate_random(crypto_provider_handle_t provider,
4712906Sgm89044     crypto_session_id_t session_id,
4713906Sgm89044     uchar_t *buf, size_t len, crypto_req_handle_t req)
4714906Sgm89044 {
4715906Sgm89044 	int error = CRYPTO_FAILED;
4716906Sgm89044 	dca_t *softc = (dca_t *)provider;
4717906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4718906Sgm89044 	int instance;
4719906Sgm89044 
4720906Sgm89044 	instance = ddi_get_instance(softc->dca_dip);
4721906Sgm89044 	DBG(softc, DENTRY, "dca_generate_random: started");
4722906Sgm89044 
4723906Sgm89044 	error = dca_rng(softc, buf, len, req);
4724906Sgm89044 
4725906Sgm89044 	DBG(softc, DENTRY, "dca_generate_random: done, err = 0x%x", error);
4726906Sgm89044 
4727906Sgm89044 	return (error);
4728906Sgm89044 }
4729906Sgm89044 
4730906Sgm89044 /*
4731906Sgm89044  * Context management entry points.
4732906Sgm89044  */
4733906Sgm89044 
4734906Sgm89044 int
4735906Sgm89044 dca_free_context(crypto_ctx_t *ctx)
4736906Sgm89044 {
4737906Sgm89044 	int error = CRYPTO_SUCCESS;
4738906Sgm89044 	dca_t *softc;
4739906Sgm89044 	/* LINTED E_FUNC_SET_NOT_USED */
4740906Sgm89044 	int instance;
4741906Sgm89044 
4742906Sgm89044 	/* extract softc and instance number from context */
4743906Sgm89044 	DCA_SOFTC_FROM_CTX(ctx, softc, instance);
4744906Sgm89044 	DBG(softc, DENTRY, "dca_free_context: entered");
4745906Sgm89044 
4746906Sgm89044 	if (ctx->cc_provider_private == NULL)
4747906Sgm89044 		return (error);
4748906Sgm89044 
4749906Sgm89044 	dca_rmlist2(ctx->cc_provider_private, &softc->dca_ctx_list_lock);
4750906Sgm89044 
4751906Sgm89044 	error = dca_free_context_low(ctx);
4752906Sgm89044 
4753906Sgm89044 	DBG(softc, DENTRY, "dca_free_context: done, err = 0x%x", error);
4754906Sgm89044 
4755906Sgm89044 	return (error);
4756906Sgm89044 }
4757906Sgm89044 
4758906Sgm89044 static int
4759906Sgm89044 dca_free_context_low(crypto_ctx_t *ctx)
4760906Sgm89044 {
4761906Sgm89044 	int error = CRYPTO_SUCCESS;
4762906Sgm89044 
4763906Sgm89044 	/* check mechanism */
4764906Sgm89044 	switch (DCA_MECH_FROM_CTX(ctx)) {
4765906Sgm89044 	case DES_CBC_MECH_INFO_TYPE:
4766906Sgm89044 	case DES3_CBC_MECH_INFO_TYPE:
4767906Sgm89044 		dca_3desctxfree(ctx);
4768906Sgm89044 		break;
4769906Sgm89044 	case RSA_PKCS_MECH_INFO_TYPE:
4770906Sgm89044 	case RSA_X_509_MECH_INFO_TYPE:
4771906Sgm89044 		dca_rsactxfree(ctx);
4772906Sgm89044 		break;
4773906Sgm89044 	case DSA_MECH_INFO_TYPE:
4774906Sgm89044 		dca_dsactxfree(ctx);
4775906Sgm89044 		break;
4776906Sgm89044 	default:
4777906Sgm89044 		/* Should never reach here */
4778906Sgm89044 		cmn_err(CE_WARN, "dca_free_context_low: unexpected mech type "
4779906Sgm89044 		    "0x%llx\n", (unsigned long long)DCA_MECH_FROM_CTX(ctx));
4780906Sgm89044 		error = CRYPTO_MECHANISM_INVALID;
4781906Sgm89044 	}
4782906Sgm89044 
4783906Sgm89044 	return (error);
4784906Sgm89044 }
4785906Sgm89044 
4786906Sgm89044 
4787906Sgm89044 /* Free any unfreed private context. It is called in detach. */
4788906Sgm89044 static void
4789906Sgm89044 dca_free_context_list(dca_t *dca)
4790906Sgm89044 {
4791906Sgm89044 	dca_listnode_t	*node;
4792906Sgm89044 	crypto_ctx_t	ctx;
4793906Sgm89044 
4794906Sgm89044 	(void) memset(&ctx, 0, sizeof (ctx));
4795906Sgm89044 	ctx.cc_provider = dca;
4796906Sgm89044 
4797906Sgm89044 	while ((node = dca_delist2(&dca->dca_ctx_list,
4798906Sgm89044 	    &dca->dca_ctx_list_lock)) != NULL) {
4799906Sgm89044 		ctx.cc_provider_private = node;
4800906Sgm89044 		(void) dca_free_context_low(&ctx);
4801906Sgm89044 	}
4802906Sgm89044 }
4803906Sgm89044 
4804906Sgm89044 static int
4805906Sgm89044 ext_info_sym(crypto_provider_handle_t prov,
4806906Sgm89044     crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq)
4807906Sgm89044 {
4808906Sgm89044 	return (ext_info_base(prov, ext_info, cfreq, IDENT_SYM));
4809906Sgm89044 }
4810906Sgm89044 
4811906Sgm89044 static int
4812906Sgm89044 ext_info_asym(crypto_provider_handle_t prov,
4813906Sgm89044     crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq)
4814906Sgm89044 {
4815906Sgm89044 	int rv;
4816906Sgm89044 
4817906Sgm89044 	rv = ext_info_base(prov, ext_info, cfreq, IDENT_ASYM);
4818906Sgm89044 	/* The asymmetric cipher slot supports random */
4819906Sgm89044 	ext_info->ei_flags |= CRYPTO_EXTF_RNG;
4820906Sgm89044 
4821906Sgm89044 	return (rv);
4822906Sgm89044 }
4823906Sgm89044 
4824906Sgm89044 /* ARGSUSED */
4825906Sgm89044 static int
4826906Sgm89044 ext_info_base(crypto_provider_handle_t prov,
4827906Sgm89044     crypto_provider_ext_info_t *ext_info, crypto_req_handle_t cfreq, char *id)
4828906Sgm89044 {
4829906Sgm89044 	dca_t   *dca = (dca_t *)prov;
4830906Sgm89044 	int len;
4831906Sgm89044 
4832906Sgm89044 	/* Label */
4833906Sgm89044 	(void) sprintf((char *)ext_info->ei_label, "%s/%d %s",
4834906Sgm89044 	    ddi_driver_name(dca->dca_dip), ddi_get_instance(dca->dca_dip), id);
4835906Sgm89044 	len = strlen((char *)ext_info->ei_label);
4836906Sgm89044 	(void) memset(ext_info->ei_label + len, ' ',
4837906Sgm89044 	    CRYPTO_EXT_SIZE_LABEL - len);
4838906Sgm89044 
4839906Sgm89044 	/* Manufacturer ID */
4840906Sgm89044 	(void) sprintf((char *)ext_info->ei_manufacturerID, "%s",
4841*5063Sgm89044 	    DCA_MANUFACTURER_ID);
4842906Sgm89044 	len = strlen((char *)ext_info->ei_manufacturerID);
4843906Sgm89044 	(void) memset(ext_info->ei_manufacturerID + len, ' ',
4844906Sgm89044 	    CRYPTO_EXT_SIZE_MANUF - len);
4845906Sgm89044 
4846906Sgm89044 	/* Model */
4847906Sgm89044 	(void) sprintf((char *)ext_info->ei_model, dca->dca_model);
4848906Sgm89044 
4849906Sgm89044 	DBG(dca, DWARN, "kCF MODEL: %s", (char *)ext_info->ei_model);
4850906Sgm89044 
4851906Sgm89044 	len = strlen((char *)ext_info->ei_model);
4852906Sgm89044 	(void) memset(ext_info->ei_model + len, ' ',
4853*5063Sgm89044 	    CRYPTO_EXT_SIZE_MODEL - len);
4854906Sgm89044 
4855906Sgm89044 	/* Serial Number. Blank for Deimos */
4856906Sgm89044 	(void) memset(ext_info->ei_serial_number, ' ', CRYPTO_EXT_SIZE_SERIAL);
4857906Sgm89044 
4858906Sgm89044 	ext_info->ei_flags = CRYPTO_EXTF_WRITE_PROTECTED;
4859906Sgm89044 
4860906Sgm89044 	ext_info->ei_max_session_count = CRYPTO_UNAVAILABLE_INFO;
4861906Sgm89044 	ext_info->ei_max_pin_len = CRYPTO_UNAVAILABLE_INFO;
4862906Sgm89044 	ext_info->ei_min_pin_len = CRYPTO_UNAVAILABLE_INFO;
4863906Sgm89044 	ext_info->ei_total_public_memory = CRYPTO_UNAVAILABLE_INFO;
4864906Sgm89044 	ext_info->ei_free_public_memory = CRYPTO_UNAVAILABLE_INFO;
4865906Sgm89044 	ext_info->ei_total_private_memory = CRYPTO_UNAVAILABLE_INFO;
4866906Sgm89044 	ext_info->ei_free_private_memory = CRYPTO_UNAVAILABLE_INFO;
4867906Sgm89044 	ext_info->ei_hardware_version.cv_major = 0;
4868906Sgm89044 	ext_info->ei_hardware_version.cv_minor = 0;
4869906Sgm89044 	ext_info->ei_firmware_version.cv_major = 0;
4870906Sgm89044 	ext_info->ei_firmware_version.cv_minor = 0;
4871906Sgm89044 
4872906Sgm89044 	/* Time. No need to be supplied for token without a clock */
4873906Sgm89044 	ext_info->ei_time[0] = '\000';
4874906Sgm89044 
4875906Sgm89044 	return (CRYPTO_SUCCESS);
4876906Sgm89044 }
4877906Sgm89044 
4878906Sgm89044 static void
4879906Sgm89044 dca_fma_init(dca_t *dca)
4880906Sgm89044 {
4881906Sgm89044 	ddi_iblock_cookie_t fm_ibc;
4882906Sgm89044 	int fm_capabilities = DDI_FM_EREPORT_CAPABLE |
4883*5063Sgm89044 	    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE |
4884*5063Sgm89044 	    DDI_FM_ERRCB_CAPABLE;
4885906Sgm89044 
4886906Sgm89044 	/* Read FMA capabilities from dca.conf file (if present) */
4887906Sgm89044 	dca->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, dca->dca_dip,
4888906Sgm89044 	    DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "fm-capable",
4889906Sgm89044 	    fm_capabilities);
4890906Sgm89044 
4891906Sgm89044 	DBG(dca, DWARN, "dca->fm_capabilities = 0x%x", dca->fm_capabilities);
4892906Sgm89044 
4893906Sgm89044 	/* Only register with IO Fault Services if we have some capability */
4894906Sgm89044 	if (dca->fm_capabilities) {
4895906Sgm89044 		dca_regsattr.devacc_attr_access = DDI_FLAGERR_ACC;
4896906Sgm89044 		dca_devattr.devacc_attr_access = DDI_FLAGERR_ACC;
4897906Sgm89044 		dca_dmaattr.dma_attr_flags = DDI_DMA_FLAGERR;
4898906Sgm89044 
4899906Sgm89044 		/* Register capabilities with IO Fault Services */
4900906Sgm89044 		ddi_fm_init(dca->dca_dip, &dca->fm_capabilities, &fm_ibc);
4901906Sgm89044 		DBG(dca, DWARN, "fm_capable() =  0x%x",
4902906Sgm89044 		    ddi_fm_capable(dca->dca_dip));
4903906Sgm89044 
4904906Sgm89044 		/*
4905906Sgm89044 		 * Initialize pci ereport capabilities if ereport capable
4906906Sgm89044 		 */
49071865Sdilpreet 		if (DDI_FM_EREPORT_CAP(dca->fm_capabilities) ||
49081865Sdilpreet 		    DDI_FM_ERRCB_CAP(dca->fm_capabilities))
4909906Sgm89044 			pci_ereport_setup(dca->dca_dip);
4910906Sgm89044 
4911906Sgm89044 		/*
4912906Sgm89044 		 * Initialize callback mutex and register error callback if
4913906Sgm89044 		 * error callback capable.
4914906Sgm89044 		 */
4915906Sgm89044 		if (DDI_FM_ERRCB_CAP(dca->fm_capabilities)) {
4916906Sgm89044 			ddi_fm_handler_register(dca->dca_dip, dca_fm_error_cb,
4917906Sgm89044 			    (void *)dca);
4918906Sgm89044 		}
4919906Sgm89044 	} else {
4920906Sgm89044 		/*
4921906Sgm89044 		 * These fields have to be cleared of FMA if there are no
4922906Sgm89044 		 * FMA capabilities at runtime.
4923906Sgm89044 		 */
4924906Sgm89044 		dca_regsattr.devacc_attr_access = DDI_DEFAULT_ACC;
4925906Sgm89044 		dca_devattr.devacc_attr_access = DDI_DEFAULT_ACC;
4926906Sgm89044 		dca_dmaattr.dma_attr_flags = 0;
4927906Sgm89044 	}
4928906Sgm89044 }
4929906Sgm89044 
4930906Sgm89044 
4931906Sgm89044 static void
4932906Sgm89044 dca_fma_fini(dca_t *dca)
4933906Sgm89044 {
4934906Sgm89044 	/* Only unregister FMA capabilities if we registered some */
4935906Sgm89044 	if (dca->fm_capabilities) {
4936906Sgm89044 
4937906Sgm89044 		/*
4938906Sgm89044 		 * Release any resources allocated by pci_ereport_setup()
4939906Sgm89044 		 */
49401865Sdilpreet 		if (DDI_FM_EREPORT_CAP(dca->fm_capabilities) ||
49411865Sdilpreet 		    DDI_FM_ERRCB_CAP(dca->fm_capabilities)) {
4942906Sgm89044 			pci_ereport_teardown(dca->dca_dip);
4943906Sgm89044 		}
4944906Sgm89044 
4945906Sgm89044 		/*
4946906Sgm89044 		 * Free callback mutex and un-register error callback if
4947906Sgm89044 		 * error callback capable.
4948906Sgm89044 		 */
4949906Sgm89044 		if (DDI_FM_ERRCB_CAP(dca->fm_capabilities)) {
4950906Sgm89044 			ddi_fm_handler_unregister(dca->dca_dip);
4951906Sgm89044 		}
4952906Sgm89044 
4953906Sgm89044 		/* Unregister from IO Fault Services */
4954906Sgm89044 		ddi_fm_fini(dca->dca_dip);
4955906Sgm89044 		DBG(dca, DWARN, "fm_capable() = 0x%x",
4956906Sgm89044 		    ddi_fm_capable(dca->dca_dip));
4957906Sgm89044 	}
4958906Sgm89044 }
4959906Sgm89044 
4960906Sgm89044 
4961906Sgm89044 /*
4962906Sgm89044  * The IO fault service error handling callback function
4963906Sgm89044  */
49641865Sdilpreet /*ARGSUSED*/
4965906Sgm89044 static int
4966906Sgm89044 dca_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data)
4967906Sgm89044 {
4968906Sgm89044 	dca_t		*dca = (dca_t *)impl_data;
49691865Sdilpreet 
49701865Sdilpreet 	pci_ereport_post(dip, err, NULL);
49711865Sdilpreet 	if (err->fme_status == DDI_FM_FATAL) {
4972906Sgm89044 		dca_failure(dca, DDI_DATAPATH_FAULT,
4973906Sgm89044 		    DCA_FM_ECLASS_NONE, dca_ena(0), CRYPTO_DEVICE_ERROR,
4974906Sgm89044 		    "fault PCI in FMA callback.");
4975906Sgm89044 	}
49761865Sdilpreet 	return (err->fme_status);
4977906Sgm89044 }
4978906Sgm89044 
4979906Sgm89044 
4980906Sgm89044 static int
4981906Sgm89044 dca_check_acc_handle(dca_t *dca, ddi_acc_handle_t handle,
4982906Sgm89044     dca_fma_eclass_t eclass_index)
4983906Sgm89044 {
4984906Sgm89044 	ddi_fm_error_t	de;
4985906Sgm89044 	int		version = 0;
49861865Sdilpreet 
49871865Sdilpreet 	ddi_fm_acc_err_get(handle, &de, version);
49881865Sdilpreet 	if (de.fme_status != DDI_FM_OK) {
49891865Sdilpreet 		dca_failure(dca, DDI_DATAPATH_FAULT,
49901865Sdilpreet 		    eclass_index, fm_ena_increment(de.fme_ena),
49911865Sdilpreet 		    CRYPTO_DEVICE_ERROR, "");
49921865Sdilpreet 		return (DDI_FAILURE);
4993906Sgm89044 	}
4994906Sgm89044 
4995906Sgm89044 	return (DDI_SUCCESS);
4996906Sgm89044 }
4997906Sgm89044 
4998906Sgm89044 int
4999906Sgm89044 dca_check_dma_handle(dca_t *dca, ddi_dma_handle_t handle,
5000906Sgm89044     dca_fma_eclass_t eclass_index)
5001906Sgm89044 {
5002906Sgm89044 	ddi_fm_error_t	de;
5003906Sgm89044 	int		version = 0;
50041865Sdilpreet 
50051865Sdilpreet 	ddi_fm_dma_err_get(handle, &de, version);
50061865Sdilpreet 	if (de.fme_status != DDI_FM_OK) {
50071865Sdilpreet 		dca_failure(dca, DDI_DATAPATH_FAULT,
50081865Sdilpreet 		    eclass_index, fm_ena_increment(de.fme_ena),
50091865Sdilpreet 		    CRYPTO_DEVICE_ERROR, "");
50101865Sdilpreet 		return (DDI_FAILURE);
5011906Sgm89044 	}
5012906Sgm89044 	return (DDI_SUCCESS);
5013906Sgm89044 }
5014906Sgm89044 
5015906Sgm89044 static uint64_t
5016906Sgm89044 dca_ena(uint64_t ena)
5017906Sgm89044 {
5018906Sgm89044 	if (ena == 0)
5019906Sgm89044 		ena = fm_ena_generate(0, FM_ENA_FMT1);
5020906Sgm89044 	else
5021906Sgm89044 		ena = fm_ena_increment(ena);
5022906Sgm89044 	return (ena);
5023906Sgm89044 }
5024906Sgm89044 
5025906Sgm89044 static char *
5026906Sgm89044 dca_fma_eclass_string(char *model, dca_fma_eclass_t index)
5027906Sgm89044 {
5028906Sgm89044 	if (strstr(model, "500"))
5029906Sgm89044 		return (dca_fma_eclass_sca500[index]);
5030906Sgm89044 	else
5031906Sgm89044 		return (dca_fma_eclass_sca1000[index]);
5032906Sgm89044 }
5033