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/kernel.h> 36 #include <sys/malloc.h> 37 #include <sys/module.h> 38 #include <sys/sglist.h> 39 #include <sys/random.h> 40 #include <sys/stdatomic.h> 41 42 #include <machine/bus.h> 43 #include <machine/resource.h> 44 #include <sys/bus.h> 45 46 #include <dev/random/randomdev.h> 47 #include <dev/random/random_harvestq.h> 48 #include <dev/virtio/virtio.h> 49 #include <dev/virtio/virtqueue.h> 50 51 struct vtrnd_softc { 52 device_t vtrnd_dev; 53 uint64_t vtrnd_features; 54 struct virtqueue *vtrnd_vq; 55 }; 56 57 static int vtrnd_modevent(module_t, int, void *); 58 59 static int vtrnd_probe(device_t); 60 static int vtrnd_attach(device_t); 61 static int vtrnd_detach(device_t); 62 63 static int vtrnd_negotiate_features(struct vtrnd_softc *); 64 static int vtrnd_setup_features(struct vtrnd_softc *); 65 static int vtrnd_alloc_virtqueue(struct vtrnd_softc *); 66 static int vtrnd_harvest(struct vtrnd_softc *, void *, size_t *); 67 static unsigned vtrnd_read(void *, unsigned); 68 69 #define VTRND_FEATURES 0 70 71 static struct virtio_feature_desc vtrnd_feature_desc[] = { 72 { 0, NULL } 73 }; 74 75 static struct random_source random_vtrnd = { 76 .rs_ident = "VirtIO Entropy Adapter", 77 .rs_source = RANDOM_PURE_VIRTIO, 78 .rs_read = vtrnd_read, 79 }; 80 81 /* Kludge for API limitations of random(4). */ 82 static _Atomic(struct vtrnd_softc *) g_vtrnd_softc; 83 84 static device_method_t vtrnd_methods[] = { 85 /* Device methods. */ 86 DEVMETHOD(device_probe, vtrnd_probe), 87 DEVMETHOD(device_attach, vtrnd_attach), 88 DEVMETHOD(device_detach, vtrnd_detach), 89 90 DEVMETHOD_END 91 }; 92 93 static driver_t vtrnd_driver = { 94 "vtrnd", 95 vtrnd_methods, 96 sizeof(struct vtrnd_softc) 97 }; 98 static devclass_t vtrnd_devclass; 99 100 DRIVER_MODULE(virtio_random, virtio_mmio, vtrnd_driver, vtrnd_devclass, 101 vtrnd_modevent, 0); 102 DRIVER_MODULE(virtio_random, virtio_pci, vtrnd_driver, vtrnd_devclass, 103 vtrnd_modevent, 0); 104 MODULE_VERSION(virtio_random, 1); 105 MODULE_DEPEND(virtio_random, virtio, 1, 1, 1); 106 MODULE_DEPEND(virtio_random, random_device, 1, 1, 1); 107 108 VIRTIO_SIMPLE_PNPTABLE(virtio_random, VIRTIO_ID_ENTROPY, 109 "VirtIO Entropy Adapter"); 110 VIRTIO_SIMPLE_PNPINFO(virtio_mmio, virtio_random); 111 VIRTIO_SIMPLE_PNPINFO(virtio_pci, virtio_random); 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 random_source_register(&random_vtrnd); 168 169 fail: 170 if (error) 171 vtrnd_detach(dev); 172 173 return (error); 174 } 175 176 static int 177 vtrnd_detach(device_t dev) 178 { 179 struct vtrnd_softc *sc; 180 181 sc = device_get_softc(dev); 182 KASSERT( 183 atomic_load_explicit(&g_vtrnd_softc, memory_order_acquire) == sc, 184 ("only one global instance at a time")); 185 186 random_source_deregister(&random_vtrnd); 187 atomic_store_explicit(&g_vtrnd_softc, NULL, memory_order_release); 188 return (0); 189 } 190 191 static int 192 vtrnd_negotiate_features(struct vtrnd_softc *sc) 193 { 194 device_t dev; 195 uint64_t features; 196 197 dev = sc->vtrnd_dev; 198 features = VTRND_FEATURES; 199 200 sc->vtrnd_features = virtio_negotiate_features(dev, features); 201 return (virtio_finalize_features(dev)); 202 } 203 204 static int 205 vtrnd_setup_features(struct vtrnd_softc *sc) 206 { 207 int error; 208 209 error = vtrnd_negotiate_features(sc); 210 if (error) 211 return (error); 212 213 return (0); 214 } 215 216 static int 217 vtrnd_alloc_virtqueue(struct vtrnd_softc *sc) 218 { 219 device_t dev; 220 struct vq_alloc_info vq_info; 221 222 dev = sc->vtrnd_dev; 223 224 VQ_ALLOC_INFO_INIT(&vq_info, 0, NULL, sc, &sc->vtrnd_vq, 225 "%s request", device_get_nameunit(dev)); 226 227 return (virtio_alloc_virtqueues(dev, 0, 1, &vq_info)); 228 } 229 230 static int 231 vtrnd_harvest(struct vtrnd_softc *sc, void *buf, size_t *sz) 232 { 233 struct sglist_seg segs[1]; 234 struct sglist sg; 235 struct virtqueue *vq; 236 uint32_t value[HARVESTSIZE] __aligned(sizeof(uint32_t) * HARVESTSIZE); 237 uint32_t rdlen; 238 int error; 239 240 _Static_assert(sizeof(value) < PAGE_SIZE, "sglist assumption"); 241 242 sglist_init(&sg, 1, segs); 243 error = sglist_append(&sg, value, *sz); 244 if (error != 0) 245 panic("%s: sglist_append error=%d", __func__, error); 246 247 vq = sc->vtrnd_vq; 248 KASSERT(virtqueue_empty(vq), ("%s: non-empty queue", __func__)); 249 250 error = virtqueue_enqueue(vq, buf, &sg, 0, 1); 251 if (error != 0) 252 return (error); 253 254 /* 255 * Poll for the response, but the command is likely already 256 * done when we return from the notify. 257 */ 258 virtqueue_notify(vq); 259 virtqueue_poll(vq, &rdlen); 260 261 if (rdlen > *sz) 262 panic("%s: random device wrote %zu bytes beyond end of provided" 263 " buffer %p:%zu", __func__, (size_t)rdlen - *sz, 264 (void *)value, *sz); 265 else if (rdlen == 0) 266 return (EAGAIN); 267 *sz = MIN(rdlen, *sz); 268 memcpy(buf, value, *sz); 269 explicit_bzero(value, *sz); 270 return (0); 271 } 272 273 static unsigned 274 vtrnd_read(void *buf, unsigned usz) 275 { 276 struct vtrnd_softc *sc; 277 size_t sz; 278 int error; 279 280 sc = g_vtrnd_softc; 281 if (sc == NULL) 282 return (0); 283 284 sz = usz; 285 error = vtrnd_harvest(sc, buf, &sz); 286 if (error != 0) 287 return (0); 288 289 return (sz); 290 } 291