The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/virtio/random/virtio_random.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    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/cdefs.h>
   32 __FBSDID("$FreeBSD$");
   33 
   34 #include <sys/param.h>
   35 #include <sys/types.h>
   36 #include <sys/eventhandler.h>
   37 #include <sys/kernel.h>
   38 #include <sys/malloc.h>
   39 #include <sys/module.h>
   40 #include <sys/sglist.h>
   41 #include <sys/random.h>
   42 #include <sys/stdatomic.h>
   43 
   44 #include <machine/bus.h>
   45 #include <machine/resource.h>
   46 #include <sys/bus.h>
   47 
   48 #include <dev/random/randomdev.h>
   49 #include <dev/random/random_harvestq.h>
   50 #include <dev/virtio/virtio.h>
   51 #include <dev/virtio/virtqueue.h>
   52 
   53 struct vtrnd_softc {
   54         device_t                 vtrnd_dev;
   55         uint64_t                 vtrnd_features;
   56         struct virtqueue        *vtrnd_vq;
   57         eventhandler_tag         eh;
   58         bool                     inactive;
   59 };
   60 
   61 static int      vtrnd_modevent(module_t, int, void *);
   62 
   63 static int      vtrnd_probe(device_t);
   64 static int      vtrnd_attach(device_t);
   65 static int      vtrnd_detach(device_t);
   66 static int      vtrnd_shutdown(device_t);
   67 
   68 static int      vtrnd_negotiate_features(struct vtrnd_softc *);
   69 static int      vtrnd_setup_features(struct vtrnd_softc *);
   70 static int      vtrnd_alloc_virtqueue(struct vtrnd_softc *);
   71 static int      vtrnd_harvest(struct vtrnd_softc *, void *, size_t *);
   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         int error;
  144 
  145         sc = device_get_softc(dev);
  146         sc->vtrnd_dev = dev;
  147         virtio_set_feature_desc(dev, vtrnd_feature_desc);
  148 
  149         error = vtrnd_setup_features(sc);
  150         if (error) {
  151                 device_printf(dev, "cannot setup features\n");
  152                 goto fail;
  153         }
  154 
  155         error = vtrnd_alloc_virtqueue(sc);
  156         if (error) {
  157                 device_printf(dev, "cannot allocate virtqueue\n");
  158                 goto fail;
  159         }
  160 
  161         exp = NULL;
  162         if (!atomic_compare_exchange_strong_explicit(&g_vtrnd_softc, &exp, sc,
  163             memory_order_release, memory_order_acquire)) {
  164                 error = EEXIST;
  165                 goto fail;
  166         }
  167 
  168         sc->eh = EVENTHANDLER_REGISTER(shutdown_post_sync,
  169                 vtrnd_shutdown, dev, SHUTDOWN_PRI_LAST + 1); /* ??? */
  170         if (sc->eh == NULL) {
  171                 device_printf(dev, "Shutdown event registration failed\n");
  172                 error = ENXIO;
  173                 goto fail;
  174         }
  175 
  176         sc->inactive = false;
  177         random_source_register(&random_vtrnd);
  178 
  179 fail:
  180         if (error)
  181                 vtrnd_detach(dev);
  182 
  183         return (error);
  184 }
  185 
  186 static int
  187 vtrnd_detach(device_t dev)
  188 {
  189         struct vtrnd_softc *sc;
  190 
  191         sc = device_get_softc(dev);
  192         KASSERT(
  193             atomic_load_explicit(&g_vtrnd_softc, memory_order_acquire) == sc,
  194             ("only one global instance at a time"));
  195 
  196         sc->inactive = true;
  197         if (sc->eh != NULL) {
  198                 EVENTHANDLER_DEREGISTER(shutdown_post_sync, sc->eh);
  199                 sc->eh = NULL;
  200         }
  201         random_source_deregister(&random_vtrnd);
  202         atomic_store_explicit(&g_vtrnd_softc, NULL, memory_order_release);
  203         return (0);
  204 }
  205 
  206 static int
  207 vtrnd_shutdown(device_t dev)
  208 {
  209         struct vtrnd_softc *sc;
  210 
  211         sc = device_get_softc(dev);
  212         sc->inactive = true;
  213 
  214         return(0);
  215 }
  216 
  217 static int
  218 vtrnd_negotiate_features(struct vtrnd_softc *sc)
  219 {
  220         device_t dev;
  221         uint64_t features;
  222 
  223         dev = sc->vtrnd_dev;
  224         features = VTRND_FEATURES;
  225 
  226         sc->vtrnd_features = virtio_negotiate_features(dev, features);
  227         return (virtio_finalize_features(dev));
  228 }
  229 
  230 static int
  231 vtrnd_setup_features(struct vtrnd_softc *sc)
  232 {
  233         int error;
  234 
  235         error = vtrnd_negotiate_features(sc);
  236         if (error)
  237                 return (error);
  238 
  239         return (0);
  240 }
  241 
  242 static int
  243 vtrnd_alloc_virtqueue(struct vtrnd_softc *sc)
  244 {
  245         device_t dev;
  246         struct vq_alloc_info vq_info;
  247 
  248         dev = sc->vtrnd_dev;
  249 
  250         VQ_ALLOC_INFO_INIT(&vq_info, 0, NULL, sc, &sc->vtrnd_vq,
  251             "%s request", device_get_nameunit(dev));
  252 
  253         return (virtio_alloc_virtqueues(dev, 0, 1, &vq_info));
  254 }
  255 
  256 static int
  257 vtrnd_harvest(struct vtrnd_softc *sc, void *buf, size_t *sz)
  258 {
  259         struct sglist_seg segs[1];
  260         struct sglist sg;
  261         struct virtqueue *vq;
  262         uint32_t value[HARVESTSIZE] __aligned(sizeof(uint32_t) * HARVESTSIZE);
  263         uint32_t rdlen;
  264         int error;
  265 
  266         _Static_assert(sizeof(value) < PAGE_SIZE, "sglist assumption");
  267 
  268         if (sc->inactive)
  269                 return (EDEADLK);
  270 
  271         sglist_init(&sg, 1, segs);
  272         error = sglist_append(&sg, value, *sz);
  273         if (error != 0)
  274                 panic("%s: sglist_append error=%d", __func__, error);
  275 
  276         vq = sc->vtrnd_vq;
  277         KASSERT(virtqueue_empty(vq), ("%s: non-empty queue", __func__));
  278 
  279         error = virtqueue_enqueue(vq, buf, &sg, 0, 1);
  280         if (error != 0)
  281                 return (error);
  282 
  283         /*
  284          * Poll for the response, but the command is likely already
  285          * done when we return from the notify.
  286          */
  287         virtqueue_notify(vq);
  288         virtqueue_poll(vq, &rdlen);
  289 
  290         if (rdlen > *sz)
  291                 panic("%s: random device wrote %zu bytes beyond end of provided"
  292                     " buffer %p:%zu", __func__, (size_t)rdlen - *sz,
  293                     (void *)value, *sz);
  294         else if (rdlen == 0)
  295                 return (EAGAIN);
  296         *sz = MIN(rdlen, *sz);
  297         memcpy(buf, value, *sz);
  298         explicit_bzero(value, *sz);
  299         return (0);
  300 }
  301 
  302 static unsigned
  303 vtrnd_read(void *buf, unsigned usz)
  304 {
  305         struct vtrnd_softc *sc;
  306         size_t sz;
  307         int error;
  308 
  309         sc = g_vtrnd_softc;
  310         if (sc == NULL)
  311                 return (0);
  312 
  313         sz = usz;
  314         error = vtrnd_harvest(sc, buf, &sz);
  315         if (error != 0)
  316                 return (0);
  317 
  318         return (sz);
  319 }

Cache object: b791ac95156dccf63f1a6e66de02f9b3


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.