183c37607SAlex Hornung /* 283c37607SAlex Hornung * Copyright (c) 2012 Alex Hornung <alex@alexhornung.com>. 383c37607SAlex Hornung * All rights reserved. 483c37607SAlex Hornung * 583c37607SAlex Hornung * Redistribution and use in source and binary forms, with or without 683c37607SAlex Hornung * modification, are permitted provided that the following conditions 783c37607SAlex Hornung * are met: 883c37607SAlex Hornung * 983c37607SAlex Hornung * 1. Redistributions of source code must retain the above copyright 1083c37607SAlex Hornung * notice, this list of conditions and the following disclaimer. 1183c37607SAlex Hornung * 2. Redistributions in binary form must reproduce the above copyright 1283c37607SAlex Hornung * notice, this list of conditions and the following disclaimer in 1383c37607SAlex Hornung * the documentation and/or other materials provided with the 1483c37607SAlex Hornung * distribution. 1583c37607SAlex Hornung * 1683c37607SAlex Hornung * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1783c37607SAlex Hornung * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1883c37607SAlex Hornung * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 1983c37607SAlex Hornung * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2083c37607SAlex Hornung * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2183c37607SAlex Hornung * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 2283c37607SAlex Hornung * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2383c37607SAlex Hornung * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2483c37607SAlex Hornung * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2583c37607SAlex Hornung * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2683c37607SAlex Hornung * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2783c37607SAlex Hornung * SUCH DAMAGE. 2883c37607SAlex Hornung */ 2983c37607SAlex Hornung #include <sys/param.h> 3083c37607SAlex Hornung #include <sys/systm.h> 3183c37607SAlex Hornung #include <sys/kernel.h> 3283c37607SAlex Hornung #include <sys/kobj.h> 3383c37607SAlex Hornung #include <sys/libkern.h> 3483c37607SAlex Hornung #include <sys/module.h> 3583c37607SAlex Hornung #include <sys/bus.h> 3683c37607SAlex Hornung #include <sys/random.h> 371cb34a03SMatthew Dillon #include <sys/malloc.h> 3851a4a9a0SMatthew Dillon #include <sys/sysctl.h> 3983c37607SAlex Hornung 4083c37607SAlex Hornung #include <machine/specialreg.h> 4183c37607SAlex Hornung 42df5c0f32SMatthew Dillon /* 43df5c0f32SMatthew Dillon * WARNING! 44df5c0f32SMatthew Dillon * 45df5c0f32SMatthew Dillon * The RDRAND instruction is a very slow instruction, burning approximately 46df5c0f32SMatthew Dillon * 0.79uS per 64-bit word on a modern ryzen cpu. Intel cpu's run this 47df5c0f32SMatthew Dillon * instruction far more quickly. The quality of the results are unknown 48*f1af7853SMatthew Dillon * either way. 49*f1af7853SMatthew Dillon * 50*f1af7853SMatthew Dillon * However, possibly an even bigger problem is the cost of calling 51*f1af7853SMatthew Dillon * add_buffer_randomness(), which takes an enormous amount of time 52*f1af7853SMatthew Dillon * when handed a large buffer. 53df5c0f32SMatthew Dillon * 54df5c0f32SMatthew Dillon * Our code harvests at a 10hz rate on every single core, and also chains 55df5c0f32SMatthew Dillon * some entropy from core to core so honestly it doesn't take much to really 56df5c0f32SMatthew Dillon * mix things up. Use a decent size (16 or 32 bytes should be good). 57*f1af7853SMatthew Dillon * 58*f1af7853SMatthew Dillon * On a TR3990 going from 512 to 16 gave userland almost 10% additional 59*f1af7853SMatthew Dillon * performance... a very stark difference. A simple test loop: 60*f1af7853SMatthew Dillon * 61*f1af7853SMatthew Dillon * BEFORE: (RDRAND_SIZE 512) 62*f1af7853SMatthew Dillon * 7.258u 0.000s 0:07.69 94.2% 2+70k 2+0io 0pf+0w 63*f1af7853SMatthew Dillon * 7.222u 0.000s 0:07.67 94.1% 2+70k 0+0io 0pf+0w 64*f1af7853SMatthew Dillon * 7.239u 0.000s 0:07.69 94.0% 2+70k 0+0io 0pf+0w 65*f1af7853SMatthew Dillon * 66*f1af7853SMatthew Dillon * AFTER: (RDRAND_SIZE 16) (9.3% faster) 67*f1af7853SMatthew Dillon * 7.019u 0.000s 0:07.02 99.8% 2+66k 0+0io 0pf+0w 68*f1af7853SMatthew Dillon * 7.019u 0.000s 0:07.02 99.8% 2+66k 0+0io 0pf+0w 69*f1af7853SMatthew Dillon * 7.028u 0.000s 0:07.02 100.0% 2+66k 0+0io 0pf+0w 70df5c0f32SMatthew Dillon */ 7183c37607SAlex Hornung #define RDRAND_ALIGN(p) (void *)(roundup2((uintptr_t)(p), 16)) 72df5c0f32SMatthew Dillon #define RDRAND_SIZE 16 7383c37607SAlex Hornung 7451a4a9a0SMatthew Dillon static int rdrand_debug; 7551a4a9a0SMatthew Dillon SYSCTL_INT(_debug, OID_AUTO, rdrand, CTLFLAG_RW, &rdrand_debug, 0, 7651a4a9a0SMatthew Dillon "Enable rdrand debugging"); 7783c37607SAlex Hornung 7883c37607SAlex Hornung struct rdrand_softc { 791cb34a03SMatthew Dillon struct callout *sc_rng_co; 8083c37607SAlex Hornung int32_t sc_rng_ticks; 8183c37607SAlex Hornung }; 8283c37607SAlex Hornung 8383c37607SAlex Hornung 8483c37607SAlex Hornung static void rdrand_rng_harvest(void *); 8551a4a9a0SMatthew Dillon int rdrand_rng(uint8_t *out, long limit); 8683c37607SAlex Hornung 8783c37607SAlex Hornung 8883c37607SAlex Hornung static void 8983c37607SAlex Hornung rdrand_identify(driver_t *drv, device_t parent) 9083c37607SAlex Hornung { 9183c37607SAlex Hornung 9283c37607SAlex Hornung /* NB: order 10 is so we get attached after h/w devices */ 9383c37607SAlex Hornung if (device_find_child(parent, "rdrand", -1) == NULL && 9483c37607SAlex Hornung BUS_ADD_CHILD(parent, parent, 10, "rdrand", -1) == 0) 9583c37607SAlex Hornung panic("rdrand: could not attach"); 9683c37607SAlex Hornung } 9783c37607SAlex Hornung 9883c37607SAlex Hornung 9983c37607SAlex Hornung static int 10083c37607SAlex Hornung rdrand_probe(device_t dev) 10183c37607SAlex Hornung { 10283c37607SAlex Hornung 10383c37607SAlex Hornung if ((cpu_feature2 & CPUID2_RDRAND) == 0) { 10483c37607SAlex Hornung device_printf(dev, "No RdRand support.\n"); 10583c37607SAlex Hornung return (EINVAL); 10683c37607SAlex Hornung } 10783c37607SAlex Hornung 10883c37607SAlex Hornung device_set_desc(dev, "RdRand RNG"); 10983c37607SAlex Hornung return 0; 11083c37607SAlex Hornung } 11183c37607SAlex Hornung 11283c37607SAlex Hornung 11383c37607SAlex Hornung static int 11483c37607SAlex Hornung rdrand_attach(device_t dev) 11583c37607SAlex Hornung { 11683c37607SAlex Hornung struct rdrand_softc *sc; 1171cb34a03SMatthew Dillon int i; 11883c37607SAlex Hornung 11983c37607SAlex Hornung sc = device_get_softc(dev); 12083c37607SAlex Hornung 12151a4a9a0SMatthew Dillon if (hz > 10) 12251a4a9a0SMatthew Dillon sc->sc_rng_ticks = hz / 10; 12383c37607SAlex Hornung else 12483c37607SAlex Hornung sc->sc_rng_ticks = 1; 12583c37607SAlex Hornung 1261cb34a03SMatthew Dillon sc->sc_rng_co = kmalloc(ncpus * sizeof(*sc->sc_rng_co), 1271cb34a03SMatthew Dillon M_TEMP, M_WAITOK | M_ZERO); 1281cb34a03SMatthew Dillon 1291cb34a03SMatthew Dillon for (i = 0; i < ncpus; ++i) { 1301cb34a03SMatthew Dillon callout_init_mp(&sc->sc_rng_co[i]); 1311cb34a03SMatthew Dillon callout_reset_bycpu(&sc->sc_rng_co[i], sc->sc_rng_ticks, 1321cb34a03SMatthew Dillon rdrand_rng_harvest, sc, i); 1331cb34a03SMatthew Dillon } 13483c37607SAlex Hornung 13583c37607SAlex Hornung return 0; 13683c37607SAlex Hornung } 13783c37607SAlex Hornung 13883c37607SAlex Hornung 13983c37607SAlex Hornung static int 14083c37607SAlex Hornung rdrand_detach(device_t dev) 14183c37607SAlex Hornung { 14283c37607SAlex Hornung struct rdrand_softc *sc; 1431cb34a03SMatthew Dillon int i; 14483c37607SAlex Hornung 14583c37607SAlex Hornung sc = device_get_softc(dev); 14683c37607SAlex Hornung 1471cb34a03SMatthew Dillon for (i = 0; i < ncpus; ++i) { 1481cb34a03SMatthew Dillon callout_terminate(&sc->sc_rng_co[i]); 1491cb34a03SMatthew Dillon } 15083c37607SAlex Hornung 15183c37607SAlex Hornung return (0); 15283c37607SAlex Hornung } 15383c37607SAlex Hornung 15483c37607SAlex Hornung 15583c37607SAlex Hornung static void 15683c37607SAlex Hornung rdrand_rng_harvest(void *arg) 15783c37607SAlex Hornung { 15883c37607SAlex Hornung struct rdrand_softc *sc = arg; 15951a4a9a0SMatthew Dillon uint8_t randomness[RDRAND_SIZE + 32]; 16083c37607SAlex Hornung uint8_t *arandomness; /* randomness aligned */ 16151a4a9a0SMatthew Dillon int cnt; 16283c37607SAlex Hornung 16383c37607SAlex Hornung arandomness = RDRAND_ALIGN(randomness); 16483c37607SAlex Hornung 165af8b80e4SMatthew Dillon cnt = rdrand_rng(arandomness, RDRAND_SIZE); 16651a4a9a0SMatthew Dillon if (cnt > 0 && cnt < sizeof(randomness)) { 1671cb34a03SMatthew Dillon add_buffer_randomness_src(arandomness, cnt, 1681cb34a03SMatthew Dillon RAND_SRC_RDRAND | 1691cb34a03SMatthew Dillon RAND_SRCF_PCPU); 17051a4a9a0SMatthew Dillon 1711cb34a03SMatthew Dillon if (rdrand_debug > 0) { 1721cb34a03SMatthew Dillon --rdrand_debug; 1731cb34a03SMatthew Dillon kprintf("rdrand(%d,cpu=%d): %02x %02x %02x %02x...\n", 1741cb34a03SMatthew Dillon cnt, mycpu->gd_cpuid, 17551a4a9a0SMatthew Dillon arandomness[0], 17651a4a9a0SMatthew Dillon arandomness[1], 17751a4a9a0SMatthew Dillon arandomness[2], 17851a4a9a0SMatthew Dillon arandomness[3]); 17951a4a9a0SMatthew Dillon } 18051a4a9a0SMatthew Dillon } 18183c37607SAlex Hornung 1821cb34a03SMatthew Dillon callout_reset(&sc->sc_rng_co[mycpu->gd_cpuid], sc->sc_rng_ticks, 18383c37607SAlex Hornung rdrand_rng_harvest, sc); 18483c37607SAlex Hornung } 18583c37607SAlex Hornung 18683c37607SAlex Hornung 18783c37607SAlex Hornung static device_method_t rdrand_methods[] = { 18883c37607SAlex Hornung DEVMETHOD(device_identify, rdrand_identify), 18983c37607SAlex Hornung DEVMETHOD(device_probe, rdrand_probe), 19083c37607SAlex Hornung DEVMETHOD(device_attach, rdrand_attach), 19183c37607SAlex Hornung DEVMETHOD(device_detach, rdrand_detach), 19283c37607SAlex Hornung 193d3c9c58eSSascha Wildner DEVMETHOD_END 19483c37607SAlex Hornung }; 19583c37607SAlex Hornung 19683c37607SAlex Hornung 19783c37607SAlex Hornung static driver_t rdrand_driver = { 19883c37607SAlex Hornung "rdrand", 19983c37607SAlex Hornung rdrand_methods, 20083c37607SAlex Hornung sizeof(struct rdrand_softc), 20183c37607SAlex Hornung }; 20283c37607SAlex Hornung 20383c37607SAlex Hornung static devclass_t rdrand_devclass; 20483c37607SAlex Hornung 20583c37607SAlex Hornung DRIVER_MODULE(rdrand, nexus, rdrand_driver, rdrand_devclass, NULL, NULL); 20683c37607SAlex Hornung MODULE_VERSION(rdrand, 1); 20783c37607SAlex Hornung MODULE_DEPEND(rdrand, crypto, 1, 1, 1); 208