Add a random device; remove old genrandom junk; remove random from #cons
[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 static uint32_t _randomread(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 urandomread(void *xp, uint32_t n)
73 {
74         ERRSTACK(1);
75         uint64_t seed[16];
76         uint8_t *e, *p;
77         uint32_t x = 0;
78         uint64_t s0;
79         uint64_t s1;
80
81         // The initial seed is from a good random pool.
82         _randomread(seed, sizeof(seed));
83         p = xp;
84         for (e = p + n; p < e;) {
85                 s0 = seed[x];
86                 s1 = seed[x = (x + 1) & 15];
87                 s1 ^= s1 << 31;
88                 s1 ^= s1 >> 11;
89                 s0 ^= s0 >> 30;
90                 *p++ = (seed[x] = s0 ^ s1) * 1181783497276652981LL;
91         }
92
93         return n;
94 }
95
96 struct dev randomdevtab;
97
98 static char *devname(void)
99 {
100         return randomdevtab.name;
101 }
102
103 enum {
104         Qdir,
105         Qrandom,
106         Qurandom
107 };
108
109 static
110 struct dirtab randomdir[] = {
111         {".", {Qdir, 0, QTDIR}, 0, DMDIR | 0500},
112         {"random", {Qrandom}, 0, 0444},
113         {"urandom", {Qurandom}, 0, 0444},
114 };
115
116 static void randominit(void)
117 {
118         qlock_init(&rl);
119 }
120
121 /*
122  *  create a random, no streams are created until an open
123  */
124 static struct chan *randomattach(char *spec)
125 {
126         return devattach(devname(), spec);
127 }
128
129 static struct walkqid *randomwalk(struct chan *c, struct chan *nc, char **name,
130                                                                 int nname)
131 {
132         return devwalk(c, nc, name, nname, randomdir,
133                        ARRAY_SIZE(randomdir), devgen);
134 }
135
136 static int randomstat(struct chan *c, uint8_t *dp, int n)
137 {
138         return devstat(c, dp, n, randomdir, ARRAY_SIZE(randomdir), devgen);
139 }
140
141 /*
142  *  if the stream doesn't exist, create it
143  */
144 static struct chan *randomopen(struct chan *c, int omode)
145 {
146         return devopen(c, omode, randomdir, ARRAY_SIZE(randomdir), devgen);
147 }
148
149 static void randomclose(struct chan *c)
150 {
151 }
152
153 static long randomread(struct chan *c, void *va, long n, int64_t ignored)
154 {
155         switch (c->qid.path) {
156                 case Qdir:
157                         return devdirread(c, va, n, randomdir,
158                                           ARRAY_SIZE(randomdir), devgen);
159                 case Qrandom:
160                         return _randomread(va, n);
161                 case Qurandom:
162                         return urandomread(va, n);
163                 default:
164                         panic("randomread: qid %d is impossible", c->qid.path);
165         }
166         return -1;      /* not reached */
167 }
168
169 /*
170  *  A write to a closed random causes an ERANDOM error to be thrown.
171  */
172 static long randomwrite(struct chan *c, void *va, long n, int64_t ignored)
173 {
174         error(EPERM, "No use for writing random just yet");
175         return -1;
176 }
177
178 static long randombwrite(struct chan *c, struct block *bp, uint32_t junk)
179 {
180         error(EPERM, "No use for writing random just yet");
181         return -1;
182 }
183
184 static int randomwstat(struct chan *c, uint8_t *dp, int n)
185 {
186         error(EPERM, "No use for wstat random just yet");
187         return -1;
188 }
189
190 struct dev randomdevtab __devtab = {
191         .name = "random",
192
193         .reset = devreset,
194         .init = randominit,
195         .shutdown = devshutdown,
196         .attach = randomattach,
197         .walk = randomwalk,
198         .stat = randomstat,
199         .open = randomopen,
200         .create = devcreate,
201         .close = randomclose,
202         .read = randomread,
203         .write = randomwrite,
204         .remove = devremove,
205         .wstat = randomwstat,
206         .power = devpower,
207         .chaninfo = devchaninfo,
208 };
209
210 /* This is something used by the TCP stack.
211  * I have no idea of why these numbers were chosen. */
212 static uint32_t randn;
213
214 static void seedrand(void)
215 {
216         ERRSTACK(2);
217         if (!waserror()) {
218                 _randomread((void *)&randn, sizeof(randn));
219                 poperror();
220         }
221 }
222
223 int nrand(int n)
224 {
225         if (randn == 0)
226                 seedrand();
227         randn = randn * 1103515245 + 12345 + read_tsc();
228         return (randn >> 16) % n;
229 }
230