mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-08-16 23:29:50 +00:00
After Windows 10, version 1607, the cross-signed drivers are forbiden to load when secure boot is enabled. Details please refer to https://docs.microsoft.com/en-us/windows-hardware/drivers/install/kernel-mode-code-signing-policy--windows-vista-and-later- That means the kvm-guest-drivers-windows can't work when secure boot enabled. So we found another windows virtio FE drivers from Oracle to resolve this issue but have to change another subsystem vendor ID for the virtio BE services. This patch introduces a new DM CMD line "--windows" to launch WaaG with Oracle virtio devices including virtio-blk, virtio-net, virtio-input instead Redhat. It can make virtio-blk, virtio-net and virtio-input devices work when WaaG enabling secure boot. Tracked-On: #3583 Signed-off-by: Yuan Liu <yuan1.liu@intel.com> Reviewed-by: Eddie Dong <eddie.dong@intel.com> Acked-by: Yin Fengwei <fengwei.yin@intel.com>
762 lines
17 KiB
C
762 lines
17 KiB
C
/*
|
|
* Copyright (C) 2018 Intel Corporation. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
|
|
#include "dm.h"
|
|
#include "pci_core.h"
|
|
#include "virtio.h"
|
|
#include "mevent.h"
|
|
#include <linux/input.h>
|
|
|
|
static int virtio_input_debug;
|
|
#define DPRINTF(params) do { if (virtio_input_debug) printf params; } while (0)
|
|
#define WPRINTF(params) (printf params)
|
|
|
|
/*
|
|
* Queue definitions.
|
|
*/
|
|
#define VIRTIO_INPUT_EVENT_QUEUE 0
|
|
#define VIRTIO_INPUT_STATUS_QUEUE 1
|
|
#define VIRTIO_INPUT_MAXQ 2
|
|
|
|
/*
|
|
* Virtqueue size.
|
|
*/
|
|
#define VIRTIO_INPUT_RINGSZ 64
|
|
|
|
/*
|
|
* Default size of the buffer used to hold events between SYN
|
|
*/
|
|
#define VIRTIO_INPUT_PACKET_SIZE 10
|
|
|
|
/*
|
|
* Host capabilities
|
|
*/
|
|
#define VIRTIO_INPUT_S_HOSTCAPS (1UL << VIRTIO_F_VERSION_1)
|
|
|
|
enum virtio_input_config_select {
|
|
VIRTIO_INPUT_CFG_UNSET = 0x00,
|
|
VIRTIO_INPUT_CFG_ID_NAME = 0x01,
|
|
VIRTIO_INPUT_CFG_ID_SERIAL = 0x02,
|
|
VIRTIO_INPUT_CFG_ID_DEVIDS = 0x03,
|
|
VIRTIO_INPUT_CFG_PROP_BITS = 0x10,
|
|
VIRTIO_INPUT_CFG_EV_BITS = 0x11,
|
|
VIRTIO_INPUT_CFG_ABS_INFO = 0x12,
|
|
};
|
|
|
|
struct virtio_input_absinfo {
|
|
uint32_t min;
|
|
uint32_t max;
|
|
uint32_t fuzz;
|
|
uint32_t flat;
|
|
uint32_t res;
|
|
};
|
|
|
|
struct virtio_input_devids {
|
|
uint16_t bustype;
|
|
uint16_t vendor;
|
|
uint16_t product;
|
|
uint16_t version;
|
|
};
|
|
|
|
struct virtio_input_event {
|
|
uint16_t type;
|
|
uint16_t code;
|
|
uint32_t value;
|
|
};
|
|
|
|
/*
|
|
* Device-specific configuration registers
|
|
* To query a specific piece of configuration information FE driver sets
|
|
* "select" and "subsel" accordingly, information size is returned in "size"
|
|
* and information data is returned in union "u"
|
|
*/
|
|
struct virtio_input_config {
|
|
uint8_t select;
|
|
uint8_t subsel;
|
|
uint8_t size;
|
|
uint8_t reserved[5];
|
|
union {
|
|
char string[128];
|
|
uint8_t bitmap[128];
|
|
struct virtio_input_absinfo abs;
|
|
struct virtio_input_devids ids;
|
|
} u;
|
|
};
|
|
|
|
struct virtio_input_event_elem {
|
|
struct virtio_input_event event;
|
|
struct iovec iov;
|
|
uint16_t idx;
|
|
};
|
|
|
|
/*
|
|
* Per-device struct
|
|
*/
|
|
struct virtio_input {
|
|
struct virtio_base base;
|
|
struct virtio_vq_info queues[VIRTIO_INPUT_MAXQ];
|
|
pthread_mutex_t mtx;
|
|
struct mevent *mevp;
|
|
uint64_t features;
|
|
struct virtio_input_config cfg;
|
|
char *evdev;
|
|
char *serial;
|
|
int fd;
|
|
bool ready;
|
|
|
|
struct virtio_input_event_elem *event_queue;
|
|
uint32_t event_qsize;
|
|
uint32_t event_qindex;
|
|
};
|
|
|
|
static void virtio_input_reset(void *);
|
|
static void virtio_input_neg_features(void *, uint64_t);
|
|
static void virtio_input_set_status(void *, uint64_t);
|
|
static int virtio_input_cfgread(void *, int, int, uint32_t *);
|
|
static int virtio_input_cfgwrite(void *, int, int, uint32_t);
|
|
static bool virtio_input_get_config(struct virtio_input *, uint8_t, uint8_t,
|
|
struct virtio_input_config *);
|
|
|
|
static struct virtio_ops virtio_input_ops = {
|
|
"virtio_input", /* our name */
|
|
VIRTIO_INPUT_MAXQ, /* we support VTCON_MAXQ virtqueues */
|
|
sizeof(struct virtio_input_config), /* config reg size */
|
|
virtio_input_reset, /* reset */
|
|
NULL, /* device-wide qnotify */
|
|
virtio_input_cfgread, /* read virtio config */
|
|
virtio_input_cfgwrite, /* write virtio config */
|
|
virtio_input_neg_features, /* apply negotiated features */
|
|
virtio_input_set_status, /* called on guest set status */
|
|
};
|
|
|
|
static void
|
|
virtio_input_reset(void *vdev)
|
|
{
|
|
struct virtio_input *vi;
|
|
|
|
vi = vdev;
|
|
|
|
DPRINTF(("vtinput: device reset requested!\n"));
|
|
vi->ready = false;
|
|
virtio_reset_dev(&vi->base);
|
|
}
|
|
|
|
static void
|
|
virtio_input_neg_features(void *vdev, uint64_t negotiated_features)
|
|
{
|
|
struct virtio_input *vi = vdev;
|
|
|
|
vi->features = negotiated_features;
|
|
}
|
|
|
|
static void
|
|
virtio_input_set_status(void *vdev, uint64_t status)
|
|
{
|
|
struct virtio_input *vi = vdev;
|
|
|
|
if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
|
|
if (!vi->ready)
|
|
vi->ready = true;
|
|
}
|
|
}
|
|
|
|
static int
|
|
virtio_input_cfgread(void *vdev, int offset, int size, uint32_t *retval)
|
|
{
|
|
struct virtio_input *vi = vdev;
|
|
struct virtio_input_config cfg;
|
|
bool rc;
|
|
|
|
rc = virtio_input_get_config(vi, vi->cfg.select,
|
|
vi->cfg.subsel, &cfg);
|
|
if (rc)
|
|
memcpy(retval, (uint8_t *)&cfg + offset, size);
|
|
else
|
|
memset(retval, 0, size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virtio_input_cfgwrite(void *vdev, int offset, int size, uint32_t val)
|
|
{
|
|
struct virtio_input *vi = vdev;
|
|
|
|
if (offset == offsetof(struct virtio_input_config, select))
|
|
vi->cfg.select = (uint8_t)val;
|
|
else if (offset == offsetof(struct virtio_input_config, subsel))
|
|
vi->cfg.subsel = (uint8_t)val;
|
|
else
|
|
DPRINTF(("vtinput: write to readonly reg %d\n", offset));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
virtio_input_ignore_event(struct virtio_input_event *event)
|
|
{
|
|
if (!event)
|
|
return true;
|
|
|
|
/*
|
|
* EV_MSC is configured as INPUT_PASS_TO_ALL. In the use case of
|
|
* virtio-input, there is a loop as follows:
|
|
* - A mt frame with (EV_MSC,*,*) is passed to FE.
|
|
* - FE will call virtinput_status to pass (EV_MSC,*,*) back to BE.
|
|
* - BE writes this event to evdev. Because (EV_MSC,*,*)
|
|
* is configured as INPUT_PASS_TO_ALL, it will be written into
|
|
* the event buffer of evdev then be read out by BE without
|
|
* SYN followed.
|
|
* - Each mt frame will introduce one (EV_MSC,*,*).
|
|
* Later the frame becomes larger and larger...
|
|
*/
|
|
if (event->type == EV_MSC)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
virtio_input_notify_event_vq(void *vdev, struct virtio_vq_info *vq)
|
|
{
|
|
DPRINTF(("%s\n", __func__));
|
|
}
|
|
|
|
static void
|
|
virtio_input_notify_status_vq(void *vdev, struct virtio_vq_info *vq)
|
|
{
|
|
struct virtio_input *vi;
|
|
struct virtio_input_event event;
|
|
struct input_event host_event;
|
|
struct iovec iov;
|
|
int n, len;
|
|
uint16_t idx;
|
|
|
|
vi = vdev;
|
|
|
|
while (vq_has_descs(vq)) {
|
|
n = vq_getchain(vq, &idx, &iov, 1, NULL);
|
|
if (n < 0) {
|
|
WPRINTF(("virtio_input: invalid descriptors\n"));
|
|
return;
|
|
}
|
|
if (n == 0) {
|
|
WPRINTF(("virtio_input: get no available descriptors\n"));
|
|
return;
|
|
}
|
|
if (n != 1) {
|
|
WPRINTF(("virtio_input: get wrong number of available descriptors\n"));
|
|
vq_relchain(vq, idx, sizeof(event)); /* Release the chain */
|
|
return;
|
|
}
|
|
|
|
if (vi->fd > 0) {
|
|
memcpy(&event, iov.iov_base, sizeof(event));
|
|
if (!virtio_input_ignore_event(&event)) {
|
|
host_event.type = event.type;
|
|
host_event.code = event.code;
|
|
host_event.value = event.value;
|
|
if (gettimeofday(&host_event.time, NULL)) {
|
|
WPRINTF(("vtinput: gettimeofday failed\n"));
|
|
break;
|
|
}
|
|
len = write(vi->fd, &host_event, sizeof(host_event));
|
|
if (len == -1)
|
|
WPRINTF(("%s: write failed, len = %d, "
|
|
"errno = %d\n",
|
|
__func__, len, errno));
|
|
}
|
|
}
|
|
vq_relchain(vq, idx, sizeof(event)); /* Release the chain */
|
|
}
|
|
vq_endchains(vq, 1); /* Generate interrupt if appropriate. */
|
|
}
|
|
|
|
static void
|
|
virtio_input_send_event(struct virtio_input *vi,
|
|
struct virtio_input_event *event)
|
|
{
|
|
struct virtio_vq_info *vq;
|
|
struct iovec iov;
|
|
int n, i;
|
|
uint16_t idx;
|
|
|
|
if (!vi->ready)
|
|
return;
|
|
|
|
if (vi->event_qindex == vi->event_qsize) {
|
|
vi->event_qsize++;
|
|
vi->event_queue = realloc(vi->event_queue,
|
|
vi->event_qsize *
|
|
sizeof(struct virtio_input_event_elem));
|
|
if (!vi->event_queue) {
|
|
WPRINTF(("virtio_input: realloc memory for vi->event_queue failed!\n"));
|
|
return;
|
|
}
|
|
}
|
|
vi->event_queue[vi->event_qindex].event = *event;
|
|
vi->event_qindex++;
|
|
|
|
if (event->type != EV_SYN || event->code != SYN_REPORT)
|
|
return;
|
|
|
|
vq = &vi->queues[VIRTIO_INPUT_EVENT_QUEUE];
|
|
for (i = 0; i < vi->event_qindex; i++) {
|
|
if (!vq_has_descs(vq)) {
|
|
while (i-- > 0)
|
|
vq_retchain(vq);
|
|
WPRINTF(("%s: not enough avail descs, dropped:%d\n",
|
|
__func__, vi->event_qindex));
|
|
goto out;
|
|
}
|
|
n = vq_getchain(vq, &idx, &iov, 1, NULL);
|
|
if (n < 0) {
|
|
WPRINTF(("virtio-input: invalid descriptors\n"));
|
|
return;
|
|
}
|
|
if (n == 0) {
|
|
WPRINTF(("virtio-input: get no available desciptors\n"));
|
|
return;
|
|
}
|
|
if (n != 1) {
|
|
WPRINTF(("virtio_input: get wrong number of available descriptors\n"));
|
|
vq_relchain(vq, idx, sizeof(event)); /* Release the chain */
|
|
return;
|
|
}
|
|
vi->event_queue[i].iov = iov;
|
|
vi->event_queue[i].idx = idx;
|
|
}
|
|
|
|
for (i = 0; i < vi->event_qindex; i++) {
|
|
memcpy(vi->event_queue[i].iov.iov_base,
|
|
&vi->event_queue[i].event,
|
|
sizeof(struct virtio_input_event));
|
|
vq_relchain(vq, vi->event_queue[i].idx,
|
|
sizeof(struct virtio_input_event));
|
|
}
|
|
|
|
out:
|
|
vi->event_qindex = 0;
|
|
vq_endchains(vq, 1);
|
|
}
|
|
|
|
static void
|
|
virtio_input_read_event(int fd __attribute__((unused)),
|
|
enum ev_type t __attribute__((unused)),
|
|
void *arg)
|
|
{
|
|
struct virtio_input *vi = arg;
|
|
struct virtio_input_event event;
|
|
struct input_event host_event;
|
|
int len;
|
|
|
|
while (1) {
|
|
len = read(vi->fd, &host_event, sizeof(host_event));
|
|
if (len != sizeof(host_event)) {
|
|
if (len == -1 && errno != EAGAIN)
|
|
WPRINTF(("vtinput: host read failed! "
|
|
"len = %d, errno = %d\n",
|
|
len, errno));
|
|
break;
|
|
}
|
|
|
|
event.type = host_event.type;
|
|
event.code = host_event.code;
|
|
event.value = host_event.value;
|
|
virtio_input_send_event(vi, &event);
|
|
}
|
|
}
|
|
|
|
static int
|
|
virtio_input_get_bitmap(struct virtio_input *vi, unsigned int cmd, int count,
|
|
struct virtio_input_config *cfg)
|
|
{
|
|
int i, size = -1;
|
|
int rc;
|
|
|
|
if (count <= 0)
|
|
return -1;
|
|
|
|
if (!cfg)
|
|
return -1;
|
|
|
|
memset(cfg, 0, sizeof(*cfg));
|
|
rc = ioctl(vi->fd, cmd, cfg->u.bitmap);
|
|
if (rc < 0)
|
|
return -1;
|
|
|
|
count = count / 8;
|
|
for (i = count - 1; i >= 0; i--) {
|
|
if (cfg->u.bitmap[i]) {
|
|
size = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static bool
|
|
virtio_input_get_propbits(struct virtio_input *vi,
|
|
struct virtio_input_config *cfg)
|
|
{
|
|
unsigned int cmd;
|
|
int size;
|
|
|
|
if (!cfg)
|
|
return false;
|
|
|
|
cmd = EVIOCGPROP(INPUT_PROP_CNT / 8);
|
|
size = virtio_input_get_bitmap(vi, cmd, INPUT_PROP_CNT, cfg);
|
|
if (size > 0) {
|
|
cfg->select = VIRTIO_INPUT_CFG_PROP_BITS;
|
|
cfg->subsel = 0;
|
|
cfg->size = size;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
virtio_input_get_evbits(struct virtio_input *vi, int type,
|
|
struct virtio_input_config *cfg)
|
|
{
|
|
unsigned int cmd;
|
|
int count, size;
|
|
|
|
if (!cfg)
|
|
return false;
|
|
|
|
switch (type) {
|
|
case EV_KEY:
|
|
count = KEY_CNT;
|
|
break;
|
|
case EV_REL:
|
|
count = REL_CNT;
|
|
break;
|
|
case EV_ABS:
|
|
count = ABS_CNT;
|
|
break;
|
|
case EV_MSC:
|
|
count = MSC_CNT;
|
|
break;
|
|
case EV_SW:
|
|
count = SW_CNT;
|
|
break;
|
|
case EV_LED:
|
|
count = LED_CNT;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
cmd = EVIOCGBIT(type, count / 8);
|
|
size = virtio_input_get_bitmap(vi, cmd, count, cfg);
|
|
if (size > 0) {
|
|
cfg->select = VIRTIO_INPUT_CFG_EV_BITS;
|
|
cfg->subsel = type;
|
|
cfg->size = size;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
virtio_input_get_absinfo(struct virtio_input *vi, int axis,
|
|
struct virtio_input_config *cfg)
|
|
{
|
|
struct virtio_input_config ev_cfg;
|
|
struct input_absinfo abs;
|
|
bool has_ev_abs;
|
|
int rc;
|
|
|
|
if (!cfg)
|
|
return false;
|
|
|
|
has_ev_abs = virtio_input_get_evbits(vi, EV_ABS, &ev_cfg);
|
|
if (!has_ev_abs)
|
|
return false;
|
|
|
|
rc = ioctl(vi->fd, EVIOCGABS(axis), &abs);
|
|
if (rc < 0)
|
|
return false;
|
|
|
|
cfg->u.abs.min = abs.minimum;
|
|
cfg->u.abs.max = abs.maximum;
|
|
cfg->u.abs.fuzz = abs.fuzz;
|
|
cfg->u.abs.flat = abs.flat;
|
|
cfg->u.abs.res = abs.resolution;
|
|
|
|
cfg->select = VIRTIO_INPUT_CFG_ABS_INFO;
|
|
cfg->subsel = axis;
|
|
cfg->size = sizeof(struct virtio_input_absinfo);
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
virtio_input_get_config(struct virtio_input *vi, uint8_t select,
|
|
uint8_t subsel, struct virtio_input_config *cfg)
|
|
{
|
|
struct input_id dev_ids;
|
|
bool found = false;
|
|
int rc;
|
|
|
|
if (!cfg)
|
|
return false;
|
|
|
|
memset(cfg, 0, sizeof(*cfg));
|
|
|
|
switch (select) {
|
|
case VIRTIO_INPUT_CFG_ID_NAME:
|
|
rc = ioctl(vi->fd, EVIOCGNAME(sizeof(cfg->u.string) - 1),
|
|
cfg->u.string);
|
|
if (rc >= 0) {
|
|
cfg->select = VIRTIO_INPUT_CFG_ID_NAME;
|
|
cfg->size = strnlen(cfg->u.string,
|
|
sizeof(cfg->u.string));
|
|
found = true;
|
|
}
|
|
break;
|
|
case VIRTIO_INPUT_CFG_ID_SERIAL:
|
|
if (vi->serial) {
|
|
cfg->select = VIRTIO_INPUT_CFG_ID_SERIAL;
|
|
cfg->size = snprintf(cfg->u.string,
|
|
sizeof(cfg->u.string), "%s", vi->serial);
|
|
found = true;
|
|
}
|
|
break;
|
|
case VIRTIO_INPUT_CFG_ID_DEVIDS:
|
|
rc = ioctl(vi->fd, EVIOCGID, &dev_ids);
|
|
if (!rc) {
|
|
cfg->u.ids.bustype = dev_ids.bustype;
|
|
cfg->u.ids.vendor = dev_ids.vendor;
|
|
cfg->u.ids.product = dev_ids.product;
|
|
cfg->u.ids.version = dev_ids.version;
|
|
cfg->select = VIRTIO_INPUT_CFG_ID_DEVIDS;
|
|
cfg->size = sizeof(struct virtio_input_devids);
|
|
found = true;
|
|
}
|
|
break;
|
|
case VIRTIO_INPUT_CFG_PROP_BITS:
|
|
found = virtio_input_get_propbits(vi, cfg);
|
|
break;
|
|
case VIRTIO_INPUT_CFG_EV_BITS:
|
|
found = virtio_input_get_evbits(vi, subsel, cfg);
|
|
break;
|
|
case VIRTIO_INPUT_CFG_ABS_INFO:
|
|
found = virtio_input_get_absinfo(vi, subsel, cfg);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
static void
|
|
virtio_input_teardown(void *param)
|
|
{
|
|
struct virtio_input *vi;
|
|
|
|
vi = (struct virtio_input *)param;
|
|
if (vi) {
|
|
pthread_mutex_destroy(&vi->mtx);
|
|
if (vi->event_queue)
|
|
free(vi->event_queue);
|
|
if (vi->fd > 0)
|
|
close(vi->fd);
|
|
if (vi->evdev)
|
|
free(vi->evdev);
|
|
if (vi->serial)
|
|
free(vi->serial);
|
|
free(vi);
|
|
vi = NULL;
|
|
}
|
|
}
|
|
|
|
static int
|
|
virtio_input_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
|
|
{
|
|
struct virtio_input *vi;
|
|
pthread_mutexattr_t attr;
|
|
char *opt;
|
|
int flags, ver;
|
|
int rc;
|
|
|
|
/* get evdev path from opts
|
|
* -s n,virtio-input,/dev/input/eventX[,serial]
|
|
*/
|
|
if (!opts) {
|
|
WPRINTF(("%s: evdev path is NULL\n", __func__));
|
|
return -1;
|
|
}
|
|
|
|
vi = calloc(1, sizeof(struct virtio_input));
|
|
if (!vi) {
|
|
WPRINTF(("%s: out of memory\n", __func__));
|
|
return -1;
|
|
}
|
|
|
|
opt = strsep(&opts, ",");
|
|
if (!opt) {
|
|
WPRINTF(("%s: evdev path is NULL\n", __func__));
|
|
goto opt_fail;
|
|
}
|
|
|
|
vi->evdev = strdup(opt);
|
|
if (!vi->evdev) {
|
|
WPRINTF(("%s: strdup failed\n", __func__));
|
|
goto opt_fail;
|
|
}
|
|
|
|
if (opts) {
|
|
vi->serial = strdup(opts);
|
|
if (!vi->serial) {
|
|
WPRINTF(("%s: strdup serial failed\n", __func__));
|
|
goto serial_fail;
|
|
}
|
|
}
|
|
|
|
vi->fd = open(vi->evdev, O_RDWR);
|
|
if (vi->fd < 0) {
|
|
WPRINTF(("open %s failed %d\n", vi->evdev, errno));
|
|
goto open_fail;
|
|
}
|
|
flags = fcntl(vi->fd, F_GETFL);
|
|
fcntl(vi->fd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
rc = ioctl(vi->fd, EVIOCGVERSION, &ver); /* is it a evdev device? */
|
|
if (rc < 0) {
|
|
WPRINTF(("%s: get version failed\n", vi->evdev));
|
|
goto evdev_fail;
|
|
}
|
|
|
|
rc = ioctl(vi->fd, EVIOCGRAB, 1); /* exclusive access */
|
|
if (rc < 0) {
|
|
WPRINTF(("%s: grab device failed %d\n", vi->evdev, errno));
|
|
goto evdev_fail;
|
|
}
|
|
|
|
/* init mutex attribute properly to avoid deadlock */
|
|
rc = pthread_mutexattr_init(&attr);
|
|
if (rc)
|
|
DPRINTF(("mutexattr init failed with erro %d!\n", rc));
|
|
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
|
if (rc)
|
|
DPRINTF(("vtinput: mutexattr_settype failed with "
|
|
"error %d!\n", rc));
|
|
rc = pthread_mutex_init(&vi->mtx, &attr);
|
|
if (rc)
|
|
DPRINTF(("vtinput: pthread_mutex_init failed with "
|
|
"error %d!\n", rc));
|
|
|
|
vi->event_qsize = VIRTIO_INPUT_PACKET_SIZE;
|
|
vi->event_qindex = 0;
|
|
vi->event_queue = calloc(vi->event_qsize,
|
|
sizeof(struct virtio_input_event_elem));
|
|
if (!vi->event_queue) {
|
|
WPRINTF(("vtinput: could not alloc event queue buf\n"));
|
|
goto evqueue_fail;
|
|
}
|
|
|
|
vi->mevp = mevent_add(vi->fd, EVF_READ, virtio_input_read_event, vi,
|
|
virtio_input_teardown, vi);
|
|
if (vi->mevp == NULL) {
|
|
WPRINTF(("vtinput: could not register event\n"));
|
|
goto mevent_fail;
|
|
}
|
|
|
|
virtio_linkup(&vi->base, &virtio_input_ops, vi, dev, vi->queues, BACKEND_VBSU);
|
|
vi->base.mtx = &vi->mtx;
|
|
vi->base.device_caps = VIRTIO_INPUT_S_HOSTCAPS;
|
|
|
|
vi->queues[VIRTIO_INPUT_EVENT_QUEUE].qsize = VIRTIO_INPUT_RINGSZ;
|
|
vi->queues[VIRTIO_INPUT_EVENT_QUEUE].notify =
|
|
virtio_input_notify_event_vq;
|
|
|
|
vi->queues[VIRTIO_INPUT_STATUS_QUEUE].qsize = VIRTIO_INPUT_RINGSZ;
|
|
vi->queues[VIRTIO_INPUT_STATUS_QUEUE].notify =
|
|
virtio_input_notify_status_vq;
|
|
|
|
/* initialize config space */
|
|
pci_set_cfgdata16(dev, PCIR_DEVICE, 0x1040 + VIRTIO_TYPE_INPUT);
|
|
pci_set_cfgdata16(dev, PCIR_VENDOR, VIRTIO_VENDOR);
|
|
pci_set_cfgdata8(dev, PCIR_CLASS, PCIC_INPUTDEV);
|
|
pci_set_cfgdata8(dev, PCIR_SUBCLASS, PCIS_INPUTDEV_OTHER);
|
|
pci_set_cfgdata16(dev, PCIR_SUBDEV_0, 0x1100);
|
|
if (is_winvm == true)
|
|
pci_set_cfgdata16(dev, PCIR_SUBVEND_0, ORACLE_VENDOR_ID);
|
|
else
|
|
pci_set_cfgdata16(dev, PCIR_SUBVEND_0, VIRTIO_VENDOR);
|
|
pci_set_cfgdata16(dev, PCIR_REVID, 1);
|
|
|
|
if (virtio_interrupt_init(&vi->base, virtio_uses_msix())) {
|
|
DPRINTF(("%s, interrupt_init failed!\n", __func__));
|
|
goto fail;
|
|
}
|
|
rc = virtio_set_modern_bar(&vi->base, true);
|
|
|
|
return rc;
|
|
|
|
fail:
|
|
/* all resources will be freed in the teardown callback */
|
|
mevent_delete(vi->mevp);
|
|
return -1;
|
|
|
|
mevent_fail:
|
|
free(vi->event_queue);
|
|
vi->event_queue = NULL;
|
|
evqueue_fail:
|
|
pthread_mutex_destroy(&vi->mtx);
|
|
evdev_fail:
|
|
close(vi->fd);
|
|
vi->fd = -1;
|
|
open_fail:
|
|
if (vi->serial) {
|
|
free(vi->serial);
|
|
vi->serial = NULL;
|
|
}
|
|
serial_fail:
|
|
free(vi->evdev);
|
|
vi->evdev = NULL;
|
|
opt_fail:
|
|
free(vi);
|
|
vi = NULL;
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
virtio_input_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
|
|
{
|
|
struct virtio_input *vi;
|
|
|
|
vi = (struct virtio_input *)dev->arg;
|
|
if (vi && vi->mevp)
|
|
mevent_delete(vi->mevp);
|
|
}
|
|
|
|
struct pci_vdev_ops pci_ops_virtio_input = {
|
|
.class_name = "virtio-input",
|
|
.vdev_init = virtio_input_init,
|
|
.vdev_deinit = virtio_input_deinit,
|
|
.vdev_barwrite = virtio_pci_write,
|
|
.vdev_barread = virtio_pci_read
|
|
};
|
|
DEFINE_PCI_DEVTYPE(pci_ops_virtio_input);
|