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