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