xref: /freebsd/sys/dev/random/random_harvestq.c (revision 7afb8adff33d47f10a11368ff54bb2eec5b30165)
1 /*-
2  * Copyright (c) 2000-2014 Mark R V Murray
3  * Copyright (c) 2013 Arthur Mesh
4  * Copyright (c) 2004 Robert N. M. Watson
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer
12  *    in this position and unchanged.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include "opt_random.h"
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/eventhandler.h>
38 #include <sys/kernel.h>
39 #include <sys/kthread.h>
40 #include <sys/linker.h>
41 #include <sys/lock.h>
42 #include <sys/malloc.h>
43 #include <sys/mutex.h>
44 #include <sys/random.h>
45 #include <sys/sbuf.h>
46 #include <sys/sysctl.h>
47 #include <sys/unistd.h>
48 
49 #include <machine/cpu.h>
50 
51 #include <dev/random/randomdev.h>
52 #include <dev/random/random_adaptors.h>
53 #include <dev/random/random_harvestq.h>
54 #include <dev/random/live_entropy_sources.h>
55 
56 /* List for the dynamic sysctls */
57 static struct sysctl_ctx_list random_clist;
58 
59 /*
60  * How many events to queue up. We create this many items in
61  * an 'empty' queue, then transfer them to the 'harvest' queue with
62  * supplied junk. When used, they are transferred back to the
63  * 'empty' queue.
64  */
65 #define RANDOM_FIFO_MAX	1024
66 
67 /*
68  * The harvest mutex protects the consistency of the entropy Fifos and
69  * empty fifo and other associated structures.
70  */
71 static struct mtx harvest_mtx;
72 
73 /*
74  * Lockable FIFO ring buffer holding entropy events
75  * If ring_in == ring_out,
76  *     the buffer is empty.
77  * If (ring_in + 1) == ring_out (MOD RANDOM_FIFO_MAX),
78  *     the buffer is full.
79  *
80  * The ring_in variable needs locking as there are multiple
81  * sources to the ring. Only the sources may change ring_in,
82  * but the consumer may examine it.
83  *
84  * The ring_out variable does not need locking as there is
85  * only one consumer. Only the consumer may change ring_out,
86  * but the sources may examine it.
87  */
88 static struct entropyfifo {
89 	struct harvest_event ring[RANDOM_FIFO_MAX];
90 	volatile u_int ring_in;
91 	volatile u_int ring_out;
92 } entropyfifo;
93 
94 /* Round-robin destination cache. */
95 u_int harvest_destination[ENTROPYSOURCE];
96 
97 /* Function called to process one harvested stochastic event */
98 void (*harvest_process_event)(struct harvest_event *);
99 
100 /* Allow the sysadmin to select the broad category of
101  * entropy types to harvest.
102  */
103 static u_int harvest_source_mask = ((1U << RANDOM_ENVIRONMENTAL_END) - 1);
104 
105 /* Pool count is used by anything needing to know how many entropy
106  * pools are currently being maintained.
107  * This is of use to (e.g.) the live source feed where we need to give
108  * all the pools a top-up.
109  */
110 int harvest_pool_count;
111 
112 /* <0 to end the kthread, 0 to let it run, 1 to flush the harvest queues */
113 static int random_kthread_control = 0;
114 
115 static struct proc *random_kthread_proc;
116 
117 static void
118 random_kthread(void *arg __unused)
119 {
120         u_int maxloop, ring_out;
121 
122 	/*
123 	 * Process until told to stop.
124 	 *
125 	 * Locking is not needed as this is the only place we modify ring_out, and
126 	 * we only examine ring_in without changing it. Both of these are volatile,
127 	 * and this is a unique thread.
128 	 */
129 	while (random_kthread_control >= 0) {
130 
131 		/* Deal with events, if any. Restrict the number we do in one go. */
132 		maxloop = RANDOM_FIFO_MAX;
133 		while (entropyfifo.ring_out != entropyfifo.ring_in) {
134 
135 			ring_out = (entropyfifo.ring_out + 1)%RANDOM_FIFO_MAX;
136 			harvest_process_event(entropyfifo.ring + ring_out);
137 			/* Modifying ring_out here ONLY. Sufficient for atomicity? */
138 			entropyfifo.ring_out = ring_out;
139 
140 			/* The ring may be filled quickly so don't loop forever.  */
141 			if (--maxloop)
142 				break;
143 
144 		}
145 
146 		/*
147 		 * Give the fast hardware sources a go
148 		 */
149 		live_entropy_sources_feed();
150 
151 		/*
152 		 * If a queue flush was commanded, it has now happened,
153 		 * and we can mark this by resetting the command.
154 		 * A negative value, however, terminates the thread.
155 		 */
156 
157 		if (random_kthread_control == 1)
158 			random_kthread_control = 0;
159 
160 		/* Some work is done, so give the rest of the OS a chance. */
161 		tsleep_sbt(&random_kthread_control, 0, "-", SBT_1S/10, 0, C_PREL(1));
162 
163 	}
164 
165 	randomdev_set_wakeup_exit(&random_kthread_control);
166 	/* NOTREACHED */
167 }
168 
169 void
170 random_harvestq_flush(void)
171 {
172 
173 	/* Command a entropy queue flush and wait for it to finish */
174 	random_kthread_control = 1;
175 	while (random_kthread_control)
176 		pause("-", hz/10);
177 }
178 
179 /* ARGSUSED */
180 RANDOM_CHECK_UINT(harvestmask, 0, ((1U << RANDOM_ENVIRONMENTAL_END) - 1));
181 
182 /* ARGSUSED */
183 static int
184 random_print_harvestmask(SYSCTL_HANDLER_ARGS)
185 {
186 	struct sbuf sbuf;
187 	int error, i;
188 
189 	error = sysctl_wire_old_buffer(req, 0);
190 	if (error == 0) {
191 		sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
192 		for (i = RANDOM_ENVIRONMENTAL_END - 1; i >= 0; i--)
193 			sbuf_cat(&sbuf, (harvest_source_mask & (1U << i)) ? "1" : "0");
194 		error = sbuf_finish(&sbuf);
195 		sbuf_delete(&sbuf);
196 	}
197 
198 	return (error);
199 }
200 
201 static const char *(random_source_descr[]) = {
202 	"CACHED",
203 	"ATTACH",
204 	"KEYBOARD",
205 	"MOUSE",
206 	"NET_TUN",
207 	"NET_ETHER",
208 	"NET_NG",
209 	"INTERRUPT",
210 	"SWI",
211 	"UMA_ALLOC",
212 	"", /* "ENVIRONMENTAL_END" */
213 	"PURE_OCTEON",
214 	"PURE_SAFE",
215 	"PURE_GLXSB",
216 	"PURE_UBSEC",
217 	"PURE_HIFN",
218 	"PURE_RDRAND",
219 	"PURE_NEHEMIAH",
220 	"PURE_RNDTEST",
221 	/* "ENTROPYSOURCE" */
222 };
223 
224 /* ARGSUSED */
225 static int
226 random_print_harvestmask_symbolic(SYSCTL_HANDLER_ARGS)
227 {
228 	struct sbuf sbuf;
229 	int error, i;
230 
231 	error = sysctl_wire_old_buffer(req, 0);
232 	if (error == 0) {
233 		sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
234 		for (i = RANDOM_ENVIRONMENTAL_END - 1; i >= 0; i--) {
235 			sbuf_cat(&sbuf, (i == RANDOM_ENVIRONMENTAL_END - 1) ? "" : ",");
236 			sbuf_cat(&sbuf, (harvest_source_mask & (1U << i)) ? random_source_descr[i] : "");
237 		}
238 		error = sbuf_finish(&sbuf);
239 		sbuf_delete(&sbuf);
240 	}
241 
242 	return (error);
243 }
244 
245 void
246 random_harvestq_init(void (*event_processor)(struct harvest_event *), int poolcount)
247 {
248 	uint8_t *keyfile, *data;
249 	int error;
250 	size_t size, j;
251 	struct sysctl_oid *random_sys_o;
252 
253 #ifdef RANDOM_DEBUG
254 	printf("random: %s\n", __func__);
255 #endif
256 
257 	random_sys_o = SYSCTL_ADD_NODE(&random_clist,
258 	    SYSCTL_STATIC_CHILDREN(_kern_random),
259 	    OID_AUTO, "harvest", CTLFLAG_RW, 0,
260 	    "Entropy Device Parameters");
261 
262 	SYSCTL_ADD_PROC(&random_clist,
263 	    SYSCTL_CHILDREN(random_sys_o),
264 	    OID_AUTO, "mask", CTLTYPE_UINT | CTLFLAG_RW,
265 	    &harvest_source_mask, ((1U << RANDOM_ENVIRONMENTAL_END) - 1),
266 	    random_check_uint_harvestmask, "IU",
267 	    "Entropy harvesting mask");
268 
269 	SYSCTL_ADD_PROC(&random_clist,
270 	    SYSCTL_CHILDREN(random_sys_o),
271 	    OID_AUTO, "mask_bin", CTLTYPE_STRING | CTLFLAG_RD,
272 	    NULL, 0, random_print_harvestmask, "A", "Entropy harvesting mask (printable)");
273 
274 	SYSCTL_ADD_PROC(&random_clist,
275 	    SYSCTL_CHILDREN(random_sys_o),
276 	    OID_AUTO, "mask_symbolic", CTLTYPE_STRING | CTLFLAG_RD,
277 	    NULL, 0, random_print_harvestmask_symbolic, "A", "Entropy harvesting mask (symbolic)");
278 
279 	/* Point to the correct event_processing function */
280 	harvest_process_event = event_processor;
281 
282 	/* Store the pool count (used by live source feed) */
283 	harvest_pool_count = poolcount;
284 
285 	/* Initialise the harvesting mutex and in/out indexes. */
286 	mtx_init(&harvest_mtx, "entropy harvest mutex", NULL, MTX_SPIN);
287 	entropyfifo.ring_in = entropyfifo.ring_out = 0U;
288 
289 	/* Start the hash/reseed thread */
290 	error = kproc_create(random_kthread, NULL,
291 	    &random_kthread_proc, RFHIGHPID, 0, "rand_harvestq");
292 
293 	if (error != 0)
294 		panic("Cannot create entropy maintenance thread.");
295 
296 	/* Get entropy that may have been preloaded by loader(8)
297 	 * and use it to pre-charge the entropy harvest queue.
298 	 */
299 	keyfile = preload_search_by_type("/boot/entropy");
300 	if (keyfile != NULL) {
301 		data = preload_fetch_addr(keyfile);
302 		size = preload_fetch_size(keyfile);
303 		if (data != NULL && size != 0) {
304 			for (j = 0; j < size; j += 16)
305 				random_harvestq_internal(data + j, 16, 16, RANDOM_CACHED);
306 			printf("random: read %zu bytes from preloaded cache\n", size);
307 			bzero(data, size);
308 		}
309 		else
310 			printf("random: no preloaded entropy cache\n");
311 	}
312 
313 }
314 
315 void
316 random_harvestq_deinit(void)
317 {
318 
319 #ifdef RANDOM_DEBUG
320 	printf("random: %s\n", __func__);
321 #endif
322 
323 	/*
324 	 * Command the hash/reseed thread to end and wait for it to finish
325 	 */
326 	random_kthread_control = -1;
327 	tsleep(&random_kthread_control, 0, "term", 0);
328 
329 	mtx_destroy(&harvest_mtx);
330 
331 	sysctl_ctx_free(&random_clist);
332 }
333 
334 /*
335  * Entropy harvesting routine.
336  * This is supposed to be fast; do not do anything slow in here!
337  *
338  * It is also illegal (and morally reprehensible) to insert any
339  * high-rate data here. "High-rate" is define as a data source
340  * that will usually cause lots of failures of the "Lockless read"
341  * check a few lines below. This includes the "always-on" sources
342  * like the Intel "rdrand" or the VIA Nehamiah "xstore" sources.
343  */
344 /* XXXRW: get_cyclecount() is cheap on most modern hardware, where cycle
345  * counters are built in, but on older hardware it will do a real time clock
346  * read which can be quite expensive.
347  */
348 void
349 random_harvestq_internal(const void *entropy, u_int count, u_int bits,
350     enum random_entropy_source origin)
351 {
352 	struct harvest_event *event;
353 	u_int ring_in;
354 
355 	KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE,
356 	    ("random_harvest_internal: origin %d invalid\n", origin));
357 
358 	/* Mask out unwanted sources */
359 	if (!(harvest_source_mask & (1U << origin)))
360 		return;
361 
362 	/* Lock ring_in against multi-thread contention */
363 	mtx_lock_spin(&harvest_mtx);
364 	ring_in = (entropyfifo.ring_in + 1)%RANDOM_FIFO_MAX;
365 	if (ring_in != entropyfifo.ring_out) {
366 		/* The ring is not full */
367 		event = entropyfifo.ring + ring_in;
368 
369 		/* Stash the harvested stuff in the *event buffer */
370 		count = MIN(count, HARVESTSIZE);
371 		event->he_somecounter = get_cyclecount();
372 		event->he_size = count;
373 		event->he_bits = bits;
374 		event->he_source = origin;
375 		event->he_destination = harvest_destination[origin]++;
376 		memcpy(event->he_entropy, entropy, count);
377 		memset(event->he_entropy + count, 0, HARVESTSIZE - count);
378 
379 		entropyfifo.ring_in = ring_in;
380 	}
381 	mtx_unlock_spin(&harvest_mtx);
382 }
383