virtio_mmio.c revision 1.12 1 /* $NetBSD: virtio_mmio.c,v 1.12 2024/01/02 07:24:50 thorpej Exp $ */
2 /* $OpenBSD: virtio_mmio.c,v 1.2 2017/02/24 17:12:31 patrick Exp $ */
3
4 /*-
5 * Copyright (c) 2024 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Jason R. Thorpe.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34 * Copyright (c) 2014 Patrick Wildt <patrick (at) blueri.se>
35 * Copyright (c) 2012 Stefan Fritsch.
36 * Copyright (c) 2010 Minoura Makoto.
37 * All rights reserved.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
49 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
50 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
51 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
52 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
53 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
54 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
55 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
56 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
57 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
58 */
59
60 #include <sys/cdefs.h>
61 __KERNEL_RCSID(0, "$NetBSD: virtio_mmio.c,v 1.12 2024/01/02 07:24:50 thorpej Exp $");
62
63 #include <sys/param.h>
64 #include <sys/systm.h>
65 #include <sys/kernel.h>
66 #include <sys/device.h>
67 #include <sys/mutex.h>
68
69 #define VIRTIO_PRIVATE
70 #include <dev/virtio/virtio_mmiovar.h>
71
72 #define VIRTIO_MMIO_MAGIC ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)
73
74 #define VIRTIO_MMIO_MAGIC_VALUE 0x000
75 #define VIRTIO_MMIO_VERSION 0x004
76 #define VIRTIO_MMIO_DEVICE_ID 0x008
77 #define VIRTIO_MMIO_VENDOR_ID 0x00c
78 #define VIRTIO_MMIO_DEVICE_FEATURES 0x010 /* "HostFeatures" in v1 */
79 #define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014 /* "HostFeaturesSel" in v1 */
80 #define VIRTIO_MMIO_DRIVER_FEATURES 0x020 /* "GuestFeatures" in v1 */
81 #define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024 /* "GuestFeaturesSel" in v1 */
82 #define VIRTIO_MMIO_V1_GUEST_PAGE_SIZE 0x028
83 #define VIRTIO_MMIO_QUEUE_SEL 0x030
84 #define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034
85 #define VIRTIO_MMIO_QUEUE_NUM 0x038
86 #define VIRTIO_MMIO_V1_QUEUE_ALIGN 0x03c
87 #define VIRTIO_MMIO_V1_QUEUE_PFN 0x040
88 #define VIRTIO_MMIO_QUEUE_READY 0x044
89 #define VIRTIO_MMIO_QUEUE_NOTIFY 0x050
90 #define VIRTIO_MMIO_INTERRUPT_STATUS 0x060
91 #define VIRTIO_MMIO_INTERRUPT_ACK 0x064
92 #define VIRTIO_MMIO_STATUS 0x070
93 #define VIRTIO_MMIO_V2_QUEUE_DESC_LOW 0x080
94 #define VIRTIO_MMIO_V2_QUEUE_DESC_HIGH 0x084
95 #define VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW 0x090
96 #define VIRTIO_MMIO_V2_QUEUE_AVAIL_HIGH 0x094
97 #define VIRTIO_MMIO_V2_QUEUE_USED_LOW 0x0a0
98 #define VIRTIO_MMIO_V2_QUEUE_USED_HIGH 0x0a4
99 #define VIRTIO_MMIO_V2_CONFIG_GEN 0x0fc
100 #define VIRTIO_MMIO_CONFIG 0x100
101
102 #define VIRTIO_MMIO_INT_VRING (1 << 0)
103 #define VIRTIO_MMIO_INT_CONFIG (1 << 1)
104
105 /*
106 * MMIO configuration space for virtio-mmio v1 is in guest byte order.
107 *
108 * XXX For big-endian aarch64 and arm, see note in virtio_pci.c.
109 */
110
111 #if (defined(__aarch64__) || defined(__arm__)) && BYTE_ORDER == BIG_ENDIAN
112 # define READ_ENDIAN LITTLE_ENDIAN
113 # define STRUCT_ENDIAN BIG_ENDIAN
114 #elif BYTE_ORDER == BIG_ENDIAN
115 # define READ_ENDIAN BIG_ENDIAN
116 # define STRUCT_ENDIAN BIG_ENDIAN
117 #else
118 # define READ_ENDIAN LITTLE_ENDIAN
119 # define STRUCT_ENDIAN LITTLE_ENDIAN
120 #endif
121
122
123 static void virtio_mmio_kick(struct virtio_softc *, uint16_t);
124 static uint16_t virtio_mmio_read_queue_size(struct virtio_softc *, uint16_t);
125 static void virtio_mmio_v1_setup_queue(struct virtio_softc *, uint16_t, uint64_t);
126 static void virtio_mmio_v2_setup_queue(struct virtio_softc *, uint16_t, uint64_t);
127 static void virtio_mmio_set_status(struct virtio_softc *, int);
128 static void virtio_mmio_negotiate_features(struct virtio_softc *, uint64_t);
129 static int virtio_mmio_alloc_interrupts(struct virtio_softc *);
130 static void virtio_mmio_free_interrupts(struct virtio_softc *);
131 static int virtio_mmio_setup_interrupts(struct virtio_softc *, int);
132
133 static uint32_t
134 virtio_mmio_reg_read(struct virtio_mmio_softc *sc, bus_addr_t reg)
135 {
136 uint32_t val;
137
138 val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg);
139 if (sc->sc_le_regs) {
140 val = le32toh(val);
141 }
142 return val;
143 }
144
145 static void
146 virtio_mmio_reg_write(struct virtio_mmio_softc *sc, bus_addr_t reg,
147 uint32_t val)
148 {
149 if (sc->sc_le_regs) {
150 val = htole32(val);
151 }
152 bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val);
153 }
154
155 static void
156 virtio_mmio_v2_set_addr(struct virtio_mmio_softc *sc, bus_addr_t reg,
157 uint64_t addr)
158 {
159 virtio_mmio_reg_write(sc, reg, BUS_ADDR_LO32(addr));
160 virtio_mmio_reg_write(sc, reg + 4, BUS_ADDR_HI32(addr));
161 }
162
163 static const struct virtio_ops virtio_mmio_v1_ops = {
164 .kick = virtio_mmio_kick,
165 .read_queue_size = virtio_mmio_read_queue_size,
166 .setup_queue = virtio_mmio_v1_setup_queue,
167 .set_status = virtio_mmio_set_status,
168 .neg_features = virtio_mmio_negotiate_features,
169 .alloc_interrupts = virtio_mmio_alloc_interrupts,
170 .free_interrupts = virtio_mmio_free_interrupts,
171 .setup_interrupts = virtio_mmio_setup_interrupts,
172 };
173
174 static const struct virtio_ops virtio_mmio_v2_ops = {
175 .kick = virtio_mmio_kick,
176 .read_queue_size = virtio_mmio_read_queue_size,
177 .setup_queue = virtio_mmio_v2_setup_queue,
178 .set_status = virtio_mmio_set_status,
179 .neg_features = virtio_mmio_negotiate_features,
180 .alloc_interrupts = virtio_mmio_alloc_interrupts,
181 .free_interrupts = virtio_mmio_free_interrupts,
182 .setup_interrupts = virtio_mmio_setup_interrupts,
183 };
184
185 static uint16_t
186 virtio_mmio_read_queue_size(struct virtio_softc *vsc, uint16_t idx)
187 {
188 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
189 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx);
190 return virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX);
191 }
192
193 static void
194 virtio_mmio_v1_setup_queue(struct virtio_softc *vsc, uint16_t idx,
195 uint64_t addr)
196 {
197 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
198
199 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx);
200 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NUM,
201 virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX));
202 virtio_mmio_reg_write(sc, VIRTIO_MMIO_V1_QUEUE_ALIGN,
203 VIRTIO_PAGE_SIZE);
204 virtio_mmio_reg_write(sc, VIRTIO_MMIO_V1_QUEUE_PFN,
205 addr / VIRTIO_PAGE_SIZE);
206 }
207
208 static void
209 virtio_mmio_v2_setup_queue(struct virtio_softc *vsc, uint16_t idx,
210 uint64_t addr)
211 {
212 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
213 struct virtqueue *vq = &vsc->sc_vqs[idx];
214 KASSERT(vq->vq_index == idx);
215
216 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx);
217 if (addr == 0) {
218 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_READY, 0);
219 virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_DESC_LOW, 0);
220 virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW, 0);
221 virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_USED_LOW, 0);
222 } else {
223 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NUM,
224 virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX));
225 virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_DESC_LOW,
226 addr);
227 virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW,
228 addr + vq->vq_availoffset);
229 virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_USED_LOW,
230 addr + vq->vq_usedoffset);
231 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_READY, 1);
232 }
233 }
234
235 static void
236 virtio_mmio_set_status(struct virtio_softc *vsc, int status)
237 {
238 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
239 int old = 0;
240
241 if (status != 0)
242 old = virtio_mmio_reg_read(sc, VIRTIO_MMIO_STATUS);
243 virtio_mmio_reg_write(sc, VIRTIO_MMIO_STATUS, status|old);
244 }
245
246 bool
247 virtio_mmio_common_probe_present(struct virtio_mmio_softc *sc)
248 {
249 uint32_t magic;
250
251 /* XXX */
252 magic = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
253 VIRTIO_MMIO_MAGIC_VALUE);
254 return (magic == VIRTIO_MMIO_MAGIC);
255 }
256
257
258 void
259 virtio_mmio_common_attach(struct virtio_mmio_softc *sc)
260 {
261 struct virtio_softc *vsc = &sc->sc_sc;
262 device_t self = vsc->sc_dev;
263 uint32_t id, magic, ver;
264
265 magic = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
266 VIRTIO_MMIO_MAGIC_VALUE);
267 if (magic != VIRTIO_MMIO_MAGIC) {
268 if (magic == le32toh(VIRTIO_MMIO_MAGIC)) {
269 sc->sc_le_regs = true;
270 } else {
271 aprint_error_dev(vsc->sc_dev,
272 "wrong magic value 0x%08x; giving up\n", magic);
273 return;
274 }
275 }
276 vsc->sc_bus_endian = READ_ENDIAN;
277 vsc->sc_struct_endian = STRUCT_ENDIAN;
278
279 ver = virtio_mmio_reg_read(sc, VIRTIO_MMIO_VERSION);
280 switch (ver) {
281 case 1:
282 /* we could use PAGE_SIZE, but virtio(4) assumes 4KiB for now */
283 virtio_mmio_reg_write(sc,
284 VIRTIO_MMIO_V1_GUEST_PAGE_SIZE, VIRTIO_PAGE_SIZE);
285 vsc->sc_ops = &virtio_mmio_v1_ops;
286 break;
287
288 case 2:
289 vsc->sc_ops = &virtio_mmio_v2_ops;
290 break;
291
292 default:
293 aprint_error_dev(vsc->sc_dev,
294 "unknown version 0x%08x; giving up\n", ver);
295 return;
296 }
297 aprint_normal_dev(self, "VirtIO-MMIO v%d\n", ver);
298
299 id = virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_ID);
300 if (id == 0) {
301 /* no device connected. */
302 return;
303 }
304
305 virtio_print_device_type(self, id, ver);
306
307 /* set up our device config tag */
308 vsc->sc_devcfg_iosize = sc->sc_iosize - VIRTIO_MMIO_CONFIG;
309 vsc->sc_devcfg_iot = sc->sc_iot;
310 if (bus_space_subregion(sc->sc_iot, sc->sc_ioh,
311 VIRTIO_MMIO_CONFIG, vsc->sc_devcfg_iosize,
312 &vsc->sc_devcfg_ioh)) {
313 aprint_error_dev(self, "can't map config i/o space\n");
314 return;
315 }
316
317 virtio_device_reset(vsc);
318 virtio_mmio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_ACK);
319 virtio_mmio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER);
320
321 /* XXX: use softc as aux... */
322 vsc->sc_childdevid = id;
323 vsc->sc_child = NULL;
324 }
325
326 int
327 virtio_mmio_common_detach(struct virtio_mmio_softc *sc, int flags)
328 {
329 struct virtio_softc *vsc = &sc->sc_sc;
330 int r;
331
332 r = config_detach_children(vsc->sc_dev, flags);
333 if (r != 0)
334 return r;
335
336 KASSERT(vsc->sc_child == NULL);
337 KASSERT(vsc->sc_vqs == NULL);
338 KASSERT(sc->sc_ih == NULL);
339
340 if (sc->sc_iosize) {
341 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
342 sc->sc_iosize = 0;
343 }
344
345 return 0;
346 }
347
348 /*
349 * Feature negotiation.
350 */
351 static void
352 virtio_mmio_negotiate_features(struct virtio_softc *vsc, uint64_t
353 driver_features)
354 {
355 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
356 uint32_t r;
357
358 virtio_mmio_reg_write(sc, VIRTIO_MMIO_DEVICE_FEATURES_SEL, 0);
359 r = virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_FEATURES);
360 r &= driver_features;
361 virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 0);
362 virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES, r);
363
364 vsc->sc_active_features = r;
365 }
366
367 /*
368 * Interrupt handler.
369 */
370 int
371 virtio_mmio_intr(void *arg)
372 {
373 struct virtio_mmio_softc *sc = arg;
374 struct virtio_softc *vsc = &sc->sc_sc;
375 int isr, r = 0;
376
377 /* check and ack the interrupt */
378 isr = virtio_mmio_reg_read(sc, VIRTIO_MMIO_INTERRUPT_STATUS);
379 virtio_mmio_reg_write(sc, VIRTIO_MMIO_INTERRUPT_ACK, isr);
380 if ((isr & VIRTIO_MMIO_INT_CONFIG) &&
381 (vsc->sc_config_change != NULL))
382 r = (vsc->sc_config_change)(vsc);
383 if ((isr & VIRTIO_MMIO_INT_VRING) &&
384 (vsc->sc_intrhand != NULL)) {
385 if (vsc->sc_soft_ih != NULL)
386 softint_schedule(vsc->sc_soft_ih);
387 else
388 r |= (vsc->sc_intrhand)(vsc);
389 }
390
391 return r;
392 }
393
394 static void
395 virtio_mmio_kick(struct virtio_softc *vsc, uint16_t idx)
396 {
397 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
398 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NOTIFY, idx);
399 }
400
401 static int
402 virtio_mmio_alloc_interrupts(struct virtio_softc *vsc)
403 {
404 struct virtio_mmio_softc * const sc = (struct virtio_mmio_softc *)vsc;
405
406 return sc->sc_alloc_interrupts(sc);
407 }
408
409 static void
410 virtio_mmio_free_interrupts(struct virtio_softc *vsc)
411 {
412 struct virtio_mmio_softc * const sc = (struct virtio_mmio_softc *)vsc;
413
414 sc->sc_free_interrupts(sc);
415 }
416
417 static int
418 virtio_mmio_setup_interrupts(struct virtio_softc *vsc __unused,
419 int reinit __unused)
420 {
421
422 return 0;
423 }
424