Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 5 Mar 2002 14:38:11 -0500 (EST)
From:      Adrian Filipi-Martin <adrian@ubergeeks.com>
To:        Terry Lambert <tlambert2@mindspring.com>
Cc:        Mark Murray <mark@grondar.za>, FreeBSD Hackers List <freebsd-hackers@FreeBSD.ORG>, <kaj@ubergeeks.com>
Subject:   Re: Intel 820 RNG
Message-ID:  <20020305135912.C52330-100000@lorax.ubergeeks.com>
In-Reply-To: <3C840862.53658404@mindspring.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Mon, 4 Mar 2002, Terry Lambert wrote:

> Mark Murray wrote:
> > >       But, back to the topic.  We have taken the OpenBSD driver for the
> > > RNG on the i810 chipset (and some other i8x0 chipsets), and ported it to
> > > FreeBSD-4.4.  We made some enhancements to get more of the available random
> > > data bandwidth.
> > >
> > >       We want to clean them up a little and submit them as a PR, but have
> > > not had time to.  If you're interested I can send you the patches and you
> > > can give them a try.
> >
> > Hi.
> >
> > Please send me what you have.
>
> Agreed.  Anything that stops the "harvesting entropy" from
> sitting in the interrupt processing path for the most
> important interrupts on the box can only be a good thing.
>
> -- Terry

	I thought some one out there would be interested...

	Attached are our diffs relative to 4.4-RELEASE.  They are mostly a
direct port of the OpenBSD drivers and their integration into the stock
random device entropy pool.

	We did make some enhancements that serve our needs, but may not be
best for everyone.  We actually need entropy in quantity since we could be
doing a lot of crypto operations back to back and it can easily become our
worst bottleneck.

	To this end, we have an entropy buffer in kernel memory that pulls
large blocks of entropy from the RNG if it's going to read from it at all.
The device puts out several orders of magnitude more entropy than the
original drivers captured, and we needed as much as we could grab.
Ideally we would not mix the entropy into the entropy pool and just use the
high quality entropy from the buffer, but we decided to minimize divergence
from the original sources and not switch to 100% hardware entropy.

	The drawback to our approach is that it can spend a lot of time in
the kernel.  To tune this behavior we added a few sysctl's.  The start/stop
script after the diff's tweaks a few of these settings after boot up.

	I cc'd Kaj Groner, who actually did the work for us.  He's not on
this list, so don't drop his address.  I was more involved at the higher
levels of what we needed to get done when we rebased our appliance from
OpenBSD to FreeBSD last Summer.

cheers,

	Adrian
--
[ adrian@ubergeeks.com ]


--- i386/isa/ic/i82802.h	Wed Oct 17 12:44:23 2001
+++ i386/isa/ic/i82802.h	Mon Oct  8 11:55:22 2001
@@ -0,0 +1,21 @@
+/* This is where the first 82802 firmware hub is found. */
+#define FWH_BASE                0xffb00000
+
+/* This is the offset where the RNG is found. */
+#define RNG_OFFSET              0xc015f
+
+/* Number of RNG ports */
+#define RNG_SPAN                3
+
+/* RNG registers */
+#define RNG_HWSTAT              0x00
+#define RNG_DATASTAT            0x01
+#define RNG_DATA                0x02
+
+/* RNG hardware status register (RNG_HWSTAT) */
+#define RNG_PRESENT             0x40
+#define RNG_ENABLED             0x01
+
+/* RNG data status register (RNG_DATASTAT) */
+#define RNG_READY               0x01
+
--- kern/kern_random.c.orig	Sun Oct 28 16:06:31 2001
+++ kern/kern_random.c	Sun Oct 28 16:06:49 2001
@@ -224,20 +224,41 @@

 	/* Prevent overflow */
 	if (r->entropy_count > POOLBITS)
 		r->entropy_count = POOLBITS;

 	if (r->entropy_count >= 8)
 		selwakeup(&random_state.rsel);
 }

 void
