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