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