CoreU virtio back-end driver

v4 -> v5
    Replace strcpy with strncpy
    Refine resource free if error happens
    Remove meaningless *in_progress* label for pthread wait/signal
    Rename the coreu thread routine name (virtio_coreu_thread)

v3 -> v4
    Move setsocketopt to connect_to_daemon
    Cleanup coreu file descriptor after close
    Reconnect CoreU daemon if the socket descriptor is invalid

v2 -> v3
    Move the daemon connection to vdev_init
    Diagram the CoreU virtualization architecture
    Create a seperate thread for sending and receving the CoreU message
    Change the socket name

v1 -> v2
    No change

v1
    Initial CoreU back-end driver

Signed-off-by: Yu Shiqiang <shiqiang.yu@intel.com>
This commit is contained in:
Yu Shiqiang 2018-10-29 03:22:34 -04:00 committed by Eddie Dong
parent 7003afbe74
commit fa012e6987
2 changed files with 356 additions and 0 deletions

View File

@ -104,6 +104,7 @@ SRCS += hw/pci/virtio/virtio_rnd.c
SRCS += hw/pci/virtio/virtio_ipu.c
SRCS += hw/pci/virtio/virtio_hyper_dmabuf.c
SRCS += hw/pci/virtio/virtio_mei.c
SRCS += hw/pci/virtio/virtio_coreu.c
SRCS += hw/pci/virtio/virtio_rpmb.c
SRCS += hw/pci/irq.c
SRCS += hw/pci/uart.c

View File

