xref: /freebsd/sys/dev/random/random_harvestq.c (revision ce3adf4362fcca6a43e500b2531f0038adbfbd21)
1 /*-
2  * Copyright (c) 2013 Arthur Mesh
3  * Copyright (c) 2000-2009 Mark R V Murray
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  * $FreeBSD$
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/kthread.h>
38 #include <sys/lock.h>
39 #include <sys/malloc.h>
40 #include <sys/mutex.h>
41 #include <sys/random.h>
42 #include <sys/sysctl.h>
43 #include <sys/unistd.h>
44 
45 #include <dev/random/randomdev_soft.h>
46 
47 #include "random_harvestq.h"
48 
49 #define RANDOM_FIFO_MAX	256	/* How many events to queue up */
50 
51 MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers");
52 
53 /*
54  * The harvest mutex protects the consistency of the entropy fifos and
55  * empty fifo.
56  */
57 struct mtx	harvest_mtx;
58 
59 /* Lockable FIFO queue holding entropy buffers */
60 struct entropyfifo {
61 	int count;
62 	STAILQ_HEAD(harvestlist, harvest) head;
63 };
64 
65 /* Empty entropy buffers */
66 static struct entropyfifo emptyfifo;
67 
68 #define EMPTYBUFFERS	1024
69 
70 /* Harvested entropy */
71 static struct entropyfifo harvestfifo[ENTROPYSOURCE];
72 
73 /* <0 to end the kthread, 0 to let it run, 1 to flush the harvest queues */
74 int random_kthread_control = 0;
75 
76 static struct proc *random_kthread_proc;
77 
78 static void
79 random_kthread(void *arg)
80 {
81 	STAILQ_HEAD(, harvest) local_queue;
82 	struct harvest *event = NULL;
83 	int local_count;
84 	enum esource source;
85 	event_proc_f func = arg;
86 
87 	STAILQ_INIT(&local_queue);
88 	local_count = 0;
89 
90 	/* Process until told to stop */
91 	mtx_lock_spin(&harvest_mtx);
92 	for (; random_kthread_control >= 0;) {
93 
94 		/* Cycle through all the entropy sources */
95 		for (source = RANDOM_START; source < ENTROPYSOURCE; source++) {
96 			/*
97 			 * Drain entropy source records into a thread-local
98 			 * queue for processing while not holding the mutex.
99 			 */
100 			STAILQ_CONCAT(&local_queue, &harvestfifo[source].head);
101 			local_count += harvestfifo[source].count;
102 			harvestfifo[source].count = 0;
103 		}
104 
105 		/*
106 		 * Deal with events, if any, dropping the mutex as we process
107 		 * each event.  Then push the events back into the empty
108 		 * fifo.
109 		 */
110 		if (!STAILQ_EMPTY(&local_queue)) {
111 			mtx_unlock_spin(&harvest_mtx);
112 			STAILQ_FOREACH(event, &local_queue, next)
113 				func(event);
114 			mtx_lock_spin(&harvest_mtx);
115 			STAILQ_CONCAT(&emptyfifo.head, &local_queue);
116 			emptyfifo.count += local_count;
117 			local_count = 0;
118 		}
119 
120 		KASSERT(local_count == 0, ("random_kthread: local_count %d",
121 		    local_count));
122 
123 		/*
124 		 * If a queue flush was commanded, it has now happened,
125 		 * and we can mark this by resetting the command.
126 		 */
127 		if (random_kthread_control == 1)
128 			random_kthread_control = 0;
129 
130 		/* Work done, so don't belabour the issue */
131 		msleep_spin_sbt(&random_kthread_control, &harvest_mtx,
132 		    "-", SBT_1S / 10, 0, C_PREL(1));
133 
134 	}
135 	mtx_unlock_spin(&harvest_mtx);
136 
137 	random_set_wakeup_exit(&random_kthread_control);
138 	/* NOTREACHED */
139 }
140 
141 void
142 random_harvestq_init(event_proc_f cb)
143 {
144 	int error, i;
145 	struct harvest *np;
146 	enum esource e;
147 
148 	/* Initialise the harvest fifos */
149 	STAILQ_INIT(&emptyfifo.head);
150 	emptyfifo.count = 0;
151 	for (i = 0; i < EMPTYBUFFERS; i++) {
152 		np = malloc(sizeof(struct harvest), M_ENTROPY, M_WAITOK);
153 		STAILQ_INSERT_TAIL(&emptyfifo.head, np, next);
154 	}
155 	for (e = RANDOM_START; e < ENTROPYSOURCE; e++) {
156 		STAILQ_INIT(&harvestfifo[e].head);
157 		harvestfifo[e].count = 0;
158 	}
159 
160 	mtx_init(&harvest_mtx, "entropy harvest mutex", NULL, MTX_SPIN);
161 
162 
163 	/* Start the hash/reseed thread */
164 	error = kproc_create(random_kthread, cb,
165 	    &random_kthread_proc, RFHIGHPID, 0, "rand_harvestq"); /* RANDOM_CSPRNG_NAME */
166 
167 	if (error != 0)
168 		panic("Cannot create entropy maintenance thread.");
169 }
170 
171 void
172 random_harvestq_deinit(void)
173 {
174 	struct harvest *np;
175 	enum esource e;
176 
177 	/* Destroy the harvest fifos */
178 	while (!STAILQ_EMPTY(&emptyfifo.head)) {
179 		np = STAILQ_FIRST(&emptyfifo.head);
180 		STAILQ_REMOVE_HEAD(&emptyfifo.head, next);
181 		free(np, M_ENTROPY);
182 	}
183 	for (e = RANDOM_START; e < ENTROPYSOURCE; e++) {
184 		while (!STAILQ_EMPTY(&harvestfifo[e].head)) {
185 			np = STAILQ_FIRST(&harvestfifo[e].head);
186 			STAILQ_REMOVE_HEAD(&harvestfifo[e].head, next);
187 			free(np, M_ENTROPY);
188 		}
189 	}
190 
191 	mtx_destroy(&harvest_mtx);
192 }
193 
194 /*
195  * Entropy harvesting routine. This is supposed to be fast; do
196  * not do anything slow in here!
197  */
198 void
199 random_harvestq_internal(u_int64_t somecounter, const void *entropy,
200     u_int count, u_int bits, u_int frac, enum esource origin)
201 {
202 	struct harvest *event;
203 
204 	KASSERT(origin >= RANDOM_START && origin <= RANDOM_PURE,
205 	    ("random_harvest_internal: origin %d invalid\n", origin));
206 
207 	/* Lockless read to avoid lock operations if fifo is full. */
208 	if (harvestfifo[origin].count >= RANDOM_FIFO_MAX)
209 		return;
210 
211 	mtx_lock_spin(&harvest_mtx);
212 
213 	/*
214 	 * Don't make the harvest queues too big - help to prevent low-grade
215 	 * entropy swamping
216 	 */
217 	if (harvestfifo[origin].count < RANDOM_FIFO_MAX) {
218 		event = STAILQ_FIRST(&emptyfifo.head);
219 		if (event != NULL) {
220 			/* Add the harvested data to the fifo */
221 			STAILQ_REMOVE_HEAD(&emptyfifo.head, next);
222 			harvestfifo[origin].count++;
223 			event->somecounter = somecounter;
224 			event->size = count;
225 			event->bits = bits;
226 			event->frac = frac;
227 			event->source = origin;
228 
229 			/* XXXX Come back and make this dynamic! */
230 			count = MIN(count, HARVESTSIZE);
231 			memcpy(event->entropy, entropy, count);
232 
233 #if 0
234 			{
235 			int i;
236 			printf("Harvest:%16jX ", event->somecounter);
237 			for (i = 0; i < event->size; i++)
238 				printf("%02X", event->entropy[i]);
239 			for (; i < 16; i++)
240 				printf("  ");
241 			printf(" %2d 0x%2X.%03X %02X\n", event->size, event->bits, event->frac, event->source);
242 			}
243 #endif
244 
245 			STAILQ_INSERT_TAIL(&harvestfifo[origin].head,
246 			    event, next);
247 		}
248 	}
249 	mtx_unlock_spin(&harvest_mtx);
250 }
251 
252