randomdev.c revision 91600
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 91600 2002-03-03 19:44:22Z markm $ 2762053Smarkm */ 2862053Smarkm 2962053Smarkm#include <sys/param.h> 3062053Smarkm#include <sys/systm.h> 3176166Smarkm#include <sys/bus.h> 3262053Smarkm#include <sys/conf.h> 3376166Smarkm#include <sys/fcntl.h> 3474771Smarkm#include <sys/filio.h> 3562053Smarkm#include <sys/kernel.h> 3674072Smarkm#include <sys/kthread.h> 3776166Smarkm#include <sys/lock.h> 3862053Smarkm#include <sys/malloc.h> 3976166Smarkm#include <sys/mutex.h> 4067112Smarkm#include <sys/poll.h> 4183366Sjulian#include <sys/proc.h> 4276166Smarkm#include <sys/random.h> 4370834Swollman#include <sys/selinfo.h> 4471037Smarkm#include <sys/sysctl.h> 4576166Smarkm#include <sys/uio.h> 4676166Smarkm#include <sys/unistd.h> 4767112Smarkm#include <sys/vnode.h> 4874072Smarkm 4962053Smarkm#include <machine/bus.h> 5074072Smarkm#include <machine/cpu.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, 7891600Smarkm /* kqfilter */ NULL 7962053Smarkm}; 8062053Smarkm 8174072Smarkmstatic void random_kthread(void *); 8274072Smarkmstatic void random_harvest_internal(u_int64_t, void *, u_int, u_int, u_int, enum esource); 8391600Smarkmstatic void random_write_internal(void *, 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 10791600Smarkm/* ARGSUSED */ 10874072Smarkmstatic int 10974072Smarkmrandom_check_boolean(SYSCTL_HANDLER_ARGS) 11074072Smarkm{ 11174072Smarkm if (oidp->oid_arg1 != NULL && *(u_int *)(oidp->oid_arg1) != 0) 11274072Smarkm *(u_int *)(oidp->oid_arg1) = 1; 11374072Smarkm return sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); 11474072Smarkm} 11562053Smarkm 11674072SmarkmRANDOM_CHECK_UINT(burst, 0, 20); 11774072Smarkm 11874072SmarkmSYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 11974072Smarkm 0, "Random Number Generator"); 12074072SmarkmSYSCTL_NODE(_kern_random, OID_AUTO, sys, CTLFLAG_RW, 12174072Smarkm 0, "Entropy Device Parameters"); 12274072SmarkmSYSCTL_PROC(_kern_random_sys, OID_AUTO, seeded, 12374072Smarkm CTLTYPE_INT|CTLFLAG_RW, &random_systat.seeded, 1, 12474072Smarkm random_check_boolean, "I", "Seeded State"); 12574072SmarkmSYSCTL_PROC(_kern_random_sys, OID_AUTO, burst, 12674072Smarkm CTLTYPE_INT|CTLFLAG_RW, &random_systat.burst, 20, 12774072Smarkm random_check_uint_burst, "I", "Harvest Burst Size"); 12874072SmarkmSYSCTL_NODE(_kern_random_sys, OID_AUTO, harvest, CTLFLAG_RW, 12974072Smarkm 0, "Entropy Sources"); 13074072SmarkmSYSCTL_PROC(_kern_random_sys_harvest, OID_AUTO, ethernet, 13174072Smarkm CTLTYPE_INT|CTLFLAG_RW, &harvest.ethernet, 0, 13274072Smarkm random_check_boolean, "I", "Harvest NIC entropy"); 13374072SmarkmSYSCTL_PROC(_kern_random_sys_harvest, OID_AUTO, point_to_point, 13474072Smarkm CTLTYPE_INT|CTLFLAG_RW, &harvest.point_to_point, 0, 13574072Smarkm random_check_boolean, "I", "Harvest serial net entropy"); 13674072SmarkmSYSCTL_PROC(_kern_random_sys_harvest, OID_AUTO, interrupt, 13774072Smarkm CTLTYPE_INT|CTLFLAG_RW, &harvest.interrupt, 0, 13874072Smarkm random_check_boolean, "I", "Harvest IRQ entropy"); 13974072Smarkm 14091600Smarkm/* ARGSUSED */ 14162053Smarkmstatic int 14291600Smarkmrandom_open(dev_t dev __unused, int flags, int fmt __unused, struct thread *td) 14363855Smarkm{ 14483976Srwatson int error; 14583976Srwatson 14683976Srwatson if (flags & FWRITE) { 14783976Srwatson error = suser(td->td_proc); 14883976Srwatson if (error) 14983976Srwatson return (error); 15091406Sjhb error = securelevel_gt(td->td_ucred, 0); 15183976Srwatson if (error) 15283976Srwatson return (error); 15383976Srwatson } 15483976Srwatson return 0; 15563855Smarkm} 15663855Smarkm 15791600Smarkm/* ARGSUSED */ 15863855Smarkmstatic int 15991600Smarkmrandom_close(dev_t dev __unused, int flags, int fmt __unused, struct thread *td) 16069172Smarkm{ 16183976Srwatson if (flags & FWRITE) { 16283976Srwatson if (!(suser(td->td_proc) || 16391406Sjhb securelevel_gt(td->td_ucred, 0))) 16483976Srwatson random_reseed(); 16583976Srwatson } 16669172Smarkm return 0; 16769172Smarkm} 16869172Smarkm 16991600Smarkm/* ARGSUSED */ 17069172Smarkmstatic int 17191600Smarkmrandom_read(dev_t dev __unused, struct uio *uio, int flag) 17262053Smarkm{ 17391600Smarkm int c, ret; 17474072Smarkm int error = 0; 17574072Smarkm void *random_buf; 17662053Smarkm 17774072Smarkm while (!random_systat.seeded) { 17867286Speter if (flag & IO_NDELAY) 17967286Speter error = EWOULDBLOCK; 18067112Smarkm else 18174072Smarkm error = tsleep(&random_systat, PUSER|PCATCH, 18274072Smarkm "block", 0); 18367286Speter if (error != 0) 18467286Speter return error; 18567112Smarkm } 18691600Smarkm c = uio->uio_resid < PAGE_SIZE ? uio->uio_resid : PAGE_SIZE; 18791600Smarkm random_buf = (void *)malloc((u_long)c, M_TEMP, M_WAITOK); 18867286Speter while (uio->uio_resid > 0 && error == 0) { 18967286Speter ret = read_random_real(random_buf, c); 19067286Speter error = uiomove(random_buf, ret, uio); 19167286Speter } 19267286Speter free(random_buf, M_TEMP); 19362053Smarkm return error; 19462053Smarkm} 19562053Smarkm 19691600Smarkm/* ARGSUSED */ 19762053Smarkmstatic int 19891600Smarkmrandom_write(dev_t dev __unused, struct uio *uio, int flag __unused) 19962053Smarkm{ 20091600Smarkm int c; 20174072Smarkm int error; 20274072Smarkm void *random_buf; 20362053Smarkm 20474072Smarkm error = 0; 20562765Smarkm random_buf = (void *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK); 20662053Smarkm while (uio->uio_resid > 0) { 20791600Smarkm c = (int)(uio->uio_resid < PAGE_SIZE 20891600Smarkm ? uio->uio_resid 20991600Smarkm : PAGE_SIZE); 21062765Smarkm error = uiomove(random_buf, c, uio); 21162053Smarkm if (error) 21262053Smarkm break; 21374072Smarkm random_write_internal(random_buf, c); 21462053Smarkm } 21562765Smarkm free(random_buf, M_TEMP); 21662053Smarkm return error; 21762053Smarkm} 21862053Smarkm 21991600Smarkm/* ARGSUSED */ 22062053Smarkmstatic int 22191600Smarkmrandom_ioctl(dev_t dev __unused, u_long cmd, caddr_t addr __unused, 22291600Smarkm int flags __unused, struct thread *td __unused) 22365686Smarkm{ 22474771Smarkm switch (cmd) { 22574771Smarkm /* Really handled in upper layer */ 22674771Smarkm case FIOASYNC: 22774771Smarkm case FIONBIO: 22874771Smarkm return 0; 22974771Smarkm default: 23074771Smarkm return ENOTTY; 23174771Smarkm } 23265686Smarkm} 23365686Smarkm 23491600Smarkm/* ARGSUSED */ 23565686Smarkmstatic int 23691600Smarkmrandom_poll(dev_t dev __unused, int events, struct thread *td) 23767112Smarkm{ 23874072Smarkm int revents; 23967112Smarkm 24067112Smarkm revents = 0; 24167112Smarkm if (events & (POLLIN | POLLRDNORM)) { 24274072Smarkm if (random_systat.seeded) 24367112Smarkm revents = events & (POLLIN | POLLRDNORM); 24467112Smarkm else 24583805Sjhb selrecord(td, &random_systat.rsel); 24667112Smarkm } 24767112Smarkm return revents; 24867112Smarkm} 24967112Smarkm 25091600Smarkm/* ARGSUSED */ 25167112Smarkmstatic int 25291600Smarkmrandom_modevent(module_t mod __unused, int type, void *data __unused) 25362053Smarkm{ 25474072Smarkm int error; 25565686Smarkm 25662053Smarkm switch(type) { 25762053Smarkm case MOD_LOAD: 25874072Smarkm random_init(); 25971037Smarkm 26074072Smarkm /* This can be turned off by the very paranoid 26174072Smarkm * a reseed will turn it back on. 26274072Smarkm */ 26374072Smarkm random_systat.seeded = 1; 26471037Smarkm 26574072Smarkm /* Number of envents to process off the harvest 26674072Smarkm * queue before giving it a break and sleeping 26774072Smarkm */ 26874072Smarkm random_systat.burst = 20; 26974072Smarkm 27074072Smarkm /* Initialise the harvest ringbuffer */ 27174072Smarkm harvestring.head = 0; 27274072Smarkm harvestring.tail = 0; 27374072Smarkm 27462053Smarkm if (bootverbose) 27562053Smarkm printf("random: <entropy source>\n"); 27662765Smarkm random_dev = make_dev(&random_cdevsw, RANDOM_MINOR, UID_ROOT, 27762149Smarkm GID_WHEEL, 0666, "random"); 27874072Smarkm urandom_dev = make_dev_alias(random_dev, "urandom"); 27974072Smarkm 28074072Smarkm /* Start the hash/reseed thread */ 28174072Smarkm error = kthread_create(random_kthread, NULL, 28274072Smarkm &random_kthread_proc, RFHIGHPID, "random"); 28374072Smarkm if (error != 0) 28474072Smarkm return error; 28574072Smarkm 28674072Smarkm /* Register the randomness harvesting routine */ 28774072Smarkm random_init_harvester(random_harvest_internal, 28874072Smarkm read_random_real); 28974072Smarkm 29062053Smarkm return 0; 29162053Smarkm 29262053Smarkm case MOD_UNLOAD: 29374072Smarkm /* Deregister the randomness harvesting routine */ 29474072Smarkm random_deinit_harvester(); 29574072Smarkm 29674072Smarkm /* Command the hash/reseed thread to end and 29774072Smarkm * wait for it to finish 29874072Smarkm */ 29974072Smarkm random_kthread_control = -1; 30074072Smarkm tsleep((void *)&random_kthread_control, PUSER, "term", 0); 30174072Smarkm 30262765Smarkm random_deinit(); 30374072Smarkm 30462765Smarkm destroy_dev(random_dev); 30574072Smarkm destroy_dev(urandom_dev); 30662053Smarkm return 0; 30762053Smarkm 30862053Smarkm case MOD_SHUTDOWN: 30962053Smarkm return 0; 31062053Smarkm 31162053Smarkm default: 31262053Smarkm return EOPNOTSUPP; 31362053Smarkm } 31462053Smarkm} 31562053Smarkm 31662053SmarkmDEV_MODULE(random, random_modevent, NULL); 31774072Smarkm 31891600Smarkm/* ARGSUSED */ 31974072Smarkmstatic void 32091600Smarkmrandom_kthread(void *arg __unused) 32174072Smarkm{ 32274072Smarkm struct harvest *event; 32391600Smarkm u_int newtail, burst; 32474072Smarkm 32574072Smarkm /* Drain the harvest queue (in 'burst' size chunks, 32674072Smarkm * if 'burst' > 0. If 'burst' == 0, then completely 32774072Smarkm * drain the queue. 32874072Smarkm */ 32974072Smarkm for (burst = 0; ; burst++) { 33074072Smarkm 33174072Smarkm if ((harvestring.tail == harvestring.head) || 33274072Smarkm (random_systat.burst && burst == random_systat.burst)) { 33374072Smarkm tsleep(&harvestring, PUSER, "sleep", hz/10); 33474072Smarkm burst = 0; 33574072Smarkm 33674072Smarkm } 33774072Smarkm else { 33874072Smarkm 33974072Smarkm /* Suck a harvested entropy event out of the queue and 34074072Smarkm * hand it to the event processor 34174072Smarkm */ 34274072Smarkm 34374072Smarkm newtail = (harvestring.tail + 1) & HARVEST_RING_MASK; 34474072Smarkm event = &harvestring.data[harvestring.tail]; 34574072Smarkm 34674072Smarkm /* Bump the ring counter. This action is assumed 34774072Smarkm * to be atomic. 34874072Smarkm */ 34974072Smarkm harvestring.tail = newtail; 35074072Smarkm 35174072Smarkm random_process_event(event); 35274072Smarkm 35374072Smarkm } 35474072Smarkm 35574072Smarkm /* Is the thread scheduled for a shutdown? */ 35674072Smarkm if (random_kthread_control != 0) { 35774072Smarkm#ifdef DEBUG 35874072Smarkm mtx_lock(&Giant); 35974072Smarkm printf("Random kthread setting terminate\n"); 36074072Smarkm mtx_unlock(&Giant); 36174072Smarkm#endif 36274072Smarkm random_set_wakeup_exit(&random_kthread_control); 36374072Smarkm /* NOTREACHED */ 36474072Smarkm break; 36574072Smarkm } 36674072Smarkm 36774072Smarkm } 36874072Smarkm 36974072Smarkm} 37074072Smarkm 37174072Smarkm/* Entropy harvesting routine. This is supposed to be fast; do 37274072Smarkm * not do anything slow in here! 37374072Smarkm */ 37474072Smarkmstatic void 37574072Smarkmrandom_harvest_internal(u_int64_t somecounter, void *entropy, u_int count, 37674072Smarkm u_int bits, u_int frac, enum esource origin) 37774072Smarkm{ 37891600Smarkm struct harvest *pharvest; 37991600Smarkm u_int newhead; 38074072Smarkm 38174072Smarkm newhead = (harvestring.head + 1) & HARVEST_RING_MASK; 38274072Smarkm 38374072Smarkm if (newhead != harvestring.tail) { 38474072Smarkm 38574072Smarkm /* Add the harvested data to the ring buffer */ 38674072Smarkm 38791600Smarkm pharvest = &harvestring.data[harvestring.head]; 38874072Smarkm 38974072Smarkm /* Stuff the harvested data into the ring */ 39091600Smarkm pharvest->somecounter = somecounter; 39174072Smarkm count = count > HARVESTSIZE ? HARVESTSIZE : count; 39291600Smarkm memcpy(pharvest->entropy, entropy, count); 39391600Smarkm pharvest->size = count; 39491600Smarkm pharvest->bits = bits; 39591600Smarkm pharvest->frac = frac; 39691600Smarkm pharvest->source = 39791600Smarkm origin < ENTROPYSOURCE ? origin : RANDOM_START; 39874072Smarkm 39974072Smarkm /* Bump the ring counter. This action is assumed 40074072Smarkm * to be atomic. 40174072Smarkm */ 40274072Smarkm harvestring.head = newhead; 40374072Smarkm 40474072Smarkm } 40574072Smarkm 40674072Smarkm} 40774072Smarkm 40874072Smarkmstatic void 40991600Smarkmrandom_write_internal(void *buf, int count) 41074072Smarkm{ 41191600Smarkm int i; 41274072Smarkm 41374072Smarkm /* Break the input up into HARVESTSIZE chunks. 41474072Smarkm * The writer has too much control here, so "estimate" the 41574072Smarkm * the entropy as zero. 41674072Smarkm */ 41774072Smarkm for (i = 0; i < count; i += HARVESTSIZE) { 41874072Smarkm random_harvest_internal(get_cyclecount(), (char *)buf + i, 41974072Smarkm HARVESTSIZE, 0, 0, RANDOM_WRITE); 42074072Smarkm } 42174072Smarkm 42274072Smarkm /* Maybe the loop iterated at least once */ 42374072Smarkm if (i > count) 42474072Smarkm i -= HARVESTSIZE; 42574072Smarkm 42674072Smarkm /* Get the last bytes even if the input length is not 42774072Smarkm * a multiple of HARVESTSIZE. 42874072Smarkm */ 42974072Smarkm count %= HARVESTSIZE; 43074072Smarkm if (count) { 43174072Smarkm random_harvest_internal(get_cyclecount(), (char *)buf + i, 43291600Smarkm (u_int)count, 0, 0, RANDOM_WRITE); 43374072Smarkm } 43474072Smarkm} 43574072Smarkm 43674072Smarkmvoid 43774072Smarkmrandom_unblock(void) 43874072Smarkm{ 43974072Smarkm if (!random_systat.seeded) { 44074072Smarkm random_systat.seeded = 1; 44174072Smarkm selwakeup(&random_systat.rsel); 44274072Smarkm wakeup(&random_systat); 44374072Smarkm } 44474072Smarkm} 445