111447b59SVenkatesh Srinivas /*-
211447b59SVenkatesh Srinivas * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
311447b59SVenkatesh Srinivas * All rights reserved.
411447b59SVenkatesh Srinivas *
511447b59SVenkatesh Srinivas * Redistribution and use in source and binary forms, with or without
611447b59SVenkatesh Srinivas * modification, are permitted provided that the following conditions
711447b59SVenkatesh Srinivas * are met:
811447b59SVenkatesh Srinivas * 1. Redistributions of source code must retain the above copyright
911447b59SVenkatesh Srinivas * notice unmodified, this list of conditions, and the following
1011447b59SVenkatesh Srinivas * disclaimer.
1111447b59SVenkatesh Srinivas * 2. Redistributions in binary form must reproduce the above copyright
1211447b59SVenkatesh Srinivas * notice, this list of conditions and the following disclaimer in the
1311447b59SVenkatesh Srinivas * documentation and/or other materials provided with the distribution.
1411447b59SVenkatesh Srinivas *
1511447b59SVenkatesh Srinivas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1611447b59SVenkatesh Srinivas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1711447b59SVenkatesh Srinivas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1811447b59SVenkatesh Srinivas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1911447b59SVenkatesh Srinivas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2011447b59SVenkatesh Srinivas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2111447b59SVenkatesh Srinivas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2211447b59SVenkatesh Srinivas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2311447b59SVenkatesh Srinivas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2411447b59SVenkatesh Srinivas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2511447b59SVenkatesh Srinivas *
2611447b59SVenkatesh Srinivas * $FreeBSD: src/sys/dev/virtio/pci/virtio_pci.c,v 1.3 2012/04/14 05:48:04 grehan Exp $
2711447b59SVenkatesh Srinivas */
2811447b59SVenkatesh Srinivas
2911447b59SVenkatesh Srinivas /* Driver for the VirtIO PCI interface. */
3011447b59SVenkatesh Srinivas
3111447b59SVenkatesh Srinivas #include <sys/param.h>
3211447b59SVenkatesh Srinivas #include <sys/systm.h>
3311447b59SVenkatesh Srinivas #include <sys/bus.h>
3411447b59SVenkatesh Srinivas #include <sys/kernel.h>
3511447b59SVenkatesh Srinivas #include <sys/module.h>
3611447b59SVenkatesh Srinivas #include <sys/malloc.h>
372f1382caSVenkatesh Srinivas #include <sys/serialize.h>
3811447b59SVenkatesh Srinivas
3911447b59SVenkatesh Srinivas #include <bus/pci/pcivar.h>
4011447b59SVenkatesh Srinivas #include <bus/pci/pcireg.h>
4111447b59SVenkatesh Srinivas
4211447b59SVenkatesh Srinivas #include <sys/rman.h>
4311447b59SVenkatesh Srinivas
44dcbcbc86SVenkatesh Srinivas #include <dev/virtual/virtio/virtio/virtio.h>
45dcbcbc86SVenkatesh Srinivas #include <dev/virtual/virtio/virtio/virtqueue.h>
4611447b59SVenkatesh Srinivas #include "virtio_pci.h"
47dcbcbc86SVenkatesh Srinivas #include "virtio_bus_if.h"
4811447b59SVenkatesh Srinivas
492f2405bbSImre Vadász struct vqentry {
502f2405bbSImre Vadász int what;
519d96478cSImre Vadász struct virtqueue *vq;
529d96478cSImre Vadász driver_intr_t *handler;
539d96478cSImre Vadász void *arg;
542f2405bbSImre Vadász TAILQ_ENTRY(vqentry) entries;
552f2405bbSImre Vadász };
562f2405bbSImre Vadász
572f2405bbSImre Vadász TAILQ_HEAD(vqirq_list, vqentry);
582f2405bbSImre Vadász
5911447b59SVenkatesh Srinivas struct vtpci_softc {
6011447b59SVenkatesh Srinivas device_t vtpci_dev;
6111447b59SVenkatesh Srinivas struct resource *vtpci_res;
6211447b59SVenkatesh Srinivas struct resource *vtpci_msix_res;
6311447b59SVenkatesh Srinivas uint64_t vtpci_features;
6411447b59SVenkatesh Srinivas uint32_t vtpci_flags;
65c8247d06SImre Vadász #define VIRTIO_PCI_FLAG_MSI 0x0001
66c8247d06SImre Vadász #define VIRTIO_PCI_FLAG_MSIX 0x0010
6711447b59SVenkatesh Srinivas
6811447b59SVenkatesh Srinivas device_t vtpci_child_dev;
6911447b59SVenkatesh Srinivas struct virtio_feature_desc *vtpci_child_feat_desc;
7011447b59SVenkatesh Srinivas
7111447b59SVenkatesh Srinivas /*
7211447b59SVenkatesh Srinivas * Ideally, each virtqueue that the driver provides a callback for
7311447b59SVenkatesh Srinivas * will receive its own MSIX vector. If there are not sufficient
7411447b59SVenkatesh Srinivas * vectors available, we will then attempt to have all the VQs
7511447b59SVenkatesh Srinivas * share one vector. Note that when using MSIX, the configuration
7611447b59SVenkatesh Srinivas * changed notifications must be on their own vector.
7711447b59SVenkatesh Srinivas *
7811447b59SVenkatesh Srinivas * If MSIX is not available, we will attempt to have the whole
7911447b59SVenkatesh Srinivas * device share one MSI vector, and then, finally, one legacy
8011447b59SVenkatesh Srinivas * interrupt.
8111447b59SVenkatesh Srinivas */
8211447b59SVenkatesh Srinivas int vtpci_nvqs;
8311447b59SVenkatesh Srinivas struct vtpci_virtqueue {
8411447b59SVenkatesh Srinivas struct virtqueue *vq;
8511447b59SVenkatesh Srinivas
862f2405bbSImre Vadász /* Index into vtpci_intr_res[] below. -1 if no IRQ assigned. */
8711447b59SVenkatesh Srinivas int ires_idx;
8811447b59SVenkatesh Srinivas } vtpci_vqx[VIRTIO_MAX_VIRTQUEUES];
8911447b59SVenkatesh Srinivas
9011447b59SVenkatesh Srinivas /*
9111447b59SVenkatesh Srinivas * When using MSIX interrupts, the first element of vtpci_intr_res[]
9211447b59SVenkatesh Srinivas * is always the configuration changed notifications. The remaining
9311447b59SVenkatesh Srinivas * element(s) are used for the virtqueues.
9411447b59SVenkatesh Srinivas *
9511447b59SVenkatesh Srinivas * With MSI and legacy interrupts, only the first element of
9611447b59SVenkatesh Srinivas * vtpci_intr_res[] is used.
9711447b59SVenkatesh Srinivas */
9811447b59SVenkatesh Srinivas int vtpci_nintr_res;
99c8247d06SImre Vadász int vtpci_irq_flags;
10011447b59SVenkatesh Srinivas struct vtpci_intr_resource {
1012f2405bbSImre Vadász struct vtpci_softc *ires_sc;
10211447b59SVenkatesh Srinivas struct resource *irq;
10311447b59SVenkatesh Srinivas int rid;
10411447b59SVenkatesh Srinivas void *intrhand;
1052f2405bbSImre Vadász struct vqirq_list ls;
10611447b59SVenkatesh Srinivas } vtpci_intr_res[1 + VIRTIO_MAX_VIRTQUEUES];
1072f2405bbSImre Vadász
1082f2405bbSImre Vadász int vtpci_config_irq;
10911447b59SVenkatesh Srinivas };
11011447b59SVenkatesh Srinivas
11111447b59SVenkatesh Srinivas static int vtpci_probe(device_t);
11211447b59SVenkatesh Srinivas static int vtpci_attach(device_t);
11311447b59SVenkatesh Srinivas static int vtpci_detach(device_t);
11411447b59SVenkatesh Srinivas static int vtpci_suspend(device_t);
11511447b59SVenkatesh Srinivas static int vtpci_resume(device_t);
11611447b59SVenkatesh Srinivas static int vtpci_shutdown(device_t);
11711447b59SVenkatesh Srinivas static void vtpci_driver_added(device_t, driver_t *);
11811447b59SVenkatesh Srinivas static void vtpci_child_detached(device_t, device_t);
11911447b59SVenkatesh Srinivas static int vtpci_read_ivar(device_t, device_t, int, uintptr_t *);
12011447b59SVenkatesh Srinivas static int vtpci_write_ivar(device_t, device_t, int, uintptr_t);
12111447b59SVenkatesh Srinivas
12211447b59SVenkatesh Srinivas static uint64_t vtpci_negotiate_features(device_t, uint64_t);
12311447b59SVenkatesh Srinivas static int vtpci_with_feature(device_t, uint64_t);
1242f2405bbSImre Vadász static int vtpci_intr_count(device_t dev);
1252f2405bbSImre Vadász static int vtpci_intr_alloc(device_t dev, int *cnt, int use_config,
1262f2405bbSImre Vadász int *cpus);
1272f2405bbSImre Vadász static int vtpci_intr_release(device_t dev);
128099c4d8eSImre Vadász static int vtpci_alloc_virtqueues(device_t, int, struct vq_alloc_info *);
1292f2405bbSImre Vadász static int vtpci_setup_intr(device_t, uint irq, lwkt_serialize_t);
1302f2405bbSImre Vadász static int vtpci_teardown_intr(device_t, uint irq);
1319d96478cSImre Vadász static int vtpci_bind_intr(device_t, uint, int, driver_intr_t, void *);
1322f2405bbSImre Vadász static int vtpci_unbind_intr(device_t, int);
13311447b59SVenkatesh Srinivas static void vtpci_stop(device_t);
13411447b59SVenkatesh Srinivas static int vtpci_reinit(device_t, uint64_t);
13511447b59SVenkatesh Srinivas static void vtpci_reinit_complete(device_t);
13611447b59SVenkatesh Srinivas static void vtpci_notify_virtqueue(device_t, uint16_t);
13711447b59SVenkatesh Srinivas static uint8_t vtpci_get_status(device_t);
13811447b59SVenkatesh Srinivas static void vtpci_set_status(device_t, uint8_t);
13911447b59SVenkatesh Srinivas static void vtpci_read_dev_config(device_t, bus_size_t, void *, int);
14011447b59SVenkatesh Srinivas static void vtpci_write_dev_config(device_t, bus_size_t, void *, int);
14111447b59SVenkatesh Srinivas
14211447b59SVenkatesh Srinivas static void vtpci_describe_features(struct vtpci_softc *, const char *,
14311447b59SVenkatesh Srinivas uint64_t);
14411447b59SVenkatesh Srinivas static void vtpci_probe_and_attach_child(struct vtpci_softc *);
14511447b59SVenkatesh Srinivas
14611447b59SVenkatesh Srinivas static int vtpci_register_msix_vector(struct vtpci_softc *, int, int);
14711447b59SVenkatesh Srinivas
14811447b59SVenkatesh Srinivas static void vtpci_free_interrupts(struct vtpci_softc *);
14911447b59SVenkatesh Srinivas static void vtpci_free_virtqueues(struct vtpci_softc *);
15011447b59SVenkatesh Srinivas static void vtpci_release_child_resources(struct vtpci_softc *);
15111447b59SVenkatesh Srinivas static void vtpci_reset(struct vtpci_softc *);
15211447b59SVenkatesh Srinivas
153df2df2fcSImre Vadász static void vtpci_legacy_intr(void *);
1542f2405bbSImre Vadász static void vtpci_msix_intr(void *);
15511447b59SVenkatesh Srinivas
15611447b59SVenkatesh Srinivas /*
15711447b59SVenkatesh Srinivas * I/O port read/write wrappers.
15811447b59SVenkatesh Srinivas */
15911447b59SVenkatesh Srinivas #define vtpci_read_config_1(sc, o) bus_read_1((sc)->vtpci_res, (o))
16011447b59SVenkatesh Srinivas #define vtpci_read_config_2(sc, o) bus_read_2((sc)->vtpci_res, (o))
16111447b59SVenkatesh Srinivas #define vtpci_read_config_4(sc, o) bus_read_4((sc)->vtpci_res, (o))
16211447b59SVenkatesh Srinivas #define vtpci_write_config_1(sc, o, v) bus_write_1((sc)->vtpci_res, (o), (v))
16311447b59SVenkatesh Srinivas #define vtpci_write_config_2(sc, o, v) bus_write_2((sc)->vtpci_res, (o), (v))
16411447b59SVenkatesh Srinivas #define vtpci_write_config_4(sc, o, v) bus_write_4((sc)->vtpci_res, (o), (v))
16511447b59SVenkatesh Srinivas
16611447b59SVenkatesh Srinivas /* Tunables. */
16711447b59SVenkatesh Srinivas static int vtpci_disable_msix = 0;
16811447b59SVenkatesh Srinivas TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix);
16911447b59SVenkatesh Srinivas
17011447b59SVenkatesh Srinivas static device_method_t vtpci_methods[] = {
17111447b59SVenkatesh Srinivas /* Device interface. */
17211447b59SVenkatesh Srinivas DEVMETHOD(device_probe, vtpci_probe),
17311447b59SVenkatesh Srinivas DEVMETHOD(device_attach, vtpci_attach),
17411447b59SVenkatesh Srinivas DEVMETHOD(device_detach, vtpci_detach),
17511447b59SVenkatesh Srinivas DEVMETHOD(device_suspend, vtpci_suspend),
17611447b59SVenkatesh Srinivas DEVMETHOD(device_resume, vtpci_resume),
17711447b59SVenkatesh Srinivas DEVMETHOD(device_shutdown, vtpci_shutdown),
17811447b59SVenkatesh Srinivas
17911447b59SVenkatesh Srinivas /* Bus interface. */
18011447b59SVenkatesh Srinivas DEVMETHOD(bus_driver_added, vtpci_driver_added),
18111447b59SVenkatesh Srinivas DEVMETHOD(bus_child_detached, vtpci_child_detached),
18211447b59SVenkatesh Srinivas DEVMETHOD(bus_read_ivar, vtpci_read_ivar),
18311447b59SVenkatesh Srinivas DEVMETHOD(bus_write_ivar, vtpci_write_ivar),
18411447b59SVenkatesh Srinivas
18511447b59SVenkatesh Srinivas /* VirtIO bus interface. */
18611447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_negotiate_features, vtpci_negotiate_features),
18711447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_with_feature, vtpci_with_feature),
1882f2405bbSImre Vadász DEVMETHOD(virtio_bus_intr_count, vtpci_intr_count),
1892f2405bbSImre Vadász DEVMETHOD(virtio_bus_intr_alloc, vtpci_intr_alloc),
1902f2405bbSImre Vadász DEVMETHOD(virtio_bus_intr_release, vtpci_intr_release),
19111447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_alloc_virtqueues, vtpci_alloc_virtqueues),
19211447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_setup_intr, vtpci_setup_intr),
1932f2405bbSImre Vadász DEVMETHOD(virtio_bus_teardown_intr, vtpci_teardown_intr),
1942f2405bbSImre Vadász DEVMETHOD(virtio_bus_bind_intr, vtpci_bind_intr),
1952f2405bbSImre Vadász DEVMETHOD(virtio_bus_unbind_intr, vtpci_unbind_intr),
19611447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_stop, vtpci_stop),
19711447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_reinit, vtpci_reinit),
19811447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_reinit_complete, vtpci_reinit_complete),
19911447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_notify_vq, vtpci_notify_virtqueue),
20011447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_read_device_config, vtpci_read_dev_config),
20111447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config),
20211447b59SVenkatesh Srinivas
203d3c9c58eSSascha Wildner DEVMETHOD_END
20411447b59SVenkatesh Srinivas };
20511447b59SVenkatesh Srinivas
20611447b59SVenkatesh Srinivas static driver_t vtpci_driver = {
20711447b59SVenkatesh Srinivas "virtio_pci",
20811447b59SVenkatesh Srinivas vtpci_methods,
20911447b59SVenkatesh Srinivas sizeof(struct vtpci_softc)
21011447b59SVenkatesh Srinivas };
21111447b59SVenkatesh Srinivas
21211447b59SVenkatesh Srinivas devclass_t vtpci_devclass;
21311447b59SVenkatesh Srinivas
214dfc199f7SSascha Wildner DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, NULL, NULL);
21511447b59SVenkatesh Srinivas MODULE_VERSION(virtio_pci, 1);
21611447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, pci, 1, 1, 1);
21711447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1);
21811447b59SVenkatesh Srinivas
21911447b59SVenkatesh Srinivas static int
vtpci_probe(device_t dev)22011447b59SVenkatesh Srinivas vtpci_probe(device_t dev)
22111447b59SVenkatesh Srinivas {
22211447b59SVenkatesh Srinivas char desc[36];
22311447b59SVenkatesh Srinivas const char *name;
22411447b59SVenkatesh Srinivas
22511447b59SVenkatesh Srinivas if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID)
22611447b59SVenkatesh Srinivas return (ENXIO);
22711447b59SVenkatesh Srinivas
22811447b59SVenkatesh Srinivas if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN ||
22911447b59SVenkatesh Srinivas pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX)
23011447b59SVenkatesh Srinivas return (ENXIO);
23111447b59SVenkatesh Srinivas
23211447b59SVenkatesh Srinivas if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION)
23311447b59SVenkatesh Srinivas return (ENXIO);
23411447b59SVenkatesh Srinivas
23511447b59SVenkatesh Srinivas name = virtio_device_name(pci_get_subdevice(dev));
23611447b59SVenkatesh Srinivas if (name == NULL)
23711447b59SVenkatesh Srinivas name = "Unknown";
23811447b59SVenkatesh Srinivas
23911447b59SVenkatesh Srinivas ksnprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name);
24011447b59SVenkatesh Srinivas device_set_desc_copy(dev, desc);
24111447b59SVenkatesh Srinivas
24211447b59SVenkatesh Srinivas return (BUS_PROBE_DEFAULT);
24311447b59SVenkatesh Srinivas }
24411447b59SVenkatesh Srinivas
24511447b59SVenkatesh Srinivas static int
vtpci_attach(device_t dev)24611447b59SVenkatesh Srinivas vtpci_attach(device_t dev)
24711447b59SVenkatesh Srinivas {
24811447b59SVenkatesh Srinivas struct vtpci_softc *sc;
24911447b59SVenkatesh Srinivas device_t child;
250c8247d06SImre Vadász int msix_cap, rid;
25111447b59SVenkatesh Srinivas
25211447b59SVenkatesh Srinivas sc = device_get_softc(dev);
25311447b59SVenkatesh Srinivas sc->vtpci_dev = dev;
2542f2405bbSImre Vadász sc->vtpci_config_irq = -1;
25511447b59SVenkatesh Srinivas
25611447b59SVenkatesh Srinivas pci_enable_busmaster(dev);
25711447b59SVenkatesh Srinivas
25811447b59SVenkatesh Srinivas rid = PCIR_BAR(0);
25911447b59SVenkatesh Srinivas sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
26011447b59SVenkatesh Srinivas RF_ACTIVE);
26111447b59SVenkatesh Srinivas if (sc->vtpci_res == NULL) {
26211447b59SVenkatesh Srinivas device_printf(dev, "cannot map I/O space\n");
26311447b59SVenkatesh Srinivas return (ENXIO);
26411447b59SVenkatesh Srinivas }
26511447b59SVenkatesh Srinivas
266c8247d06SImre Vadász if (pci_find_extcap(dev, PCIY_MSIX, &msix_cap) == 0) {
267c8247d06SImre Vadász uint32_t val;
268c8247d06SImre Vadász val = pci_read_config(dev, msix_cap + PCIR_MSIX_TABLE, 4);
269c8247d06SImre Vadász rid = PCIR_BAR(val & PCIM_MSIX_BIR_MASK);
27011447b59SVenkatesh Srinivas sc->vtpci_msix_res = bus_alloc_resource_any(dev,
27111447b59SVenkatesh Srinivas SYS_RES_MEMORY, &rid, RF_ACTIVE);
27211447b59SVenkatesh Srinivas }
27311447b59SVenkatesh Srinivas
27411447b59SVenkatesh Srinivas vtpci_reset(sc);
27511447b59SVenkatesh Srinivas
27611447b59SVenkatesh Srinivas /* Tell the host we've noticed this device. */
27711447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
27811447b59SVenkatesh Srinivas
27911447b59SVenkatesh Srinivas if ((child = device_add_child(dev, NULL, -1)) == NULL) {
28011447b59SVenkatesh Srinivas device_printf(dev, "cannot create child device\n");
28111447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
28211447b59SVenkatesh Srinivas vtpci_detach(dev);
28311447b59SVenkatesh Srinivas return (ENOMEM);
28411447b59SVenkatesh Srinivas }
28511447b59SVenkatesh Srinivas
28611447b59SVenkatesh Srinivas sc->vtpci_child_dev = child;
28711447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(sc);
28811447b59SVenkatesh Srinivas
28911447b59SVenkatesh Srinivas return (0);
29011447b59SVenkatesh Srinivas }
29111447b59SVenkatesh Srinivas
29211447b59SVenkatesh Srinivas static int
vtpci_detach(device_t dev)29311447b59SVenkatesh Srinivas vtpci_detach(device_t dev)
29411447b59SVenkatesh Srinivas {
29511447b59SVenkatesh Srinivas struct vtpci_softc *sc;
29611447b59SVenkatesh Srinivas device_t child;
29711447b59SVenkatesh Srinivas int error;
29811447b59SVenkatesh Srinivas
29911447b59SVenkatesh Srinivas sc = device_get_softc(dev);
30011447b59SVenkatesh Srinivas
30111447b59SVenkatesh Srinivas if ((child = sc->vtpci_child_dev) != NULL) {
30211447b59SVenkatesh Srinivas error = device_delete_child(dev, child);
30311447b59SVenkatesh Srinivas if (error)
30411447b59SVenkatesh Srinivas return (error);
30511447b59SVenkatesh Srinivas sc->vtpci_child_dev = NULL;
30611447b59SVenkatesh Srinivas }
30711447b59SVenkatesh Srinivas
30811447b59SVenkatesh Srinivas vtpci_reset(sc);
30911447b59SVenkatesh Srinivas
31011447b59SVenkatesh Srinivas if (sc->vtpci_msix_res != NULL) {
311c8247d06SImre Vadász bus_release_resource(dev, SYS_RES_MEMORY,
312c8247d06SImre Vadász rman_get_rid(sc->vtpci_msix_res), sc->vtpci_msix_res);
31311447b59SVenkatesh Srinivas sc->vtpci_msix_res = NULL;
31411447b59SVenkatesh Srinivas }
31511447b59SVenkatesh Srinivas
31611447b59SVenkatesh Srinivas if (sc->vtpci_res != NULL) {
31711447b59SVenkatesh Srinivas bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0),
31811447b59SVenkatesh Srinivas sc->vtpci_res);
31911447b59SVenkatesh Srinivas sc->vtpci_res = NULL;
32011447b59SVenkatesh Srinivas }
32111447b59SVenkatesh Srinivas
32211447b59SVenkatesh Srinivas return (0);
32311447b59SVenkatesh Srinivas }
32411447b59SVenkatesh Srinivas
32511447b59SVenkatesh Srinivas static int
vtpci_suspend(device_t dev)32611447b59SVenkatesh Srinivas vtpci_suspend(device_t dev)
32711447b59SVenkatesh Srinivas {
32811447b59SVenkatesh Srinivas
32911447b59SVenkatesh Srinivas return (bus_generic_suspend(dev));
33011447b59SVenkatesh Srinivas }
33111447b59SVenkatesh Srinivas
33211447b59SVenkatesh Srinivas static int
vtpci_resume(device_t dev)33311447b59SVenkatesh Srinivas vtpci_resume(device_t dev)
33411447b59SVenkatesh Srinivas {
33511447b59SVenkatesh Srinivas
33611447b59SVenkatesh Srinivas return (bus_generic_resume(dev));
33711447b59SVenkatesh Srinivas }
33811447b59SVenkatesh Srinivas
33911447b59SVenkatesh Srinivas static int
vtpci_shutdown(device_t dev)34011447b59SVenkatesh Srinivas vtpci_shutdown(device_t dev)
34111447b59SVenkatesh Srinivas {
34211447b59SVenkatesh Srinivas
34311447b59SVenkatesh Srinivas (void) bus_generic_shutdown(dev);
34411447b59SVenkatesh Srinivas /* Forcibly stop the host device. */
34511447b59SVenkatesh Srinivas vtpci_stop(dev);
34611447b59SVenkatesh Srinivas
34711447b59SVenkatesh Srinivas return (0);
34811447b59SVenkatesh Srinivas }
34911447b59SVenkatesh Srinivas
35011447b59SVenkatesh Srinivas static void
vtpci_driver_added(device_t dev,driver_t * driver)35111447b59SVenkatesh Srinivas vtpci_driver_added(device_t dev, driver_t *driver)
35211447b59SVenkatesh Srinivas {
35311447b59SVenkatesh Srinivas struct vtpci_softc *sc;
35411447b59SVenkatesh Srinivas
35511447b59SVenkatesh Srinivas sc = device_get_softc(dev);
35611447b59SVenkatesh Srinivas
35711447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(sc);
35811447b59SVenkatesh Srinivas }
35911447b59SVenkatesh Srinivas
36011447b59SVenkatesh Srinivas static void
vtpci_child_detached(device_t dev,device_t child)36111447b59SVenkatesh Srinivas vtpci_child_detached(device_t dev, device_t child)
36211447b59SVenkatesh Srinivas {
36311447b59SVenkatesh Srinivas struct vtpci_softc *sc;
36411447b59SVenkatesh Srinivas
36511447b59SVenkatesh Srinivas sc = device_get_softc(dev);
36611447b59SVenkatesh Srinivas
36711447b59SVenkatesh Srinivas vtpci_reset(sc);
36811447b59SVenkatesh Srinivas vtpci_release_child_resources(sc);
36911447b59SVenkatesh Srinivas }
37011447b59SVenkatesh Srinivas
37111447b59SVenkatesh Srinivas static int
vtpci_read_ivar(device_t dev,device_t child,int index,uintptr_t * result)37211447b59SVenkatesh Srinivas vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
37311447b59SVenkatesh Srinivas {
37411447b59SVenkatesh Srinivas struct vtpci_softc *sc;
37511447b59SVenkatesh Srinivas
37611447b59SVenkatesh Srinivas sc = device_get_softc(dev);
37711447b59SVenkatesh Srinivas
37811447b59SVenkatesh Srinivas if (sc->vtpci_child_dev != child)
37911447b59SVenkatesh Srinivas return (ENOENT);
38011447b59SVenkatesh Srinivas
38111447b59SVenkatesh Srinivas switch (index) {
38211447b59SVenkatesh Srinivas case VIRTIO_IVAR_DEVTYPE:
38311447b59SVenkatesh Srinivas *result = pci_get_subdevice(dev);
38411447b59SVenkatesh Srinivas break;
38511447b59SVenkatesh Srinivas default:
38611447b59SVenkatesh Srinivas return (ENOENT);
38711447b59SVenkatesh Srinivas }
38811447b59SVenkatesh Srinivas
38911447b59SVenkatesh Srinivas return (0);
39011447b59SVenkatesh Srinivas }
39111447b59SVenkatesh Srinivas
39211447b59SVenkatesh Srinivas static int
vtpci_write_ivar(device_t dev,device_t child,int index,uintptr_t value)39311447b59SVenkatesh Srinivas vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
39411447b59SVenkatesh Srinivas {
39511447b59SVenkatesh Srinivas struct vtpci_softc *sc;
39611447b59SVenkatesh Srinivas
39711447b59SVenkatesh Srinivas sc = device_get_softc(dev);
39811447b59SVenkatesh Srinivas
39911447b59SVenkatesh Srinivas if (sc->vtpci_child_dev != child)
40011447b59SVenkatesh Srinivas return (ENOENT);
40111447b59SVenkatesh Srinivas
40211447b59SVenkatesh Srinivas switch (index) {
40311447b59SVenkatesh Srinivas case VIRTIO_IVAR_FEATURE_DESC:
40411447b59SVenkatesh Srinivas sc->vtpci_child_feat_desc = (void *) value;
40511447b59SVenkatesh Srinivas break;
40611447b59SVenkatesh Srinivas default:
40711447b59SVenkatesh Srinivas return (ENOENT);
40811447b59SVenkatesh Srinivas }
40911447b59SVenkatesh Srinivas
41011447b59SVenkatesh Srinivas return (0);
41111447b59SVenkatesh Srinivas }
41211447b59SVenkatesh Srinivas
41311447b59SVenkatesh Srinivas static uint64_t
vtpci_negotiate_features(device_t dev,uint64_t child_features)41411447b59SVenkatesh Srinivas vtpci_negotiate_features(device_t dev, uint64_t child_features)
41511447b59SVenkatesh Srinivas {
41611447b59SVenkatesh Srinivas struct vtpci_softc *sc;
41711447b59SVenkatesh Srinivas uint64_t host_features, features;
41811447b59SVenkatesh Srinivas
41911447b59SVenkatesh Srinivas sc = device_get_softc(dev);
42011447b59SVenkatesh Srinivas
42111447b59SVenkatesh Srinivas host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES);
42211447b59SVenkatesh Srinivas vtpci_describe_features(sc, "host", host_features);
42311447b59SVenkatesh Srinivas
42411447b59SVenkatesh Srinivas /*
42511447b59SVenkatesh Srinivas * Limit negotiated features to what the driver, virtqueue, and
42611447b59SVenkatesh Srinivas * host all support.
42711447b59SVenkatesh Srinivas */
42811447b59SVenkatesh Srinivas features = host_features & child_features;
42911447b59SVenkatesh Srinivas features = virtqueue_filter_features(features);
43011447b59SVenkatesh Srinivas sc->vtpci_features = features;
43111447b59SVenkatesh Srinivas
43211447b59SVenkatesh Srinivas vtpci_describe_features(sc, "negotiated", features);
43311447b59SVenkatesh Srinivas vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features);
43411447b59SVenkatesh Srinivas
43511447b59SVenkatesh Srinivas return (features);
43611447b59SVenkatesh Srinivas }
43711447b59SVenkatesh Srinivas
43811447b59SVenkatesh Srinivas static int
vtpci_with_feature(device_t dev,uint64_t feature)43911447b59SVenkatesh Srinivas vtpci_with_feature(device_t dev, uint64_t feature)
44011447b59SVenkatesh Srinivas {
44111447b59SVenkatesh Srinivas struct vtpci_softc *sc;
44211447b59SVenkatesh Srinivas
44311447b59SVenkatesh Srinivas sc = device_get_softc(dev);
44411447b59SVenkatesh Srinivas
44511447b59SVenkatesh Srinivas return ((sc->vtpci_features & feature) != 0);
44611447b59SVenkatesh Srinivas }
44711447b59SVenkatesh Srinivas
44811447b59SVenkatesh Srinivas static int
vtpci_intr_count(device_t dev)4492f2405bbSImre Vadász vtpci_intr_count(device_t dev)
4502f2405bbSImre Vadász {
4512f2405bbSImre Vadász struct vtpci_softc *sc = device_get_softc(dev);
4522f2405bbSImre Vadász
4532f2405bbSImre Vadász if (vtpci_disable_msix != 0 || sc->vtpci_msix_res == NULL)
4542f2405bbSImre Vadász return 1;
4552f2405bbSImre Vadász else
4562f2405bbSImre Vadász return pci_msix_count(dev);
4572f2405bbSImre Vadász }
4582f2405bbSImre Vadász
4592f2405bbSImre Vadász /* Will never return 0, with *cnt <= 0. */
4602f2405bbSImre Vadász static int
vtpci_intr_alloc(device_t dev,int * cnt,int use_config,int * cpus)4612f2405bbSImre Vadász vtpci_intr_alloc(device_t dev, int *cnt, int use_config, int *cpus)
4622f2405bbSImre Vadász {
4632f2405bbSImre Vadász struct vtpci_softc *sc = device_get_softc(dev);
4642f2405bbSImre Vadász int i;
4652f2405bbSImre Vadász
4662f2405bbSImre Vadász if (sc->vtpci_nintr_res > 0)
4672f2405bbSImre Vadász return (EINVAL);
4682f2405bbSImre Vadász
4692f2405bbSImre Vadász if (*cnt <= 0)
4702f2405bbSImre Vadász return (EINVAL);
4712f2405bbSImre Vadász
4722f2405bbSImre Vadász if (vtpci_disable_msix == 0 && sc->vtpci_msix_res != NULL) {
4732f2405bbSImre Vadász int nmsix = pci_msix_count(dev);
4742f2405bbSImre Vadász if (nmsix < *cnt)
4752f2405bbSImre Vadász *cnt = nmsix;
4762f2405bbSImre Vadász }
4772f2405bbSImre Vadász
4782f2405bbSImre Vadász if ((*cnt > 1 || use_config == 0) &&
4792f2405bbSImre Vadász vtpci_disable_msix == 0 && sc->vtpci_msix_res != NULL) {
4802f2405bbSImre Vadász if (pci_setup_msix(dev) != 0) {
4812f2405bbSImre Vadász device_printf(dev, "pci_setup_msix failed\n");
4822f2405bbSImre Vadász /* Just fallthrough to legacy IRQ code instead. */
4832f2405bbSImre Vadász } else {
4842f2405bbSImre Vadász for (i = 0; i < *cnt; i++) {
4852f2405bbSImre Vadász int cpu, rid;
4862f2405bbSImre Vadász
4872f2405bbSImre Vadász if (cpus != NULL && cpus[i] >= 0 &&
4882f2405bbSImre Vadász cpus[i] < ncpus) {
4892f2405bbSImre Vadász cpu = cpus[i];
4902f2405bbSImre Vadász } else {
4912f2405bbSImre Vadász cpu = device_get_unit(dev) + i;
4922f2405bbSImre Vadász cpu %= ncpus;
4932f2405bbSImre Vadász }
4942f2405bbSImre Vadász if (pci_alloc_msix_vector(dev, i, &rid, cpu)
4952f2405bbSImre Vadász != 0) {
4962f2405bbSImre Vadász if (i > 1 || (i == 1 && !use_config)) {
4972f2405bbSImre Vadász *cnt = i;
4982f2405bbSImre Vadász /* Got some MSI-X vectors. */
4992f2405bbSImre Vadász sc->vtpci_irq_flags = RF_ACTIVE;
5002f2405bbSImre Vadász sc->vtpci_flags |=
5012f2405bbSImre Vadász VIRTIO_PCI_FLAG_MSIX;
5022f2405bbSImre Vadász goto finish;
5032f2405bbSImre Vadász }
5042f2405bbSImre Vadász /*
5052f2405bbSImre Vadász * Allocate the legacy IRQ instead.
5062f2405bbSImre Vadász */
5072f2405bbSImre Vadász if (i == 1) {
5082f2405bbSImre Vadász pci_release_msix_vector(dev, 0);
5092f2405bbSImre Vadász }
5102f2405bbSImre Vadász pci_teardown_msix(dev);
5112f2405bbSImre Vadász break;
5122f2405bbSImre Vadász }
5132f2405bbSImre Vadász sc->vtpci_intr_res[i].rid = rid;
5142f2405bbSImre Vadász }
5152f2405bbSImre Vadász /* Got all the MSI-X vectors we wanted. */
5162f2405bbSImre Vadász sc->vtpci_irq_flags = RF_ACTIVE;
5172f2405bbSImre Vadász sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSIX;
5182f2405bbSImre Vadász /* Successfully allocated all MSI-X vectors */
5192f2405bbSImre Vadász goto finish;
5202f2405bbSImre Vadász }
5212f2405bbSImre Vadász }
5222f2405bbSImre Vadász
5232f2405bbSImre Vadász /* Legacy IRQ code: */
5242f2405bbSImre Vadász *cnt = 1;
5252f2405bbSImre Vadász /*
5262f2405bbSImre Vadász * Use MSI interrupts if available. Otherwise, we fallback
5272f2405bbSImre Vadász * to legacy interrupts.
5282f2405bbSImre Vadász */
5292f2405bbSImre Vadász sc->vtpci_intr_res[0].rid = 0;
5302f2405bbSImre Vadász if (pci_alloc_1intr(sc->vtpci_dev, 1,
5312f2405bbSImre Vadász &sc->vtpci_intr_res[0].rid,
5322f2405bbSImre Vadász &sc->vtpci_irq_flags) == PCI_INTR_TYPE_MSI) {
5332f2405bbSImre Vadász sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSI;
5342f2405bbSImre Vadász }
5352f2405bbSImre Vadász
5362f2405bbSImre Vadász finish:
5372f2405bbSImre Vadász KKASSERT(!((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) != 0 &&
5382f2405bbSImre Vadász (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) != 0));
5392f2405bbSImre Vadász
5402f2405bbSImre Vadász sc->vtpci_nintr_res = *cnt;
5412f2405bbSImre Vadász for (i = 0; i < sc->vtpci_nintr_res; i++) {
5422f2405bbSImre Vadász struct resource *irq;
5432f2405bbSImre Vadász
5442f2405bbSImre Vadász TAILQ_INIT(&sc->vtpci_intr_res[i].ls);
5452f2405bbSImre Vadász sc->vtpci_intr_res[i].ires_sc = sc;
5462f2405bbSImre Vadász irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
5472f2405bbSImre Vadász &sc->vtpci_intr_res[i].rid, sc->vtpci_irq_flags);
5482f2405bbSImre Vadász if (irq == NULL)
5492f2405bbSImre Vadász return (ENXIO);
5502f2405bbSImre Vadász if (cpus != NULL)
5512f2405bbSImre Vadász cpus[i] = rman_get_cpuid(irq);
5522f2405bbSImre Vadász
5532f2405bbSImre Vadász sc->vtpci_intr_res[i].irq = irq;
5542f2405bbSImre Vadász }
5552f2405bbSImre Vadász
5562f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
5572f2405bbSImre Vadász device_printf(dev, "using %d MSI-X vectors\n", *cnt);
5582f2405bbSImre Vadász pci_enable_msix(dev);
5592f2405bbSImre Vadász }
5602f2405bbSImre Vadász
5612f2405bbSImre Vadász return (0);
5622f2405bbSImre Vadász }
5632f2405bbSImre Vadász
5642f2405bbSImre Vadász static int
vtpci_intr_release(device_t dev)5652f2405bbSImre Vadász vtpci_intr_release(device_t dev)
5662f2405bbSImre Vadász {
5672f2405bbSImre Vadász struct vtpci_softc *sc = device_get_softc(dev);
5682f2405bbSImre Vadász struct vtpci_intr_resource *ires;
5692f2405bbSImre Vadász int i;
5702f2405bbSImre Vadász
5712f2405bbSImre Vadász if (sc->vtpci_nintr_res == 0)
5722f2405bbSImre Vadász return (EINVAL);
5732f2405bbSImre Vadász
5742f2405bbSImre Vadász /* XXX Make sure none of the interrupts is used at the moment. */
5752f2405bbSImre Vadász
5762f2405bbSImre Vadász for (i = 0; i < sc->vtpci_nintr_res; i++) {
5772f2405bbSImre Vadász ires = &sc->vtpci_intr_res[i];
5782f2405bbSImre Vadász
5792f2405bbSImre Vadász KKASSERT(TAILQ_EMPTY(&ires->ls));
5802f2405bbSImre Vadász if (ires->irq != NULL) {
5812f2405bbSImre Vadász bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
5822f2405bbSImre Vadász ires->irq);
5832f2405bbSImre Vadász ires->irq = NULL;
5842f2405bbSImre Vadász }
5852f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX)
5862f2405bbSImre Vadász pci_release_msix_vector(dev, ires->rid);
5872f2405bbSImre Vadász ires->rid = 0;
5882f2405bbSImre Vadász }
5892f2405bbSImre Vadász sc->vtpci_nintr_res = 0;
5902f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) {
5912f2405bbSImre Vadász pci_release_msi(dev);
5922f2405bbSImre Vadász sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSI;
5932f2405bbSImre Vadász }
5942f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
5952f2405bbSImre Vadász pci_teardown_msix(dev);
5962f2405bbSImre Vadász sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSIX;
5972f2405bbSImre Vadász }
5982f2405bbSImre Vadász return (0);
5992f2405bbSImre Vadász }
6002f2405bbSImre Vadász
6012f2405bbSImre Vadász static int
vtpci_alloc_virtqueues(device_t dev,int nvqs,struct vq_alloc_info * vq_info)602099c4d8eSImre Vadász vtpci_alloc_virtqueues(device_t dev, int nvqs, struct vq_alloc_info *vq_info)
60311447b59SVenkatesh Srinivas {
60411447b59SVenkatesh Srinivas struct vtpci_softc *sc;
60511447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx;
60611447b59SVenkatesh Srinivas struct vq_alloc_info *info;
60711447b59SVenkatesh Srinivas int queue, error;
60811447b59SVenkatesh Srinivas uint16_t vq_size;
60911447b59SVenkatesh Srinivas
61011447b59SVenkatesh Srinivas sc = device_get_softc(dev);
61111447b59SVenkatesh Srinivas
61211447b59SVenkatesh Srinivas if (sc->vtpci_nvqs != 0 || nvqs <= 0 ||
61311447b59SVenkatesh Srinivas nvqs > VIRTIO_MAX_VIRTQUEUES)
61411447b59SVenkatesh Srinivas return (EINVAL);
61511447b59SVenkatesh Srinivas
61611447b59SVenkatesh Srinivas for (queue = 0; queue < nvqs; queue++) {
61711447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[queue];
61811447b59SVenkatesh Srinivas info = &vq_info[queue];
61911447b59SVenkatesh Srinivas
6202f2405bbSImre Vadász vqx->ires_idx = -1;
62111447b59SVenkatesh Srinivas vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
62211447b59SVenkatesh Srinivas
62311447b59SVenkatesh Srinivas vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
62411447b59SVenkatesh Srinivas error = virtqueue_alloc(dev, queue, vq_size,
62511447b59SVenkatesh Srinivas VIRTIO_PCI_VRING_ALIGN, 0xFFFFFFFFUL, info, &vqx->vq);
62611447b59SVenkatesh Srinivas if (error)
62711447b59SVenkatesh Srinivas return (error);
62811447b59SVenkatesh Srinivas
62911447b59SVenkatesh Srinivas vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
63011447b59SVenkatesh Srinivas virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
63111447b59SVenkatesh Srinivas
63211447b59SVenkatesh Srinivas *info->vqai_vq = vqx->vq;
63311447b59SVenkatesh Srinivas sc->vtpci_nvqs++;
63411447b59SVenkatesh Srinivas }
63511447b59SVenkatesh Srinivas
63611447b59SVenkatesh Srinivas return (0);
63711447b59SVenkatesh Srinivas }
63811447b59SVenkatesh Srinivas
6392f2405bbSImre Vadász /* XXX Add argument to specify the callback function here. */
64011447b59SVenkatesh Srinivas static int
vtpci_setup_intr(device_t dev,uint irq,lwkt_serialize_t slz)6412f2405bbSImre Vadász vtpci_setup_intr(device_t dev, uint irq, lwkt_serialize_t slz)
64211447b59SVenkatesh Srinivas {
64311447b59SVenkatesh Srinivas struct vtpci_softc *sc;
64411447b59SVenkatesh Srinivas struct vtpci_intr_resource *ires;
6452f2405bbSImre Vadász int flags, error;
64611447b59SVenkatesh Srinivas
64711447b59SVenkatesh Srinivas sc = device_get_softc(dev);
64811447b59SVenkatesh Srinivas flags = INTR_MPSAFE;
6492f2405bbSImre Vadász
6502f2405bbSImre Vadász if ((int)irq >= sc->vtpci_nintr_res)
6512f2405bbSImre Vadász return (EINVAL);
6522f2405bbSImre Vadász ires = &sc->vtpci_intr_res[irq];
65311447b59SVenkatesh Srinivas
65411447b59SVenkatesh Srinivas if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) == 0) {
65511447b59SVenkatesh Srinivas error = bus_setup_intr(dev, ires->irq, flags,
656df2df2fcSImre Vadász vtpci_legacy_intr,
6572f2405bbSImre Vadász ires, &ires->intrhand, slz);
6582f2405bbSImre Vadász } else {
6592f2405bbSImre Vadász error = bus_setup_intr(dev, ires->irq, flags,
6602f2405bbSImre Vadász vtpci_msix_intr,
6612f2405bbSImre Vadász ires, &ires->intrhand, slz);
6622f2405bbSImre Vadász }
66311447b59SVenkatesh Srinivas return (error);
66411447b59SVenkatesh Srinivas }
66511447b59SVenkatesh Srinivas
6662f2405bbSImre Vadász static int
vtpci_teardown_intr(device_t dev,uint irq)6672f2405bbSImre Vadász vtpci_teardown_intr(device_t dev, uint irq)
6682f2405bbSImre Vadász {
6692f2405bbSImre Vadász struct vtpci_softc *sc = device_get_softc(dev);
6702f2405bbSImre Vadász struct vtpci_intr_resource *ires;
67111447b59SVenkatesh Srinivas
6722f2405bbSImre Vadász if ((int)irq >= sc->vtpci_nintr_res)
6732f2405bbSImre Vadász return (EINVAL);
67411447b59SVenkatesh Srinivas
6752f2405bbSImre Vadász ires = &sc->vtpci_intr_res[irq];
6762f2405bbSImre Vadász
6772f2405bbSImre Vadász if (ires->intrhand == NULL)
6782f2405bbSImre Vadász return (ENXIO);
6792f2405bbSImre Vadász
6802f2405bbSImre Vadász bus_teardown_intr(dev, ires->irq, ires->intrhand);
6812f2405bbSImre Vadász ires->intrhand = NULL;
6822f2405bbSImre Vadász return (0);
68311447b59SVenkatesh Srinivas }
68411447b59SVenkatesh Srinivas
6852f2405bbSImre Vadász static void
vtpci_add_irqentry(struct vtpci_intr_resource * intr_res,int what,driver_intr_t handler,void * arg)6869d96478cSImre Vadász vtpci_add_irqentry(struct vtpci_intr_resource *intr_res, int what,
6879d96478cSImre Vadász driver_intr_t handler, void *arg)
6882f2405bbSImre Vadász {
6892f2405bbSImre Vadász struct vqentry *e;
69011447b59SVenkatesh Srinivas
6912f2405bbSImre Vadász TAILQ_FOREACH(e, &intr_res->ls, entries) {
6922f2405bbSImre Vadász if (e->what == what)
6932f2405bbSImre Vadász return;
6942f2405bbSImre Vadász }
6959d96478cSImre Vadász e = kmalloc(sizeof(*e), M_DEVBUF, M_WAITOK | M_ZERO);
6962f2405bbSImre Vadász e->what = what;
6979d96478cSImre Vadász if (e->what == -1) {
6989d96478cSImre Vadász e->vq = NULL;
6999d96478cSImre Vadász } else {
7009d96478cSImre Vadász e->vq = intr_res->ires_sc->vtpci_vqx[e->what].vq;
7019d96478cSImre Vadász }
7029d96478cSImre Vadász e->handler = handler;
7039d96478cSImre Vadász e->arg = arg;
7042f2405bbSImre Vadász TAILQ_INSERT_TAIL(&intr_res->ls, e, entries);
7052f2405bbSImre Vadász }
7062f2405bbSImre Vadász
7072f2405bbSImre Vadász static void
vtpci_del_irqentry(struct vtpci_intr_resource * intr_res,int what)7082f2405bbSImre Vadász vtpci_del_irqentry(struct vtpci_intr_resource *intr_res, int what)
7092f2405bbSImre Vadász {
7102f2405bbSImre Vadász struct vqentry *e;
7112f2405bbSImre Vadász
7122f2405bbSImre Vadász TAILQ_FOREACH(e, &intr_res->ls, entries) {
7132f2405bbSImre Vadász if (e->what == what)
7142f2405bbSImre Vadász break;
7152f2405bbSImre Vadász }
7162f2405bbSImre Vadász if (e != NULL) {
7172f2405bbSImre Vadász TAILQ_REMOVE(&intr_res->ls, e, entries);
7182f2405bbSImre Vadász kfree(e, M_DEVBUF);
7192f2405bbSImre Vadász }
7202f2405bbSImre Vadász }
7212f2405bbSImre Vadász
7222f2405bbSImre Vadász /*
7232f2405bbSImre Vadász * Config intr can be bound after intr_alloc, virtqueue intrs can be bound
7242f2405bbSImre Vadász * after intr_alloc and alloc_virtqueues.
7252f2405bbSImre Vadász */
7262f2405bbSImre Vadász static int
vtpci_bind_intr(device_t dev,uint irq,int what,driver_intr_t handler,void * arg)7279d96478cSImre Vadász vtpci_bind_intr(device_t dev, uint irq, int what,
7289d96478cSImre Vadász driver_intr_t handler, void *arg)
7292f2405bbSImre Vadász {
7302f2405bbSImre Vadász struct vtpci_softc *sc = device_get_softc(dev);
7312f2405bbSImre Vadász struct vtpci_virtqueue *vqx;
7322f2405bbSImre Vadász int error;
7332f2405bbSImre Vadász
7342f2405bbSImre Vadász if (irq >= sc->vtpci_nintr_res)
7352f2405bbSImre Vadász return (EINVAL);
7362f2405bbSImre Vadász
7372f2405bbSImre Vadász if (what == -1) {
7382f2405bbSImre Vadász if (sc->vtpci_config_irq != -1)
7392f2405bbSImre Vadász return (EINVAL);
7402f2405bbSImre Vadász
7412f2405bbSImre Vadász sc->vtpci_config_irq = irq;
7422f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
7432f2405bbSImre Vadász error = vtpci_register_msix_vector(sc,
7442f2405bbSImre Vadász VIRTIO_MSI_CONFIG_VECTOR, irq);
74511447b59SVenkatesh Srinivas if (error)
74611447b59SVenkatesh Srinivas return (error);
74711447b59SVenkatesh Srinivas }
7482f2405bbSImre Vadász goto done;
7492f2405bbSImre Vadász }
75011447b59SVenkatesh Srinivas
7512f2405bbSImre Vadász if (sc->vtpci_nvqs <= what || what < 0)
7522f2405bbSImre Vadász return (EINVAL);
7532f2405bbSImre Vadász
7542f2405bbSImre Vadász vqx = &sc->vtpci_vqx[what];
7552f2405bbSImre Vadász if (vqx->ires_idx != -1)
7562f2405bbSImre Vadász return (EINVAL);
7572f2405bbSImre Vadász
7582f2405bbSImre Vadász vqx->ires_idx = irq;
7592f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
7602f2405bbSImre Vadász vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, what);
7612f2405bbSImre Vadász error = vtpci_register_msix_vector(sc, VIRTIO_MSI_QUEUE_VECTOR,
7622f2405bbSImre Vadász irq);
7632f2405bbSImre Vadász if (error)
7642f2405bbSImre Vadász return (error);
7652f2405bbSImre Vadász }
7662f2405bbSImre Vadász done:
7679d96478cSImre Vadász vtpci_add_irqentry(&sc->vtpci_intr_res[irq], what, handler, arg);
7682f2405bbSImre Vadász return (0);
7692f2405bbSImre Vadász }
7702f2405bbSImre Vadász
7712f2405bbSImre Vadász static int
vtpci_unbind_intr(device_t dev,int what)7722f2405bbSImre Vadász vtpci_unbind_intr(device_t dev, int what)
7732f2405bbSImre Vadász {
7742f2405bbSImre Vadász struct vtpci_softc *sc = device_get_softc(dev);
7752f2405bbSImre Vadász struct vtpci_virtqueue *vqx;
7762f2405bbSImre Vadász uint irq;
7772f2405bbSImre Vadász
7782f2405bbSImre Vadász if (what == -1) {
7792f2405bbSImre Vadász if (sc->vtpci_config_irq == -1)
7802f2405bbSImre Vadász return (EINVAL);
7812f2405bbSImre Vadász
7822f2405bbSImre Vadász irq = sc->vtpci_config_irq;
7832f2405bbSImre Vadász sc->vtpci_config_irq = -1;
7842f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
7852f2405bbSImre Vadász vtpci_register_msix_vector(sc,
7862f2405bbSImre Vadász VIRTIO_MSI_CONFIG_VECTOR, -1);
7872f2405bbSImre Vadász }
7882f2405bbSImre Vadász goto done;
7892f2405bbSImre Vadász }
7902f2405bbSImre Vadász
7912f2405bbSImre Vadász if (sc->vtpci_nvqs <= what || what < 0)
7922f2405bbSImre Vadász return (EINVAL);
7932f2405bbSImre Vadász
7942f2405bbSImre Vadász vqx = &sc->vtpci_vqx[what];
7952f2405bbSImre Vadász if (vqx->ires_idx == -1)
7962f2405bbSImre Vadász return (EINVAL);
7972f2405bbSImre Vadász
7982f2405bbSImre Vadász irq = vqx->ires_idx;
7992f2405bbSImre Vadász vqx->ires_idx = -1;
8002f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
8012f2405bbSImre Vadász vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, what);
8022f2405bbSImre Vadász vtpci_register_msix_vector(sc, VIRTIO_MSI_QUEUE_VECTOR, -1);
8032f2405bbSImre Vadász }
8042f2405bbSImre Vadász done:
8052f2405bbSImre Vadász KKASSERT(irq >= 0 && irq < sc->vtpci_nintr_res);
8062f2405bbSImre Vadász vtpci_del_irqentry(&sc->vtpci_intr_res[irq], what);
80711447b59SVenkatesh Srinivas return (0);
80811447b59SVenkatesh Srinivas }
80911447b59SVenkatesh Srinivas
81011447b59SVenkatesh Srinivas static void
vtpci_stop(device_t dev)81111447b59SVenkatesh Srinivas vtpci_stop(device_t dev)
81211447b59SVenkatesh Srinivas {
81311447b59SVenkatesh Srinivas vtpci_reset(device_get_softc(dev));
81411447b59SVenkatesh Srinivas }
81511447b59SVenkatesh Srinivas
81611447b59SVenkatesh Srinivas static int
vtpci_reinit(device_t dev,uint64_t features)81711447b59SVenkatesh Srinivas vtpci_reinit(device_t dev, uint64_t features)
81811447b59SVenkatesh Srinivas {
81911447b59SVenkatesh Srinivas struct vtpci_softc *sc;
82011447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx;
82111447b59SVenkatesh Srinivas struct virtqueue *vq;
82211447b59SVenkatesh Srinivas int queue, error;
82311447b59SVenkatesh Srinivas uint16_t vq_size;
82411447b59SVenkatesh Srinivas
82511447b59SVenkatesh Srinivas sc = device_get_softc(dev);
82611447b59SVenkatesh Srinivas
82711447b59SVenkatesh Srinivas /*
82811447b59SVenkatesh Srinivas * Redrive the device initialization. This is a bit of an abuse
82911447b59SVenkatesh Srinivas * of the specification, but both VirtualBox and QEMU/KVM seem
83011447b59SVenkatesh Srinivas * to play nice. We do not allow the host device to change from
83111447b59SVenkatesh Srinivas * what was originally negotiated beyond what the guest driver
83211447b59SVenkatesh Srinivas * changed (MSIX state should not change, number of virtqueues
83311447b59SVenkatesh Srinivas * and their size remain the same, etc).
83411447b59SVenkatesh Srinivas */
83511447b59SVenkatesh Srinivas
83611447b59SVenkatesh Srinivas if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET)
83711447b59SVenkatesh Srinivas vtpci_stop(dev);
83811447b59SVenkatesh Srinivas
83911447b59SVenkatesh Srinivas /*
84011447b59SVenkatesh Srinivas * Quickly drive the status through ACK and DRIVER. The device
84111447b59SVenkatesh Srinivas * does not become usable again until vtpci_reinit_complete().
84211447b59SVenkatesh Srinivas */
84311447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
84411447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
84511447b59SVenkatesh Srinivas
84611447b59SVenkatesh Srinivas vtpci_negotiate_features(dev, features);
84711447b59SVenkatesh Srinivas
84811447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
849c8247d06SImre Vadász pci_enable_msix(dev);
850*92d9e66dSImre Vadász if (sc->vtpci_config_irq != -1) {
85111447b59SVenkatesh Srinivas error = vtpci_register_msix_vector(sc,
852*92d9e66dSImre Vadász VIRTIO_MSI_CONFIG_VECTOR, sc->vtpci_config_irq);
85311447b59SVenkatesh Srinivas if (error)
85411447b59SVenkatesh Srinivas return (error);
85511447b59SVenkatesh Srinivas }
856*92d9e66dSImre Vadász }
85711447b59SVenkatesh Srinivas
85811447b59SVenkatesh Srinivas for (queue = 0; queue < sc->vtpci_nvqs; queue++) {
85911447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[queue];
86011447b59SVenkatesh Srinivas vq = vqx->vq;
86111447b59SVenkatesh Srinivas
86211447b59SVenkatesh Srinivas KASSERT(vq != NULL, ("vq %d not allocated", queue));
86311447b59SVenkatesh Srinivas vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
86411447b59SVenkatesh Srinivas
86511447b59SVenkatesh Srinivas vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
86611447b59SVenkatesh Srinivas error = virtqueue_reinit(vq, vq_size);
86711447b59SVenkatesh Srinivas if (error)
86811447b59SVenkatesh Srinivas return (error);
86911447b59SVenkatesh Srinivas
870*92d9e66dSImre Vadász if (vqx->ires_idx != -1 &&
871*92d9e66dSImre Vadász (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX)) {
87211447b59SVenkatesh Srinivas error = vtpci_register_msix_vector(sc,
87311447b59SVenkatesh Srinivas VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx);
87411447b59SVenkatesh Srinivas if (error)
87511447b59SVenkatesh Srinivas return (error);
87611447b59SVenkatesh Srinivas }
87711447b59SVenkatesh Srinivas
87811447b59SVenkatesh Srinivas vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
87911447b59SVenkatesh Srinivas virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
88011447b59SVenkatesh Srinivas }
88111447b59SVenkatesh Srinivas
88211447b59SVenkatesh Srinivas return (0);
88311447b59SVenkatesh Srinivas }
88411447b59SVenkatesh Srinivas
88511447b59SVenkatesh Srinivas static void
vtpci_reinit_complete(device_t dev)88611447b59SVenkatesh Srinivas vtpci_reinit_complete(device_t dev)
88711447b59SVenkatesh Srinivas {
88811447b59SVenkatesh Srinivas
88911447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
89011447b59SVenkatesh Srinivas }
89111447b59SVenkatesh Srinivas
89211447b59SVenkatesh Srinivas static void
vtpci_notify_virtqueue(device_t dev,uint16_t queue)89311447b59SVenkatesh Srinivas vtpci_notify_virtqueue(device_t dev, uint16_t queue)
89411447b59SVenkatesh Srinivas {
89511447b59SVenkatesh Srinivas struct vtpci_softc *sc;
89611447b59SVenkatesh Srinivas
89711447b59SVenkatesh Srinivas sc = device_get_softc(dev);
89811447b59SVenkatesh Srinivas
89911447b59SVenkatesh Srinivas vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue);
90011447b59SVenkatesh Srinivas }
90111447b59SVenkatesh Srinivas
90211447b59SVenkatesh Srinivas static uint8_t
vtpci_get_status(device_t dev)90311447b59SVenkatesh Srinivas vtpci_get_status(device_t dev)
90411447b59SVenkatesh Srinivas {
90511447b59SVenkatesh Srinivas struct vtpci_softc *sc;
90611447b59SVenkatesh Srinivas
90711447b59SVenkatesh Srinivas sc = device_get_softc(dev);
90811447b59SVenkatesh Srinivas
90911447b59SVenkatesh Srinivas return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS));
91011447b59SVenkatesh Srinivas }
91111447b59SVenkatesh Srinivas
91211447b59SVenkatesh Srinivas static void
vtpci_set_status(device_t dev,uint8_t status)91311447b59SVenkatesh Srinivas vtpci_set_status(device_t dev, uint8_t status)
91411447b59SVenkatesh Srinivas {
91511447b59SVenkatesh Srinivas struct vtpci_softc *sc;
91611447b59SVenkatesh Srinivas
91711447b59SVenkatesh Srinivas sc = device_get_softc(dev);
91811447b59SVenkatesh Srinivas
91911447b59SVenkatesh Srinivas if (status != VIRTIO_CONFIG_STATUS_RESET)
92011447b59SVenkatesh Srinivas status |= vtpci_get_status(dev);
92111447b59SVenkatesh Srinivas
92211447b59SVenkatesh Srinivas vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status);
92311447b59SVenkatesh Srinivas }
92411447b59SVenkatesh Srinivas
92511447b59SVenkatesh Srinivas static void
vtpci_read_dev_config(device_t dev,bus_size_t offset,void * dst,int length)92611447b59SVenkatesh Srinivas vtpci_read_dev_config(device_t dev, bus_size_t offset,
92711447b59SVenkatesh Srinivas void *dst, int length)
92811447b59SVenkatesh Srinivas {
92911447b59SVenkatesh Srinivas struct vtpci_softc *sc;
93011447b59SVenkatesh Srinivas bus_size_t off;
93111447b59SVenkatesh Srinivas uint8_t *d;
93211447b59SVenkatesh Srinivas int size;
93311447b59SVenkatesh Srinivas
93411447b59SVenkatesh Srinivas sc = device_get_softc(dev);
93511447b59SVenkatesh Srinivas off = VIRTIO_PCI_CONFIG(sc) + offset;
93611447b59SVenkatesh Srinivas
93711447b59SVenkatesh Srinivas for (d = dst; length > 0; d += size, off += size, length -= size) {
93811447b59SVenkatesh Srinivas if (length >= 4) {
93911447b59SVenkatesh Srinivas size = 4;
94011447b59SVenkatesh Srinivas *(uint32_t *)d = vtpci_read_config_4(sc, off);
94111447b59SVenkatesh Srinivas } else if (length >= 2) {
94211447b59SVenkatesh Srinivas size = 2;
94311447b59SVenkatesh Srinivas *(uint16_t *)d = vtpci_read_config_2(sc, off);
94411447b59SVenkatesh Srinivas } else {
94511447b59SVenkatesh Srinivas size = 1;
94611447b59SVenkatesh Srinivas *d = vtpci_read_config_1(sc, off);
94711447b59SVenkatesh Srinivas }
94811447b59SVenkatesh Srinivas }
94911447b59SVenkatesh Srinivas }
95011447b59SVenkatesh Srinivas
95111447b59SVenkatesh Srinivas static void
vtpci_write_dev_config(device_t dev,bus_size_t offset,void * src,int length)95211447b59SVenkatesh Srinivas vtpci_write_dev_config(device_t dev, bus_size_t offset,
95311447b59SVenkatesh Srinivas void *src, int length)
95411447b59SVenkatesh Srinivas {
95511447b59SVenkatesh Srinivas struct vtpci_softc *sc;
95611447b59SVenkatesh Srinivas bus_size_t off;
95711447b59SVenkatesh Srinivas uint8_t *s;
95811447b59SVenkatesh Srinivas int size;
95911447b59SVenkatesh Srinivas
96011447b59SVenkatesh Srinivas sc = device_get_softc(dev);
96111447b59SVenkatesh Srinivas off = VIRTIO_PCI_CONFIG(sc) + offset;
96211447b59SVenkatesh Srinivas
96311447b59SVenkatesh Srinivas for (s = src; length > 0; s += size, off += size, length -= size) {
96411447b59SVenkatesh Srinivas if (length >= 4) {
96511447b59SVenkatesh Srinivas size = 4;
96611447b59SVenkatesh Srinivas vtpci_write_config_4(sc, off, *(uint32_t *)s);
96711447b59SVenkatesh Srinivas } else if (length >= 2) {
96811447b59SVenkatesh Srinivas size = 2;
96911447b59SVenkatesh Srinivas vtpci_write_config_2(sc, off, *(uint16_t *)s);
97011447b59SVenkatesh Srinivas } else {
97111447b59SVenkatesh Srinivas size = 1;
97211447b59SVenkatesh Srinivas vtpci_write_config_1(sc, off, *s);
97311447b59SVenkatesh Srinivas }
97411447b59SVenkatesh Srinivas }
97511447b59SVenkatesh Srinivas }
97611447b59SVenkatesh Srinivas
97711447b59SVenkatesh Srinivas static void
vtpci_describe_features(struct vtpci_softc * sc,const char * msg,uint64_t features)97811447b59SVenkatesh Srinivas vtpci_describe_features(struct vtpci_softc *sc, const char *msg,
97911447b59SVenkatesh Srinivas uint64_t features)
98011447b59SVenkatesh Srinivas {
98111447b59SVenkatesh Srinivas device_t dev, child;
98211447b59SVenkatesh Srinivas
98311447b59SVenkatesh Srinivas dev = sc->vtpci_dev;
98411447b59SVenkatesh Srinivas child = sc->vtpci_child_dev;
98511447b59SVenkatesh Srinivas
98611447b59SVenkatesh Srinivas if (device_is_attached(child) && bootverbose == 0)
98711447b59SVenkatesh Srinivas return;
98811447b59SVenkatesh Srinivas
98911447b59SVenkatesh Srinivas virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc);
99011447b59SVenkatesh Srinivas }
99111447b59SVenkatesh Srinivas
99211447b59SVenkatesh Srinivas static void
vtpci_probe_and_attach_child(struct vtpci_softc * sc)99311447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(struct vtpci_softc *sc)
99411447b59SVenkatesh Srinivas {
99511447b59SVenkatesh Srinivas device_t dev, child;
996b17fe9c5SImre Vadasz int error;
99711447b59SVenkatesh Srinivas
99811447b59SVenkatesh Srinivas dev = sc->vtpci_dev;
99911447b59SVenkatesh Srinivas child = sc->vtpci_child_dev;
100011447b59SVenkatesh Srinivas
100111447b59SVenkatesh Srinivas if (child == NULL)
100211447b59SVenkatesh Srinivas return;
100311447b59SVenkatesh Srinivas
100411447b59SVenkatesh Srinivas if (device_get_state(child) != DS_NOTPRESENT)
100511447b59SVenkatesh Srinivas return;
100611447b59SVenkatesh Srinivas
100711447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
1008b17fe9c5SImre Vadasz error = device_probe_and_attach(child);
1009b17fe9c5SImre Vadasz if (error != 0 || device_get_state(child) == DS_NOTPRESENT) {
101011447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
101111447b59SVenkatesh Srinivas vtpci_reset(sc);
101211447b59SVenkatesh Srinivas vtpci_release_child_resources(sc);
101311447b59SVenkatesh Srinivas
101411447b59SVenkatesh Srinivas /* Reset status for future attempt. */
101511447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
101611447b59SVenkatesh Srinivas } else
101711447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
101811447b59SVenkatesh Srinivas }
101911447b59SVenkatesh Srinivas
102011447b59SVenkatesh Srinivas static int
vtpci_register_msix_vector(struct vtpci_softc * sc,int offset,int res_idx)102111447b59SVenkatesh Srinivas vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx)
102211447b59SVenkatesh Srinivas {
102311447b59SVenkatesh Srinivas device_t dev;
102411447b59SVenkatesh Srinivas uint16_t vector;
102511447b59SVenkatesh Srinivas
102611447b59SVenkatesh Srinivas dev = sc->vtpci_dev;
102711447b59SVenkatesh Srinivas
102811447b59SVenkatesh Srinivas if (offset != VIRTIO_MSI_CONFIG_VECTOR &&
102911447b59SVenkatesh Srinivas offset != VIRTIO_MSI_QUEUE_VECTOR)
103011447b59SVenkatesh Srinivas return (EINVAL);
103111447b59SVenkatesh Srinivas
103211447b59SVenkatesh Srinivas if (res_idx != -1) {
103311447b59SVenkatesh Srinivas /* Map from rid to host vector. */
1034c8247d06SImre Vadász vector = res_idx;
1035c8247d06SImre Vadász } else {
103611447b59SVenkatesh Srinivas vector = VIRTIO_MSI_NO_VECTOR;
1037c8247d06SImre Vadász }
103811447b59SVenkatesh Srinivas
103911447b59SVenkatesh Srinivas vtpci_write_config_2(sc, offset, vector);
104011447b59SVenkatesh Srinivas
104111447b59SVenkatesh Srinivas if (vtpci_read_config_2(sc, offset) != vector) {
104211447b59SVenkatesh Srinivas device_printf(dev, "insufficient host resources for "
104311447b59SVenkatesh Srinivas "MSIX interrupts\n");
104411447b59SVenkatesh Srinivas return (ENODEV);
104511447b59SVenkatesh Srinivas }
104611447b59SVenkatesh Srinivas
104711447b59SVenkatesh Srinivas return (0);
104811447b59SVenkatesh Srinivas }
104911447b59SVenkatesh Srinivas
105011447b59SVenkatesh Srinivas static void
vtpci_free_interrupts(struct vtpci_softc * sc)105111447b59SVenkatesh Srinivas vtpci_free_interrupts(struct vtpci_softc *sc)
105211447b59SVenkatesh Srinivas {
1053c8247d06SImre Vadász device_t dev = sc->vtpci_dev;
105411447b59SVenkatesh Srinivas struct vtpci_intr_resource *ires;
105511447b59SVenkatesh Srinivas int i;
105611447b59SVenkatesh Srinivas
1057c8247d06SImre Vadász for (i = 0; i < sc->vtpci_nintr_res; i++) {
105811447b59SVenkatesh Srinivas ires = &sc->vtpci_intr_res[i];
105911447b59SVenkatesh Srinivas
106011447b59SVenkatesh Srinivas if (ires->intrhand != NULL) {
106111447b59SVenkatesh Srinivas bus_teardown_intr(dev, ires->irq, ires->intrhand);
106211447b59SVenkatesh Srinivas ires->intrhand = NULL;
106311447b59SVenkatesh Srinivas }
106411447b59SVenkatesh Srinivas if (ires->irq != NULL) {
106511447b59SVenkatesh Srinivas bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
106611447b59SVenkatesh Srinivas ires->irq);
106711447b59SVenkatesh Srinivas ires->irq = NULL;
106811447b59SVenkatesh Srinivas }
10692f2405bbSImre Vadász }
10702f2405bbSImre Vadász
10712f2405bbSImre Vadász vtpci_unbind_intr(sc->vtpci_dev, -1);
10722f2405bbSImre Vadász for (i = 0; i < sc->vtpci_nvqs; i++)
10732f2405bbSImre Vadász vtpci_unbind_intr(sc->vtpci_dev, i);
10742f2405bbSImre Vadász
10752f2405bbSImre Vadász for (i = 0; i < sc->vtpci_nintr_res; i++) {
10762f2405bbSImre Vadász ires = &sc->vtpci_intr_res[i];
10772f2405bbSImre Vadász
1078c8247d06SImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX)
1079c8247d06SImre Vadász pci_release_msix_vector(dev, ires->rid);
1080c8247d06SImre Vadász ires->rid = 0;
108111447b59SVenkatesh Srinivas }
1082c8247d06SImre Vadász sc->vtpci_nintr_res = 0;
1083c8247d06SImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) {
1084c8247d06SImre Vadász pci_release_msi(dev);
1085c8247d06SImre Vadász sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSI;
1086c8247d06SImre Vadász }
1087c8247d06SImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
1088c8247d06SImre Vadász pci_disable_msix(dev);
1089c8247d06SImre Vadász pci_teardown_msix(dev);
10902f2405bbSImre Vadász sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSIX;
1091c8247d06SImre Vadász }
1092c8247d06SImre Vadász
109311447b59SVenkatesh Srinivas }
109411447b59SVenkatesh Srinivas
109511447b59SVenkatesh Srinivas static void
vtpci_free_virtqueues(struct vtpci_softc * sc)109611447b59SVenkatesh Srinivas vtpci_free_virtqueues(struct vtpci_softc *sc)
109711447b59SVenkatesh Srinivas {
109811447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx;
109911447b59SVenkatesh Srinivas int i;
110011447b59SVenkatesh Srinivas
110111447b59SVenkatesh Srinivas sc->vtpci_nvqs = 0;
110211447b59SVenkatesh Srinivas
110311447b59SVenkatesh Srinivas for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) {
110411447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[i];
110511447b59SVenkatesh Srinivas
110611447b59SVenkatesh Srinivas if (vqx->vq != NULL) {
110711447b59SVenkatesh Srinivas virtqueue_free(vqx->vq);
110811447b59SVenkatesh Srinivas vqx->vq = NULL;
110911447b59SVenkatesh Srinivas }
111011447b59SVenkatesh Srinivas }
111111447b59SVenkatesh Srinivas }
111211447b59SVenkatesh Srinivas
111311447b59SVenkatesh Srinivas static void
vtpci_release_child_resources(struct vtpci_softc * sc)111411447b59SVenkatesh Srinivas vtpci_release_child_resources(struct vtpci_softc *sc)
111511447b59SVenkatesh Srinivas {
111611447b59SVenkatesh Srinivas vtpci_free_interrupts(sc);
111711447b59SVenkatesh Srinivas vtpci_free_virtqueues(sc);
111811447b59SVenkatesh Srinivas }
111911447b59SVenkatesh Srinivas
112011447b59SVenkatesh Srinivas static void
vtpci_reset(struct vtpci_softc * sc)112111447b59SVenkatesh Srinivas vtpci_reset(struct vtpci_softc *sc)
112211447b59SVenkatesh Srinivas {
112311447b59SVenkatesh Srinivas
112411447b59SVenkatesh Srinivas /*
112511447b59SVenkatesh Srinivas * Setting the status to RESET sets the host device to
112611447b59SVenkatesh Srinivas * the original, uninitialized state.
112711447b59SVenkatesh Srinivas */
112811447b59SVenkatesh Srinivas vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET);
112911447b59SVenkatesh Srinivas }
113011447b59SVenkatesh Srinivas
1131df2df2fcSImre Vadász static void
vtpci_legacy_intr(void * arg)11322f2405bbSImre Vadász vtpci_legacy_intr(void *arg)
113311447b59SVenkatesh Srinivas {
11342f2405bbSImre Vadász struct vtpci_intr_resource *ires;
113511447b59SVenkatesh Srinivas struct vtpci_softc *sc;
11362f2405bbSImre Vadász struct vqentry *e;
113711447b59SVenkatesh Srinivas uint8_t isr;
113811447b59SVenkatesh Srinivas
11392f2405bbSImre Vadász ires = arg;
11402f2405bbSImre Vadász sc = ires->ires_sc;
114111447b59SVenkatesh Srinivas
114211447b59SVenkatesh Srinivas /* Reading the ISR also clears it. */
114311447b59SVenkatesh Srinivas isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR);
114411447b59SVenkatesh Srinivas
11452f2405bbSImre Vadász TAILQ_FOREACH(e, &ires->ls, entries) {
1146f852cf82SImre Vadász /*
1147f852cf82SImre Vadász * The lwkt_serialize_handler_call API doesn't seem to fit
1148f852cf82SImre Vadász * properly here. Instead move the virtqueue pending check
1149f852cf82SImre Vadász * into the driver, who can then properly implement masking
1150f852cf82SImre Vadász * of the handler itself.
1151f852cf82SImre Vadász */
11522f2405bbSImre Vadász if (e->what == -1) {
115311447b59SVenkatesh Srinivas if (isr & VIRTIO_PCI_ISR_CONFIG)
1154b817dce2SImre Vadász e->handler(e->arg);
1155f852cf82SImre Vadász } else if (isr & VIRTIO_PCI_ISR_INTR) {
11569d96478cSImre Vadász e->handler(e->arg);
11572f2405bbSImre Vadász }
1158c8247d06SImre Vadász }
115911447b59SVenkatesh Srinivas }
116011447b59SVenkatesh Srinivas
1161df2df2fcSImre Vadász static void
vtpci_msix_intr(void * arg)11622f2405bbSImre Vadász vtpci_msix_intr(void *arg)
116311447b59SVenkatesh Srinivas {
11642f2405bbSImre Vadász struct vtpci_intr_resource *ires;
116511447b59SVenkatesh Srinivas struct vtpci_softc *sc;
11662f2405bbSImre Vadász struct vqentry *e;
116711447b59SVenkatesh Srinivas
11682f2405bbSImre Vadász ires = arg;
11692f2405bbSImre Vadász sc = ires->ires_sc;
11702f2405bbSImre Vadász TAILQ_FOREACH(e, &ires->ls, entries) {
1171f852cf82SImre Vadász /*
1172f852cf82SImre Vadász * The lwkt_serialize_handler_call API doesn't seem to fit
1173f852cf82SImre Vadász * properly here. Instead move the virtqueue pending check
1174f852cf82SImre Vadász * into the driver, who can then properly implement masking
1175f852cf82SImre Vadász * of the handler itself.
1176f852cf82SImre Vadász */
1177b817dce2SImre Vadász e->handler(e->arg);
11782f2405bbSImre Vadász }
117911447b59SVenkatesh Srinivas }
1180