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.
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.
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]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * Deimos - cryptographic acceleration based upon Broadcom 582x.
33 #include <sys/types.h>
35 #include <sys/sunddi.h>
37 #include <sys/crypto/dca.h>
38 #include <sys/atomic.h>
41 * Random number implementation.
44 static int dca_rngstart(dca_t
*, dca_request_t
*);
45 static void dca_rngdone(dca_request_t
*, int);
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();
53 dca_rng(dca_t
*dca
, uchar_t
*buf
, size_t len
, crypto_req_handle_t req
)
59 if ((reqp
= dca_getreq(dca
, MCR2
, 1)) == NULL
) {
60 dca_error(dca
, "unable to allocate request for RNG");
61 return (CRYPTO_HOST_MEMORY
);
64 reqp
->dr_kcf_req
= req
;
66 data
= &reqp
->dr_ctx
.in_dup
;
67 data
->cd_format
= CRYPTO_DATA_RAW
;
70 data
->cd_raw
.iov_base
= (char *)buf
;
71 data
->cd_raw
.iov_len
= len
;
75 rv
= dca_rngstart(dca
, reqp
);
76 if (rv
!= CRYPTO_QUEUED
) {
86 dca_rngstart(dca_t
*dca
, dca_request_t
*reqp
)
91 crypto_data_t
*out
= reqp
->dr_out
;
93 if (dca
->dca_flags
& DCA_RNGSHA1
) {
94 reqp
->dr_job_stat
= DS_RNGSHA1JOBS
;
95 reqp
->dr_byte_stat
= DS_RNGSHA1BYTES
;
98 reqp
->dr_job_stat
= DS_RNGJOBS
;
99 reqp
->dr_byte_stat
= DS_RNGBYTES
;
103 len
= out
->cd_raw
.iov_len
- out
->cd_length
;
104 len
= min(len
, MAXPACKET
& ~0xf);
105 chunk
= ROUNDUP(len
, sizeof (uint32_t));
107 if ((len
< dca_mindma
) ||
108 dca_sgcheck(dca
, reqp
->dr_out
, DCA_SG_WALIGN
)) {
109 reqp
->dr_flags
|= DR_SCATTER
;
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
);
119 reqp
->dr_in_paddr
= 0;
120 reqp
->dr_in_next
= 0;
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
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
;
134 reqp
->dr_out_len
= chunk
;
136 reqp
->dr_param
.dp_rng
.dr_chunklen
= len
;
137 reqp
->dr_pkt_length
= (uint16_t)chunk
;
138 reqp
->dr_callback
= dca_rngdone
;
140 /* write out the context structure */
141 PUTCTX16(reqp
, CTX_LENGTH
, CTX_RNG_LENGTH
);
142 PUTCTX16(reqp
, CTX_CMD
, cmd
);
144 /* schedule the work by doing a submit */
145 return (dca_start(dca
, reqp
, MCR2
, 1));
149 dca_rngdone(dca_request_t
*reqp
, int errno
)
151 if (errno
== CRYPTO_SUCCESS
) {
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
) !=
159 reqp
->destroy
= TRUE
;
160 errno
= CRYPTO_DEVICE_ERROR
;
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
) {
169 reqp
->dr_out
->cd_length
+=
170 reqp
->dr_param
.dp_rng
.dr_chunklen
;
174 * If there is more to do, then reschedule another
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
) {
187 if (reqp
->dr_kcf_req
) {
188 /* notify framework that request is completed */
189 crypto_op_notification(reqp
->dr_kcf_req
, errno
);
191 /* For internal random number generation */
192 dca_random_done(reqp
->dr_dca
);
196 "dca_rngdone: returning %d to the kef via crypto_op_notification",
199 dca_destroyreq(reqp
);
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
210 #define RANDOM_BUFFER_SIZE (1<<15)
211 #define DCA_RANDOM_MAX_WAIT 10000
214 dca_random_init(dca_t
*dca
)
216 /* Mutex for the local random number pool */
217 mutex_init(&dca
->dca_random_lock
, NULL
, MUTEX_DRIVER
, NULL
);
219 if ((dca
->dca_buf1
= kmem_alloc(RANDOM_BUFFER_SIZE
, KM_SLEEP
)) ==
221 mutex_destroy(&dca
->dca_random_lock
);
222 return (CRYPTO_FAILED
);
225 if ((dca
->dca_buf2
= kmem_alloc(RANDOM_BUFFER_SIZE
, KM_SLEEP
)) ==
227 mutex_destroy(&dca
->dca_random_lock
);
228 kmem_free(dca
->dca_buf1
, RANDOM_BUFFER_SIZE
);
229 return (CRYPTO_FAILED
);
232 return (CRYPTO_SUCCESS
);
236 dca_random_fini(dca_t
*dca
)
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
);
245 dca_random_buffer(dca_t
*dca
, caddr_t buf
, int len
)
251 mutex_enter(&dca
->dca_random_lock
);
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
);
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
);
266 atomic_or_32(&dca
->dca_random_filling
, 0x1);
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
;
275 if (dca
->dca_index
>= RANDOM_BUFFER_SIZE
) {
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
)
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
);
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
;
294 dca
->dca_buf_ptr
= dca
->dca_buf1
;
295 fill_buf
= dca
->dca_buf2
;
298 atomic_or_32(&dca
->dca_random_filling
, 0x1);
301 if ((rv
= dca_rng(dca
, (uchar_t
*)fill_buf
,
302 RANDOM_BUFFER_SIZE
, NULL
)) != CRYPTO_QUEUED
) {
303 mutex_exit(&dca
->dca_random_lock
);
308 if (dca
->dca_buf_ptr
[dca
->dca_index
] != '\0')
309 buf
[i
++] = dca
->dca_buf_ptr
[dca
->dca_index
];
314 mutex_exit(&dca
->dca_random_lock
);
316 DBG(NULL
, DENTRY
, "dca_random_buffer: i: %d", i
);
317 return (CRYPTO_SUCCESS
);
321 dca_random_done(dca_t
*dca
)
323 DBG(NULL
, DENTRY
, "dca_random_done");
324 atomic_and_32(&dca
->dca_random_filling
, 0x0);