2bbee4bf291cdae83e00ee455052a7a01491ac58
[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 <ip.h>
15
16 static struct
17 {
18         qlock_t qlock;
19         struct rendez   producer;
20         struct rendez consumer;
21         uint32_t        randomcount;
22         uint8_t buf[1024];
23         uint8_t *ep;
24         uint8_t *rp;
25         uint8_t *wp;
26         uint8_t next;
27         uint8_t bits;
28         uint8_t wakeme;
29         uint8_t filled;
30         int     target;
31         int     kprocstarted;
32         uint32_t        randn;
33 } rb;
34
35 static int
36 rbnotfull(void*unused)
37 {
38         int i;
39
40         i = rb.wp - rb.rp;
41         if(i < 0)
42                 i += sizeof(rb.buf);
43         return i < rb.target;
44 }
45
46 static int
47 rbnotempty(void*unused)
48 {
49         return rb.wp != rb.rp;
50 }
51
52 static void
53 genrandom(void*unused)
54 {
55 #warning "priority?"
56         //setpri(PriBackground);
57
58         for(;;) {
59                 for(;;)
60                         if(++rb.randomcount > 100000)
61                                 break;
62                 //if(anyhigher())
63                 //      sched();
64                 if(rb.filled || !rbnotfull(0))
65                         rendez_sleep(&rb.producer, rbnotfull, 0);
66         }
67 }
68
69 /*
70  *  produce random bits in a circular buffer
71  */
72 static void
73 randomclock(void)
74 {
75         uint8_t *p;
76
77         if(rb.randomcount == 0)
78                 return;
79
80         if(!rbnotfull(0)) {
81                 rb.filled = 1;
82                 return;
83         }
84
85         rb.bits = (rb.bits<<2) ^ (rb.randomcount&3);
86         rb.randomcount = 0;
87
88         rb.next += 2;
89         if(rb.next != 8)
90                 return;
91
92         rb.next = 0;
93         *rb.wp ^= rb.bits ^ *rb.rp;
94         p = rb.wp+1;
95         if(p == rb.ep)
96                 p = rb.buf;
97         rb.wp = p;
98
99         if(rb.wakeme)
100                 rendez_wakeup(&rb.consumer);
101 }
102
103 void
104 randominit(void)
105 {
106         /* Frequency close but not equal to HZ */
107         //addclock0link(randomclock, 13);
108         rb.target = 16;
109         rb.ep = rb.buf + sizeof(rb.buf);
110         rb.rp = rb.wp = rb.buf;
111 }
112
113 /*
114  *  consume random bytes from a circular buffer
115  */
116 uint32_t
117 randomread(void *xp, uint32_t n)
118 {
119         ERRSTACK(1);
120         int i, sofar;
121         uint8_t *e, *p;
122
123         p = xp;
124
125         qlock(&(&rb)->qlock);
126         if(waserror()){
127                 qunlock(&(&rb)->qlock);
128                 nexterror();
129         }
130         if(!rb.kprocstarted){
131                 rb.kprocstarted = 1;
132                 ktask("genrand", genrandom, NULL);
133         }
134
135         for(sofar = 0; sofar < n; sofar += i){
136                 i = rb.wp - rb.rp;
137                 if(i == 0){
138                         rb.wakeme = 1;
139                         rendez_wakeup(&rb.producer);
140                         rendez_sleep(&rb.consumer, rbnotempty, 0);
141                         rb.wakeme = 0;
142                         continue;
143                 }
144                 if(i < 0)
145                         i = rb.ep - rb.rp;
146                 if((i+sofar) > n)
147                         i = n - sofar;
148                 memmove(p + sofar, rb.rp, i);
149                 e = rb.rp + i;
150                 if(e == rb.ep)
151                         e = rb.buf;
152                 rb.rp = e;
153         }
154         if(rb.filled && rb.wp == rb.rp){
155                 i = 2*rb.target;
156                 if(i > sizeof(rb.buf) - 1)
157                         i = sizeof(rb.buf) - 1;
158                 rb.target = i;
159                 rb.filled = 0;
160         }
161         poperror();
162         qunlock(&(&rb)->qlock);
163
164         rendez_wakeup(&rb.producer);
165
166         return n;
167 }