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