1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2013, Bryan Venteicher <[email protected]>
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 static devclass_t vtrnd_devclass;
105
106 VIRTIO_DRIVER_MODULE(virtio_random, vtrnd_driver, vtrnd_devclass,
107 vtrnd_modevent, 0);
108 MODULE_VERSION(virtio_random, 1);
109 MODULE_DEPEND(virtio_random, virtio, 1, 1, 1);
110 MODULE_DEPEND(virtio_random, random_device, 1, 1, 1);
111
112 VIRTIO_SIMPLE_PNPINFO(virtio_random, VIRTIO_ID_ENTROPY,
113 "VirtIO Entropy Adapter");
114
115 static int
vtrnd_modevent(module_t mod,int type,void * unused)116 vtrnd_modevent(module_t mod, int type, void *unused)
117 {
118 int error;
119
120 switch (type) {
121 case MOD_LOAD:
122 case MOD_QUIESCE:
123 case MOD_UNLOAD:
124 case MOD_SHUTDOWN:
125 error = 0;
126 break;
127 default:
128 error = EOPNOTSUPP;
129 break;
130 }
131
132 return (error);
133 }
134
135 static int
vtrnd_probe(device_t dev)136 vtrnd_probe(device_t dev)
137 {
138 return (VIRTIO_SIMPLE_PROBE(dev, virtio_random));
139 }
140
141 static int
vtrnd_attach(device_t dev)142 vtrnd_attach(device_t dev)
143 {
144 struct vtrnd_softc *sc, *exp;
145 int error;
146
147 sc = device_get_softc(dev);
148 sc->vtrnd_dev = dev;
149 virtio_set_feature_desc(dev, vtrnd_feature_desc);
150
151 error = vtrnd_setup_features(sc);
152 if (error) {
153 device_printf(dev, "cannot setup features\n");
154 goto fail;
155 }
156
157 error = vtrnd_alloc_virtqueue(sc);
158 if (error) {
159 device_printf(dev, "cannot allocate virtqueue\n");
160 goto fail;
161 }
162
163 exp = NULL;
164 if (!atomic_compare_exchange_strong_explicit(&g_vtrnd_softc, &exp, sc,
165 memory_order_release, memory_order_acquire)) {
166 error = EEXIST;
167 goto fail;
168 }
169
170 sc->eh = EVENTHANDLER_REGISTER(shutdown_post_sync,
171 vtrnd_shutdown, dev, SHUTDOWN_PRI_LAST + 1); /* ??? */
172 if (sc->eh == NULL) {
173 device_printf(dev, "Shutdown event registration failed\n");
174 error = ENXIO;
175 goto fail;
176 }
177
178 sc->inactive = false;
179 random_source_register(&random_vtrnd);
180
181 fail:
182 if (error)
183 vtrnd_detach(dev);
184
185 return (error);
186 }
187
188 static int
vtrnd_detach(device_t dev)189 vtrnd_detach(device_t dev)
190 {
191 struct vtrnd_softc *sc;
192
193 sc = device_get_softc(dev);
194 KASSERT(
195 atomic_load_explicit(&g_vtrnd_softc, memory_order_acquire) == sc,
196 ("only one global instance at a time"));
197
198 sc->inactive = true;
199 if (sc->eh != NULL) {
200 EVENTHANDLER_DEREGISTER(shutdown_post_sync, sc->eh);
201 sc->eh = NULL;
202 }
203 random_source_deregister(&random_vtrnd);
204 atomic_store_explicit(&g_vtrnd_softc, NULL, memory_order_release);
205 return (0);
206 }
207
208 static int
vtrnd_shutdown(device_t dev)209 vtrnd_shutdown(device_t dev)
210 {
211 struct vtrnd_softc *sc;
212
213 sc = device_get_softc(dev);
214 sc->inactive = true;
215
216 return(0);
217 }
218
219 static int
vtrnd_negotiate_features(struct vtrnd_softc * sc)220 vtrnd_negotiate_features(struct vtrnd_softc *sc)
221 {
222 device_t dev;
223 uint64_t features;
224
225 dev = sc->vtrnd_dev;
226 features = VTRND_FEATURES;
227
228 sc->vtrnd_features = virtio_negotiate_features(dev, features);
229 return (virtio_finalize_features(dev));
230 }
231
232 static int
vtrnd_setup_features(struct vtrnd_softc * sc)233 vtrnd_setup_features(struct vtrnd_softc *sc)
234 {
235 int error;
236
237 error = vtrnd_negotiate_features(sc);
238 if (error)
239 return (error);
240
241 return (0);
242 }
243
244 static int
vtrnd_alloc_virtqueue(struct vtrnd_softc * sc)245 vtrnd_alloc_virtqueue(struct vtrnd_softc *sc)
246 {
247 device_t dev;
248 struct vq_alloc_info vq_info;
249
250 dev = sc->vtrnd_dev;
251
252 VQ_ALLOC_INFO_INIT(&vq_info, 0, NULL, sc, &sc->vtrnd_vq,
253 "%s request", device_get_nameunit(dev));
254
255 return (virtio_alloc_virtqueues(dev, 0, 1, &vq_info));
256 }
257
258 static int
vtrnd_harvest(struct vtrnd_softc * sc,void * buf,size_t * sz)259 vtrnd_harvest(struct vtrnd_softc *sc, void *buf, size_t *sz)
260 {
261 struct sglist_seg segs[1];
262 struct sglist sg;
263 struct virtqueue *vq;
264 uint32_t value[HARVESTSIZE] __aligned(sizeof(uint32_t) * HARVESTSIZE);
265 uint32_t rdlen;
266 int error;
267
268 _Static_assert(sizeof(value) < PAGE_SIZE, "sglist assumption");
269
270 if (sc->inactive)
271 return (EDEADLK);
272
273 sglist_init(&sg, 1, segs);
274 error = sglist_append(&sg, value, *sz);
275 if (error != 0)
276 panic("%s: sglist_append error=%d", __func__, error);
277
278 vq = sc->vtrnd_vq;
279 KASSERT(virtqueue_empty(vq), ("%s: non-empty queue", __func__));
280
281 error = virtqueue_enqueue(vq, buf, &sg, 0, 1);
282 if (error != 0)
283 return (error);
284
285 /*
286 * Poll for the response, but the command is likely already
287 * done when we return from the notify.
288 */
289 virtqueue_notify(vq);
290 virtqueue_poll(vq, &rdlen);
291
292 if (rdlen > *sz)
293 panic("%s: random device wrote %zu bytes beyond end of provided"
294 " buffer %p:%zu", __func__, (size_t)rdlen - *sz,
295 (void *)value, *sz);
296 else if (rdlen == 0)
297 return (EAGAIN);
298 *sz = MIN(rdlen, *sz);
299 memcpy(buf, value, *sz);
300 explicit_bzero(value, *sz);
301 return (0);
302 }
303
304 static unsigned
vtrnd_read(void * buf,unsigned usz)305 vtrnd_read(void *buf, unsigned usz)
306 {
307 struct vtrnd_softc *sc;
308 size_t sz;
309 int error;
310
311 sc = g_vtrnd_softc;
312 if (sc == NULL)
313 return (0);
314
315 sz = usz;
316 error = vtrnd_harvest(sc, buf, &sz);
317 if (error != 0)
318 return (0);
319
320 return (sz);
321 }
322