diff --git a/devicemodel/Makefile b/devicemodel/Makefile index 4707ab259..48d5baa82 100644 --- a/devicemodel/Makefile +++ b/devicemodel/Makefile @@ -112,6 +112,7 @@ SRCS += hw/pci/virtio/virtio_mei.c SRCS += hw/pci/virtio/virtio_coreu.c SRCS += hw/pci/virtio/virtio_hdcp.c SRCS += hw/pci/virtio/virtio_rpmb.c +SRCS += hw/pci/virtio/virtio_gpio.c SRCS += hw/pci/irq.c SRCS += hw/pci/uart.c SRCS += hw/pci/gvt.c diff --git a/devicemodel/hw/pci/virtio/virtio_gpio.c b/devicemodel/hw/pci/virtio/virtio_gpio.c new file mode 100644 index 000000000..a012914d1 --- /dev/null +++ b/devicemodel/hw/pci/virtio/virtio_gpio.c @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "acpi.h" +#include "dm.h" +#include "pci_core.h" +#include "mevent.h" +#include "virtio.h" + +/* + * GPIO virtualization architecture + * + * +--------------------------+ + * |ACRN DM | + * | +--------------------+ | + * | | | | virtqueue + * | | GPIO mediator |<-+-----------+ + * | | | | | + * | +-+-----+--------+---+ | | + * User space +----|-----|--------|------+ | + * +---------+ | | | + * v v v | + * +----------------+ +-----+ +----------------+ | +---------------+ + * -+ /dev/gpiochip0 +---+ ... +---+ /dev/gpiochipN +-----+ UOS +- + * + + + + + + | +/dev/gpiochip0 + + * +------------+---+ +--+--+ +-------------+--+ | +------+--------+ + * Kernel space | +--------------+ | | | + * +--------------------+ | | | | + * v v v | v + * +---------------------+ +---------------------+ | +-------------+ + * | | | | | |UOS Virtio | + * | pinctrl subsystem |<---+ gpiolib subsystem | +->+GPIO Driver | + * | | | | | | + * +--------+------------+ +----------+----------+ +-------------+ + * | +------------------------+ + * | | + * ----------|---|---------------------------------------------------------- + * Hardware | | + * v v + * +------------------+ + * | | + * | GPIO controllers | + * | | + * +------------------+ + */ + +static int gpio_debug; +static FILE *dbg_file; +#define VIRTIO_GPIO_LOG_INIT do { \ + if (gpio_debug && !dbg_file) { \ + dbg_file = fopen("/tmp/log.gpio", "w+"); \ + if (!dbg_file) \ + printf("virtio_gpio log open failed\r\n"); \ + } \ +} while (0) + +#define VIRTIO_GPIO_LOG_DEINIT do { \ + if (dbg_file) { \ + fclose(dbg_file); \ + dbg_file = NULL; \ + } \ +} while (0) + +#define DPRINTF(format, arg...) do { \ + if (gpio_debug && dbg_file) { \ + fprintf(dbg_file, format, arg); \ + fflush(dbg_file); \ + } \ +} while (0) + +/* Virtio GPIO supports maximum number of virtual gpio */ +#define VIRTIO_GPIO_MAX_VLINES 64 + +/* Virtio GPIO supports maximum native gpio chips */ +#define VIRTIO_GPIO_MAX_CHIPS 8 + +/* Virtio GPIO virtqueue numbers*/ +#define VIRTIO_GPIO_MAXQ 1 + +/* Virtio GPIO capabilities */ +#define VIRTIO_GPIO_F_CHIP 1 +#define VIRTIO_GPIO_S_HOSTCAPS VIRTIO_GPIO_F_CHIP + +/* make virtio gpio mediator a singleton mode */ +static bool virtio_gpio_is_active; + +struct virtio_gpio_config { + uint16_t base; /* base number */ + uint16_t ngpio; /* number of gpios */ +} __attribute__((packed)); + +struct virtio_gpio { + pthread_mutex_t mtx; + struct virtio_base base; + struct virtio_vq_info queues[VIRTIO_GPIO_MAXQ]; + struct virtio_gpio_config config; +}; + +static void virtio_gpio_reset(void *vdev) +{ + struct virtio_gpio *gpio; + + gpio = vdev; + + DPRINTF("%s", "virtio_gpio: device reset requested!\n"); + virtio_reset_dev(&gpio->base); +} + +static int +virtio_gpio_cfgread(void *vdev, int offset, int size, uint32_t *retval) +{ + struct virtio_gpio *gpio = vdev; + void *ptr; + + ptr = (uint8_t *)&gpio->config + offset; + memcpy(retval, ptr, size); + return 0; +} + +static struct virtio_ops virtio_gpio_ops = { + "virtio_gpio", /* our name */ + VIRTIO_GPIO_MAXQ, /* we support VTGPIO_MAXQ virtqueues */ + sizeof(struct virtio_gpio_config), /* config reg size */ + virtio_gpio_reset, /* reset */ + NULL, /* device-wide qnotify */ + virtio_gpio_cfgread, /* read virtio config */ + NULL, /* write virtio config */ + NULL, /* apply negotiated features */ + NULL, /* called on guest set status */ + +}; + +static void +virtio_gpio_proc(struct virtio_gpio *gpio, struct iovec *iov, uint16_t flag) +{ + /* Implemented in the subsequent patch */ +} + +static void +virtio_gpio_notify(void *vdev, struct virtio_vq_info *vq) +{ + struct iovec iov[2]; + struct virtio_gpio *gpio; + uint16_t idx; + int n; + + gpio = (struct virtio_gpio *)vdev; + if (vq_has_descs(vq)) { + n = vq_getchain(vq, &idx, iov, 2, NULL); + assert(n < 3); + + virtio_gpio_proc(gpio, iov, n); + /* + * Release this chain and handle more + */ + vq_relchain(vq, idx, 1); + + } +} + +static int +virtio_gpio_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) +{ + struct virtio_gpio *gpio; + pthread_mutexattr_t attr; + int rc; + + /* Just support one bdf */ + if (virtio_gpio_is_active) + return -1; + + VIRTIO_GPIO_LOG_INIT; + + if (!opts) { + DPRINTF("%s", "virtio gpio: needs gpio information\n"); + rc = -EINVAL; + goto init_fail; + } + + gpio = calloc(1, sizeof(struct virtio_gpio)); + if (!gpio) { + DPRINTF("%s", "virtio gpio: failed to calloc virtio_gpio\n"); + rc = -ENOMEM; + goto init_fail; + } + + /* init mutex attribute properly to avoid deadlock */ + rc = pthread_mutexattr_init(&attr); + if (rc) { + DPRINTF("mutexattr init failed with error %d!\n", rc); + goto mtx_fail; + } + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (rc) { + DPRINTF("mutexattr_settype failed with error %d!\n", rc); + goto fail; + } + + rc = pthread_mutex_init(&gpio->mtx, &attr); + if (rc) { + DPRINTF("pthread_mutex_init failed with error %d!\n", rc); + goto fail; + } + + virtio_linkup(&gpio->base, &virtio_gpio_ops, gpio, dev, gpio->queues, + BACKEND_VBSU); + + /* gpio base for frontend gpio chip */ + gpio->config.base = 0; + + gpio->base.device_caps = VIRTIO_GPIO_S_HOSTCAPS; + gpio->base.mtx = &gpio->mtx; + gpio->queues[0].qsize = 64; + gpio->queues[0].notify = virtio_gpio_notify; + + /* initialize config space */ + pci_set_cfgdata16(dev, PCIR_DEVICE, VIRTIO_DEV_GPIO); + pci_set_cfgdata16(dev, PCIR_VENDOR, INTEL_VENDOR_ID); + pci_set_cfgdata8(dev, PCIR_CLASS, PCIC_BASEPERIPH); + pci_set_cfgdata16(dev, PCIR_SUBDEV_0, VIRTIO_TYPE_GPIO); + pci_set_cfgdata16(dev, PCIR_SUBVEND_0, INTEL_VENDOR_ID); + + /* use BAR 1 to map MSI-X table and PBA, if we're using MSI-X */ + if (virtio_interrupt_init(&gpio->base, virtio_uses_msix())) { + rc = -1; + goto fail; + } + + /* use BAR 0 to map config regs in IO space */ + virtio_set_io_bar(&gpio->base, 0); + + virtio_gpio_is_active = true; + return 0; + +fail: + pthread_mutex_destroy(&gpio->mtx); + +mtx_fail: + free(gpio); + dev->arg = NULL; + +init_fail: + VIRTIO_GPIO_LOG_DEINIT; + return rc; +} + +static void +virtio_gpio_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts) +{ + struct virtio_gpio *gpio; + + DPRINTF("%s", "virtio gpio: pci_gpio_deinit\r\n"); + virtio_gpio_is_active = false; + gpio = (struct virtio_gpio *)dev->arg; + if (gpio) { + pthread_mutex_destroy(&gpio->mtx); + free(gpio); + dev->arg = NULL; + } + + VIRTIO_GPIO_LOG_DEINIT; +} + +struct pci_vdev_ops pci_ops_virtio_gpio = { + .class_name = "virtio-gpio", + .vdev_init = virtio_gpio_init, + .vdev_deinit = virtio_gpio_deinit, + .vdev_barwrite = virtio_pci_write, + .vdev_barread = virtio_pci_read, +}; + +DEFINE_PCI_DEVTYPE(pci_ops_virtio_gpio); diff --git a/devicemodel/include/virtio.h b/devicemodel/include/virtio.h index 16b1a8a00..c217fabc1 100644 --- a/devicemodel/include/virtio.h +++ b/devicemodel/include/virtio.h @@ -206,6 +206,7 @@ enum { #define VIRTIO_TYPE_HYPERDMABUF 0xFFFA #define VIRTIO_TYPE_HDCP 0xFFF9 #define VIRTIO_TYPE_COREU 0xFFF8 +#define VIRTIO_TYPE_GPIO 0xFFF7 /* * PCI vendor/device IDs @@ -228,6 +229,7 @@ enum { #define VIRTIO_DEV_HYPERDMABUF 0x8606 #define VIRTIO_DEV_HDCP 0x8607 #define VIRTIO_DEV_COREU 0x8608 +#define VIRTIO_DEV_GPIO 0x8609 /* * VIRTIO_CONFIG_S_NEEDS_RESET is not defined