| /* |
| * 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, |
| }; |