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