akaros/kern/drivers/dev/random.c
<<
>>
Prefs
   1/*
   2 * This file is part of the UCB release of Plan 9. It is subject to the license
   3 * terms in the LICENSE file found in the top-level directory of this
   4 * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
   5 * part of the UCB release of Plan 9, including this file, may be copied,
   6 * modified, propagated, or distributed except according to the terms contained
   7 * in the LICENSE file.
   8 */
   9
  10#include <slab.h>
  11#include <kmalloc.h>
  12#include <kref.h>
  13#include <string.h>
  14#include <stdio.h>
  15#include <assert.h>
  16#include <error.h>
  17#include <cpio.h>
  18#include <pmap.h>
  19#include <smp.h>
  20#include <net/ip.h>
  21#include <random/fortuna.h>
  22
  23static qlock_t rl;
  24
  25/*
  26 * Add entropy. This is not currently used but we might want to hook it into a
  27 * hardware entropy source.
  28 */
  29void random_add(void *xp)
  30{
  31        ERRSTACK(1);
  32
  33        qlock(&rl);
  34        if (waserror()) {
  35                qunlock(&rl);
  36                nexterror();
  37        }
  38
  39        fortuna_add_entropy(xp, sizeof(xp));
  40        qunlock(&rl);
  41
  42        poperror();
  43}
  44
  45/*
  46 *  consume random bytes
  47 */
  48uint32_t random_read(void *xp, uint32_t n)
  49{
  50        ERRSTACK(1);
  51
  52        qlock(&rl);
  53
  54        if (waserror()) {
  55                qunlock(&rl);
  56                nexterror();
  57        }
  58
  59        fortuna_get_bytes(n, xp);
  60        qunlock(&rl);
  61
  62        poperror();
  63
  64        return n;
  65}
  66
  67/**
  68 * Fast random generator
  69 **/
  70uint32_t urandom_read(void *xp, uint32_t n)
  71{
  72        uint64_t seed[16];
  73        uint8_t *e, *p;
  74        uint32_t x = 0;
  75        uint64_t s0;
  76        uint64_t s1;
  77
  78        if (n <= sizeof(seed))
  79                return random_read(xp, n);
  80        // The initial seed is from a good random pool.
  81        random_read(seed, sizeof(seed));
  82        p = xp;
  83        for (e = p + n; p < e;) {
  84                s0 = seed[x];
  85                s1 = seed[x = (x + 1) & 15];
  86                s1 ^= s1 << 31;
  87                s1 ^= s1 >> 11;
  88                s0 ^= s0 >> 30;
  89                *p++ = (seed[x] = s0 ^ s1) * 1181783497276652981LL;
  90        }
  91
  92        return n;
  93}
  94
  95struct dev randomdevtab;
  96
  97static char *devname(void)
  98{
  99        return randomdevtab.name;
 100}
 101
 102enum {
 103        Qdir,
 104        Qrandom,
 105        Qurandom
 106};
 107
 108static struct dirtab randomdir[] = {
 109        {".", {Qdir, 0, QTDIR}, 0, DMDIR | 0500},
 110        {"random", {Qrandom}, 0, 0444},
 111        {"urandom", {Qurandom}, 0, 0444},
 112};
 113
 114static void randominit(void)
 115{
 116        qlock_init(&rl);
 117}
 118
 119/*
 120 *  create a random, no streams are created until an open
 121 */
 122static struct chan *randomattach(char *spec)
 123{
 124        return devattach(devname(), spec);
 125}
 126
 127static struct walkqid *randomwalk(struct chan *c, struct chan *nc, char **name,
 128                                  unsigned int nname)
 129{
 130        return devwalk(c, nc, name, nname, randomdir,
 131                       ARRAY_SIZE(randomdir), devgen);
 132}
 133
 134static size_t randomstat(struct chan *c, uint8_t *dp, size_t n)
 135{
 136        struct dir dir;
 137        struct dirtab *tab;
 138        int perm;
 139
 140        switch (c->qid.path) {
 141        case Qrandom:
 142                tab = &randomdir[Qrandom];
 143                perm = tab->perm | DMREADABLE;
 144                devdir(c, tab->qid, tab->name, 0, eve.name, perm, &dir);
 145                return dev_make_stat(c, &dir, dp, n);
 146        case Qurandom:
 147                tab = &randomdir[Qurandom];
 148                perm = tab->perm | DMREADABLE;
 149                devdir(c, tab->qid, tab->name, 0, eve.name, perm, &dir);
 150                return dev_make_stat(c, &dir, dp, n);
 151        default:
 152                return devstat(c, dp, n, randomdir, ARRAY_SIZE(randomdir),
 153                               devgen);
 154        }
 155}
 156
 157/*
 158 *  if the stream doesn't exist, create it
 159 */
 160static struct chan *randomopen(struct chan *c, int omode)
 161{
 162        return devopen(c, omode, randomdir, ARRAY_SIZE(randomdir), devgen);
 163}
 164
 165static void randomclose(struct chan *c)
 166{
 167}
 168
 169static size_t randomread(struct chan *c, void *va, size_t n, off64_t ignored)
 170{
 171        switch (c->qid.path) {
 172        case Qdir:
 173                return devdirread(c, va, n, randomdir,
 174                                  ARRAY_SIZE(randomdir), devgen);
 175        case Qrandom:
 176                return random_read(va, n);
 177        case Qurandom:
 178                return urandom_read(va, n);
 179        default:
 180                panic("randomread: qid %d is impossible", c->qid.path);
 181        }
 182        return -1;      /* not reached */
 183}
 184
 185/*
 186 *  A write to a closed random causes an ERANDOM error to be thrown.
 187 */
 188static size_t randomwrite(struct chan *c, void *va, size_t n, off64_t ignored)
 189{
 190        error(EPERM, "No use for writing random just yet");
 191        return -1;
 192}
 193
 194static long randombwrite(struct chan *c, struct block *bp, uint32_t junk)
 195{
 196        error(EPERM, "No use for writing random just yet");
 197        return -1;
 198}
 199
 200static int random_tapfd(struct chan *c, struct fd_tap *tap, int cmd)
 201{
 202        /* We don't actually support HANGUP, but epoll implies it. */
 203        #define RANDOM_TAPS (FDTAP_FILT_READABLE | FDTAP_FILT_HANGUP)
 204
 205        if (tap->filter & ~RANDOM_TAPS) {
 206                set_error(ENOSYS, "Unsupported #%s tap %p, must be %p",
 207                          devname(), tap->filter, RANDOM_TAPS);
 208                return -1;
 209        }
 210        switch (c->qid.path) {
 211        case Qrandom:
 212        case Qurandom:
 213                /* Faking any legit command on (u)random, which never blocks. */
 214                return 0;
 215        default:
 216                set_error(ENOSYS, "Can't tap #%s file type %d", devname(),
 217                          c->qid.path);
 218                return -1;
 219        }
 220}
 221
 222struct dev randomdevtab __devtab = {
 223        .name = "random",
 224
 225        .reset = devreset,
 226        .init = randominit,
 227        .shutdown = devshutdown,
 228        .attach = randomattach,
 229        .walk = randomwalk,
 230        .stat = randomstat,
 231        .open = randomopen,
 232        .create = devcreate,
 233        .close = randomclose,
 234        .read = randomread,
 235        .write = randomwrite,
 236        .remove = devremove,
 237        .wstat = devwstat,
 238        .power = devpower,
 239        .chaninfo = devchaninfo,
 240        .tapfd = random_tapfd,
 241};
 242