Path: blob/main/sys/arm/broadcom/bcm2835/bcm2835_rng.c
39566 views
/*1* Copyright (c) 2015, 2016, Stephen J. Kiernan2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/252627#include <sys/param.h>28#include <sys/kernel.h>29#include <sys/ktr.h>30#include <sys/lock.h>31#include <sys/malloc.h>32#include <sys/module.h>33#include <sys/random.h>34#include <sys/sbuf.h>35#include <sys/sysctl.h>36#include <sys/selinfo.h>37#include <sys/systm.h>38#include <sys/bus.h>39#include <sys/rman.h>4041#include <machine/bus.h>42#include <machine/resource.h>4344#include <dev/ofw/openfirm.h>45#include <dev/ofw/ofw_bus.h>46#include <dev/ofw/ofw_bus_subr.h>4748#include <dev/random/randomdev.h>49#include <dev/random/random_harvestq.h>5051static device_attach_t bcm2835_rng_attach;52static device_detach_t bcm2835_rng_detach;53static device_probe_t bcm2835_rng_probe;5455#define RNG_CTRL 0x00 /* RNG Control Register */56#define RNG_COMBLK1_OSC 0x003f0000 /* Combiner Blk 1 Oscillator */57#define RNG_COMBLK1_OSC_SHIFT 1658#define RNG_COMBLK2_OSC 0x0fc00000 /* Combiner Blk 2 Oscillator */59#define RNG_COMBLK2_OSC_SHIFT 2260#define RNG_JCLK_BYP_DIV_CNT 0x0000ff00 /* Jitter clk bypass divider61count */62#define RNG_JCLK_BYP_DIV_CNT_SHIFT 863#define RNG_JCLK_BYP_SRC 0x00000020 /* Jitter clk bypass source */64#define RNG_JCLK_BYP_SEL 0x00000010 /* Jitter clk bypass select */65#define RNG_RBG2X 0x00000002 /* RBG 2X SPEED */66#define RNG_RBGEN_BIT 0x00000001 /* Enable RNG bit */6768#define BCM2835_RNG_STATUS 0x04 /* BCM2835 RNG status register */69#define BCM2838_RNG_STATUS 0x18 /* BCM2838 RNG status register */7071#define BCM2838_RNG_COUNT 0x24 /* How many values available */72#define BCM2838_COUNT_VAL_MASK 0x000000ff7374#define BCM2835_RND_VAL_SHIFT 24 /* Shift for valid words */75#define BCM2835_RND_VAL_MASK 0x000000ff /* Number valid words mask */76#define BCM2835_RND_VAL_WARM_CNT 0x40000 /* RNG Warm Up count */77#define BCM2835_RND_WARM_CNT 0xfffff /* RNG Warm Up Count mask */7879#define BCM2835_RNG_DATA 0x08 /* RNG Data Register */80#define BCM2838_RNG_DATA 0x2081#define RNG_FF_THRES 0x0c82#define RNG_FF_THRES_MASK 0x0000001f8384#define BCM2835_RNG_INT_MASK 0x1085#define BCM2835_RNG_INT_OFF_BIT 0x000000018687#define RNG_FF_DEFAULT 0x10 /* FIFO threshold default */88#define RNG_FIFO_WORDS (RNG_FF_DEFAULT / sizeof(uint32_t))8990#define RNG_NUM_OSCILLATORS 691#define RNG_STALL_COUNT_DEFAULT 109293#define RNG_CALLOUT_TICKS (hz * 4)9495struct bcm_rng_conf {96bus_size_t control_reg;97bus_size_t status_reg;98bus_size_t count_reg;99bus_size_t data_reg;100bus_size_t intr_mask_reg;101uint32_t intr_disable_bit;102uint32_t count_value_shift;103uint32_t count_value_mask;104uint32_t warmup_count;105bool allow_2x_mode;106bool can_diagnose;107/* XXX diag regs */108};109110static const struct bcm_rng_conf bcm2835_rng_conf = {111.control_reg = RNG_CTRL,112.status_reg = BCM2835_RNG_STATUS,113.count_reg = BCM2835_RNG_STATUS, /* Same register */114.data_reg = BCM2835_RNG_DATA,115.intr_mask_reg = BCM2835_RNG_INT_MASK,116.intr_disable_bit = BCM2835_RNG_INT_OFF_BIT,117.count_value_shift = BCM2835_RND_VAL_SHIFT,118.count_value_mask = BCM2835_RND_VAL_MASK,119.warmup_count = BCM2835_RND_VAL_WARM_CNT,120.allow_2x_mode = true,121.can_diagnose = true122};123124static const struct bcm_rng_conf bcm2838_rng_conf = {125.control_reg = RNG_CTRL,126.status_reg = BCM2838_RNG_STATUS,127.count_reg = BCM2838_RNG_COUNT,128.data_reg = BCM2838_RNG_DATA,129.intr_mask_reg = 0,130.intr_disable_bit = 0,131.count_value_shift = 0,132.count_value_mask = BCM2838_COUNT_VAL_MASK,133.warmup_count = 0,134.allow_2x_mode = false,135.can_diagnose = false136};137138struct bcm2835_rng_softc {139device_t sc_dev;140struct resource * sc_mem_res;141struct resource * sc_irq_res;142void * sc_intr_hdl;143struct bcm_rng_conf const* conf;144uint32_t sc_buf[RNG_FIFO_WORDS];145struct callout sc_rngto;146int sc_stall_count;147int sc_rbg2x;148long sc_underrun;149};150151static struct ofw_compat_data compat_data[] = {152{"broadcom,bcm2835-rng", (uintptr_t)&bcm2835_rng_conf},153{"brcm,bcm2835-rng", (uintptr_t)&bcm2835_rng_conf},154155{"brcm,bcm2711-rng200", (uintptr_t)&bcm2838_rng_conf},156{"brcm,bcm2838-rng", (uintptr_t)&bcm2838_rng_conf},157{"brcm,bcm2838-rng200", (uintptr_t)&bcm2838_rng_conf},158{"brcm,bcm7211-rng", (uintptr_t)&bcm2838_rng_conf},159{"brcm,bcm7278-rng", (uintptr_t)&bcm2838_rng_conf},160{"brcm,iproc-rng200", (uintptr_t)&bcm2838_rng_conf},161{NULL, 0}162};163164static __inline void165bcm2835_rng_stat_inc_underrun(struct bcm2835_rng_softc *sc)166{167168atomic_add_long(&sc->sc_underrun, 1);169}170171static __inline uint32_t172bcm2835_rng_read4(struct bcm2835_rng_softc *sc, bus_size_t off)173{174175return bus_read_4(sc->sc_mem_res, off);176}177178static __inline void179bcm2835_rng_read_multi4(struct bcm2835_rng_softc *sc, bus_size_t off,180uint32_t *datap, bus_size_t count)181{182183bus_read_multi_4(sc->sc_mem_res, off, datap, count);184}185186static __inline void187bcm2835_rng_write4(struct bcm2835_rng_softc *sc, bus_size_t off, uint32_t val)188{189190bus_write_4(sc->sc_mem_res, off, val);191}192193static void194bcm2835_rng_dump_registers(struct bcm2835_rng_softc *sc, struct sbuf *sbp)195{196uint32_t comblk2_osc, comblk1_osc, jclk_byp_div, val;197int i;198199if (!sc->conf->can_diagnose)200/* Not implemented. */201return;202203/* Display RNG control register contents */204val = bcm2835_rng_read4(sc, sc->conf->control_reg);205sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val);206207comblk2_osc = (val & RNG_COMBLK2_OSC) >> RNG_COMBLK2_OSC_SHIFT;208sbuf_printf(sbp, " RNG_COMBLK2_OSC (%02x)\n", comblk2_osc);209for (i = 0; i < RNG_NUM_OSCILLATORS; i++)210if ((comblk2_osc & (1 << i)) == 0)211sbuf_printf(sbp, " Oscillator %d enabled\n", i + 1);212213comblk1_osc = (val & RNG_COMBLK1_OSC) >> RNG_COMBLK1_OSC_SHIFT;214sbuf_printf(sbp, " RNG_COMBLK1_OSC (%02x)\n", comblk1_osc);215for (i = 0; i < RNG_NUM_OSCILLATORS; i++)216if ((comblk1_osc & (1 << i)) == 0)217sbuf_printf(sbp, " Oscillator %d enabled\n", i + 1);218219jclk_byp_div = (val & RNG_JCLK_BYP_DIV_CNT) >>220RNG_JCLK_BYP_DIV_CNT_SHIFT;221sbuf_printf(sbp,222" RNG_JCLK_BYP_DIV_CNT (%02x)\n APB clock frequency / %d\n",223jclk_byp_div, 2 * (jclk_byp_div + 1));224225sbuf_printf(sbp, " RNG_JCLK_BYP_SRC:\n %s\n",226(val & RNG_JCLK_BYP_SRC) ? "Use divided down APB clock" :227"Use RNG clock (APB clock)");228229sbuf_printf(sbp, " RNG_JCLK_BYP_SEL:\n %s\n",230(val & RNG_JCLK_BYP_SEL) ? "Bypass internal jitter clock" :231"Use internal jitter clock");232233if ((val & RNG_RBG2X) != 0)234sbuf_cat(sbp, " RNG_RBG2X: RNG 2X SPEED enabled\n");235236if ((val & RNG_RBGEN_BIT) != 0)237sbuf_cat(sbp, " RNG_RBGEN_BIT: RBG enabled\n");238239/* Display RNG status register contents */240val = bcm2835_rng_read4(sc, sc->conf->status_reg);241sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val);242sbuf_printf(sbp, " RND_VAL: %02x\n",243(val >> sc->conf->count_value_shift) & sc->conf->count_value_mask);244sbuf_printf(sbp, " RND_WARM_CNT: %05x\n", val & sc->conf->warmup_count);245246/* Display FIFO threshold register contents */247val = bcm2835_rng_read4(sc, RNG_FF_THRES);248sbuf_printf(sbp, "RNG_FF_THRES: %05x\n", val & RNG_FF_THRES_MASK);249250/* Display interrupt mask register contents */251val = bcm2835_rng_read4(sc, sc->conf->intr_mask_reg);252sbuf_printf(sbp, "RNG_INT_MASK: interrupt %s\n",253((val & sc->conf->intr_disable_bit) != 0) ? "disabled" : "enabled");254}255256static void257bcm2835_rng_disable_intr(struct bcm2835_rng_softc *sc)258{259uint32_t mask;260261/* Set the interrupt off bit in the interrupt mask register */262mask = bcm2835_rng_read4(sc, sc->conf->intr_mask_reg);263mask |= sc->conf->intr_disable_bit;264bcm2835_rng_write4(sc, sc->conf->intr_mask_reg, mask);265}266267static void268bcm2835_rng_start(struct bcm2835_rng_softc *sc)269{270uint32_t ctrl;271272/* Disable the interrupt */273if (sc->conf->intr_mask_reg)274bcm2835_rng_disable_intr(sc);275276/* Set the warmup count */277if (sc->conf->warmup_count > 0)278bcm2835_rng_write4(sc, sc->conf->status_reg,279sc->conf->warmup_count);280281/* Enable the RNG */282ctrl = bcm2835_rng_read4(sc, sc->conf->control_reg);283ctrl |= RNG_RBGEN_BIT;284if (sc->sc_rbg2x && sc->conf->allow_2x_mode)285ctrl |= RNG_RBG2X;286bcm2835_rng_write4(sc, sc->conf->control_reg, ctrl);287}288289static void290bcm2835_rng_stop(struct bcm2835_rng_softc *sc)291{292uint32_t ctrl;293294/* Disable the RNG */295ctrl = bcm2835_rng_read4(sc, sc->conf->control_reg);296ctrl &= ~RNG_RBGEN_BIT;297bcm2835_rng_write4(sc, sc->conf->control_reg, ctrl);298}299300static void301bcm2835_rng_enqueue_harvest(struct bcm2835_rng_softc *sc, uint32_t nread)302{303char *sc_buf_chunk;304uint32_t chunk_size;305uint32_t cnt;306307chunk_size = sizeof(((struct harvest_event *)0)->he_entropy);308cnt = nread * sizeof(uint32_t);309sc_buf_chunk = (void*)sc->sc_buf;310311while (cnt > 0) {312uint32_t size;313314size = MIN(cnt, chunk_size);315316random_harvest_queue(sc_buf_chunk, size, RANDOM_PURE_BROADCOM);317318sc_buf_chunk += size;319cnt -= size;320}321}322323static void324bcm2835_rng_harvest(void *arg)325{326uint32_t *dest;327uint32_t hwcount;328u_int cnt, nread, num_avail, num_words;329int seen_underrun, num_stalls;330struct bcm2835_rng_softc *sc = arg;331332dest = sc->sc_buf;333nread = num_words = 0;334seen_underrun = num_stalls = 0;335336for (cnt = sizeof(sc->sc_buf) / sizeof(uint32_t); cnt > 0;337cnt -= num_words) {338/* Read count register to find out how many words available */339hwcount = bcm2835_rng_read4(sc, sc->conf->count_reg);340num_avail = (hwcount >> sc->conf->count_value_shift) &341sc->conf->count_value_mask;342343/* If we have none... */344if (num_avail == 0) {345bcm2835_rng_stat_inc_underrun(sc);346if (++seen_underrun >= sc->sc_stall_count) {347if (num_stalls++ > 0) {348device_printf(sc->sc_dev,349"RNG stalled, disabling device\n");350bcm2835_rng_stop(sc);351break;352} else {353device_printf(sc->sc_dev,354"Too many underruns, resetting\n");355bcm2835_rng_stop(sc);356bcm2835_rng_start(sc);357seen_underrun = 0;358}359}360/* Try again */361continue;362}363364CTR2(KTR_DEV, "%s: %d words available in RNG FIFO",365device_get_nameunit(sc->sc_dev), num_avail);366367/* Pull MIN(num_avail, cnt) words from the FIFO */368num_words = (num_avail > cnt) ? cnt : num_avail;369bcm2835_rng_read_multi4(sc, sc->conf->data_reg, dest,370num_words);371dest += num_words;372nread += num_words;373}374375bcm2835_rng_enqueue_harvest(sc, nread);376377callout_reset(&sc->sc_rngto, RNG_CALLOUT_TICKS, bcm2835_rng_harvest, sc);378}379380static int381sysctl_bcm2835_rng_2xspeed(SYSCTL_HANDLER_ARGS)382{383struct bcm2835_rng_softc *sc = arg1;384int error, rbg2x;385386rbg2x = sc->sc_rbg2x;387error = sysctl_handle_int(oidp, &rbg2x, 0, req);388if (error)389return (error);390if (req->newptr == NULL)391return (error);392if (rbg2x == sc->sc_rbg2x)393return (0);394395/* Reset the RNG */396bcm2835_rng_stop(sc);397sc->sc_rbg2x = rbg2x;398bcm2835_rng_start(sc);399400return (0);401}402403#ifdef BCM2835_RNG_DEBUG_REGISTERS404static int405sysctl_bcm2835_rng_dump(SYSCTL_HANDLER_ARGS)406{407struct sbuf sb;408struct bcm2835_rng_softc *sc = arg1;409int error;410411error = sysctl_wire_old_buffer(req, 0);412if (error != 0)413return (error);414sbuf_new_for_sysctl(&sb, NULL, 128, req);415bcm2835_rng_dump_registers(sc, &sb);416error = sbuf_finish(&sb);417sbuf_delete(&sb);418return (error);419}420#endif421422static int423bcm2835_rng_probe(device_t dev)424{425426if (!ofw_bus_status_okay(dev))427return (ENXIO);428429if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)430return (ENXIO);431432device_set_desc(dev, "Broadcom BCM2835/BCM2838 RNG");433434return (BUS_PROBE_DEFAULT);435}436437static int438bcm2835_rng_attach(device_t dev)439{440struct bcm2835_rng_softc *sc;441struct sysctl_ctx_list *sysctl_ctx;442struct sysctl_oid *sysctl_tree;443int error, rid;444445error = 0;446sc = device_get_softc(dev);447sc->sc_dev = dev;448449sc->conf = (void const*)ofw_bus_search_compatible(dev, compat_data)->ocd_data;450KASSERT(sc->conf != NULL, ("bcm2835_rng_attach: sc->conf == NULL"));451452sc->sc_stall_count = RNG_STALL_COUNT_DEFAULT;453454/* Initialize callout */455callout_init(&sc->sc_rngto, CALLOUT_MPSAFE);456457TUNABLE_INT_FETCH("bcmrng.stall_count", &sc->sc_stall_count);458if (sc->conf->allow_2x_mode)459TUNABLE_INT_FETCH("bcmrng.2xspeed", &sc->sc_rbg2x);460461/* Allocate memory resources */462rid = 0;463sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,464RF_ACTIVE);465if (sc->sc_mem_res == NULL) {466bcm2835_rng_detach(dev);467return (ENXIO);468}469470/* Start the RNG */471bcm2835_rng_start(sc);472473/* Dump the registers if booting verbose */474if (bootverbose) {475struct sbuf sb;476477(void) sbuf_new(&sb, NULL, 256,478SBUF_AUTOEXTEND | SBUF_INCLUDENUL);479bcm2835_rng_dump_registers(sc, &sb);480sbuf_trim(&sb);481error = sbuf_finish(&sb);482if (error == 0)483device_printf(dev, "%s", sbuf_data(&sb));484sbuf_delete(&sb);485}486487sysctl_ctx = device_get_sysctl_ctx(dev);488sysctl_tree = device_get_sysctl_tree(dev);489SYSCTL_ADD_LONG(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,490"underrun", CTLFLAG_RD, &sc->sc_underrun,491"Number of FIFO underruns");492if (sc->conf->allow_2x_mode)493SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,494"2xspeed", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,495sysctl_bcm2835_rng_2xspeed, "I", "Enable RBG 2X SPEED");496SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,497"stall_count", CTLFLAG_RW, &sc->sc_stall_count,498RNG_STALL_COUNT_DEFAULT, "Number of underruns to assume RNG stall");499#ifdef BCM2835_RNG_DEBUG_REGISTERS500SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,501"dumpregs", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,502sysctl_bcm2835_rng_dump, "S", "Dump RNG registers");503#endif504505/*506* Schedule the initial harvesting one second from now, which should give the507* hardware RNG plenty of time to generate the first random bytes.508*/509callout_reset(&sc->sc_rngto, hz, bcm2835_rng_harvest, sc);510511return (0);512}513514static int515bcm2835_rng_detach(device_t dev)516{517struct bcm2835_rng_softc *sc;518519sc = device_get_softc(dev);520521/* Stop the RNG */522bcm2835_rng_stop(sc);523524/* Drain the callout it */525callout_drain(&sc->sc_rngto);526527/* Release memory resource */528if (sc->sc_mem_res != NULL)529bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);530531return (0);532}533534static device_method_t bcm2835_rng_methods[] = {535/* Device interface */536DEVMETHOD(device_probe, bcm2835_rng_probe),537DEVMETHOD(device_attach, bcm2835_rng_attach),538DEVMETHOD(device_detach, bcm2835_rng_detach),539540DEVMETHOD_END541};542543static driver_t bcm2835_rng_driver = {544"bcmrng",545bcm2835_rng_methods,546sizeof(struct bcm2835_rng_softc)547};548549DRIVER_MODULE(bcm2835_rng, simplebus, bcm2835_rng_driver, 0, 0);550DRIVER_MODULE(bcm2835_rng, ofwbus, bcm2835_rng_driver, 0, 0);551MODULE_VERSION(bcm2835_rng, 1);552MODULE_DEPEND(bcm2835_rng, randomdev, 1, 1, 1);553554555