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
48f1af7853SMatthew Dillon * either way.
49f1af7853SMatthew Dillon *
50f1af7853SMatthew Dillon * However, possibly an even bigger problem is the cost of calling
51f1af7853SMatthew Dillon * add_buffer_randomness(), which takes an enormous amount of time
52f1af7853SMatthew 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).
57f1af7853SMatthew Dillon *
58f1af7853SMatthew Dillon * On a TR3990 going from 512 to 16 gave userland almost 10% additional
59f1af7853SMatthew Dillon * performance... a very stark difference. A simple test loop:
60f1af7853SMatthew Dillon *
61f1af7853SMatthew Dillon * BEFORE: (RDRAND_SIZE 512)
62f1af7853SMatthew Dillon * 7.258u 0.000s 0:07.69 94.2% 2+70k 2+0io 0pf+0w
63f1af7853SMatthew Dillon * 7.222u 0.000s 0:07.67 94.1% 2+70k 0+0io 0pf+0w
64f1af7853SMatthew Dillon * 7.239u 0.000s 0:07.69 94.0% 2+70k 0+0io 0pf+0w
65f1af7853SMatthew Dillon *
66f1af7853SMatthew Dillon * AFTER: (RDRAND_SIZE 16) (9.3% faster)
67f1af7853SMatthew Dillon * 7.019u 0.000s 0:07.02 99.8% 2+66k 0+0io 0pf+0w
68f1af7853SMatthew Dillon * 7.019u 0.000s 0:07.02 99.8% 2+66k 0+0io 0pf+0w
69f1af7853SMatthew 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
rdrand_identify(driver_t * drv,device_t parent)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
rdrand_probe(device_t dev)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
rdrand_attach(device_t dev)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
129*91dc43ddSMatthew Dillon /*
130*91dc43ddSMatthew Dillon * Set an initial offset so we don't pound all cores simultaneously
131*91dc43ddSMatthew Dillon * for no good reason.
132*91dc43ddSMatthew Dillon */
1331cb34a03SMatthew Dillon for (i = 0; i < ncpus; ++i) {
1341cb34a03SMatthew Dillon callout_init_mp(&sc->sc_rng_co[i]);
135*91dc43ddSMatthew Dillon callout_reset_bycpu(&sc->sc_rng_co[i],
136*91dc43ddSMatthew Dillon i, rdrand_rng_harvest, sc, i);
1371cb34a03SMatthew Dillon }
13883c37607SAlex Hornung
13983c37607SAlex Hornung return 0;
14083c37607SAlex Hornung }
14183c37607SAlex Hornung
14283c37607SAlex Hornung
14383c37607SAlex Hornung static int
rdrand_detach(device_t dev)14483c37607SAlex Hornung rdrand_detach(device_t dev)
14583c37607SAlex Hornung {
14683c37607SAlex Hornung struct rdrand_softc *sc;
1471cb34a03SMatthew Dillon int i;
14883c37607SAlex Hornung
14983c37607SAlex Hornung sc = device_get_softc(dev);
15083c37607SAlex Hornung
1511cb34a03SMatthew Dillon for (i = 0; i < ncpus; ++i) {
1521cb34a03SMatthew Dillon callout_terminate(&sc->sc_rng_co[i]);
1531cb34a03SMatthew Dillon }
15483c37607SAlex Hornung
15583c37607SAlex Hornung return (0);
15683c37607SAlex Hornung }
15783c37607SAlex Hornung
15883c37607SAlex Hornung
15983c37607SAlex Hornung static void
rdrand_rng_harvest(void * arg)16083c37607SAlex Hornung rdrand_rng_harvest(void *arg)
16183c37607SAlex Hornung {
16283c37607SAlex Hornung struct rdrand_softc *sc = arg;
16351a4a9a0SMatthew Dillon uint8_t randomness[RDRAND_SIZE + 32];
16483c37607SAlex Hornung uint8_t *arandomness; /* randomness aligned */
16551a4a9a0SMatthew Dillon int cnt;
16683c37607SAlex Hornung
16783c37607SAlex Hornung arandomness = RDRAND_ALIGN(randomness);
16883c37607SAlex Hornung
169af8b80e4SMatthew Dillon cnt = rdrand_rng(arandomness, RDRAND_SIZE);
17051a4a9a0SMatthew Dillon if (cnt > 0 && cnt < sizeof(randomness)) {
1711cb34a03SMatthew Dillon add_buffer_randomness_src(arandomness, cnt,
1721cb34a03SMatthew Dillon RAND_SRC_RDRAND |
1731cb34a03SMatthew Dillon RAND_SRCF_PCPU);
17451a4a9a0SMatthew Dillon
1751cb34a03SMatthew Dillon if (rdrand_debug > 0) {
1761cb34a03SMatthew Dillon --rdrand_debug;
1771cb34a03SMatthew Dillon kprintf("rdrand(%d,cpu=%d): %02x %02x %02x %02x...\n",
1781cb34a03SMatthew Dillon cnt, mycpu->gd_cpuid,
17951a4a9a0SMatthew Dillon arandomness[0],
18051a4a9a0SMatthew Dillon arandomness[1],
18151a4a9a0SMatthew Dillon arandomness[2],
18251a4a9a0SMatthew Dillon arandomness[3]);
18351a4a9a0SMatthew Dillon }
18451a4a9a0SMatthew Dillon }
18583c37607SAlex Hornung
1861cb34a03SMatthew Dillon callout_reset(&sc->sc_rng_co[mycpu->gd_cpuid], sc->sc_rng_ticks,
18783c37607SAlex Hornung rdrand_rng_harvest, sc);
18883c37607SAlex Hornung }
18983c37607SAlex Hornung
19083c37607SAlex Hornung
19183c37607SAlex Hornung static device_method_t rdrand_methods[] = {
19283c37607SAlex Hornung DEVMETHOD(device_identify, rdrand_identify),
19383c37607SAlex Hornung DEVMETHOD(device_probe, rdrand_probe),
19483c37607SAlex Hornung DEVMETHOD(device_attach, rdrand_attach),
19583c37607SAlex Hornung DEVMETHOD(device_detach, rdrand_detach),
19683c37607SAlex Hornung
197d3c9c58eSSascha Wildner DEVMETHOD_END
19883c37607SAlex Hornung };
19983c37607SAlex Hornung
20083c37607SAlex Hornung
20183c37607SAlex Hornung static driver_t rdrand_driver = {
20283c37607SAlex Hornung "rdrand",
20383c37607SAlex Hornung rdrand_methods,
20483c37607SAlex Hornung sizeof(struct rdrand_softc),
20583c37607SAlex Hornung };
20683c37607SAlex Hornung
20783c37607SAlex Hornung static devclass_t rdrand_devclass;
20883c37607SAlex Hornung
20983c37607SAlex Hornung DRIVER_MODULE(rdrand, nexus, rdrand_driver, rdrand_devclass, NULL, NULL);
21083c37607SAlex Hornung MODULE_VERSION(rdrand, 1);
21183c37607SAlex Hornung MODULE_DEPEND(rdrand, crypto, 1, 1, 1);
212