randomdev_soft.c revision 249631
17030SN/A/*- 214349Stvaleev * Copyright (c) 2000-2009 Mark R V Murray 37030SN/A * Copyright (c) 2004 Robert N. M. Watson 47030SN/A * All rights reserved. 57030SN/A * 67030SN/A * Redistribution and use in source and binary forms, with or without 77030SN/A * modification, are permitted provided that the following conditions 87030SN/A * are met: 97030SN/A * 1. Redistributions of source code must retain the above copyright 107030SN/A * notice, this list of conditions and the following disclaimer 117030SN/A * in this position and unchanged. 127030SN/A * 2. Redistributions in binary form must reproduce the above copyright 137030SN/A * notice, this list of conditions and the following disclaimer in the 147030SN/A * documentation and/or other materials provided with the distribution. 157030SN/A * 167030SN/A * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 177030SN/A * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 187030SN/A * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 197030SN/A * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 207030SN/A * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 217030SN/A * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 227030SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 237030SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 247030SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 257030SN/A * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 267030SN/A * 277030SN/A */ 287030SN/A 297030SN/A#include <sys/cdefs.h> 307030SN/A__FBSDID("$FreeBSD: head/sys/dev/random/randomdev_soft.c 249631 2013-04-19 00:30:52Z ache $"); 317030SN/A 327030SN/A#include <sys/param.h> 337030SN/A#include <sys/systm.h> 347030SN/A#include <sys/bus.h> 357030SN/A#include <sys/conf.h> 367030SN/A#include <sys/fcntl.h> 377030SN/A#include <sys/kernel.h> 387030SN/A#include <sys/kthread.h> 397030SN/A#include <sys/lock.h> 407030SN/A#include <sys/malloc.h> 417030SN/A#include <sys/mutex.h> 427030SN/A#include <sys/poll.h> 437030SN/A#include <sys/proc.h> 447030SN/A#include <sys/random.h> 457030SN/A#include <sys/selinfo.h> 467030SN/A#include <sys/sysctl.h> 477030SN/A#include <sys/uio.h> 487030SN/A#include <sys/unistd.h> 497030SN/A 507030SN/A#include <machine/bus.h> 517030SN/A#include <machine/cpu.h> 527030SN/A 537030SN/A#include <dev/random/randomdev.h> 547030SN/A#include <dev/random/randomdev_soft.h> 557030SN/A 567030SN/A#define RANDOM_FIFO_MAX 256 /* How many events to queue up */ 577030SN/A 587030SN/Astatic void random_kthread(void *); 597030SN/Astatic void 607030SN/Arandom_harvest_internal(u_int64_t, const void *, u_int, 617030SN/A u_int, u_int, enum esource); 627030SN/Astatic int random_yarrow_poll(int event,struct thread *td); 637030SN/Astatic int random_yarrow_block(int flag); 647030SN/Astatic void random_yarrow_flush_reseed(void); 657030SN/A 667030SN/Astruct random_systat random_yarrow = { 677030SN/A .ident = "Software, Yarrow", 687030SN/A .init = random_yarrow_init, 697030SN/A .deinit = random_yarrow_deinit, 707030SN/A .block = random_yarrow_block, 717030SN/A .read = random_yarrow_read, 727030SN/A .write = random_yarrow_write, 737030SN/A .poll = random_yarrow_poll, 747030SN/A .reseed = random_yarrow_flush_reseed, 757030SN/A .seeded = 1, 767030SN/A}; 777030SN/A 787030SN/AMALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers"); 797030SN/A 807030SN/A/* 817030SN/A * The harvest mutex protects the consistency of the entropy fifos and 827030SN/A * empty fifo. 837030SN/A */ 847030SN/Astruct mtx harvest_mtx; 857030SN/A 867030SN/A/* Lockable FIFO queue holding entropy buffers */ 877030SN/Astruct entropyfifo { 887030SN/A int count; 897030SN/A STAILQ_HEAD(harvestlist, harvest) head; 907030SN/A}; 917030SN/A 927030SN/A/* Empty entropy buffers */ 937030SN/Astatic struct entropyfifo emptyfifo; 947030SN/A 957030SN/A#define EMPTYBUFFERS 1024 967030SN/A 977030SN/A/* Harvested entropy */ 987030SN/Astatic struct entropyfifo harvestfifo[ENTROPYSOURCE]; 997030SN/A 1007030SN/A/* <0 to end the kthread, 0 to let it run, 1 to flush the harvest queues */ 1017030SN/Astatic int random_kthread_control = 0; 1027030SN/A 1037030SN/Astatic struct proc *random_kthread_proc; 1047030SN/A 1057030SN/A/* List for the dynamic sysctls */ 1067030SN/Astruct sysctl_ctx_list random_clist; 1077030SN/A 1087030SN/A/* ARGSUSED */ 1097030SN/Astatic int 1107030SN/Arandom_check_boolean(SYSCTL_HANDLER_ARGS) 1117030SN/A{ 1127030SN/A if (oidp->oid_arg1 != NULL && *(u_int *)(oidp->oid_arg1) != 0) 1137030SN/A *(u_int *)(oidp->oid_arg1) = 1; 1147030SN/A return sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); 1157030SN/A} 1167030SN/A 1177030SN/A/* ARGSUSED */ 1187030SN/Avoid 1197030SN/Arandom_yarrow_init(void) 1207030SN/A{ 1217030SN/A int error, i; 1227030SN/A struct harvest *np; 1237030SN/A struct sysctl_oid *random_o, *random_sys_o, *random_sys_harvest_o; 1247030SN/A enum esource e; 1257030SN/A 1267030SN/A random_o = SYSCTL_ADD_NODE(&random_clist, 1277030SN/A SYSCTL_STATIC_CHILDREN(_kern), 1287030SN/A OID_AUTO, "random", CTLFLAG_RW, 0, 1297030SN/A "Software Random Number Generator"); 1307030SN/A 1317030SN/A random_yarrow_init_alg(&random_clist, random_o); 1327030SN/A 1337030SN/A random_sys_o = SYSCTL_ADD_NODE(&random_clist, 1347030SN/A SYSCTL_CHILDREN(random_o), 1357030SN/A OID_AUTO, "sys", CTLFLAG_RW, 0, 1367030SN/A "Entropy Device Parameters"); 1377030SN/A 1387030SN/A SYSCTL_ADD_PROC(&random_clist, 1397030SN/A SYSCTL_CHILDREN(random_sys_o), 1407030SN/A OID_AUTO, "seeded", CTLTYPE_INT | CTLFLAG_RW, 1417030SN/A &random_systat.seeded, 1, random_check_boolean, "I", 1427030SN/A "Seeded State"); 1437030SN/A 1447030SN/A random_sys_harvest_o = SYSCTL_ADD_NODE(&random_clist, 1457030SN/A SYSCTL_CHILDREN(random_sys_o), 1467030SN/A OID_AUTO, "harvest", CTLFLAG_RW, 0, 1477030SN/A "Entropy Sources"); 1487030SN/A 1497030SN/A SYSCTL_ADD_PROC(&random_clist, 1507030SN/A SYSCTL_CHILDREN(random_sys_harvest_o), 1517819SN/A OID_AUTO, "ethernet", CTLTYPE_INT | CTLFLAG_RW, 1527030SN/A &harvest.ethernet, 1, random_check_boolean, "I", 1537030SN/A "Harvest NIC entropy"); 1547030SN/A SYSCTL_ADD_PROC(&random_clist, 1557030SN/A SYSCTL_CHILDREN(random_sys_harvest_o), 1567030SN/A OID_AUTO, "point_to_point", CTLTYPE_INT | CTLFLAG_RW, 15712325Spsandoz &harvest.point_to_point, 1, random_check_boolean, "I", 1587030SN/A "Harvest serial net entropy"); 1597030SN/A SYSCTL_ADD_PROC(&random_clist, 16012325Spsandoz SYSCTL_CHILDREN(random_sys_harvest_o), 16112325Spsandoz OID_AUTO, "interrupt", CTLTYPE_INT | CTLFLAG_RW, 16212325Spsandoz &harvest.interrupt, 1, random_check_boolean, "I", 1637030SN/A "Harvest IRQ entropy"); 1647030SN/A SYSCTL_ADD_PROC(&random_clist, 1657030SN/A SYSCTL_CHILDREN(random_sys_harvest_o), 1667030SN/A OID_AUTO, "swi", CTLTYPE_INT | CTLFLAG_RW, 1677030SN/A &harvest.swi, 0, random_check_boolean, "I", 1687030SN/A "Harvest SWI entropy"); 1697030SN/A 17014349Stvaleev /* Initialise the harvest fifos */ 17114349Stvaleev STAILQ_INIT(&emptyfifo.head); 17214349Stvaleev emptyfifo.count = 0; 17314349Stvaleev for (i = 0; i < EMPTYBUFFERS; i++) { 17414349Stvaleev np = malloc(sizeof(struct harvest), M_ENTROPY, M_WAITOK); 17514349Stvaleev STAILQ_INSERT_TAIL(&emptyfifo.head, np, next); 17614349Stvaleev } 17714349Stvaleev for (e = RANDOM_START; e < ENTROPYSOURCE; e++) { 17814349Stvaleev STAILQ_INIT(&harvestfifo[e].head); 17914349Stvaleev harvestfifo[e].count = 0; 18014349Stvaleev } 18114349Stvaleev 18214349Stvaleev mtx_init(&harvest_mtx, "entropy harvest mutex", NULL, MTX_SPIN); 1837030SN/A 1847030SN/A /* Start the hash/reseed thread */ 1857030SN/A error = kproc_create(random_kthread, NULL, 1867030SN/A &random_kthread_proc, RFHIGHPID, 0, "yarrow"); 1877030SN/A if (error != 0) 1887364SN/A panic("Cannot create entropy maintenance thread."); 1897030SN/A 1907030SN/A /* Register the randomness harvesting routine */ 1917030SN/A random_yarrow_init_harvester(random_harvest_internal, 1927030SN/A random_yarrow_read); 1937030SN/A} 1947030SN/A 1957030SN/A/* ARGSUSED */ 1967030SN/Avoid 1977030SN/Arandom_yarrow_deinit(void) 1987030SN/A{ 1997363SN/A struct harvest *np; 20014349Stvaleev enum esource e; 2017030SN/A 2027030SN/A /* Deregister the randomness harvesting routine */ 2037956SN/A random_yarrow_deinit_harvester(); 2047030SN/A 2057030SN/A /* 2067030SN/A * Command the hash/reseed thread to end and wait for it to finish 2077030SN/A */ 2087030SN/A random_kthread_control = -1; 2097030SN/A tsleep((void *)&random_kthread_control, 0, "term", 0); 2107030SN/A 2117030SN/A /* Destroy the harvest fifos */ 2127030SN/A while (!STAILQ_EMPTY(&emptyfifo.head)) { 2137030SN/A np = STAILQ_FIRST(&emptyfifo.head); 2147030SN/A STAILQ_REMOVE_HEAD(&emptyfifo.head, next); 21514349Stvaleev free(np, M_ENTROPY); 2167030SN/A } 2177030SN/A for (e = RANDOM_START; e < ENTROPYSOURCE; e++) { 2187030SN/A while (!STAILQ_EMPTY(&harvestfifo[e].head)) { 2197030SN/A np = STAILQ_FIRST(&harvestfifo[e].head); 2207030SN/A STAILQ_REMOVE_HEAD(&harvestfifo[e].head, next); 2217030SN/A free(np, M_ENTROPY); 2227030SN/A } 2237030SN/A } 2247030SN/A 2257956SN/A random_yarrow_deinit_alg(); 2267030SN/A 2277030SN/A mtx_destroy(&harvest_mtx); 2287030SN/A 2297030SN/A sysctl_ctx_free(&random_clist); 2307030SN/A} 2317030SN/A 2327030SN/A/* ARGSUSED */ 2337030SN/Astatic void 2347030SN/Arandom_kthread(void *arg __unused) 2357030SN/A{ 2367030SN/A STAILQ_HEAD(, harvest) local_queue; 2377030SN/A struct harvest *event = NULL; 23814349Stvaleev int local_count; 2397030SN/A enum esource source; 2407030SN/A 2417030SN/A STAILQ_INIT(&local_queue); 2427030SN/A local_count = 0; 2437030SN/A 2447030SN/A /* Process until told to stop */ 2457030SN/A mtx_lock_spin(&harvest_mtx); 2467030SN/A for (; random_kthread_control >= 0;) { 2477030SN/A 2487956SN/A /* Cycle through all the entropy sources */ 2497030SN/A for (source = RANDOM_START; source < ENTROPYSOURCE; source++) { 2507030SN/A /* 2517030SN/A * Drain entropy source records into a thread-local 2527030SN/A * queue for processing while not holding the mutex. 2537030SN/A */ 2547030SN/A STAILQ_CONCAT(&local_queue, &harvestfifo[source].head); 2557030SN/A local_count += harvestfifo[source].count; 2567030SN/A harvestfifo[source].count = 0; 2577030SN/A } 2587030SN/A 2597030SN/A /* 2607030SN/A * Deal with events, if any, dropping the mutex as we process 2617030SN/A * each event. Then push the events back into the empty 2627030SN/A * fifo. 2637030SN/A */ 2647030SN/A if (!STAILQ_EMPTY(&local_queue)) { 2657956SN/A mtx_unlock_spin(&harvest_mtx); 2667030SN/A STAILQ_FOREACH(event, &local_queue, next) 2677030SN/A random_process_event(event); 2687030SN/A mtx_lock_spin(&harvest_mtx); 2697030SN/A STAILQ_CONCAT(&emptyfifo.head, &local_queue); 2707030SN/A emptyfifo.count += local_count; 2717030SN/A local_count = 0; 2727030SN/A } 2737030SN/A 2747030SN/A KASSERT(local_count == 0, ("random_kthread: local_count %d", 2757030SN/A local_count)); 2767030SN/A 27710298SN/A /* 2787030SN/A * If a queue flush was commanded, it has now happened, 2797030SN/A * and we can mark this by resetting the command. 2807030SN/A */ 2817030SN/A if (random_kthread_control == 1) 2827956SN/A random_kthread_control = 0; 2837526SN/A 2847526SN/A /* Work done, so don't belabour the issue */ 2857526SN/A msleep_spin_sbt(&random_kthread_control, &harvest_mtx, 2867526SN/A "-", SBT_1S / 10, 0, C_PREL(1)); 2877526SN/A 2887526SN/A } 2897030SN/A mtx_unlock_spin(&harvest_mtx); 2908001SN/A 2918001SN/A random_set_wakeup_exit(&random_kthread_control); 2928001SN/A /* NOTREACHED */ 2938001SN/A} 2948001SN/A 2957030SN/A/* Entropy harvesting routine. This is supposed to be fast; do 2967030SN/A * not do anything slow in here! 2977030SN/A */ 2987030SN/Astatic void 2997030SN/Arandom_harvest_internal(u_int64_t somecounter, const void *entropy, 3007030SN/A u_int count, u_int bits, u_int frac, enum esource origin) 3017030SN/A{ 3027030SN/A struct harvest *event; 3037030SN/A 3047030SN/A KASSERT(origin == RANDOM_START || origin == RANDOM_WRITE || 3057030SN/A origin == RANDOM_KEYBOARD || origin == RANDOM_MOUSE || 3067030SN/A origin == RANDOM_NET || origin == RANDOM_INTERRUPT || 3077030SN/A origin == RANDOM_PURE, 3087030SN/A ("random_harvest_internal: origin %d invalid\n", origin)); 3097030SN/A 3107030SN/A /* Lockless read to avoid lock operations if fifo is full. */ 3117030SN/A if (harvestfifo[origin].count >= RANDOM_FIFO_MAX) 3127030SN/A return; 3137030SN/A 3147030SN/A mtx_lock_spin(&harvest_mtx); 3157030SN/A 3167030SN/A /* 3177030SN/A * Don't make the harvest queues too big - help to prevent low-grade 3187030SN/A * entropy swamping 3197030SN/A */ 3207956SN/A if (harvestfifo[origin].count < RANDOM_FIFO_MAX) { 3217030SN/A event = STAILQ_FIRST(&emptyfifo.head); 3227526SN/A if (event != NULL) { 3237526SN/A /* Add the harvested data to the fifo */ 3247526SN/A STAILQ_REMOVE_HEAD(&emptyfifo.head, next); 3257526SN/A harvestfifo[origin].count++; 3267526SN/A event->somecounter = somecounter; 3277030SN/A event->size = count; 3287030SN/A event->bits = bits; 3297030SN/A event->frac = frac; 3307030SN/A event->source = origin; 3317030SN/A 3327030SN/A /* XXXX Come back and make this dynamic! */ 3337030SN/A count = MIN(count, HARVESTSIZE); 3347030SN/A memcpy(event->entropy, entropy, count); 3357030SN/A 3367030SN/A STAILQ_INSERT_TAIL(&harvestfifo[origin].head, 3378051SN/A event, next); 3388051SN/A } 3397030SN/A } 3407030SN/A mtx_unlock_spin(&harvest_mtx); 3417030SN/A} 3427030SN/A 3437956SN/Avoid 3447030SN/Arandom_yarrow_write(void *buf, int count) 3457030SN/A{ 3468051SN/A int i; 3477030SN/A u_int chunk; 3487030SN/A 3497030SN/A /* 3507030SN/A * Break the input up into HARVESTSIZE chunks. The writer has too 3517030SN/A * much control here, so "estimate" the entropy as zero. 3527030SN/A */ 3537030SN/A for (i = 0; i < count; i += HARVESTSIZE) { 3547030SN/A chunk = HARVESTSIZE; 3557030SN/A if (i + chunk >= count) 3567030SN/A chunk = (u_int)(count - i); 3577030SN/A random_harvest_internal(get_cyclecount(), (char *)buf + i, 3587030SN/A chunk, 0, 0, RANDOM_WRITE); 3597030SN/A } 3608461SN/A} 3617030SN/A 3627030SN/Avoid 3637030SN/Arandom_yarrow_unblock(void) 3648461SN/A{ 3658461SN/A if (!random_systat.seeded) { 3668461SN/A random_systat.seeded = 1; 3678461SN/A selwakeuppri(&random_systat.rsel, PUSER); 3687030SN/A wakeup(&random_systat); 3697030SN/A } 3708461SN/A (void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, 3717030SN/A ARC4_ENTR_HAVE); 3727030SN/A} 3737030SN/A 37412325Spsandozstatic int 37512325Spsandozrandom_yarrow_poll(int events, struct thread *td) 37612325Spsandoz{ 37712325Spsandoz int revents = 0; 37812325Spsandoz mtx_lock(&random_reseed_mtx); 37912325Spsandoz 38012325Spsandoz if (random_systat.seeded) 38112325Spsandoz revents = events & (POLLIN | POLLRDNORM); 38212325Spsandoz else 38312325Spsandoz selrecord(td, &random_systat.rsel); 3847030SN/A 3857030SN/A mtx_unlock(&random_reseed_mtx); 3867030SN/A return revents; 3877030SN/A} 3887030SN/A 3897030SN/Astatic int 3907030SN/Arandom_yarrow_block(int flag) 3917030SN/A{ 3927030SN/A int error = 0; 3937030SN/A 3947030SN/A mtx_lock(&random_reseed_mtx); 3957030SN/A 3967030SN/A /* Blocking logic */ 3977030SN/A while (!random_systat.seeded && !error) { 3987030SN/A if (flag & O_NONBLOCK) 3997030SN/A error = EWOULDBLOCK; 4007030SN/A else { 4017030SN/A printf("Entropy device is blocking.\n"); 4027030SN/A error = msleep(&random_systat, 4037030SN/A &random_reseed_mtx, 4047030SN/A PUSER | PCATCH, "block", 0); 4057030SN/A } 4067030SN/A } 4077030SN/A mtx_unlock(&random_reseed_mtx); 4087030SN/A 4097030SN/A return error; 4107030SN/A} 4117030SN/A 4127030SN/A/* Helper routine to perform explicit reseeds */ 4137030SN/Astatic void 4147030SN/Arandom_yarrow_flush_reseed(void) 4157030SN/A{ 4167030SN/A /* Command a entropy queue flush and wait for it to finish */ 4177030SN/A random_kthread_control = 1; 4187030SN/A while (random_kthread_control) 4197030SN/A pause("-", hz / 10); 4207030SN/A 4217030SN/A random_yarrow_reseed(); 4227030SN/A} 4237030SN/A