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>