jehanne/sys/src/kern/port/random.c

159 lines
2.3 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
static struct
{
QLock;
Rendez producer;
Rendez consumer;
uint32_t randomcount;
uint8_t buf[1024];
uint8_t *ep;
uint8_t *rp;
uint8_t *wp;
uint8_t next;
uint8_t wakeme;
uint8_t filled;
uint16_t bits;
uint32_t randn;
int target;
} rb;
static int
rbnotfull(void* _1)
{
int i;
i = rb.wp - rb.rp;
if(i < 0)
i += sizeof(rb.buf);
return i < rb.target;
}
static int
rbnotempty(void* _1)
{
return rb.wp != rb.rp;
}
static void
genrandom(void* _1)
{
up->basepri = PriNormal;
up->priority = up->basepri;
for(;;){
for(;;)
if(++rb.randomcount > 100000)
break;
if(anyhigher())
sched();
if(rb.filled || !rbnotfull(0))
sleep(&rb.producer, rbnotfull, 0);
}
}
/*
* produce random bits in a circular buffer
*/
static void
randomclock(void)
{
uint8_t *p;
if(rb.randomcount == 0)
return;
if(!rbnotfull(0)) {
rb.filled = 1;
return;
}
rb.bits = (rb.bits<<2) ^ rb.randomcount;
rb.randomcount = 0;
rb.next++;
if(rb.next != 8/2)
return;
rb.next = 0;
*rb.wp ^= rb.bits;
p = rb.wp+1;
if(p == rb.ep)
p = rb.buf;
rb.wp = p;
if(rb.wakeme){
rb.wakeme = 0;
wakeup(&rb.consumer);
}
}
void
randominit(void)
{
/* Frequency close but not equal to HZ */
addclock0link(randomclock, 13);
rb.target = 16;
rb.ep = rb.buf + sizeof(rb.buf);
rb.rp = rb.wp = rb.buf;
kproc("genrandom", genrandom, 0);
}
/*
* consume random bytes from a circular buffer
*/
uint32_t
randomread(void *xp, uint32_t n)
{
uint8_t *e, *p, *r;
uint32_t x;
int i;
p = xp;
qlock(&rb);
if(waserror()){
qunlock(&rb);
nexterror();
}
for(e = p + n; p < e; ){
if(rb.wp == rb.rp){
rb.wakeme = 1;
wakeup(&rb.producer);
sleep(&rb.consumer, rbnotempty, 0);
continue;
}
/*
* beating clocks will be predictable if
* they are synchronized. Use a cheap pseudo-
* random number generator to obscure any cycles.
*/
x = rb.randn*1103515245 ^ *rb.rp;
*p++ = rb.randn = x;
r = rb.rp + 1;
if(r == rb.ep)
r = rb.buf;
rb.rp = r;
}
if(rb.filled && rb.wp == rb.rp){
i = 2*rb.target;
if(i > sizeof(rb.buf) - 1)
i = sizeof(rb.buf) - 1;
rb.target = i;
rb.filled = 0;
}
poperror();
qunlock(&rb);
wakeup(&rb.producer);
return n;
}