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