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 }