xref: /dflybsd-src/sys/dev/crypto/rdrand/rdrand.c (revision df5c0f32875b9ea77d002c2f672af88fec0b7ff7)
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 
42*df5c0f32SMatthew Dillon /*
43*df5c0f32SMatthew Dillon  * WARNING!
44*df5c0f32SMatthew Dillon  *
45*df5c0f32SMatthew Dillon  * The RDRAND instruction is a very slow instruction, burning approximately
46*df5c0f32SMatthew Dillon  * 0.79uS per 64-bit word on a modern ryzen cpu.  Intel cpu's run this
47*df5c0f32SMatthew Dillon  * instruction far more quickly.  The quality of the results are unknown
48*df5c0f32SMatthew Dillon  * either way.  The add_buffer_randomness() call is also not cheap.
49*df5c0f32SMatthew Dillon  *
50*df5c0f32SMatthew Dillon  * Our code harvests at a 10hz rate on every single core, and also chains
51*df5c0f32SMatthew Dillon  * some entropy from core to core so honestly it doesn't take much to really
52*df5c0f32SMatthew Dillon  * mix things up.  Use a decent size (16 or 32 bytes should be good).
53*df5c0f32SMatthew Dillon  */
5483c37607SAlex Hornung #define	RDRAND_ALIGN(p)	(void *)(roundup2((uintptr_t)(p), 16))
55*df5c0f32SMatthew Dillon #define RDRAND_SIZE	16
5683c37607SAlex Hornung 
5751a4a9a0SMatthew Dillon static int rdrand_debug;
5851a4a9a0SMatthew Dillon SYSCTL_INT(_debug, OID_AUTO, rdrand, CTLFLAG_RW, &rdrand_debug, 0,
5951a4a9a0SMatthew Dillon 	   "Enable rdrand debugging");
6083c37607SAlex Hornung 
6183c37607SAlex Hornung struct rdrand_softc {
621cb34a03SMatthew Dillon 	struct callout	*sc_rng_co;
6383c37607SAlex Hornung 	int32_t		sc_rng_ticks;
6483c37607SAlex Hornung };
6583c37607SAlex Hornung 
6683c37607SAlex Hornung 
6783c37607SAlex Hornung static void rdrand_rng_harvest(void *);
6851a4a9a0SMatthew Dillon int rdrand_rng(uint8_t *out, long limit);
6983c37607SAlex Hornung 
7083c37607SAlex Hornung 
7183c37607SAlex Hornung static void
7283c37607SAlex Hornung rdrand_identify(driver_t *drv, device_t parent)
7383c37607SAlex Hornung {
7483c37607SAlex Hornung 
7583c37607SAlex Hornung 	/* NB: order 10 is so we get attached after h/w devices */
7683c37607SAlex Hornung 	if (device_find_child(parent, "rdrand", -1) == NULL &&
7783c37607SAlex Hornung 	    BUS_ADD_CHILD(parent, parent, 10, "rdrand", -1) == 0)
7883c37607SAlex Hornung 		panic("rdrand: could not attach");
7983c37607SAlex Hornung }
8083c37607SAlex Hornung 
8183c37607SAlex Hornung 
8283c37607SAlex Hornung static int
8383c37607SAlex Hornung rdrand_probe(device_t dev)
8483c37607SAlex Hornung {
8583c37607SAlex Hornung 
8683c37607SAlex Hornung 	if ((cpu_feature2 & CPUID2_RDRAND) == 0) {
8783c37607SAlex Hornung 		device_printf(dev, "No RdRand support.\n");
8883c37607SAlex Hornung 		return (EINVAL);
8983c37607SAlex Hornung 	}
9083c37607SAlex Hornung 
9183c37607SAlex Hornung 	device_set_desc(dev, "RdRand RNG");
9283c37607SAlex Hornung 	return 0;
9383c37607SAlex Hornung }
9483c37607SAlex Hornung 
9583c37607SAlex Hornung 
9683c37607SAlex Hornung static int
9783c37607SAlex Hornung rdrand_attach(device_t dev)
9883c37607SAlex Hornung {
9983c37607SAlex Hornung 	struct rdrand_softc *sc;
1001cb34a03SMatthew Dillon 	int i;
10183c37607SAlex Hornung 
10283c37607SAlex Hornung 	sc = device_get_softc(dev);
10383c37607SAlex Hornung 
10451a4a9a0SMatthew Dillon 	if (hz > 10)
10551a4a9a0SMatthew Dillon 		sc->sc_rng_ticks = hz / 10;
10683c37607SAlex Hornung 	else
10783c37607SAlex Hornung 		sc->sc_rng_ticks = 1;
10883c37607SAlex Hornung 
1091cb34a03SMatthew Dillon 	sc->sc_rng_co = kmalloc(ncpus * sizeof(*sc->sc_rng_co),
1101cb34a03SMatthew Dillon 				M_TEMP, M_WAITOK | M_ZERO);
1111cb34a03SMatthew Dillon 
1121cb34a03SMatthew Dillon 	for (i = 0; i < ncpus; ++i) {
1131cb34a03SMatthew Dillon 		callout_init_mp(&sc->sc_rng_co[i]);
1141cb34a03SMatthew Dillon 		callout_reset_bycpu(&sc->sc_rng_co[i], sc->sc_rng_ticks,
1151cb34a03SMatthew Dillon 				    rdrand_rng_harvest, sc, i);
1161cb34a03SMatthew Dillon 	}
11783c37607SAlex Hornung 
11883c37607SAlex Hornung 	return 0;
11983c37607SAlex Hornung }
12083c37607SAlex Hornung 
12183c37607SAlex Hornung 
12283c37607SAlex Hornung static int
12383c37607SAlex Hornung rdrand_detach(device_t dev)
12483c37607SAlex Hornung {
12583c37607SAlex Hornung 	struct rdrand_softc *sc;
1261cb34a03SMatthew Dillon 	int i;
12783c37607SAlex Hornung 
12883c37607SAlex Hornung 	sc = device_get_softc(dev);
12983c37607SAlex Hornung 
1301cb34a03SMatthew Dillon 	for (i = 0; i < ncpus; ++i) {
1311cb34a03SMatthew Dillon 		callout_terminate(&sc->sc_rng_co[i]);
1321cb34a03SMatthew Dillon 	}
13383c37607SAlex Hornung 
13483c37607SAlex Hornung 	return (0);
13583c37607SAlex Hornung }
13683c37607SAlex Hornung 
13783c37607SAlex Hornung 
13883c37607SAlex Hornung static void
13983c37607SAlex Hornung rdrand_rng_harvest(void *arg)
14083c37607SAlex Hornung {
14183c37607SAlex Hornung 	struct rdrand_softc *sc = arg;
14251a4a9a0SMatthew Dillon 	uint8_t randomness[RDRAND_SIZE + 32];
14383c37607SAlex Hornung 	uint8_t *arandomness; /* randomness aligned */
14451a4a9a0SMatthew Dillon 	int cnt;
14583c37607SAlex Hornung 
14683c37607SAlex Hornung 	arandomness = RDRAND_ALIGN(randomness);
14783c37607SAlex Hornung 
148af8b80e4SMatthew Dillon 	cnt = rdrand_rng(arandomness, RDRAND_SIZE);
14951a4a9a0SMatthew Dillon 	if (cnt > 0 && cnt < sizeof(randomness)) {
1501cb34a03SMatthew Dillon 		add_buffer_randomness_src(arandomness, cnt,
1511cb34a03SMatthew Dillon 					  RAND_SRC_RDRAND |
1521cb34a03SMatthew Dillon 					  RAND_SRCF_PCPU);
15351a4a9a0SMatthew Dillon 
1541cb34a03SMatthew Dillon 		if (rdrand_debug > 0) {
1551cb34a03SMatthew Dillon 			--rdrand_debug;
1561cb34a03SMatthew Dillon 			kprintf("rdrand(%d,cpu=%d): %02x %02x %02x %02x...\n",
1571cb34a03SMatthew Dillon 				cnt, mycpu->gd_cpuid,
15851a4a9a0SMatthew Dillon 				arandomness[0],
15951a4a9a0SMatthew Dillon 				arandomness[1],
16051a4a9a0SMatthew Dillon 				arandomness[2],
16151a4a9a0SMatthew Dillon 				arandomness[3]);
16251a4a9a0SMatthew Dillon 		}
16351a4a9a0SMatthew Dillon 	}
16483c37607SAlex Hornung 
1651cb34a03SMatthew Dillon 	callout_reset(&sc->sc_rng_co[mycpu->gd_cpuid], sc->sc_rng_ticks,
16683c37607SAlex Hornung 		      rdrand_rng_harvest, sc);
16783c37607SAlex Hornung }
16883c37607SAlex Hornung 
16983c37607SAlex Hornung 
17083c37607SAlex Hornung static device_method_t rdrand_methods[] = {
17183c37607SAlex Hornung 	DEVMETHOD(device_identify, rdrand_identify),
17283c37607SAlex Hornung 	DEVMETHOD(device_probe, rdrand_probe),
17383c37607SAlex Hornung 	DEVMETHOD(device_attach, rdrand_attach),
17483c37607SAlex Hornung 	DEVMETHOD(device_detach, rdrand_detach),
17583c37607SAlex Hornung 
176d3c9c58eSSascha Wildner 	DEVMETHOD_END
17783c37607SAlex Hornung };
17883c37607SAlex Hornung 
17983c37607SAlex Hornung 
18083c37607SAlex Hornung static driver_t rdrand_driver = {
18183c37607SAlex Hornung 	"rdrand",
18283c37607SAlex Hornung 	rdrand_methods,
18383c37607SAlex Hornung 	sizeof(struct rdrand_softc),
18483c37607SAlex Hornung };
18583c37607SAlex Hornung 
18683c37607SAlex Hornung static devclass_t rdrand_devclass;
18783c37607SAlex Hornung 
18883c37607SAlex Hornung DRIVER_MODULE(rdrand, nexus, rdrand_driver, rdrand_devclass, NULL, NULL);
18983c37607SAlex Hornung MODULE_VERSION(rdrand, 1);
19083c37607SAlex Hornung MODULE_DEPEND(rdrand, crypto, 1, 1, 1);
191