@ -0,0 +1,355 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
/*
* CoreU Virtualization
*
* CoreU is for PAVP session management. CoreU contains two parts, the daemon
* and library used by libpavp. When playback premium content which should be
* protected in GPU memory, media application calls libpavp which uses MEI to
* create PAVP session. CoreU daemon has the capability to read GPU registers
* to know whether the PAVP session status is valid. For now, CoreU is not open
* source.
*
* +---------------+
* | |
* +----------+----------+ | +------------------+
* | ACRN DM | | | Media APP |
* | +-----------------+ | | +------------------+
* | | CoreU Backend | | | |
* | +-----------------+ | | +------------------+
* +---------------------+ | | LibPAVP |
* | | +------------------+
* | | |
* +------------------+ | +------------------+
* | CoreU SOS Daemon | | | CoreU UOS Daemon |
* +------------------+ | +------------------+
* |
* Service OS User Space | User OS User Space
* |
* -------------------------- | ---------------------------
* |
* Service OS Kernel Space | User OS Kernel Space
* |
* | +------------------+
* | | CoreU Frontend |
* | +------------------+
* | |
* +-------------+
*
* Above diagram illustrates the CoreU architecture in ACRN. In SOS, CoreU
* daemon starts upon the system boots. In UOS, CoreU daemon gets the PAVP
* session status by open/read/write /dev/coreu0 which is created by CoreU
* frontend, instead of accessing GPU. Then the CoreU frontend sends the
* requests to the CoreU backend thru virtio mechanism. CoreU backend talks to
* CoreU SOS daemon to get the PAVP session status.
*
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>
#include <sysexits.h>
#include <dlfcn.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "dm.h"
#include "pci_core.h"
#include "virtio.h"
#define VIRTIO_COREU_RINGSZ 64
#define COREU_MSG_SIZE 72
#define VIRTIO_COREU_NUMQ 1
#define COREU_SERVICE_NAME "/var/run/coreu"
/* CoreU raw message */
struct coreu_msg {
uint8_t bytes[COREU_MSG_SIZE];
};
/* Per-device struct - VBS-U */
struct virtio_coreu {
struct virtio_base base;
struct virtio_vq_info queues[VIRTIO_COREU_NUMQ];
pthread_mutex_t mtx;
pthread_t rx_tid;
pthread_mutex_t rx_mtx;
pthread_cond_t rx_cond;
/* socket handle to CoreU daemon */
int fd;
};
/* VBS-U virtio_ops */
static void virtio_coreu_reset(void *);
static struct virtio_ops virtio_coreu_ops = {
"virtio_coreu", /* our name */
VIRTIO_COREU_NUMQ, /* we support one virtqueue */
0, /* config reg size */
virtio_coreu_reset, /* reset */
NULL, /* device-wide qnotify */
NULL, /* read virtio config */
NULL, /* write virtio config */
NULL, /* apply negotiated features */
NULL, /* called on guest set status */
};
/* Debug printf */
static int virtio_coreu_debug;
#define DPRINTF(params) do { if (virtio_coreu_debug) printf params; } while (0)
#define WPRINTF(params) (printf params)
static void
virtio_coreu_reset(void *vdev)
{
struct virtio_coreu *vcoreu = vdev;
DPRINTF(("virtio_coreu: device reset requested\n"));
virtio_reset_dev(&vcoreu->base);
}
static int
send_and_receive(int fd, struct coreu_msg *msg)
{
uint32_t msg_size = sizeof(struct coreu_msg);
int ret;
ret = send(fd, (void *)msg, msg_size, 0);
if (ret < 0) {
WPRINTF(("send error\n"));
return ret;
}
ret = recv(fd, (void *)msg, msg_size, 0);
if (ret < 0) {
WPRINTF(("recv error\n"));
return ret;
}
if (ret < msg_size) {
WPRINTF(("received part of the data, %d instead of %d\n",
ret,
msg_size));
return ret;
}
return 0;
}
static void *
virtio_coreu_thread(void *param)
{
struct virtio_coreu *vcoreu = param;
struct virtio_vq_info *rvq = &vcoreu->queues[0];
struct iovec iov;
uint16_t idx;
int ret;
struct coreu_msg *msg;
for (;;) {
pthread_mutex_lock(&vcoreu->rx_mtx);
ret = pthread_cond_wait(&vcoreu->rx_cond, &vcoreu->rx_mtx);
pthread_mutex_unlock(&vcoreu->rx_mtx);
if (ret)
break;
while(vq_has_descs(rvq)) {
vq_getchain(rvq, &idx, &iov, 1, NULL);
msg = (struct coreu_msg *)(iov.iov_base);
ret = send_and_receive(vcoreu->fd, msg);
if (ret < 0)
{
close(vcoreu->fd);
vcoreu->fd = -1;
}
/* release this chain and handle more */
vq_relchain(rvq, idx, sizeof(struct coreu_msg));
}
vq_endchains(rvq, 1);
}
pthread_exit(NULL);
}
static int
connect_coreu_daemon()
{
struct sockaddr_un addr;
int msg_size;
int fd;
int ret;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) {
WPRINTF(("socket error %d\n", errno));
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, COREU_SERVICE_NAME, sizeof(&COREU_SERVICE_NAME));
ret = connect(fd, &addr, sizeof(struct sockaddr_un));
if (ret < 0) {
WPRINTF(("connect error %d\n", errno));
close(fd);
return -1;
}
msg_size = sizeof(struct coreu_msg);
ret = setsockopt(fd, SOL_SOCKET,
SO_RCVLOWAT, &msg_size, sizeof(msg_size));
if (ret < 0) {
WPRINTF(("setsockopt error\n"));
close(fd);
return -1;
}
return fd;
}
static void
virtio_coreu_notify(void *vdev, struct virtio_vq_info *vq)
{
struct virtio_coreu *vcoreu = vdev;
/* Any ring entries to process */
if (!vq_has_descs(vq))
return;
vcoreu->fd = (vcoreu->fd < 0) ? connect_coreu_daemon() : vcoreu->fd;
if (vcoreu->fd < 0)
{
WPRINTF(("Invalid CoreU daemon file descriptor\n"));
return;
}
/* Signal the thread for processing */
pthread_mutex_lock(&vcoreu->rx_mtx);
pthread_cond_signal(&vcoreu->rx_cond);
pthread_mutex_unlock(&vcoreu->rx_mtx);
}
static int
virtio_coreu_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
struct virtio_coreu *vcoreu;
pthread_mutexattr_t attr;
char tname[MAXCOMLEN + 1];
int rc;
vcoreu = calloc(1, sizeof(struct virtio_coreu));
if (!vcoreu) {
WPRINTF(("vcoreu init: fail to alloc virtio_coreu\n"));
return -1;
}
/* init mutex attribute properly */
int mutexattr_type = virtio_uses_msix()
? PTHREAD_MUTEX_DEFAULT
: PTHREAD_MUTEX_RECURSIVE;
rc = pthread_mutexattr_init(&attr);
if (rc)
WPRINTF(("vcoreu init: mutexattr init fail, erro %d\n", rc));
rc = pthread_mutexattr_settype(&attr, mutexattr_type);
if (rc)
WPRINTF(("vcoreu init: mutexattr_settype fail, erro %d\n", rc));
rc = pthread_mutex_init(&vcoreu->mtx, &attr);
if (rc)
WPRINTF(("vcoreu init: mutexattr_settype fail, erro %d\n", rc));
DPRINTF(("vcoreu init: using VBS-U...\n"));
virtio_linkup(&vcoreu->base, &virtio_coreu_ops,
vcoreu, dev, vcoreu->queues);
vcoreu->base.mtx = &vcoreu->mtx;
vcoreu->queues[0].qsize = VIRTIO_COREU_RINGSZ;
vcoreu->queues[0].notify = virtio_coreu_notify;
/* initialize config space */
pci_set_cfgdata16(dev, PCIR_DEVICE, VIRTIO_DEV_COREU);
pci_set_cfgdata16(dev, PCIR_VENDOR, INTEL_VENDOR_ID);
pci_set_cfgdata8(dev, PCIR_CLASS, PCIC_CRYPTO);
pci_set_cfgdata8(dev, PCIR_SUBCLASS, PCIS_SIMPLECOMM_OTHER);
pci_set_cfgdata16(dev, PCIR_SUBDEV_0, VIRTIO_TYPE_COREU);
pci_set_cfgdata16(dev, PCIR_SUBVEND_0, INTEL_VENDOR_ID);
if (virtio_interrupt_init(&vcoreu->base, virtio_uses_msix())) {
WPRINTF(("vcoreu init: interrupt init fail\n"));
free(vcoreu);
return -1;
}
virtio_set_io_bar(&vcoreu->base, 0);
/*
* connect to coreu daemon in init phase
*
* @FIXME if failed connecting to CoreU daemon, the return value should
* be set appropriately for SOS not exposing the CoreU PCI device to UOS
*/
vcoreu->fd = connect_coreu_daemon();
if (vcoreu->fd < 0) {
WPRINTF(("connection to server failed\n"));
pthread_mutex_destroy(&vcoreu->mtx);
free(vcoreu);
return -errno;
}
pthread_mutex_init(&vcoreu->rx_mtx, NULL);
pthread_cond_init(&vcoreu->rx_cond, NULL);
pthread_create(&vcoreu->rx_tid, NULL,
virtio_coreu_thread, (void *)vcoreu);
snprintf(tname, sizeof(tname), "vtcoreu-%d:%d tx",
dev->slot, dev->func);
pthread_setname_np(vcoreu->rx_tid, tname);
return 0;
}
static void
virtio_coreu_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
struct virtio_coreu *vcoreu = (struct virtio_coreu *)dev->arg;
if (!vcoreu)
return;
pthread_mutex_destroy(&vcoreu->mtx);
pthread_mutex_destroy(&vcoreu->rx_mtx);
pthread_cond_destroy(&vcoreu->rx_cond);
pthread_join(vcoreu->rx_tid, NULL);
free(vcoreu);
}
struct pci_vdev_ops pci_ops_virtio_coreu = {
.class_name = "virtio-coreu",
.vdev_init = virtio_coreu_init,
.vdev_deinit = virtio_coreu_deinit,
.vdev_barwrite = virtio_pci_write,
.vdev_barread = virtio_pci_read
};
DEFINE_PCI_DEVTYPE(pci_ops_virtio_coreu);