summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjpathy <jpathy@mail.nanosouffle.net>2013-05-08 23:27:58 +0530
committerjpathy <jpathy@mail.nanosouffle.net>2013-05-08 23:27:58 +0530
commite3883b050e1784f97bd6474c6af73023fe3bbe44 (patch)
treed1a49c51668bbab97194d67ae1f10b31be9efae8
parentcd66b11f67654edfa54cab05422310d5c923784a (diff)
Add RDRAND Support for /dev/random
-rw-r--r--sys/src/9/pc/dat.h1
-rw-r--r--sys/src/9/pc/fns.h1
-rw-r--r--sys/src/9/pc/l.s32
-rw-r--r--sys/src/9/pc/mkfile2
-rw-r--r--sys/src/9/pc/pcrandom.c151
5 files changed, 186 insertions, 1 deletions
diff --git a/sys/src/9/pc/dat.h b/sys/src/9/pc/dat.h
index caf2e0770..c55b35e35 100644
--- a/sys/src/9/pc/dat.h
+++ b/sys/src/9/pc/dat.h
@@ -299,6 +299,7 @@ enum {
Mmx = 1<<23,
Sse = 1<<25, /* thus sfence instr. */
Sse2 = 1<<26, /* thus mfence & lfence instr.s */
+ Rdrnd = 1<<30, /* RDRAND support bit */
};
/*
diff --git a/sys/src/9/pc/fns.h b/sys/src/9/pc/fns.h
index dca0cc614..010025f5a 100644
--- a/sys/src/9/pc/fns.h
+++ b/sys/src/9/pc/fns.h
@@ -181,6 +181,7 @@ void vunmap(void*, int);
void wbinvd(void);
int wrmsr(int, vlong);
int xchgw(ushort*, int);
+void rdrandbuf(void*, ulong);
#define userureg(ur) (((ur)->cs & 3) == 3)
#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
diff --git a/sys/src/9/pc/l.s b/sys/src/9/pc/l.s
index 61394e6fb..418570e88 100644
--- a/sys/src/9/pc/l.s
+++ b/sys/src/9/pc/l.s
@@ -881,6 +881,38 @@ TEXT mwait(SB), $0
_mwaitdone:
RET
+#define RDRANDAX BYTE $0x0f; BYTE $0xc7; BYTE $0xf0
+
+TEXT rdrand32(SB), $-4
+_rloop32:
+ RDRANDAX
+ JCC _rloop32
+ RET
+
+TEXT rdrandbuf(SB), $0
+ MOVL buf+0(FP), DI
+ MOVL cnt+4(FP), CX
+ CLD
+ MOVL CX, DX
+ SHRL $2, CX
+ CMPL CX, $0
+ JE _rndleft
+_rnddwords:
+ CALL rdrand32(SB)
+ STOSL
+ LOOP _rnddwords
+_rndleft:
+ MOVL DX, CX
+ ANDL $3, CX
+ CMPL CX, $0
+ JE _rnddone
+_rndbytes:
+ CALL rdrand32(SB)
+ STOSB
+ LOOP _rndbytes
+_rnddone:
+ RET
+
/*
* Interrupt/exception handling.
* Each entry in the vector table calls either _strayintr or _strayintrx depending
diff --git a/sys/src/9/pc/mkfile b/sys/src/9/pc/mkfile
index 5b68b85a2..5298ed0d4 100644
--- a/sys/src/9/pc/mkfile
+++ b/sys/src/9/pc/mkfile
@@ -41,7 +41,7 @@ PORT=\
taslock.$O\
tod.$O\
xalloc.$O\
- random.$O\
+ pcrandom.$O\
OBJ=\
l.$O\
diff --git a/sys/src/9/pc/pcrandom.c b/sys/src/9/pc/pcrandom.c
new file mode 100644
index 000000000..db77fbbdd
--- /dev/null
+++ b/sys/src/9/pc/pcrandom.c
@@ -0,0 +1,151 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+static int haverdrand;
+
+struct Rb
+{
+ QLock;
+ Rendez producer;
+ Rendez consumer;
+ ulong randomcount;
+ uchar buf[128];
+ uchar *ep;
+ uchar *rp;
+ uchar *wp;
+ uchar next;
+ uchar wakeme;
+ ushort bits;
+ ulong randn;
+} rb;
+
+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);
+}
+
+void
+randominit(void)
+{
+ if(!strcmp(m->cpuidid, "GenuineIntel")
+ && (m->cpuidcx & Rdrnd)){
+ haverdrand = 1;
+ }
+ else{
+ /* Frequency close but not equal to HZ */
+ addclock0link(randomclock, MS2HZ+3);
+ rb.ep = rb.buf + sizeof(rb.buf);
+ rb.rp = rb.wp = rb.buf;
+ kproc("genrandom", genrandom, 0);
+ }
+}
+
+/*
+ * consume random bytes from a circular buffer
+ */
+ulong
+randomread(void *xp, ulong n)
+{
+ uchar *e, *p;
+ ulong x;
+
+ p = xp;
+
+ if(haverdrand){
+ rdrandbuf(p, n);
+ return n;
+ }
+
+ 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;
+}