xref: /freebsd/sys/dev/vmware/vmci/vmci.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
163a93856SMark Peek /*-
23eeb7511SMark Peek  * Copyright (c) 2018 VMware, Inc.
363a93856SMark Peek  *
48c302b2eSMark Peek  * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
563a93856SMark Peek  */
663a93856SMark Peek 
763a93856SMark Peek /* Driver for VMware Virtual Machine Communication Interface (VMCI) device. */
863a93856SMark Peek 
9e2e050c8SConrad Meyer #include <sys/param.h>
1063a93856SMark Peek #include <sys/bus.h>
1163a93856SMark Peek #include <sys/kernel.h>
1263a93856SMark Peek #include <sys/malloc.h>
1363a93856SMark Peek #include <sys/module.h>
1463a93856SMark Peek #include <sys/rman.h>
1563a93856SMark Peek #include <sys/systm.h>
1663a93856SMark Peek 
1763a93856SMark Peek #include <dev/pci/pcireg.h>
1863a93856SMark Peek #include <dev/pci/pcivar.h>
1963a93856SMark Peek 
2063a93856SMark Peek #include <machine/bus.h>
2163a93856SMark Peek 
2263a93856SMark Peek #include "vmci.h"
2363a93856SMark Peek #include "vmci_doorbell.h"
2463a93856SMark Peek #include "vmci_driver.h"
2563a93856SMark Peek #include "vmci_kernel_defs.h"
2663a93856SMark Peek #include "vmci_queue_pair.h"
2763a93856SMark Peek 
2863a93856SMark Peek static int	vmci_probe(device_t);
2963a93856SMark Peek static int	vmci_attach(device_t);
3063a93856SMark Peek static int	vmci_detach(device_t);
3163a93856SMark Peek static int	vmci_shutdown(device_t);
3263a93856SMark Peek 
3363a93856SMark Peek static int	vmci_map_bars(struct vmci_softc *);
3463a93856SMark Peek static void	vmci_unmap_bars(struct vmci_softc *);
3563a93856SMark Peek 
3663a93856SMark Peek static int	vmci_config_capabilities(struct vmci_softc *);
3763a93856SMark Peek 
3863a93856SMark Peek static int	vmci_dma_malloc_int(struct vmci_softc *, bus_size_t,
3963a93856SMark Peek 		    bus_size_t, struct vmci_dma_alloc *);
4063a93856SMark Peek static void	vmci_dma_free_int(struct vmci_softc *,
4163a93856SMark Peek 		    struct vmci_dma_alloc *);
4263a93856SMark Peek 
4363a93856SMark Peek static int	vmci_config_interrupts(struct vmci_softc *);
4463a93856SMark Peek static int	vmci_config_interrupt(struct vmci_softc *);
4563a93856SMark Peek static int	vmci_check_intr_cnt(struct vmci_softc *);
4663a93856SMark Peek static int	vmci_allocate_interrupt_resources(struct vmci_softc *);
4763a93856SMark Peek static int	vmci_setup_interrupts(struct vmci_softc *);
4863a93856SMark Peek static void	vmci_dismantle_interrupts(struct vmci_softc *);
4963a93856SMark Peek static void	vmci_interrupt(void *);
5063a93856SMark Peek static void	vmci_interrupt_bm(void *);
5163a93856SMark Peek static void	dispatch_datagrams(void *, int);
5263a93856SMark Peek static void	process_bitmap(void *, int);
5363a93856SMark Peek 
5463a93856SMark Peek static void	vmci_delayed_work_fn_cb(void *context, int data);
5563a93856SMark Peek 
5663a93856SMark Peek static device_method_t vmci_methods[] = {
5763a93856SMark Peek 	/* Device interface. */
5863a93856SMark Peek 	DEVMETHOD(device_probe,		vmci_probe),
5963a93856SMark Peek 	DEVMETHOD(device_attach,	vmci_attach),
6063a93856SMark Peek 	DEVMETHOD(device_detach,	vmci_detach),
6163a93856SMark Peek 	DEVMETHOD(device_shutdown,	vmci_shutdown),
6263a93856SMark Peek 
6363a93856SMark Peek 	DEVMETHOD_END
6463a93856SMark Peek };
6563a93856SMark Peek 
6663a93856SMark Peek static driver_t vmci_driver = {
6763a93856SMark Peek 	"vmci", vmci_methods, sizeof(struct vmci_softc)
6863a93856SMark Peek };
6963a93856SMark Peek 
70*43df074dSJohn Baldwin DRIVER_MODULE(vmci, pci, vmci_driver, 0, 0);
7163a93856SMark Peek MODULE_VERSION(vmci, VMCI_VERSION);
72639eac20SHiroki Sato const struct {
73639eac20SHiroki Sato 	uint16_t vendor;
74639eac20SHiroki Sato 	uint16_t device;
75639eac20SHiroki Sato 	const char *desc;
76639eac20SHiroki Sato } vmci_ids[] = {
77639eac20SHiroki Sato 	{ VMCI_VMWARE_VENDOR_ID, VMCI_VMWARE_DEVICE_ID,
78639eac20SHiroki Sato 	    "VMware Virtual Machine Communication Interface" },
79639eac20SHiroki Sato };
80639eac20SHiroki Sato MODULE_PNP_INFO("U16:vendor;U16:device;D:#", pci, vmci, vmci_ids,
81639eac20SHiroki Sato     nitems(vmci_ids));
8263a93856SMark Peek 
8363a93856SMark Peek MODULE_DEPEND(vmci, pci, 1, 1, 1);
8463a93856SMark Peek 
8563a93856SMark Peek static struct vmci_softc *vmci_sc;
8663a93856SMark Peek 
8763a93856SMark Peek #define LGPFX	"vmci: "
8863a93856SMark Peek /*
8963a93856SMark Peek  * Allocate a buffer for incoming datagrams globally to avoid repeated
9063a93856SMark Peek  * allocation in the interrupt handler's atomic context.
9163a93856SMark Peek  */
9263a93856SMark Peek static uint8_t *data_buffer = NULL;
9363a93856SMark Peek static uint32_t data_buffer_size = VMCI_MAX_DG_SIZE;
9463a93856SMark Peek 
9563a93856SMark Peek struct vmci_delayed_work_info {
9663a93856SMark Peek 	vmci_work_fn	*work_fn;
9763a93856SMark Peek 	void		*data;
9863a93856SMark Peek 	vmci_list_item(vmci_delayed_work_info) entry;
9963a93856SMark Peek };
10063a93856SMark Peek 
10163a93856SMark Peek /*
10263a93856SMark Peek  *------------------------------------------------------------------------------
10363a93856SMark Peek  *
10463a93856SMark Peek  * vmci_probe --
10563a93856SMark Peek  *
10663a93856SMark Peek  *     Probe to see if the VMCI device is present.
10763a93856SMark Peek  *
10863a93856SMark Peek  * Results:
10963a93856SMark Peek  *     BUS_PROBE_DEFAULT if device exists, ENXIO otherwise.
11063a93856SMark Peek  *
11163a93856SMark Peek  * Side effects:
11263a93856SMark Peek  *     None.
11363a93856SMark Peek  *
11463a93856SMark Peek  *------------------------------------------------------------------------------
11563a93856SMark Peek  */
11663a93856SMark Peek 
11763a93856SMark Peek static int
vmci_probe(device_t dev)11863a93856SMark Peek vmci_probe(device_t dev)
11963a93856SMark Peek {
12063a93856SMark Peek 
121639eac20SHiroki Sato 	if (pci_get_vendor(dev) == vmci_ids[0].vendor &&
122639eac20SHiroki Sato 	    pci_get_device(dev) == vmci_ids[0].device) {
123639eac20SHiroki Sato 		device_set_desc(dev, vmci_ids[0].desc);
12463a93856SMark Peek 
12563a93856SMark Peek 		return (BUS_PROBE_DEFAULT);
12663a93856SMark Peek 	}
12763a93856SMark Peek 
12863a93856SMark Peek 	return (ENXIO);
12963a93856SMark Peek }
13063a93856SMark Peek 
13163a93856SMark Peek /*
13263a93856SMark Peek  *------------------------------------------------------------------------------
13363a93856SMark Peek  *
13463a93856SMark Peek  * vmci_attach --
13563a93856SMark Peek  *
13663a93856SMark Peek  *     Attach VMCI device to the system after vmci_probe() has been called and
13763a93856SMark Peek  *     the device has been detected.
13863a93856SMark Peek  *
13963a93856SMark Peek  * Results:
14063a93856SMark Peek  *     0 if success, ENXIO otherwise.
14163a93856SMark Peek  *
14263a93856SMark Peek  * Side effects:
14363a93856SMark Peek  *     None.
14463a93856SMark Peek  *
14563a93856SMark Peek  *------------------------------------------------------------------------------
14663a93856SMark Peek  */
14763a93856SMark Peek 
14863a93856SMark Peek static int
vmci_attach(device_t dev)14963a93856SMark Peek vmci_attach(device_t dev)
15063a93856SMark Peek {
15163a93856SMark Peek 	struct vmci_softc *sc;
15263a93856SMark Peek 	int error, i;
15363a93856SMark Peek 
15463a93856SMark Peek 	sc = device_get_softc(dev);
15563a93856SMark Peek 	sc->vmci_dev = dev;
15663a93856SMark Peek 	vmci_sc = sc;
15763a93856SMark Peek 
15863a93856SMark Peek 	data_buffer = NULL;
15963a93856SMark Peek 	sc->vmci_num_intr = 0;
16063a93856SMark Peek 	for (i = 0; i < VMCI_MAX_INTRS; i++) {
16163a93856SMark Peek 		sc->vmci_intrs[i].vmci_irq = NULL;
16263a93856SMark Peek 		sc->vmci_intrs[i].vmci_handler = NULL;
16363a93856SMark Peek 	}
16463a93856SMark Peek 
16563a93856SMark Peek 	TASK_INIT(&sc->vmci_interrupt_dq_task, 0, dispatch_datagrams, sc);
16663a93856SMark Peek 	TASK_INIT(&sc->vmci_interrupt_bm_task, 0, process_bitmap, sc);
16763a93856SMark Peek 
16863a93856SMark Peek 	TASK_INIT(&sc->vmci_delayed_work_task, 0, vmci_delayed_work_fn_cb, sc);
16963a93856SMark Peek 
17063a93856SMark Peek 	pci_enable_busmaster(dev);
17163a93856SMark Peek 
17263a93856SMark Peek 	mtx_init(&sc->vmci_spinlock, "VMCI Spinlock", NULL, MTX_SPIN);
17363a93856SMark Peek 	mtx_init(&sc->vmci_delayed_work_lock, "VMCI Delayed Work Lock",
17463a93856SMark Peek 	    NULL, MTX_DEF);
17563a93856SMark Peek 
17663a93856SMark Peek 	error = vmci_map_bars(sc);
17763a93856SMark Peek 	if (error) {
17863a93856SMark Peek 		VMCI_LOG_ERROR(LGPFX"Failed to map PCI BARs.\n");
17963a93856SMark Peek 		goto fail;
18063a93856SMark Peek 	}
18163a93856SMark Peek 
18263a93856SMark Peek 	error = vmci_config_capabilities(sc);
18363a93856SMark Peek 	if (error) {
18463a93856SMark Peek 		VMCI_LOG_ERROR(LGPFX"Failed to configure capabilities.\n");
18563a93856SMark Peek 		goto fail;
18663a93856SMark Peek 	}
18763a93856SMark Peek 
18863a93856SMark Peek 	vmci_list_init(&sc->vmci_delayed_work_infos);
18963a93856SMark Peek 
19063a93856SMark Peek 	vmci_components_init();
19163a93856SMark Peek 	vmci_util_init();
19263a93856SMark Peek 	error = vmci_qp_guest_endpoints_init();
19363a93856SMark Peek 	if (error) {
19463a93856SMark Peek 		VMCI_LOG_ERROR(LGPFX"vmci_qp_guest_endpoints_init failed.\n");
19563a93856SMark Peek 		goto fail;
19663a93856SMark Peek 	}
19763a93856SMark Peek 
19863a93856SMark Peek 	error = vmci_config_interrupts(sc);
19963a93856SMark Peek 	if (error)
20063a93856SMark Peek 		VMCI_LOG_ERROR(LGPFX"Failed to enable interrupts.\n");
20163a93856SMark Peek 
20263a93856SMark Peek fail:
20363a93856SMark Peek 	if (error) {
20463a93856SMark Peek 		vmci_detach(dev);
20563a93856SMark Peek 		return (ENXIO);
20663a93856SMark Peek 	}
20763a93856SMark Peek 
20863a93856SMark Peek 	return (0);
20963a93856SMark Peek }
21063a93856SMark Peek 
21163a93856SMark Peek /*
21263a93856SMark Peek  *------------------------------------------------------------------------------
21363a93856SMark Peek  *
21463a93856SMark Peek  * vmci_detach --
21563a93856SMark Peek  *
21663a93856SMark Peek  *     Detach the VMCI device.
21763a93856SMark Peek  *
21863a93856SMark Peek  * Results:
21963a93856SMark Peek  *     0
22063a93856SMark Peek  *
22163a93856SMark Peek  * Side effects:
22263a93856SMark Peek  *     None.
22363a93856SMark Peek  *
22463a93856SMark Peek  *------------------------------------------------------------------------------
22563a93856SMark Peek  */
22663a93856SMark Peek 
22763a93856SMark Peek static int
vmci_detach(device_t dev)22863a93856SMark Peek vmci_detach(device_t dev)
22963a93856SMark Peek {
23063a93856SMark Peek 	struct vmci_softc *sc;
23163a93856SMark Peek 
23263a93856SMark Peek 	sc = device_get_softc(dev);
23363a93856SMark Peek 
23463a93856SMark Peek 	vmci_qp_guest_endpoints_exit();
23563a93856SMark Peek 	vmci_util_exit();
23663a93856SMark Peek 
23763a93856SMark Peek 	vmci_dismantle_interrupts(sc);
23863a93856SMark Peek 
23963a93856SMark Peek 	vmci_components_cleanup();
24063a93856SMark Peek 
2410f14bcbeSMark Peek 	if mtx_initialized(&sc->vmci_spinlock) {
24263a93856SMark Peek 		taskqueue_drain(taskqueue_thread, &sc->vmci_delayed_work_task);
24363a93856SMark Peek 		mtx_destroy(&sc->vmci_delayed_work_lock);
2440f14bcbeSMark Peek 	}
24563a93856SMark Peek 
24663a93856SMark Peek 	if (sc->vmci_res0 != NULL)
24763a93856SMark Peek 		bus_space_write_4(sc->vmci_iot0, sc->vmci_ioh0,
24863a93856SMark Peek 		    VMCI_CONTROL_ADDR, VMCI_CONTROL_RESET);
24963a93856SMark Peek 
25063a93856SMark Peek 	if (sc->vmci_notifications_bitmap.dma_vaddr != NULL)
25163a93856SMark Peek 		vmci_dma_free(&sc->vmci_notifications_bitmap);
25263a93856SMark Peek 
25363a93856SMark Peek 	vmci_unmap_bars(sc);
25463a93856SMark Peek 
2550f14bcbeSMark Peek 	if mtx_initialized(&sc->vmci_spinlock)
25663a93856SMark Peek 		mtx_destroy(&sc->vmci_spinlock);
25763a93856SMark Peek 
25863a93856SMark Peek 	pci_disable_busmaster(dev);
25963a93856SMark Peek 
26063a93856SMark Peek 	return (0);
26163a93856SMark Peek }
26263a93856SMark Peek 
26363a93856SMark Peek /*
26463a93856SMark Peek  *------------------------------------------------------------------------------
26563a93856SMark Peek  *
26663a93856SMark Peek  * vmci_shutdown --
26763a93856SMark Peek  *
26863a93856SMark Peek  *     This function is called during system shutdown. We don't do anything.
26963a93856SMark Peek  *
27063a93856SMark Peek  * Results:
27163a93856SMark Peek  *     0
27263a93856SMark Peek  *
27363a93856SMark Peek  * Side effects:
27463a93856SMark Peek  *     None.
27563a93856SMark Peek  *
27663a93856SMark Peek  *------------------------------------------------------------------------------
27763a93856SMark Peek  */
27863a93856SMark Peek 
27963a93856SMark Peek static int
vmci_shutdown(device_t dev)28063a93856SMark Peek vmci_shutdown(device_t dev)
28163a93856SMark Peek {
28263a93856SMark Peek 
28363a93856SMark Peek 	return (0);
28463a93856SMark Peek }
28563a93856SMark Peek 
28663a93856SMark Peek /*
28763a93856SMark Peek  *------------------------------------------------------------------------------
28863a93856SMark Peek  *
28963a93856SMark Peek  * vmci_map_bars --
29063a93856SMark Peek  *
29163a93856SMark Peek  *     Maps the PCI I/O and MMIO BARs.
29263a93856SMark Peek  *
29363a93856SMark Peek  * Results:
29463a93856SMark Peek  *     0 on success, ENXIO otherwise.
29563a93856SMark Peek  *
29663a93856SMark Peek  * Side effects:
29763a93856SMark Peek  *     None.
29863a93856SMark Peek  *
29963a93856SMark Peek  *------------------------------------------------------------------------------
30063a93856SMark Peek  */
30163a93856SMark Peek 
30263a93856SMark Peek static int
vmci_map_bars(struct vmci_softc * sc)30363a93856SMark Peek vmci_map_bars(struct vmci_softc *sc)
30463a93856SMark Peek {
30563a93856SMark Peek 	int rid;
30663a93856SMark Peek 
30763a93856SMark Peek 	/* Map the PCI I/O BAR: BAR0 */
30863a93856SMark Peek 	rid = PCIR_BAR(0);
30963a93856SMark Peek 	sc->vmci_res0 = bus_alloc_resource_any(sc->vmci_dev, SYS_RES_IOPORT,
31063a93856SMark Peek 	    &rid, RF_ACTIVE);
31163a93856SMark Peek 	if (sc->vmci_res0 == NULL) {
31263a93856SMark Peek 		VMCI_LOG_ERROR(LGPFX"Could not map: BAR0\n");
31363a93856SMark Peek 		return (ENXIO);
31463a93856SMark Peek 	}
31563a93856SMark Peek 
31663a93856SMark Peek 	sc->vmci_iot0 = rman_get_bustag(sc->vmci_res0);
31763a93856SMark Peek 	sc->vmci_ioh0 = rman_get_bushandle(sc->vmci_res0);
31863a93856SMark Peek 	sc->vmci_ioaddr = rman_get_start(sc->vmci_res0);
31963a93856SMark Peek 
32063a93856SMark Peek 	/* Map the PCI MMIO BAR: BAR1 */
32163a93856SMark Peek 	rid = PCIR_BAR(1);
32263a93856SMark Peek 	sc->vmci_res1 = bus_alloc_resource_any(sc->vmci_dev, SYS_RES_MEMORY,
32363a93856SMark Peek 	    &rid, RF_ACTIVE);
32463a93856SMark Peek 	if (sc->vmci_res1 == NULL) {
32563a93856SMark Peek 		VMCI_LOG_ERROR(LGPFX"Could not map: BAR1\n");
32663a93856SMark Peek 		return (ENXIO);
32763a93856SMark Peek 	}
32863a93856SMark Peek 
32963a93856SMark Peek 	sc->vmci_iot1 = rman_get_bustag(sc->vmci_res1);
33063a93856SMark Peek 	sc->vmci_ioh1 = rman_get_bushandle(sc->vmci_res1);
33163a93856SMark Peek 
33263a93856SMark Peek 	return (0);
33363a93856SMark Peek }
33463a93856SMark Peek 
33563a93856SMark Peek /*
33663a93856SMark Peek  *------------------------------------------------------------------------------
33763a93856SMark Peek  *
33863a93856SMark Peek  * vmci_unmap_bars --
33963a93856SMark Peek  *
34063a93856SMark Peek  *     Unmaps the VMCI PCI I/O and MMIO BARs.
34163a93856SMark Peek  *
34263a93856SMark Peek  * Results:
34363a93856SMark Peek  *     None.
34463a93856SMark Peek  *
34563a93856SMark Peek  * Side effects:
34663a93856SMark Peek  *     None.
34763a93856SMark Peek  *
34863a93856SMark Peek  *------------------------------------------------------------------------------
34963a93856SMark Peek  */
35063a93856SMark Peek 
35163a93856SMark Peek static void
vmci_unmap_bars(struct vmci_softc * sc)35263a93856SMark Peek vmci_unmap_bars(struct vmci_softc *sc)
35363a93856SMark Peek {
35463a93856SMark Peek 	int rid;
35563a93856SMark Peek 
35663a93856SMark Peek 	if (sc->vmci_res0 != NULL) {
35763a93856SMark Peek 		rid = PCIR_BAR(0);
35863a93856SMark Peek 		bus_release_resource(sc->vmci_dev, SYS_RES_IOPORT, rid,
35963a93856SMark Peek 		    sc->vmci_res0);
36063a93856SMark Peek 		sc->vmci_res0 = NULL;
36163a93856SMark Peek 	}
36263a93856SMark Peek 
36363a93856SMark Peek 	if (sc->vmci_res1 != NULL) {
36463a93856SMark Peek 		rid = PCIR_BAR(1);
36563a93856SMark Peek 		bus_release_resource(sc->vmci_dev, SYS_RES_MEMORY, rid,
36663a93856SMark Peek 		    sc->vmci_res1);
36763a93856SMark Peek 		sc->vmci_res1 = NULL;
36863a93856SMark Peek 	}
36963a93856SMark Peek }
37063a93856SMark Peek 
37163a93856SMark Peek /*
37263a93856SMark Peek  *------------------------------------------------------------------------------
37363a93856SMark Peek  *
37463a93856SMark Peek  * vmci_config_capabilities --
37563a93856SMark Peek  *
37663a93856SMark Peek  *     Check the VMCI device capabilities and configure the device accordingly.
37763a93856SMark Peek  *
37863a93856SMark Peek  * Results:
37963a93856SMark Peek  *     0 if success, ENODEV otherwise.
38063a93856SMark Peek  *
38163a93856SMark Peek  * Side effects:
38263a93856SMark Peek  *     Device capabilities are enabled.
38363a93856SMark Peek  *
38463a93856SMark Peek  *------------------------------------------------------------------------------
38563a93856SMark Peek  */
38663a93856SMark Peek 
38763a93856SMark Peek static int
vmci_config_capabilities(struct vmci_softc * sc)38863a93856SMark Peek vmci_config_capabilities(struct vmci_softc *sc)
38963a93856SMark Peek {
39063a93856SMark Peek 	unsigned long bitmap_PPN;
39163a93856SMark Peek 	int error;
39263a93856SMark Peek 
39363a93856SMark Peek 	/*
39463a93856SMark Peek 	 * Verify that the VMCI device supports the capabilities that we
39563a93856SMark Peek 	 * need. Datagrams are necessary and notifications will be used
39663a93856SMark Peek 	 * if the device supports it.
39763a93856SMark Peek 	 */
39863a93856SMark Peek 	sc->capabilities = bus_space_read_4(sc->vmci_iot0, sc->vmci_ioh0,
39963a93856SMark Peek 	    VMCI_CAPS_ADDR);
40063a93856SMark Peek 
40163a93856SMark Peek 	if ((sc->capabilities & VMCI_CAPS_DATAGRAM) == 0) {
40263a93856SMark Peek 		VMCI_LOG_ERROR(LGPFX"VMCI device does not support "
40363a93856SMark Peek 		    "datagrams.\n");
40463a93856SMark Peek 		return (ENODEV);
40563a93856SMark Peek 	}
40663a93856SMark Peek 
40763a93856SMark Peek 	if (sc->capabilities & VMCI_CAPS_NOTIFICATIONS) {
40863a93856SMark Peek 		sc->capabilities = VMCI_CAPS_DATAGRAM;
40963a93856SMark Peek 		error = vmci_dma_malloc(PAGE_SIZE, 1,
41063a93856SMark Peek 		    &sc->vmci_notifications_bitmap);
41163a93856SMark Peek 		if (error)
41263a93856SMark Peek 			VMCI_LOG_ERROR(LGPFX"Failed to alloc memory for "
41363a93856SMark Peek 			    "notification bitmap.\n");
41463a93856SMark Peek 		else {
41563a93856SMark Peek 			memset(sc->vmci_notifications_bitmap.dma_vaddr, 0,
41663a93856SMark Peek 			    PAGE_SIZE);
41763a93856SMark Peek 			sc->capabilities |= VMCI_CAPS_NOTIFICATIONS;
41863a93856SMark Peek 		}
41963a93856SMark Peek 	} else
42063a93856SMark Peek 		sc->capabilities = VMCI_CAPS_DATAGRAM;
42163a93856SMark Peek 
42263a93856SMark Peek 	/* Let the host know which capabilities we intend to use. */
42363a93856SMark Peek 	bus_space_write_4(sc->vmci_iot0, sc->vmci_ioh0,
42463a93856SMark Peek 	    VMCI_CAPS_ADDR, sc->capabilities);
42563a93856SMark Peek 
42663a93856SMark Peek 	/*
42763a93856SMark Peek 	 * Register notification bitmap with device if that capability is
42863a93856SMark Peek 	 * used.
42963a93856SMark Peek 	 */
43063a93856SMark Peek 	if (sc->capabilities & VMCI_CAPS_NOTIFICATIONS) {
43163a93856SMark Peek 		bitmap_PPN =
43263a93856SMark Peek 		    sc->vmci_notifications_bitmap.dma_paddr >> PAGE_SHIFT;
43363a93856SMark Peek 		vmci_register_notification_bitmap(bitmap_PPN);
43463a93856SMark Peek 	}
43563a93856SMark Peek 
43663a93856SMark Peek 	/* Check host capabilities. */
43763a93856SMark Peek 	if (!vmci_check_host_capabilities())
43863a93856SMark Peek 		return (ENODEV);
43963a93856SMark Peek 
44063a93856SMark Peek 	return (0);
44163a93856SMark Peek }
44263a93856SMark Peek 
44363a93856SMark Peek /*
44463a93856SMark Peek  *------------------------------------------------------------------------------
44563a93856SMark Peek  *
44663a93856SMark Peek  * vmci_dmamap_cb --
44763a93856SMark Peek  *
44863a93856SMark Peek  *     Callback to receive mapping information resulting from the load of a
44963a93856SMark Peek  *     bus_dmamap_t via bus_dmamap_load()
45063a93856SMark Peek  *
45163a93856SMark Peek  * Results:
45263a93856SMark Peek  *     None.
45363a93856SMark Peek  *
45463a93856SMark Peek  * Side effects:
45563a93856SMark Peek  *     None.
45663a93856SMark Peek  *
45763a93856SMark Peek  *------------------------------------------------------------------------------
45863a93856SMark Peek  */
45963a93856SMark Peek 
46063a93856SMark Peek static void
vmci_dmamap_cb(void * arg,bus_dma_segment_t * segs,int nseg,int error)46163a93856SMark Peek vmci_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
46263a93856SMark Peek {
46363a93856SMark Peek 	bus_addr_t *baddr = arg;
46463a93856SMark Peek 
46563a93856SMark Peek 	if (error == 0)
46663a93856SMark Peek 		*baddr = segs->ds_addr;
46763a93856SMark Peek }
46863a93856SMark Peek 
46963a93856SMark Peek /*
47063a93856SMark Peek  *------------------------------------------------------------------------------
47163a93856SMark Peek  *
47263a93856SMark Peek  * vmci_dma_malloc_int --
47363a93856SMark Peek  *
47463a93856SMark Peek  *     Internal function that allocates DMA memory.
47563a93856SMark Peek  *
47663a93856SMark Peek  * Results:
47763a93856SMark Peek  *     0 if success.
47863a93856SMark Peek  *     ENOMEM if insufficient memory.
47963a93856SMark Peek  *     EINPROGRESS if mapping is deferred.
48063a93856SMark Peek  *     EINVAL if the request was invalid.
48163a93856SMark Peek  *
48263a93856SMark Peek  * Side effects:
48363a93856SMark Peek  *     DMA memory is allocated.
48463a93856SMark Peek  *
48563a93856SMark Peek  *------------------------------------------------------------------------------
48663a93856SMark Peek  */
48763a93856SMark Peek 
48863a93856SMark Peek static int
vmci_dma_malloc_int(struct vmci_softc * sc,bus_size_t size,bus_size_t align,struct vmci_dma_alloc * dma)48963a93856SMark Peek vmci_dma_malloc_int(struct vmci_softc *sc, bus_size_t size, bus_size_t align,
49063a93856SMark Peek     struct vmci_dma_alloc *dma)
49163a93856SMark Peek {
49263a93856SMark Peek 	int error;
49363a93856SMark Peek 
49463a93856SMark Peek 	bzero(dma, sizeof(struct vmci_dma_alloc));
49563a93856SMark Peek 
49663a93856SMark Peek 	error = bus_dma_tag_create(bus_get_dma_tag(vmci_sc->vmci_dev),
49763a93856SMark Peek 	    align, 0,		/* alignment, bounds */
49863a93856SMark Peek 	    BUS_SPACE_MAXADDR,	/* lowaddr */
49963a93856SMark Peek 	    BUS_SPACE_MAXADDR,	/* highaddr */
50063a93856SMark Peek 	    NULL, NULL,		/* filter, filterarg */
50163a93856SMark Peek 	    size,		/* maxsize */
50263a93856SMark Peek 	    1,			/* nsegments */
50363a93856SMark Peek 	    size,		/* maxsegsize */
50463a93856SMark Peek 	    BUS_DMA_ALLOCNOW,	/* flags */
50563a93856SMark Peek 	    NULL,		/* lockfunc */
50663a93856SMark Peek 	    NULL,		/* lockfuncarg */
50763a93856SMark Peek 	    &dma->dma_tag);
50863a93856SMark Peek 	if (error) {
50963a93856SMark Peek 		VMCI_LOG_ERROR(LGPFX"bus_dma_tag_create failed: %d\n", error);
51063a93856SMark Peek 		goto fail;
51163a93856SMark Peek 	}
51263a93856SMark Peek 
51363a93856SMark Peek 	error = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr,
51463a93856SMark Peek 	    BUS_DMA_ZERO | BUS_DMA_NOWAIT, &dma->dma_map);
51563a93856SMark Peek 	if (error) {
51663a93856SMark Peek 		VMCI_LOG_ERROR(LGPFX"bus_dmamem_alloc failed: %d\n", error);
51763a93856SMark Peek 		goto fail;
51863a93856SMark Peek 	}
51963a93856SMark Peek 
52063a93856SMark Peek 	error = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr,
52163a93856SMark Peek 	    size, vmci_dmamap_cb, &dma->dma_paddr, BUS_DMA_NOWAIT);
52263a93856SMark Peek 	if (error) {
52363a93856SMark Peek 		VMCI_LOG_ERROR(LGPFX"bus_dmamap_load failed: %d\n", error);
52463a93856SMark Peek 		goto fail;
52563a93856SMark Peek 	}
52663a93856SMark Peek 
52763a93856SMark Peek 	dma->dma_size = size;
52863a93856SMark Peek 
52963a93856SMark Peek fail:
53063a93856SMark Peek 	if (error)
53163a93856SMark Peek 		vmci_dma_free(dma);
53263a93856SMark Peek 
53363a93856SMark Peek 	return (error);
53463a93856SMark Peek }
53563a93856SMark Peek 
53663a93856SMark Peek /*
53763a93856SMark Peek  *------------------------------------------------------------------------------
53863a93856SMark Peek  *
53963a93856SMark Peek  * vmci_dma_malloc --
54063a93856SMark Peek  *
54163a93856SMark Peek  *     This function is a wrapper around vmci_dma_malloc_int for callers
54263a93856SMark Peek  *     outside of this module. Since we only support a single VMCI device, this
54363a93856SMark Peek  *     wrapper provides access to the device softc structure.
54463a93856SMark Peek  *
54563a93856SMark Peek  * Results:
54663a93856SMark Peek  *     0 if success.
54763a93856SMark Peek  *     ENOMEM if insufficient memory.
54863a93856SMark Peek  *     EINPROGRESS if mapping is deferred.
54963a93856SMark Peek  *     EINVAL if the request was invalid.
55063a93856SMark Peek  *
55163a93856SMark Peek  * Side effects:
55263a93856SMark Peek  *     DMA memory is allocated.
55363a93856SMark Peek  *
55463a93856SMark Peek  *------------------------------------------------------------------------------
55563a93856SMark Peek  */
55663a93856SMark Peek 
55763a93856SMark Peek int
vmci_dma_malloc(bus_size_t size,bus_size_t align,struct vmci_dma_alloc * dma)55863a93856SMark Peek vmci_dma_malloc(bus_size_t size, bus_size_t align, struct vmci_dma_alloc *dma)
55963a93856SMark Peek {
56063a93856SMark Peek 
56163a93856SMark Peek 	return (vmci_dma_malloc_int(vmci_sc, size, align, dma));
56263a93856SMark Peek }
56363a93856SMark Peek 
56463a93856SMark Peek /*
56563a93856SMark Peek  *------------------------------------------------------------------------------
56663a93856SMark Peek  *
56763a93856SMark Peek  * vmci_dma_free_int --
56863a93856SMark Peek  *
56963a93856SMark Peek  *     Internal function that frees DMA memory.
57063a93856SMark Peek  *
57163a93856SMark Peek  * Results:
57263a93856SMark Peek  *     None.
57363a93856SMark Peek  *
57463a93856SMark Peek  * Side effects:
57563a93856SMark Peek  *     Frees DMA memory.
57663a93856SMark Peek  *
57763a93856SMark Peek  *------------------------------------------------------------------------------
57863a93856SMark Peek  */
57963a93856SMark Peek 
58063a93856SMark Peek static void
vmci_dma_free_int(struct vmci_softc * sc,struct vmci_dma_alloc * dma)58163a93856SMark Peek vmci_dma_free_int(struct vmci_softc *sc, struct vmci_dma_alloc *dma)
58263a93856SMark Peek {
58363a93856SMark Peek 
58463a93856SMark Peek 	if (dma->dma_tag != NULL) {
58563a93856SMark Peek 		if (dma->dma_paddr != 0) {
58663a93856SMark Peek 			bus_dmamap_sync(dma->dma_tag, dma->dma_map,
58763a93856SMark Peek 			    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
58863a93856SMark Peek 			bus_dmamap_unload(dma->dma_tag, dma->dma_map);
58963a93856SMark Peek 		}
59063a93856SMark Peek 
59163a93856SMark Peek 		if (dma->dma_vaddr != NULL)
59263a93856SMark Peek 			bus_dmamem_free(dma->dma_tag, dma->dma_vaddr,
59363a93856SMark Peek 			    dma->dma_map);
59463a93856SMark Peek 
59563a93856SMark Peek 		bus_dma_tag_destroy(dma->dma_tag);
59663a93856SMark Peek 	}
59763a93856SMark Peek 	bzero(dma, sizeof(struct vmci_dma_alloc));
59863a93856SMark Peek }
59963a93856SMark Peek 
60063a93856SMark Peek /*
60163a93856SMark Peek  *------------------------------------------------------------------------------
60263a93856SMark Peek  *
60363a93856SMark Peek  * vmci_dma_free --
60463a93856SMark Peek  *
60563a93856SMark Peek  *     This function is a wrapper around vmci_dma_free_int for callers outside
60663a93856SMark Peek  *     of this module. Since we only support a single VMCI device, this wrapper
60763a93856SMark Peek  *     provides access to the device softc structure.
60863a93856SMark Peek  *
60963a93856SMark Peek  * Results:
61063a93856SMark Peek  *     None.
61163a93856SMark Peek  *
61263a93856SMark Peek  * Side effects:
61363a93856SMark Peek  *     Frees DMA memory.
61463a93856SMark Peek  *
61563a93856SMark Peek  *------------------------------------------------------------------------------
61663a93856SMark Peek  */
61763a93856SMark Peek 
61863a93856SMark Peek void
vmci_dma_free(struct vmci_dma_alloc * dma)61963a93856SMark Peek vmci_dma_free(struct vmci_dma_alloc *dma)
62063a93856SMark Peek {
62163a93856SMark Peek 
62263a93856SMark Peek 	vmci_dma_free_int(vmci_sc, dma);
62363a93856SMark Peek }
62463a93856SMark Peek 
62563a93856SMark Peek /*
62663a93856SMark Peek  *------------------------------------------------------------------------------
62763a93856SMark Peek  *
62863a93856SMark Peek  * vmci_config_interrupts --
62963a93856SMark Peek  *
63063a93856SMark Peek  *     Configures and enables interrupts. Try to configure MSI-X. If this fails,
63163a93856SMark Peek  *     try to configure MSI. If even this fails, try legacy interrupts.
63263a93856SMark Peek  *
63363a93856SMark Peek  * Results:
63463a93856SMark Peek  *     0 if success.
63563a93856SMark Peek  *     ENOMEM if insufficient memory.
63663a93856SMark Peek  *     ENODEV if the device doesn't support interrupts.
63763a93856SMark Peek  *     ENXIO if the device configuration failed.
63863a93856SMark Peek  *
63963a93856SMark Peek  * Side effects:
64063a93856SMark Peek  *     Interrupts get enabled if successful.
64163a93856SMark Peek  *
64263a93856SMark Peek  *------------------------------------------------------------------------------
64363a93856SMark Peek  */
64463a93856SMark Peek 
64563a93856SMark Peek static int
vmci_config_interrupts(struct vmci_softc * sc)64663a93856SMark Peek vmci_config_interrupts(struct vmci_softc *sc)
64763a93856SMark Peek {
64863a93856SMark Peek 	int error;
64963a93856SMark Peek 
65063a93856SMark Peek 	data_buffer = malloc(data_buffer_size, M_DEVBUF, M_ZERO | M_NOWAIT);
65163a93856SMark Peek 	if (data_buffer == NULL)
65263a93856SMark Peek 		return (ENOMEM);
65363a93856SMark Peek 
65463a93856SMark Peek 	sc->vmci_intr_type = VMCI_INTR_TYPE_MSIX;
65563a93856SMark Peek 	error = vmci_config_interrupt(sc);
65663a93856SMark Peek 	if (error) {
65763a93856SMark Peek 		sc->vmci_intr_type = VMCI_INTR_TYPE_MSI;
65863a93856SMark Peek 		error = vmci_config_interrupt(sc);
65963a93856SMark Peek 	}
66063a93856SMark Peek 	if (error) {
66163a93856SMark Peek 		sc->vmci_intr_type = VMCI_INTR_TYPE_INTX;
66263a93856SMark Peek 		error = vmci_config_interrupt(sc);
66363a93856SMark Peek 	}
66463a93856SMark Peek 	if (error)
66563a93856SMark Peek 		return (error);
66663a93856SMark Peek 
66763a93856SMark Peek 	/* Enable specific interrupt bits. */
66863a93856SMark Peek 	if (sc->capabilities & VMCI_CAPS_NOTIFICATIONS)
66963a93856SMark Peek 		bus_space_write_4(sc->vmci_iot0, sc->vmci_ioh0,
67063a93856SMark Peek 		    VMCI_IMR_ADDR, VMCI_IMR_DATAGRAM | VMCI_IMR_NOTIFICATION);
67163a93856SMark Peek 	else
67263a93856SMark Peek 		bus_space_write_4(sc->vmci_iot0, sc->vmci_ioh0,
67363a93856SMark Peek 		    VMCI_IMR_ADDR, VMCI_IMR_DATAGRAM);
67463a93856SMark Peek 
67563a93856SMark Peek 	/* Enable interrupts. */
67663a93856SMark Peek 	bus_space_write_4(sc->vmci_iot0, sc->vmci_ioh0,
67763a93856SMark Peek 	    VMCI_CONTROL_ADDR, VMCI_CONTROL_INT_ENABLE);
67863a93856SMark Peek 
67963a93856SMark Peek 	return (0);
68063a93856SMark Peek }
68163a93856SMark Peek 
68263a93856SMark Peek /*
68363a93856SMark Peek  *------------------------------------------------------------------------------
68463a93856SMark Peek  *
68563a93856SMark Peek  * vmci_config_interrupt --
68663a93856SMark Peek  *
68763a93856SMark Peek  *     Check the number of interrupts supported, allocate resources and setup
68863a93856SMark Peek  *     interrupts.
68963a93856SMark Peek  *
69063a93856SMark Peek  * Results:
69163a93856SMark Peek  *     0 if success.
69263a93856SMark Peek  *     ENOMEM if insufficient memory.
69363a93856SMark Peek  *     ENODEV if the device doesn't support interrupts.
69463a93856SMark Peek  *     ENXIO if the device configuration failed.
69563a93856SMark Peek  *
69663a93856SMark Peek  * Side effects:
69763a93856SMark Peek  *     Resources get allocated and interrupts get setup (but not enabled) if
69863a93856SMark Peek  *     successful.
69963a93856SMark Peek  *
70063a93856SMark Peek  *------------------------------------------------------------------------------
70163a93856SMark Peek  */
70263a93856SMark Peek 
70363a93856SMark Peek static int
vmci_config_interrupt(struct vmci_softc * sc)70463a93856SMark Peek vmci_config_interrupt(struct vmci_softc *sc)
70563a93856SMark Peek {
70663a93856SMark Peek 	int error;
70763a93856SMark Peek 
70863a93856SMark Peek 	error = vmci_check_intr_cnt(sc);
70963a93856SMark Peek 	if (error)
71063a93856SMark Peek 		return (error);
71163a93856SMark Peek 
71263a93856SMark Peek 	error = vmci_allocate_interrupt_resources(sc);
71363a93856SMark Peek 	if (error)
71463a93856SMark Peek 		return (error);
71563a93856SMark Peek 
71663a93856SMark Peek 	error = vmci_setup_interrupts(sc);
71763a93856SMark Peek 	if (error)
71863a93856SMark Peek 		return (error);
71963a93856SMark Peek 
72063a93856SMark Peek 	return (0);
72163a93856SMark Peek }
72263a93856SMark Peek 
72363a93856SMark Peek /*
72463a93856SMark Peek  *------------------------------------------------------------------------------
72563a93856SMark Peek  *
72663a93856SMark Peek  * vmci_check_intr_cnt --
72763a93856SMark Peek  *
72863a93856SMark Peek  *     Check the number of interrupts supported by the device and ask PCI bus
72963a93856SMark Peek  *     to allocate appropriate number of interrupts.
73063a93856SMark Peek  *
73163a93856SMark Peek  * Results:
73263a93856SMark Peek  *     0 if success.
73363a93856SMark Peek  *     ENODEV if the device doesn't support any interrupts.
73463a93856SMark Peek  *     ENXIO if the device configuration failed.
73563a93856SMark Peek  *
73663a93856SMark Peek  * Side effects:
73763a93856SMark Peek  *     Resources get allocated on success.
73863a93856SMark Peek  *
73963a93856SMark Peek  *------------------------------------------------------------------------------
74063a93856SMark Peek  */
74163a93856SMark Peek 
74263a93856SMark Peek static int
vmci_check_intr_cnt(struct vmci_softc * sc)74363a93856SMark Peek vmci_check_intr_cnt(struct vmci_softc *sc)
74463a93856SMark Peek {
74563a93856SMark Peek 
74663a93856SMark Peek 	if (sc->vmci_intr_type == VMCI_INTR_TYPE_INTX) {
74763a93856SMark Peek 		sc->vmci_num_intr = 1;
74863a93856SMark Peek 		return (0);
74963a93856SMark Peek 	}
75063a93856SMark Peek 
75163a93856SMark Peek 	/*
75263a93856SMark Peek 	 * Make sure that the device supports the required number of MSI/MSI-X
75363a93856SMark Peek 	 * messages. We try for 2 MSI-X messages but 1 is good too. We need at
75463a93856SMark Peek 	 * least 1 MSI message.
75563a93856SMark Peek 	 */
75663a93856SMark Peek 	sc->vmci_num_intr = (sc->vmci_intr_type == VMCI_INTR_TYPE_MSIX) ?
75763a93856SMark Peek 	    pci_msix_count(sc->vmci_dev) : pci_msi_count(sc->vmci_dev);
75863a93856SMark Peek 
75963a93856SMark Peek 	if (!sc->vmci_num_intr) {
76063a93856SMark Peek 		VMCI_LOG_ERROR(LGPFX"Device does not support any interrupt"
76163a93856SMark Peek 		    " messages");
76263a93856SMark Peek 		return (ENODEV);
76363a93856SMark Peek 	}
76463a93856SMark Peek 
76563a93856SMark Peek 	sc->vmci_num_intr = (sc->vmci_intr_type == VMCI_INTR_TYPE_MSIX) ?
76663a93856SMark Peek 	    VMCI_MAX_INTRS : 1;
76763a93856SMark Peek 	if (sc->vmci_intr_type == VMCI_INTR_TYPE_MSIX) {
76863a93856SMark Peek 		if (pci_alloc_msix(sc->vmci_dev, &sc->vmci_num_intr))
76963a93856SMark Peek 			return (ENXIO);
77063a93856SMark Peek 	} else if (sc->vmci_intr_type == VMCI_INTR_TYPE_MSI) {
77163a93856SMark Peek 		if (pci_alloc_msi(sc->vmci_dev, &sc->vmci_num_intr))
77263a93856SMark Peek 			return (ENXIO);
77363a93856SMark Peek 	}
77463a93856SMark Peek 
77563a93856SMark Peek 	return (0);
77663a93856SMark Peek }
77763a93856SMark Peek 
77863a93856SMark Peek /*
77963a93856SMark Peek  *------------------------------------------------------------------------------
78063a93856SMark Peek  *
78163a93856SMark Peek  * vmci_allocate_interrupt_resources --
78263a93856SMark Peek  *
78363a93856SMark Peek  *     Allocate resources necessary for interrupts.
78463a93856SMark Peek  *
78563a93856SMark Peek  * Results:
78663a93856SMark Peek  *     0 if success, ENXIO otherwise.
78763a93856SMark Peek  *
78863a93856SMark Peek  * Side effects:
78963a93856SMark Peek  *     Resources get allocated on success.
79063a93856SMark Peek  *
79163a93856SMark Peek  *------------------------------------------------------------------------------
79263a93856SMark Peek  */
79363a93856SMark Peek 
79463a93856SMark Peek static int
vmci_allocate_interrupt_resources(struct vmci_softc * sc)79563a93856SMark Peek vmci_allocate_interrupt_resources(struct vmci_softc *sc)
79663a93856SMark Peek {
79763a93856SMark Peek 	struct resource *irq;
79863a93856SMark Peek 	int flags, i, rid;
79963a93856SMark Peek 
80063a93856SMark Peek 	flags = RF_ACTIVE;
80163a93856SMark Peek 	flags |= (sc->vmci_num_intr == 1) ? RF_SHAREABLE : 0;
80263a93856SMark Peek 	rid = (sc->vmci_intr_type == VMCI_INTR_TYPE_INTX) ? 0 : 1;
80363a93856SMark Peek 
80463a93856SMark Peek 	for (i = 0; i < sc->vmci_num_intr; i++, rid++) {
80563a93856SMark Peek 		irq = bus_alloc_resource_any(sc->vmci_dev, SYS_RES_IRQ, &rid,
80663a93856SMark Peek 		    flags);
80763a93856SMark Peek 		if (irq == NULL)
80863a93856SMark Peek 			return (ENXIO);
80963a93856SMark Peek 		sc->vmci_intrs[i].vmci_irq = irq;
81063a93856SMark Peek 		sc->vmci_intrs[i].vmci_rid = rid;
81163a93856SMark Peek 	}
81263a93856SMark Peek 
81363a93856SMark Peek 	return (0);
81463a93856SMark Peek }
81563a93856SMark Peek 
81663a93856SMark Peek /*
81763a93856SMark Peek  *------------------------------------------------------------------------------
81863a93856SMark Peek  *
81963a93856SMark Peek  * vmci_setup_interrupts --
82063a93856SMark Peek  *
82163a93856SMark Peek  *     Sets up the interrupts.
82263a93856SMark Peek  *
82363a93856SMark Peek  * Results:
82463a93856SMark Peek  *     0 if success, appropriate error code from bus_setup_intr otherwise.
82563a93856SMark Peek  *
82663a93856SMark Peek  * Side effects:
82763a93856SMark Peek  *     Interrupt handler gets attached.
82863a93856SMark Peek  *
82963a93856SMark Peek  *------------------------------------------------------------------------------
83063a93856SMark Peek  */
83163a93856SMark Peek 
83263a93856SMark Peek static int
vmci_setup_interrupts(struct vmci_softc * sc)83363a93856SMark Peek vmci_setup_interrupts(struct vmci_softc *sc)
83463a93856SMark Peek {
83563a93856SMark Peek 	struct vmci_interrupt *intr;
83663a93856SMark Peek 	int error, flags;
83763a93856SMark Peek 
83863a93856SMark Peek 	flags = INTR_TYPE_NET | INTR_MPSAFE;
83963a93856SMark Peek 	if (sc->vmci_num_intr > 1)
84063a93856SMark Peek 		flags |= INTR_EXCL;
84163a93856SMark Peek 
84263a93856SMark Peek 	intr = &sc->vmci_intrs[0];
84363a93856SMark Peek 	error = bus_setup_intr(sc->vmci_dev, intr->vmci_irq, flags, NULL,
84463a93856SMark Peek 	    vmci_interrupt, NULL, &intr->vmci_handler);
84563a93856SMark Peek 	if (error)
84663a93856SMark Peek 		return (error);
84763a93856SMark Peek 
84863a93856SMark Peek 	if (sc->vmci_num_intr == 2) {
849ab38c12bSAlexander Motin 		bus_describe_intr(sc->vmci_dev, intr->vmci_irq,
850ab38c12bSAlexander Motin 		    intr->vmci_handler, "dg");
85163a93856SMark Peek 		intr = &sc->vmci_intrs[1];
85263a93856SMark Peek 		error = bus_setup_intr(sc->vmci_dev, intr->vmci_irq, flags,
85363a93856SMark Peek 		    NULL, vmci_interrupt_bm, NULL, &intr->vmci_handler);
85463a93856SMark Peek 		if (error)
85563a93856SMark Peek 			return (error);
85663a93856SMark Peek 		bus_describe_intr(sc->vmci_dev, intr->vmci_irq,
857ab38c12bSAlexander Motin 		    intr->vmci_handler, "bm");
85863a93856SMark Peek 	}
85963a93856SMark Peek 
86063a93856SMark Peek 	return (0);
86163a93856SMark Peek }
86263a93856SMark Peek 
86363a93856SMark Peek /*
86463a93856SMark Peek  *------------------------------------------------------------------------------
86563a93856SMark Peek  *
86663a93856SMark Peek  * vmci_interrupt --
86763a93856SMark Peek  *
86863a93856SMark Peek  *     Interrupt handler for legacy or MSI interrupt, or for first MSI-X
86963a93856SMark Peek  *     interrupt (vector VMCI_INTR_DATAGRAM).
87063a93856SMark Peek  *
87163a93856SMark Peek  * Results:
87263a93856SMark Peek  *     None.
87363a93856SMark Peek  *
87463a93856SMark Peek  * Side effects:
87563a93856SMark Peek  *     None.
87663a93856SMark Peek  *
87763a93856SMark Peek  *------------------------------------------------------------------------------
87863a93856SMark Peek  */
87963a93856SMark Peek 
88063a93856SMark Peek static void
vmci_interrupt(void * arg)88163a93856SMark Peek vmci_interrupt(void *arg)
88263a93856SMark Peek {
88363a93856SMark Peek 
88463a93856SMark Peek 	if (vmci_sc->vmci_num_intr == 2)
88563a93856SMark Peek 		taskqueue_enqueue(taskqueue_swi,
88663a93856SMark Peek 		    &vmci_sc->vmci_interrupt_dq_task);
88763a93856SMark Peek 	else {
88863a93856SMark Peek 		unsigned int icr;
88963a93856SMark Peek 
89063a93856SMark Peek 		icr = inl(vmci_sc->vmci_ioaddr + VMCI_ICR_ADDR);
89163a93856SMark Peek 		if (icr == 0 || icr == 0xffffffff)
89263a93856SMark Peek 			return;
89363a93856SMark Peek 		if (icr & VMCI_ICR_DATAGRAM) {
89463a93856SMark Peek 			taskqueue_enqueue(taskqueue_swi,
89563a93856SMark Peek 			    &vmci_sc->vmci_interrupt_dq_task);
89663a93856SMark Peek 			icr &= ~VMCI_ICR_DATAGRAM;
89763a93856SMark Peek 		}
89863a93856SMark Peek 		if (icr & VMCI_ICR_NOTIFICATION) {
89963a93856SMark Peek 			taskqueue_enqueue(taskqueue_swi,
90063a93856SMark Peek 			    &vmci_sc->vmci_interrupt_bm_task);
90163a93856SMark Peek 			icr &= ~VMCI_ICR_NOTIFICATION;
90263a93856SMark Peek 		}
90363a93856SMark Peek 		if (icr != 0)
90463a93856SMark Peek 			VMCI_LOG_INFO(LGPFX"Ignoring unknown interrupt "
90563a93856SMark Peek 			    "cause");
90663a93856SMark Peek 	}
90763a93856SMark Peek }
90863a93856SMark Peek 
90963a93856SMark Peek /*
91063a93856SMark Peek  *------------------------------------------------------------------------------
91163a93856SMark Peek  *
91263a93856SMark Peek  * vmci_interrupt_bm --
91363a93856SMark Peek  *
91463a93856SMark Peek  *     Interrupt handler for MSI-X interrupt vector VMCI_INTR_NOTIFICATION,
91563a93856SMark Peek  *     which is for the notification bitmap. Will only get called if we are
91663a93856SMark Peek  *     using MSI-X with exclusive vectors.
91763a93856SMark Peek  *
91863a93856SMark Peek  * Results:
91963a93856SMark Peek  *     None.
92063a93856SMark Peek  *
92163a93856SMark Peek  * Side effects:
92263a93856SMark Peek  *     None.
92363a93856SMark Peek  *
92463a93856SMark Peek  *------------------------------------------------------------------------------
92563a93856SMark Peek  */
92663a93856SMark Peek 
92763a93856SMark Peek static void
vmci_interrupt_bm(void * arg)92863a93856SMark Peek vmci_interrupt_bm(void *arg)
92963a93856SMark Peek {
93063a93856SMark Peek 
93163a93856SMark Peek 	ASSERT(vmci_sc->vmci_num_intr == 2);
93263a93856SMark Peek 	taskqueue_enqueue(taskqueue_swi, &vmci_sc->vmci_interrupt_bm_task);
93363a93856SMark Peek }
93463a93856SMark Peek 
93563a93856SMark Peek /*
93663a93856SMark Peek  *------------------------------------------------------------------------------
93763a93856SMark Peek  *
93863a93856SMark Peek  * dispatch_datagrams --
93963a93856SMark Peek  *
94063a93856SMark Peek  *     Reads and dispatches incoming datagrams.
94163a93856SMark Peek  *
94263a93856SMark Peek  * Results:
94363a93856SMark Peek  *     None.
94463a93856SMark Peek  *
94563a93856SMark Peek  * Side effects:
94663a93856SMark Peek  *     Reads data from the device.
94763a93856SMark Peek  *
94863a93856SMark Peek  *------------------------------------------------------------------------------
94963a93856SMark Peek  */
95063a93856SMark Peek 
95163a93856SMark Peek static void
dispatch_datagrams(void * context,int data)95263a93856SMark Peek dispatch_datagrams(void *context, int data)
95363a93856SMark Peek {
95463a93856SMark Peek 
95563a93856SMark Peek 	if (data_buffer == NULL)
95663a93856SMark Peek 		VMCI_LOG_INFO(LGPFX"dispatch_datagrams(): no buffer "
95763a93856SMark Peek 		    "present");
95863a93856SMark Peek 
95963a93856SMark Peek 	vmci_read_datagrams_from_port((vmci_io_handle) 0,
96063a93856SMark Peek 	    vmci_sc->vmci_ioaddr + VMCI_DATA_IN_ADDR,
96163a93856SMark Peek 	    data_buffer, data_buffer_size);
96263a93856SMark Peek }
96363a93856SMark Peek 
96463a93856SMark Peek /*
96563a93856SMark Peek  *------------------------------------------------------------------------------
96663a93856SMark Peek  *
96763a93856SMark Peek  * process_bitmap --
96863a93856SMark Peek  *
96963a93856SMark Peek  *     Scans the notification bitmap for raised flags, clears them and handles
97063a93856SMark Peek  *     the notifications.
97163a93856SMark Peek  *
97263a93856SMark Peek  * Results:
97363a93856SMark Peek  *     None.
97463a93856SMark Peek  *
97563a93856SMark Peek  * Side effects:
97663a93856SMark Peek  *     None.
97763a93856SMark Peek  *
97863a93856SMark Peek  *------------------------------------------------------------------------------
97963a93856SMark Peek  */
98063a93856SMark Peek 
98163a93856SMark Peek static void
process_bitmap(void * context,int data)98263a93856SMark Peek process_bitmap(void *context, int data)
98363a93856SMark Peek {
98463a93856SMark Peek 
98563a93856SMark Peek 	if (vmci_sc->vmci_notifications_bitmap.dma_vaddr == NULL)
98663a93856SMark Peek 		VMCI_LOG_INFO(LGPFX"process_bitmaps(): no bitmap present");
98763a93856SMark Peek 
98863a93856SMark Peek 	vmci_scan_notification_bitmap(
98963a93856SMark Peek 	    vmci_sc->vmci_notifications_bitmap.dma_vaddr);
99063a93856SMark Peek }
99163a93856SMark Peek 
99263a93856SMark Peek /*
99363a93856SMark Peek  *------------------------------------------------------------------------------
99463a93856SMark Peek  *
99563a93856SMark Peek  * vmci_dismantle_interrupts --
99663a93856SMark Peek  *
99763a93856SMark Peek  *     Releases resources, detaches the interrupt handler and drains the task
99863a93856SMark Peek  *     queue.
99963a93856SMark Peek  *
100063a93856SMark Peek  * Results:
100163a93856SMark Peek  *     None.
100263a93856SMark Peek  *
100363a93856SMark Peek  * Side effects:
100463a93856SMark Peek  *     No more interrupts.
100563a93856SMark Peek  *
100663a93856SMark Peek  *------------------------------------------------------------------------------
100763a93856SMark Peek  */
100863a93856SMark Peek 
100963a93856SMark Peek static void
vmci_dismantle_interrupts(struct vmci_softc * sc)101063a93856SMark Peek vmci_dismantle_interrupts(struct vmci_softc *sc)
101163a93856SMark Peek {
101263a93856SMark Peek 	struct vmci_interrupt *intr;
101363a93856SMark Peek 	int i;
101463a93856SMark Peek 
101563a93856SMark Peek 	for (i = 0; i < sc->vmci_num_intr; i++) {
101663a93856SMark Peek 		intr = &sc->vmci_intrs[i];
101763a93856SMark Peek 		if (intr->vmci_handler != NULL) {
101863a93856SMark Peek 			bus_teardown_intr(sc->vmci_dev, intr->vmci_irq,
101963a93856SMark Peek 			    intr->vmci_handler);
102063a93856SMark Peek 			intr->vmci_handler = NULL;
102163a93856SMark Peek 		}
102263a93856SMark Peek 		if (intr->vmci_irq != NULL) {
102363a93856SMark Peek 			bus_release_resource(sc->vmci_dev, SYS_RES_IRQ,
102463a93856SMark Peek 			    intr->vmci_rid, intr->vmci_irq);
102563a93856SMark Peek 			intr->vmci_irq = NULL;
102663a93856SMark Peek 			intr->vmci_rid = -1;
102763a93856SMark Peek 		}
102863a93856SMark Peek 	}
102963a93856SMark Peek 
103063a93856SMark Peek 	if ((sc->vmci_intr_type != VMCI_INTR_TYPE_INTX) &&
103163a93856SMark Peek 	    (sc->vmci_num_intr))
103263a93856SMark Peek 		pci_release_msi(sc->vmci_dev);
103363a93856SMark Peek 
103463a93856SMark Peek 	taskqueue_drain(taskqueue_swi, &sc->vmci_interrupt_dq_task);
103563a93856SMark Peek 	taskqueue_drain(taskqueue_swi, &sc->vmci_interrupt_bm_task);
103663a93856SMark Peek 
103763a93856SMark Peek 	if (data_buffer != NULL)
103863a93856SMark Peek 		free(data_buffer, M_DEVBUF);
103963a93856SMark Peek }
104063a93856SMark Peek 
104163a93856SMark Peek /*
104263a93856SMark Peek  *------------------------------------------------------------------------------
104363a93856SMark Peek  *
104463a93856SMark Peek  * vmci_delayed_work_fn_cb --
104563a93856SMark Peek  *
104663a93856SMark Peek  *     Callback function that executes the queued up delayed work functions.
104763a93856SMark Peek  *
104863a93856SMark Peek  * Results:
104963a93856SMark Peek  *     None.
105063a93856SMark Peek  *
105163a93856SMark Peek  * Side effects:
105263a93856SMark Peek  *     None.
105363a93856SMark Peek  *
105463a93856SMark Peek  *------------------------------------------------------------------------------
105563a93856SMark Peek  */
105663a93856SMark Peek 
105763a93856SMark Peek static void
vmci_delayed_work_fn_cb(void * context,int data)105863a93856SMark Peek vmci_delayed_work_fn_cb(void *context, int data)
105963a93856SMark Peek {
106063a93856SMark Peek 	vmci_list(vmci_delayed_work_info) temp_list;
106163a93856SMark Peek 
106263a93856SMark Peek 	vmci_list_init(&temp_list);
106363a93856SMark Peek 
106463a93856SMark Peek 	/*
106563a93856SMark Peek 	 * Swap vmci_delayed_work_infos list with the empty temp_list while
106663a93856SMark Peek 	 * holding a lock. vmci_delayed_work_infos would then be an empty list
106763a93856SMark Peek 	 * and temp_list would contain the elements from the original
106863a93856SMark Peek 	 * vmci_delayed_work_infos. Finally, iterate through temp_list
106963a93856SMark Peek 	 * executing the delayed callbacks.
107063a93856SMark Peek 	 */
107163a93856SMark Peek 
107263a93856SMark Peek 	mtx_lock(&vmci_sc->vmci_delayed_work_lock);
107363a93856SMark Peek 	vmci_list_swap(&temp_list, &vmci_sc->vmci_delayed_work_infos,
107463a93856SMark Peek 	    vmci_delayed_work_info, entry);
107563a93856SMark Peek 	mtx_unlock(&vmci_sc->vmci_delayed_work_lock);
107663a93856SMark Peek 
107763a93856SMark Peek 	while (!vmci_list_empty(&temp_list)) {
107863a93856SMark Peek 		struct vmci_delayed_work_info *delayed_work_info =
107963a93856SMark Peek 		    vmci_list_first(&temp_list);
108063a93856SMark Peek 
108163a93856SMark Peek 		delayed_work_info->work_fn(delayed_work_info->data);
108263a93856SMark Peek 
108363a93856SMark Peek 		vmci_list_remove(delayed_work_info, entry);
108463a93856SMark Peek 		vmci_free_kernel_mem(delayed_work_info,
108563a93856SMark Peek 		    sizeof(*delayed_work_info));
108663a93856SMark Peek 	}
108763a93856SMark Peek }
108863a93856SMark Peek 
108963a93856SMark Peek /*
109063a93856SMark Peek  *------------------------------------------------------------------------------
109163a93856SMark Peek  *
109263a93856SMark Peek  * vmci_schedule_delayed_work_fn --
109363a93856SMark Peek  *
109463a93856SMark Peek  *     Schedule the specified callback.
109563a93856SMark Peek  *
109663a93856SMark Peek  * Results:
109763a93856SMark Peek  *     0 if success, error code otherwise.
109863a93856SMark Peek  *
109963a93856SMark Peek  * Side effects:
110063a93856SMark Peek  *     None.
110163a93856SMark Peek  *
110263a93856SMark Peek  *------------------------------------------------------------------------------
110363a93856SMark Peek  */
110463a93856SMark Peek 
110563a93856SMark Peek int
vmci_schedule_delayed_work_fn(vmci_work_fn * work_fn,void * data)110663a93856SMark Peek vmci_schedule_delayed_work_fn(vmci_work_fn *work_fn, void *data)
110763a93856SMark Peek {
110863a93856SMark Peek 	struct vmci_delayed_work_info *delayed_work_info;
110963a93856SMark Peek 
111063a93856SMark Peek 	delayed_work_info = vmci_alloc_kernel_mem(sizeof(*delayed_work_info),
111163a93856SMark Peek 	    VMCI_MEMORY_ATOMIC);
111263a93856SMark Peek 
111363a93856SMark Peek 	if (!delayed_work_info)
111463a93856SMark Peek 		return (VMCI_ERROR_NO_MEM);
111563a93856SMark Peek 
111663a93856SMark Peek 	delayed_work_info->work_fn = work_fn;
111763a93856SMark Peek 	delayed_work_info->data = data;
111863a93856SMark Peek 	mtx_lock(&vmci_sc->vmci_delayed_work_lock);
111963a93856SMark Peek 	vmci_list_insert(&vmci_sc->vmci_delayed_work_infos,
112063a93856SMark Peek 	    delayed_work_info, entry);
112163a93856SMark Peek 	mtx_unlock(&vmci_sc->vmci_delayed_work_lock);
112263a93856SMark Peek 
112363a93856SMark Peek 	taskqueue_enqueue(taskqueue_thread,
112463a93856SMark Peek 	    &vmci_sc->vmci_delayed_work_task);
112563a93856SMark Peek 
112663a93856SMark Peek 	return (VMCI_SUCCESS);
112763a93856SMark Peek }
112863a93856SMark Peek 
112963a93856SMark Peek /*
113063a93856SMark Peek  *------------------------------------------------------------------------------
113163a93856SMark Peek  *
113263a93856SMark Peek  * vmci_send_datagram --
113363a93856SMark Peek  *
113463a93856SMark Peek  *     VM to hypervisor call mechanism.
113563a93856SMark Peek  *
113663a93856SMark Peek  * Results:
113763a93856SMark Peek  *     The result of the hypercall.
113863a93856SMark Peek  *
113963a93856SMark Peek  * Side effects:
114063a93856SMark Peek  *     None.
114163a93856SMark Peek  *
114263a93856SMark Peek  *------------------------------------------------------------------------------
114363a93856SMark Peek  */
114463a93856SMark Peek 
114563a93856SMark Peek int
vmci_send_datagram(struct vmci_datagram * dg)114663a93856SMark Peek vmci_send_datagram(struct vmci_datagram *dg)
114763a93856SMark Peek {
114863a93856SMark Peek 	int result;
114963a93856SMark Peek 
115063a93856SMark Peek 	if (dg == NULL)
115163a93856SMark Peek 		return (VMCI_ERROR_INVALID_ARGS);
115263a93856SMark Peek 
115363a93856SMark Peek 	/*
115463a93856SMark Peek 	 * Need to acquire spinlock on the device because
115563a93856SMark Peek 	 * the datagram data may be spread over multiple pages and the monitor
115663a93856SMark Peek 	 * may interleave device user rpc calls from multiple VCPUs. Acquiring
115763a93856SMark Peek 	 * the spinlock precludes that possibility. Disabling interrupts to
115863a93856SMark Peek 	 * avoid incoming datagrams during a "rep out" and possibly landing up
115963a93856SMark Peek 	 * in this function.
116063a93856SMark Peek 	 */
116163a93856SMark Peek 	mtx_lock_spin(&vmci_sc->vmci_spinlock);
116263a93856SMark Peek 
116363a93856SMark Peek 	/*
116463a93856SMark Peek 	 * Send the datagram and retrieve the return value from the result
116563a93856SMark Peek 	 * register.
116663a93856SMark Peek 	 */
116763a93856SMark Peek 	__asm__ __volatile__(
116863a93856SMark Peek 	    "cld\n\t"
116963a93856SMark Peek 	    "rep outsb\n\t"
117063a93856SMark Peek 	    : /* No output. */
117163a93856SMark Peek 	    : "d"(vmci_sc->vmci_ioaddr + VMCI_DATA_OUT_ADDR),
117263a93856SMark Peek 	    "c"(VMCI_DG_SIZE(dg)), "S"(dg)
117363a93856SMark Peek 	    );
117463a93856SMark Peek 
117563a93856SMark Peek 	/*
117663a93856SMark Peek 	 * XXX: Should read result high port as well when updating handlers to
117763a93856SMark Peek 	 * return 64bit.
117863a93856SMark Peek 	 */
117963a93856SMark Peek 
118063a93856SMark Peek 	result = bus_space_read_4(vmci_sc->vmci_iot0,
118163a93856SMark Peek 	    vmci_sc->vmci_ioh0, VMCI_RESULT_LOW_ADDR);
118263a93856SMark Peek 	mtx_unlock_spin(&vmci_sc->vmci_spinlock);
118363a93856SMark Peek 
118463a93856SMark Peek 	return (result);
118563a93856SMark Peek }
1186