The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/random/randomdev_soft.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2000-2009 Mark R V Murray
    3  * Copyright (c) 2004 Robert N. M. Watson
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer
   11  *    in this position and unchanged.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   26  *
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD: releng/8.1/sys/dev/random/randomdev_soft.c 192774 2009-05-25 22:50:11Z markm $");
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/bus.h>
   35 #include <sys/conf.h>
   36 #include <sys/fcntl.h>
   37 #include <sys/kernel.h>
   38 #include <sys/kthread.h>
   39 #include <sys/lock.h>
   40 #include <sys/malloc.h>
   41 #include <sys/mutex.h>
   42 #include <sys/poll.h>
   43 #include <sys/proc.h>
   44 #include <sys/random.h>
   45 #include <sys/selinfo.h>
   46 #include <sys/sysctl.h>
   47 #include <sys/uio.h>
   48 #include <sys/unistd.h>
   49 
   50 #include <machine/bus.h>
   51 #include <machine/cpu.h>
   52 
   53 #include <dev/random/randomdev.h>
   54 #include <dev/random/randomdev_soft.h>
   55 
   56 #define RANDOM_FIFO_MAX 256     /* How many events to queue up */
   57 
   58 static void random_kthread(void *);
   59 static void
   60 random_harvest_internal(u_int64_t, const void *, u_int,
   61     u_int, u_int, enum esource);
   62 static int random_yarrow_poll(int event,struct thread *td);
   63 static int random_yarrow_block(int flag);
   64 static void random_yarrow_flush_reseed(void);
   65 
   66 struct random_systat random_yarrow = {
   67         .ident = "Software, Yarrow",
   68         .init = random_yarrow_init,
   69         .deinit = random_yarrow_deinit,
   70         .block = random_yarrow_block,
   71         .read = random_yarrow_read,
   72         .write = random_yarrow_write,
   73         .poll = random_yarrow_poll,
   74         .reseed = random_yarrow_flush_reseed,
   75         .seeded = 1,
   76 };
   77 
   78 MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers");
   79 
   80 /*
   81  * The harvest mutex protects the consistency of the entropy fifos and
   82  * empty fifo.
   83  */
   84 struct mtx      harvest_mtx;
   85 
   86 /* Lockable FIFO queue holding entropy buffers */
   87 struct entropyfifo {
   88         int count;
   89         STAILQ_HEAD(harvestlist, harvest) head;
   90 };
   91 
   92 /* Empty entropy buffers */
   93 static struct entropyfifo emptyfifo;
   94 
   95 #define EMPTYBUFFERS    1024
   96 
   97 /* Harvested entropy */
   98 static struct entropyfifo harvestfifo[ENTROPYSOURCE];
   99 
  100 /* <0 to end the kthread, 0 to let it run, 1 to flush the harvest queues */
  101 static int random_kthread_control = 0;
  102 
  103 static struct proc *random_kthread_proc;
  104 
  105 /* List for the dynamic sysctls */
  106 struct sysctl_ctx_list random_clist;
  107 
  108 /* ARGSUSED */
  109 static int
  110 random_check_boolean(SYSCTL_HANDLER_ARGS)
  111 {
  112         if (oidp->oid_arg1 != NULL && *(u_int *)(oidp->oid_arg1) != 0)
  113                 *(u_int *)(oidp->oid_arg1) = 1;
  114         return sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req);
  115 }
  116 
  117 /* ARGSUSED */
  118 void
  119 random_yarrow_init(void)
  120 {
  121         int error, i;
  122         struct harvest *np;
  123         struct sysctl_oid *random_o, *random_sys_o, *random_sys_harvest_o;
  124         enum esource e;
  125 
  126         random_o = SYSCTL_ADD_NODE(&random_clist,
  127             SYSCTL_STATIC_CHILDREN(_kern),
  128             OID_AUTO, "random", CTLFLAG_RW, 0,
  129             "Software Random Number Generator");
  130 
  131         random_yarrow_init_alg(&random_clist, random_o);
  132 
  133         random_sys_o = SYSCTL_ADD_NODE(&random_clist,
  134             SYSCTL_CHILDREN(random_o),
  135             OID_AUTO, "sys", CTLFLAG_RW, 0,
  136             "Entropy Device Parameters");
  137 
  138         SYSCTL_ADD_PROC(&random_clist,
  139             SYSCTL_CHILDREN(random_sys_o),
  140             OID_AUTO, "seeded", CTLTYPE_INT | CTLFLAG_RW,
  141             &random_systat.seeded, 1, random_check_boolean, "I",
  142             "Seeded State");
  143 
  144         random_sys_harvest_o = SYSCTL_ADD_NODE(&random_clist,
  145             SYSCTL_CHILDREN(random_sys_o),
  146             OID_AUTO, "harvest", CTLFLAG_RW, 0,
  147             "Entropy Sources");
  148 
  149         SYSCTL_ADD_PROC(&random_clist,
  150             SYSCTL_CHILDREN(random_sys_harvest_o),
  151             OID_AUTO, "ethernet", CTLTYPE_INT | CTLFLAG_RW,
  152             &harvest.ethernet, 1, random_check_boolean, "I",
  153             "Harvest NIC entropy");
  154         SYSCTL_ADD_PROC(&random_clist,
  155             SYSCTL_CHILDREN(random_sys_harvest_o),
  156             OID_AUTO, "point_to_point", CTLTYPE_INT | CTLFLAG_RW,
  157             &harvest.point_to_point, 1, random_check_boolean, "I",
  158             "Harvest serial net entropy");
  159         SYSCTL_ADD_PROC(&random_clist,
  160             SYSCTL_CHILDREN(random_sys_harvest_o),
  161             OID_AUTO, "interrupt", CTLTYPE_INT | CTLFLAG_RW,
  162             &harvest.interrupt, 1, random_check_boolean, "I",
  163             "Harvest IRQ entropy");
  164         SYSCTL_ADD_PROC(&random_clist,
  165             SYSCTL_CHILDREN(random_sys_harvest_o),
  166             OID_AUTO, "swi", CTLTYPE_INT | CTLFLAG_RW,
  167             &harvest.swi, 0, random_check_boolean, "I",
  168             "Harvest SWI entropy");
  169 
  170         /* Initialise the harvest fifos */
  171         STAILQ_INIT(&emptyfifo.head);
  172         emptyfifo.count = 0;
  173         for (i = 0; i < EMPTYBUFFERS; i++) {
  174                 np = malloc(sizeof(struct harvest), M_ENTROPY, M_WAITOK);
  175                 STAILQ_INSERT_TAIL(&emptyfifo.head, np, next);
  176         }
  177         for (e = RANDOM_START; e < ENTROPYSOURCE; e++) {
  178                 STAILQ_INIT(&harvestfifo[e].head);
  179                 harvestfifo[e].count = 0;
  180         }
  181 
  182         mtx_init(&harvest_mtx, "entropy harvest mutex", NULL, MTX_SPIN);
  183 
  184         /* Start the hash/reseed thread */
  185         error = kproc_create(random_kthread, NULL,
  186             &random_kthread_proc, RFHIGHPID, 0, "yarrow");
  187         if (error != 0)
  188                 panic("Cannot create entropy maintenance thread.");
  189 
  190         /* Register the randomness harvesting routine */
  191         random_yarrow_init_harvester(random_harvest_internal,
  192             random_yarrow_read);
  193 }
  194 
  195 /* ARGSUSED */
  196 void
  197 random_yarrow_deinit(void)
  198 {
  199         struct harvest *np;
  200         enum esource e;
  201 
  202         /* Deregister the randomness harvesting routine */
  203         random_yarrow_deinit_harvester();
  204 
  205         /*
  206          * Command the hash/reseed thread to end and wait for it to finish
  207          */
  208         random_kthread_control = -1;
  209         tsleep((void *)&random_kthread_control, 0, "term", 0);
  210 
  211         /* Destroy the harvest fifos */
  212         while (!STAILQ_EMPTY(&emptyfifo.head)) {
  213                 np = STAILQ_FIRST(&emptyfifo.head);
  214                 STAILQ_REMOVE_HEAD(&emptyfifo.head, next);
  215                 free(np, M_ENTROPY);
  216         }
  217         for (e = RANDOM_START; e < ENTROPYSOURCE; e++) {
  218                 while (!STAILQ_EMPTY(&harvestfifo[e].head)) {
  219                         np = STAILQ_FIRST(&harvestfifo[e].head);
  220                         STAILQ_REMOVE_HEAD(&harvestfifo[e].head, next);
  221                         free(np, M_ENTROPY);
  222                 }
  223         }
  224 
  225         random_yarrow_deinit_alg();
  226 
  227         mtx_destroy(&harvest_mtx);
  228 
  229         sysctl_ctx_free(&random_clist);
  230 }
  231 
  232 /* ARGSUSED */
  233 static void
  234 random_kthread(void *arg __unused)
  235 {
  236         STAILQ_HEAD(, harvest) local_queue;
  237         struct harvest *event = NULL;
  238         int local_count;
  239         enum esource source;
  240 
  241         STAILQ_INIT(&local_queue);
  242         local_count = 0;
  243 
  244         /* Process until told to stop */
  245         for (; random_kthread_control >= 0;) {
  246 
  247                 /* Cycle through all the entropy sources */
  248                 mtx_lock_spin(&harvest_mtx);
  249                 for (source = RANDOM_START; source < ENTROPYSOURCE; source++) {
  250                         /*
  251                          * Drain entropy source records into a thread-local
  252                          * queue for processing while not holding the mutex.
  253                          */
  254                         STAILQ_CONCAT(&local_queue, &harvestfifo[source].head);
  255                         local_count += harvestfifo[source].count;
  256                         harvestfifo[source].count = 0;
  257                 }
  258 
  259                 /*
  260                  * Deal with events, if any, dropping the mutex as we process
  261                  * each event.  Then push the events back into the empty
  262                  * fifo.
  263                  */
  264                 if (!STAILQ_EMPTY(&local_queue)) {
  265                         mtx_unlock_spin(&harvest_mtx);
  266                         STAILQ_FOREACH(event, &local_queue, next)
  267                                 random_process_event(event);
  268                         mtx_lock_spin(&harvest_mtx);
  269                         STAILQ_CONCAT(&emptyfifo.head, &local_queue);
  270                         emptyfifo.count += local_count;
  271                         local_count = 0;
  272                 }
  273                 mtx_unlock_spin(&harvest_mtx);
  274 
  275                 KASSERT(local_count == 0, ("random_kthread: local_count %d",
  276                     local_count));
  277 
  278                 /*
  279                  * If a queue flush was commanded, it has now happened,
  280                  * and we can mark this by resetting the command.
  281                  */
  282                 if (random_kthread_control == 1)
  283                         random_kthread_control = 0;
  284 
  285                 /* Work done, so don't belabour the issue */
  286                 pause("-", hz / 10);
  287 
  288         }
  289 
  290         random_set_wakeup_exit(&random_kthread_control);
  291         /* NOTREACHED */
  292 }
  293 
  294 /* Entropy harvesting routine. This is supposed to be fast; do
  295  * not do anything slow in here!
  296  */
  297 static void
  298 random_harvest_internal(u_int64_t somecounter, const void *entropy,
  299     u_int count, u_int bits, u_int frac, enum esource origin)
  300 {
  301         struct harvest *event;
  302 
  303         KASSERT(origin == RANDOM_START || origin == RANDOM_WRITE ||
  304             origin == RANDOM_KEYBOARD || origin == RANDOM_MOUSE ||
  305             origin == RANDOM_NET || origin == RANDOM_INTERRUPT ||
  306             origin == RANDOM_PURE,
  307             ("random_harvest_internal: origin %d invalid\n", origin));
  308 
  309         /* Lockless read to avoid lock operations if fifo is full. */
  310         if (harvestfifo[origin].count >= RANDOM_FIFO_MAX)
  311                 return;
  312 
  313         mtx_lock_spin(&harvest_mtx);
  314 
  315         /*
  316          * Don't make the harvest queues too big - help to prevent low-grade
  317          * entropy swamping
  318          */
  319         if (harvestfifo[origin].count < RANDOM_FIFO_MAX) {
  320                 event = STAILQ_FIRST(&emptyfifo.head);
  321                 if (event != NULL) {
  322                         /* Add the harvested data to the fifo */
  323                         STAILQ_REMOVE_HEAD(&emptyfifo.head, next);
  324                         harvestfifo[origin].count++;
  325                         event->somecounter = somecounter;
  326                         event->size = count;
  327                         event->bits = bits;
  328                         event->frac = frac;
  329                         event->source = origin;
  330 
  331                         /* XXXX Come back and make this dynamic! */
  332                         count = MIN(count, HARVESTSIZE);
  333                         memcpy(event->entropy, entropy, count);
  334 
  335                         STAILQ_INSERT_TAIL(&harvestfifo[origin].head,
  336                             event, next);
  337                 }
  338         }
  339         mtx_unlock_spin(&harvest_mtx);
  340 }
  341 
  342 void
  343 random_yarrow_write(void *buf, int count)
  344 {
  345         int i;
  346         u_int chunk;
  347 
  348         /*
  349          * Break the input up into HARVESTSIZE chunks. The writer has too
  350          * much control here, so "estimate" the the entropy as zero.
  351          */
  352         for (i = 0; i < count; i += HARVESTSIZE) {
  353                 chunk = HARVESTSIZE;
  354                 if (i + chunk >= count)
  355                         chunk = (u_int)(count - i);
  356                 random_harvest_internal(get_cyclecount(), (char *)buf + i,
  357                     chunk, 0, 0, RANDOM_WRITE);
  358         }
  359 }
  360 
  361 void
  362 random_yarrow_unblock(void)
  363 {
  364         if (!random_systat.seeded) {
  365                 random_systat.seeded = 1;
  366                 selwakeuppri(&random_systat.rsel, PUSER);
  367                 wakeup(&random_systat);
  368         }
  369 }
  370 
  371 static int
  372 random_yarrow_poll(int events, struct thread *td)
  373 {
  374         int revents = 0;
  375         mtx_lock(&random_reseed_mtx);
  376 
  377         if (random_systat.seeded)
  378                 revents = events & (POLLIN | POLLRDNORM);
  379         else
  380                 selrecord(td, &random_systat.rsel);
  381 
  382         mtx_unlock(&random_reseed_mtx);
  383         return revents;
  384 }
  385 
  386 static int
  387 random_yarrow_block(int flag)
  388 {
  389         int error = 0;
  390 
  391         mtx_lock(&random_reseed_mtx);
  392 
  393         /* Blocking logic */
  394         while (random_systat.seeded && !error) {
  395                 if (flag & O_NONBLOCK)
  396                         error = EWOULDBLOCK;
  397                 else {
  398                         printf("Entropy device is blocking.\n");
  399                         error = msleep(&random_systat,
  400                             &random_reseed_mtx,
  401                             PUSER | PCATCH, "block", 0);
  402                 }
  403         }
  404         mtx_unlock(&random_reseed_mtx);
  405 
  406         return error;
  407 }
  408 
  409 /* Helper routine to perform explicit reseeds */
  410 static void
  411 random_yarrow_flush_reseed(void)
  412 {
  413         /* Command a entropy queue flush and wait for it to finish */
  414         random_kthread_control = 1;
  415         while (random_kthread_control)
  416                 pause("-", hz / 10);
  417 
  418         random_yarrow_reseed();
  419 }

Cache object: 5240ede5877c0fbd20c80914bd19c784


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.