Home | History | Annotate | Line # | Download | only in x86
      1 /* $NetBSD: cpu_rng.c,v 1.23 2024/08/01 11:18:54 riastradh Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2015 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Thor Lancelot Simon.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * For reference on VIA XSTORERNG, see the VIA PadLock Programming
     34  * Guide (`VIA PPG'), August 4, 2005.
     35  * https://web.archive.org/web/20210322141743/http://linux.via.com.tw/support/beginDownload.action?eleid=181&fid=261
     36  *
     37  * For reference on Intel RDRAND/RDSEED, see the Intel Digital Random
     38  * Number Generator Software Implementation Guide (`Intel DRNG SIG'),
     39  * Revision 2.1, October 17, 2018.
     40  * https://web.archive.org/web/20200505093404/https://software.intel.com/sites/default/files/managed/98/4a/DRNG_Software_Implementation_Guide_2.1.pdf
     41  *
     42  * Intel's hardware implementation is analyzed by Mike Hamburg, Paul
     43  * Kocher, and Mark E. Marson, `Analysis of Intel's Ivy Bridge Digital
     44  * Random Number Generator', Cryptography Research, Inc., March 12,
     45  * 2012.
     46  * https://web.archive.org/web/20141230024150/http://www.cryptography.com/public/pdf/Intel_TRNG_Report_20120312.pdf
     47  *
     48  * For reference on AMD RDRAND/RDSEED, which are designed to be
     49  * compatible with Intel RDRAND/RDSEED, see the somewhat less detailed
     50  * AMD Random Number Generator documentation, 2017-06-27.
     51  * https://web.archive.org/web/20220402133945/https://www.amd.com/system/files/TechDocs/amd-random-number-generator.pdf
     52  */
     53 
     54 #include <sys/param.h>
     55 #include <sys/systm.h>
     56 #include <sys/cpu.h>
     57 #include <sys/rndsource.h>
     58 #include <sys/sha2.h>
     59 
     60 #include <x86/specialreg.h>
     61 
     62 #include <machine/cpufunc.h>
     63 #include <machine/cpuvar.h>
     64 #include <machine/cpu_rng.h>
     65 #include <machine/limits.h>
     66 
     67 static enum cpu_rng_mode {
     68 	CPU_RNG_NONE = 0,
     69 	CPU_RNG_RDRAND,
     70 	CPU_RNG_RDSEED,
     71 	CPU_RNG_RDSEED_RDRAND,
     72 	CPU_RNG_VIA
     73 } cpu_rng_mode __read_mostly = CPU_RNG_NONE;
     74 
     75 static const char *const cpu_rng_name[] = {
     76 	[CPU_RNG_RDRAND] = "rdrand",
     77 	[CPU_RNG_RDSEED] = "rdseed",
     78 	[CPU_RNG_RDSEED_RDRAND] = "rdrand/rdseed",
     79 	[CPU_RNG_VIA] = "via",
     80 };
     81 
     82 static struct krndsource cpu_rng_source __read_mostly;
     83 
     84 static enum cpu_rng_mode
     85 cpu_rng_detect(void)
     86 {
     87 	bool has_rdseed = (cpu_feature[5] & CPUID_SEF_RDSEED);
     88 	bool has_rdrand = (cpu_feature[1] & CPUID2_RDRAND);
     89 	bool has_viarng = (cpu_feature[4] & CPUID_VIA_HAS_RNG);
     90 
     91 	if (has_rdseed && has_rdrand)
     92 		return CPU_RNG_RDSEED_RDRAND;
     93 	else if (has_rdseed)
     94 		return CPU_RNG_RDSEED;
     95 	else if (has_rdrand)
     96 		return CPU_RNG_RDRAND;
     97 	else if (has_viarng)
     98 		return CPU_RNG_VIA;
     99 	else
    100 		return CPU_RNG_NONE;
    101 }
    102 
    103 static size_t
    104 cpu_rng_rdrand(uint64_t *out)
    105 {
    106 	uint8_t rndsts;
    107 
    108 	/*
    109 	 * XXX The Intel DRNG SIG recommends (Sec. 5.2.1 `Retry
    110 	 * recommendations', p. 22) that we retry up to ten times
    111 	 * before giving up and panicking because something must be
    112 	 * seriously awry with the CPU.
    113 	 *
    114 	 * XXX The Intel DRNG SIG also recommends (Sec. 5.2.6
    115 	 * `Generating Seeds from RDRAND', p. 28) drawing 1024 64-bit
    116 	 * samples (or, 512 128-bit samples) in order to guarantee that
    117 	 * the CPU has drawn an independent sample from the physical
    118 	 * entropy source, since the AES CTR_DRBG behind RDRAND will be
    119 	 * used to generate at most 511 128-bit samples before it is
    120 	 * reseeded from the physical entropy source.  It is unclear
    121 	 * whether the same considerations about RDSEED starvation
    122 	 * apply to this advice.
    123 	 */
    124 
    125 #ifdef __i386__
    126 	uint32_t lo, hi;
    127 
    128 	__asm __volatile("rdrand %0; setc %1" : "=r"(lo), "=qm"(rndsts));
    129 	if (rndsts != 1)
    130 		return 0;
    131 	__asm __volatile("rdrand %0; setc %1" : "=r"(hi), "=qm"(rndsts));
    132 
    133 	*out = (uint64_t)lo | ((uint64_t)hi << 32);
    134 	explicit_memset(&lo, 0, sizeof(lo));
    135 	explicit_memset(&hi, 0, sizeof(hi));
    136 	if (rndsts != 1)
    137 		return sizeof(lo) * NBBY;
    138 #else
    139 	__asm __volatile("rdrand %0; setc %1" : "=r"(*out), "=qm"(rndsts));
    140 	if (rndsts != 1)
    141 		return 0;
    142 #endif
    143 	return sizeof(*out) * NBBY;
    144 }
    145 
    146 static size_t
    147 cpu_rng_rdseed(uint64_t *out)
    148 {
    149 	uint8_t rndsts;
    150 
    151 	/*
    152 	 * XXX The Intel DRNG SIG recommends (Sec. 5.3.1 `Retry
    153 	 * recommendations', p. 22) that we consider retrying up to 100
    154 	 * times, separated by PAUSE, but offers no guarantees about
    155 	 * success after that many retries.  In particular, userland
    156 	 * threads could starve the kernel by issuing RDSEED.
    157 	 */
    158 
    159 #ifdef __i386__
    160 	uint32_t lo, hi;
    161 
    162 	__asm __volatile("rdseed %0; setc %1" : "=r"(lo), "=qm"(rndsts));
    163 	if (rndsts != 1)
    164 		return 0;
    165 	__asm __volatile("rdseed %0; setc %1" : "=r"(hi), "=qm"(rndsts));
    166 	if (rndsts != 1)
    167 		return 0;
    168 
    169 	*out = (uint64_t)lo | ((uint64_t)hi << 32);
    170 	explicit_memset(&lo, 0, sizeof(lo));
    171 	explicit_memset(&hi, 0, sizeof(hi));
    172 #else
    173 	__asm __volatile("rdseed %0; setc %1" : "=r"(*out), "=qm"(rndsts));
    174 #endif
    175 	if (rndsts != 1)
    176 		return 0;
    177 
    178 	return sizeof(*out) * NBBY;
    179 }
    180 
    181 static size_t
    182 cpu_rng_rdseed_rdrand(uint64_t *out)
    183 {
    184 	size_t n = cpu_rng_rdseed(out);
    185 
    186 	if (n == 0)
    187 		n = cpu_rng_rdrand(out);
    188 
    189 	return n;
    190 }
    191 
    192 /*
    193  * VIA PPG says EAX[4:0] is nbytes, but the only documented numbers of
    194  * bytes are 0,1,2,4,8 -- and there's only 8 bytes of output buffer
    195  * anyway, so let's ignore bit 4 and treat it like EAX[3:0] instead.
    196  */
    197 #define	VIA_RNG_STATUS_NBYTES	__BITS(3,0)
    198 #define	VIA_RNG_STATUS_MSR110B	__BITS(31,5)
    199 
    200 static size_t
    201 cpu_rng_via(uint64_t *out)
    202 {
    203 	u_long psl;
    204 	uint32_t cr0, status, nbytes;
    205 
    206 	/*
    207 	 * The XSTORE instruction is handled by the SSE unit, which
    208 	 * requires the CR0 TS and CR0 EM bits to be clear.  We disable
    209 	 * all processor interrupts so there is no danger of any
    210 	 * interrupt handler changing CR0 while we work -- although
    211 	 * really, software splvm or fpu_kern_enter/leave should be
    212 	 * enough (but we'll do that in a separate change for the
    213 	 * benefit of bisection in case I'm wrong).
    214 	 */
    215 	psl = x86_read_psl();
    216 	x86_disable_intr();
    217 	cr0 = rcr0();
    218 	lcr0(cr0 & ~(CR0_EM|CR0_TS));
    219 
    220 	/* Read up to eight bytes out of the buffer.  */
    221 	asm volatile("xstorerng"
    222 	    : "=a"(status)
    223 	    : "D"(out), "d"(0) /* EDX[1:0]=00 -> wait for 8 bytes or fail */
    224 	    : "memory");
    225 
    226 	/* Restore CR0 and interrupts.  */
    227 	lcr0(cr0);
    228 	x86_write_psl(psl);
    229 
    230 	/* Get the number of bytes stored.  (Should always be 8 or 0.)  */
    231 	nbytes = __SHIFTOUT(status, VIA_RNG_STATUS_NBYTES);
    232 
    233 	/*
    234 	 * The Cryptography Research paper on the VIA RNG estimates
    235 	 * 0.75 bits of entropy per output bit and advises users to
    236 	 * be "even more conservative".
    237 	 *
    238 	 *	`Evaluation of VIA C3 Nehemiah Random Number
    239 	 *	Generator', Cryptography Research, Inc., February 27,
    240 	 *	2003.
    241 	 *	https://www.rambus.com/wp-content/uploads/2015/08/VIA_rng.pdf
    242 	 */
    243 	return nbytes * NBBY/2;
    244 }
    245 
    246 static size_t
    247 cpu_rng(enum cpu_rng_mode mode, uint64_t *out)
    248 {
    249 
    250 	switch (mode) {
    251 	case CPU_RNG_NONE:
    252 		return 0;
    253 	case CPU_RNG_RDSEED:
    254 		return cpu_rng_rdseed(out);
    255 	case CPU_RNG_RDRAND:
    256 		return cpu_rng_rdrand(out);
    257 	case CPU_RNG_RDSEED_RDRAND:
    258 		return cpu_rng_rdseed_rdrand(out);
    259 	case CPU_RNG_VIA:
    260 		return cpu_rng_via(out);
    261 	default:
    262 		panic("cpu_rng: unknown mode %d", (int)mode);
    263 	}
    264 }
    265 
    266 static void
    267 cpu_rng_get(size_t nbytes, void *cookie)
    268 {
    269 	enum {
    270 		NBITS = 256,
    271 		NBYTES = howmany(NBITS, 8),
    272 		NWORDS = howmany(NBITS, 64),
    273 	};
    274 	uint64_t buf[2*NWORDS];
    275 	unsigned i, nbits = 0;
    276 
    277 	while (nbytes) {
    278 		/*
    279 		 * The fraction of outputs this rejects in correct
    280 		 * operation is 1/2^256, which is close enough to zero
    281 		 * that we round it to having no effect on the number
    282 		 * of bits of entropy.
    283 		 */
    284 		for (i = 0; i < __arraycount(buf); i++)
    285 			nbits += cpu_rng(cpu_rng_mode, &buf[i]);
    286 		if (consttime_memequal(buf, buf + NWORDS, NBYTES)) {
    287 			printf("cpu_rng %s: failed repetition test\n",
    288 			    cpu_rng_name[cpu_rng_mode]);
    289 			nbits = 0;
    290 		}
    291 		rnd_add_data_sync(&cpu_rng_source, buf, sizeof buf, nbits);
    292 		nbytes -= MIN(MIN(nbytes, sizeof buf), MAX(1, 8*nbits));
    293 	}
    294 }
    295 
    296 void
    297 cpu_rng_init(void)
    298 {
    299 
    300 	cpu_rng_mode = cpu_rng_detect();
    301 	if (cpu_rng_mode == CPU_RNG_NONE)
    302 		return;
    303 	aprint_normal("cpu_rng: %s\n", cpu_rng_name[cpu_rng_mode]);
    304 	rndsource_setcb(&cpu_rng_source, cpu_rng_get, NULL);
    305 	rnd_attach_source(&cpu_rng_source, cpu_rng_name[cpu_rng_mode],
    306 	    RND_TYPE_RNG, RND_FLAG_COLLECT_VALUE|RND_FLAG_HASCB);
    307 }
    308 
    309 /* -------------------------------------------------------------------------- */
    310 
    311 void
    312 cpu_rng_early_sample(uint64_t *sample)
    313 {
    314 	static bool has_rdseed = false;
    315 	static bool has_rdrand = false;
    316 	static bool inited = false;
    317 	u_int descs[4];
    318 	size_t n;
    319 
    320 	if (!inited) {
    321 		if (cpuid_level >= 7) {
    322 			x86_cpuid(0x07, descs);
    323 			has_rdseed = (descs[1] & CPUID_SEF_RDSEED) != 0;
    324 		}
    325 		if (cpuid_level >= 1) {
    326 			x86_cpuid(0x01, descs);
    327 			has_rdrand = (descs[2] & CPUID2_RDRAND) != 0;
    328 		}
    329 		inited = true;
    330 	}
    331 
    332 	n = 0;
    333 	if (has_rdseed && has_rdrand)
    334 		n = cpu_rng_rdseed_rdrand(sample);
    335 	else if (has_rdseed)
    336 		n = cpu_rng_rdseed(sample);
    337 	else if (has_rdrand)
    338 		n = cpu_rng_rdrand(sample);
    339 	if (n == 0)
    340 		*sample = rdtsc();
    341 }
    342