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;