xref: /netbsd-src/sys/dev/virtio/virtio_mmio.c (revision 345cf9fb81bd0411c53e25d62cd93bdcaa865312)
1 /*	$NetBSD: virtio_mmio.c,v 1.13 2024/01/06 06:59:33 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@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.13 2024/01/06 06:59:33 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 /*
103  * MMIO configuration space for virtio-mmio v1 is in guest byte order.
104  *
105  * XXX For big-endian aarch64 and arm, see note in virtio_pci.c.
106  */
107 
108 #if (defined(__aarch64__) || defined(__arm__)) && BYTE_ORDER == BIG_ENDIAN
109 #	define READ_ENDIAN	LITTLE_ENDIAN
110 #	define STRUCT_ENDIAN	BIG_ENDIAN
111 #elif BYTE_ORDER == BIG_ENDIAN
112 #	define READ_ENDIAN	BIG_ENDIAN
113 #	define STRUCT_ENDIAN	BIG_ENDIAN
114 #else
115 #	define READ_ENDIAN	LITTLE_ENDIAN
116 #	define STRUCT_ENDIAN	LITTLE_ENDIAN
117 #endif
118 
119 
120 static void	virtio_mmio_kick(struct virtio_softc *, uint16_t);
121 static uint16_t	virtio_mmio_read_queue_size(struct virtio_softc *, uint16_t);
122 static void	virtio_mmio_v1_setup_queue(struct virtio_softc *, uint16_t, uint64_t);
123 static void	virtio_mmio_v2_setup_queue(struct virtio_softc *, uint16_t, uint64_t);
124 static int	virtio_mmio_get_status(struct virtio_softc *);
125 static void	virtio_mmio_set_status(struct virtio_softc *, int);
126 static void	virtio_mmio_negotiate_features(struct virtio_softc *, uint64_t);
127 static int	virtio_mmio_alloc_interrupts(struct virtio_softc *);
128 static void	virtio_mmio_free_interrupts(struct virtio_softc *);
129 static int	virtio_mmio_setup_interrupts(struct virtio_softc *, int);
130 
131 static uint32_t
132 virtio_mmio_reg_read(struct virtio_mmio_softc *sc, bus_addr_t reg)
133 {
134 	uint32_t val;
135 
136 	val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg);
137 	if (sc->sc_le_regs) {
138 		val = le32toh(val);
139 	}
140 	return val;
141 }
142 
143 static void
144 virtio_mmio_reg_write(struct virtio_mmio_softc *sc, bus_addr_t reg,
145     uint32_t val)
146 {
147 	if (sc->sc_le_regs) {
148 		val = htole32(val);
149 	}
150 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val);
151 }
152 
153 static void
154 virtio_mmio_v2_set_addr(struct virtio_mmio_softc *sc, bus_addr_t reg,
155     uint64_t addr)
156 {
157 	virtio_mmio_reg_write(sc, reg,     BUS_ADDR_LO32(addr));
158 	virtio_mmio_reg_write(sc, reg + 4, BUS_ADDR_HI32(addr));
159 }
160 
161 static const struct virtio_ops virtio_mmio_v1_ops = {
162 	.kick = virtio_mmio_kick,
163 	.read_queue_size = virtio_mmio_read_queue_size,
164 	.setup_queue = virtio_mmio_v1_setup_queue,
165 	.set_status = virtio_mmio_set_status,
166 	.neg_features = virtio_mmio_negotiate_features,
167 	.alloc_interrupts = virtio_mmio_alloc_interrupts,
168 	.free_interrupts = virtio_mmio_free_interrupts,
169 	.setup_interrupts = virtio_mmio_setup_interrupts,
170 };
171 
172 static const struct virtio_ops virtio_mmio_v2_ops = {
173 	.kick = virtio_mmio_kick,
174 	.read_queue_size = virtio_mmio_read_queue_size,
175 	.setup_queue = virtio_mmio_v2_setup_queue,
176 	.set_status = virtio_mmio_set_status,
177 	.neg_features = virtio_mmio_negotiate_features,
178 	.alloc_interrupts = virtio_mmio_alloc_interrupts,
179 	.free_interrupts = virtio_mmio_free_interrupts,
180 	.setup_interrupts = virtio_mmio_setup_interrupts,
181 };
182 
183 static uint16_t
184 virtio_mmio_read_queue_size(struct virtio_softc *vsc, uint16_t idx)
185 {
186 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
187 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx);
188 	return virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX);
189 }
190 
191 static void
192 virtio_mmio_v1_setup_queue(struct virtio_softc *vsc, uint16_t idx,
193     uint64_t addr)
194 {
195 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
196 
197 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx);
198 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NUM,
199 	    virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX));
200 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_V1_QUEUE_ALIGN,
201 	    VIRTIO_PAGE_SIZE);
202 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_V1_QUEUE_PFN,
203 	    addr / VIRTIO_PAGE_SIZE);
204 }
205 
206 static void
207 virtio_mmio_v2_setup_queue(struct virtio_softc *vsc, uint16_t idx,
208     uint64_t addr)
209 {
210 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
211 	struct virtqueue *vq = &vsc->sc_vqs[idx];
212 	KASSERT(vq->vq_index == idx);
213 
214 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx);
215 	if (addr == 0) {
216 		virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_READY, 0);
217 		virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_DESC_LOW, 0);
218 		virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW, 0);
219 		virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_USED_LOW, 0);
220 	} else {
221 		virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NUM,
222 		    virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX));
223 		virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_DESC_LOW,
224 		    addr);
225 		virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW,
226 		    addr + vq->vq_availoffset);
227 		virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_USED_LOW,
228 		    addr + vq->vq_usedoffset);
229 		virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_READY, 1);
230 	}
231 }
232 
233 static int
234 virtio_mmio_get_status(struct virtio_softc *vsc)
235 {
236 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
237 
238 	return virtio_mmio_reg_read(sc, VIRTIO_MMIO_STATUS);
239 }
240 
241 static void
242 virtio_mmio_set_status(struct virtio_softc *vsc, int status)
243 {
244 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
245 	int old = 0;
246 
247 	if (status != 0)
248 		old = virtio_mmio_reg_read(sc, VIRTIO_MMIO_STATUS);
249 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_STATUS, status|old);
250 }
251 
252 bool
253 virtio_mmio_common_probe_present(struct virtio_mmio_softc *sc)
254 {
255 	uint32_t magic;
256 
257 	/* XXX */
258 	magic = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
259 	    VIRTIO_MMIO_MAGIC_VALUE);
260 	return (magic == VIRTIO_MMIO_MAGIC);
261 }
262 
263 
264 void
265 virtio_mmio_common_attach(struct virtio_mmio_softc *sc)
266 {
267 	struct virtio_softc *vsc = &sc->sc_sc;
268 	device_t self = vsc->sc_dev;
269 	uint32_t id, magic;
270 	int virtio_vers;
271 
272 	magic = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
273 	    VIRTIO_MMIO_MAGIC_VALUE);
274 	if (magic != VIRTIO_MMIO_MAGIC) {
275 		if (magic == le32toh(VIRTIO_MMIO_MAGIC)) {
276 			sc->sc_le_regs = true;
277 		} else {
278 			aprint_error_dev(vsc->sc_dev,
279 			    "wrong magic value 0x%08x; giving up\n", magic);
280 			return;
281 		}
282 	}
283 	vsc->sc_bus_endian    = READ_ENDIAN;
284 	vsc->sc_struct_endian = STRUCT_ENDIAN;
285 
286 	sc->sc_mmio_vers = virtio_mmio_reg_read(sc, VIRTIO_MMIO_VERSION);
287 	switch (sc->sc_mmio_vers) {
288 	case 1:
289 		/* we could use PAGE_SIZE, but virtio(4) assumes 4KiB for now */
290 		virtio_mmio_reg_write(sc,
291 		    VIRTIO_MMIO_V1_GUEST_PAGE_SIZE, VIRTIO_PAGE_SIZE);
292 		vsc->sc_ops = &virtio_mmio_v1_ops;
293 		/*
294 		 * MMIO v1 ("legacy") is documented in the VirtIO 0.9.x
295 		 * draft(s) and uses the same page-oriented queue setup,
296 		 * so that's what we'll report as the VirtIO version.
297 		 */
298 		virtio_vers = 0;
299 		break;
300 
301 	case 2:
302 		vsc->sc_ops = &virtio_mmio_v2_ops;
303 		/*
304 		 * MMIO v2 is documented in the VirtIO 1.0 spec.
305 		 */
306 		virtio_vers = 1;
307 		break;
308 
309 	default:
310 		aprint_error_dev(vsc->sc_dev,
311 		    "unknown version 0x%08x; giving up\n", sc->sc_mmio_vers);
312 		return;
313 	}
314 	aprint_normal_dev(self, "VirtIO-MMIO-v%u\n", sc->sc_mmio_vers);
315 
316 	id = virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_ID);
317 	if (id == 0) {
318 		/* no device connected. */
319 		return;
320 	}
321 
322 	virtio_print_device_type(self, id, virtio_vers);
323 
324 	/* set up our device config tag */
325 	vsc->sc_devcfg_iosize = sc->sc_iosize - VIRTIO_MMIO_CONFIG;
326 	vsc->sc_devcfg_iot = sc->sc_iot;
327 	if (bus_space_subregion(sc->sc_iot, sc->sc_ioh,
328 			VIRTIO_MMIO_CONFIG, vsc->sc_devcfg_iosize,
329 			&vsc->sc_devcfg_ioh)) {
330 		aprint_error_dev(self, "can't map config i/o space\n");
331 		return;
332 	}
333 
334 	virtio_device_reset(vsc);
335 	virtio_mmio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_ACK);
336 	virtio_mmio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER);
337 
338 	/* XXX: use softc as aux... */
339 	vsc->sc_childdevid = id;
340 	vsc->sc_child = NULL;
341 }
342 
343 int
344 virtio_mmio_common_detach(struct virtio_mmio_softc *sc, int flags)
345 {
346 	struct virtio_softc *vsc = &sc->sc_sc;
347 	int r;
348 
349 	r = config_detach_children(vsc->sc_dev, flags);
350 	if (r != 0)
351 		return r;
352 
353 	KASSERT(vsc->sc_child == NULL);
354 	KASSERT(vsc->sc_vqs == NULL);
355 	KASSERT(sc->sc_ih == NULL);
356 
357 	if (sc->sc_iosize) {
358 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
359 		sc->sc_iosize = 0;
360 	}
361 
362 	return 0;
363 }
364 
365 /*
366  * Feature negotiation.
367  *
368  * We fold pre-VirtIO-1.0 feature negotiation into this single routine
369  * because the "legacy" (MMIO-v1) also had the feature sel registers.
370  */
371 static void
372 virtio_mmio_negotiate_features(struct virtio_softc *vsc, uint64_t
373     driver_features)
374 {
375 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
376 	device_t self = vsc->sc_dev;
377 	uint64_t saved_driver_features = driver_features;
378 	uint64_t device_features, negotiated;
379 	uint32_t device_status;
380 
381 	driver_features |= VIRTIO_F_VERSION_1;
382 	vsc->sc_active_features = 0;
383 
384 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_DEVICE_FEATURES_SEL, 0);
385 	device_features = virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_FEATURES);
386 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_DEVICE_FEATURES_SEL, 1);
387 	device_features |= (uint64_t)
388 	    virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_FEATURES) << 32;
389 
390 	/* notify on empty is 0.9 only */
391 	if (device_features & VIRTIO_F_VERSION_1) {
392 		driver_features &= ~VIRTIO_F_NOTIFY_ON_EMPTY;
393 	} else {
394 		/*
395 		 * Require version 1 for MMIO-v2 transport.
396 		 */
397 		if (sc->sc_mmio_vers >= 2) {
398 			aprint_error_dev(self, "MMIO-v%u requires version 1\n",
399 			    sc->sc_mmio_vers);
400 			virtio_mmio_set_status(vsc,
401 			    VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
402 			return;
403 		}
404 		/*
405 		 * If the driver requires version 1, but the device doesn't
406 		 * support it, fail now.
407 		 */
408 		if (saved_driver_features & VIRTIO_F_VERSION_1) {
409 			aprint_error_dev(self, "device rejected version 1\n");
410 			virtio_mmio_set_status(vsc,
411 			    VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
412 			return;
413 		}
414 	}
415 
416 	negotiated = device_features & driver_features;
417 
418 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 0);
419 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES,
420 	    (uint32_t)negotiated);
421 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 1);
422 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES,
423 	    (uint32_t)(negotiated >> 32));
424 
425 	/*
426 	 * FEATURES_OK status is not present pre-1.0.
427 	 */
428 	if (device_features & VIRTIO_F_VERSION_1) {
429 		virtio_mmio_set_status(vsc,
430 		    VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK);
431 		device_status = virtio_mmio_get_status(vsc);
432 		if ((device_status &
433 		     VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK) == 0) {
434 			aprint_error_dev(self, "feature negotiation failed\n");
435 			virtio_mmio_set_status(vsc,
436 			    VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
437 			return;
438 		}
439 	}
440 
441 	if (negotiated & VIRTIO_F_VERSION_1) {
442 		/*
443 		 * All VirtIO 1.0 access is little-endian.
444 		 */
445 		vsc->sc_bus_endian    = LITTLE_ENDIAN;
446 		vsc->sc_struct_endian = LITTLE_ENDIAN;
447 	}
448 
449 	vsc->sc_active_features = negotiated;
450 }
451 
452 /*
453  * Interrupt handler.
454  */
455 int
456 virtio_mmio_intr(void *arg)
457 {
458 	struct virtio_mmio_softc *sc = arg;
459 	struct virtio_softc *vsc = &sc->sc_sc;
460 	int isr, r = 0;
461 
462 	/* check and ack the interrupt */
463 	isr = virtio_mmio_reg_read(sc, VIRTIO_MMIO_INTERRUPT_STATUS);
464 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_INTERRUPT_ACK, isr);
465 	if ((isr & VIRTIO_CONFIG_ISR_CONFIG_CHANGE) &&
466 	    (vsc->sc_config_change != NULL))
467 		r = (vsc->sc_config_change)(vsc);
468 	if ((isr & VIRTIO_CONFIG_ISR_QUEUE_INTERRUPT) &&
469 	    (vsc->sc_intrhand != NULL)) {
470 		if (vsc->sc_soft_ih != NULL)
471 			softint_schedule(vsc->sc_soft_ih);
472 		else
473 			r |= (vsc->sc_intrhand)(vsc);
474 	}
475 
476 	return r;
477 }
478 
479 static void
480 virtio_mmio_kick(struct virtio_softc *vsc, uint16_t idx)
481 {
482 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
483 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NOTIFY, idx);
484 }
485 
486 static int
487 virtio_mmio_alloc_interrupts(struct virtio_softc *vsc)
488 {
489 	struct virtio_mmio_softc * const sc = (struct virtio_mmio_softc *)vsc;
490 
491 	return sc->sc_alloc_interrupts(sc);
492 }
493 
494 static void
495 virtio_mmio_free_interrupts(struct virtio_softc *vsc)
496 {
497 	struct virtio_mmio_softc * const sc = (struct virtio_mmio_softc *)vsc;
498 
499 	sc->sc_free_interrupts(sc);
500 }
501 
502 static int
503 virtio_mmio_setup_interrupts(struct virtio_softc *vsc __unused,
504     int reinit __unused)
505 {
506 
507 	return 0;
508 }
509