xref: /freebsd/sys/dev/virtio/random/virtio_random.c (revision fdafd315)
110c40180SBryan Venteicher /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
410c40180SBryan Venteicher  * Copyright (c) 2013, Bryan Venteicher <bryanv@FreeBSD.org>
510c40180SBryan Venteicher  * All rights reserved.
610c40180SBryan Venteicher  *
710c40180SBryan Venteicher  * Redistribution and use in source and binary forms, with or without
810c40180SBryan Venteicher  * modification, are permitted provided that the following conditions
910c40180SBryan Venteicher  * are met:
1010c40180SBryan Venteicher  * 1. Redistributions of source code must retain the above copyright
1110c40180SBryan Venteicher  *    notice unmodified, this list of conditions, and the following
1210c40180SBryan Venteicher  *    disclaimer.
1310c40180SBryan Venteicher  * 2. Redistributions in binary form must reproduce the above copyright
1410c40180SBryan Venteicher  *    notice, this list of conditions and the following disclaimer in the
1510c40180SBryan Venteicher  *    documentation and/or other materials provided with the distribution.
1610c40180SBryan Venteicher  *
1710c40180SBryan Venteicher  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1810c40180SBryan Venteicher  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1910c40180SBryan Venteicher  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2010c40180SBryan Venteicher  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2110c40180SBryan Venteicher  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2210c40180SBryan Venteicher  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2310c40180SBryan Venteicher  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2410c40180SBryan Venteicher  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2510c40180SBryan Venteicher  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2610c40180SBryan Venteicher  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2710c40180SBryan Venteicher  */
2810c40180SBryan Venteicher 
2910c40180SBryan Venteicher /* Driver for VirtIO entropy device. */
3010c40180SBryan Venteicher 
3110c40180SBryan Venteicher #include <sys/param.h>
32adbf7727SEugene Grosbein #include <sys/types.h>
33adbf7727SEugene Grosbein #include <sys/eventhandler.h>
3410c40180SBryan Venteicher #include <sys/kernel.h>
35ad5979f7SConrad Meyer #include <sys/malloc.h>
3610c40180SBryan Venteicher #include <sys/module.h>
3710c40180SBryan Venteicher #include <sys/sglist.h>
3810c40180SBryan Venteicher #include <sys/random.h>
39ad5979f7SConrad Meyer #include <sys/stdatomic.h>
4010c40180SBryan Venteicher 
4110c40180SBryan Venteicher #include <machine/bus.h>
4210c40180SBryan Venteicher #include <machine/resource.h>
4310c40180SBryan Venteicher #include <sys/bus.h>
4410c40180SBryan Venteicher 
45ad5979f7SConrad Meyer #include <dev/random/randomdev.h>
46ad5979f7SConrad Meyer #include <dev/random/random_harvestq.h>
4710c40180SBryan Venteicher #include <dev/virtio/virtio.h>
4810c40180SBryan Venteicher #include <dev/virtio/virtqueue.h>
4910c40180SBryan Venteicher 
5010c40180SBryan Venteicher struct vtrnd_softc {
51e6cc42f1SBryan Venteicher 	device_t		 vtrnd_dev;
5210c40180SBryan Venteicher 	uint64_t		 vtrnd_features;
5310c40180SBryan Venteicher 	struct virtqueue	*vtrnd_vq;
54adbf7727SEugene Grosbein 	eventhandler_tag	 eh;
55adbf7727SEugene Grosbein 	bool			 inactive;
56f1c5a2e3SJohn-Mark Gurney 	struct sglist		 *vtrnd_sg;
57f1c5a2e3SJohn-Mark Gurney 	uint32_t		 *vtrnd_value;
5810c40180SBryan Venteicher };
5910c40180SBryan Venteicher 
6010c40180SBryan Venteicher static int	vtrnd_modevent(module_t, int, void *);
6110c40180SBryan Venteicher 
6210c40180SBryan Venteicher static int	vtrnd_probe(device_t);
6310c40180SBryan Venteicher static int	vtrnd_attach(device_t);
6410c40180SBryan Venteicher static int	vtrnd_detach(device_t);
65adbf7727SEugene Grosbein static int	vtrnd_shutdown(device_t);
6610c40180SBryan Venteicher 
67e6cc42f1SBryan Venteicher static int	vtrnd_negotiate_features(struct vtrnd_softc *);
68e6cc42f1SBryan Venteicher static int	vtrnd_setup_features(struct vtrnd_softc *);
69e6cc42f1SBryan Venteicher static int	vtrnd_alloc_virtqueue(struct vtrnd_softc *);
70ad5979f7SConrad Meyer static int	vtrnd_harvest(struct vtrnd_softc *, void *, size_t *);
71f1c5a2e3SJohn-Mark Gurney static void	vtrnd_enqueue(struct vtrnd_softc *sc);
72ad5979f7SConrad Meyer static unsigned	vtrnd_read(void *, unsigned);
7310c40180SBryan Venteicher 
7410c40180SBryan Venteicher #define VTRND_FEATURES	0
7510c40180SBryan Venteicher 
7610c40180SBryan Venteicher static struct virtio_feature_desc vtrnd_feature_desc[] = {
7710c40180SBryan Venteicher 	{ 0, NULL }
7810c40180SBryan Venteicher };
7910c40180SBryan Venteicher 
80ad5979f7SConrad Meyer static struct random_source random_vtrnd = {
81ad5979f7SConrad Meyer 	.rs_ident = "VirtIO Entropy Adapter",
82ad5979f7SConrad Meyer 	.rs_source = RANDOM_PURE_VIRTIO,
83ad5979f7SConrad Meyer 	.rs_read = vtrnd_read,
84ad5979f7SConrad Meyer };
85ad5979f7SConrad Meyer 
86ad5979f7SConrad Meyer /* Kludge for API limitations of random(4). */
87ad5979f7SConrad Meyer static _Atomic(struct vtrnd_softc *) g_vtrnd_softc;
88ad5979f7SConrad Meyer 
8910c40180SBryan Venteicher static device_method_t vtrnd_methods[] = {
9010c40180SBryan Venteicher 	/* Device methods. */
9110c40180SBryan Venteicher 	DEVMETHOD(device_probe,		vtrnd_probe),
9210c40180SBryan Venteicher 	DEVMETHOD(device_attach,	vtrnd_attach),
9310c40180SBryan Venteicher 	DEVMETHOD(device_detach,	vtrnd_detach),
94adbf7727SEugene Grosbein 	DEVMETHOD(device_shutdown,	vtrnd_shutdown),
9510c40180SBryan Venteicher 
9610c40180SBryan Venteicher 	DEVMETHOD_END
9710c40180SBryan Venteicher };
9810c40180SBryan Venteicher 
9910c40180SBryan Venteicher static driver_t vtrnd_driver = {
10010c40180SBryan Venteicher 	"vtrnd",
10110c40180SBryan Venteicher 	vtrnd_methods,
10210c40180SBryan Venteicher 	sizeof(struct vtrnd_softc)
10310c40180SBryan Venteicher };
10410c40180SBryan Venteicher 
1055c4c96d3SJohn Baldwin VIRTIO_DRIVER_MODULE(virtio_random, vtrnd_driver, vtrnd_modevent, NULL);
10610c40180SBryan Venteicher MODULE_VERSION(virtio_random, 1);
10710c40180SBryan Venteicher MODULE_DEPEND(virtio_random, virtio, 1, 1, 1);
1085ca5dfe9SConrad Meyer MODULE_DEPEND(virtio_random, random_device, 1, 1, 1);
10910c40180SBryan Venteicher 
110633218eeSJessica Clarke VIRTIO_SIMPLE_PNPINFO(virtio_random, VIRTIO_ID_ENTROPY,
1110f6040f0SConrad Meyer     "VirtIO Entropy Adapter");
1120f6040f0SConrad Meyer 
11310c40180SBryan Venteicher static int
vtrnd_modevent(module_t mod,int type,void * unused)11410c40180SBryan Venteicher vtrnd_modevent(module_t mod, int type, void *unused)
11510c40180SBryan Venteicher {
11610c40180SBryan Venteicher 	int error;
11710c40180SBryan Venteicher 
11810c40180SBryan Venteicher 	switch (type) {
11910c40180SBryan Venteicher 	case MOD_LOAD:
12010c40180SBryan Venteicher 	case MOD_QUIESCE:
12110c40180SBryan Venteicher 	case MOD_UNLOAD:
12210c40180SBryan Venteicher 	case MOD_SHUTDOWN:
12310c40180SBryan Venteicher 		error = 0;
12410c40180SBryan Venteicher 		break;
12510c40180SBryan Venteicher 	default:
12610c40180SBryan Venteicher 		error = EOPNOTSUPP;
12710c40180SBryan Venteicher 		break;
12810c40180SBryan Venteicher 	}
12910c40180SBryan Venteicher 
13010c40180SBryan Venteicher 	return (error);
13110c40180SBryan Venteicher }
13210c40180SBryan Venteicher 
13310c40180SBryan Venteicher static int
vtrnd_probe(device_t dev)13410c40180SBryan Venteicher vtrnd_probe(device_t dev)
13510c40180SBryan Venteicher {
1360f6040f0SConrad Meyer 	return (VIRTIO_SIMPLE_PROBE(dev, virtio_random));
13710c40180SBryan Venteicher }
13810c40180SBryan Venteicher 
13910c40180SBryan Venteicher static int
vtrnd_attach(device_t dev)14010c40180SBryan Venteicher vtrnd_attach(device_t dev)
14110c40180SBryan Venteicher {
142ad5979f7SConrad Meyer 	struct vtrnd_softc *sc, *exp;
143f1c5a2e3SJohn-Mark Gurney 	size_t len;
14410c40180SBryan Venteicher 	int error;
14510c40180SBryan Venteicher 
14610c40180SBryan Venteicher 	sc = device_get_softc(dev);
147e6cc42f1SBryan Venteicher 	sc->vtrnd_dev = dev;
14810c40180SBryan Venteicher 	virtio_set_feature_desc(dev, vtrnd_feature_desc);
14910c40180SBryan Venteicher 
150f1c5a2e3SJohn-Mark Gurney 	len = sizeof(*sc->vtrnd_value) * HARVESTSIZE;
151f1c5a2e3SJohn-Mark Gurney 	sc->vtrnd_value = malloc_aligned(len, len, M_DEVBUF, M_WAITOK);
152f1c5a2e3SJohn-Mark Gurney 	sc->vtrnd_sg = sglist_build(sc->vtrnd_value, len, M_WAITOK);
153f1c5a2e3SJohn-Mark Gurney 
154e6cc42f1SBryan Venteicher 	error = vtrnd_setup_features(sc);
155e6cc42f1SBryan Venteicher 	if (error) {
156e6cc42f1SBryan Venteicher 		device_printf(dev, "cannot setup features\n");
157e6cc42f1SBryan Venteicher 		goto fail;
158e6cc42f1SBryan Venteicher 	}
159e6cc42f1SBryan Venteicher 
160e6cc42f1SBryan Venteicher 	error = vtrnd_alloc_virtqueue(sc);
16110c40180SBryan Venteicher 	if (error) {
16210c40180SBryan Venteicher 		device_printf(dev, "cannot allocate virtqueue\n");
16310c40180SBryan Venteicher 		goto fail;
16410c40180SBryan Venteicher 	}
16510c40180SBryan Venteicher 
166ad5979f7SConrad Meyer 	exp = NULL;
167ad5979f7SConrad Meyer 	if (!atomic_compare_exchange_strong_explicit(&g_vtrnd_softc, &exp, sc,
168ad5979f7SConrad Meyer 	    memory_order_release, memory_order_acquire)) {
169ad5979f7SConrad Meyer 		error = EEXIST;
170ad5979f7SConrad Meyer 		goto fail;
171ad5979f7SConrad Meyer 	}
172adbf7727SEugene Grosbein 
173adbf7727SEugene Grosbein 	sc->eh = EVENTHANDLER_REGISTER(shutdown_post_sync,
174adbf7727SEugene Grosbein 		vtrnd_shutdown, dev, SHUTDOWN_PRI_LAST + 1); /* ??? */
175adbf7727SEugene Grosbein 	if (sc->eh == NULL) {
176adbf7727SEugene Grosbein 		device_printf(dev, "Shutdown event registration failed\n");
177adbf7727SEugene Grosbein 		error = ENXIO;
178adbf7727SEugene Grosbein 		goto fail;
179adbf7727SEugene Grosbein 	}
180adbf7727SEugene Grosbein 
181adbf7727SEugene Grosbein 	sc->inactive = false;
182ad5979f7SConrad Meyer 	random_source_register(&random_vtrnd);
18310c40180SBryan Venteicher 
184f1c5a2e3SJohn-Mark Gurney 	vtrnd_enqueue(sc);
185f1c5a2e3SJohn-Mark Gurney 
18610c40180SBryan Venteicher fail:
18710c40180SBryan Venteicher 	if (error)
18810c40180SBryan Venteicher 		vtrnd_detach(dev);
18910c40180SBryan Venteicher 
19010c40180SBryan Venteicher 	return (error);
19110c40180SBryan Venteicher }
19210c40180SBryan Venteicher 
19310c40180SBryan Venteicher static int
vtrnd_detach(device_t dev)19410c40180SBryan Venteicher vtrnd_detach(device_t dev)
19510c40180SBryan Venteicher {
19610c40180SBryan Venteicher 	struct vtrnd_softc *sc;
197f1c5a2e3SJohn-Mark Gurney 	uint32_t rdlen;
19810c40180SBryan Venteicher 
19910c40180SBryan Venteicher 	sc = device_get_softc(dev);
200ad5979f7SConrad Meyer 	KASSERT(
201ad5979f7SConrad Meyer 	    atomic_load_explicit(&g_vtrnd_softc, memory_order_acquire) == sc,
202ad5979f7SConrad Meyer 	    ("only one global instance at a time"));
20310c40180SBryan Venteicher 
204adbf7727SEugene Grosbein 	sc->inactive = true;
205adbf7727SEugene Grosbein 	if (sc->eh != NULL) {
206adbf7727SEugene Grosbein 		EVENTHANDLER_DEREGISTER(shutdown_post_sync, sc->eh);
207adbf7727SEugene Grosbein 		sc->eh = NULL;
208adbf7727SEugene Grosbein 	}
209ad5979f7SConrad Meyer 	random_source_deregister(&random_vtrnd);
210f1c5a2e3SJohn-Mark Gurney 
211f1c5a2e3SJohn-Mark Gurney 	/* clear the queue */
212f1c5a2e3SJohn-Mark Gurney 	virtqueue_poll(sc->vtrnd_vq, &rdlen);
213f1c5a2e3SJohn-Mark Gurney 
214ad5979f7SConrad Meyer 	atomic_store_explicit(&g_vtrnd_softc, NULL, memory_order_release);
215f1c5a2e3SJohn-Mark Gurney 	sglist_free(sc->vtrnd_sg);
216f1c5a2e3SJohn-Mark Gurney 	zfree(sc->vtrnd_value, M_DEVBUF);
21710c40180SBryan Venteicher 	return (0);
21810c40180SBryan Venteicher }
21910c40180SBryan Venteicher 
220e6cc42f1SBryan Venteicher static int
vtrnd_shutdown(device_t dev)221adbf7727SEugene Grosbein vtrnd_shutdown(device_t dev)
222adbf7727SEugene Grosbein {
223adbf7727SEugene Grosbein 	struct vtrnd_softc *sc;
224adbf7727SEugene Grosbein 
225adbf7727SEugene Grosbein 	sc = device_get_softc(dev);
226adbf7727SEugene Grosbein 	sc->inactive = true;
227adbf7727SEugene Grosbein 
228adbf7727SEugene Grosbein 	return(0);
229adbf7727SEugene Grosbein }
230adbf7727SEugene Grosbein 
231adbf7727SEugene Grosbein static int
vtrnd_negotiate_features(struct vtrnd_softc * sc)232e6cc42f1SBryan Venteicher vtrnd_negotiate_features(struct vtrnd_softc *sc)
23310c40180SBryan Venteicher {
234e6cc42f1SBryan Venteicher 	device_t dev;
235e6cc42f1SBryan Venteicher 	uint64_t features;
23610c40180SBryan Venteicher 
237e6cc42f1SBryan Venteicher 	dev = sc->vtrnd_dev;
238e6cc42f1SBryan Venteicher 	features = VTRND_FEATURES;
239e6cc42f1SBryan Venteicher 
240e6cc42f1SBryan Venteicher 	sc->vtrnd_features = virtio_negotiate_features(dev, features);
241e6cc42f1SBryan Venteicher 	return (virtio_finalize_features(dev));
24210c40180SBryan Venteicher }
24310c40180SBryan Venteicher 
24410c40180SBryan Venteicher static int
vtrnd_setup_features(struct vtrnd_softc * sc)245e6cc42f1SBryan Venteicher vtrnd_setup_features(struct vtrnd_softc *sc)
24610c40180SBryan Venteicher {
247e6cc42f1SBryan Venteicher 	int error;
248e6cc42f1SBryan Venteicher 
249e6cc42f1SBryan Venteicher 	error = vtrnd_negotiate_features(sc);
250e6cc42f1SBryan Venteicher 	if (error)
251e6cc42f1SBryan Venteicher 		return (error);
252e6cc42f1SBryan Venteicher 
253e6cc42f1SBryan Venteicher 	return (0);
254e6cc42f1SBryan Venteicher }
255e6cc42f1SBryan Venteicher 
256e6cc42f1SBryan Venteicher static int
vtrnd_alloc_virtqueue(struct vtrnd_softc * sc)257e6cc42f1SBryan Venteicher vtrnd_alloc_virtqueue(struct vtrnd_softc *sc)
258e6cc42f1SBryan Venteicher {
259e6cc42f1SBryan Venteicher 	device_t dev;
26010c40180SBryan Venteicher 	struct vq_alloc_info vq_info;
26110c40180SBryan Venteicher 
262e6cc42f1SBryan Venteicher 	dev = sc->vtrnd_dev;
26310c40180SBryan Venteicher 
26410c40180SBryan Venteicher 	VQ_ALLOC_INFO_INIT(&vq_info, 0, NULL, sc, &sc->vtrnd_vq,
26510c40180SBryan Venteicher 	    "%s request", device_get_nameunit(dev));
26610c40180SBryan Venteicher 
267180c0240SMina Galić 	return (virtio_alloc_virtqueues(dev, 1, &vq_info));
26810c40180SBryan Venteicher }
26910c40180SBryan Venteicher 
270f1c5a2e3SJohn-Mark Gurney static void
vtrnd_enqueue(struct vtrnd_softc * sc)271f1c5a2e3SJohn-Mark Gurney vtrnd_enqueue(struct vtrnd_softc *sc)
272f1c5a2e3SJohn-Mark Gurney {
273f1c5a2e3SJohn-Mark Gurney 	struct virtqueue *vq;
274f1c5a2e3SJohn-Mark Gurney 	int error __diagused;
275f1c5a2e3SJohn-Mark Gurney 
276f1c5a2e3SJohn-Mark Gurney 	vq = sc->vtrnd_vq;
277f1c5a2e3SJohn-Mark Gurney 
278f1c5a2e3SJohn-Mark Gurney 	KASSERT(virtqueue_empty(vq), ("%s: non-empty queue", __func__));
279f1c5a2e3SJohn-Mark Gurney 
280f1c5a2e3SJohn-Mark Gurney 	error = virtqueue_enqueue(vq, sc, sc->vtrnd_sg, 0, 1);
281f1c5a2e3SJohn-Mark Gurney 	KASSERT(error == 0, ("%s: virtqueue_enqueue returned error: %d",
282f1c5a2e3SJohn-Mark Gurney 	    __func__, error));
283f1c5a2e3SJohn-Mark Gurney 
284f1c5a2e3SJohn-Mark Gurney 	virtqueue_notify(vq);
285f1c5a2e3SJohn-Mark Gurney }
286f1c5a2e3SJohn-Mark Gurney 
287ad5979f7SConrad Meyer static int
vtrnd_harvest(struct vtrnd_softc * sc,void * buf,size_t * sz)288ad5979f7SConrad Meyer vtrnd_harvest(struct vtrnd_softc *sc, void *buf, size_t *sz)
28910c40180SBryan Venteicher {
29010c40180SBryan Venteicher 	struct virtqueue *vq;
291f1c5a2e3SJohn-Mark Gurney 	void *cookie;
292ad5979f7SConrad Meyer 	uint32_t rdlen;
29310c40180SBryan Venteicher 
294adbf7727SEugene Grosbein 	if (sc->inactive)
295adbf7727SEugene Grosbein 		return (EDEADLK);
296adbf7727SEugene Grosbein 
297ad5979f7SConrad Meyer 	vq = sc->vtrnd_vq;
298ad5979f7SConrad Meyer 
299f1c5a2e3SJohn-Mark Gurney 	cookie = virtqueue_dequeue(vq, &rdlen);
300f1c5a2e3SJohn-Mark Gurney 	if (cookie == NULL)
301ad5979f7SConrad Meyer 		return (EAGAIN);
302f1c5a2e3SJohn-Mark Gurney 	KASSERT(cookie == sc, ("%s: cookie mismatch", __func__));
303f1c5a2e3SJohn-Mark Gurney 
304ad5979f7SConrad Meyer 	*sz = MIN(rdlen, *sz);
305f1c5a2e3SJohn-Mark Gurney 	memcpy(buf, sc->vtrnd_value, *sz);
306f1c5a2e3SJohn-Mark Gurney 
307f1c5a2e3SJohn-Mark Gurney 	vtrnd_enqueue(sc);
308f1c5a2e3SJohn-Mark Gurney 
309ad5979f7SConrad Meyer 	return (0);
31010c40180SBryan Venteicher }
31110c40180SBryan Venteicher 
312ad5979f7SConrad Meyer static unsigned
vtrnd_read(void * buf,unsigned usz)313ad5979f7SConrad Meyer vtrnd_read(void *buf, unsigned usz)
31410c40180SBryan Venteicher {
31510c40180SBryan Venteicher 	struct vtrnd_softc *sc;
316ad5979f7SConrad Meyer 	size_t sz;
317ad5979f7SConrad Meyer 	int error;
31810c40180SBryan Venteicher 
319ad5979f7SConrad Meyer 	sc = g_vtrnd_softc;
320ad5979f7SConrad Meyer 	if (sc == NULL)
321ad5979f7SConrad Meyer 		return (0);
32210c40180SBryan Venteicher 
323ad5979f7SConrad Meyer 	sz = usz;
324ad5979f7SConrad Meyer 	error = vtrnd_harvest(sc, buf, &sz);
325ad5979f7SConrad Meyer 	if (error != 0)
326ad5979f7SConrad Meyer 		return (0);
327ad5979f7SConrad Meyer 
328ad5979f7SConrad Meyer 	return (sz);
32910c40180SBryan Venteicher }
330