Use the new RNG for the networking stack
[akaros.git] / kern / drivers / dev / random.c
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 <vfs.h>
11 #include <kfs.h>
12 #include <slab.h>
13 #include <kmalloc.h>
14 #include <kref.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <error.h>
19 #include <cpio.h>
20 #include <pmap.h>
21 #include <smp.h>
22 #include <ip.h>
23 #include <random/fortuna.h>
24
25 static qlock_t rl;
26
27 /*
28  * Add entropy. This is not currently used but we might want to hook it into a
29  * hardware entropy source.
30  */
31 void random_add(void *xp)
32 {
33         ERRSTACK(1);
34
35         qlock(&rl);
36         if (waserror()) {
37                 qunlock(&rl);
38                 nexterror();
39         }
40
41         fortuna_add_entropy(xp, sizeof(xp));
42         qunlock(&rl);
43
44         poperror();
45 }
46
47 /*
48  *  consume random bytes
49  */
50 uint32_t random_read(void *xp, uint32_t n)
51 {
52         ERRSTACK(1);
53
54         qlock(&rl);
55
56         if (waserror()) {
57                 qunlock(&rl);
58                 nexterror();
59         }
60
61         fortuna_get_bytes(n, xp);
62         qunlock(&rl);
63
64         poperror();
65
66         return n;
67 }
68
69 /**
70  * Fast random generator
71  **/
72 uint32_t urandom_read(void *xp, uint32_t n)
73 {
74         uint64_t seed[16];
75         uint8_t *e, *p;
76         uint32_t x = 0;
77         uint64_t s0;
78         uint64_t s1;
79
80         if (n <= sizeof(seed))
81                 return random_read(xp, n);
82         // The initial seed is from a good random pool.
83         random_read(seed, sizeof(seed));
84         p = xp;
85         for (e = p + n; p < e;) {
86                 s0 = seed[x];
87                 s1 = seed[x = (x + 1) & 15];
88                 s1 ^= s1 << 31;
89                 s1 ^= s1 >> 11;
90                 s0 ^= s0 >> 30;
91                 *p++ = (seed[x] = s0 ^ s1) * 1181783497276652981LL;
92         }
93
94         return n;
95 }
96
97 struct dev randomdevtab;
98
99 static char *devname(void)
100 {
101         return randomdevtab.name;
102 }
103
104 enum {
105         Qdir,
106         Qrandom,
107         Qurandom
108 };
109
110 static
111 struct dirtab randomdir[] = {
112         {".", {Qdir, 0, QTDIR}, 0, DMDIR | 0500},
113         {"random", {Qrandom}, 0, 0444},
114         {"urandom", {Qurandom}, 0, 0444},
115 };
116
117 static void randominit(void)
118 {
119         qlock_init(&rl);
120 }
121
122 /*
123  *  create a random, no streams are created until an open
124  */
125 static struct chan *randomattach(char *spec)
126 {
127         return devattach(devname(), spec);
128 }
129
130 static struct walkqid *randomwalk(struct chan *c, struct chan *nc, char **name,
131                                                                 int nname)
132 {
133         return devwalk(c, nc, name, nname, randomdir,
134                        ARRAY_SIZE(randomdir), devgen);
135 }
136
137 static int randomstat(struct chan *c, uint8_t *dp, int n)
138 {
139         return devstat(c, dp, n, randomdir, ARRAY_SIZE(randomdir), devgen);
140 }
141
142 /*
143  *  if the stream doesn't exist, create it
144  */
145 static struct chan *randomopen(struct chan *c, int omode)
146 {
147         return devopen(c, omode, randomdir, ARRAY_SIZE(randomdir), devgen);
148 }
149
150 static void randomclose(struct chan *c)
151 {
152 }
153
154 static long randomread(struct chan *c, void *va, long n, int64_t ignored)
155 {
156         switch (c->qid.path) {
157                 case Qdir:
158                         return devdirread(c, va, n, randomdir,
159                                           ARRAY_SIZE(randomdir), devgen);
160                 case Qrandom:
161                         return random_read(va, n);
162                 case Qurandom:
163                         return urandom_read(va, n);
164                 default:
165                         panic("randomread: qid %d is impossible", c->qid.path);
166         }
167         return -1;      /* not reached */
168 }
169
170 /*
171  *  A write to a closed random causes an ERANDOM error to be thrown.
172  */
173 static long randomwrite(struct chan *c, void *va, long n, int64_t ignored)
174 {
175         error(EPERM, "No use for writing random just yet");
176         return -1;
177 }
178
179 static long randombwrite(struct chan *c, struct block *bp, uint32_t junk)
180 {
181         error(EPERM, "No use for writing random just yet");
182         return -1;
183 }
184
185 static int randomwstat(struct chan *c, uint8_t *dp, int n)
186 {
187         error(EPERM, "No use for wstat random just yet");
188         return -1;
189 }
190
191 struct dev randomdevtab __devtab = {
192         .name = "random",
193
194         .reset = devreset,
195         .init = randominit,
196         .shutdown = devshutdown,
197         .attach = randomattach,
198         .walk = randomwalk,
199         .stat = randomstat,
200         .open = randomopen,
201         .create = devcreate,
202         .close = randomclose,
203         .read = randomread,
204         .write = randomwrite,
205         .remove = devremove,
206         .wstat = randomwstat,
207         .power = devpower,
208         .chaninfo = devchaninfo,
209 };