From e797dc58bd29c5bc0873fc620fc11d5332f90e7f Mon Sep 17 00:00:00 2001 From: Gordon Bergling Date: Sat, 6 Mar 2021 12:28:35 +0100 Subject: [PATCH] arm64: Add support for bcm2838 RNG The hardware random number generator of the RPi4 differs slightly from the version found on the RPi3. This commit extends the existing bcm2835_rng driver to function on the RPi4. Submitted by: James Mintram Reviewed by: markm, cem, delphij Approved by: csprng(cem, markm) MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D22493 --- sys/arm/broadcom/bcm2835/bcm2835_rng.c | 174 +++++++++++++++++++------ 1 file changed, 133 insertions(+), 41 deletions(-) diff --git a/sys/arm/broadcom/bcm2835/bcm2835_rng.c b/sys/arm/broadcom/bcm2835/bcm2835_rng.c index b73580c5eb53..c403bc3542e0 100644 --- a/sys/arm/broadcom/bcm2835/bcm2835_rng.c +++ b/sys/arm/broadcom/bcm2835/bcm2835_rng.c @@ -69,21 +69,26 @@ static device_probe_t bcm2835_rng_probe; #define RNG_RBG2X 0x00000002 /* RBG 2X SPEED */ #define RNG_RBGEN_BIT 0x00000001 /* Enable RNG bit */ -#define RNG_STATUS 0x04 /* RNG status register */ -#define RND_VAL_SHIFT 24 /* Shift for valid words */ -#define RND_VAL_MASK 0x000000ff /* Number valid words mask */ -#define RND_VAL_WARM_CNT 0x40000 /* RNG Warm Up count */ -#define RND_WARM_CNT 0xfffff /* RNG Warm Up Count mask */ +#define BCM2835_RNG_STATUS 0x04 /* BCM2835 RNG status register */ +#define BCM2838_RNG_STATUS 0x18 /* BCM2838 RNG status register */ -#define RNG_DATA 0x08 /* RNG Data Register */ +#define BCM2838_RNG_COUNT 0x24 /* How many values available */ +#define BCM2838_COUNT_VAL_MASK 0x000000ff + +#define BCM2835_RND_VAL_SHIFT 24 /* Shift for valid words */ +#define BCM2835_RND_VAL_MASK 0x000000ff /* Number valid words mask */ +#define BCM2835_RND_VAL_WARM_CNT 0x40000 /* RNG Warm Up count */ +#define BCM2835_RND_WARM_CNT 0xfffff /* RNG Warm Up Count mask */ + +#define BCM2835_RNG_DATA 0x08 /* RNG Data Register */ +#define BCM2838_RNG_DATA 0x20 #define RNG_FF_THRES 0x0c #define RNG_FF_THRES_MASK 0x0000001f -#define RNG_INT_MASK 0x10 -#define RNG_INT_OFF_BIT 0x00000001 +#define BCM2835_RNG_INT_MASK 0x10 +#define BCM2835_RNG_INT_OFF_BIT 0x00000001 #define RNG_FF_DEFAULT 0x10 /* FIFO threshold default */ - #define RNG_FIFO_WORDS (RNG_FF_DEFAULT / sizeof(uint32_t)) #define RNG_NUM_OSCILLATORS 6 @@ -91,11 +96,55 @@ static device_probe_t bcm2835_rng_probe; #define RNG_CALLOUT_TICKS (hz * 4) +struct bcm_rng_conf { + bus_size_t control_reg; + bus_size_t status_reg; + bus_size_t count_reg; + bus_size_t data_reg; + bus_size_t intr_mask_reg; + uint32_t intr_disable_bit; + uint32_t count_value_shift; + uint32_t count_value_mask; + uint32_t warmup_count; + bool allow_2x_mode; + bool can_diagnose; + /* XXX diag regs */ +}; + +static const struct bcm_rng_conf bcm2835_rng_conf = { + .control_reg = RNG_CTRL, + .status_reg = BCM2835_RNG_STATUS, + .count_reg = BCM2835_RNG_STATUS, /* Same register */ + .data_reg = BCM2835_RNG_DATA, + .intr_mask_reg = BCM2835_RNG_INT_MASK, + .intr_disable_bit = BCM2835_RNG_INT_OFF_BIT, + .count_value_shift = BCM2835_RND_VAL_SHIFT, + .count_value_mask = BCM2835_RND_VAL_MASK, + .warmup_count = BCM2835_RND_VAL_WARM_CNT, + .allow_2x_mode = true, + .can_diagnose = true +}; + +static const struct bcm_rng_conf bcm2838_rng_conf = { + .control_reg = RNG_CTRL, + .status_reg = BCM2838_RNG_STATUS, + .count_reg = BCM2838_RNG_COUNT, + .data_reg = BCM2838_RNG_DATA, + .intr_mask_reg = 0, + .intr_disable_bit = 0, + .count_value_shift = 0, + .count_value_mask = BCM2838_COUNT_VAL_MASK, + .warmup_count = 0, + .allow_2x_mode = false, + .can_diagnose = false +}; + struct bcm2835_rng_softc { device_t sc_dev; struct resource * sc_mem_res; struct resource * sc_irq_res; void * sc_intr_hdl; + struct bcm_rng_conf const* conf; uint32_t sc_buf[RNG_FIFO_WORDS]; struct callout sc_rngto; int sc_stall_count; @@ -104,8 +153,15 @@ struct bcm2835_rng_softc { }; static struct ofw_compat_data compat_data[] = { - {"broadcom,bcm2835-rng", 1}, - {"brcm,bcm2835-rng", 1}, + {"broadcom,bcm2835-rng", (uintptr_t)&bcm2835_rng_conf}, + {"brcm,bcm2835-rng", (uintptr_t)&bcm2835_rng_conf}, + + {"brcm,bcm2711-rng200", (uintptr_t)&bcm2838_rng_conf}, + {"brcm,bcm2838-rng", (uintptr_t)&bcm2838_rng_conf}, + {"brcm,bcm2838-rng200", (uintptr_t)&bcm2838_rng_conf}, + {"brcm,bcm7211-rng", (uintptr_t)&bcm2838_rng_conf}, + {"brcm,bcm7278-rng", (uintptr_t)&bcm2838_rng_conf}, + {"brcm,iproc-rng200", (uintptr_t)&bcm2838_rng_conf}, {NULL, 0} }; @@ -144,8 +200,12 @@ bcm2835_rng_dump_registers(struct bcm2835_rng_softc *sc, struct sbuf *sbp) uint32_t comblk2_osc, comblk1_osc, jclk_byp_div, val; int i; + if (!sc->conf->can_diagnose) + /* Not implemented. */ + return; + /* Display RNG control register contents */ - val = bcm2835_rng_read4(sc, RNG_CTRL); + val = bcm2835_rng_read4(sc, sc->conf->control_reg); sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val); comblk2_osc = (val & RNG_COMBLK2_OSC) >> RNG_COMBLK2_OSC_SHIFT; @@ -181,20 +241,20 @@ bcm2835_rng_dump_registers(struct bcm2835_rng_softc *sc, struct sbuf *sbp) sbuf_cat(sbp, " RNG_RBGEN_BIT: RBG enabled\n"); /* Display RNG status register contents */ - val = bcm2835_rng_read4(sc, RNG_STATUS); + val = bcm2835_rng_read4(sc, sc->conf->status_reg); sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val); sbuf_printf(sbp, " RND_VAL: %02x\n", - (val >> RND_VAL_SHIFT) & RND_VAL_MASK); - sbuf_printf(sbp, " RND_WARM_CNT: %05x\n", val & RND_WARM_CNT); + (val >> sc->conf->count_value_shift) & sc->conf->count_value_mask); + sbuf_printf(sbp, " RND_WARM_CNT: %05x\n", val & sc->conf->warmup_count); /* Display FIFO threshold register contents */ val = bcm2835_rng_read4(sc, RNG_FF_THRES); sbuf_printf(sbp, "RNG_FF_THRES: %05x\n", val & RNG_FF_THRES_MASK); /* Display interrupt mask register contents */ - val = bcm2835_rng_read4(sc, RNG_INT_MASK); + val = bcm2835_rng_read4(sc, sc->conf->intr_mask_reg); sbuf_printf(sbp, "RNG_INT_MASK: interrupt %s\n", - ((val & RNG_INT_OFF_BIT) != 0) ? "disabled" : "enabled"); + ((val & sc->conf->intr_disable_bit) != 0) ? "disabled" : "enabled"); } static void @@ -203,9 +263,9 @@ bcm2835_rng_disable_intr(struct bcm2835_rng_softc *sc) uint32_t mask; /* Set the interrupt off bit in the interrupt mask register */ - mask = bcm2835_rng_read4(sc, RNG_INT_MASK); - mask |= RNG_INT_OFF_BIT; - bcm2835_rng_write4(sc, RNG_INT_MASK, mask); + mask = bcm2835_rng_read4(sc, sc->conf->intr_mask_reg); + mask |= sc->conf->intr_disable_bit; + bcm2835_rng_write4(sc, sc->conf->intr_mask_reg, mask); } static void @@ -214,17 +274,20 @@ bcm2835_rng_start(struct bcm2835_rng_softc *sc) uint32_t ctrl; /* Disable the interrupt */ - bcm2835_rng_disable_intr(sc); + if (sc->conf->intr_mask_reg) + bcm2835_rng_disable_intr(sc); /* Set the warmup count */ - bcm2835_rng_write4(sc, RNG_STATUS, RND_VAL_WARM_CNT); + if (sc->conf->warmup_count > 0) + bcm2835_rng_write4(sc, sc->conf->status_reg, + sc->conf->warmup_count); /* Enable the RNG */ - ctrl = bcm2835_rng_read4(sc, RNG_CTRL); + ctrl = bcm2835_rng_read4(sc, sc->conf->control_reg); ctrl |= RNG_RBGEN_BIT; - if (sc->sc_rbg2x) + if (sc->sc_rbg2x && sc->conf->allow_2x_mode) ctrl |= RNG_RBG2X; - bcm2835_rng_write4(sc, RNG_CTRL, ctrl); + bcm2835_rng_write4(sc, sc->conf->control_reg, ctrl); } static void @@ -233,16 +296,39 @@ bcm2835_rng_stop(struct bcm2835_rng_softc *sc) uint32_t ctrl; /* Disable the RNG */ - ctrl = bcm2835_rng_read4(sc, RNG_CTRL); + ctrl = bcm2835_rng_read4(sc, sc->conf->control_reg); ctrl &= ~RNG_RBGEN_BIT; - bcm2835_rng_write4(sc, RNG_CTRL, ctrl); + bcm2835_rng_write4(sc, sc->conf->control_reg, ctrl); +} + +static void +bcm2835_rng_enqueue_harvest(struct bcm2835_rng_softc *sc, uint32_t nread) +{ + char *sc_buf_chunk; + uint32_t chunk_size; + uint32_t cnt; + + chunk_size = sizeof(((struct harvest_event *)0)->he_entropy); + cnt = nread * sizeof(uint32_t); + sc_buf_chunk = (void*)sc->sc_buf; + + while (cnt > 0) { + uint32_t size; + + size = MIN(cnt, chunk_size); + + random_harvest_queue(sc_buf_chunk, size, RANDOM_PURE_BROADCOM); + + sc_buf_chunk += size; + cnt -= size; + } } static void bcm2835_rng_harvest(void *arg) { uint32_t *dest; - uint32_t status; + uint32_t hwcount; u_int cnt, nread, num_avail, num_words; int seen_underrun, num_stalls; struct bcm2835_rng_softc *sc = arg; @@ -250,11 +336,13 @@ bcm2835_rng_harvest(void *arg) dest = sc->sc_buf; nread = num_words = 0; seen_underrun = num_stalls = 0; + for (cnt = sizeof(sc->sc_buf) / sizeof(uint32_t); cnt > 0; cnt -= num_words) { - /* Read status register to find out how many words available */ - status = bcm2835_rng_read4(sc, RNG_STATUS); - num_avail = (status >> RND_VAL_SHIFT) & RND_VAL_MASK; + /* Read count register to find out how many words available */ + hwcount = bcm2835_rng_read4(sc, sc->conf->count_reg); + num_avail = (hwcount >> sc->conf->count_value_shift) & + sc->conf->count_value_mask; /* If we have none... */ if (num_avail == 0) { @@ -282,15 +370,13 @@ bcm2835_rng_harvest(void *arg) /* Pull MIN(num_avail, cnt) words from the FIFO */ num_words = (num_avail > cnt) ? cnt : num_avail; - bcm2835_rng_read_multi4(sc, RNG_DATA, dest, + bcm2835_rng_read_multi4(sc, sc->conf->data_reg, dest, num_words); dest += num_words; nread += num_words; } - cnt = nread * sizeof(uint32_t); - if (cnt > 0) - random_harvest_queue(sc->sc_buf, cnt, RANDOM_PURE_BROADCOM); + bcm2835_rng_enqueue_harvest(sc, nread); callout_reset(&sc->sc_rngto, RNG_CALLOUT_TICKS, bcm2835_rng_harvest, sc); } @@ -347,7 +433,7 @@ bcm2835_rng_probe(device_t dev) if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); - device_set_desc(dev, "Broadcom BCM2835 RNG"); + device_set_desc(dev, "Broadcom BCM2835/BCM2838 RNG"); return (BUS_PROBE_DEFAULT); } @@ -363,13 +449,18 @@ bcm2835_rng_attach(device_t dev) error = 0; sc = device_get_softc(dev); sc->sc_dev = dev; + + sc->conf = (void const*)ofw_bus_search_compatible(dev, compat_data)->ocd_data; + KASSERT(sc->conf != NULL, ("bcm2835_rng_attach: sc->conf == NULL")); + sc->sc_stall_count = RNG_STALL_COUNT_DEFAULT; /* Initialize callout */ callout_init(&sc->sc_rngto, CALLOUT_MPSAFE); - TUNABLE_INT_FETCH("bcmrng.2xspeed", &sc->sc_rbg2x); TUNABLE_INT_FETCH("bcmrng.stall_count", &sc->sc_stall_count); + if (sc->conf->allow_2x_mode) + TUNABLE_INT_FETCH("bcmrng.2xspeed", &sc->sc_rbg2x); /* Allocate memory resources */ rid = 0; @@ -402,9 +493,10 @@ bcm2835_rng_attach(device_t dev) SYSCTL_ADD_LONG(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "underrun", CTLFLAG_RD, &sc->sc_underrun, "Number of FIFO underruns"); - SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, - "2xspeed", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, - sysctl_bcm2835_rng_2xspeed, "I", "Enable RBG 2X SPEED"); + if (sc->conf->allow_2x_mode) + SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, + "2xspeed", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, + sysctl_bcm2835_rng_2xspeed, "I", "Enable RBG 2X SPEED"); SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "stall_count", CTLFLAG_RW, &sc->sc_stall_count, RNG_STALL_COUNT_DEFAULT, "Number of underruns to assume RNG stall"); @@ -414,7 +506,7 @@ bcm2835_rng_attach(device_t dev) sysctl_bcm2835_rng_dump, "S", "Dump RNG registers"); #endif - /* + /* * Schedule the initial harvesting one second from now, which should give the * hardware RNG plenty of time to generate the first random bytes. */