Line data Source code
1 : /* $OpenBSD: viornd.c,v 1.2 2018/04/28 15:44:59 jasper Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2014 Stefan Fritsch <sf@sfritsch.de>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : #include <sys/param.h>
20 : #include <sys/systm.h>
21 : #include <sys/kernel.h>
22 : #include <sys/timeout.h>
23 : #include <machine/bus.h>
24 : #include <sys/device.h>
25 : #include <sys/pool.h>
26 : #include <dev/rndvar.h>
27 : #include <dev/pv/virtioreg.h>
28 : #include <dev/pv/virtiovar.h>
29 :
30 : /*
31 : * The host may not have an unlimited supply of entropy. Therefore, we must
32 : * not blindly get as much entropy as we can. Instead, we just request
33 : * VIORND_BUFSIZE bytes at boot and every 15 * (1 << interval_shift) seconds.
34 : * XXX There should be an API to check if we actually need more entropy.
35 : *
36 : * The lowest byte in the flags is used for transport specific settings.
37 : * Therefore we use the second byte.
38 : */
39 : #define VIORND_INTERVAL_SHIFT(f) ((f >> 8) & 0xf)
40 : #define VIORND_INTERVAL_SHIFT_DEFAULT 5
41 : #define VIORND_ONESHOT 0x1000
42 : #define VIORND_BUFSIZE 16
43 :
44 : #define VIORND_DEBUG 0
45 :
46 : struct viornd_softc {
47 : struct device sc_dev;
48 : struct virtio_softc *sc_virtio;
49 :
50 : struct virtqueue sc_vq;
51 : int *sc_buf;
52 : bus_dmamap_t sc_dmamap;
53 :
54 : unsigned int sc_interval;
55 : struct timeout sc_tick;
56 : };
57 :
58 : int viornd_match(struct device *, void *, void *);
59 : void viornd_attach(struct device *, struct device *, void *);
60 : int viornd_vq_done(struct virtqueue *);
61 : void viornd_tick(void *);
62 :
63 : struct cfattach viornd_ca = {
64 : sizeof(struct viornd_softc),
65 : viornd_match,
66 : viornd_attach,
67 : NULL
68 : };
69 :
70 : struct cfdriver viornd_cd = {
71 : NULL, "viornd", DV_DULL
72 : };
73 :
74 :
75 0 : int viornd_match(struct device *parent, void *match, void *aux)
76 : {
77 0 : struct virtio_softc *va = aux;
78 0 : if (va->sc_childdevid == PCI_PRODUCT_VIRTIO_ENTROPY)
79 0 : return 1;
80 0 : return 0;
81 0 : }
82 :
83 : void
84 0 : viornd_attach(struct device *parent, struct device *self, void *aux)
85 : {
86 0 : struct viornd_softc *sc = (struct viornd_softc *)self;
87 0 : struct virtio_softc *vsc = (struct virtio_softc *)parent;
88 : unsigned int shift;
89 :
90 0 : vsc->sc_vqs = &sc->sc_vq;
91 0 : vsc->sc_nvqs = 1;
92 0 : vsc->sc_config_change = 0;
93 0 : if (vsc->sc_child != NULL)
94 0 : panic("already attached to something else");
95 0 : vsc->sc_child = self;
96 0 : vsc->sc_ipl = IPL_NET;
97 0 : sc->sc_virtio = vsc;
98 :
99 0 : virtio_negotiate_features(vsc, 0, NULL);
100 :
101 0 : if (sc->sc_dev.dv_cfdata->cf_flags & VIORND_ONESHOT) {
102 0 : sc->sc_interval = 0;
103 0 : } else {
104 0 : shift = VIORND_INTERVAL_SHIFT(sc->sc_dev.dv_cfdata->cf_flags);
105 0 : if (shift == 0)
106 : shift = VIORND_INTERVAL_SHIFT_DEFAULT;
107 0 : sc->sc_interval = 15 * (1 << shift);
108 : }
109 : #if VIORND_DEBUG
110 : printf(": request interval: %us\n", sc->sc_interval);
111 : #endif
112 :
113 0 : sc->sc_buf = dma_alloc(VIORND_BUFSIZE, PR_NOWAIT|PR_ZERO);
114 0 : if (sc->sc_buf == NULL) {
115 0 : printf(": Can't alloc dma buffer\n");
116 0 : goto err;
117 : }
118 0 : if (bus_dmamap_create(sc->sc_virtio->sc_dmat, VIORND_BUFSIZE, 1,
119 : VIORND_BUFSIZE, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW,
120 : &sc->sc_dmamap)) {
121 0 : printf(": Can't alloc dmamap\n");
122 0 : goto err;
123 : }
124 0 : if (bus_dmamap_load(sc->sc_virtio->sc_dmat, sc->sc_dmamap,
125 : sc->sc_buf, VIORND_BUFSIZE, NULL, BUS_DMA_NOWAIT|BUS_DMA_READ)) {
126 0 : printf(": Can't load dmamap\n");
127 0 : goto err2;
128 : }
129 :
130 0 : if (virtio_alloc_vq(vsc, &sc->sc_vq, 0, VIORND_BUFSIZE, 1,
131 0 : "Entropy request") != 0) {
132 0 : printf(": Can't alloc virtqueue\n");
133 0 : goto err2;
134 : }
135 :
136 0 : sc->sc_vq.vq_done = viornd_vq_done;
137 0 : virtio_start_vq_intr(vsc, &sc->sc_vq);
138 0 : timeout_set(&sc->sc_tick, viornd_tick, sc);
139 0 : timeout_add(&sc->sc_tick, 1);
140 :
141 0 : printf("\n");
142 0 : return;
143 : err2:
144 0 : bus_dmamap_destroy(vsc->sc_dmat, sc->sc_dmamap);
145 : err:
146 0 : vsc->sc_child = VIRTIO_CHILD_ERROR;
147 0 : if (sc->sc_buf != NULL) {
148 0 : dma_free(sc->sc_buf, VIORND_BUFSIZE);
149 0 : sc->sc_buf = NULL;
150 0 : }
151 0 : return;
152 0 : }
153 :
154 : int
155 0 : viornd_vq_done(struct virtqueue *vq)
156 : {
157 0 : struct virtio_softc *vsc = vq->vq_owner;
158 0 : struct viornd_softc *sc = (struct viornd_softc *)vsc->sc_child;
159 0 : int slot, len, i;
160 :
161 0 : if (virtio_dequeue(vsc, vq, &slot, &len) != 0)
162 0 : return 0;
163 0 : bus_dmamap_sync(vsc->sc_dmat, sc->sc_dmamap, 0, VIORND_BUFSIZE,
164 : BUS_DMASYNC_POSTREAD);
165 0 : if (len > VIORND_BUFSIZE) {
166 0 : printf("%s: inconsistent descriptor length %d > %d\n",
167 0 : sc->sc_dev.dv_xname, len, VIORND_BUFSIZE);
168 0 : goto out;
169 : }
170 :
171 : #if VIORND_DEBUG
172 : printf("%s: got %d bytes of entropy\n", __func__, len);
173 : #endif
174 0 : for (i = 0; (i + 1) * sizeof(int) <= len; i++)
175 0 : enqueue_randomness(sc->sc_buf[i]);
176 :
177 0 : if (sc->sc_interval)
178 0 : timeout_add_sec(&sc->sc_tick, sc->sc_interval);
179 :
180 : out:
181 0 : virtio_dequeue_commit(vq, slot);
182 0 : return 1;
183 0 : }
184 :
185 : void
186 0 : viornd_tick(void *arg)
187 : {
188 0 : struct viornd_softc *sc = arg;
189 0 : struct virtio_softc *vsc = sc->sc_virtio;
190 0 : struct virtqueue *vq = &sc->sc_vq;
191 0 : int slot;
192 :
193 0 : bus_dmamap_sync(vsc->sc_dmat, sc->sc_dmamap, 0, VIORND_BUFSIZE,
194 : BUS_DMASYNC_PREREAD);
195 0 : if (virtio_enqueue_prep(vq, &slot) != 0 ||
196 0 : virtio_enqueue_reserve(vq, slot, 1) != 0) {
197 0 : panic("%s: virtqueue enqueue failed", sc->sc_dev.dv_xname);
198 : }
199 0 : virtio_enqueue(vq, slot, sc->sc_dmamap, 0);
200 0 : virtio_enqueue_commit(vsc, vq, slot, 1);
201 0 : }
|