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 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 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 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 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 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 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 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