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
cpu_rng_detect(void)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
cpu_rng_rdrand(uint64_t * out)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
cpu_rng_rdseed(uint64_t * out)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
cpu_rng_rdseed_rdrand(uint64_t * out)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
cpu_rng_via(uint64_t * out)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
cpu_rng(enum cpu_rng_mode mode,uint64_t * out)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
cpu_rng_get(size_t nbytes,void * cookie)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
cpu_rng_init(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
cpu_rng_early_sample(uint64_t * sample)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