Module Name: src Committed By: jakllsch Date: Sat Jun 2 22:43:15 UTC 2018
Modified Files: src/sys/dev/pci: virtio.c virtio_pci.c virtiovar.h Log Message: Begin to detangle virtio from its PCI attachment To generate a diff of this commit: cvs rdiff -u -r1.30 -r1.31 src/sys/dev/pci/virtio.c cvs rdiff -u -r1.3 -r1.4 src/sys/dev/pci/virtio_pci.c cvs rdiff -u -r1.10 -r1.11 src/sys/dev/pci/virtiovar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/pci/virtio.c diff -u src/sys/dev/pci/virtio.c:1.30 src/sys/dev/pci/virtio.c:1.31 --- src/sys/dev/pci/virtio.c:1.30 Wed Feb 14 14:04:48 2018 +++ src/sys/dev/pci/virtio.c Sat Jun 2 22:43:15 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: virtio.c,v 1.30 2018/02/14 14:04:48 uwe Exp $ */ +/* $NetBSD: virtio.c,v 1.31 2018/06/02 22:43:15 jakllsch Exp $ */ /* * Copyright (c) 2010 Minoura Makoto. @@ -26,7 +26,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: virtio.c,v 1.30 2018/02/14 14:04:48 uwe Exp $"); +__KERNEL_RCSID(0, "$NetBSD: virtio.c,v 1.31 2018/06/02 22:43:15 jakllsch Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -48,300 +48,15 @@ __KERNEL_RCSID(0, "$NetBSD: virtio.c,v 1 #define MINSEG_INDIRECT 2 /* use indirect if nsegs >= this value */ -static int virtio_intr(void *arg); -static int virtio_msix_queue_intr(void *); -static int virtio_msix_config_intr(void *); -static int virtio_setup_msix_vectors(struct virtio_softc *); -static int virtio_setup_msix_interrupts(struct virtio_softc *, - struct pci_attach_args *); -static int virtio_setup_intx_interrupt(struct virtio_softc *, - struct pci_attach_args *); -static int virtio_setup_interrupts(struct virtio_softc *); -static void virtio_free_interrupts(struct virtio_softc *); -static void virtio_soft_intr(void *arg); static void virtio_init_vq(struct virtio_softc *, struct virtqueue *, const bool); - -/* we use the legacy virtio spec, so the pci registers are host native - * byte order, not pci (i.e. LE) byte order */ -static inline uint16_t -nbo_bus_space_read_2(bus_space_tag_t space, bus_space_handle_t handle, - bus_size_t offset) -{ - return le16toh(bus_space_read_2(space, handle, offset)); -} - -static inline uint32_t -nbo_bus_space_read_4(bus_space_tag_t space, bus_space_handle_t handle, - bus_size_t offset) -{ - return le32toh(bus_space_read_4(space, handle, offset)); -} - -static void -nbo_bus_space_write_2(bus_space_tag_t space, bus_space_handle_t handle, - bus_size_t offset, uint16_t value) -{ - bus_space_write_2(space, handle, offset, htole16(value)); -} - -static void -nbo_bus_space_write_4(bus_space_tag_t space, bus_space_handle_t handle, - bus_size_t offset, uint32_t value) -{ - bus_space_write_4(space, handle, offset, htole32(value)); -} - -/* some functions access registers at 4 byte offset for little/high halves */ -#if BYTE_ORDER == BIG_ENDIAN -#define REG_HI_OFF 0 -#define REG_LO_OFF 4 -#else -#define REG_HI_OFF 4 -#define REG_LO_OFF 0 -#endif - void virtio_set_status(struct virtio_softc *sc, int status) { - int old = 0; - - if (status != 0) - old = bus_space_read_1(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_DEVICE_STATUS); - bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIRTIO_CONFIG_DEVICE_STATUS, - status|old); -} - -#define VIRTIO_MSIX_CONFIG_VECTOR_INDEX 0 -#define VIRTIO_MSIX_QUEUE_VECTOR_INDEX 1 - -static int -virtio_setup_msix_vectors(struct virtio_softc *sc) -{ - int offset, vector, ret, qid; - - offset = VIRTIO_CONFIG_MSI_CONFIG_VECTOR; - vector = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; - - nbo_bus_space_write_2(sc->sc_iot, sc->sc_ioh, offset, vector); - ret = nbo_bus_space_read_2(sc->sc_iot, sc->sc_ioh, offset); - aprint_debug_dev(sc->sc_dev, "expected=%d, actual=%d\n", - vector, ret); - if (ret != vector) - return -1; - - for (qid = 0; qid < sc->sc_nvqs; qid++) { - offset = VIRTIO_CONFIG_QUEUE_SELECT; - nbo_bus_space_write_2(sc->sc_iot, sc->sc_ioh, offset, qid); - - offset = VIRTIO_CONFIG_MSI_QUEUE_VECTOR; - vector = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; - - nbo_bus_space_write_2(sc->sc_iot, sc->sc_ioh, offset, vector); - ret = nbo_bus_space_read_2(sc->sc_iot, sc->sc_ioh, offset); - aprint_debug_dev(sc->sc_dev, "expected=%d, actual=%d\n", - vector, ret); - if (ret != vector) - return -1; - } - - return 0; -} - -static int -virtio_setup_msix_interrupts(struct virtio_softc *sc, - struct pci_attach_args *pa) -{ - device_t self = sc->sc_dev; - pci_chipset_tag_t pc = pa->pa_pc; - char intrbuf[PCI_INTRSTR_LEN]; - char const *intrstr; - int idx; - - idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; - if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE) - pci_intr_setattr(pc, &sc->sc_ihp[idx], PCI_INTR_MPSAFE, true); - - sc->sc_ihs[idx] = pci_intr_establish_xname(pc, sc->sc_ihp[idx], - sc->sc_ipl, virtio_msix_config_intr, sc, device_xname(sc->sc_dev)); - if (sc->sc_ihs[idx] == NULL) { - aprint_error_dev(self, "couldn't establish MSI-X for config\n"); - goto error; - } - - idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; - if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE) - pci_intr_setattr(pc, &sc->sc_ihp[idx], PCI_INTR_MPSAFE, true); - - sc->sc_ihs[idx] = pci_intr_establish_xname(pc, sc->sc_ihp[idx], - sc->sc_ipl, virtio_msix_queue_intr, sc, device_xname(sc->sc_dev)); - if (sc->sc_ihs[idx] == NULL) { - aprint_error_dev(self, "couldn't establish MSI-X for queues\n"); - goto error; - } - - if (virtio_setup_msix_vectors(sc) != 0) { - aprint_error_dev(self, "couldn't setup MSI-X vectors\n"); - goto error; - } - - idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; - intrstr = pci_intr_string(pc, sc->sc_ihp[idx], intrbuf, sizeof(intrbuf)); - aprint_normal_dev(self, "config interrupting at %s\n", intrstr); - idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; - intrstr = pci_intr_string(pc, sc->sc_ihp[idx], intrbuf, sizeof(intrbuf)); - aprint_normal_dev(self, "queues interrupting at %s\n", intrstr); - - return 0; - -error: - idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; - if (sc->sc_ihs[idx] != NULL) - pci_intr_disestablish(sc->sc_pc, sc->sc_ihs[idx]); - idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; - if (sc->sc_ihs[idx] != NULL) - pci_intr_disestablish(sc->sc_pc, sc->sc_ihs[idx]); - - return -1; -} - -static int -virtio_setup_intx_interrupt(struct virtio_softc *sc, - struct pci_attach_args *pa) -{ - device_t self = sc->sc_dev; - pci_chipset_tag_t pc = pa->pa_pc; - char intrbuf[PCI_INTRSTR_LEN]; - char const *intrstr; - - if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE) - pci_intr_setattr(pc, &sc->sc_ihp[0], PCI_INTR_MPSAFE, true); - - sc->sc_ihs[0] = pci_intr_establish_xname(pc, sc->sc_ihp[0], - sc->sc_ipl, virtio_intr, sc, device_xname(sc->sc_dev)); - if (sc->sc_ihs[0] == NULL) { - aprint_error_dev(self, "couldn't establish INTx\n"); - return -1; - } - - intrstr = pci_intr_string(pc, sc->sc_ihp[0], intrbuf, sizeof(intrbuf)); - aprint_normal_dev(self, "interrupting at %s\n", intrstr); - - return 0; -} - -static int -virtio_setup_interrupts(struct virtio_softc *sc) -{ - device_t self = sc->sc_dev; - pci_chipset_tag_t pc = sc->sc_pa.pa_pc; - int error; - int nmsix; - int counts[PCI_INTR_TYPE_SIZE]; - pci_intr_type_t max_type; - - nmsix = pci_msix_count(sc->sc_pa.pa_pc, sc->sc_pa.pa_tag); - aprint_debug_dev(self, "pci_msix_count=%d\n", nmsix); - - /* We need at least two: one for config and the other for queues */ - if ((sc->sc_flags & VIRTIO_F_PCI_INTR_MSIX) == 0 || nmsix < 2) { - /* Try INTx only */ - max_type = PCI_INTR_TYPE_INTX; - counts[PCI_INTR_TYPE_INTX] = 1; - } else { - /* Try MSI-X first and INTx second */ - max_type = PCI_INTR_TYPE_MSIX; - counts[PCI_INTR_TYPE_MSIX] = 2; - counts[PCI_INTR_TYPE_MSI] = 0; - counts[PCI_INTR_TYPE_INTX] = 1; - } - - retry: - error = pci_intr_alloc(&sc->sc_pa, &sc->sc_ihp, counts, max_type); - if (error != 0) { - aprint_error_dev(self, "couldn't map interrupt\n"); - return -1; - } - - if (pci_intr_type(pc, sc->sc_ihp[0]) == PCI_INTR_TYPE_MSIX) { - sc->sc_ihs = kmem_alloc(sizeof(*sc->sc_ihs) * 2, - KM_SLEEP); - - error = virtio_setup_msix_interrupts(sc, &sc->sc_pa); - if (error != 0) { - kmem_free(sc->sc_ihs, sizeof(*sc->sc_ihs) * 2); - pci_intr_release(pc, sc->sc_ihp, 2); - - /* Retry INTx */ - max_type = PCI_INTR_TYPE_INTX; - counts[PCI_INTR_TYPE_INTX] = 1; - goto retry; - } - - sc->sc_ihs_num = 2; - sc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_MSI; - } else if (pci_intr_type(pc, sc->sc_ihp[0]) == PCI_INTR_TYPE_INTX) { - sc->sc_ihs = kmem_alloc(sizeof(*sc->sc_ihs) * 1, - KM_SLEEP); - - error = virtio_setup_intx_interrupt(sc, &sc->sc_pa); - if (error != 0) { - kmem_free(sc->sc_ihs, sizeof(*sc->sc_ihs) * 1); - pci_intr_release(pc, sc->sc_ihp, 1); - return -1; - } - - sc->sc_ihs_num = 1; - sc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI; - } - - KASSERT(sc->sc_soft_ih == NULL); - if (sc->sc_flags & VIRTIO_F_PCI_INTR_SOFTINT) { - u_int flags = SOFTINT_NET; - if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE) - flags |= SOFTINT_MPSAFE; - - sc->sc_soft_ih = softint_establish(flags, virtio_soft_intr, sc); - if (sc->sc_soft_ih == NULL) { - virtio_free_interrupts(sc); - aprint_error_dev(sc->sc_dev, - "failed to establish soft interrupt\n"); - return -1; - } - } - - return 0; + sc->sc_ops->set_status(sc, status); } -static void -virtio_free_interrupts(struct virtio_softc *sc) -{ - for (int i = 0; i < sc->sc_ihs_num; i++) { - if (sc->sc_ihs[i] == NULL) - continue; - pci_intr_disestablish(sc->sc_pc, sc->sc_ihs[i]); - sc->sc_ihs[i] = NULL; - } - - if (sc->sc_ihs_num > 0) - pci_intr_release(sc->sc_pc, sc->sc_ihp, sc->sc_ihs_num); - - if (sc->sc_soft_ih) { - softint_disestablish(sc->sc_soft_ih); - sc->sc_soft_ih = NULL; - } - - if (sc->sc_ihs != NULL) { - kmem_free(sc->sc_ihs, sizeof(*sc->sc_ihs) * sc->sc_ihs_num); - sc->sc_ihs = NULL; - } - sc->sc_ihs_num = 0; -} - - - /* * Reset the device. */ @@ -372,11 +87,7 @@ virtio_reinit_start(struct virtio_softc for (i = 0; i < sc->sc_nvqs; i++) { int n; struct virtqueue *vq = &sc->sc_vqs[i]; - nbo_bus_space_write_2(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_QUEUE_SELECT, - vq->vq_index); - n = nbo_bus_space_read_2(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_QUEUE_SIZE); + n = sc->sc_ops->read_queue_size(sc, vq->vq_index); if (n == 0) /* vq disappeared */ continue; if (n != vq->vq_num) { @@ -385,19 +96,8 @@ virtio_reinit_start(struct virtio_softc vq->vq_index); } virtio_init_vq(sc, vq, true); - nbo_bus_space_write_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_QUEUE_ADDRESS, - (vq->vq_dmamap->dm_segs[0].ds_addr - / VIRTIO_PAGE_SIZE)); - } - - /* MSI-X should have more than one handles where INTx has just one */ - if (sc->sc_ihs_num > 1) { - if (virtio_setup_msix_vectors(sc) != 0) { - aprint_error_dev(sc->sc_dev, - "couldn't setup MSI-X vectors\n"); - return; - } + sc->sc_ops->setup_queue(sc, vq->vq_index, + vq->vq_dmamap->dm_segs[0].ds_addr / VIRTIO_PAGE_SIZE); } } @@ -418,11 +118,7 @@ virtio_negotiate_features(struct virtio_ if (!(device_cfdata(sc->sc_dev)->cf_flags & 1) && !(device_cfdata(sc->sc_child)->cf_flags & 1)) /* XXX */ guest_features |= VIRTIO_F_RING_INDIRECT_DESC; - r = nbo_bus_space_read_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_DEVICE_FEATURES); - r &= guest_features; - nbo_bus_space_write_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_GUEST_FEATURES, r); + r = sc->sc_ops->neg_features(sc, guest_features); sc->sc_features = r; if (r & VIRTIO_F_RING_INDIRECT_DESC) sc->sc_indirect = true; @@ -438,128 +134,58 @@ virtio_negotiate_features(struct virtio_ uint8_t virtio_read_device_config_1(struct virtio_softc *sc, int index) { - return bus_space_read_1(sc->sc_iot, sc->sc_ioh, - sc->sc_config_offset + index); + return sc->sc_ops->read_dev_cfg_1(sc, index); } uint16_t virtio_read_device_config_2(struct virtio_softc *sc, int index) { - return nbo_bus_space_read_2(sc->sc_iot, sc->sc_ioh, - sc->sc_config_offset + index); + return sc->sc_ops->read_dev_cfg_2(sc, index); } uint32_t virtio_read_device_config_4(struct virtio_softc *sc, int index) { - return nbo_bus_space_read_4(sc->sc_iot, sc->sc_ioh, - sc->sc_config_offset + index); + return sc->sc_ops->read_dev_cfg_4(sc, index); } uint64_t virtio_read_device_config_8(struct virtio_softc *sc, int index) { - uint64_t r; - - r = nbo_bus_space_read_4(sc->sc_iot, sc->sc_ioh, - sc->sc_config_offset + index + REG_HI_OFF); - r <<= 32; - r |= nbo_bus_space_read_4(sc->sc_iot, sc->sc_ioh, - sc->sc_config_offset + index + REG_LO_OFF); - - return r; + return sc->sc_ops->read_dev_cfg_8(sc, index); } void virtio_write_device_config_1(struct virtio_softc *sc, int index, uint8_t value) { - bus_space_write_1(sc->sc_iot, sc->sc_ioh, - sc->sc_config_offset + index, value); + return sc->sc_ops->write_dev_cfg_1(sc, index, value); } void virtio_write_device_config_2(struct virtio_softc *sc, int index, uint16_t value) { - nbo_bus_space_write_2(sc->sc_iot, sc->sc_ioh, - sc->sc_config_offset + index, value); + return sc->sc_ops->write_dev_cfg_2(sc, index, value); } void virtio_write_device_config_4(struct virtio_softc *sc, int index, uint32_t value) { - nbo_bus_space_write_4(sc->sc_iot, sc->sc_ioh, - sc->sc_config_offset + index, value); + return sc->sc_ops->write_dev_cfg_4(sc, index, value); } void virtio_write_device_config_8(struct virtio_softc *sc, int index, uint64_t value) { - nbo_bus_space_write_4(sc->sc_iot, sc->sc_ioh, - sc->sc_config_offset + index + REG_LO_OFF, - value & 0xffffffff); - nbo_bus_space_write_4(sc->sc_iot, sc->sc_ioh, - sc->sc_config_offset + index + REG_HI_OFF, - value >> 32); + return sc->sc_ops->write_dev_cfg_8(sc, index, value); } /* * Interrupt handler. */ -static int -virtio_intr(void *arg) -{ - struct virtio_softc *sc = arg; - int isr, r = 0; - - /* check and ack the interrupt */ - isr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_ISR_STATUS); - if (isr == 0) - return 0; - if ((isr & VIRTIO_CONFIG_ISR_CONFIG_CHANGE) && - (sc->sc_config_change != NULL)) - r = (sc->sc_config_change)(sc); - if (sc->sc_intrhand != NULL) { - if (sc->sc_soft_ih != NULL) - softint_schedule(sc->sc_soft_ih); - else - r |= (sc->sc_intrhand)(sc); - } - - return r; -} - -static int -virtio_msix_queue_intr(void *arg) -{ - struct virtio_softc *sc = arg; - int r = 0; - - if (sc->sc_intrhand != NULL) { - if (sc->sc_soft_ih != NULL) - softint_schedule(sc->sc_soft_ih); - else - r |= (sc->sc_intrhand)(sc); - } - - return r; -} - -static int -virtio_msix_config_intr(void *arg) -{ - struct virtio_softc *sc = arg; - int r = 0; - - if (sc->sc_config_change != NULL) - r = (sc->sc_config_change)(sc); - return r; -} - static void virtio_soft_intr(void *arg) { @@ -727,10 +353,7 @@ virtio_alloc_vq(struct virtio_softc *sc, memset(vq, 0, sizeof(*vq)); - nbo_bus_space_write_2(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_QUEUE_SELECT, index); - vq_size = nbo_bus_space_read_2(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_QUEUE_SIZE); + vq_size = sc->sc_ops->read_queue_size(sc, index); if (vq_size == 0) { aprint_error_dev(sc->sc_dev, "virtqueue not exist, index %d for %s\n", @@ -785,10 +408,8 @@ virtio_alloc_vq(struct virtio_softc *sc, } /* set the vq address */ - nbo_bus_space_write_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_QUEUE_ADDRESS, - (vq->vq_dmamap->dm_segs[0].ds_addr - / VIRTIO_PAGE_SIZE)); + sc->sc_ops->setup_queue(sc, vq->vq_index, + vq->vq_dmamap->dm_segs[0].ds_addr / VIRTIO_PAGE_SIZE); /* remember addresses and offsets for later use */ vq->vq_owner = sc; @@ -832,8 +453,7 @@ virtio_alloc_vq(struct virtio_softc *sc, return 0; err: - nbo_bus_space_write_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_QUEUE_ADDRESS, 0); + sc->sc_ops->setup_queue(sc, vq->vq_index, 0); if (vq->vq_dmamap) bus_dmamap_destroy(sc->sc_dmat, vq->vq_dmamap); if (vq->vq_vaddr) @@ -863,10 +483,7 @@ virtio_free_vq(struct virtio_softc *sc, } /* tell device that there's no virtqueue any longer */ - nbo_bus_space_write_2(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_QUEUE_SELECT, vq->vq_index); - nbo_bus_space_write_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_QUEUE_ADDRESS, 0); + sc->sc_ops->setup_queue(sc, vq->vq_index, 0); kmem_free(vq->vq_entries, sizeof(*vq->vq_entries) * vq->vq_num); bus_dmamap_unload(sc->sc_dmat, vq->vq_dmamap); @@ -1117,9 +734,7 @@ notify: vq_sync_uring(sc, vq, BUS_DMASYNC_POSTREAD); membar_consumer(); if (!(vq->vq_used->flags & VRING_USED_F_NO_NOTIFY)) - nbo_bus_space_write_2(sc->sc_iot, sc->sc_ioh, - VIRTIO_CONFIG_QUEUE_NOTIFY, - vq->vq_index); + sc->sc_ops->kick(sc, vq->vq_index); } mutex_exit(&vq->vq_aring_lock); @@ -1238,16 +853,33 @@ virtio_child_attach_finish(struct virtio { int r; - r = virtio_setup_interrupts(sc); + r = sc->sc_ops->setup_interrupts(sc); if (r != 0) { aprint_error_dev(sc->sc_dev, "failed to setup interrupts\n"); - virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_FAILED); - return 1; + goto fail; } - virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK); + KASSERT(sc->sc_soft_ih == NULL); + if (sc->sc_flags & VIRTIO_F_PCI_INTR_SOFTINT) { + u_int flags = SOFTINT_NET; + if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE) + flags |= SOFTINT_MPSAFE; + + sc->sc_soft_ih = softint_establish(flags, virtio_soft_intr, sc); + if (sc->sc_soft_ih == NULL) { + sc->sc_ops->free_interrupts(sc); + aprint_error_dev(sc->sc_dev, + "failed to establish soft interrupt\n"); + goto fail; + } + } + virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK); return 0; + +fail: + virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_FAILED); + return 1; } void @@ -1258,7 +890,12 @@ virtio_child_detach(struct virtio_softc virtio_device_reset(sc); - virtio_free_interrupts(sc); + sc->sc_ops->free_interrupts(sc); + + if (sc->sc_soft_ih) { + softint_disestablish(sc->sc_soft_ih); + sc->sc_soft_ih = NULL; + } } void Index: src/sys/dev/pci/virtio_pci.c diff -u src/sys/dev/pci/virtio_pci.c:1.3 src/sys/dev/pci/virtio_pci.c:1.4 --- src/sys/dev/pci/virtio_pci.c:1.3 Sat Jun 2 13:30:32 2018 +++ src/sys/dev/pci/virtio_pci.c Sat Jun 2 22:43:15 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: virtio_pci.c,v 1.3 2018/06/02 13:30:32 jakllsch Exp $ */ +/* $NetBSD: virtio_pci.c,v 1.4 2018/06/02 22:43:15 jakllsch Exp $ */ /* * Copyright (c) 2010 Minoura Makoto. @@ -26,10 +26,11 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: virtio_pci.c,v 1.3 2018/06/02 13:30:32 jakllsch Exp $"); +__KERNEL_RCSID(0, "$NetBSD: virtio_pci.c,v 1.4 2018/06/02 22:43:15 jakllsch Exp $"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/kmem.h> #include <sys/device.h> @@ -42,10 +43,97 @@ __KERNEL_RCSID(0, "$NetBSD: virtio_pci.c #include <dev/pci/virtioreg.h> /* XXX: move to non-pci */ #include <dev/pci/virtiovar.h> /* XXX: move to non-pci */ -static int virtio_match(device_t, cfdata_t, void *); -static void virtio_attach(device_t, device_t, void *); -static int virtio_rescan(device_t, const char *, const int *); -static int virtio_detach(device_t, int); +static int virtio_pci_match(device_t, cfdata_t, void *); +static void virtio_pci_attach(device_t, device_t, void *); +static int virtio_pci_rescan(device_t, const char *, const int *); +static int virtio_pci_detach(device_t, int); + +struct virtio_pci_softc { + struct virtio_softc sc_sc; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_size_t sc_iosize; + struct pci_attach_args sc_pa; + pci_intr_handle_t *sc_ihp; + void **sc_ihs; + int sc_ihs_num; + int sc_config_offset; +}; + +static void virtio_pci_kick(struct virtio_softc *, uint16_t); +static uint8_t virtio_pci_read_device_config_1(struct virtio_softc *, int); +static uint16_t virtio_pci_read_device_config_2(struct virtio_softc *, int); +static uint32_t virtio_pci_read_device_config_4(struct virtio_softc *, int); +static uint64_t virtio_pci_read_device_config_8(struct virtio_softc *, int); +static void virtio_pci_write_device_config_1(struct virtio_softc *, int, uint8_t); +static void virtio_pci_write_device_config_2(struct virtio_softc *, int, uint16_t); +static void virtio_pci_write_device_config_4(struct virtio_softc *, int, uint32_t); +static void virtio_pci_write_device_config_8(struct virtio_softc *, int, uint64_t); +static uint16_t virtio_pci_read_queue_size(struct virtio_softc *, uint16_t); +static void virtio_pci_setup_queue(struct virtio_softc *, uint16_t, uint32_t); +static void virtio_pci_set_status(struct virtio_softc *, int); +static uint32_t virtio_pci_negotiate_features(struct virtio_softc *, uint32_t); +static int virtio_pci_setup_interrupts(struct virtio_softc *); +static void virtio_pci_free_interrupts(struct virtio_softc *); + +static int virtio_pci_intr(void *arg); +static int virtio_pci_msix_queue_intr(void *); +static int virtio_pci_msix_config_intr(void *); +static int virtio_pci_setup_msix_vectors(struct virtio_softc *); +static int virtio_pci_setup_msix_interrupts(struct virtio_softc *, + struct pci_attach_args *); +static int virtio_pci_setup_intx_interrupt(struct virtio_softc *, + struct pci_attach_args *); + +#define VIRTIO_MSIX_CONFIG_VECTOR_INDEX 0 +#define VIRTIO_MSIX_QUEUE_VECTOR_INDEX 1 + +/* we use the legacy virtio spec, so the PCI registers are host native + * byte order, not PCI (i.e. LE) byte order */ +#if BYTE_ORDER == BIG_ENDIAN +#define REG_HI_OFF 0 +#define REG_LO_OFF 4 +#ifndef __BUS_SPACE_HAS_STREAM_METHODS +#define bus_space_read_stream_1 bus_space_read_1 +#define bus_space_write_stream_1 bus_space_write_1 +static inline uint16_t +bus_space_read_stream_2(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t o) +{ + return le16toh(bus_space_read_2(t, h, o)); +} +static inline void +bus_space_write_stream_2(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t o, uint16_t v) +{ + bus_space_write_2(t, h, o, htole16(v)); +} +static inline uint32_t +bus_space_read_stream_4(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t o) +{ + return le32toh(bus_space_read_4(t, h, o)); +} +static inline void +bus_space_write_stream_4(bus_space_tag_t t, bus_space_handle_t h, + bus_size_t o, uint32_t v) +{ + bus_space_write_4(t, h, o, htole32(v)); +} +#endif +#else +#define REG_HI_OFF 4 +#define REG_LO_OFF 0 +#ifndef __BUS_SPACE_HAS_STREAM_METHODS +#define bus_space_read_stream_1 bus_space_read_1 +#define bus_space_read_stream_2 bus_space_read_2 +#define bus_space_read_stream_4 bus_space_read_4 +#define bus_space_write_stream_1 bus_space_write_1 +#define bus_space_write_stream_2 bus_space_write_2 +#define bus_space_write_stream_4 bus_space_write_4 +#endif +#endif + static const char *virtio_device_name[] = { "Unknown (0)", /* 0 */ @@ -62,12 +150,30 @@ static const char *virtio_device_name[] }; #define NDEVNAMES __arraycount(virtio_device_name) -CFATTACH_DECL3_NEW(virtio_pci, sizeof(struct virtio_softc), - virtio_match, virtio_attach, virtio_detach, NULL, virtio_rescan, NULL, - DVF_DETACH_SHUTDOWN); +CFATTACH_DECL3_NEW(virtio_pci, sizeof(struct virtio_pci_softc), + virtio_pci_match, virtio_pci_attach, virtio_pci_detach, NULL, + virtio_pci_rescan, NULL, DVF_DETACH_SHUTDOWN); + +static const struct virtio_ops virtio_pci_ops = { + .kick = virtio_pci_kick, + .read_dev_cfg_1 = virtio_pci_read_device_config_1, + .read_dev_cfg_2 = virtio_pci_read_device_config_2, + .read_dev_cfg_4 = virtio_pci_read_device_config_4, + .read_dev_cfg_8 = virtio_pci_read_device_config_8, + .write_dev_cfg_1 = virtio_pci_write_device_config_1, + .write_dev_cfg_2 = virtio_pci_write_device_config_2, + .write_dev_cfg_4 = virtio_pci_write_device_config_4, + .write_dev_cfg_8 = virtio_pci_write_device_config_8, + .read_queue_size = virtio_pci_read_queue_size, + .setup_queue = virtio_pci_setup_queue, + .set_status = virtio_pci_set_status, + .neg_features = virtio_pci_negotiate_features, + .setup_interrupts = virtio_pci_setup_interrupts, + .free_interrupts = virtio_pci_free_interrupts, +}; static int -virtio_match(device_t parent, cfdata_t match, void *aux) +virtio_pci_match(device_t parent, cfdata_t match, void *aux) { struct pci_attach_args *pa; @@ -86,9 +192,10 @@ virtio_match(device_t parent, cfdata_t m } static void -virtio_attach(device_t parent, device_t self, void *aux) +virtio_pci_attach(device_t parent, device_t self, void *aux) { - struct virtio_softc *sc = device_private(self); + struct virtio_pci_softc * const psc = device_private(self); + struct virtio_softc * const sc = &psc->sc_sc; struct pci_attach_args *pa = (struct pci_attach_args *)aux; pci_chipset_tag_t pc = pa->pa_pc; pcitag_t tag = pa->pa_tag; @@ -117,17 +224,17 @@ virtio_attach(device_t parent, device_t pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr); sc->sc_dev = self; - sc->sc_pc = pc; - sc->sc_tag = tag; - sc->sc_iot = pa->pa_iot; + sc->sc_ops = &virtio_pci_ops; + psc->sc_pa = *pa; + psc->sc_iot = pa->pa_iot; if (pci_dma64_available(pa)) sc->sc_dmat = pa->pa_dmat64; else sc->sc_dmat = pa->pa_dmat; - sc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI; + psc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI; if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_IO, 0, - &sc->sc_iot, &sc->sc_ioh, NULL, &sc->sc_iosize)) { + &psc->sc_iot, &psc->sc_ioh, NULL, &psc->sc_iosize)) { aprint_error_dev(self, "can't map i/o space\n"); return; } @@ -138,19 +245,18 @@ virtio_attach(device_t parent, device_t sc->sc_childdevid = PCI_SUBSYS_ID(id); sc->sc_child = NULL; - sc->sc_pa = *pa; - virtio_rescan(self, "virtio", 0); + virtio_pci_rescan(self, "virtio", 0); return; } /* ARGSUSED */ static int -virtio_rescan(device_t self, const char *attr, const int *scan_flags) +virtio_pci_rescan(device_t self, const char *attr, const int *scan_flags) { - struct virtio_softc *sc; + struct virtio_pci_softc * const psc = device_private(self); + struct virtio_softc * const sc = &psc->sc_sc; struct virtio_attach_args va; - sc = device_private(self); if (sc->sc_child) /* Child already attached? */ return 0; @@ -164,7 +270,7 @@ virtio_rescan(device_t self, const char "no matching child driver; not configured\n"); return 0; } - + if (sc->sc_child == VIRTIO_CHILD_FAILED) { aprint_error_dev(self, "virtio configuration failed\n"); @@ -175,16 +281,17 @@ virtio_rescan(device_t self, const char * Make sure child drivers initialize interrupts via call * to virtio_child_attach_finish(). */ - KASSERT(sc->sc_ihs_num != 0); + KASSERT(psc->sc_ihs_num != 0); return 0; } static int -virtio_detach(device_t self, int flags) +virtio_pci_detach(device_t self, int flags) { - struct virtio_softc *sc = device_private(self); + struct virtio_pci_softc * const psc = device_private(self); + struct virtio_softc * const sc = &psc->sc_sc; int r; if (sc->sc_child != NULL) { @@ -196,11 +303,427 @@ virtio_detach(device_t self, int flags) /* Check that child detached properly */ KASSERT(sc->sc_child == NULL); KASSERT(sc->sc_vqs == NULL); - KASSERT(sc->sc_ihs_num == 0); + KASSERT(psc->sc_ihs_num == 0); - if (sc->sc_iosize) - bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize); - sc->sc_iosize = 0; + if (psc->sc_iosize) + bus_space_unmap(psc->sc_iot, psc->sc_ioh, psc->sc_iosize); + psc->sc_iosize = 0; return 0; } + +static void +virtio_pci_kick(struct virtio_softc *sc, uint16_t idx) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + + bus_space_write_stream_2(psc->sc_iot, psc->sc_ioh, + VIRTIO_CONFIG_QUEUE_NOTIFY, idx); +} + +static uint8_t +virtio_pci_read_device_config_1(struct virtio_softc *sc, int index) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + return bus_space_read_stream_1(psc->sc_iot, psc->sc_ioh, + psc->sc_config_offset + index); +} + +static uint16_t +virtio_pci_read_device_config_2(struct virtio_softc *sc, int index) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + return bus_space_read_stream_2(psc->sc_iot, psc->sc_ioh, + psc->sc_config_offset + index); +} + +static uint32_t +virtio_pci_read_device_config_4(struct virtio_softc *sc, int index) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + return bus_space_read_stream_4(psc->sc_iot, psc->sc_ioh, + psc->sc_config_offset + index); +} + +static uint64_t +virtio_pci_read_device_config_8(struct virtio_softc *sc, int index) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + uint64_t r; + + r = bus_space_read_stream_4(psc->sc_iot, psc->sc_ioh, + psc->sc_config_offset + index + REG_HI_OFF); + r <<= 32; + r |= bus_space_read_stream_4(psc->sc_iot, psc->sc_ioh, + psc->sc_config_offset + index + REG_LO_OFF); + + return r; +} + +static void +virtio_pci_write_device_config_1(struct virtio_softc *sc, int index, + uint8_t value) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + + bus_space_write_stream_1(psc->sc_iot, psc->sc_ioh, + psc->sc_config_offset + index, value); +} + +static void +virtio_pci_write_device_config_2(struct virtio_softc *sc, int index, + uint16_t value) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + + bus_space_write_stream_2(psc->sc_iot, psc->sc_ioh, + psc->sc_config_offset + index, value); +} + +static void +virtio_pci_write_device_config_4(struct virtio_softc *sc, int index, + uint32_t value) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + + bus_space_write_stream_4(psc->sc_iot, psc->sc_ioh, + psc->sc_config_offset + index, value); +} + +static void +virtio_pci_write_device_config_8(struct virtio_softc *sc, int index, + uint64_t value) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + + bus_space_write_stream_4(psc->sc_iot, psc->sc_ioh, + psc->sc_config_offset + index + REG_LO_OFF, + value & 0xffffffff); + bus_space_write_stream_4(psc->sc_iot, psc->sc_ioh, + psc->sc_config_offset + index + REG_HI_OFF, + value >> 32); +} + +static uint16_t +virtio_pci_read_queue_size(struct virtio_softc *sc, uint16_t idx) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + + bus_space_write_stream_2(psc->sc_iot, psc->sc_ioh, + VIRTIO_CONFIG_QUEUE_SELECT, idx); + return bus_space_read_stream_2(psc->sc_iot, psc->sc_ioh, + VIRTIO_CONFIG_QUEUE_SIZE); +} + +static void +virtio_pci_setup_queue(struct virtio_softc *sc, uint16_t idx, uint32_t addr) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + + bus_space_write_stream_2(psc->sc_iot, psc->sc_ioh, + VIRTIO_CONFIG_QUEUE_SELECT, idx); + bus_space_write_stream_4(psc->sc_iot, psc->sc_ioh, + VIRTIO_CONFIG_QUEUE_ADDRESS, addr); + + if (psc->sc_ihs_num > 1) { + int vec = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; + if (false) /* (for per-vq vectors) */ + vec += idx; + bus_space_write_stream_2(psc->sc_iot, psc->sc_ioh, + VIRTIO_CONFIG_MSI_QUEUE_VECTOR, vec); + } +} + +static void +virtio_pci_set_status(struct virtio_softc *sc, int status) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + int old = 0; + + if (status != 0) { + old = bus_space_read_stream_1(psc->sc_iot, psc->sc_ioh, + VIRTIO_CONFIG_DEVICE_STATUS); + } + bus_space_write_stream_1(psc->sc_iot, psc->sc_ioh, + VIRTIO_CONFIG_DEVICE_STATUS, status|old); +} + +static uint32_t +virtio_pci_negotiate_features(struct virtio_softc *sc, uint32_t guest_features) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + uint32_t r; + + r = bus_space_read_stream_4(psc->sc_iot, psc->sc_ioh, + VIRTIO_CONFIG_DEVICE_FEATURES); + r &= guest_features; + bus_space_write_stream_4(psc->sc_iot, psc->sc_ioh, + VIRTIO_CONFIG_GUEST_FEATURES, r); + + return r; +} + + +static int +virtio_pci_setup_msix_vectors(struct virtio_softc *sc) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + int offset, vector, ret, qid; + + offset = VIRTIO_CONFIG_MSI_CONFIG_VECTOR; + vector = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; + + bus_space_write_stream_2(psc->sc_iot, psc->sc_ioh, offset, vector); + ret = bus_space_read_stream_2(psc->sc_iot, psc->sc_ioh, offset); + aprint_debug_dev(sc->sc_dev, "expected=%d, actual=%d\n", + vector, ret); + if (ret != vector) + return -1; + + for (qid = 0; qid < sc->sc_nvqs; qid++) { + offset = VIRTIO_CONFIG_QUEUE_SELECT; + bus_space_write_stream_2(psc->sc_iot, psc->sc_ioh, offset, qid); + + offset = VIRTIO_CONFIG_MSI_QUEUE_VECTOR; + vector = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; + + bus_space_write_stream_2(psc->sc_iot, psc->sc_ioh, offset, vector); + ret = bus_space_read_stream_2(psc->sc_iot, psc->sc_ioh, offset); + aprint_debug_dev(sc->sc_dev, "expected=%d, actual=%d\n", + vector, ret); + if (ret != vector) + return -1; + } + + return 0; +} + +static int +virtio_pci_setup_msix_interrupts(struct virtio_softc *sc, + struct pci_attach_args *pa) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + device_t self = sc->sc_dev; + pci_chipset_tag_t pc = pa->pa_pc; + char intrbuf[PCI_INTRSTR_LEN]; + char const *intrstr; + int idx; + + idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; + if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE) + pci_intr_setattr(pc, &psc->sc_ihp[idx], PCI_INTR_MPSAFE, true); + + psc->sc_ihs[idx] = pci_intr_establish_xname(pc, psc->sc_ihp[idx], + sc->sc_ipl, virtio_pci_msix_config_intr, sc, device_xname(sc->sc_dev)); + if (psc->sc_ihs[idx] == NULL) { + aprint_error_dev(self, "couldn't establish MSI-X for config\n"); + goto error; + } + + idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; + if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE) + pci_intr_setattr(pc, &psc->sc_ihp[idx], PCI_INTR_MPSAFE, true); + + psc->sc_ihs[idx] = pci_intr_establish_xname(pc, psc->sc_ihp[idx], + sc->sc_ipl, virtio_pci_msix_queue_intr, sc, device_xname(sc->sc_dev)); + if (psc->sc_ihs[idx] == NULL) { + aprint_error_dev(self, "couldn't establish MSI-X for queues\n"); + goto error; + } + + if (virtio_pci_setup_msix_vectors(sc) != 0) { + aprint_error_dev(self, "couldn't setup MSI-X vectors\n"); + goto error; + } + + idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; + intrstr = pci_intr_string(pc, psc->sc_ihp[idx], intrbuf, sizeof(intrbuf)); + aprint_normal_dev(self, "config interrupting at %s\n", intrstr); + idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; + intrstr = pci_intr_string(pc, psc->sc_ihp[idx], intrbuf, sizeof(intrbuf)); + aprint_normal_dev(self, "queues interrupting at %s\n", intrstr); + + return 0; + +error: + idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; + if (psc->sc_ihs[idx] != NULL) + pci_intr_disestablish(psc->sc_pa.pa_pc, psc->sc_ihs[idx]); + idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; + if (psc->sc_ihs[idx] != NULL) + pci_intr_disestablish(psc->sc_pa.pa_pc, psc->sc_ihs[idx]); + + return -1; +} + +static int +virtio_pci_setup_intx_interrupt(struct virtio_softc *sc, + struct pci_attach_args *pa) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + device_t self = sc->sc_dev; + pci_chipset_tag_t pc = pa->pa_pc; + char intrbuf[PCI_INTRSTR_LEN]; + char const *intrstr; + + if (sc->sc_flags & VIRTIO_F_PCI_INTR_MPSAFE) + pci_intr_setattr(pc, &psc->sc_ihp[0], PCI_INTR_MPSAFE, true); + + psc->sc_ihs[0] = pci_intr_establish_xname(pc, psc->sc_ihp[0], + sc->sc_ipl, virtio_pci_intr, sc, device_xname(sc->sc_dev)); + if (psc->sc_ihs[0] == NULL) { + aprint_error_dev(self, "couldn't establish INTx\n"); + return -1; + } + + intrstr = pci_intr_string(pc, psc->sc_ihp[0], intrbuf, sizeof(intrbuf)); + aprint_normal_dev(self, "interrupting at %s\n", intrstr); + + return 0; +} + +static int +virtio_pci_setup_interrupts(struct virtio_softc *sc) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + device_t self = sc->sc_dev; + pci_chipset_tag_t pc = psc->sc_pa.pa_pc; + int error; + int nmsix; + int counts[PCI_INTR_TYPE_SIZE]; + pci_intr_type_t max_type; + + nmsix = pci_msix_count(psc->sc_pa.pa_pc, psc->sc_pa.pa_tag); + aprint_debug_dev(self, "pci_msix_count=%d\n", nmsix); + + /* We need at least two: one for config and the other for queues */ + if ((sc->sc_flags & VIRTIO_F_PCI_INTR_MSIX) == 0 || nmsix < 2) { + /* Try INTx only */ + max_type = PCI_INTR_TYPE_INTX; + counts[PCI_INTR_TYPE_INTX] = 1; + } else { + /* Try MSI-X first and INTx second */ + max_type = PCI_INTR_TYPE_MSIX; + counts[PCI_INTR_TYPE_MSIX] = 2; + counts[PCI_INTR_TYPE_MSI] = 0; + counts[PCI_INTR_TYPE_INTX] = 1; + } + +retry: + error = pci_intr_alloc(&psc->sc_pa, &psc->sc_ihp, counts, max_type); + if (error != 0) { + aprint_error_dev(self, "couldn't map interrupt\n"); + return -1; + } + + if (pci_intr_type(pc, psc->sc_ihp[0]) == PCI_INTR_TYPE_MSIX) { + psc->sc_ihs = kmem_alloc(sizeof(*psc->sc_ihs) * 2, + KM_SLEEP); + + error = virtio_pci_setup_msix_interrupts(sc, &psc->sc_pa); + if (error != 0) { + kmem_free(psc->sc_ihs, sizeof(*psc->sc_ihs) * 2); + pci_intr_release(pc, psc->sc_ihp, 2); + + /* Retry INTx */ + max_type = PCI_INTR_TYPE_INTX; + counts[PCI_INTR_TYPE_INTX] = 1; + goto retry; + } + + psc->sc_ihs_num = 2; + psc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_MSI; + } else if (pci_intr_type(pc, psc->sc_ihp[0]) == PCI_INTR_TYPE_INTX) { + psc->sc_ihs = kmem_alloc(sizeof(*psc->sc_ihs) * 1, + KM_SLEEP); + + error = virtio_pci_setup_intx_interrupt(sc, &psc->sc_pa); + if (error != 0) { + kmem_free(psc->sc_ihs, sizeof(*psc->sc_ihs) * 1); + pci_intr_release(pc, psc->sc_ihp, 1); + return -1; + } + + psc->sc_ihs_num = 1; + psc->sc_config_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI; + } + + return 0; +} + +static void +virtio_pci_free_interrupts(struct virtio_softc *sc) +{ + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + + for (int i = 0; i < psc->sc_ihs_num; i++) { + if (psc->sc_ihs[i] == NULL) + continue; + pci_intr_disestablish(psc->sc_pa.pa_pc, psc->sc_ihs[i]); + psc->sc_ihs[i] = NULL; + } + + if (psc->sc_ihs_num > 0) + pci_intr_release(psc->sc_pa.pa_pc, psc->sc_ihp, psc->sc_ihs_num); + + if (psc->sc_ihs != NULL) { + kmem_free(psc->sc_ihs, sizeof(*psc->sc_ihs) * psc->sc_ihs_num); + psc->sc_ihs = NULL; + } + psc->sc_ihs_num = 0; +} + +/* + * Interrupt handler. + */ +static int +virtio_pci_intr(void *arg) +{ + struct virtio_softc *sc = arg; + struct virtio_pci_softc * const psc = (struct virtio_pci_softc *)sc; + int isr, r = 0; + + /* check and ack the interrupt */ + isr = bus_space_read_stream_1(psc->sc_iot, psc->sc_ioh, + VIRTIO_CONFIG_ISR_STATUS); + if (isr == 0) + return 0; + if ((isr & VIRTIO_CONFIG_ISR_CONFIG_CHANGE) && + (sc->sc_config_change != NULL)) + r = (sc->sc_config_change)(sc); + if (sc->sc_intrhand != NULL) { + if (sc->sc_soft_ih != NULL) + softint_schedule(sc->sc_soft_ih); + else + r |= (sc->sc_intrhand)(sc); + } + + return r; +} + +static int +virtio_pci_msix_queue_intr(void *arg) +{ + struct virtio_softc *sc = arg; + int r = 0; + + if (sc->sc_intrhand != NULL) { + if (sc->sc_soft_ih != NULL) + softint_schedule(sc->sc_soft_ih); + else + r |= (sc->sc_intrhand)(sc); + } + + return r; +} + +static int +virtio_pci_msix_config_intr(void *arg) +{ + struct virtio_softc *sc = arg; + int r = 0; + + if (sc->sc_config_change != NULL) + r = (sc->sc_config_change)(sc); + return r; +} Index: src/sys/dev/pci/virtiovar.h diff -u src/sys/dev/pci/virtiovar.h:1.10 src/sys/dev/pci/virtiovar.h:1.11 --- src/sys/dev/pci/virtiovar.h:1.10 Wed Aug 2 08:39:14 2017 +++ src/sys/dev/pci/virtiovar.h Sat Jun 2 22:43:15 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: virtiovar.h,v 1.10 2017/08/02 08:39:14 cherry Exp $ */ +/* $NetBSD: virtiovar.h,v 1.11 2018/06/02 22:43:15 jakllsch Exp $ */ /* * Copyright (c) 2010 Minoura Makoto. @@ -125,25 +125,34 @@ struct virtio_attach_args { typedef int (*virtio_callback)(struct virtio_softc*); #ifdef VIRTIO_PRIVATE +struct virtio_ops { + void (*kick)(struct virtio_softc *, uint16_t); + uint8_t (*read_dev_cfg_1)(struct virtio_softc *, int); + uint16_t (*read_dev_cfg_2)(struct virtio_softc *, int); + uint32_t (*read_dev_cfg_4)(struct virtio_softc *, int); + uint64_t (*read_dev_cfg_8)(struct virtio_softc *, int); + void (*write_dev_cfg_1)(struct virtio_softc *, int, uint8_t); + void (*write_dev_cfg_2)(struct virtio_softc *, int, uint16_t); + void (*write_dev_cfg_4)(struct virtio_softc *, int, uint32_t); + void (*write_dev_cfg_8)(struct virtio_softc *, int, uint64_t); + uint16_t (*read_queue_size)(struct virtio_softc *, uint16_t); + void (*setup_queue)(struct virtio_softc *, uint16_t, uint32_t); + void (*set_status)(struct virtio_softc *, int); + uint32_t (*neg_features)(struct virtio_softc *, uint32_t); + int (*setup_interrupts)(struct virtio_softc *); + void (*free_interrupts)(struct virtio_softc *); +}; + struct virtio_softc { device_t sc_dev; - pci_chipset_tag_t sc_pc; - pcitag_t sc_tag; + const struct virtio_ops *sc_ops; bus_dma_tag_t sc_dmat; int sc_ipl; /* set by child */ - pci_intr_handle_t *sc_ihp; - void **sc_ihs; - int sc_ihs_num; void *sc_soft_ih; int sc_flags; /* set by child */ - bus_space_tag_t sc_iot; - bus_space_handle_t sc_ioh; - bus_size_t sc_iosize; - int sc_config_offset; - uint32_t sc_features; bool sc_indirect; @@ -154,7 +163,6 @@ struct virtio_softc { device_t sc_child; /* set by child */ virtio_callback sc_config_change; /* set by child */ virtio_callback sc_intrhand; /* set by child */ - struct pci_attach_args sc_pa; /* need for rescan to set interrupts */ }; #else struct virtio_softc;