xref: /illumos-gate/usr/src/uts/common/crypto/io/dca_rng.c (revision 88f8b78a)
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 
44 static int dca_rngstart(dca_t *, dca_request_t *);
45 static void dca_rngdone(dca_request_t *, int);
46 
47 static void dca_random_done();
48 int dca_random_buffer(dca_t *dca, caddr_t buf, int len);
49 int dca_random_init();
50 void dca_random_fini();
51 
52 int
dca_rng(dca_t * dca,uchar_t * buf,size_t len,crypto_req_handle_t req)53 dca_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 
85 int
dca_rngstart(dca_t * dca,dca_request_t * reqp)86 dca_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 
148 void
dca_rngdone(dca_request_t * reqp,int errno)149 dca_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 
185 errout:
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 
213 int
dca_random_init(dca_t * dca)214 dca_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 
235 void
dca_random_fini(dca_t * dca)236 dca_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 
244 int
dca_random_buffer(dca_t * dca,caddr_t buf,int len)245 dca_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 
320 static void
dca_random_done(dca_t * dca)321 dca_random_done(dca_t *dca)
322 {
323 	DBG(NULL, DENTRY, "dca_random_done");
324 	atomic_and_32(&dca->dca_random_filling, 0x0);
325 }
326