alarm/cons/eventfd/random: improve tap errstr message
[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 <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
23 static 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  */
29 void 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  */
48 uint32_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  **/
70 uint32_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
95 struct dev randomdevtab;
96
97 static char *devname(void)
98 {
99         return randomdevtab.name;
100 }
101
102 enum {
103         Qdir,
104         Qrandom,
105         Qurandom
106 };
107
108 static struct dirtab randomdir[] = {
109         {".", {Qdir, 0, QTDIR}, 0, DMDIR | 0500},
110         {"random", {Qrandom}, 0, 0444},
111         {"urandom", {Qurandom}, 0, 0444},
112 };
113
114 static void randominit(void)
115 {
116         qlock_init(&rl);
117 }
118
119 /*
120  *  create a random, no streams are created until an open
121  */
122 static struct chan *randomattach(char *spec)
123 {
124         return devattach(devname(), spec);
125 }
126
127 static 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
134 static 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  */
160 static struct chan *randomopen(struct chan *c, int omode)
161 {
162         return devopen(c, omode, randomdir, ARRAY_SIZE(randomdir), devgen);
163 }
164
165 static void randomclose(struct chan *c)
166 {
167 }
168
169 static 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  */
188 static 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
194 static 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
200 static 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
222 struct 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 };