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         dca->dca_buf1 = kmem_alloc(RANDOM_BUFFER_SIZE, KM_SLEEP);
 218 
 219         dca->dca_buf2 = kmem_alloc(RANDOM_BUFFER_SIZE, KM_SLEEP);
 220 
 221         return (CRYPTO_SUCCESS);
 222 }
 223 
 224 void
 225 dca_random_fini(dca_t *dca)
 226 {
 227         kmem_free(dca->dca_buf1, RANDOM_BUFFER_SIZE);
 228         kmem_free(dca->dca_buf2, RANDOM_BUFFER_SIZE);
 229         dca->dca_buf1 = dca->dca_buf2 = dca->dca_buf_ptr = NULL;
 230         (void) mutex_destroy(&dca->dca_random_lock);
 231 }
 232 
 233 int
 234 dca_random_buffer(dca_t *dca, caddr_t buf, int len)
 235 {
 236         int rv;
 237         int i, j;
 238         char *fill_buf;
 239 
 240         mutex_enter(&dca->dca_random_lock);
 241 
 242         if (dca->dca_buf_ptr == NULL) {
 243                 if (dca->dca_buf1 == NULL || dca->dca_buf2 == NULL) {
 244                         mutex_exit(&dca->dca_random_lock);
 245                         return (CRYPTO_FAILED);
 246                 }
 247 
 248                 /* Very first time. Let us fill the first buffer */
 249                 if (dca_rng(dca, (uchar_t *)dca->dca_buf1, RANDOM_BUFFER_SIZE,
 250                     NULL) != CRYPTO_QUEUED) {
 251                         mutex_exit(&dca->dca_random_lock);
 252                         return (CRYPTO_FAILED);
 253                 }
 254 
 255                 atomic_or_32(&dca->dca_random_filling, 0x1);
 256 
 257                 /* Pretend we are using buffer2 and it is empty */
 258                 dca->dca_buf_ptr = dca->dca_buf2;
 259                 dca->dca_index = RANDOM_BUFFER_SIZE;
 260         }
 261 
 262         i = 0;
 263         while (i < len) {
 264                 if (dca->dca_index >= RANDOM_BUFFER_SIZE) {
 265                         j = 0;
 266                         while (dca->dca_random_filling) {
 267                                 /* Only wait here at the first time */
 268                                 delay(drv_usectohz(100));
 269                                 if (j++ >= DCA_RANDOM_MAX_WAIT)
 270                                         break;
 271                         }
 272                         DBG(NULL, DENTRY, "dca_random_buffer: j: %d", j);
 273                         if (j > DCA_RANDOM_MAX_WAIT) {
 274                                 mutex_exit(&dca->dca_random_lock);
 275                                 return (CRYPTO_FAILED);
 276                         }
 277 
 278                         /* switch to the other buffer */
 279                         if (dca->dca_buf_ptr == dca->dca_buf1) {
 280                                 dca->dca_buf_ptr = dca->dca_buf2;
 281                                 fill_buf = dca->dca_buf1;
 282                         } else {
 283                                 dca->dca_buf_ptr = dca->dca_buf1;
 284                                 fill_buf = dca->dca_buf2;
 285                         }
 286 
 287                         atomic_or_32(&dca->dca_random_filling, 0x1);
 288                         dca->dca_index = 0;
 289 
 290                         if ((rv = dca_rng(dca, (uchar_t *)fill_buf,
 291                             RANDOM_BUFFER_SIZE, NULL)) != CRYPTO_QUEUED) {
 292                                 mutex_exit(&dca->dca_random_lock);
 293                                 return (rv);
 294                         }
 295                 }
 296 
 297                 if (dca->dca_buf_ptr[dca->dca_index] != '\0')
 298                         buf[i++] = dca->dca_buf_ptr[dca->dca_index];
 299 
 300                 dca->dca_index++;
 301         }
 302 
 303         mutex_exit(&dca->dca_random_lock);
 304 
 305         DBG(NULL, DENTRY, "dca_random_buffer: i: %d", i);
 306         return (CRYPTO_SUCCESS);
 307 }
 308 
 309 static void
 310 dca_random_done(dca_t *dca)
 311 {
 312         DBG(NULL, DENTRY, "dca_random_done");
 313         atomic_and_32(&dca->dca_random_filling, 0x0);
 314 }