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;

Reply via email to