1e0724c53SAlexey Zaytsev /* 2e0724c53SAlexey Zaytsev * CDDL HEADER START 3e0724c53SAlexey Zaytsev * 4e0724c53SAlexey Zaytsev * The contents of this file are subject to the terms of the 5e0724c53SAlexey Zaytsev * Common Development and Distribution License (the "License"). 6e0724c53SAlexey Zaytsev * You may not use this file except in compliance with the License. 7e0724c53SAlexey Zaytsev * 8e0724c53SAlexey Zaytsev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9e0724c53SAlexey Zaytsev * or http://www.opensolaris.org/os/licensing. 10e0724c53SAlexey Zaytsev * See the License for the specific language governing permissions 11e0724c53SAlexey Zaytsev * and limitations under the License. 12e0724c53SAlexey Zaytsev * 13e0724c53SAlexey Zaytsev * When distributing Covered Code, include this CDDL HEADER in each 14e0724c53SAlexey Zaytsev * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15e0724c53SAlexey Zaytsev * If applicable, add the following below this CDDL HEADER, with the 16e0724c53SAlexey Zaytsev * fields enclosed by brackets "[]" replaced with your own identifying 17e0724c53SAlexey Zaytsev * information: Portions Copyright [yyyy] [name of copyright owner] 18e0724c53SAlexey Zaytsev * 19e0724c53SAlexey Zaytsev * CDDL HEADER END 20e0724c53SAlexey Zaytsev */ 21e0724c53SAlexey Zaytsev 22e0724c53SAlexey Zaytsev /* 238a324c92SDan McDonald * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 24e0724c53SAlexey Zaytsev * Copyright 2012 Alexey Zaytsev <alexey.zaytsev@gmail.com> 25e0724c53SAlexey Zaytsev */ 26e0724c53SAlexey Zaytsev 27e0724c53SAlexey Zaytsev /* Based on the NetBSD virtio driver by Minoura Makoto. */ 28e0724c53SAlexey Zaytsev /* 29e0724c53SAlexey Zaytsev * Copyright (c) 2010 Minoura Makoto. 30e0724c53SAlexey Zaytsev * All rights reserved. 31e0724c53SAlexey Zaytsev * 32e0724c53SAlexey Zaytsev * Redistribution and use in source and binary forms, with or without 33e0724c53SAlexey Zaytsev * modification, are permitted provided that the following conditions 34e0724c53SAlexey Zaytsev * are met: 35e0724c53SAlexey Zaytsev * 1. Redistributions of source code must retain the above copyright 36e0724c53SAlexey Zaytsev * notice, this list of conditions and the following disclaimer. 37e0724c53SAlexey Zaytsev * 2. Redistributions in binary form must reproduce the above copyright 38e0724c53SAlexey Zaytsev * notice, this list of conditions and the following disclaimer in the 39e0724c53SAlexey Zaytsev * documentation and/or other materials provided with the distribution. 40e0724c53SAlexey Zaytsev * 41e0724c53SAlexey Zaytsev * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 42e0724c53SAlexey Zaytsev * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43e0724c53SAlexey Zaytsev * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 44e0724c53SAlexey Zaytsev * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 45e0724c53SAlexey Zaytsev * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46e0724c53SAlexey Zaytsev * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 47e0724c53SAlexey Zaytsev * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 48e0724c53SAlexey Zaytsev * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 49e0724c53SAlexey Zaytsev * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 50e0724c53SAlexey Zaytsev * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 51e0724c53SAlexey Zaytsev * 52e0724c53SAlexey Zaytsev */ 53e0724c53SAlexey Zaytsev 54e0724c53SAlexey Zaytsev #include <sys/conf.h> 55e0724c53SAlexey Zaytsev #include <sys/kmem.h> 56e0724c53SAlexey Zaytsev #include <sys/debug.h> 57e0724c53SAlexey Zaytsev #include <sys/modctl.h> 58e0724c53SAlexey Zaytsev #include <sys/autoconf.h> 59e0724c53SAlexey Zaytsev #include <sys/ddi_impldefs.h> 60e0724c53SAlexey Zaytsev #include <sys/ddi.h> 61e0724c53SAlexey Zaytsev #include <sys/sunddi.h> 62e0724c53SAlexey Zaytsev #include <sys/sunndi.h> 63e0724c53SAlexey Zaytsev #include <sys/avintr.h> 64e0724c53SAlexey Zaytsev #include <sys/spl.h> 65e0724c53SAlexey Zaytsev #include <sys/promif.h> 66e0724c53SAlexey Zaytsev #include <sys/list.h> 67e0724c53SAlexey Zaytsev #include <sys/bootconf.h> 68e0724c53SAlexey Zaytsev #include <sys/bootsvcs.h> 69e0724c53SAlexey Zaytsev #include <sys/sysmacros.h> 70e0724c53SAlexey Zaytsev #include <sys/pci.h> 71e0724c53SAlexey Zaytsev 72e0724c53SAlexey Zaytsev #include "virtiovar.h" 73e0724c53SAlexey Zaytsev #include "virtioreg.h" 748a324c92SDan McDonald 75e0724c53SAlexey Zaytsev #define NDEVNAMES (sizeof (virtio_device_name) / sizeof (char *)) 76e0724c53SAlexey Zaytsev #define MINSEG_INDIRECT 2 /* use indirect if nsegs >= this value */ 77e0724c53SAlexey Zaytsev #define VIRTQUEUE_ALIGN(n) (((n)+(VIRTIO_PAGE_SIZE-1)) & \ 78e0724c53SAlexey Zaytsev ~(VIRTIO_PAGE_SIZE-1)) 79e0724c53SAlexey Zaytsev 80e0724c53SAlexey Zaytsev void 81e0724c53SAlexey Zaytsev virtio_set_status(struct virtio_softc *sc, unsigned int status) 82e0724c53SAlexey Zaytsev { 83e0724c53SAlexey Zaytsev int old = 0; 84e0724c53SAlexey Zaytsev 858a324c92SDan McDonald if (status != 0) { 868a324c92SDan McDonald old = ddi_get8(sc->sc_ioh, (uint8_t *)(sc->sc_io_addr + 87e0724c53SAlexey Zaytsev VIRTIO_CONFIG_DEVICE_STATUS)); 888a324c92SDan McDonald } 89e0724c53SAlexey Zaytsev 908a324c92SDan McDonald ddi_put8(sc->sc_ioh, (uint8_t *)(sc->sc_io_addr + 918a324c92SDan McDonald VIRTIO_CONFIG_DEVICE_STATUS), status | old); 92e0724c53SAlexey Zaytsev } 93e0724c53SAlexey Zaytsev 94e0724c53SAlexey Zaytsev /* 95e0724c53SAlexey Zaytsev * Negotiate features, save the result in sc->sc_features 96e0724c53SAlexey Zaytsev */ 97e0724c53SAlexey Zaytsev uint32_t 98e0724c53SAlexey Zaytsev virtio_negotiate_features(struct virtio_softc *sc, uint32_t guest_features) 99e0724c53SAlexey Zaytsev { 100e0724c53SAlexey Zaytsev uint32_t host_features; 101e0724c53SAlexey Zaytsev uint32_t features; 102e0724c53SAlexey Zaytsev 103e0724c53SAlexey Zaytsev host_features = ddi_get32(sc->sc_ioh, 104e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 105e0724c53SAlexey Zaytsev (uint32_t *)(sc->sc_io_addr + VIRTIO_CONFIG_DEVICE_FEATURES)); 106e0724c53SAlexey Zaytsev 1078a324c92SDan McDonald dev_debug(sc->sc_dev, CE_NOTE, "host features: %x, guest features: %x", 108e0724c53SAlexey Zaytsev host_features, guest_features); 109e0724c53SAlexey Zaytsev 110e0724c53SAlexey Zaytsev features = host_features & guest_features; 111e0724c53SAlexey Zaytsev ddi_put32(sc->sc_ioh, 112e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 113e0724c53SAlexey Zaytsev (uint32_t *)(sc->sc_io_addr + VIRTIO_CONFIG_GUEST_FEATURES), 114e0724c53SAlexey Zaytsev features); 115e0724c53SAlexey Zaytsev 116e0724c53SAlexey Zaytsev sc->sc_features = features; 117e0724c53SAlexey Zaytsev 118e0724c53SAlexey Zaytsev return (host_features); 119e0724c53SAlexey Zaytsev } 120e0724c53SAlexey Zaytsev 121e0724c53SAlexey Zaytsev size_t 1228a324c92SDan McDonald virtio_show_features(uint32_t features, char *buf, size_t len) 123e0724c53SAlexey Zaytsev { 124e0724c53SAlexey Zaytsev char *orig_buf = buf; 125e0724c53SAlexey Zaytsev char *bufend = buf + len; 126e0724c53SAlexey Zaytsev 127e0724c53SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 128e0724c53SAlexey Zaytsev buf += snprintf(buf, bufend - buf, "Generic ( "); 129e0724c53SAlexey Zaytsev if (features & VIRTIO_F_RING_INDIRECT_DESC) 130e0724c53SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 131e0724c53SAlexey Zaytsev buf += snprintf(buf, bufend - buf, "INDIRECT_DESC "); 132e0724c53SAlexey Zaytsev 133e0724c53SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 134e0724c53SAlexey Zaytsev buf += snprintf(buf, bufend - buf, ") "); 135e0724c53SAlexey Zaytsev 136e0724c53SAlexey Zaytsev /* LINTED E_PTRDIFF_OVERFLOW */ 137e0724c53SAlexey Zaytsev return (buf - orig_buf); 138e0724c53SAlexey Zaytsev } 139e0724c53SAlexey Zaytsev 140e0724c53SAlexey Zaytsev boolean_t 141e0724c53SAlexey Zaytsev virtio_has_feature(struct virtio_softc *sc, uint32_t feature) 142e0724c53SAlexey Zaytsev { 143e0724c53SAlexey Zaytsev return (sc->sc_features & feature); 144e0724c53SAlexey Zaytsev } 145e0724c53SAlexey Zaytsev 146e0724c53SAlexey Zaytsev /* 147e0724c53SAlexey Zaytsev * Device configuration registers. 148e0724c53SAlexey Zaytsev */ 149e0724c53SAlexey Zaytsev uint8_t 150e0724c53SAlexey Zaytsev virtio_read_device_config_1(struct virtio_softc *sc, unsigned int index) 151e0724c53SAlexey Zaytsev { 152e0724c53SAlexey Zaytsev ASSERT(sc->sc_config_offset); 153e0724c53SAlexey Zaytsev return ddi_get8(sc->sc_ioh, 154e0724c53SAlexey Zaytsev (uint8_t *)(sc->sc_io_addr + sc->sc_config_offset + index)); 155e0724c53SAlexey Zaytsev } 156e0724c53SAlexey Zaytsev 157e0724c53SAlexey Zaytsev uint16_t 158e0724c53SAlexey Zaytsev virtio_read_device_config_2(struct virtio_softc *sc, unsigned int index) 159e0724c53SAlexey Zaytsev { 160e0724c53SAlexey Zaytsev ASSERT(sc->sc_config_offset); 161e0724c53SAlexey Zaytsev return ddi_get16(sc->sc_ioh, 162e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 163e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + sc->sc_config_offset + index)); 164e0724c53SAlexey Zaytsev } 165e0724c53SAlexey Zaytsev 166e0724c53SAlexey Zaytsev uint32_t 167e0724c53SAlexey Zaytsev virtio_read_device_config_4(struct virtio_softc *sc, unsigned int index) 168e0724c53SAlexey Zaytsev { 169e0724c53SAlexey Zaytsev ASSERT(sc->sc_config_offset); 170e0724c53SAlexey Zaytsev return ddi_get32(sc->sc_ioh, 171e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 172e0724c53SAlexey Zaytsev (uint32_t *)(sc->sc_io_addr + sc->sc_config_offset + index)); 173e0724c53SAlexey Zaytsev } 174e0724c53SAlexey Zaytsev 175e0724c53SAlexey Zaytsev uint64_t 176e0724c53SAlexey Zaytsev virtio_read_device_config_8(struct virtio_softc *sc, unsigned int index) 177e0724c53SAlexey Zaytsev { 178e0724c53SAlexey Zaytsev uint64_t r; 179e0724c53SAlexey Zaytsev 180e0724c53SAlexey Zaytsev ASSERT(sc->sc_config_offset); 181e0724c53SAlexey Zaytsev r = ddi_get32(sc->sc_ioh, 182e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 183e0724c53SAlexey Zaytsev (uint32_t *)(sc->sc_io_addr + sc->sc_config_offset + 184e0724c53SAlexey Zaytsev index + sizeof (uint32_t))); 185e0724c53SAlexey Zaytsev 186e0724c53SAlexey Zaytsev r <<= 32; 187e0724c53SAlexey Zaytsev 188e0724c53SAlexey Zaytsev r += ddi_get32(sc->sc_ioh, 189e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 190e0724c53SAlexey Zaytsev (uint32_t *)(sc->sc_io_addr + sc->sc_config_offset + index)); 191e0724c53SAlexey Zaytsev return (r); 192e0724c53SAlexey Zaytsev } 193e0724c53SAlexey Zaytsev 194e0724c53SAlexey Zaytsev void 1958a324c92SDan McDonald virtio_write_device_config_1(struct virtio_softc *sc, unsigned int index, 1968a324c92SDan McDonald uint8_t value) 197e0724c53SAlexey Zaytsev { 198e0724c53SAlexey Zaytsev ASSERT(sc->sc_config_offset); 199e0724c53SAlexey Zaytsev ddi_put8(sc->sc_ioh, 200e0724c53SAlexey Zaytsev (uint8_t *)(sc->sc_io_addr + sc->sc_config_offset + index), value); 201e0724c53SAlexey Zaytsev } 202e0724c53SAlexey Zaytsev 203e0724c53SAlexey Zaytsev void 2048a324c92SDan McDonald virtio_write_device_config_2(struct virtio_softc *sc, unsigned int index, 2058a324c92SDan McDonald uint16_t value) 206e0724c53SAlexey Zaytsev { 207e0724c53SAlexey Zaytsev ASSERT(sc->sc_config_offset); 208e0724c53SAlexey Zaytsev ddi_put16(sc->sc_ioh, 209e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 210e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + sc->sc_config_offset + index), value); 211e0724c53SAlexey Zaytsev } 212e0724c53SAlexey Zaytsev 213e0724c53SAlexey Zaytsev void 2148a324c92SDan McDonald virtio_write_device_config_4(struct virtio_softc *sc, unsigned int index, 2158a324c92SDan McDonald uint32_t value) 216e0724c53SAlexey Zaytsev { 217e0724c53SAlexey Zaytsev ASSERT(sc->sc_config_offset); 218e0724c53SAlexey Zaytsev ddi_put32(sc->sc_ioh, 219e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 220e0724c53SAlexey Zaytsev (uint32_t *)(sc->sc_io_addr + sc->sc_config_offset + index), value); 221e0724c53SAlexey Zaytsev } 222e0724c53SAlexey Zaytsev 223e0724c53SAlexey Zaytsev void 2248a324c92SDan McDonald virtio_write_device_config_8(struct virtio_softc *sc, unsigned int index, 2258a324c92SDan McDonald uint64_t value) 226e0724c53SAlexey Zaytsev { 227e0724c53SAlexey Zaytsev ASSERT(sc->sc_config_offset); 228e0724c53SAlexey Zaytsev ddi_put32(sc->sc_ioh, 229e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 230e0724c53SAlexey Zaytsev (uint32_t *)(sc->sc_io_addr + sc->sc_config_offset + index), 231e0724c53SAlexey Zaytsev value & 0xFFFFFFFF); 232e0724c53SAlexey Zaytsev ddi_put32(sc->sc_ioh, 233e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 234e0724c53SAlexey Zaytsev (uint32_t *)(sc->sc_io_addr + sc->sc_config_offset + 235e0724c53SAlexey Zaytsev index + sizeof (uint32_t)), value >> 32); 236e0724c53SAlexey Zaytsev } 237e0724c53SAlexey Zaytsev 238e0724c53SAlexey Zaytsev /* 239e0724c53SAlexey Zaytsev * Start/stop vq interrupt. No guarantee. 240e0724c53SAlexey Zaytsev */ 241e0724c53SAlexey Zaytsev void 242e0724c53SAlexey Zaytsev virtio_stop_vq_intr(struct virtqueue *vq) 243e0724c53SAlexey Zaytsev { 244e0724c53SAlexey Zaytsev vq->vq_avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; 245e0724c53SAlexey Zaytsev } 246e0724c53SAlexey Zaytsev 247e0724c53SAlexey Zaytsev void 248e0724c53SAlexey Zaytsev virtio_start_vq_intr(struct virtqueue *vq) 249e0724c53SAlexey Zaytsev { 250e0724c53SAlexey Zaytsev vq->vq_avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; 251e0724c53SAlexey Zaytsev } 252e0724c53SAlexey Zaytsev 253e0724c53SAlexey Zaytsev static ddi_dma_attr_t virtio_vq_dma_attr = { 254e0724c53SAlexey Zaytsev DMA_ATTR_V0, /* Version number */ 255e0724c53SAlexey Zaytsev 0, /* low address */ 2568a324c92SDan McDonald 0x00000FFFFFFFFFFF, /* high address. Has to fit into 32 bits */ 2578a324c92SDan McDonald /* after page-shifting */ 258e0724c53SAlexey Zaytsev 0xFFFFFFFF, /* counter register max */ 259e0724c53SAlexey Zaytsev VIRTIO_PAGE_SIZE, /* page alignment required */ 260e0724c53SAlexey Zaytsev 0x3F, /* burst sizes: 1 - 32 */ 261e0724c53SAlexey Zaytsev 0x1, /* minimum transfer size */ 262e0724c53SAlexey Zaytsev 0xFFFFFFFF, /* max transfer size */ 263e0724c53SAlexey Zaytsev 0xFFFFFFFF, /* address register max */ 264e0724c53SAlexey Zaytsev 1, /* no scatter-gather */ 265e0724c53SAlexey Zaytsev 1, /* device operates on bytes */ 266e0724c53SAlexey Zaytsev 0, /* attr flag: set to 0 */ 267e0724c53SAlexey Zaytsev }; 268e0724c53SAlexey Zaytsev 269e0724c53SAlexey Zaytsev static ddi_dma_attr_t virtio_vq_indirect_dma_attr = { 270e0724c53SAlexey Zaytsev DMA_ATTR_V0, /* Version number */ 271e0724c53SAlexey Zaytsev 0, /* low address */ 272e0724c53SAlexey Zaytsev 0xFFFFFFFFFFFFFFFF, /* high address */ 273e0724c53SAlexey Zaytsev 0xFFFFFFFF, /* counter register max */ 274e0724c53SAlexey Zaytsev 1, /* No specific alignment */ 275e0724c53SAlexey Zaytsev 0x3F, /* burst sizes: 1 - 32 */ 276e0724c53SAlexey Zaytsev 0x1, /* minimum transfer size */ 277e0724c53SAlexey Zaytsev 0xFFFFFFFF, /* max transfer size */ 278e0724c53SAlexey Zaytsev 0xFFFFFFFF, /* address register max */ 279e0724c53SAlexey Zaytsev 1, /* no scatter-gather */ 280e0724c53SAlexey Zaytsev 1, /* device operates on bytes */ 281e0724c53SAlexey Zaytsev 0, /* attr flag: set to 0 */ 282e0724c53SAlexey Zaytsev }; 283e0724c53SAlexey Zaytsev 284e0724c53SAlexey Zaytsev /* Same for direct and indirect descriptors. */ 285e0724c53SAlexey Zaytsev static ddi_device_acc_attr_t virtio_vq_devattr = { 286e0724c53SAlexey Zaytsev DDI_DEVICE_ATTR_V0, 287e0724c53SAlexey Zaytsev DDI_NEVERSWAP_ACC, 288e0724c53SAlexey Zaytsev DDI_STORECACHING_OK_ACC, 289e0724c53SAlexey Zaytsev DDI_DEFAULT_ACC 290e0724c53SAlexey Zaytsev }; 291e0724c53SAlexey Zaytsev 292e0724c53SAlexey Zaytsev static void 293e0724c53SAlexey Zaytsev virtio_free_indirect(struct vq_entry *entry) 294e0724c53SAlexey Zaytsev { 295e0724c53SAlexey Zaytsev 296e0724c53SAlexey Zaytsev (void) ddi_dma_unbind_handle(entry->qe_indirect_dma_handle); 297e0724c53SAlexey Zaytsev ddi_dma_mem_free(&entry->qe_indirect_dma_acch); 298e0724c53SAlexey Zaytsev ddi_dma_free_handle(&entry->qe_indirect_dma_handle); 299e0724c53SAlexey Zaytsev 300e0724c53SAlexey Zaytsev entry->qe_indirect_descs = NULL; 301e0724c53SAlexey Zaytsev } 302e0724c53SAlexey Zaytsev 303e0724c53SAlexey Zaytsev 304e0724c53SAlexey Zaytsev static int 305e0724c53SAlexey Zaytsev virtio_alloc_indirect(struct virtio_softc *sc, struct vq_entry *entry) 306e0724c53SAlexey Zaytsev { 307e0724c53SAlexey Zaytsev int allocsize, num; 308e0724c53SAlexey Zaytsev size_t len; 309e0724c53SAlexey Zaytsev unsigned int ncookies; 310e0724c53SAlexey Zaytsev int ret; 311e0724c53SAlexey Zaytsev 312e0724c53SAlexey Zaytsev num = entry->qe_queue->vq_indirect_num; 313e0724c53SAlexey Zaytsev ASSERT(num > 1); 314e0724c53SAlexey Zaytsev 315e0724c53SAlexey Zaytsev allocsize = sizeof (struct vring_desc) * num; 316e0724c53SAlexey Zaytsev 317e0724c53SAlexey Zaytsev ret = ddi_dma_alloc_handle(sc->sc_dev, &virtio_vq_indirect_dma_attr, 318e0724c53SAlexey Zaytsev DDI_DMA_SLEEP, NULL, &entry->qe_indirect_dma_handle); 319e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 320e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 321e0724c53SAlexey Zaytsev "Failed to allocate dma handle for indirect descriptors, " 322e0724c53SAlexey Zaytsev "entry %d, vq %d", entry->qe_index, 323e0724c53SAlexey Zaytsev entry->qe_queue->vq_index); 324e0724c53SAlexey Zaytsev goto out_alloc_handle; 325e0724c53SAlexey Zaytsev } 326e0724c53SAlexey Zaytsev 3278a324c92SDan McDonald ret = ddi_dma_mem_alloc(entry->qe_indirect_dma_handle, allocsize, 3288a324c92SDan McDonald &virtio_vq_devattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, 329e0724c53SAlexey Zaytsev (caddr_t *)&entry->qe_indirect_descs, &len, 330e0724c53SAlexey Zaytsev &entry->qe_indirect_dma_acch); 331e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 332e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 3338a324c92SDan McDonald "Failed to allocate dma memory for indirect descriptors, " 334e0724c53SAlexey Zaytsev "entry %d, vq %d,", entry->qe_index, 335e0724c53SAlexey Zaytsev entry->qe_queue->vq_index); 336e0724c53SAlexey Zaytsev goto out_alloc; 337e0724c53SAlexey Zaytsev } 338e0724c53SAlexey Zaytsev 339e0724c53SAlexey Zaytsev (void) memset(entry->qe_indirect_descs, 0xff, allocsize); 340e0724c53SAlexey Zaytsev 341e0724c53SAlexey Zaytsev ret = ddi_dma_addr_bind_handle(entry->qe_indirect_dma_handle, NULL, 342e0724c53SAlexey Zaytsev (caddr_t)entry->qe_indirect_descs, len, 3438a324c92SDan McDonald DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, 3448a324c92SDan McDonald &entry->qe_indirect_dma_cookie, &ncookies); 345e0724c53SAlexey Zaytsev if (ret != DDI_DMA_MAPPED) { 346e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 347e0724c53SAlexey Zaytsev "Failed to bind dma memory for indirect descriptors, " 348e0724c53SAlexey Zaytsev "entry %d, vq %d", entry->qe_index, 349e0724c53SAlexey Zaytsev entry->qe_queue->vq_index); 350e0724c53SAlexey Zaytsev goto out_bind; 351e0724c53SAlexey Zaytsev } 352e0724c53SAlexey Zaytsev 353e0724c53SAlexey Zaytsev /* We asked for a single segment */ 354e0724c53SAlexey Zaytsev ASSERT(ncookies == 1); 355e0724c53SAlexey Zaytsev 356e0724c53SAlexey Zaytsev return (0); 357e0724c53SAlexey Zaytsev 358e0724c53SAlexey Zaytsev out_bind: 359e0724c53SAlexey Zaytsev ddi_dma_mem_free(&entry->qe_indirect_dma_acch); 360e0724c53SAlexey Zaytsev out_alloc: 361e0724c53SAlexey Zaytsev ddi_dma_free_handle(&entry->qe_indirect_dma_handle); 362e0724c53SAlexey Zaytsev out_alloc_handle: 363e0724c53SAlexey Zaytsev 364e0724c53SAlexey Zaytsev return (ret); 365e0724c53SAlexey Zaytsev } 366e0724c53SAlexey Zaytsev 367e0724c53SAlexey Zaytsev /* 368e0724c53SAlexey Zaytsev * Initialize the vq structure. 369e0724c53SAlexey Zaytsev */ 370e0724c53SAlexey Zaytsev static int 371e0724c53SAlexey Zaytsev virtio_init_vq(struct virtio_softc *sc, struct virtqueue *vq) 372e0724c53SAlexey Zaytsev { 373e0724c53SAlexey Zaytsev int ret; 374e0724c53SAlexey Zaytsev uint16_t i; 375e0724c53SAlexey Zaytsev int vq_size = vq->vq_num; 376e0724c53SAlexey Zaytsev int indirect_num = vq->vq_indirect_num; 377e0724c53SAlexey Zaytsev 378e0724c53SAlexey Zaytsev /* free slot management */ 379e0724c53SAlexey Zaytsev list_create(&vq->vq_freelist, sizeof (struct vq_entry), 380e0724c53SAlexey Zaytsev offsetof(struct vq_entry, qe_list)); 381e0724c53SAlexey Zaytsev 382e0724c53SAlexey Zaytsev for (i = 0; i < vq_size; i++) { 383e0724c53SAlexey Zaytsev struct vq_entry *entry = &vq->vq_entries[i]; 384e0724c53SAlexey Zaytsev list_insert_tail(&vq->vq_freelist, entry); 385e0724c53SAlexey Zaytsev entry->qe_index = i; 386e0724c53SAlexey Zaytsev entry->qe_desc = &vq->vq_descs[i]; 387e0724c53SAlexey Zaytsev entry->qe_queue = vq; 388e0724c53SAlexey Zaytsev 389e0724c53SAlexey Zaytsev if (indirect_num) { 390e0724c53SAlexey Zaytsev ret = virtio_alloc_indirect(sc, entry); 391e0724c53SAlexey Zaytsev if (ret) 392e0724c53SAlexey Zaytsev goto out_indirect; 393e0724c53SAlexey Zaytsev } 394e0724c53SAlexey Zaytsev } 395e0724c53SAlexey Zaytsev 3968a324c92SDan McDonald mutex_init(&vq->vq_freelist_lock, "virtio-freelist", MUTEX_DRIVER, 3978a324c92SDan McDonald DDI_INTR_PRI(sc->sc_intr_prio)); 3988a324c92SDan McDonald mutex_init(&vq->vq_avail_lock, "virtio-avail", MUTEX_DRIVER, 3998a324c92SDan McDonald DDI_INTR_PRI(sc->sc_intr_prio)); 4008a324c92SDan McDonald mutex_init(&vq->vq_used_lock, "virtio-used", MUTEX_DRIVER, 4018a324c92SDan McDonald DDI_INTR_PRI(sc->sc_intr_prio)); 402e0724c53SAlexey Zaytsev 403e0724c53SAlexey Zaytsev return (0); 404e0724c53SAlexey Zaytsev 405e0724c53SAlexey Zaytsev out_indirect: 406e0724c53SAlexey Zaytsev for (i = 0; i < vq_size; i++) { 407e0724c53SAlexey Zaytsev struct vq_entry *entry = &vq->vq_entries[i]; 408e0724c53SAlexey Zaytsev if (entry->qe_indirect_descs) 409e0724c53SAlexey Zaytsev virtio_free_indirect(entry); 410e0724c53SAlexey Zaytsev } 411e0724c53SAlexey Zaytsev 412e0724c53SAlexey Zaytsev return (ret); 413e0724c53SAlexey Zaytsev } 414e0724c53SAlexey Zaytsev 415e0724c53SAlexey Zaytsev /* 416e0724c53SAlexey Zaytsev * Allocate/free a vq. 417e0724c53SAlexey Zaytsev */ 418e0724c53SAlexey Zaytsev struct virtqueue * 4198a324c92SDan McDonald virtio_alloc_vq(struct virtio_softc *sc, unsigned int index, unsigned int size, 4208a324c92SDan McDonald unsigned int indirect_num, const char *name) 421e0724c53SAlexey Zaytsev { 422e0724c53SAlexey Zaytsev int vq_size, allocsize1, allocsize2, allocsize = 0; 423e0724c53SAlexey Zaytsev int ret; 424e0724c53SAlexey Zaytsev unsigned int ncookies; 425e0724c53SAlexey Zaytsev size_t len; 426e0724c53SAlexey Zaytsev struct virtqueue *vq; 427e0724c53SAlexey Zaytsev 428e0724c53SAlexey Zaytsev ddi_put16(sc->sc_ioh, 429e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 430e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + VIRTIO_CONFIG_QUEUE_SELECT), index); 431e0724c53SAlexey Zaytsev vq_size = ddi_get16(sc->sc_ioh, 432e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 433e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + VIRTIO_CONFIG_QUEUE_SIZE)); 434e0724c53SAlexey Zaytsev if (vq_size == 0) { 435e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 436e0724c53SAlexey Zaytsev "virtqueue dest not exist, index %d for %s\n", index, name); 437e0724c53SAlexey Zaytsev goto out; 438e0724c53SAlexey Zaytsev } 439e0724c53SAlexey Zaytsev 440e0724c53SAlexey Zaytsev vq = kmem_zalloc(sizeof (struct virtqueue), KM_SLEEP); 441e0724c53SAlexey Zaytsev 442e0724c53SAlexey Zaytsev /* size 0 => use native vq size, good for receive queues. */ 443e0724c53SAlexey Zaytsev if (size) 444e0724c53SAlexey Zaytsev vq_size = MIN(vq_size, size); 445e0724c53SAlexey Zaytsev 446e0724c53SAlexey Zaytsev /* allocsize1: descriptor table + avail ring + pad */ 447e0724c53SAlexey Zaytsev allocsize1 = VIRTQUEUE_ALIGN(sizeof (struct vring_desc) * vq_size + 4488a324c92SDan McDonald sizeof (struct vring_avail) + sizeof (uint16_t) * vq_size); 449e0724c53SAlexey Zaytsev /* allocsize2: used ring + pad */ 4508a324c92SDan McDonald allocsize2 = VIRTQUEUE_ALIGN(sizeof (struct vring_used) + 4518a324c92SDan McDonald sizeof (struct vring_used_elem) * vq_size); 452e0724c53SAlexey Zaytsev 453e0724c53SAlexey Zaytsev allocsize = allocsize1 + allocsize2; 454e0724c53SAlexey Zaytsev 455e0724c53SAlexey Zaytsev ret = ddi_dma_alloc_handle(sc->sc_dev, &virtio_vq_dma_attr, 456e0724c53SAlexey Zaytsev DDI_DMA_SLEEP, NULL, &vq->vq_dma_handle); 457e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 458e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 459e0724c53SAlexey Zaytsev "Failed to allocate dma handle for vq %d", index); 460e0724c53SAlexey Zaytsev goto out_alloc_handle; 461e0724c53SAlexey Zaytsev } 462e0724c53SAlexey Zaytsev 463e0724c53SAlexey Zaytsev ret = ddi_dma_mem_alloc(vq->vq_dma_handle, allocsize, 464e0724c53SAlexey Zaytsev &virtio_vq_devattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, 465e0724c53SAlexey Zaytsev (caddr_t *)&vq->vq_vaddr, &len, &vq->vq_dma_acch); 466e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 467e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 4688a324c92SDan McDonald "Failed to allocate dma memory for vq %d", index); 469e0724c53SAlexey Zaytsev goto out_alloc; 470e0724c53SAlexey Zaytsev } 471e0724c53SAlexey Zaytsev 472e0724c53SAlexey Zaytsev ret = ddi_dma_addr_bind_handle(vq->vq_dma_handle, NULL, 4738a324c92SDan McDonald (caddr_t)vq->vq_vaddr, len, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 474e0724c53SAlexey Zaytsev DDI_DMA_SLEEP, NULL, &vq->vq_dma_cookie, &ncookies); 475e0724c53SAlexey Zaytsev if (ret != DDI_DMA_MAPPED) { 476e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 477e0724c53SAlexey Zaytsev "Failed to bind dma memory for vq %d", index); 478e0724c53SAlexey Zaytsev goto out_bind; 479e0724c53SAlexey Zaytsev } 480e0724c53SAlexey Zaytsev 481e0724c53SAlexey Zaytsev /* We asked for a single segment */ 482e0724c53SAlexey Zaytsev ASSERT(ncookies == 1); 483e0724c53SAlexey Zaytsev /* and page-ligned buffers. */ 484e0724c53SAlexey Zaytsev ASSERT(vq->vq_dma_cookie.dmac_laddress % VIRTIO_PAGE_SIZE == 0); 485e0724c53SAlexey Zaytsev 486e0724c53SAlexey Zaytsev (void) memset(vq->vq_vaddr, 0, allocsize); 487e0724c53SAlexey Zaytsev 488e0724c53SAlexey Zaytsev /* Make sure all zeros hit the buffer before we point the host to it */ 489e0724c53SAlexey Zaytsev membar_producer(); 490e0724c53SAlexey Zaytsev 491e0724c53SAlexey Zaytsev /* set the vq address */ 492e0724c53SAlexey Zaytsev ddi_put32(sc->sc_ioh, 493e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 494e0724c53SAlexey Zaytsev (uint32_t *)(sc->sc_io_addr + VIRTIO_CONFIG_QUEUE_ADDRESS), 495e0724c53SAlexey Zaytsev (vq->vq_dma_cookie.dmac_laddress / VIRTIO_PAGE_SIZE)); 496e0724c53SAlexey Zaytsev 497e0724c53SAlexey Zaytsev /* remember addresses and offsets for later use */ 498e0724c53SAlexey Zaytsev vq->vq_owner = sc; 499e0724c53SAlexey Zaytsev vq->vq_num = vq_size; 500e0724c53SAlexey Zaytsev vq->vq_index = index; 501e0724c53SAlexey Zaytsev vq->vq_descs = vq->vq_vaddr; 502e0724c53SAlexey Zaytsev vq->vq_availoffset = sizeof (struct vring_desc)*vq_size; 503e0724c53SAlexey Zaytsev vq->vq_avail = (void *)(((char *)vq->vq_descs) + vq->vq_availoffset); 504e0724c53SAlexey Zaytsev vq->vq_usedoffset = allocsize1; 505e0724c53SAlexey Zaytsev vq->vq_used = (void *)(((char *)vq->vq_descs) + vq->vq_usedoffset); 506e0724c53SAlexey Zaytsev 507e0724c53SAlexey Zaytsev ASSERT(indirect_num == 0 || 508e0724c53SAlexey Zaytsev virtio_has_feature(sc, VIRTIO_F_RING_INDIRECT_DESC)); 509e0724c53SAlexey Zaytsev vq->vq_indirect_num = indirect_num; 510e0724c53SAlexey Zaytsev 511e0724c53SAlexey Zaytsev /* free slot management */ 512e0724c53SAlexey Zaytsev vq->vq_entries = kmem_zalloc(sizeof (struct vq_entry) * vq_size, 513e0724c53SAlexey Zaytsev KM_SLEEP); 514e0724c53SAlexey Zaytsev 515e0724c53SAlexey Zaytsev ret = virtio_init_vq(sc, vq); 516e0724c53SAlexey Zaytsev if (ret) 517e0724c53SAlexey Zaytsev goto out_init; 518e0724c53SAlexey Zaytsev 519e0724c53SAlexey Zaytsev dev_debug(sc->sc_dev, CE_NOTE, 5208a324c92SDan McDonald "Allocated %d entries for vq %d:%s (%d indirect descs)", 521e0724c53SAlexey Zaytsev vq_size, index, name, indirect_num * vq_size); 522e0724c53SAlexey Zaytsev 523e0724c53SAlexey Zaytsev return (vq); 524e0724c53SAlexey Zaytsev 525e0724c53SAlexey Zaytsev out_init: 526e0724c53SAlexey Zaytsev kmem_free(vq->vq_entries, sizeof (struct vq_entry) * vq_size); 527e0724c53SAlexey Zaytsev (void) ddi_dma_unbind_handle(vq->vq_dma_handle); 528e0724c53SAlexey Zaytsev out_bind: 529e0724c53SAlexey Zaytsev ddi_dma_mem_free(&vq->vq_dma_acch); 530e0724c53SAlexey Zaytsev out_alloc: 531e0724c53SAlexey Zaytsev ddi_dma_free_handle(&vq->vq_dma_handle); 532e0724c53SAlexey Zaytsev out_alloc_handle: 533e0724c53SAlexey Zaytsev kmem_free(vq, sizeof (struct virtqueue)); 534e0724c53SAlexey Zaytsev out: 535e0724c53SAlexey Zaytsev return (NULL); 536e0724c53SAlexey Zaytsev } 537e0724c53SAlexey Zaytsev 538e0724c53SAlexey Zaytsev void 539e0724c53SAlexey Zaytsev virtio_free_vq(struct virtqueue *vq) 540e0724c53SAlexey Zaytsev { 541e0724c53SAlexey Zaytsev struct virtio_softc *sc = vq->vq_owner; 542e0724c53SAlexey Zaytsev int i; 543e0724c53SAlexey Zaytsev 544e0724c53SAlexey Zaytsev /* tell device that there's no virtqueue any longer */ 545e0724c53SAlexey Zaytsev ddi_put16(sc->sc_ioh, 546e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 547e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + VIRTIO_CONFIG_QUEUE_SELECT), 548e0724c53SAlexey Zaytsev vq->vq_index); 549e0724c53SAlexey Zaytsev ddi_put32(sc->sc_ioh, 550e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 551e0724c53SAlexey Zaytsev (uint32_t *)(sc->sc_io_addr + VIRTIO_CONFIG_QUEUE_ADDRESS), 0); 552e0724c53SAlexey Zaytsev 553e0724c53SAlexey Zaytsev /* Free the indirect descriptors, if any. */ 554e0724c53SAlexey Zaytsev for (i = 0; i < vq->vq_num; i++) { 555e0724c53SAlexey Zaytsev struct vq_entry *entry = &vq->vq_entries[i]; 556e0724c53SAlexey Zaytsev if (entry->qe_indirect_descs) 557e0724c53SAlexey Zaytsev virtio_free_indirect(entry); 558e0724c53SAlexey Zaytsev } 559e0724c53SAlexey Zaytsev 560e0724c53SAlexey Zaytsev kmem_free(vq->vq_entries, sizeof (struct vq_entry) * vq->vq_num); 561e0724c53SAlexey Zaytsev 562e0724c53SAlexey Zaytsev (void) ddi_dma_unbind_handle(vq->vq_dma_handle); 563e0724c53SAlexey Zaytsev ddi_dma_mem_free(&vq->vq_dma_acch); 564e0724c53SAlexey Zaytsev ddi_dma_free_handle(&vq->vq_dma_handle); 565e0724c53SAlexey Zaytsev 566e0724c53SAlexey Zaytsev mutex_destroy(&vq->vq_used_lock); 567e0724c53SAlexey Zaytsev mutex_destroy(&vq->vq_avail_lock); 568e0724c53SAlexey Zaytsev mutex_destroy(&vq->vq_freelist_lock); 569e0724c53SAlexey Zaytsev 570e0724c53SAlexey Zaytsev kmem_free(vq, sizeof (struct virtqueue)); 571e0724c53SAlexey Zaytsev } 572e0724c53SAlexey Zaytsev 573e0724c53SAlexey Zaytsev /* 574e0724c53SAlexey Zaytsev * Free descriptor management. 575e0724c53SAlexey Zaytsev */ 576e0724c53SAlexey Zaytsev struct vq_entry * 577e0724c53SAlexey Zaytsev vq_alloc_entry(struct virtqueue *vq) 578e0724c53SAlexey Zaytsev { 579e0724c53SAlexey Zaytsev struct vq_entry *qe; 580e0724c53SAlexey Zaytsev 581e0724c53SAlexey Zaytsev mutex_enter(&vq->vq_freelist_lock); 582e0724c53SAlexey Zaytsev if (list_is_empty(&vq->vq_freelist)) { 583e0724c53SAlexey Zaytsev mutex_exit(&vq->vq_freelist_lock); 584e0724c53SAlexey Zaytsev return (NULL); 585e0724c53SAlexey Zaytsev } 586e0724c53SAlexey Zaytsev qe = list_remove_head(&vq->vq_freelist); 587e0724c53SAlexey Zaytsev 588e0724c53SAlexey Zaytsev ASSERT(vq->vq_used_entries >= 0); 589e0724c53SAlexey Zaytsev vq->vq_used_entries++; 590e0724c53SAlexey Zaytsev 591e0724c53SAlexey Zaytsev mutex_exit(&vq->vq_freelist_lock); 592e0724c53SAlexey Zaytsev 593e0724c53SAlexey Zaytsev qe->qe_next = NULL; 594e0724c53SAlexey Zaytsev qe->qe_indirect_next = 0; 595e0724c53SAlexey Zaytsev (void) memset(qe->qe_desc, 0, sizeof (struct vring_desc)); 596e0724c53SAlexey Zaytsev 597e0724c53SAlexey Zaytsev return (qe); 598e0724c53SAlexey Zaytsev } 599e0724c53SAlexey Zaytsev 600e0724c53SAlexey Zaytsev void 601e0724c53SAlexey Zaytsev vq_free_entry(struct virtqueue *vq, struct vq_entry *qe) 602e0724c53SAlexey Zaytsev { 603e0724c53SAlexey Zaytsev mutex_enter(&vq->vq_freelist_lock); 604e0724c53SAlexey Zaytsev 605e0724c53SAlexey Zaytsev list_insert_head(&vq->vq_freelist, qe); 606e0724c53SAlexey Zaytsev vq->vq_used_entries--; 607e0724c53SAlexey Zaytsev ASSERT(vq->vq_used_entries >= 0); 608e0724c53SAlexey Zaytsev mutex_exit(&vq->vq_freelist_lock); 609e0724c53SAlexey Zaytsev } 610e0724c53SAlexey Zaytsev 611e0724c53SAlexey Zaytsev /* 612e0724c53SAlexey Zaytsev * We (intentionally) don't have a global vq mutex, so you are 613e0724c53SAlexey Zaytsev * responsible for external locking to avoid allocting/freeing any 614e0724c53SAlexey Zaytsev * entries before using the returned value. Have fun. 615e0724c53SAlexey Zaytsev */ 616e0724c53SAlexey Zaytsev uint_t 617e0724c53SAlexey Zaytsev vq_num_used(struct virtqueue *vq) 618e0724c53SAlexey Zaytsev { 619e0724c53SAlexey Zaytsev /* vq->vq_freelist_lock would not help here. */ 620e0724c53SAlexey Zaytsev return (vq->vq_used_entries); 621e0724c53SAlexey Zaytsev } 622e0724c53SAlexey Zaytsev 623e0724c53SAlexey Zaytsev static inline void 624e0724c53SAlexey Zaytsev virtio_ve_set_desc(struct vring_desc *desc, uint64_t paddr, uint32_t len, 625e0724c53SAlexey Zaytsev boolean_t write) 626e0724c53SAlexey Zaytsev { 627e0724c53SAlexey Zaytsev desc->addr = paddr; 628e0724c53SAlexey Zaytsev desc->len = len; 629e0724c53SAlexey Zaytsev desc->next = 0; 630e0724c53SAlexey Zaytsev desc->flags = 0; 631e0724c53SAlexey Zaytsev 632e0724c53SAlexey Zaytsev /* 'write' - from the driver's point of view */ 633e0724c53SAlexey Zaytsev if (!write) 634e0724c53SAlexey Zaytsev desc->flags = VRING_DESC_F_WRITE; 635e0724c53SAlexey Zaytsev } 636e0724c53SAlexey Zaytsev 637e0724c53SAlexey Zaytsev void 638e0724c53SAlexey Zaytsev virtio_ve_set(struct vq_entry *qe, uint64_t paddr, uint32_t len, 639e0724c53SAlexey Zaytsev boolean_t write) 640e0724c53SAlexey Zaytsev { 641e0724c53SAlexey Zaytsev virtio_ve_set_desc(qe->qe_desc, paddr, len, write); 642e0724c53SAlexey Zaytsev } 643e0724c53SAlexey Zaytsev 6448a324c92SDan McDonald unsigned int 6458a324c92SDan McDonald virtio_ve_indirect_available(struct vq_entry *qe) 6468a324c92SDan McDonald { 6478a324c92SDan McDonald return (qe->qe_queue->vq_indirect_num - (qe->qe_indirect_next - 1)); 6488a324c92SDan McDonald } 6498a324c92SDan McDonald 650e0724c53SAlexey Zaytsev void 651e0724c53SAlexey Zaytsev virtio_ve_add_indirect_buf(struct vq_entry *qe, uint64_t paddr, uint32_t len, 652e0724c53SAlexey Zaytsev boolean_t write) 653e0724c53SAlexey Zaytsev { 654e0724c53SAlexey Zaytsev struct vring_desc *indirect_desc; 655e0724c53SAlexey Zaytsev 656e0724c53SAlexey Zaytsev ASSERT(qe->qe_queue->vq_indirect_num); 657e0724c53SAlexey Zaytsev ASSERT(qe->qe_indirect_next < qe->qe_queue->vq_indirect_num); 658e0724c53SAlexey Zaytsev 659e0724c53SAlexey Zaytsev indirect_desc = &qe->qe_indirect_descs[qe->qe_indirect_next]; 660e0724c53SAlexey Zaytsev virtio_ve_set_desc(indirect_desc, paddr, len, write); 661e0724c53SAlexey Zaytsev qe->qe_indirect_next++; 662e0724c53SAlexey Zaytsev } 663e0724c53SAlexey Zaytsev 664e0724c53SAlexey Zaytsev void 665e0724c53SAlexey Zaytsev virtio_ve_add_cookie(struct vq_entry *qe, ddi_dma_handle_t dma_handle, 666e0724c53SAlexey Zaytsev ddi_dma_cookie_t dma_cookie, unsigned int ncookies, boolean_t write) 667e0724c53SAlexey Zaytsev { 668e0724c53SAlexey Zaytsev int i; 669e0724c53SAlexey Zaytsev 670e0724c53SAlexey Zaytsev for (i = 0; i < ncookies; i++) { 671e0724c53SAlexey Zaytsev virtio_ve_add_indirect_buf(qe, dma_cookie.dmac_laddress, 672e0724c53SAlexey Zaytsev dma_cookie.dmac_size, write); 673e0724c53SAlexey Zaytsev ddi_dma_nextcookie(dma_handle, &dma_cookie); 674e0724c53SAlexey Zaytsev } 675e0724c53SAlexey Zaytsev } 676e0724c53SAlexey Zaytsev 677e0724c53SAlexey Zaytsev void 678e0724c53SAlexey Zaytsev virtio_sync_vq(struct virtqueue *vq) 679e0724c53SAlexey Zaytsev { 680e0724c53SAlexey Zaytsev struct virtio_softc *vsc = vq->vq_owner; 681e0724c53SAlexey Zaytsev 682e0724c53SAlexey Zaytsev /* Make sure the avail ring update hit the buffer */ 683e0724c53SAlexey Zaytsev membar_producer(); 684e0724c53SAlexey Zaytsev 685e0724c53SAlexey Zaytsev vq->vq_avail->idx = vq->vq_avail_idx; 686e0724c53SAlexey Zaytsev 687e0724c53SAlexey Zaytsev /* Make sure the avail idx update hits the buffer */ 688e0724c53SAlexey Zaytsev membar_producer(); 689e0724c53SAlexey Zaytsev 690e0724c53SAlexey Zaytsev /* Make sure we see the flags update */ 691e0724c53SAlexey Zaytsev membar_consumer(); 692e0724c53SAlexey Zaytsev 6938a324c92SDan McDonald if (!(vq->vq_used->flags & VRING_USED_F_NO_NOTIFY)) { 694e0724c53SAlexey Zaytsev ddi_put16(vsc->sc_ioh, 695e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 696e0724c53SAlexey Zaytsev (uint16_t *)(vsc->sc_io_addr + 697e0724c53SAlexey Zaytsev VIRTIO_CONFIG_QUEUE_NOTIFY), 698e0724c53SAlexey Zaytsev vq->vq_index); 699e0724c53SAlexey Zaytsev } 7008a324c92SDan McDonald } 701e0724c53SAlexey Zaytsev 702e0724c53SAlexey Zaytsev void 703e0724c53SAlexey Zaytsev virtio_push_chain(struct vq_entry *qe, boolean_t sync) 704e0724c53SAlexey Zaytsev { 705e0724c53SAlexey Zaytsev struct virtqueue *vq = qe->qe_queue; 706e0724c53SAlexey Zaytsev struct vq_entry *head = qe; 707e0724c53SAlexey Zaytsev struct vring_desc *desc; 708e0724c53SAlexey Zaytsev int idx; 709e0724c53SAlexey Zaytsev 710e0724c53SAlexey Zaytsev ASSERT(qe); 711e0724c53SAlexey Zaytsev 712e0724c53SAlexey Zaytsev /* 713e0724c53SAlexey Zaytsev * Bind the descs together, paddr and len should be already 714e0724c53SAlexey Zaytsev * set with virtio_ve_set 715e0724c53SAlexey Zaytsev */ 716e0724c53SAlexey Zaytsev do { 717e0724c53SAlexey Zaytsev /* Bind the indirect descriptors */ 718e0724c53SAlexey Zaytsev if (qe->qe_indirect_next > 1) { 719e0724c53SAlexey Zaytsev uint16_t i = 0; 720e0724c53SAlexey Zaytsev 721e0724c53SAlexey Zaytsev /* 722e0724c53SAlexey Zaytsev * Set the pointer/flags to the 723e0724c53SAlexey Zaytsev * first indirect descriptor 724e0724c53SAlexey Zaytsev */ 725e0724c53SAlexey Zaytsev virtio_ve_set_desc(qe->qe_desc, 726e0724c53SAlexey Zaytsev qe->qe_indirect_dma_cookie.dmac_laddress, 727e0724c53SAlexey Zaytsev sizeof (struct vring_desc) * qe->qe_indirect_next, 728e0724c53SAlexey Zaytsev B_FALSE); 729e0724c53SAlexey Zaytsev qe->qe_desc->flags |= VRING_DESC_F_INDIRECT; 730e0724c53SAlexey Zaytsev 731e0724c53SAlexey Zaytsev /* For all but the last one, add the next index/flag */ 732e0724c53SAlexey Zaytsev do { 733e0724c53SAlexey Zaytsev desc = &qe->qe_indirect_descs[i]; 734e0724c53SAlexey Zaytsev i++; 735e0724c53SAlexey Zaytsev 736e0724c53SAlexey Zaytsev desc->flags |= VRING_DESC_F_NEXT; 737e0724c53SAlexey Zaytsev desc->next = i; 738e0724c53SAlexey Zaytsev } while (i < qe->qe_indirect_next - 1); 739e0724c53SAlexey Zaytsev 740e0724c53SAlexey Zaytsev } 741e0724c53SAlexey Zaytsev 742e0724c53SAlexey Zaytsev if (qe->qe_next) { 743e0724c53SAlexey Zaytsev qe->qe_desc->flags |= VRING_DESC_F_NEXT; 744e0724c53SAlexey Zaytsev qe->qe_desc->next = qe->qe_next->qe_index; 745e0724c53SAlexey Zaytsev } 746e0724c53SAlexey Zaytsev 747e0724c53SAlexey Zaytsev qe = qe->qe_next; 748e0724c53SAlexey Zaytsev } while (qe); 749e0724c53SAlexey Zaytsev 750e0724c53SAlexey Zaytsev mutex_enter(&vq->vq_avail_lock); 751e0724c53SAlexey Zaytsev idx = vq->vq_avail_idx; 752e0724c53SAlexey Zaytsev vq->vq_avail_idx++; 753e0724c53SAlexey Zaytsev 754e0724c53SAlexey Zaytsev /* Make sure the bits hit the descriptor(s) */ 755e0724c53SAlexey Zaytsev membar_producer(); 756e0724c53SAlexey Zaytsev vq->vq_avail->ring[idx % vq->vq_num] = head->qe_index; 757e0724c53SAlexey Zaytsev 758e0724c53SAlexey Zaytsev /* Notify the device, if needed. */ 759e0724c53SAlexey Zaytsev if (sync) 760e0724c53SAlexey Zaytsev virtio_sync_vq(vq); 761e0724c53SAlexey Zaytsev 762e0724c53SAlexey Zaytsev mutex_exit(&vq->vq_avail_lock); 763e0724c53SAlexey Zaytsev } 764e0724c53SAlexey Zaytsev 7658a324c92SDan McDonald /* 7668a324c92SDan McDonald * Get a chain of descriptors from the used ring, if one is available. 7678a324c92SDan McDonald */ 768e0724c53SAlexey Zaytsev struct vq_entry * 769e0724c53SAlexey Zaytsev virtio_pull_chain(struct virtqueue *vq, uint32_t *len) 770e0724c53SAlexey Zaytsev { 771e0724c53SAlexey Zaytsev struct vq_entry *head; 772e0724c53SAlexey Zaytsev int slot; 773e0724c53SAlexey Zaytsev int usedidx; 774e0724c53SAlexey Zaytsev 775e0724c53SAlexey Zaytsev mutex_enter(&vq->vq_used_lock); 776e0724c53SAlexey Zaytsev 777e0724c53SAlexey Zaytsev /* No used entries? Bye. */ 778e0724c53SAlexey Zaytsev if (vq->vq_used_idx == vq->vq_used->idx) { 779e0724c53SAlexey Zaytsev mutex_exit(&vq->vq_used_lock); 780e0724c53SAlexey Zaytsev return (NULL); 781e0724c53SAlexey Zaytsev } 782e0724c53SAlexey Zaytsev 783e0724c53SAlexey Zaytsev usedidx = vq->vq_used_idx; 784e0724c53SAlexey Zaytsev vq->vq_used_idx++; 785e0724c53SAlexey Zaytsev mutex_exit(&vq->vq_used_lock); 786e0724c53SAlexey Zaytsev 787e0724c53SAlexey Zaytsev usedidx %= vq->vq_num; 788e0724c53SAlexey Zaytsev 789e0724c53SAlexey Zaytsev /* Make sure we do the next step _after_ checking the idx. */ 790e0724c53SAlexey Zaytsev membar_consumer(); 791e0724c53SAlexey Zaytsev 792e0724c53SAlexey Zaytsev slot = vq->vq_used->ring[usedidx].id; 793e0724c53SAlexey Zaytsev *len = vq->vq_used->ring[usedidx].len; 794e0724c53SAlexey Zaytsev 795e0724c53SAlexey Zaytsev head = &vq->vq_entries[slot]; 796e0724c53SAlexey Zaytsev 797e0724c53SAlexey Zaytsev return (head); 798e0724c53SAlexey Zaytsev } 799e0724c53SAlexey Zaytsev 800e0724c53SAlexey Zaytsev void 801e0724c53SAlexey Zaytsev virtio_free_chain(struct vq_entry *qe) 802e0724c53SAlexey Zaytsev { 803e0724c53SAlexey Zaytsev struct vq_entry *tmp; 804e0724c53SAlexey Zaytsev struct virtqueue *vq = qe->qe_queue; 805e0724c53SAlexey Zaytsev 806e0724c53SAlexey Zaytsev ASSERT(qe); 807e0724c53SAlexey Zaytsev 808e0724c53SAlexey Zaytsev do { 809e0724c53SAlexey Zaytsev ASSERT(qe->qe_queue == vq); 810e0724c53SAlexey Zaytsev tmp = qe->qe_next; 811e0724c53SAlexey Zaytsev vq_free_entry(vq, qe); 812e0724c53SAlexey Zaytsev qe = tmp; 8138a324c92SDan McDonald } while (tmp != NULL); 814e0724c53SAlexey Zaytsev } 815e0724c53SAlexey Zaytsev 816e0724c53SAlexey Zaytsev void 817e0724c53SAlexey Zaytsev virtio_ventry_stick(struct vq_entry *first, struct vq_entry *second) 818e0724c53SAlexey Zaytsev { 819e0724c53SAlexey Zaytsev first->qe_next = second; 820e0724c53SAlexey Zaytsev } 821e0724c53SAlexey Zaytsev 822e0724c53SAlexey Zaytsev static int 823e0724c53SAlexey Zaytsev virtio_register_msi(struct virtio_softc *sc, 824e0724c53SAlexey Zaytsev struct virtio_int_handler *config_handler, 8258a324c92SDan McDonald struct virtio_int_handler vq_handlers[], int intr_types) 826e0724c53SAlexey Zaytsev { 827e0724c53SAlexey Zaytsev int count, actual; 828e0724c53SAlexey Zaytsev int int_type; 829e0724c53SAlexey Zaytsev int i; 830e0724c53SAlexey Zaytsev int handler_count; 831e0724c53SAlexey Zaytsev int ret; 832e0724c53SAlexey Zaytsev 833e0724c53SAlexey Zaytsev /* If both MSI and MSI-x are reported, prefer MSI-x. */ 834e0724c53SAlexey Zaytsev int_type = DDI_INTR_TYPE_MSI; 835e0724c53SAlexey Zaytsev if (intr_types & DDI_INTR_TYPE_MSIX) 836e0724c53SAlexey Zaytsev int_type = DDI_INTR_TYPE_MSIX; 837e0724c53SAlexey Zaytsev 838e0724c53SAlexey Zaytsev /* Walk the handler table to get the number of handlers. */ 839e0724c53SAlexey Zaytsev for (handler_count = 0; 840e0724c53SAlexey Zaytsev vq_handlers && vq_handlers[handler_count].vh_func; 841e0724c53SAlexey Zaytsev handler_count++) 842e0724c53SAlexey Zaytsev ; 843e0724c53SAlexey Zaytsev 844e0724c53SAlexey Zaytsev /* +1 if there is a config change handler. */ 8458a324c92SDan McDonald if (config_handler != NULL) 846e0724c53SAlexey Zaytsev handler_count++; 847e0724c53SAlexey Zaytsev 848e0724c53SAlexey Zaytsev /* Number of MSIs supported by the device. */ 849e0724c53SAlexey Zaytsev ret = ddi_intr_get_nintrs(sc->sc_dev, int_type, &count); 850e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 851e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, "ddi_intr_get_nintrs failed"); 852e0724c53SAlexey Zaytsev return (ret); 853e0724c53SAlexey Zaytsev } 854e0724c53SAlexey Zaytsev 855e0724c53SAlexey Zaytsev /* 856e0724c53SAlexey Zaytsev * Those who try to register more handlers then the device 857e0724c53SAlexey Zaytsev * supports shall suffer. 858e0724c53SAlexey Zaytsev */ 859e0724c53SAlexey Zaytsev ASSERT(handler_count <= count); 860e0724c53SAlexey Zaytsev 8618a324c92SDan McDonald sc->sc_intr_htable = kmem_zalloc(sizeof (ddi_intr_handle_t) * 8628a324c92SDan McDonald handler_count, KM_SLEEP); 863e0724c53SAlexey Zaytsev 864e0724c53SAlexey Zaytsev ret = ddi_intr_alloc(sc->sc_dev, sc->sc_intr_htable, int_type, 0, 865e0724c53SAlexey Zaytsev handler_count, &actual, DDI_INTR_ALLOC_NORMAL); 866e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 867e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, "Failed to allocate MSI: %d", ret); 868e0724c53SAlexey Zaytsev goto out_msi_alloc; 869e0724c53SAlexey Zaytsev } 870e0724c53SAlexey Zaytsev 871e0724c53SAlexey Zaytsev if (actual != handler_count) { 872e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 873e0724c53SAlexey Zaytsev "Not enough MSI available: need %d, available %d", 874e0724c53SAlexey Zaytsev handler_count, actual); 875e0724c53SAlexey Zaytsev goto out_msi_available; 876e0724c53SAlexey Zaytsev } 877e0724c53SAlexey Zaytsev 878e0724c53SAlexey Zaytsev sc->sc_intr_num = handler_count; 879e0724c53SAlexey Zaytsev sc->sc_intr_config = B_FALSE; 8808a324c92SDan McDonald if (config_handler != NULL) { 881e0724c53SAlexey Zaytsev sc->sc_intr_config = B_TRUE; 882e0724c53SAlexey Zaytsev } 883e0724c53SAlexey Zaytsev 884e0724c53SAlexey Zaytsev /* Assume they are all same priority */ 885e0724c53SAlexey Zaytsev ret = ddi_intr_get_pri(sc->sc_intr_htable[0], &sc->sc_intr_prio); 886e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 887e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, "ddi_intr_get_pri failed"); 888e0724c53SAlexey Zaytsev goto out_msi_prio; 889e0724c53SAlexey Zaytsev } 890e0724c53SAlexey Zaytsev 891e0724c53SAlexey Zaytsev /* Add the vq handlers */ 892e0724c53SAlexey Zaytsev for (i = 0; vq_handlers[i].vh_func; i++) { 893e0724c53SAlexey Zaytsev ret = ddi_intr_add_handler(sc->sc_intr_htable[i], 8948a324c92SDan McDonald vq_handlers[i].vh_func, sc, vq_handlers[i].vh_priv); 895e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 896e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 897e0724c53SAlexey Zaytsev "ddi_intr_add_handler failed"); 898e0724c53SAlexey Zaytsev /* Remove the handlers that succeeded. */ 899e0724c53SAlexey Zaytsev while (--i >= 0) { 900e0724c53SAlexey Zaytsev (void) ddi_intr_remove_handler( 901e0724c53SAlexey Zaytsev sc->sc_intr_htable[i]); 902e0724c53SAlexey Zaytsev } 903e0724c53SAlexey Zaytsev goto out_add_handlers; 904e0724c53SAlexey Zaytsev } 905e0724c53SAlexey Zaytsev } 906e0724c53SAlexey Zaytsev 907e0724c53SAlexey Zaytsev /* Don't forget the config handler */ 9088a324c92SDan McDonald if (config_handler != NULL) { 909e0724c53SAlexey Zaytsev ret = ddi_intr_add_handler(sc->sc_intr_htable[i], 9108a324c92SDan McDonald config_handler->vh_func, sc, config_handler->vh_priv); 911e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 912e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 913e0724c53SAlexey Zaytsev "ddi_intr_add_handler failed"); 914e0724c53SAlexey Zaytsev /* Remove the handlers that succeeded. */ 915e0724c53SAlexey Zaytsev while (--i >= 0) { 916e0724c53SAlexey Zaytsev (void) ddi_intr_remove_handler( 917e0724c53SAlexey Zaytsev sc->sc_intr_htable[i]); 918e0724c53SAlexey Zaytsev } 919e0724c53SAlexey Zaytsev goto out_add_handlers; 920e0724c53SAlexey Zaytsev } 921e0724c53SAlexey Zaytsev } 922e0724c53SAlexey Zaytsev 9238a324c92SDan McDonald ret = ddi_intr_get_cap(sc->sc_intr_htable[0], &sc->sc_intr_cap); 924*17ad7f9fSAndriy Gapon if (ret == DDI_SUCCESS) { 925*17ad7f9fSAndriy Gapon sc->sc_int_type = int_type; 926*17ad7f9fSAndriy Gapon return (DDI_SUCCESS); 927*17ad7f9fSAndriy Gapon } 928e0724c53SAlexey Zaytsev 929e0724c53SAlexey Zaytsev out_add_handlers: 930e0724c53SAlexey Zaytsev out_msi_prio: 931e0724c53SAlexey Zaytsev out_msi_available: 932e0724c53SAlexey Zaytsev for (i = 0; i < actual; i++) 933e0724c53SAlexey Zaytsev (void) ddi_intr_free(sc->sc_intr_htable[i]); 934e0724c53SAlexey Zaytsev out_msi_alloc: 935*17ad7f9fSAndriy Gapon kmem_free(sc->sc_intr_htable, 936*17ad7f9fSAndriy Gapon sizeof (ddi_intr_handle_t) * handler_count); 937e0724c53SAlexey Zaytsev 938e0724c53SAlexey Zaytsev return (ret); 939e0724c53SAlexey Zaytsev } 940e0724c53SAlexey Zaytsev 941e0724c53SAlexey Zaytsev struct virtio_handler_container { 942e0724c53SAlexey Zaytsev int nhandlers; 943e0724c53SAlexey Zaytsev struct virtio_int_handler config_handler; 944e0724c53SAlexey Zaytsev struct virtio_int_handler vq_handlers[]; 945e0724c53SAlexey Zaytsev }; 946e0724c53SAlexey Zaytsev 947e0724c53SAlexey Zaytsev uint_t 948e0724c53SAlexey Zaytsev virtio_intx_dispatch(caddr_t arg1, caddr_t arg2) 949e0724c53SAlexey Zaytsev { 950e0724c53SAlexey Zaytsev struct virtio_softc *sc = (void *)arg1; 951e0724c53SAlexey Zaytsev struct virtio_handler_container *vhc = (void *)arg2; 952e0724c53SAlexey Zaytsev uint8_t isr_status; 953e0724c53SAlexey Zaytsev int i; 954e0724c53SAlexey Zaytsev 955e0724c53SAlexey Zaytsev isr_status = ddi_get8(sc->sc_ioh, (uint8_t *)(sc->sc_io_addr + 956e0724c53SAlexey Zaytsev VIRTIO_CONFIG_ISR_STATUS)); 957e0724c53SAlexey Zaytsev 958e0724c53SAlexey Zaytsev if (!isr_status) 959e0724c53SAlexey Zaytsev return (DDI_INTR_UNCLAIMED); 960e0724c53SAlexey Zaytsev 961e0724c53SAlexey Zaytsev if ((isr_status & VIRTIO_CONFIG_ISR_CONFIG_CHANGE) && 962e0724c53SAlexey Zaytsev vhc->config_handler.vh_func) { 963e0724c53SAlexey Zaytsev vhc->config_handler.vh_func((void *)sc, 964e0724c53SAlexey Zaytsev vhc->config_handler.vh_priv); 965e0724c53SAlexey Zaytsev } 966e0724c53SAlexey Zaytsev 967e0724c53SAlexey Zaytsev /* Notify all handlers */ 968e0724c53SAlexey Zaytsev for (i = 0; i < vhc->nhandlers; i++) { 969e0724c53SAlexey Zaytsev vhc->vq_handlers[i].vh_func((void *)sc, 970e0724c53SAlexey Zaytsev vhc->vq_handlers[i].vh_priv); 971e0724c53SAlexey Zaytsev } 972e0724c53SAlexey Zaytsev 973e0724c53SAlexey Zaytsev return (DDI_INTR_CLAIMED); 974e0724c53SAlexey Zaytsev } 975e0724c53SAlexey Zaytsev 976e0724c53SAlexey Zaytsev /* 977e0724c53SAlexey Zaytsev * config_handler and vq_handlers may be allocated on stack. 978e0724c53SAlexey Zaytsev * Take precautions not to loose them. 979e0724c53SAlexey Zaytsev */ 980e0724c53SAlexey Zaytsev static int 981e0724c53SAlexey Zaytsev virtio_register_intx(struct virtio_softc *sc, 982e0724c53SAlexey Zaytsev struct virtio_int_handler *config_handler, 983e0724c53SAlexey Zaytsev struct virtio_int_handler vq_handlers[]) 984e0724c53SAlexey Zaytsev { 985e0724c53SAlexey Zaytsev int vq_handler_count; 986e0724c53SAlexey Zaytsev int config_handler_count = 0; 987e0724c53SAlexey Zaytsev int actual; 988e0724c53SAlexey Zaytsev struct virtio_handler_container *vhc; 989e0724c53SAlexey Zaytsev int ret = DDI_FAILURE; 990e0724c53SAlexey Zaytsev 991e0724c53SAlexey Zaytsev /* Walk the handler table to get the number of handlers. */ 992e0724c53SAlexey Zaytsev for (vq_handler_count = 0; 993e0724c53SAlexey Zaytsev vq_handlers && vq_handlers[vq_handler_count].vh_func; 994e0724c53SAlexey Zaytsev vq_handler_count++) 995e0724c53SAlexey Zaytsev ; 996e0724c53SAlexey Zaytsev 9978a324c92SDan McDonald if (config_handler != NULL) 998e0724c53SAlexey Zaytsev config_handler_count = 1; 999e0724c53SAlexey Zaytsev 1000e0724c53SAlexey Zaytsev vhc = kmem_zalloc(sizeof (struct virtio_handler_container) + 10018a324c92SDan McDonald sizeof (struct virtio_int_handler) * vq_handler_count, KM_SLEEP); 1002e0724c53SAlexey Zaytsev 1003e0724c53SAlexey Zaytsev vhc->nhandlers = vq_handler_count; 1004e0724c53SAlexey Zaytsev (void) memcpy(vhc->vq_handlers, vq_handlers, 1005e0724c53SAlexey Zaytsev sizeof (struct virtio_int_handler) * vq_handler_count); 1006e0724c53SAlexey Zaytsev 10078a324c92SDan McDonald if (config_handler != NULL) { 1008e0724c53SAlexey Zaytsev (void) memcpy(&vhc->config_handler, config_handler, 1009e0724c53SAlexey Zaytsev sizeof (struct virtio_int_handler)); 1010e0724c53SAlexey Zaytsev } 1011e0724c53SAlexey Zaytsev 1012e0724c53SAlexey Zaytsev /* Just a single entry for a single interrupt. */ 1013e0724c53SAlexey Zaytsev sc->sc_intr_htable = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP); 1014e0724c53SAlexey Zaytsev 1015e0724c53SAlexey Zaytsev ret = ddi_intr_alloc(sc->sc_dev, sc->sc_intr_htable, 10168a324c92SDan McDonald DDI_INTR_TYPE_FIXED, 0, 1, &actual, DDI_INTR_ALLOC_NORMAL); 1017e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 1018e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 1019e0724c53SAlexey Zaytsev "Failed to allocate a fixed interrupt: %d", ret); 1020e0724c53SAlexey Zaytsev goto out_int_alloc; 1021e0724c53SAlexey Zaytsev } 1022e0724c53SAlexey Zaytsev 1023e0724c53SAlexey Zaytsev ASSERT(actual == 1); 1024e0724c53SAlexey Zaytsev sc->sc_intr_num = 1; 1025e0724c53SAlexey Zaytsev 1026e0724c53SAlexey Zaytsev ret = ddi_intr_get_pri(sc->sc_intr_htable[0], &sc->sc_intr_prio); 1027e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 1028e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, "ddi_intr_get_pri failed"); 1029e0724c53SAlexey Zaytsev goto out_prio; 1030e0724c53SAlexey Zaytsev } 1031e0724c53SAlexey Zaytsev 1032e0724c53SAlexey Zaytsev ret = ddi_intr_add_handler(sc->sc_intr_htable[0], 1033e0724c53SAlexey Zaytsev virtio_intx_dispatch, sc, vhc); 1034e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 1035e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, "ddi_intr_add_handler failed"); 1036e0724c53SAlexey Zaytsev goto out_add_handlers; 1037e0724c53SAlexey Zaytsev } 1038e0724c53SAlexey Zaytsev 1039*17ad7f9fSAndriy Gapon sc->sc_int_type = DDI_INTR_TYPE_FIXED; 1040e0724c53SAlexey Zaytsev 1041e0724c53SAlexey Zaytsev return (DDI_SUCCESS); 1042e0724c53SAlexey Zaytsev 1043e0724c53SAlexey Zaytsev out_add_handlers: 1044e0724c53SAlexey Zaytsev out_prio: 1045e0724c53SAlexey Zaytsev (void) ddi_intr_free(sc->sc_intr_htable[0]); 1046e0724c53SAlexey Zaytsev out_int_alloc: 1047e0724c53SAlexey Zaytsev kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t)); 1048e0724c53SAlexey Zaytsev kmem_free(vhc, sizeof (struct virtio_int_handler) * 1049e0724c53SAlexey Zaytsev (vq_handler_count + config_handler_count)); 1050e0724c53SAlexey Zaytsev return (ret); 1051e0724c53SAlexey Zaytsev } 1052e0724c53SAlexey Zaytsev 1053e0724c53SAlexey Zaytsev /* 1054e0724c53SAlexey Zaytsev * We find out if we support MSI during this, and the register layout 1055e0724c53SAlexey Zaytsev * depends on the MSI (doh). Don't acces the device specific bits in 1056e0724c53SAlexey Zaytsev * BAR 0 before calling it! 1057e0724c53SAlexey Zaytsev */ 1058e0724c53SAlexey Zaytsev int 1059e0724c53SAlexey Zaytsev virtio_register_ints(struct virtio_softc *sc, 1060e0724c53SAlexey Zaytsev struct virtio_int_handler *config_handler, 1061e0724c53SAlexey Zaytsev struct virtio_int_handler vq_handlers[]) 1062e0724c53SAlexey Zaytsev { 1063e0724c53SAlexey Zaytsev int ret; 1064e0724c53SAlexey Zaytsev int intr_types; 1065e0724c53SAlexey Zaytsev 1066*17ad7f9fSAndriy Gapon /* Default offset until MSI-X is enabled, if ever. */ 1067*17ad7f9fSAndriy Gapon sc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSIX; 1068*17ad7f9fSAndriy Gapon 1069e0724c53SAlexey Zaytsev /* Determine which types of interrupts are supported */ 1070e0724c53SAlexey Zaytsev ret = ddi_intr_get_supported_types(sc->sc_dev, &intr_types); 1071e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 1072e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, "Can't get supported int types"); 1073e0724c53SAlexey Zaytsev goto out_inttype; 1074e0724c53SAlexey Zaytsev } 1075e0724c53SAlexey Zaytsev 1076e0724c53SAlexey Zaytsev /* If we have msi, let's use them. */ 1077e0724c53SAlexey Zaytsev if (intr_types & (DDI_INTR_TYPE_MSIX | DDI_INTR_TYPE_MSI)) { 1078e0724c53SAlexey Zaytsev ret = virtio_register_msi(sc, config_handler, 1079e0724c53SAlexey Zaytsev vq_handlers, intr_types); 1080e0724c53SAlexey Zaytsev if (!ret) 1081e0724c53SAlexey Zaytsev return (0); 1082e0724c53SAlexey Zaytsev } 1083e0724c53SAlexey Zaytsev 1084e0724c53SAlexey Zaytsev /* Fall back to old-fashioned interrupts. */ 1085e0724c53SAlexey Zaytsev if (intr_types & DDI_INTR_TYPE_FIXED) { 1086e0724c53SAlexey Zaytsev dev_debug(sc->sc_dev, CE_WARN, 1087e0724c53SAlexey Zaytsev "Using legacy interrupts"); 1088e0724c53SAlexey Zaytsev 1089e0724c53SAlexey Zaytsev return (virtio_register_intx(sc, config_handler, vq_handlers)); 1090e0724c53SAlexey Zaytsev } 1091e0724c53SAlexey Zaytsev 1092e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 1093e0724c53SAlexey Zaytsev "MSI failed and fixed interrupts not supported. Giving up."); 1094e0724c53SAlexey Zaytsev ret = DDI_FAILURE; 1095e0724c53SAlexey Zaytsev 1096e0724c53SAlexey Zaytsev out_inttype: 1097e0724c53SAlexey Zaytsev return (ret); 1098e0724c53SAlexey Zaytsev } 1099e0724c53SAlexey Zaytsev 1100e0724c53SAlexey Zaytsev static int 1101e0724c53SAlexey Zaytsev virtio_enable_msi(struct virtio_softc *sc) 1102e0724c53SAlexey Zaytsev { 1103e0724c53SAlexey Zaytsev int ret, i; 1104e0724c53SAlexey Zaytsev int vq_handler_count = sc->sc_intr_num; 1105e0724c53SAlexey Zaytsev 1106e0724c53SAlexey Zaytsev /* Number of handlers, not counting the counfig. */ 1107e0724c53SAlexey Zaytsev if (sc->sc_intr_config) 1108e0724c53SAlexey Zaytsev vq_handler_count--; 1109e0724c53SAlexey Zaytsev 1110*17ad7f9fSAndriy Gapon /* Enable the interrupts. Either the whole block, or one by one. */ 1111e0724c53SAlexey Zaytsev if (sc->sc_intr_cap & DDI_INTR_FLAG_BLOCK) { 1112e0724c53SAlexey Zaytsev ret = ddi_intr_block_enable(sc->sc_intr_htable, 1113e0724c53SAlexey Zaytsev sc->sc_intr_num); 1114e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 1115e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 1116e0724c53SAlexey Zaytsev "Failed to enable MSI, falling back to INTx"); 1117e0724c53SAlexey Zaytsev goto out_enable; 1118e0724c53SAlexey Zaytsev } 1119e0724c53SAlexey Zaytsev } else { 1120e0724c53SAlexey Zaytsev for (i = 0; i < sc->sc_intr_num; i++) { 1121e0724c53SAlexey Zaytsev ret = ddi_intr_enable(sc->sc_intr_htable[i]); 1122e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 1123e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 1124e0724c53SAlexey Zaytsev "Failed to enable MSI %d, " 1125e0724c53SAlexey Zaytsev "falling back to INTx", i); 1126e0724c53SAlexey Zaytsev 1127e0724c53SAlexey Zaytsev while (--i >= 0) { 1128e0724c53SAlexey Zaytsev (void) ddi_intr_disable( 1129e0724c53SAlexey Zaytsev sc->sc_intr_htable[i]); 1130e0724c53SAlexey Zaytsev } 1131e0724c53SAlexey Zaytsev goto out_enable; 1132e0724c53SAlexey Zaytsev } 1133e0724c53SAlexey Zaytsev } 1134e0724c53SAlexey Zaytsev } 1135e0724c53SAlexey Zaytsev 1136e0724c53SAlexey Zaytsev /* Bind the allocated MSI to the queues and config */ 1137e0724c53SAlexey Zaytsev for (i = 0; i < vq_handler_count; i++) { 1138e0724c53SAlexey Zaytsev int check; 11398a324c92SDan McDonald 1140e0724c53SAlexey Zaytsev ddi_put16(sc->sc_ioh, 1141e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 1142e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + 1143e0724c53SAlexey Zaytsev VIRTIO_CONFIG_QUEUE_SELECT), i); 1144e0724c53SAlexey Zaytsev 1145e0724c53SAlexey Zaytsev ddi_put16(sc->sc_ioh, 1146e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 1147e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + 1148e0724c53SAlexey Zaytsev VIRTIO_CONFIG_QUEUE_VECTOR), i); 1149e0724c53SAlexey Zaytsev 1150e0724c53SAlexey Zaytsev check = ddi_get16(sc->sc_ioh, 1151e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 1152e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + 1153e0724c53SAlexey Zaytsev VIRTIO_CONFIG_QUEUE_VECTOR)); 1154e0724c53SAlexey Zaytsev if (check != i) { 1155e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, "Failed to bind handler " 1156e0724c53SAlexey Zaytsev "for VQ %d, MSI %d. Check = %x", i, i, check); 1157e0724c53SAlexey Zaytsev ret = ENODEV; 1158e0724c53SAlexey Zaytsev goto out_bind; 1159e0724c53SAlexey Zaytsev } 1160e0724c53SAlexey Zaytsev } 1161e0724c53SAlexey Zaytsev 1162e0724c53SAlexey Zaytsev if (sc->sc_intr_config) { 1163e0724c53SAlexey Zaytsev int check; 11648a324c92SDan McDonald 1165e0724c53SAlexey Zaytsev ddi_put16(sc->sc_ioh, 1166e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 1167e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + 1168e0724c53SAlexey Zaytsev VIRTIO_CONFIG_CONFIG_VECTOR), i); 1169e0724c53SAlexey Zaytsev 1170e0724c53SAlexey Zaytsev check = ddi_get16(sc->sc_ioh, 1171e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 1172e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + 1173e0724c53SAlexey Zaytsev VIRTIO_CONFIG_CONFIG_VECTOR)); 1174e0724c53SAlexey Zaytsev if (check != i) { 1175e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, "Failed to bind handler " 1176e0724c53SAlexey Zaytsev "for Config updates, MSI %d", i); 1177e0724c53SAlexey Zaytsev ret = ENODEV; 1178e0724c53SAlexey Zaytsev goto out_bind; 1179e0724c53SAlexey Zaytsev } 1180e0724c53SAlexey Zaytsev } 1181e0724c53SAlexey Zaytsev 1182*17ad7f9fSAndriy Gapon /* Configuration offset depends on whether MSI-X is used. */ 1183*17ad7f9fSAndriy Gapon if (sc->sc_int_type == DDI_INTR_TYPE_MSIX) 1184*17ad7f9fSAndriy Gapon sc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_MSIX; 1185*17ad7f9fSAndriy Gapon else 1186*17ad7f9fSAndriy Gapon ASSERT(sc->sc_int_type == DDI_INTR_TYPE_MSI); 1187*17ad7f9fSAndriy Gapon 1188e0724c53SAlexey Zaytsev return (DDI_SUCCESS); 1189e0724c53SAlexey Zaytsev 1190e0724c53SAlexey Zaytsev out_bind: 1191e0724c53SAlexey Zaytsev /* Unbind the vqs */ 1192e0724c53SAlexey Zaytsev for (i = 0; i < vq_handler_count - 1; i++) { 1193e0724c53SAlexey Zaytsev ddi_put16(sc->sc_ioh, 1194e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 1195e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + 1196e0724c53SAlexey Zaytsev VIRTIO_CONFIG_QUEUE_SELECT), i); 1197e0724c53SAlexey Zaytsev 1198e0724c53SAlexey Zaytsev ddi_put16(sc->sc_ioh, 1199e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 1200e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + 1201e0724c53SAlexey Zaytsev VIRTIO_CONFIG_QUEUE_VECTOR), 1202e0724c53SAlexey Zaytsev VIRTIO_MSI_NO_VECTOR); 1203e0724c53SAlexey Zaytsev } 1204e0724c53SAlexey Zaytsev /* And the config */ 1205e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 1206e0724c53SAlexey Zaytsev ddi_put16(sc->sc_ioh, (uint16_t *)(sc->sc_io_addr + 1207e0724c53SAlexey Zaytsev VIRTIO_CONFIG_CONFIG_VECTOR), VIRTIO_MSI_NO_VECTOR); 1208e0724c53SAlexey Zaytsev 1209*17ad7f9fSAndriy Gapon /* Disable the interrupts. Either the whole block, or one by one. */ 1210*17ad7f9fSAndriy Gapon if (sc->sc_intr_cap & DDI_INTR_FLAG_BLOCK) { 1211*17ad7f9fSAndriy Gapon ret = ddi_intr_block_disable(sc->sc_intr_htable, 1212*17ad7f9fSAndriy Gapon sc->sc_intr_num); 1213*17ad7f9fSAndriy Gapon if (ret != DDI_SUCCESS) { 1214*17ad7f9fSAndriy Gapon dev_err(sc->sc_dev, CE_WARN, 1215*17ad7f9fSAndriy Gapon "Failed to disable MSIs, won't be able to " 1216*17ad7f9fSAndriy Gapon "reuse next time"); 1217*17ad7f9fSAndriy Gapon } 1218*17ad7f9fSAndriy Gapon } else { 1219*17ad7f9fSAndriy Gapon for (i = 0; i < sc->sc_intr_num; i++) { 1220*17ad7f9fSAndriy Gapon ret = ddi_intr_disable(sc->sc_intr_htable[i]); 1221*17ad7f9fSAndriy Gapon if (ret != DDI_SUCCESS) { 1222*17ad7f9fSAndriy Gapon dev_err(sc->sc_dev, CE_WARN, 1223*17ad7f9fSAndriy Gapon "Failed to disable interrupt %d, " 1224*17ad7f9fSAndriy Gapon "won't be able to reuse", i); 1225*17ad7f9fSAndriy Gapon } 1226*17ad7f9fSAndriy Gapon } 1227*17ad7f9fSAndriy Gapon } 1228*17ad7f9fSAndriy Gapon 1229e0724c53SAlexey Zaytsev ret = DDI_FAILURE; 1230e0724c53SAlexey Zaytsev 1231e0724c53SAlexey Zaytsev out_enable: 1232e0724c53SAlexey Zaytsev return (ret); 1233e0724c53SAlexey Zaytsev } 1234e0724c53SAlexey Zaytsev 12358a324c92SDan McDonald static int 12368a324c92SDan McDonald virtio_enable_intx(struct virtio_softc *sc) 1237e0724c53SAlexey Zaytsev { 1238e0724c53SAlexey Zaytsev int ret; 1239e0724c53SAlexey Zaytsev 1240e0724c53SAlexey Zaytsev ret = ddi_intr_enable(sc->sc_intr_htable[0]); 12418a324c92SDan McDonald if (ret != DDI_SUCCESS) { 1242e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 1243e0724c53SAlexey Zaytsev "Failed to enable interrupt: %d", ret); 12448a324c92SDan McDonald } 12458a324c92SDan McDonald 1246e0724c53SAlexey Zaytsev return (ret); 1247e0724c53SAlexey Zaytsev } 1248e0724c53SAlexey Zaytsev 1249e0724c53SAlexey Zaytsev /* 1250e0724c53SAlexey Zaytsev * We can't enable/disable individual handlers in the INTx case so do 1251e0724c53SAlexey Zaytsev * the whole bunch even in the msi case. 1252e0724c53SAlexey Zaytsev */ 1253e0724c53SAlexey Zaytsev int 1254e0724c53SAlexey Zaytsev virtio_enable_ints(struct virtio_softc *sc) 1255e0724c53SAlexey Zaytsev { 1256e0724c53SAlexey Zaytsev 1257*17ad7f9fSAndriy Gapon ASSERT(sc->sc_config_offset == VIRTIO_CONFIG_DEVICE_CONFIG_NOMSIX); 1258*17ad7f9fSAndriy Gapon 1259e0724c53SAlexey Zaytsev /* See if we are using MSI. */ 1260*17ad7f9fSAndriy Gapon if (sc->sc_int_type == DDI_INTR_TYPE_MSIX || 1261*17ad7f9fSAndriy Gapon sc->sc_int_type == DDI_INTR_TYPE_MSI) 1262e0724c53SAlexey Zaytsev return (virtio_enable_msi(sc)); 1263e0724c53SAlexey Zaytsev 1264*17ad7f9fSAndriy Gapon ASSERT(sc->sc_int_type == DDI_INTR_TYPE_FIXED); 1265e0724c53SAlexey Zaytsev return (virtio_enable_intx(sc)); 1266e0724c53SAlexey Zaytsev } 1267e0724c53SAlexey Zaytsev 1268e0724c53SAlexey Zaytsev void 1269e0724c53SAlexey Zaytsev virtio_release_ints(struct virtio_softc *sc) 1270e0724c53SAlexey Zaytsev { 1271e0724c53SAlexey Zaytsev int i; 1272e0724c53SAlexey Zaytsev int ret; 1273e0724c53SAlexey Zaytsev 1274e0724c53SAlexey Zaytsev /* We were running with MSI, unbind them. */ 1275*17ad7f9fSAndriy Gapon if (sc->sc_int_type == DDI_INTR_TYPE_MSIX || 1276*17ad7f9fSAndriy Gapon sc->sc_int_type == DDI_INTR_TYPE_MSI) { 1277e0724c53SAlexey Zaytsev /* Unbind all vqs */ 1278e0724c53SAlexey Zaytsev for (i = 0; i < sc->sc_nvqs; i++) { 1279e0724c53SAlexey Zaytsev ddi_put16(sc->sc_ioh, 1280e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 1281e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + 1282e0724c53SAlexey Zaytsev VIRTIO_CONFIG_QUEUE_SELECT), i); 1283e0724c53SAlexey Zaytsev 1284e0724c53SAlexey Zaytsev ddi_put16(sc->sc_ioh, 1285e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 1286e0724c53SAlexey Zaytsev (uint16_t *)(sc->sc_io_addr + 1287e0724c53SAlexey Zaytsev VIRTIO_CONFIG_QUEUE_VECTOR), 1288e0724c53SAlexey Zaytsev VIRTIO_MSI_NO_VECTOR); 1289e0724c53SAlexey Zaytsev } 1290e0724c53SAlexey Zaytsev /* And the config */ 1291e0724c53SAlexey Zaytsev /* LINTED E_BAD_PTR_CAST_ALIGN */ 1292e0724c53SAlexey Zaytsev ddi_put16(sc->sc_ioh, (uint16_t *)(sc->sc_io_addr + 1293e0724c53SAlexey Zaytsev VIRTIO_CONFIG_CONFIG_VECTOR), 1294e0724c53SAlexey Zaytsev VIRTIO_MSI_NO_VECTOR); 1295e0724c53SAlexey Zaytsev 1296e0724c53SAlexey Zaytsev } 1297e0724c53SAlexey Zaytsev 1298*17ad7f9fSAndriy Gapon /* Disable the interrupts. Either the whole block, or one by one. */ 1299e0724c53SAlexey Zaytsev if (sc->sc_intr_cap & DDI_INTR_FLAG_BLOCK) { 1300e0724c53SAlexey Zaytsev ret = ddi_intr_block_disable(sc->sc_intr_htable, 1301e0724c53SAlexey Zaytsev sc->sc_intr_num); 1302e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 1303e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 1304e0724c53SAlexey Zaytsev "Failed to disable MSIs, won't be able to " 1305e0724c53SAlexey Zaytsev "reuse next time"); 1306e0724c53SAlexey Zaytsev } 1307e0724c53SAlexey Zaytsev } else { 1308e0724c53SAlexey Zaytsev for (i = 0; i < sc->sc_intr_num; i++) { 1309e0724c53SAlexey Zaytsev ret = ddi_intr_disable(sc->sc_intr_htable[i]); 1310e0724c53SAlexey Zaytsev if (ret != DDI_SUCCESS) { 1311e0724c53SAlexey Zaytsev dev_err(sc->sc_dev, CE_WARN, 1312e0724c53SAlexey Zaytsev "Failed to disable interrupt %d, " 1313e0724c53SAlexey Zaytsev "won't be able to reuse", i); 1314e0724c53SAlexey Zaytsev } 1315e0724c53SAlexey Zaytsev } 1316e0724c53SAlexey Zaytsev } 1317e0724c53SAlexey Zaytsev 1318e0724c53SAlexey Zaytsev 1319e0724c53SAlexey Zaytsev for (i = 0; i < sc->sc_intr_num; i++) { 1320e0724c53SAlexey Zaytsev (void) ddi_intr_remove_handler(sc->sc_intr_htable[i]); 1321e0724c53SAlexey Zaytsev } 1322e0724c53SAlexey Zaytsev 1323e0724c53SAlexey Zaytsev for (i = 0; i < sc->sc_intr_num; i++) 1324e0724c53SAlexey Zaytsev (void) ddi_intr_free(sc->sc_intr_htable[i]); 1325e0724c53SAlexey Zaytsev 13268a324c92SDan McDonald kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t) * 13278a324c92SDan McDonald sc->sc_intr_num); 1328e0724c53SAlexey Zaytsev 1329*17ad7f9fSAndriy Gapon /* After disabling interrupts, the config offset is non-MSI-X. */ 1330*17ad7f9fSAndriy Gapon sc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSIX; 1331e0724c53SAlexey Zaytsev } 1332e0724c53SAlexey Zaytsev 1333e0724c53SAlexey Zaytsev /* 1334e0724c53SAlexey Zaytsev * Module linkage information for the kernel. 1335e0724c53SAlexey Zaytsev */ 1336e0724c53SAlexey Zaytsev static struct modlmisc modlmisc = { 1337e0724c53SAlexey Zaytsev &mod_miscops, /* Type of module */ 1338e0724c53SAlexey Zaytsev "VirtIO common library module", 1339e0724c53SAlexey Zaytsev }; 1340e0724c53SAlexey Zaytsev 1341e0724c53SAlexey Zaytsev static struct modlinkage modlinkage = { 1342e0724c53SAlexey Zaytsev MODREV_1, 1343e0724c53SAlexey Zaytsev { 1344e0724c53SAlexey Zaytsev (void *)&modlmisc, 1345e0724c53SAlexey Zaytsev NULL 1346e0724c53SAlexey Zaytsev } 1347e0724c53SAlexey Zaytsev }; 1348e0724c53SAlexey Zaytsev 1349e0724c53SAlexey Zaytsev int 1350e0724c53SAlexey Zaytsev _init(void) 1351e0724c53SAlexey Zaytsev { 1352e0724c53SAlexey Zaytsev return (mod_install(&modlinkage)); 1353e0724c53SAlexey Zaytsev } 1354e0724c53SAlexey Zaytsev 1355e0724c53SAlexey Zaytsev int 1356e0724c53SAlexey Zaytsev _fini(void) 1357e0724c53SAlexey Zaytsev { 1358e0724c53SAlexey Zaytsev return (mod_remove(&modlinkage)); 1359e0724c53SAlexey Zaytsev } 1360e0724c53SAlexey Zaytsev 1361e0724c53SAlexey Zaytsev int 1362e0724c53SAlexey Zaytsev _info(struct modinfo *modinfop) 1363e0724c53SAlexey Zaytsev { 1364e0724c53SAlexey Zaytsev return (mod_info(&modlinkage, modinfop)); 1365e0724c53SAlexey Zaytsev } 1366