| /* | 
 |  * This file is part of the UCB release of Plan 9. It is subject to the license | 
 |  * terms in the LICENSE file found in the top-level directory of this | 
 |  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
 |  * part of the UCB release of Plan 9, including this file, may be copied, | 
 |  * modified, propagated, or distributed except according to the terms contained | 
 |  * in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include <slab.h> | 
 | #include <kmalloc.h> | 
 | #include <kref.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <assert.h> | 
 | #include <error.h> | 
 | #include <cpio.h> | 
 | #include <pmap.h> | 
 | #include <smp.h> | 
 | #include <net/ip.h> | 
 | #include <random/fortuna.h> | 
 |  | 
 | static qlock_t rl; | 
 |  | 
 | /* | 
 |  * Add entropy. This is not currently used but we might want to hook it into a | 
 |  * hardware entropy source. | 
 |  */ | 
 | void random_add(void *xp) | 
 | { | 
 | 	ERRSTACK(1); | 
 |  | 
 | 	qlock(&rl); | 
 | 	if (waserror()) { | 
 | 		qunlock(&rl); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	fortuna_add_entropy(xp, sizeof(xp)); | 
 | 	qunlock(&rl); | 
 |  | 
 | 	poperror(); | 
 | } | 
 |  | 
 | /* | 
 |  *  consume random bytes | 
 |  */ | 
 | uint32_t random_read(void *xp, uint32_t n) | 
 | { | 
 | 	ERRSTACK(1); | 
 |  | 
 | 	qlock(&rl); | 
 |  | 
 | 	if (waserror()) { | 
 | 		qunlock(&rl); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	fortuna_get_bytes(n, xp); | 
 | 	qunlock(&rl); | 
 |  | 
 | 	poperror(); | 
 |  | 
 | 	return n; | 
 | } | 
 |  | 
 | /** | 
 |  * Fast random generator | 
 |  **/ | 
 | uint32_t urandom_read(void *xp, uint32_t n) | 
 | { | 
 | 	uint64_t seed[16]; | 
 | 	uint8_t *e, *p; | 
 | 	uint32_t x = 0; | 
 | 	uint64_t s0; | 
 | 	uint64_t s1; | 
 |  | 
 | 	if (n <= sizeof(seed)) | 
 | 		return random_read(xp, n); | 
 | 	// The initial seed is from a good random pool. | 
 | 	random_read(seed, sizeof(seed)); | 
 | 	p = xp; | 
 | 	for (e = p + n; p < e;) { | 
 | 		s0 = seed[x]; | 
 | 		s1 = seed[x = (x + 1) & 15]; | 
 | 		s1 ^= s1 << 31; | 
 | 		s1 ^= s1 >> 11; | 
 | 		s0 ^= s0 >> 30; | 
 | 		*p++ = (seed[x] = s0 ^ s1) * 1181783497276652981LL; | 
 | 	} | 
 |  | 
 | 	return n; | 
 | } | 
 |  | 
 | struct dev randomdevtab; | 
 |  | 
 | static char *devname(void) | 
 | { | 
 | 	return randomdevtab.name; | 
 | } | 
 |  | 
 | enum { | 
 | 	Qdir, | 
 | 	Qrandom, | 
 | 	Qurandom | 
 | }; | 
 |  | 
 | static | 
 | struct dirtab randomdir[] = { | 
 | 	{".", {Qdir, 0, QTDIR}, 0, DMDIR | 0500}, | 
 | 	{"random", {Qrandom}, 0, 0444}, | 
 | 	{"urandom", {Qurandom}, 0, 0444}, | 
 | }; | 
 |  | 
 | static void randominit(void) | 
 | { | 
 | 	qlock_init(&rl); | 
 | } | 
 |  | 
 | /* | 
 |  *  create a random, no streams are created until an open | 
 |  */ | 
 | static struct chan *randomattach(char *spec) | 
 | { | 
 | 	return devattach(devname(), spec); | 
 | } | 
 |  | 
 | static struct walkqid *randomwalk(struct chan *c, struct chan *nc, char **name, | 
 |                                   unsigned int nname) | 
 | { | 
 | 	return devwalk(c, nc, name, nname, randomdir, | 
 | 		       ARRAY_SIZE(randomdir), devgen); | 
 | } | 
 |  | 
 | static size_t randomstat(struct chan *c, uint8_t *dp, size_t n) | 
 | { | 
 | 	struct dir dir; | 
 | 	struct dirtab *tab; | 
 | 	int perm; | 
 |  | 
 | 	switch (c->qid.path) { | 
 | 	case Qrandom: | 
 | 		tab = &randomdir[Qrandom]; | 
 | 		perm = tab->perm | DMREADABLE; | 
 | 		devdir(c, tab->qid, tab->name, 0, eve.name, perm, &dir); | 
 | 		return dev_make_stat(c, &dir, dp, n); | 
 | 	case Qurandom: | 
 | 		tab = &randomdir[Qurandom]; | 
 | 		perm = tab->perm | DMREADABLE; | 
 | 		devdir(c, tab->qid, tab->name, 0, eve.name, perm, &dir); | 
 | 		return dev_make_stat(c, &dir, dp, n); | 
 | 	default: | 
 | 		return devstat(c, dp, n, randomdir, ARRAY_SIZE(randomdir), devgen); | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  *  if the stream doesn't exist, create it | 
 |  */ | 
 | static struct chan *randomopen(struct chan *c, int omode) | 
 | { | 
 | 	return devopen(c, omode, randomdir, ARRAY_SIZE(randomdir), devgen); | 
 | } | 
 |  | 
 | static void randomclose(struct chan *c) | 
 | { | 
 | } | 
 |  | 
 | static size_t randomread(struct chan *c, void *va, size_t n, off64_t ignored) | 
 | { | 
 | 	switch (c->qid.path) { | 
 | 		case Qdir: | 
 | 			return devdirread(c, va, n, randomdir, | 
 | 					  ARRAY_SIZE(randomdir), devgen); | 
 | 		case Qrandom: | 
 | 			return random_read(va, n); | 
 | 		case Qurandom: | 
 | 			return urandom_read(va, n); | 
 | 		default: | 
 | 			panic("randomread: qid %d is impossible", c->qid.path); | 
 | 	} | 
 | 	return -1;	/* not reached */ | 
 | } | 
 |  | 
 | /* | 
 |  *  A write to a closed random causes an ERANDOM error to be thrown. | 
 |  */ | 
 | static size_t randomwrite(struct chan *c, void *va, size_t n, off64_t ignored) | 
 | { | 
 | 	error(EPERM, "No use for writing random just yet"); | 
 | 	return -1; | 
 | } | 
 |  | 
 | static long randombwrite(struct chan *c, struct block *bp, uint32_t junk) | 
 | { | 
 | 	error(EPERM, "No use for writing random just yet"); | 
 | 	return -1; | 
 | } | 
 |  | 
 | static int random_tapfd(struct chan *c, struct fd_tap *tap, int cmd) | 
 | { | 
 | 	/* We don't actually support HANGUP, but epoll implies it. */ | 
 | 	#define RANDOM_TAPS (FDTAP_FILT_READABLE | FDTAP_FILT_HANGUP) | 
 |  | 
 | 	if (tap->filter & ~RANDOM_TAPS) { | 
 | 		set_error(ENOSYS, "Unsupported #%s tap, must be %p", devname(), | 
 | 		          RANDOM_TAPS); | 
 | 		return -1; | 
 | 	} | 
 | 	switch (c->qid.path) { | 
 | 	case Qrandom: | 
 | 	case Qurandom: | 
 | 		/* Faking any legit command on (u)random, which never blocks. */ | 
 | 		return 0; | 
 | 	default: | 
 | 		set_error(ENOSYS, "Can't tap #%s file type %d", devname(), | 
 | 		          c->qid.path); | 
 | 		return -1; | 
 | 	} | 
 | } | 
 |  | 
 | struct dev randomdevtab __devtab = { | 
 | 	.name = "random", | 
 |  | 
 | 	.reset = devreset, | 
 | 	.init = randominit, | 
 | 	.shutdown = devshutdown, | 
 | 	.attach = randomattach, | 
 | 	.walk = randomwalk, | 
 | 	.stat = randomstat, | 
 | 	.open = randomopen, | 
 | 	.create = devcreate, | 
 | 	.close = randomclose, | 
 | 	.read = randomread, | 
 | 	.write = randomwrite, | 
 | 	.remove = devremove, | 
 | 	.wstat = devwstat, | 
 | 	.power = devpower, | 
 | 	.chaninfo = devchaninfo, | 
 | 	.tapfd = random_tapfd, | 
 | }; |