Uses apipes and no alarms for random numbers
[akaros.git] / kern / src / ns / random.c
1 // INFERNO
2 #include <vfs.h>
3 #include <kfs.h>
4 #include <slab.h>
5 #include <kmalloc.h>
6 #include <kref.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <error.h>
11 #include <cpio.h>
12 #include <pmap.h>
13 #include <smp.h>
14 #include <apipe.h>
15
16 #define RAND_BUF_SZ 1024
17
18 static struct rb {
19         uint8_t buf[RAND_BUF_SZ];
20         struct atomic_pipe ap;
21 } rb;
22
23 /* The old style seemed to have genrandom incrementing a shared mem variable,
24  * randomcount, while randomclock, sort of an interrupt handler, would use this
25  * variable to generate random numbers.  They needed to run concurrently to get
26  * some sort of randonmess.  To do so, randomclock wouldn't run if genrandom
27  * wasn't in the process of incrementing the variable.  So we'd need genrandom
28  * working (in the background, but that doesn't work so well for us), when the
29  * randomclock alarm fired.  Then we could get 2 bits.  We'd do that 4 times
30  * to make our random byte.
31  *
32  * In akaros, genrandom would spin to 100,000 every time, without interruption,
33  * since the randomcount's alarm wouldn't interrupt: they are both routine
34  * kernel messages.  So we might as well just call kthread yield each time.
35  * Even then, it was still really slow.  This was because we only got 2 bits of
36  * randomness every 13ms (randomcount would make 2 bits per run, it would run
37  * once every 13ms, unless I screwed up the math on that.  It was supposed to
38  * run with a "Frequency close but not equal to HZ").
39  *
40  * We'd also use the old random values in the ring buffer to muck with the
41  * randomness. */
42 static void genrandom(void *unused)
43 {
44         uint8_t rand_two_bits, rp_byte, wp_byte;
45         uint8_t rand_byte = 0;
46         unsigned int mod_four = 0;
47         //setpri(PriBackground);
48
49         for(;;) {
50                 /* this seems just as good (or bad) as the old genrandom incrementing a
51                  * shared memory variable concurrently */
52                 rand_two_bits = read_tsc() & 0x3;
53                 rand_byte = (rand_byte << 2) ^ rand_two_bits;
54
55                 /* put in a kthread_yield or something here, if we want to replicate the
56                  * way randomclock would return, waiting til its next tick to get two
57                  * more bits. */
58
59                 /* every four times, we built a full random byte */
60                 if (++mod_four % 4 == 0)
61                         continue;
62
63                 /* the old plan9 generator would xor our rand_byte with both the value
64                  * of the read pointer and the write pointer:
65                  *              *rb.wp ^= rb.bits ^ *rb.rp;
66                  * we'll peak into the apipe to do the same */
67                 rp_byte = rb.buf[rb.ap.ap_rd_off & (RAND_BUF_SZ - 1)];
68                 wp_byte = rb.buf[rb.ap.ap_wr_off & (RAND_BUF_SZ - 1)];
69                 rand_byte ^= rp_byte ^ wp_byte;
70                 apipe_write(&rb.ap, &rand_byte, 1);
71         }
72 }
73
74 void randominit(void)
75 {
76         apipe_init(&rb.ap, rb.buf, sizeof(rb.buf), 1);
77         ktask("genrandom", genrandom, 0);
78 }
79
80 uint32_t randomread(void *buf, uint32_t n)
81 {
82         int amt;
83         uint32_t ret = 0;
84
85         for (int i = 0; i < n; i++) {
86                 /* read the random byte directly into the (user) buffer */
87                 amt = apipe_read(&rb.ap, buf, 1);
88                 if (amt < 0)
89                         error("randomread apipe");
90                 if (amt != 1)
91                         warn("Odd amount read from random apipe");
92                 buf++;
93                 ret += amt;
94         }
95         return ret;
96 }