1/* $NetBSD: cpu_rng.c,v 1.5 2016/02/29 00:17: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 * The VIA RNG code in this file is inspired by Jason Wright and
34 * Theo de Raadt's OpenBSD version but has been rewritten in light of
35 * comments from Henric Jungheim on the tech@openbsd.org mailing list.
36 */
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/cpu.h>
41
42#include <x86/specialreg.h>
43
44#include <machine/cpufunc.h>
45#include <machine/cpuvar.h>
46#include <machine/cpu_rng.h>
47
48static enum {
49 CPU_RNG_NONE = 0,
50 CPU_RNG_RDRAND,
51 CPU_RNG_RDSEED,
52 CPU_RNG_VIA
53} cpu_rng_mode __read_mostly = CPU_RNG_NONE;
54
55bool
56cpu_rng_init(void)
57{
58
59 if (cpu_feature[5] & CPUID_SEF_RDSEED) {
60 cpu_rng_mode = CPU_RNG_RDSEED;
61 aprint_normal("cpu_rng: RDSEED\n");
62 return true;
63 } else if (cpu_feature[1] & CPUID2_RDRAND) {
64 cpu_rng_mode = CPU_RNG_RDRAND;
65 aprint_normal("cpu_rng: RDRAND\n");
66 return true;
67 } else if (cpu_feature[4] & CPUID_VIA_HAS_RNG) {
68 cpu_rng_mode = CPU_RNG_VIA;
69 aprint_normal("cpu_rng: VIA\n");
70 return true;
71 }
72 return false;
73}
74
75static size_t
76cpu_rng_rdrand(cpu_rng_t *out)
77{
78 uint8_t rndsts;
79
80#ifdef __i386__
81 uint32_t lo, hi;
82
83 __asm __volatile("rdrand %0; setc %1" : "=r"(lo), "=qm"(rndsts));
84 if (rndsts != 1)
85 return 0;
86 __asm __volatile("rdrand %0; setc %1" : "=r"(hi), "=qm"(rndsts));
87
88 *out = (uint64_t)lo | ((uint64_t)hi << 32);
89 explicit_memset(&lo, 0, sizeof(lo));
90 explicit_memset(&hi, 0, sizeof(hi));
91 if (rndsts != 1)
92 return sizeof(lo) * NBBY;
93#else
94 __asm __volatile("rdrand %0; setc %1" : "=r"(*out), "=qm"(rndsts));
95 if (rndsts != 1)
96 return 0;
97#endif
98 return sizeof(*out) * NBBY;
99}
100
101static size_t
102cpu_rng_rdseed(cpu_rng_t *out)
103{
104 uint8_t rndsts;
105
106#ifdef __i386__
107 uint32_t lo, hi;
108
109 __asm __volatile("rdseed %0; setc %1" : "=r"(lo), "=qm"(rndsts));
110 if (rndsts != 1)
111 goto exhausted;
112 __asm __volatile("rdseed %0; setc %1" : "=r"(hi), "=qm"(rndsts));
113 if (rndsts != 1)
114 goto exhausted;
115
116 *out = (uint64_t)lo | ((uint64_t)hi << 32);
117 explicit_memset(&lo, 0, sizeof(lo));
118 explicit_memset(&hi, 0, sizeof(hi));
119#else
120 __asm __volatile("rdseed %0; setc %1" : "=r"(*out), "=qm"(rndsts));
121#endif
122 if (rndsts != 1)
123 goto exhausted;
124
125 return sizeof(*out) * NBBY;
126
127 /*
128 * Userspace could have exhausted RDSEED, but the
129 * CPU-internal generator feeding RDRAND is guaranteed
130 * to be seeded even in this case.
131 */
132exhausted:
133 return cpu_rng_rdrand(out);
134}
135
136static size_t
137cpu_rng_via(cpu_rng_t *out)
138{
139 uint32_t creg0, rndsts;
140
141 /*
142 * Sadly, we have to monkey with the coprocessor enable and fault
143 * registers, which are really for the FPU, in order to read
144 * from the RNG.
145 *
146 * Don't remove CR0_TS from the call below -- comments in the Linux
147 * driver indicate that the xstorerng instruction can generate
148 * spurious DNA faults though no FPU or SIMD state is changed
149 * even if such a fault is generated.
150 *
151 * XXX can this really happen if we don't use "rep xstorrng"?
152 *
153 */
154 kpreempt_disable();
155 x86_disable_intr();
156 creg0 = rcr0();
157 lcr0(creg0 & ~(CR0_EM|CR0_TS)); /* Permit access to SIMD/FPU path */
158 /*
159 * The VIA RNG has an output queue of 8-byte values. Read one.
160 * This is atomic, so if the FPU were already enabled, we could skip
161 * all the preemption and interrupt frobbing. If we had bread,
162 * we could have a ham sandwich, if we had any ham.
163 */
164 __asm __volatile("xstorerng"
165 : "=a" (rndsts), "+D" (out) : "d" (0) : "memory");
166 /* Put CR0 back how it was */
167 lcr0(creg0);
168 x86_enable_intr();
169 kpreempt_enable();
170
171 /*
172 * The Cryptography Research paper on the VIA RNG estimates
173 * 0.75 bits of entropy per output bit and advises users to
174 * be "even more conservative".
175 */
176 return rndsts & 0xf ? 0 : sizeof(cpu_rng_t) * NBBY / 2;
177}
178
179size_t
180cpu_rng(cpu_rng_t *out)
181{
182
183 switch (cpu_rng_mode) {
184 case CPU_RNG_NONE:
185 return 0;
186 case CPU_RNG_RDSEED:
187 return cpu_rng_rdseed(out);
188 case CPU_RNG_RDRAND:
189 return cpu_rng_rdrand(out);
190 case CPU_RNG_VIA:
191 return cpu_rng_via(out);
192 default:
193 panic("cpu_rng: unknown mode %d", (int)cpu_rng_mode);
194 }
195}
196