9ns: Fix devtab function pointer signatures
[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 <net/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                                   unsigned int nname)
132 {
133         return devwalk(c, nc, name, nname, randomdir,
134                        ARRAY_SIZE(randomdir), devgen);
135 }
136
137 static size_t randomstat(struct chan *c, uint8_t *dp, size_t n)
138 {
139         struct dir dir;
140         struct dirtab *tab;
141         int perm;
142
143         switch (c->qid.path) {
144         case Qrandom:
145                 tab = &randomdir[Qrandom];
146                 perm = tab->perm | DMREADABLE;
147                 devdir(c, tab->qid, tab->name, 0, eve.name, perm, &dir);
148                 return dev_make_stat(c, &dir, dp, n);
149         case Qurandom:
150                 tab = &randomdir[Qurandom];
151                 perm = tab->perm | DMREADABLE;
152                 devdir(c, tab->qid, tab->name, 0, eve.name, perm, &dir);
153                 return dev_make_stat(c, &dir, dp, n);
154         default:
155                 return devstat(c, dp, n, randomdir, ARRAY_SIZE(randomdir), devgen);
156         }
157 }
158
159 /*
160  *  if the stream doesn't exist, create it
161  */
162 static struct chan *randomopen(struct chan *c, int omode)
163 {
164         return devopen(c, omode, randomdir, ARRAY_SIZE(randomdir), devgen);
165 }
166
167 static void randomclose(struct chan *c)
168 {
169 }
170
171 static size_t randomread(struct chan *c, void *va, size_t n, off64_t ignored)
172 {
173         switch (c->qid.path) {
174                 case Qdir:
175                         return devdirread(c, va, n, randomdir,
176                                           ARRAY_SIZE(randomdir), devgen);
177                 case Qrandom:
178                         return random_read(va, n);
179                 case Qurandom:
180                         return urandom_read(va, n);
181                 default:
182                         panic("randomread: qid %d is impossible", c->qid.path);
183         }
184         return -1;      /* not reached */
185 }
186
187 /*
188  *  A write to a closed random causes an ERANDOM error to be thrown.
189  */
190 static size_t randomwrite(struct chan *c, void *va, size_t n, off64_t ignored)
191 {
192         error(EPERM, "No use for writing random just yet");
193         return -1;
194 }
195
196 static long randombwrite(struct chan *c, struct block *bp, uint32_t junk)
197 {
198         error(EPERM, "No use for writing random just yet");
199         return -1;
200 }
201
202 static int random_tapfd(struct chan *c, struct fd_tap *tap, int cmd)
203 {
204         /* We don't actually support HANGUP, but epoll implies it. */
205         #define RANDOM_TAPS (FDTAP_FILT_READABLE | FDTAP_FILT_HANGUP)
206
207         if (tap->filter & ~RANDOM_TAPS) {
208                 set_error(ENOSYS, "Unsupported #%s tap, must be %p", devname(),
209                           RANDOM_TAPS);
210                 return -1;
211         }
212         switch (c->qid.path) {
213         case Qrandom:
214         case Qurandom:
215                 /* Faking any legit command on (u)random, which never blocks. */
216                 return 0;
217         default:
218                 set_error(ENOSYS, "Can't tap #%s file type %d", devname(),
219                           c->qid.path);
220                 return -1;
221         }
222 }
223
224 struct dev randomdevtab __devtab = {
225         .name = "random",
226
227         .reset = devreset,
228         .init = randominit,
229         .shutdown = devshutdown,
230         .attach = randomattach,
231         .walk = randomwalk,
232         .stat = randomstat,
233         .open = randomopen,
234         .create = devcreate,
235         .close = randomclose,
236         .read = randomread,
237         .write = randomwrite,
238         .remove = devremove,
239         .wstat = devwstat,
240         .power = devpower,
241         .chaninfo = devchaninfo,
242         .tapfd = random_tapfd,
243 };