/* * See http://pastehtml.com/view/crkxyohmp.rtxt * */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "io.h" typedef struct RndNumGen RndNumGen; struct RndNumGen { u32int ctl; u32int stat; u32int data; u32int threshold; u32int int_mask; }; enum { RNGBASE = VIRTIO+0x104000, RngCtl = RNGBASE, RngCtlG2x = 2, // double speed generation RngCtlEna = 1, // enable RngStat = RNGBASE + 4, RngCnt = 0xff000000, // valid word count RngCntShift = 24, // valid word count shift RngWrmCnt = 0x000fffff, // warm up count RngData = RNGBASE + 8, RngFFthresh = RNGBASE + 12, RngFFTmsk = 0xff, // number of words before intr RngIntMask = RNGBASE + 16, RngIntdis = 1, // write 1 to disable intr }; struct Rb { QLock; Rendez producer; Rendez consumer; ulong randomcount; uchar buf[1024]; uchar *ep; uchar *rp; uchar *wp; uchar next; uchar wakeme; ushort bits; ulong randn; } rb; static ulong (*rndread)(void *xp, ulong n); static int rbnotfull(void*) { int i; i = rb.rp - rb.wp; return i != 1 && i != (1 - sizeof(rb.buf)); } static int rbnotempty(void*) { return rb.wp != rb.rp; } static void genrandom(void*) { up->basepri = PriNormal; up->priority = up->basepri; for(;;){ for(;;) if(++rb.randomcount > 100000) break; if(anyhigher()) sched(); if(!rbnotfull(0)) sleep(&rb.producer, rbnotfull, 0); } } /* * produce random bits in a circular buffer */ static void randomclock(void) { if(rb.randomcount == 0 || !rbnotfull(0)) 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; if(rb.wp+1 == rb.ep) rb.wp = rb.buf; else rb.wp = rb.wp+1; if(rb.wakeme) wakeup(&rb.consumer); } static ulong hwrandomread(void *xp, ulong n) { RndNumGen* rng = (RndNumGen*)RNGBASE; uchar *e, *p; p = xp; if (waserror()) { qunlock(&rb); nexterror(); } qlock(&rb); for(e = p + n; p < e; ){ int cnt; u32int stat = rng->stat; cnt = stat >> RngCntShift; while (cnt > 0) { union { u32int i; uchar c[4];} data; data.i = rng->data; coherence(); cnt--; *p++ = data.c[0]; if (p >= e) break; *p++ = data.c[1]; if (p >= e) break; *p++ = data.c[2]; if (p >= e) break; *p++ = data.c[3]; if (p >= e) break; } } qunlock(&rb); poperror(); return n; } /* * consume random bytes from a circular buffer */ static ulong swrandomread(void *xp, ulong n) { uchar *e, *p; ulong x; p = xp; if(waserror()){ qunlock(&rb); nexterror(); } qlock(&rb); for(e = p + n; p < e; ){ if(rb.wp == rb.rp){ rb.wakeme = 1; wakeup(&rb.producer); sleep(&rb.consumer, rbnotempty, 0); rb.wakeme = 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; if(rb.rp+1 == rb.ep) rb.rp = rb.buf; else rb.rp = rb.rp+1; } qunlock(&rb); poperror(); wakeup(&rb.producer); return n; } ulong randomread(void *xp, ulong n) { return rndread(xp,n); } static void hwrandominit(void) { RndNumGen* rng = (RndNumGen*)RNGBASE; u32int ctl; // as per Broadcom the first 0x40000 numbers are less random. Dicard. rng->stat = 0x40000; ctl = rng->ctl; coherence(); rng->ctl = ctl|RngCtlEna; } void randominit(void) { /* firmware support added on Wed Jan 30 13:30:14 2013 GMT */ if (getfirmware() >= 366104) { rndread = hwrandomread; hwrandominit(); } else { rndread = swrandomread; /* Frequency close but not equal to HZ */ addclock0link(randomclock, 13); rb.ep = rb.buf + sizeof(rb.buf); rb.rp = rb.wp = rb.buf; kproc("genrandom", genrandom, 0); } }