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