xref: /onnv-gate/usr/src/uts/common/crypto/io/dca_rng.c (revision 906:1b5a12bcb15b)
1*906Sgm89044 /*
2*906Sgm89044  * CDDL HEADER START
3*906Sgm89044  *
4*906Sgm89044  * The contents of this file are subject to the terms of the
5*906Sgm89044  * Common Development and Distribution License (the "License").
6*906Sgm89044  * You may not use this file except in compliance with the License.
7*906Sgm89044  *
8*906Sgm89044  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*906Sgm89044  * or http://www.opensolaris.org/os/licensing.
10*906Sgm89044  * See the License for the specific language governing permissions
11*906Sgm89044  * and limitations under the License.
12*906Sgm89044  *
13*906Sgm89044  * When distributing Covered Code, include this CDDL HEADER in each
14*906Sgm89044  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*906Sgm89044  * If applicable, add the following below this CDDL HEADER, with the
16*906Sgm89044  * fields enclosed by brackets "[]" replaced with your own identifying
17*906Sgm89044  * information: Portions Copyright [yyyy] [name of copyright owner]
18*906Sgm89044  *
19*906Sgm89044  * CDDL HEADER END
20*906Sgm89044  */
21*906Sgm89044 
22*906Sgm89044 /*
23*906Sgm89044  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*906Sgm89044  * Use is subject to license terms.
25*906Sgm89044  */
26*906Sgm89044 
27*906Sgm89044 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*906Sgm89044 
29*906Sgm89044 /*
30*906Sgm89044  * Deimos - cryptographic acceleration based upon Broadcom 582x.
31*906Sgm89044  */
32*906Sgm89044 
33*906Sgm89044 #include <sys/types.h>
34*906Sgm89044 #include <sys/ddi.h>
35*906Sgm89044 #include <sys/sunddi.h>
36*906Sgm89044 #include <sys/kmem.h>
37*906Sgm89044 #include <sys/crypto/dca.h>
38*906Sgm89044 #include <sys/atomic.h>
39*906Sgm89044 
40*906Sgm89044 /*
41*906Sgm89044  * Random number implementation.
42*906Sgm89044  */
43*906Sgm89044 
44*906Sgm89044 static int dca_rngstart(dca_t *, dca_request_t *);
45*906Sgm89044 static void dca_rngdone(dca_request_t *, int);
46*906Sgm89044 
47*906Sgm89044 static void dca_random_done();
48*906Sgm89044 int dca_random_buffer(dca_t *dca, caddr_t buf, int len);
49*906Sgm89044 int dca_random_init();
50*906Sgm89044 void dca_random_fini();
51*906Sgm89044 
52*906Sgm89044 int
dca_rng(dca_t * dca,uchar_t * buf,size_t len,crypto_req_handle_t req)53*906Sgm89044 dca_rng(dca_t *dca, uchar_t *buf, size_t len, crypto_req_handle_t req)
54*906Sgm89044 {
55*906Sgm89044 	dca_request_t	*reqp;
56*906Sgm89044 	int		rv;
57*906Sgm89044 	crypto_data_t	*data;
58*906Sgm89044 
59*906Sgm89044 	if ((reqp = dca_getreq(dca, MCR2, 1)) == NULL) {
60*906Sgm89044 		dca_error(dca, "unable to allocate request for RNG");
61*906Sgm89044 		return (CRYPTO_HOST_MEMORY);
62*906Sgm89044 	}
63*906Sgm89044 
64*906Sgm89044 	reqp->dr_kcf_req = req;
65*906Sgm89044 
66*906Sgm89044 	data = &reqp->dr_ctx.in_dup;
67*906Sgm89044 	data->cd_format = CRYPTO_DATA_RAW;
68*906Sgm89044 	data->cd_offset = 0;
69*906Sgm89044 	data->cd_length = 0;
70*906Sgm89044 	data->cd_raw.iov_base = (char *)buf;
71*906Sgm89044 	data->cd_raw.iov_len = len;
72*906Sgm89044 	reqp->dr_out = data;
73*906Sgm89044 	reqp->dr_in = NULL;
74*906Sgm89044 
75*906Sgm89044 	rv = dca_rngstart(dca, reqp);
76*906Sgm89044 	if (rv != CRYPTO_QUEUED) {
77*906Sgm89044 		if (reqp->destroy)
78*906Sgm89044 			dca_destroyreq(reqp);
79*906Sgm89044 		else
80*906Sgm89044 			dca_freereq(reqp);
81*906Sgm89044 	}
82*906Sgm89044 	return (rv);
83*906Sgm89044 }
84*906Sgm89044 
85*906Sgm89044 int
dca_rngstart(dca_t * dca,dca_request_t * reqp)86*906Sgm89044 dca_rngstart(dca_t *dca, dca_request_t *reqp)
87*906Sgm89044 {
88*906Sgm89044 	uint16_t	cmd;
89*906Sgm89044 	size_t		len;
90*906Sgm89044 	uint16_t	chunk;
91*906Sgm89044 	crypto_data_t	*out = reqp->dr_out;
92*906Sgm89044 
93*906Sgm89044 	if (dca->dca_flags & DCA_RNGSHA1) {
94*906Sgm89044 		reqp->dr_job_stat = DS_RNGSHA1JOBS;
95*906Sgm89044 		reqp->dr_byte_stat = DS_RNGSHA1BYTES;
96*906Sgm89044 		cmd = CMD_RNGSHA1;
97*906Sgm89044 	} else {
98*906Sgm89044 		reqp->dr_job_stat = DS_RNGJOBS;
99*906Sgm89044 		reqp->dr_byte_stat = DS_RNGBYTES;
100*906Sgm89044 		cmd = CMD_RNGDIRECT;
101*906Sgm89044 	}
102*906Sgm89044 
103*906Sgm89044 	len = out->cd_raw.iov_len - out->cd_length;
104*906Sgm89044 	len = min(len, MAXPACKET & ~0xf);
105*906Sgm89044 	chunk = ROUNDUP(len, sizeof (uint32_t));
106*906Sgm89044 
107*906Sgm89044 	if ((len < dca_mindma) ||
108*906Sgm89044 	    dca_sgcheck(dca, reqp->dr_out, DCA_SG_WALIGN)) {
109*906Sgm89044 		reqp->dr_flags |= DR_SCATTER;
110*906Sgm89044 	}
111*906Sgm89044 
112*906Sgm89044 	/* Try to do direct DMA. */
113*906Sgm89044 	if (!(reqp->dr_flags & DR_SCATTER)) {
114*906Sgm89044 		if (dca_bindchains(reqp, 0, len) != DDI_SUCCESS) {
115*906Sgm89044 			return (CRYPTO_DEVICE_ERROR);
116*906Sgm89044 		}
117*906Sgm89044 	}
118*906Sgm89044 
119*906Sgm89044 	reqp->dr_in_paddr = 0;
120*906Sgm89044 	reqp->dr_in_next = 0;
121*906Sgm89044 	reqp->dr_in_len = 0;
122*906Sgm89044 
123*906Sgm89044 	/*
124*906Sgm89044 	 * Setup for scattering the result back out
125*906Sgm89044 	 * Using the pre-mapped buffers to store random numbers. Since the
126*906Sgm89044 	 * data buffer is a linked list, we need to transfer its head to MCR
127*906Sgm89044 	 */
128*906Sgm89044 	if (reqp->dr_flags & DR_SCATTER) {
129*906Sgm89044 		reqp->dr_out_paddr = reqp->dr_obuf_head.dc_buffer_paddr;
130*906Sgm89044 		reqp->dr_out_next = reqp->dr_obuf_head.dc_next_paddr;
131*906Sgm89044 		if (chunk > reqp->dr_obuf_head.dc_buffer_length)
132*906Sgm89044 			reqp->dr_out_len = reqp->dr_obuf_head.dc_buffer_length;
133*906Sgm89044 		else
134*906Sgm89044 			reqp->dr_out_len = chunk;
135*906Sgm89044 	}
136*906Sgm89044 	reqp->dr_param.dp_rng.dr_chunklen = len;
137*906Sgm89044 	reqp->dr_pkt_length = (uint16_t)chunk;
138*906Sgm89044 	reqp->dr_callback = dca_rngdone;
139*906Sgm89044 
140*906Sgm89044 	/* write out the context structure */
141*906Sgm89044 	PUTCTX16(reqp, CTX_LENGTH, CTX_RNG_LENGTH);
142*906Sgm89044 	PUTCTX16(reqp, CTX_CMD, cmd);
143*906Sgm89044 
144*906Sgm89044 	/* schedule the work by doing a submit */
145*906Sgm89044 	return (dca_start(dca, reqp, MCR2, 1));
146*906Sgm89044 }
147*906Sgm89044 
148*906Sgm89044 void
dca_rngdone(dca_request_t * reqp,int errno)149*906Sgm89044 dca_rngdone(dca_request_t *reqp, int errno)
150*906Sgm89044 {
151*906Sgm89044 	if (errno == CRYPTO_SUCCESS) {
152*906Sgm89044 
153*906Sgm89044 		if (reqp->dr_flags & DR_SCATTER) {
154*906Sgm89044 			(void) ddi_dma_sync(reqp->dr_obuf_dmah, 0,
155*906Sgm89044 				reqp->dr_out_len, DDI_DMA_SYNC_FORKERNEL);
156*906Sgm89044 			if (dca_check_dma_handle(reqp->dr_dca,
157*906Sgm89044 			    reqp->dr_obuf_dmah, DCA_FM_ECLASS_NONE) !=
158*906Sgm89044 			    DDI_SUCCESS) {
159*906Sgm89044 				reqp->destroy = TRUE;
160*906Sgm89044 				errno = CRYPTO_DEVICE_ERROR;
161*906Sgm89044 				goto errout;
162*906Sgm89044 			}
163*906Sgm89044 			errno = dca_scatter(reqp->dr_obuf_kaddr,
164*906Sgm89044 			    reqp->dr_out, reqp->dr_param.dp_rng.dr_chunklen, 0);
165*906Sgm89044 			if (errno != CRYPTO_SUCCESS) {
166*906Sgm89044 				goto errout;
167*906Sgm89044 			}
168*906Sgm89044 		} else {
169*906Sgm89044 			reqp->dr_out->cd_length +=
170*906Sgm89044 			    reqp->dr_param.dp_rng.dr_chunklen;
171*906Sgm89044 		}
172*906Sgm89044 
173*906Sgm89044 		/*
174*906Sgm89044 		 * If there is more to do, then reschedule another
175*906Sgm89044 		 * pass.
176*906Sgm89044 		 */
177*906Sgm89044 		if (reqp->dr_out->cd_length < reqp->dr_out->cd_raw.iov_len) {
178*906Sgm89044 			errno = dca_rngstart(reqp->dr_dca, reqp);
179*906Sgm89044 			if (errno == CRYPTO_QUEUED) {
180*906Sgm89044 				return;
181*906Sgm89044 			}
182*906Sgm89044 		}
183*906Sgm89044 	}
184*906Sgm89044 
185*906Sgm89044 errout:
186*906Sgm89044 
187*906Sgm89044 	if (reqp->dr_kcf_req) {
188*906Sgm89044 		/* notify framework that request is completed */
189*906Sgm89044 		crypto_op_notification(reqp->dr_kcf_req, errno);
190*906Sgm89044 	} else {
191*906Sgm89044 		/* For internal random number generation */
192*906Sgm89044 		dca_random_done(reqp->dr_dca);
193*906Sgm89044 	}
194*906Sgm89044 
195*906Sgm89044 	DBG(NULL, DINTR,
196*906Sgm89044 	    "dca_rngdone: returning %d to the kef via crypto_op_notification",
197*906Sgm89044 	    errno);
198*906Sgm89044 	if (reqp->destroy)
199*906Sgm89044 		dca_destroyreq(reqp);
200*906Sgm89044 	else
201*906Sgm89044 		dca_freereq(reqp);
202*906Sgm89044 }
203*906Sgm89044 
204*906Sgm89044 /*
205*906Sgm89044  * This gives a 32k random bytes per buffer. The two buffers will switch back
206*906Sgm89044  * and forth. When a buffer is used up, a request will be submitted to refill
207*906Sgm89044  * this buffer before switching to the other one
208*906Sgm89044  */
209*906Sgm89044 
210*906Sgm89044 #define	RANDOM_BUFFER_SIZE		(1<<15)
211*906Sgm89044 #define	DCA_RANDOM_MAX_WAIT		10000
212*906Sgm89044 
213*906Sgm89044 int
dca_random_init(dca_t * dca)214*906Sgm89044 dca_random_init(dca_t *dca)
215*906Sgm89044 {
216*906Sgm89044 	/* Mutex for the local random number pool */
217*906Sgm89044 	mutex_init(&dca->dca_random_lock, NULL, MUTEX_DRIVER, NULL);
218*906Sgm89044 
219*906Sgm89044 	if ((dca->dca_buf1 = kmem_alloc(RANDOM_BUFFER_SIZE, KM_SLEEP)) ==
220*906Sgm89044 	    NULL) {
221*906Sgm89044 		mutex_destroy(&dca->dca_random_lock);
222*906Sgm89044 		return (CRYPTO_FAILED);
223*906Sgm89044 	}
224*906Sgm89044 
225*906Sgm89044 	if ((dca->dca_buf2 = kmem_alloc(RANDOM_BUFFER_SIZE, KM_SLEEP)) ==
226*906Sgm89044 	    NULL) {
227*906Sgm89044 		mutex_destroy(&dca->dca_random_lock);
228*906Sgm89044 		kmem_free(dca->dca_buf1, RANDOM_BUFFER_SIZE);
229*906Sgm89044 		return (CRYPTO_FAILED);
230*906Sgm89044 	}
231*906Sgm89044 
232*906Sgm89044 	return (CRYPTO_SUCCESS);
233*906Sgm89044 }
234*906Sgm89044 
235*906Sgm89044 void
dca_random_fini(dca_t * dca)236*906Sgm89044 dca_random_fini(dca_t *dca)
237*906Sgm89044 {
238*906Sgm89044 	kmem_free(dca->dca_buf1, RANDOM_BUFFER_SIZE);
239*906Sgm89044 	kmem_free(dca->dca_buf2, RANDOM_BUFFER_SIZE);
240*906Sgm89044 	dca->dca_buf1 = dca->dca_buf2 = dca->dca_buf_ptr = NULL;
241*906Sgm89044 	(void) mutex_destroy(&dca->dca_random_lock);
242*906Sgm89044 }
243*906Sgm89044 
244*906Sgm89044 int
dca_random_buffer(dca_t * dca,caddr_t buf,int len)245*906Sgm89044 dca_random_buffer(dca_t *dca, caddr_t buf, int len)
246*906Sgm89044 {
247*906Sgm89044 	int rv;
248*906Sgm89044 	int i, j;
249*906Sgm89044 	char *fill_buf;
250*906Sgm89044 
251*906Sgm89044 	mutex_enter(&dca->dca_random_lock);
252*906Sgm89044 
253*906Sgm89044 	if (dca->dca_buf_ptr == NULL) {
254*906Sgm89044 		if (dca->dca_buf1 == NULL || dca->dca_buf2 == NULL) {
255*906Sgm89044 			mutex_exit(&dca->dca_random_lock);
256*906Sgm89044 			return (CRYPTO_FAILED);
257*906Sgm89044 		}
258*906Sgm89044 
259*906Sgm89044 		/* Very first time. Let us fill the first buffer */
260*906Sgm89044 		if (dca_rng(dca, (uchar_t *)dca->dca_buf1, RANDOM_BUFFER_SIZE,
261*906Sgm89044 		    NULL) != CRYPTO_QUEUED) {
262*906Sgm89044 			mutex_exit(&dca->dca_random_lock);
263*906Sgm89044 			return (CRYPTO_FAILED);
264*906Sgm89044 		}
265*906Sgm89044 
266*906Sgm89044 		atomic_or_32(&dca->dca_random_filling, 0x1);
267*906Sgm89044 
268*906Sgm89044 		/* Pretend we are using buffer2 and it is empty */
269*906Sgm89044 		dca->dca_buf_ptr = dca->dca_buf2;
270*906Sgm89044 		dca->dca_index = RANDOM_BUFFER_SIZE;
271*906Sgm89044 	}
272*906Sgm89044 
273*906Sgm89044 	i = 0;
274*906Sgm89044 	while (i < len) {
275*906Sgm89044 		if (dca->dca_index >= RANDOM_BUFFER_SIZE) {
276*906Sgm89044 			j = 0;
277*906Sgm89044 			while (dca->dca_random_filling) {
278*906Sgm89044 				/* Only wait here at the first time */
279*906Sgm89044 				delay(drv_usectohz(100));
280*906Sgm89044 				if (j++ >= DCA_RANDOM_MAX_WAIT)
281*906Sgm89044 					break;
282*906Sgm89044 			}
283*906Sgm89044 			DBG(NULL, DENTRY, "dca_random_buffer: j: %d", j);
284*906Sgm89044 			if (j > DCA_RANDOM_MAX_WAIT) {
285*906Sgm89044 				mutex_exit(&dca->dca_random_lock);
286*906Sgm89044 				return (CRYPTO_FAILED);
287*906Sgm89044 			}
288*906Sgm89044 
289*906Sgm89044 			/* switch to the other buffer */
290*906Sgm89044 			if (dca->dca_buf_ptr == dca->dca_buf1) {
291*906Sgm89044 				dca->dca_buf_ptr = dca->dca_buf2;
292*906Sgm89044 				fill_buf = dca->dca_buf1;
293*906Sgm89044 			} else {
294*906Sgm89044 				dca->dca_buf_ptr = dca->dca_buf1;
295*906Sgm89044 				fill_buf = dca->dca_buf2;
296*906Sgm89044 			}
297*906Sgm89044 
298*906Sgm89044 			atomic_or_32(&dca->dca_random_filling, 0x1);
299*906Sgm89044 			dca->dca_index = 0;
300*906Sgm89044 
301*906Sgm89044 			if ((rv = dca_rng(dca, (uchar_t *)fill_buf,
302*906Sgm89044 			    RANDOM_BUFFER_SIZE, NULL)) != CRYPTO_QUEUED) {
303*906Sgm89044 				mutex_exit(&dca->dca_random_lock);
304*906Sgm89044 				return (rv);
305*906Sgm89044 			}
306*906Sgm89044 		}
307*906Sgm89044 
308*906Sgm89044 		if (dca->dca_buf_ptr[dca->dca_index] != '\0')
309*906Sgm89044 			buf[i++] = dca->dca_buf_ptr[dca->dca_index];
310*906Sgm89044 
311*906Sgm89044 		dca->dca_index++;
312*906Sgm89044 	}
313*906Sgm89044 
314*906Sgm89044 	mutex_exit(&dca->dca_random_lock);
315*906Sgm89044 
316*906Sgm89044 	DBG(NULL, DENTRY, "dca_random_buffer: i: %d", i);
317*906Sgm89044 	return (CRYPTO_SUCCESS);
318*906Sgm89044 }
319*906Sgm89044 
320*906Sgm89044 static void
dca_random_done(dca_t * dca)321*906Sgm89044 dca_random_done(dca_t *dca)
322*906Sgm89044 {
323*906Sgm89044 	DBG(NULL, DENTRY, "dca_random_done");
324*906Sgm89044 	atomic_and_32(&dca->dca_random_filling, 0x0);
325*906Sgm89044 }
326