randomdev.c revision 74771
162053Smarkm/*- 262765Smarkm * Copyright (c) 2000 Mark R V Murray 362053Smarkm * All rights reserved. 462053Smarkm * 562053Smarkm * Redistribution and use in source and binary forms, with or without 662053Smarkm * modification, are permitted provided that the following conditions 762053Smarkm * are met: 862053Smarkm * 1. Redistributions of source code must retain the above copyright 962053Smarkm * notice, this list of conditions and the following disclaimer 1062053Smarkm * in this position and unchanged. 1162053Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1262053Smarkm * notice, this list of conditions and the following disclaimer in the 1362053Smarkm * documentation and/or other materials provided with the distribution. 1462053Smarkm * 1562053Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1662053Smarkm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1762053Smarkm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1862053Smarkm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1962053Smarkm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2062053Smarkm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2162053Smarkm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2262053Smarkm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2362053Smarkm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2462053Smarkm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2562053Smarkm * 2662053Smarkm * $FreeBSD: head/sys/dev/random/randomdev.c 74771 2001-03-25 06:55:19Z markm $ 2762053Smarkm */ 2862053Smarkm 2962053Smarkm#include <sys/param.h> 3062765Smarkm#include <sys/queue.h> 3162053Smarkm#include <sys/systm.h> 3262053Smarkm#include <sys/conf.h> 3374771Smarkm#include <sys/filio.h> 3462053Smarkm#include <sys/fcntl.h> 3562053Smarkm#include <sys/uio.h> 3662053Smarkm#include <sys/kernel.h> 3774072Smarkm#include <sys/kthread.h> 3862053Smarkm#include <sys/malloc.h> 3962053Smarkm#include <sys/module.h> 4062053Smarkm#include <sys/bus.h> 4167112Smarkm#include <sys/poll.h> 4270834Swollman#include <sys/selinfo.h> 4362053Smarkm#include <sys/random.h> 4471037Smarkm#include <sys/sysctl.h> 4567112Smarkm#include <sys/vnode.h> 4674072Smarkm#include <sys/unistd.h> 4774072Smarkm 4862053Smarkm#include <machine/bus.h> 4974072Smarkm#include <machine/cpu.h> 5062053Smarkm#include <machine/resource.h> 5162053Smarkm 5274072Smarkm#include <dev/random/randomdev.h> 5362053Smarkm 5474072Smarkmstatic d_open_t random_open; 5574072Smarkmstatic d_close_t random_close; 5674072Smarkmstatic d_read_t random_read; 5774072Smarkmstatic d_write_t random_write; 5874072Smarkmstatic d_ioctl_t random_ioctl; 5974072Smarkmstatic d_poll_t random_poll; 6062053Smarkm 6162053Smarkm#define CDEV_MAJOR 2 6262053Smarkm#define RANDOM_MINOR 3 6362053Smarkm 6462053Smarkmstatic struct cdevsw random_cdevsw = { 6563855Smarkm /* open */ random_open, 6669172Smarkm /* close */ random_close, 6762765Smarkm /* read */ random_read, 6862765Smarkm /* write */ random_write, 6965686Smarkm /* ioctl */ random_ioctl, 7067112Smarkm /* poll */ random_poll, 7162053Smarkm /* mmap */ nommap, 7262053Smarkm /* strategy */ nostrategy, 7362053Smarkm /* name */ "random", 7462053Smarkm /* maj */ CDEV_MAJOR, 7562053Smarkm /* dump */ nodump, 7662053Smarkm /* psize */ nopsize, 7762053Smarkm /* flags */ 0, 7862053Smarkm /* bmaj */ -1 7962053Smarkm}; 8062053Smarkm 8174072Smarkmstatic void random_kthread(void *); 8274072Smarkmstatic void random_harvest_internal(u_int64_t, void *, u_int, u_int, u_int, enum esource); 8374072Smarkmstatic void random_write_internal(void *, u_int); 8474072Smarkm 8574072Smarkm/* Ring buffer holding harvested entropy */ 8674072Smarkmstatic struct harvestring { 8774072Smarkm volatile u_int head; 8874072Smarkm volatile u_int tail; 8974072Smarkm struct harvest data[HARVEST_RING_SIZE]; 9074072Smarkm} harvestring; 9174072Smarkm 9274072Smarkmstatic struct random_systat { 9374072Smarkm u_int seeded; /* 0 causes blocking 1 allows normal output */ 9474072Smarkm u_int burst; /* number of events to do before sleeping */ 9574072Smarkm struct selinfo rsel; /* For poll(2) */ 9674072Smarkm} random_systat; 9774072Smarkm 9874072Smarkm/* <0 to end the kthread, 0 to let it run */ 9974072Smarkmstatic int random_kthread_control = 0; 10074072Smarkm 10174072Smarkmstatic struct proc *random_kthread_proc; 10274072Smarkm 10362053Smarkm/* For use with make_dev(9)/destroy_dev(9). */ 10474072Smarkmstatic dev_t random_dev; 10574072Smarkmstatic dev_t urandom_dev; 10662053Smarkm 10774072Smarkmstatic int 10874072Smarkmrandom_check_boolean(SYSCTL_HANDLER_ARGS) 10974072Smarkm{ 11074072Smarkm if (oidp->oid_arg1 != NULL && *(u_int *)(oidp->oid_arg1) != 0) 11174072Smarkm *(u_int *)(oidp->oid_arg1) = 1; 11274072Smarkm return sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); 11374072Smarkm} 11462053Smarkm 11574072SmarkmRANDOM_CHECK_UINT(burst, 0, 20); 11674072Smarkm 11774072SmarkmSYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 11874072Smarkm 0, "Random Number Generator"); 11974072SmarkmSYSCTL_NODE(_kern_random, OID_AUTO, sys, CTLFLAG_RW, 12074072Smarkm 0, "Entropy Device Parameters"); 12174072SmarkmSYSCTL_PROC(_kern_random_sys, OID_AUTO, seeded, 12274072Smarkm CTLTYPE_INT|CTLFLAG_RW, &random_systat.seeded, 1, 12374072Smarkm random_check_boolean, "I", "Seeded State"); 12474072SmarkmSYSCTL_PROC(_kern_random_sys, OID_AUTO, burst, 12574072Smarkm CTLTYPE_INT|CTLFLAG_RW, &random_systat.burst, 20, 12674072Smarkm random_check_uint_burst, "I", "Harvest Burst Size"); 12774072SmarkmSYSCTL_NODE(_kern_random_sys, OID_AUTO, harvest, CTLFLAG_RW, 12874072Smarkm 0, "Entropy Sources"); 12974072SmarkmSYSCTL_PROC(_kern_random_sys_harvest, OID_AUTO, ethernet, 13074072Smarkm CTLTYPE_INT|CTLFLAG_RW, &harvest.ethernet, 0, 13174072Smarkm random_check_boolean, "I", "Harvest NIC entropy"); 13274072SmarkmSYSCTL_PROC(_kern_random_sys_harvest, OID_AUTO, point_to_point, 13374072Smarkm CTLTYPE_INT|CTLFLAG_RW, &harvest.point_to_point, 0, 13474072Smarkm random_check_boolean, "I", "Harvest serial net entropy"); 13574072SmarkmSYSCTL_PROC(_kern_random_sys_harvest, OID_AUTO, interrupt, 13674072Smarkm CTLTYPE_INT|CTLFLAG_RW, &harvest.interrupt, 0, 13774072Smarkm random_check_boolean, "I", "Harvest IRQ entropy"); 13874072Smarkm 13962053Smarkmstatic int 14063855Smarkmrandom_open(dev_t dev, int flags, int fmt, struct proc *p) 14163855Smarkm{ 14263855Smarkm if ((flags & FWRITE) && (securelevel > 0 || suser(p))) 14363855Smarkm return EPERM; 14463855Smarkm else 14563855Smarkm return 0; 14663855Smarkm} 14763855Smarkm 14863855Smarkmstatic int 14969172Smarkmrandom_close(dev_t dev, int flags, int fmt, struct proc *p) 15069172Smarkm{ 15169174Smarkm if ((flags & FWRITE) && !(securelevel > 0 || suser(p))) 15269172Smarkm random_reseed(); 15369172Smarkm return 0; 15469172Smarkm} 15569172Smarkm 15669172Smarkmstatic int 15762765Smarkmrandom_read(dev_t dev, struct uio *uio, int flag) 15862053Smarkm{ 15974072Smarkm u_int c, ret; 16074072Smarkm int error = 0; 16174072Smarkm void *random_buf; 16262053Smarkm 16374072Smarkm while (!random_systat.seeded) { 16467286Speter if (flag & IO_NDELAY) 16567286Speter error = EWOULDBLOCK; 16667112Smarkm else 16774072Smarkm error = tsleep(&random_systat, PUSER|PCATCH, 16874072Smarkm "block", 0); 16967286Speter if (error != 0) 17067286Speter return error; 17167112Smarkm } 17267286Speter c = min(uio->uio_resid, PAGE_SIZE); 17367286Speter random_buf = (void *)malloc(c, M_TEMP, M_WAITOK); 17467286Speter while (uio->uio_resid > 0 && error == 0) { 17567286Speter ret = read_random_real(random_buf, c); 17667286Speter error = uiomove(random_buf, ret, uio); 17767286Speter } 17867286Speter free(random_buf, M_TEMP); 17962053Smarkm return error; 18062053Smarkm} 18162053Smarkm 18262053Smarkmstatic int 18362765Smarkmrandom_write(dev_t dev, struct uio *uio, int flag) 18462053Smarkm{ 18574072Smarkm u_int c; 18674072Smarkm int error; 18774072Smarkm void *random_buf; 18862053Smarkm 18974072Smarkm error = 0; 19062765Smarkm random_buf = (void *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK); 19162053Smarkm while (uio->uio_resid > 0) { 19262053Smarkm c = min(uio->uio_resid, PAGE_SIZE); 19362765Smarkm error = uiomove(random_buf, c, uio); 19462053Smarkm if (error) 19562053Smarkm break; 19674072Smarkm random_write_internal(random_buf, c); 19762053Smarkm } 19862765Smarkm free(random_buf, M_TEMP); 19962053Smarkm return error; 20062053Smarkm} 20162053Smarkm 20262053Smarkmstatic int 20365686Smarkmrandom_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) 20465686Smarkm{ 20574771Smarkm switch (cmd) { 20674771Smarkm /* Really handled in upper layer */ 20774771Smarkm case FIOASYNC: 20874771Smarkm case FIONBIO: 20974771Smarkm return 0; 21074771Smarkm default: 21174771Smarkm return ENOTTY; 21274771Smarkm } 21365686Smarkm} 21465686Smarkm 21565686Smarkmstatic int 21667112Smarkmrandom_poll(dev_t dev, int events, struct proc *p) 21767112Smarkm{ 21874072Smarkm int revents; 21967112Smarkm 22067112Smarkm revents = 0; 22167112Smarkm if (events & (POLLIN | POLLRDNORM)) { 22274072Smarkm if (random_systat.seeded) 22367112Smarkm revents = events & (POLLIN | POLLRDNORM); 22467112Smarkm else 22574072Smarkm selrecord(p, &random_systat.rsel); 22667112Smarkm } 22767112Smarkm return revents; 22867112Smarkm} 22967112Smarkm 23067112Smarkmstatic int 23162053Smarkmrandom_modevent(module_t mod, int type, void *data) 23262053Smarkm{ 23374072Smarkm int error; 23465686Smarkm 23562053Smarkm switch(type) { 23662053Smarkm case MOD_LOAD: 23774072Smarkm random_init(); 23871037Smarkm 23974072Smarkm /* This can be turned off by the very paranoid 24074072Smarkm * a reseed will turn it back on. 24174072Smarkm */ 24274072Smarkm random_systat.seeded = 1; 24371037Smarkm 24474072Smarkm /* Number of envents to process off the harvest 24574072Smarkm * queue before giving it a break and sleeping 24674072Smarkm */ 24774072Smarkm random_systat.burst = 20; 24874072Smarkm 24974072Smarkm /* Initialise the harvest ringbuffer */ 25074072Smarkm harvestring.head = 0; 25174072Smarkm harvestring.tail = 0; 25274072Smarkm 25362053Smarkm if (bootverbose) 25462053Smarkm printf("random: <entropy source>\n"); 25562765Smarkm random_dev = make_dev(&random_cdevsw, RANDOM_MINOR, UID_ROOT, 25662149Smarkm GID_WHEEL, 0666, "random"); 25774072Smarkm urandom_dev = make_dev_alias(random_dev, "urandom"); 25874072Smarkm 25974072Smarkm /* Start the hash/reseed thread */ 26074072Smarkm error = kthread_create(random_kthread, NULL, 26174072Smarkm &random_kthread_proc, RFHIGHPID, "random"); 26274072Smarkm if (error != 0) 26374072Smarkm return error; 26474072Smarkm 26574072Smarkm /* Register the randomness harvesting routine */ 26674072Smarkm random_init_harvester(random_harvest_internal, 26774072Smarkm read_random_real); 26874072Smarkm 26962053Smarkm return 0; 27062053Smarkm 27162053Smarkm case MOD_UNLOAD: 27274072Smarkm /* Deregister the randomness harvesting routine */ 27374072Smarkm random_deinit_harvester(); 27474072Smarkm 27574072Smarkm /* Command the hash/reseed thread to end and 27674072Smarkm * wait for it to finish 27774072Smarkm */ 27874072Smarkm random_kthread_control = -1; 27974072Smarkm tsleep((void *)&random_kthread_control, PUSER, "term", 0); 28074072Smarkm 28162765Smarkm random_deinit(); 28274072Smarkm 28362765Smarkm destroy_dev(random_dev); 28474072Smarkm destroy_dev(urandom_dev); 28562053Smarkm return 0; 28662053Smarkm 28762053Smarkm case MOD_SHUTDOWN: 28862053Smarkm return 0; 28962053Smarkm 29062053Smarkm default: 29162053Smarkm return EOPNOTSUPP; 29262053Smarkm } 29362053Smarkm} 29462053Smarkm 29562053SmarkmDEV_MODULE(random, random_modevent, NULL); 29674072Smarkm 29774072Smarkmstatic void 29874072Smarkmrandom_kthread(void *arg /* NOTUSED */) 29974072Smarkm{ 30074072Smarkm struct harvest *event; 30174072Smarkm int newtail, burst; 30274072Smarkm 30374072Smarkm /* Drain the harvest queue (in 'burst' size chunks, 30474072Smarkm * if 'burst' > 0. If 'burst' == 0, then completely 30574072Smarkm * drain the queue. 30674072Smarkm */ 30774072Smarkm for (burst = 0; ; burst++) { 30874072Smarkm 30974072Smarkm if ((harvestring.tail == harvestring.head) || 31074072Smarkm (random_systat.burst && burst == random_systat.burst)) { 31174072Smarkm tsleep(&harvestring, PUSER, "sleep", hz/10); 31274072Smarkm burst = 0; 31374072Smarkm 31474072Smarkm } 31574072Smarkm else { 31674072Smarkm 31774072Smarkm /* Suck a harvested entropy event out of the queue and 31874072Smarkm * hand it to the event processor 31974072Smarkm */ 32074072Smarkm 32174072Smarkm newtail = (harvestring.tail + 1) & HARVEST_RING_MASK; 32274072Smarkm event = &harvestring.data[harvestring.tail]; 32374072Smarkm 32474072Smarkm /* Bump the ring counter. This action is assumed 32574072Smarkm * to be atomic. 32674072Smarkm */ 32774072Smarkm harvestring.tail = newtail; 32874072Smarkm 32974072Smarkm random_process_event(event); 33074072Smarkm 33174072Smarkm } 33274072Smarkm 33374072Smarkm /* Is the thread scheduled for a shutdown? */ 33474072Smarkm if (random_kthread_control != 0) { 33574072Smarkm#ifdef DEBUG 33674072Smarkm mtx_lock(&Giant); 33774072Smarkm printf("Random kthread setting terminate\n"); 33874072Smarkm mtx_unlock(&Giant); 33974072Smarkm#endif 34074072Smarkm random_set_wakeup_exit(&random_kthread_control); 34174072Smarkm /* NOTREACHED */ 34274072Smarkm break; 34374072Smarkm } 34474072Smarkm 34574072Smarkm } 34674072Smarkm 34774072Smarkm} 34874072Smarkm 34974072Smarkm/* Entropy harvesting routine. This is supposed to be fast; do 35074072Smarkm * not do anything slow in here! 35174072Smarkm */ 35274072Smarkmstatic void 35374072Smarkmrandom_harvest_internal(u_int64_t somecounter, void *entropy, u_int count, 35474072Smarkm u_int bits, u_int frac, enum esource origin) 35574072Smarkm{ 35674072Smarkm struct harvest *harvest; 35774072Smarkm int newhead; 35874072Smarkm 35974072Smarkm newhead = (harvestring.head + 1) & HARVEST_RING_MASK; 36074072Smarkm 36174072Smarkm if (newhead != harvestring.tail) { 36274072Smarkm 36374072Smarkm /* Add the harvested data to the ring buffer */ 36474072Smarkm 36574072Smarkm harvest = &harvestring.data[harvestring.head]; 36674072Smarkm 36774072Smarkm /* Stuff the harvested data into the ring */ 36874072Smarkm harvest->somecounter = somecounter; 36974072Smarkm count = count > HARVESTSIZE ? HARVESTSIZE : count; 37074072Smarkm memcpy(harvest->entropy, entropy, count); 37174072Smarkm harvest->size = count; 37274072Smarkm harvest->bits = bits; 37374072Smarkm harvest->frac = frac; 37474072Smarkm harvest->source = origin < ENTROPYSOURCE ? origin : 0; 37574072Smarkm 37674072Smarkm /* Bump the ring counter. This action is assumed 37774072Smarkm * to be atomic. 37874072Smarkm */ 37974072Smarkm harvestring.head = newhead; 38074072Smarkm 38174072Smarkm } 38274072Smarkm 38374072Smarkm} 38474072Smarkm 38574072Smarkmstatic void 38674072Smarkmrandom_write_internal(void *buf, u_int count) 38774072Smarkm{ 38874072Smarkm u_int i; 38974072Smarkm 39074072Smarkm /* Break the input up into HARVESTSIZE chunks. 39174072Smarkm * The writer has too much control here, so "estimate" the 39274072Smarkm * the entropy as zero. 39374072Smarkm */ 39474072Smarkm for (i = 0; i < count; i += HARVESTSIZE) { 39574072Smarkm random_harvest_internal(get_cyclecount(), (char *)buf + i, 39674072Smarkm HARVESTSIZE, 0, 0, RANDOM_WRITE); 39774072Smarkm } 39874072Smarkm 39974072Smarkm /* Maybe the loop iterated at least once */ 40074072Smarkm if (i > count) 40174072Smarkm i -= HARVESTSIZE; 40274072Smarkm 40374072Smarkm /* Get the last bytes even if the input length is not 40474072Smarkm * a multiple of HARVESTSIZE. 40574072Smarkm */ 40674072Smarkm count %= HARVESTSIZE; 40774072Smarkm if (count) { 40874072Smarkm random_harvest_internal(get_cyclecount(), (char *)buf + i, 40974072Smarkm count, 0, 0, RANDOM_WRITE); 41074072Smarkm } 41174072Smarkm} 41274072Smarkm 41374072Smarkmvoid 41474072Smarkmrandom_unblock(void) 41574072Smarkm{ 41674072Smarkm if (!random_systat.seeded) { 41774072Smarkm random_systat.seeded = 1; 41874072Smarkm selwakeup(&random_systat.rsel); 41974072Smarkm wakeup(&random_systat); 42074072Smarkm } 42174072Smarkm} 422