1 /*- 2 * Copyright (c) 2013, Bryan Venteicher <bryanv@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 /* Driver for VirtIO entropy device. */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/module.h> 35 #include <sys/sglist.h> 36 #include <sys/callout.h> 37 #include <sys/random.h> 38 39 #include <machine/bus.h> 40 #include <machine/resource.h> 41 #include <sys/bus.h> 42 43 #include <dev/virtio/virtio.h> 44 #include <dev/virtio/virtqueue.h> 45 46 struct vtrnd_softc { 47 device_t vtrnd_dev; 48 uint64_t vtrnd_features; 49 struct callout vtrnd_callout; 50 struct virtqueue *vtrnd_vq; 51 }; 52 53 static int vtrnd_modevent(module_t, int, void *); 54 55 static int vtrnd_probe(device_t); 56 static int vtrnd_attach(device_t); 57 static int vtrnd_detach(device_t); 58 59 static void vtrnd_negotiate_features(struct vtrnd_softc *); 60 static int vtrnd_alloc_virtqueue(struct vtrnd_softc *); 61 static void vtrnd_harvest(struct vtrnd_softc *); 62 static void vtrnd_timer(void *); 63 64 #define VTRND_FEATURES 0 65 66 static struct virtio_feature_desc vtrnd_feature_desc[] = { 67 { 0, NULL } 68 }; 69 70 static device_method_t vtrnd_methods[] = { 71 /* Device methods. */ 72 DEVMETHOD(device_probe, vtrnd_probe), 73 DEVMETHOD(device_attach, vtrnd_attach), 74 DEVMETHOD(device_detach, vtrnd_detach), 75 76 DEVMETHOD_END 77 }; 78 79 static driver_t vtrnd_driver = { 80 "vtrnd", 81 vtrnd_methods, 82 sizeof(struct vtrnd_softc) 83 }; 84 static devclass_t vtrnd_devclass; 85 86 DRIVER_MODULE(virtio_random, virtio_pci, vtrnd_driver, vtrnd_devclass, 87 vtrnd_modevent, 0); 88 MODULE_VERSION(virtio_random, 1); 89 MODULE_DEPEND(virtio_random, virtio, 1, 1, 1); 90 91 static int 92 vtrnd_modevent(module_t mod, int type, void *unused) 93 { 94 int error; 95 96 switch (type) { 97 case MOD_LOAD: 98 case MOD_QUIESCE: 99 case MOD_UNLOAD: 100 case MOD_SHUTDOWN: 101 error = 0; 102 break; 103 default: 104 error = EOPNOTSUPP; 105 break; 106 } 107 108 return (error); 109 } 110 111 static int 112 vtrnd_probe(device_t dev) 113 { 114 115 if (virtio_get_device_type(dev) != VIRTIO_ID_ENTROPY) 116 return (ENXIO); 117 118 device_set_desc(dev, "VirtIO Entropy Adapter"); 119 120 return (BUS_PROBE_DEFAULT); 121 } 122 123 static int 124 vtrnd_attach(device_t dev) 125 { 126 struct vtrnd_softc *sc; 127 int error; 128 129 sc = device_get_softc(dev); 130 sc->vtrnd_dev = dev; 131 132 callout_init(&sc->vtrnd_callout, 1); 133 134 virtio_set_feature_desc(dev, vtrnd_feature_desc); 135 vtrnd_negotiate_features(sc); 136 137 error = vtrnd_alloc_virtqueue(sc); 138 if (error) { 139 device_printf(dev, "cannot allocate virtqueue\n"); 140 goto fail; 141 } 142 143 callout_reset(&sc->vtrnd_callout, 5 * hz, vtrnd_timer, sc); 144 145 fail: 146 if (error) 147 vtrnd_detach(dev); 148 149 return (error); 150 } 151 152 static int 153 vtrnd_detach(device_t dev) 154 { 155 struct vtrnd_softc *sc; 156 157 sc = device_get_softc(dev); 158 159 callout_drain(&sc->vtrnd_callout); 160 161 return (0); 162 } 163 164 static void 165 vtrnd_negotiate_features(struct vtrnd_softc *sc) 166 { 167 device_t dev; 168 uint64_t features; 169 170 dev = sc->vtrnd_dev; 171 features = VTRND_FEATURES; 172 173 sc->vtrnd_features = virtio_negotiate_features(dev, features); 174 } 175 176 static int 177 vtrnd_alloc_virtqueue(struct vtrnd_softc *sc) 178 { 179 device_t dev; 180 struct vq_alloc_info vq_info; 181 182 dev = sc->vtrnd_dev; 183 184 VQ_ALLOC_INFO_INIT(&vq_info, 0, NULL, sc, &sc->vtrnd_vq, 185 "%s request", device_get_nameunit(dev)); 186 187 return (virtio_alloc_virtqueues(dev, 0, 1, &vq_info)); 188 } 189 190 static void 191 vtrnd_harvest(struct vtrnd_softc *sc) 192 { 193 struct sglist_seg segs[1]; 194 struct sglist sg; 195 struct virtqueue *vq; 196 uint32_t value; 197 int error; 198 199 vq = sc->vtrnd_vq; 200 201 sglist_init(&sg, 1, segs); 202 error = sglist_append(&sg, &value, sizeof(value)); 203 KASSERT(error == 0 && sg.sg_nseg == 1, 204 ("%s: error %d adding buffer to sglist", __func__, error)); 205 206 if (!virtqueue_empty(vq)) 207 return; 208 if (virtqueue_enqueue(vq, &value, &sg, 0, 1) != 0) 209 return; 210 211 /* 212 * Poll for the response, but the command is likely already 213 * done when we return from the notify. 214 */ 215 virtqueue_notify(vq); 216 virtqueue_poll(vq, NULL); 217 218 random_harvest(&value, sizeof(value), sizeof(value) * NBBY / 2, 219 RANDOM_PURE_VIRTIO); 220 } 221 222 static void 223 vtrnd_timer(void *xsc) 224 { 225 struct vtrnd_softc *sc; 226 227 sc = xsc; 228 229 vtrnd_harvest(sc); 230 callout_schedule(&sc->vtrnd_callout, 5 * hz); 231 } 232