+add_true_randomness(u_int32_t num)
+{
+        add_entropy_word(&random_state, num);
+
+        random_state.entropy_count += sizeof (num) * 8;
+
+	/* Prevent overflow */
+	if (random_state.entropy_count > POOLBITS)
+		random_state.entropy_count = POOLBITS;
+
+	if (random_state.entropy_count >= 8)
+		selwakeup(&random_state.rsel);
+}
+
+u_int
+get_entropy_deficit(void)
+{
+        return (POOLBITS - random_state.entropy_count);
+}
+
+void
 add_keyboard_randomness(u_char scancode)
 {
 	add_timer_randomness(&random_state, &keyboard_timer_state, scancode);
 }

 void
 add_interrupt_randomness(void *vsc)
 {
 	int intr;
 	struct random_softc *sc = vsc;
--- pci/pcisupport.c	15 Aug 2001 04:04:59 -0000	1.154.2.7
+++ pci/pcisupport.c	22 Oct 2001 18:39:54 -0000
@@ -48,7 +48,15 @@
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <sys/kernel.h>
+
+#include <machine/resource.h>
+#include <machine/bus.h>
+#include <machine/clock.h>
+
+#include <sys/rman.h>
 #include <sys/bus.h>
+#include <sys/random.h>
+#include <sys/sysctl.h>

 #include <pci/pcivar.h>
 #include <pci/pcireg.h>
@@ -57,6 +65,8 @@
 #include <vm/vm_object.h>
 #include <vm/pmap.h>

+#include <i386/isa/ic/i82802.h>
+
 /*---------------------------------------------------------
 **
 **	Intel chipsets for 486 / Pentium processor
@@ -669,6 +679,170 @@
     return descr;
 }

+#define RNG_RESERVE     0x10000
+
+struct pcib_softc {
+  /* RNG stuff */
+  struct {
+    int rid;
+    struct resource *res;
+    bus_space_tag_t bt;
+    bus_space_handle_t bh;
+
+    struct callout_handle toh;
+
+    u_int8_t reserve[RNG_RESERVE];
+    int reserve_start, reserve_end;
+  } rng;
+};
+
+static void rng_poll_cb(void *arg);
+
+static void
+rng_attach(device_t dev) {
+  struct pcib_softc *sc;
+  int ok = 1;
+
+  sc = (struct pcib_softc *)device_get_softc(dev);
+
+  switch (pci_get_devid(dev)) {
+  case 0x24188086:
+  case 0x244e8086:
+    /* This is the rng_probe() segment */
+
+    if (ok) {
+      bus_set_resource(dev, SYS_RES_MEMORY, 0, FWH_BASE + RNG_OFFSET, RNG_SPAN);
+      sc->rng.rid = 0;
+      sc->rng.res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->rng.rid,
+          0, ~0, 0, RF_ACTIVE);
+
+      if (sc->rng.res != NULL) {
+        sc->rng.bt = rman_get_bustag(sc->rng.res);
+        sc->rng.bh = rman_get_bushandle(sc->rng.res);
+      } else {
+        ok = 0;
+      }
+    }
+
+    if (ok) {
+      /* Check for RNG presence */
+      u_int8_t hwstat =
+          bus_space_read_1(sc->rng.bt, sc->rng.bh, RNG_HWSTAT);
+      if ((hwstat & RNG_PRESENT) == 0)
+        ok = 0;
+    }
+
+    if (ok) {
+      /* Enable RNG */
+      u_int8_t hwstat =
+          bus_space_read_1(sc->rng.bt, sc->rng.bh, RNG_HWSTAT) | RNG_ENABLED;
+      bus_space_write_1(sc->rng.bt, sc->rng.bh, RNG_HWSTAT, hwstat);
+
+      hwstat = bus_space_read_1(sc->rng.bt, sc->rng.bh, RNG_HWSTAT);
+      if ((hwstat & RNG_ENABLED) == 0)
+        ok = 0;
+    }
+
+    if (ok) {
+      int i = 1000;
+
+      while (--i) {
+        if (bus_space_read_1(sc->rng.bt, sc->rng.bh, RNG_DATASTAT) & RNG_READY)
+          break;
+        DELAY(10);
+      }
+
+      if (bus_space_read_1(sc->rng.bt, sc->rng.bh, RNG_DATASTAT) & RNG_READY) {
+        printf("rng: initial byte was %02x\n",
+            bus_space_read_1(sc->rng.bt, sc->rng.bh, RNG_DATA));
+      } else {
+        ok = 0;
+      }
+    }
+
+    /* FIPS test here */
+
+    /* This is the rng_attach() segment */
+    if (ok) {
+      /* One tick is a very coarse interval to poll this device with.
+       * Intel recommnds polling it every 4.5ms.  When hz = 100, we are
+       * polling about 22 times less often than we could.  Yay.
+       */
+      sc->rng.toh = timeout(rng_poll_cb, sc, 1);
+      sc->rng.reserve_start = sc->rng.reserve_end = 0;
+      printf("rng: registered polling timeout\n");
+    }
+
+    if (!ok) {
+      if (sc->rng.res != NULL)
+        bus_release_resource(dev, SYS_RES_MEMORY, sc->rng.rid, sc->rng.res);
+    }
+
+    break;
+  }
+}
+
+SYSCTL_NODE(_hw, OID_AUTO, rng, CTLFLAG_RW, 0, "RNG stuff");
+static unsigned long rng_tries = 0;
+SYSCTL_ULONG(_hw_rng, OID_AUTO, tries, CTLFLAG_RW, &rng_tries, 0, "RNG polling attempts");
+static unsigned long rng_hits = 0;
+SYSCTL_ULONG(_hw_rng, OID_AUTO, hits, CTLFLAG_RW, &rng_hits, 0, "RNG polling hits");
+static unsigned long rng_full = 0;
+SYSCTL_ULONG(_hw_rng, OID_AUTO, full, CTLFLAG_RW, &rng_full, 0, "RNG extra loops");
+static unsigned int rng_delay = 600;
+SYSCTL_UINT(_hw_rng, OID_AUTO, delay, CTLFLAG_RW, &rng_delay, 0, "RNG busy polling interval");
+static unsigned int rng_loop = 1;
+SYSCTL_UINT(_hw_rng, OID_AUTO, loops, CTLFLAG_RW, &rng_loop, 0, "RNG busy polling iterations per tick");
+static unsigned int rng_reserve = 0;
+SYSCTL_UINT(_hw_rng, OID_AUTO, reserve, CTLFLAG_RD, &rng_reserve, 0, "RNG reserve bytes");
+
+static void
+rng_poll_cb(void *arg) {
+  struct pcib_softc *sc = (struct pcib_softc *)arg;
+  int loop = rng_loop;
+  int i;
+
+  for (i = get_entropy_deficit() / (sizeof (u_int32_t) * 8); i; i--) {
+    u_int32_t *word;
+    int avail = sc->rng.reserve_end - sc->rng.reserve_start;
+    if (avail < 0)
+      avail += RNG_RESERVE;
+
+    if (avail < sizeof (u_int32_t))
+      break;
+
+    word = (u_int32_t *)(sc->rng.reserve + sc->rng.reserve_start);
+    add_true_randomness(*word);
+
+    rng_reserve -= sizeof (u_int32_t);
+
+    sc->rng.reserve_start =
+        (sc->rng.reserve_start + sizeof (u_int32_t)) % RNG_RESERVE;
+  }
+
+  while (loop--) {
+    if ((sc->rng.reserve_end + 1) % RNG_RESERVE == sc->rng.reserve_start) {
+      /* enough! */
+      rng_full += loop + 1;
+      break;
+    }
+
+    rng_tries++;
+    if (bus_space_read_1(sc->rng.bt, sc->rng.bh, RNG_DATASTAT) & RNG_READY) {
+      rng_hits++;
+      sc->rng.reserve[sc->rng.reserve_end] =
+          bus_space_read_1(sc->rng.bt, sc->rng.bh, RNG_DATA);
+      sc->rng.reserve_end = (sc->rng.reserve_end + 1) % RNG_RESERVE;
+      rng_reserve++;
+    }
+
+    if (loop)
+      DELAY(rng_delay);
+  }
+
+  sc->rng.toh = timeout(rng_poll_cb, sc, 1);
+}
+
 static const char*
 pcib_match(device_t dev)
 {
@@ -763,6 +937,8 @@

 	chipset_attach(dev, device_get_unit(dev));

+        rng_attach(dev);
+
 	secondary = pci_get_secondarybus(dev);
 	if (secondary) {
 		device_add_child(dev, "pci", secondary);
@@ -847,7 +1023,7 @@
 static driver_t pcib_driver = {
 	"pcib",
 	pcib_methods,
-	1,
+	sizeof (struct pcib_softc),
 };

 static devclass_t pcib_devclass;
--- sys/random.h	2000/05/10 02:04:52	1.19.2.1
+++ sys/random.h	2001/10/09 15:01:19
@@ -69,6 +69,8 @@
 /* Exported functions */

 void rand_initialize(void);
+void add_true_randomness(u_int32_t num);
+u_int get_entropy_deficit(void);
 void add_keyboard_randomness(u_char scancode);
 inthand2_t add_interrupt_randomness;
 #ifdef notused


#!/bin/sh
# hwrng.sh: A script to tune the RNG driver to poll more aggressively.

hwrngop() {
	if ! sysctl hw.rng >/dev/null 2>&1; then
		echo "${0##*/}: rng not found" >&2
		exit 1
	fi

	if [ $# -gt 0 ]; then
		sysctl "$@"
	fi
}

case "$1" in
start)
	echo "Enabling aggressive RNG polling."
	hwrngop hw.rng.loops=10
;;

stop)
	echo "Disabling aggressive RNG polling."
	hwrngop hw.rng.loops=1
;;

status)
	hwrngop # check for presence

	echo -n "Aggressive RNG polling is "
	if [ "$(sysctl -n hw.rng.loops)" -gt 1 ]; then
		echo "enabled"
	else
		echo "disabled"
	fi
;;
*)
	echo "usage: ${0##*/} [ start | stop | status ]" >&2
	exit 1
;;
esac
exit 0


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020305135912.C52330-100000>