1260847Sbryanv/*- 2260847Sbryanv * Copyright (c) 2013, Bryan Venteicher <bryanv@FreeBSD.org> 3260847Sbryanv * All rights reserved. 4260847Sbryanv * 5260847Sbryanv * Redistribution and use in source and binary forms, with or without 6260847Sbryanv * modification, are permitted provided that the following conditions 7260847Sbryanv * are met: 8260847Sbryanv * 1. Redistributions of source code must retain the above copyright 9260847Sbryanv * notice unmodified, this list of conditions, and the following 10260847Sbryanv * disclaimer. 11260847Sbryanv * 2. Redistributions in binary form must reproduce the above copyright 12260847Sbryanv * notice, this list of conditions and the following disclaimer in the 13260847Sbryanv * documentation and/or other materials provided with the distribution. 14260847Sbryanv * 15260847Sbryanv * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16260847Sbryanv * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17260847Sbryanv * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18260847Sbryanv * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19260847Sbryanv * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20260847Sbryanv * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21260847Sbryanv * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22260847Sbryanv * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23260847Sbryanv * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24260847Sbryanv * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25260847Sbryanv */ 26260847Sbryanv 27260847Sbryanv/* Driver for VirtIO entropy device. */ 28260847Sbryanv 29260847Sbryanv#include <sys/cdefs.h> 30260847Sbryanv__FBSDID("$FreeBSD: releng/10.3/sys/dev/virtio/random/virtio_random.c 268933 2014-07-21 00:21:56Z jhb $"); 31260847Sbryanv 32260847Sbryanv#include <sys/param.h> 33260847Sbryanv#include <sys/kernel.h> 34260847Sbryanv#include <sys/module.h> 35260847Sbryanv#include <sys/sglist.h> 36260847Sbryanv#include <sys/callout.h> 37260847Sbryanv#include <sys/random.h> 38260847Sbryanv 39260847Sbryanv#include <machine/bus.h> 40260847Sbryanv#include <machine/resource.h> 41260847Sbryanv#include <sys/bus.h> 42260847Sbryanv 43260847Sbryanv#include <dev/virtio/virtio.h> 44260847Sbryanv#include <dev/virtio/virtqueue.h> 45260847Sbryanv 46260847Sbryanvstruct vtrnd_softc { 47260847Sbryanv device_t vtrnd_dev; 48260847Sbryanv uint64_t vtrnd_features; 49260847Sbryanv struct callout vtrnd_callout; 50260847Sbryanv struct virtqueue *vtrnd_vq; 51260847Sbryanv}; 52260847Sbryanv 53260847Sbryanvstatic int vtrnd_modevent(module_t, int, void *); 54260847Sbryanv 55260847Sbryanvstatic int vtrnd_probe(device_t); 56260847Sbryanvstatic int vtrnd_attach(device_t); 57260847Sbryanvstatic int vtrnd_detach(device_t); 58260847Sbryanv 59260847Sbryanvstatic void vtrnd_negotiate_features(struct vtrnd_softc *); 60260847Sbryanvstatic int vtrnd_alloc_virtqueue(struct vtrnd_softc *); 61260847Sbryanvstatic void vtrnd_harvest(struct vtrnd_softc *); 62260847Sbryanvstatic void vtrnd_timer(void *); 63260847Sbryanv 64260847Sbryanv#define VTRND_FEATURES 0 65260847Sbryanv 66260847Sbryanvstatic struct virtio_feature_desc vtrnd_feature_desc[] = { 67260847Sbryanv { 0, NULL } 68260847Sbryanv}; 69260847Sbryanv 70260847Sbryanvstatic device_method_t vtrnd_methods[] = { 71260847Sbryanv /* Device methods. */ 72260847Sbryanv DEVMETHOD(device_probe, vtrnd_probe), 73260847Sbryanv DEVMETHOD(device_attach, vtrnd_attach), 74260847Sbryanv DEVMETHOD(device_detach, vtrnd_detach), 75260847Sbryanv 76260847Sbryanv DEVMETHOD_END 77260847Sbryanv}; 78260847Sbryanv 79260847Sbryanvstatic driver_t vtrnd_driver = { 80260847Sbryanv "vtrnd", 81260847Sbryanv vtrnd_methods, 82260847Sbryanv sizeof(struct vtrnd_softc) 83260847Sbryanv}; 84260847Sbryanvstatic devclass_t vtrnd_devclass; 85260847Sbryanv 86260847SbryanvDRIVER_MODULE(virtio_random, virtio_pci, vtrnd_driver, vtrnd_devclass, 87260847Sbryanv vtrnd_modevent, 0); 88260847SbryanvMODULE_VERSION(virtio_random, 1); 89260847SbryanvMODULE_DEPEND(virtio_random, virtio, 1, 1, 1); 90260847Sbryanv 91260847Sbryanvstatic int 92260847Sbryanvvtrnd_modevent(module_t mod, int type, void *unused) 93260847Sbryanv{ 94260847Sbryanv int error; 95260847Sbryanv 96260847Sbryanv switch (type) { 97260847Sbryanv case MOD_LOAD: 98260847Sbryanv case MOD_QUIESCE: 99260847Sbryanv case MOD_UNLOAD: 100260847Sbryanv case MOD_SHUTDOWN: 101260847Sbryanv error = 0; 102260847Sbryanv break; 103260847Sbryanv default: 104260847Sbryanv error = EOPNOTSUPP; 105260847Sbryanv break; 106260847Sbryanv } 107260847Sbryanv 108260847Sbryanv return (error); 109260847Sbryanv} 110260847Sbryanv 111260847Sbryanvstatic int 112260847Sbryanvvtrnd_probe(device_t dev) 113260847Sbryanv{ 114260847Sbryanv 115260847Sbryanv if (virtio_get_device_type(dev) != VIRTIO_ID_ENTROPY) 116260847Sbryanv return (ENXIO); 117260847Sbryanv 118260847Sbryanv device_set_desc(dev, "VirtIO Entropy Adapter"); 119260847Sbryanv 120260847Sbryanv return (BUS_PROBE_DEFAULT); 121260847Sbryanv} 122260847Sbryanv 123260847Sbryanvstatic int 124260847Sbryanvvtrnd_attach(device_t dev) 125260847Sbryanv{ 126260847Sbryanv struct vtrnd_softc *sc; 127260847Sbryanv int error; 128260847Sbryanv 129260847Sbryanv sc = device_get_softc(dev); 130260847Sbryanv sc->vtrnd_dev = dev; 131260847Sbryanv 132260847Sbryanv callout_init(&sc->vtrnd_callout, CALLOUT_MPSAFE); 133260847Sbryanv 134260847Sbryanv virtio_set_feature_desc(dev, vtrnd_feature_desc); 135260847Sbryanv vtrnd_negotiate_features(sc); 136260847Sbryanv 137260847Sbryanv error = vtrnd_alloc_virtqueue(sc); 138260847Sbryanv if (error) { 139260847Sbryanv device_printf(dev, "cannot allocate virtqueue\n"); 140260847Sbryanv goto fail; 141260847Sbryanv } 142260847Sbryanv 143260847Sbryanv callout_reset(&sc->vtrnd_callout, 5 * hz, vtrnd_timer, sc); 144260847Sbryanv 145260847Sbryanvfail: 146260847Sbryanv if (error) 147260847Sbryanv vtrnd_detach(dev); 148260847Sbryanv 149260847Sbryanv return (error); 150260847Sbryanv} 151260847Sbryanv 152260847Sbryanvstatic int 153260847Sbryanvvtrnd_detach(device_t dev) 154260847Sbryanv{ 155260847Sbryanv struct vtrnd_softc *sc; 156260847Sbryanv 157260847Sbryanv sc = device_get_softc(dev); 158260847Sbryanv 159268933Sjhb callout_drain(&sc->vtrnd_callout); 160260847Sbryanv 161260847Sbryanv return (0); 162260847Sbryanv} 163260847Sbryanv 164260847Sbryanvstatic void 165260847Sbryanvvtrnd_negotiate_features(struct vtrnd_softc *sc) 166260847Sbryanv{ 167260847Sbryanv device_t dev; 168260847Sbryanv uint64_t features; 169260847Sbryanv 170260847Sbryanv dev = sc->vtrnd_dev; 171260847Sbryanv features = VTRND_FEATURES; 172260847Sbryanv 173260847Sbryanv sc->vtrnd_features = virtio_negotiate_features(dev, features); 174260847Sbryanv} 175260847Sbryanv 176260847Sbryanvstatic int 177260847Sbryanvvtrnd_alloc_virtqueue(struct vtrnd_softc *sc) 178260847Sbryanv{ 179260847Sbryanv device_t dev; 180260847Sbryanv struct vq_alloc_info vq_info; 181260847Sbryanv 182260847Sbryanv dev = sc->vtrnd_dev; 183260847Sbryanv 184260847Sbryanv VQ_ALLOC_INFO_INIT(&vq_info, 0, NULL, sc, &sc->vtrnd_vq, 185260847Sbryanv "%s request", device_get_nameunit(dev)); 186260847Sbryanv 187260847Sbryanv return (virtio_alloc_virtqueues(dev, 0, 1, &vq_info)); 188260847Sbryanv} 189260847Sbryanv 190260847Sbryanvstatic void 191260847Sbryanvvtrnd_harvest(struct vtrnd_softc *sc) 192260847Sbryanv{ 193260847Sbryanv struct sglist_seg segs[1]; 194260847Sbryanv struct sglist sg; 195260847Sbryanv struct virtqueue *vq; 196260847Sbryanv uint32_t value; 197260847Sbryanv int error; 198260847Sbryanv 199260847Sbryanv vq = sc->vtrnd_vq; 200260847Sbryanv 201260847Sbryanv sglist_init(&sg, 1, segs); 202260847Sbryanv error = sglist_append(&sg, &value, sizeof(value)); 203260847Sbryanv KASSERT(error == 0 && sg.sg_nseg == 1, 204260847Sbryanv ("%s: error %d adding buffer to sglist", __func__, error)); 205260847Sbryanv 206260847Sbryanv if (!virtqueue_empty(vq)) 207260847Sbryanv return; 208260847Sbryanv if (virtqueue_enqueue(vq, &value, &sg, 0, 1) != 0) 209260847Sbryanv return; 210260847Sbryanv 211260847Sbryanv /* 212260847Sbryanv * Poll for the response, but the command is likely already 213260847Sbryanv * done when we return from the notify. 214260847Sbryanv */ 215260847Sbryanv virtqueue_notify(vq); 216260847Sbryanv virtqueue_poll(vq, NULL); 217260847Sbryanv 218260847Sbryanv random_harvest(&value, sizeof(value), sizeof(value) * NBBY / 2, 219260847Sbryanv RANDOM_PURE_VIRTIO); 220260847Sbryanv} 221260847Sbryanv 222260847Sbryanvstatic void 223260847Sbryanvvtrnd_timer(void *xsc) 224260847Sbryanv{ 225260847Sbryanv struct vtrnd_softc *sc; 226260847Sbryanv 227260847Sbryanv sc = xsc; 228260847Sbryanv 229260847Sbryanv vtrnd_harvest(sc); 230260847Sbryanv callout_schedule(&sc->vtrnd_callout, 5 * hz); 231260847Sbryanv} 232