blob: 75e4acb42f9b96861acdc66f98f8be6b65e08922 [file] [log] [blame]
/*
* 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,
};