xref: /freebsd/sys/dev/virtio/random/virtio_random.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2013, Bryan Venteicher <bryanv@FreeBSD.org>
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 unmodified, this list of conditions, and the following
12  *    disclaimer.
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 /* Driver for VirtIO entropy device. */
30 
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <sys/eventhandler.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/module.h>
37 #include <sys/sglist.h>
38 #include <sys/random.h>
39 #include <sys/stdatomic.h>
40 
41 #include <machine/bus.h>
42 #include <machine/resource.h>
43 #include <sys/bus.h>
44 
45 #include <dev/random/randomdev.h>
46 #include <dev/random/random_harvestq.h>
47 #include <dev/virtio/virtio.h>
48 #include <dev/virtio/virtqueue.h>
49 
50 struct vtrnd_softc {
51 	device_t		 vtrnd_dev;
52 	uint64_t		 vtrnd_features;
53 	struct virtqueue	*vtrnd_vq;
54 	eventhandler_tag	 eh;
55 	bool			 inactive;
56 	struct sglist		 *vtrnd_sg;
57 	uint32_t		 *vtrnd_value;
58 };
59 
60 static int	vtrnd_modevent(module_t, int, void *);
61 
62 static int	vtrnd_probe(device_t);
63 static int	vtrnd_attach(device_t);
64 static int	vtrnd_detach(device_t);
65 static int	vtrnd_shutdown(device_t);
66 
67 static int	vtrnd_negotiate_features(struct vtrnd_softc *);
68 static int	vtrnd_setup_features(struct vtrnd_softc *);
69 static int	vtrnd_alloc_virtqueue(struct vtrnd_softc *);
70 static int	vtrnd_harvest(struct vtrnd_softc *, void *, size_t *);
71 static void	vtrnd_enqueue(struct vtrnd_softc *sc);
72 static unsigned	vtrnd_read(void *, unsigned);
73 
74 #define VTRND_FEATURES	0
75 
76 static struct virtio_feature_desc vtrnd_feature_desc[] = {
77 	{ 0, NULL }
78 };
79 
80 static struct random_source random_vtrnd = {
81 	.rs_ident = "VirtIO Entropy Adapter",
82 	.rs_source = RANDOM_PURE_VIRTIO,
83 	.rs_read = vtrnd_read,
84 };
85 
86 /* Kludge for API limitations of random(4). */
87 static _Atomic(struct vtrnd_softc *) g_vtrnd_softc;
88 
89 static device_method_t vtrnd_methods[] = {
90 	/* Device methods. */
91 	DEVMETHOD(device_probe,		vtrnd_probe),
92 	DEVMETHOD(device_attach,	vtrnd_attach),
93 	DEVMETHOD(device_detach,	vtrnd_detach),
94 	DEVMETHOD(device_shutdown,	vtrnd_shutdown),
95 
96 	DEVMETHOD_END
97 };
98 
99 static driver_t vtrnd_driver = {
100 	"vtrnd",
101 	vtrnd_methods,
102 	sizeof(struct vtrnd_softc)
103 };
104 
105 VIRTIO_DRIVER_MODULE(virtio_random, vtrnd_driver, vtrnd_modevent, NULL);
106 MODULE_VERSION(virtio_random, 1);
107 MODULE_DEPEND(virtio_random, virtio, 1, 1, 1);
108 MODULE_DEPEND(virtio_random, random_device, 1, 1, 1);
109 
110 VIRTIO_SIMPLE_PNPINFO(virtio_random, VIRTIO_ID_ENTROPY,
111     "VirtIO Entropy Adapter");
112 
113 static int
114 vtrnd_modevent(module_t mod, int type, void *unused)
115 {
116 	int error;
117 
118 	switch (type) {
119 	case MOD_LOAD:
120 	case MOD_QUIESCE:
121 	case MOD_UNLOAD:
122 	case MOD_SHUTDOWN:
123 		error = 0;
124 		break;
125 	default:
126 		error = EOPNOTSUPP;
127 		break;
128 	}
129 
130 	return (error);
131 }
132 
133 static int
134 vtrnd_probe(device_t dev)
135 {
136 	return (VIRTIO_SIMPLE_PROBE(dev, virtio_random));
137 }
138 
139 static int
140 vtrnd_attach(device_t dev)
141 {
142 	struct vtrnd_softc *sc, *exp;
143 	size_t len;
144 	int error;
145 
146 	sc = device_get_softc(dev);
147 	sc->vtrnd_dev = dev;
148 	virtio_set_feature_desc(dev, vtrnd_feature_desc);
149 
150 	len = sizeof(*sc->vtrnd_value) * HARVESTSIZE;
151 	sc->vtrnd_value = malloc_aligned(len, len, M_DEVBUF, M_WAITOK);
152 	sc->vtrnd_sg = sglist_build(sc->vtrnd_value, len, M_WAITOK);
153 
154 	error = vtrnd_setup_features(sc);
155 	if (error) {
156 		device_printf(dev, "cannot setup features\n");
157 		goto fail;
158 	}
159 
160 	error = vtrnd_alloc_virtqueue(sc);
161 	if (error) {
162 		device_printf(dev, "cannot allocate virtqueue\n");
163 		goto fail;
164 	}
165 
166 	exp = NULL;
167 	if (!atomic_compare_exchange_strong_explicit(&g_vtrnd_softc, &exp, sc,
168 	    memory_order_release, memory_order_acquire)) {
169 		error = EEXIST;
170 		goto fail;
171 	}
172 
173 	sc->eh = EVENTHANDLER_REGISTER(shutdown_post_sync,
174 		vtrnd_shutdown, dev, SHUTDOWN_PRI_LAST + 1); /* ??? */
175 	if (sc->eh == NULL) {
176 		device_printf(dev, "Shutdown event registration failed\n");
177 		error = ENXIO;
178 		goto fail;
179 	}
180 
181 	sc->inactive = false;
182 	random_source_register(&random_vtrnd);
183 
184 	vtrnd_enqueue(sc);
185 
186 fail:
187 	if (error)
188 		vtrnd_detach(dev);
189 
190 	return (error);
191 }
192 
193 static int
194 vtrnd_detach(device_t dev)
195 {
196 	struct vtrnd_softc *sc;
197 	uint32_t rdlen;
198 
199 	sc = device_get_softc(dev);
200 	KASSERT(
201 	    atomic_load_explicit(&g_vtrnd_softc, memory_order_acquire) == sc,
202 	    ("only one global instance at a time"));
203 
204 	sc->inactive = true;
205 	if (sc->eh != NULL) {
206 		EVENTHANDLER_DEREGISTER(shutdown_post_sync, sc->eh);
207 		sc->eh = NULL;
208 	}
209 	random_source_deregister(&random_vtrnd);
210 
211 	/* clear the queue */
212 	virtqueue_poll(sc->vtrnd_vq, &rdlen);
213 
214 	atomic_store_explicit(&g_vtrnd_softc, NULL, memory_order_release);
215 	sglist_free(sc->vtrnd_sg);
216 	zfree(sc->vtrnd_value, M_DEVBUF);
217 	return (0);
218 }
219 
220 static int
221 vtrnd_shutdown(device_t dev)
222 {
223 	struct vtrnd_softc *sc;
224 
225 	sc = device_get_softc(dev);
226 	sc->inactive = true;
227 
228 	return(0);
229 }
230 
231 static int
232 vtrnd_negotiate_features(struct vtrnd_softc *sc)
233 {
234 	device_t dev;
235 	uint64_t features;
236 
237 	dev = sc->vtrnd_dev;
238 	features = VTRND_FEATURES;
239 
240 	sc->vtrnd_features = virtio_negotiate_features(dev, features);
241 	return (virtio_finalize_features(dev));
242 }
243 
244 static int
245 vtrnd_setup_features(struct vtrnd_softc *sc)
246 {
247 	int error;
248 
249 	error = vtrnd_negotiate_features(sc);
250 	if (error)
251 		return (error);
252 
253 	return (0);
254 }
255 
256 static int
257 vtrnd_alloc_virtqueue(struct vtrnd_softc *sc)
258 {
259 	device_t dev;
260 	struct vq_alloc_info vq_info;
261 
262 	dev = sc->vtrnd_dev;
263 
264 	VQ_ALLOC_INFO_INIT(&vq_info, 0, NULL, sc, &sc->vtrnd_vq,
265 	    "%s request", device_get_nameunit(dev));
266 
267 	return (virtio_alloc_virtqueues(dev, 1, &vq_info));
268 }
269 
270 static void
271 vtrnd_enqueue(struct vtrnd_softc *sc)
272 {
273 	struct virtqueue *vq;
274 	int error __diagused;
275 
276 	vq = sc->vtrnd_vq;
277 
278 	KASSERT(virtqueue_empty(vq), ("%s: non-empty queue", __func__));
279 
280 	error = virtqueue_enqueue(vq, sc, sc->vtrnd_sg, 0, 1);
281 	KASSERT(error == 0, ("%s: virtqueue_enqueue returned error: %d",
282 	    __func__, error));
283 
284 	virtqueue_notify(vq);
285 }
286 
287 static int
288 vtrnd_harvest(struct vtrnd_softc *sc, void *buf, size_t *sz)
289 {
290 	struct virtqueue *vq;
291 	void *cookie;
292 	uint32_t rdlen;
293 
294 	if (sc->inactive)
295 		return (EDEADLK);
296 
297 	vq = sc->vtrnd_vq;
298 
299 	cookie = virtqueue_dequeue(vq, &rdlen);
300 	if (cookie == NULL)
301 		return (EAGAIN);
302 	KASSERT(cookie == sc, ("%s: cookie mismatch", __func__));
303 
304 	*sz = MIN(rdlen, *sz);
305 	memcpy(buf, sc->vtrnd_value, *sz);
306 
307 	vtrnd_enqueue(sc);
308 
309 	return (0);
310 }
311 
312 static unsigned
313 vtrnd_read(void *buf, unsigned usz)
314 {
315 	struct vtrnd_softc *sc;
316 	size_t sz;
317 	int error;
318 
319 	sc = g_vtrnd_softc;
320 	if (sc == NULL)
321 		return (0);
322 
323 	sz = usz;
324 	error = vtrnd_harvest(sc, buf, &sz);
325 	if (error != 0)
326 		return (0);
327 
328 	return (sz);
329 }
330