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