mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-05-29 18:35:01 +00:00
HDCP virtio back-end driver
Initial HDCP back-end driver. Signed-off-by: Romli, Khairul Anuar <khairul.anuar.romli@intel.com>
This commit is contained in:
parent
fa012e6987
commit
cbe1b74eee
@ -105,6 +105,7 @@ 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_hdcp.c
|
||||
SRCS += hw/pci/virtio/virtio_rpmb.c
|
||||
SRCS += hw/pci/irq.c
|
||||
SRCS += hw/pci/uart.c
|
||||
|
481
devicemodel/hw/pci/virtio/virtio_hdcp.c
Normal file
481
devicemodel/hw/pci/virtio/virtio_hdcp.c
Normal file
@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* HDCP Virtualization
|
||||
*
|
||||
*
|
||||
* +---------------+
|
||||
* | |
|
||||
* +----------+----------+ | +------------------+
|
||||
* | ACRN DM | | | Media APP |
|
||||
* | +-----------------+ | | +------------------+
|
||||
* | | HDCP Backend | | | |
|
||||
* | +-----------------+ | | +------------------+
|
||||
* +---------------------+ | | HDCP Libraries |
|
||||
* | | +------------------+
|
||||
* | | |
|
||||
* +------------------+ | +------------------+
|
||||
* | HDCP SOS Daemon | | | HDCP UOS Daemon |
|
||||
* +------------------+ | +------------------+
|
||||
* |
|
||||
* Service OS User Space | User OS User Space
|
||||
* |
|
||||
* -------------------------- | ---------------------------
|
||||
* |
|
||||
* Service OS Kernel Space | User OS Kernel Space
|
||||
* |
|
||||
* +------------------+ | +------------------+
|
||||
* | i915 HDCP Driver | | | HDCP Front End |
|
||||
* +------------------+ | +------------------+
|
||||
* | |
|
||||
* +-------------+
|
||||
*
|
||||
* Above diagram illustrates the HDCP architecture in ACRN. In SOS, HDCP
|
||||
* library being used by media app. In UOS, HDCP Daemon gets the HDCP
|
||||
* request by open/read/write /dev/hdcp0 which is created by HDCP
|
||||
* frontend, instead of accessing GPU. Then the HDCP frontend sends the
|
||||
* requests to the HDCP backend thru virtio mechanism. HDCP backend talks to
|
||||
* HDCP SOS daemon that will ask HDCP Kernel Driver to execute the requsted
|
||||
* operation.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 NUM_PHYSICAL_PORTS_MAX 5
|
||||
#define VIRTIO_HDCP_RINGSZ 64
|
||||
#define VIRTIO_HDCP_NUMQ 1
|
||||
#define HDCP_DIR_BASE "/var/run/hdcp/"
|
||||
#define HDCP_SDK_SOCKET_PATH HDCP_DIR_BASE ".sdk_socket"
|
||||
|
||||
typedef enum _HDCP_API_TYPE
|
||||
{
|
||||
HDCP_API_INVALID,
|
||||
HDCP_API_CREATE,
|
||||
HDCP_API_DESTROY,
|
||||
HDCP_API_ENUMERATE_HDCP_DISPLAY,
|
||||
HDCP_API_SENDSRMDATA,
|
||||
HDCP_API_GETSRMVERSION,
|
||||
HDCP_API_ENABLE,
|
||||
HDCP_API_DISABLE,
|
||||
HDCP_API_GETSTATUS,
|
||||
HDCP_API_GETKSVLIST,
|
||||
HDCP_API_REPORTSTATUS,
|
||||
HDCP_API_TERM_MSG_LOOP,
|
||||
HDCP_API_CREATE_CALLBACK,
|
||||
HDCP_API_SET_PROTECTION_LEVEL,
|
||||
HDCP_API_CONFIG,
|
||||
HDCP_API_ILLEGAL
|
||||
} HDCP_API_TYPE;
|
||||
|
||||
typedef enum _PORT_EVENT
|
||||
{
|
||||
PORT_EVENT_NONE = 0,
|
||||
PORT_EVENT_PLUG_IN, // hot plug in
|
||||
PORT_EVENT_PLUG_OUT, // hot plug out
|
||||
PORT_EVENT_LINK_LOST, // HDCP authentication step3 fail
|
||||
} PORT_EVENT;
|
||||
|
||||
typedef struct _Port
|
||||
{
|
||||
uint32_t Id;
|
||||
int Status;
|
||||
PORT_EVENT Event;
|
||||
} Port;
|
||||
|
||||
typedef enum _HDCP_STATUS
|
||||
{
|
||||
HDCP_STATUS_SUCCESSFUL = 0,
|
||||
HDCP_STATUS_ERROR_ALREADY_CREATED,
|
||||
HDCP_STATUS_ERROR_INVALID_PARAMETER,
|
||||
HDCP_STATUS_ERROR_NO_DISPLAY,
|
||||
HDCP_STATUS_ERROR_REVOKED_DEVICE,
|
||||
HDCP_STATUS_ERROR_SRM_INVALID,
|
||||
HDCP_STATUS_ERROR_INSUFFICIENT_MEMORY,
|
||||
HDCP_STATUS_ERROR_INTERNAL,
|
||||
HDCP_STATUS_ERROR_SRM_NOT_RECENT,
|
||||
HDCP_STATUS_ERROR_SRM_FILE_STORAGE,
|
||||
HDCP_STATUS_ERROR_MAX_DEVICES_EXCEEDED,
|
||||
HDCP_STATUS_ERROR_MAX_DEPTH_EXCEEDED,
|
||||
HDCP_STATUS_ERROR_MSG_TRANSACTION,
|
||||
} HDCP_STATUS;
|
||||
|
||||
enum HDCP_CONFIG_TYPE
|
||||
{
|
||||
INVALID_CONFIG = 0, // invalid configure type
|
||||
SRM_STORAGE_CONFIG, // config to disable/enable SRM storage
|
||||
};
|
||||
|
||||
typedef struct _HDCP_CONFIG
|
||||
{
|
||||
enum HDCP_CONFIG_TYPE type;
|
||||
bool disableSrmStorage;
|
||||
} HDCP_CONFIG;
|
||||
|
||||
/* HDCP socket data */
|
||||
struct SocketData
|
||||
{
|
||||
union
|
||||
{
|
||||
uint8_t Bytes;
|
||||
struct
|
||||
{
|
||||
uint32_t Size;
|
||||
HDCP_API_TYPE Command;
|
||||
HDCP_STATUS Status;
|
||||
|
||||
uint8_t KsvCount; // Number of KSV in topology
|
||||
uint8_t Depth; // Depth of topology
|
||||
bool isType1Capable; // Port whether support HDCP2.2
|
||||
union
|
||||
{
|
||||
Port Ports[NUM_PHYSICAL_PORTS_MAX];
|
||||
Port SinglePort;
|
||||
};
|
||||
|
||||
uint32_t PortCount;
|
||||
|
||||
uint32_t SrmOrKsvListDataSz;
|
||||
uint16_t SrmVersion;
|
||||
|
||||
HDCP_CONFIG Config;
|
||||
|
||||
uint8_t Level;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/* Per-device struct - VBS-U */
|
||||
struct virtio_hdcp {
|
||||
struct virtio_base base;
|
||||
struct virtio_vq_info queues[VIRTIO_HDCP_NUMQ];
|
||||
pthread_mutex_t mtx;
|
||||
|
||||
int in_progress;
|
||||
pthread_t rx_tid;
|
||||
pthread_mutex_t rx_mtx;
|
||||
pthread_cond_t rx_cond;
|
||||
|
||||
/* socket handle to HDCP daemon */
|
||||
int fd;
|
||||
};
|
||||
|
||||
/* VBS-U virtio_ops */
|
||||
static void virtio_hdcp_reset(void *);
|
||||
|
||||
static struct virtio_ops virtio_hdcp_ops = {
|
||||
"virtio_hdcp", /* our name */
|
||||
VIRTIO_HDCP_NUMQ, /* we support one virtqueue */
|
||||
0, /* config reg size */
|
||||
virtio_hdcp_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_hdcp_debug;
|
||||
#define DPRINTF(params) do { if (virtio_hdcp_debug) printf params; } while (0)
|
||||
#define WPRINTF(params) (printf params)
|
||||
|
||||
static void
|
||||
virtio_hdcp_reset(void *vdev)
|
||||
{
|
||||
struct virtio_hdcp *vhdcp = vdev;
|
||||
|
||||
DPRINTF(("virtio_hdcp: device reset requested\n"));
|
||||
virtio_reset_dev(&vhdcp->base);
|
||||
}
|
||||
|
||||
static int
|
||||
SendMessage(int fd, uint8_t* data, int dataSz)
|
||||
{
|
||||
int bytesRemaining = dataSz;
|
||||
int offset = 0;
|
||||
|
||||
while (bytesRemaining > 0)
|
||||
{
|
||||
ssize_t count = send(fd, &data[offset], bytesRemaining, MSG_NOSIGNAL);
|
||||
if (-1 == count)
|
||||
{
|
||||
if ((EINTR == errno) || (EAGAIN == errno))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
WPRINTF(("Failed to send!"));
|
||||
return errno;
|
||||
}
|
||||
|
||||
bytesRemaining -= count;
|
||||
offset += count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
GetMessage(int fd, uint8_t* data, int dataSz)
|
||||
{
|
||||
int count = 0;
|
||||
int bytesRemaining = dataSz;
|
||||
int offset = 0;
|
||||
|
||||
while (bytesRemaining > 0)
|
||||
{
|
||||
count = read(fd, &data[offset], bytesRemaining);
|
||||
if (-1 == count)
|
||||
{
|
||||
if ((EINTR == errno) || (EAGAIN == errno))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
WPRINTF(("Failed to read!"));
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (0 == count)
|
||||
{
|
||||
WPRINTF(("Success to read, but the content is empty!"));
|
||||
return ENOTCONN;
|
||||
}
|
||||
|
||||
bytesRemaining -= count;
|
||||
offset += count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
performMessageTransaction(int fd, struct SocketData data)
|
||||
{
|
||||
int ret;
|
||||
// Send the request to daemon
|
||||
ret = SendMessage(fd, &data.Bytes, sizeof(data));
|
||||
if (ret < 0) {
|
||||
WPRINTF(("send error\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Get reply from daemon
|
||||
ret = GetMessage(fd, &data.Bytes, sizeof(data));
|
||||
if (ret < 0) {
|
||||
WPRINTF(("recv error\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
virtio_hdcp_talk_to_daemon(void *param)
|
||||
{
|
||||
struct virtio_hdcp *vhdcp = param;
|
||||
struct virtio_vq_info *rvq = &vhdcp->queues[0];
|
||||
struct iovec iov;
|
||||
uint16_t idx;
|
||||
int ret;
|
||||
struct SocketData *msg;
|
||||
|
||||
for (;;) {
|
||||
pthread_mutex_lock(&vhdcp->rx_mtx);
|
||||
vhdcp->in_progress = 0;
|
||||
ret = pthread_cond_wait(&vhdcp->rx_cond, &vhdcp->rx_mtx);
|
||||
assert(ret == 0);
|
||||
|
||||
vhdcp->in_progress = 1;
|
||||
pthread_mutex_unlock(&vhdcp->rx_mtx);
|
||||
|
||||
while(vq_has_descs(rvq)) {
|
||||
vq_getchain(rvq, &idx, &iov, 1, NULL);
|
||||
|
||||
msg = (struct SocketData*)(iov.iov_base);
|
||||
|
||||
ret = performMessageTransaction(vhdcp->fd, *msg);
|
||||
if (ret < 0)
|
||||
{
|
||||
close(vhdcp->fd);
|
||||
vhdcp->fd = -1;
|
||||
}
|
||||
|
||||
/* release this chain and handle more */
|
||||
vq_relchain(rvq, idx, sizeof(struct SocketData));
|
||||
}
|
||||
|
||||
vq_endchains(rvq, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
connect_hdcp_daemon()
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int fd;
|
||||
int ret;
|
||||
|
||||
fd = socket(PF_LOCAL, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
WPRINTF(("socket error %d\n", errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, HDCP_SDK_SOCKET_PATH);
|
||||
|
||||
ret = connect(fd, &addr, sizeof(struct sockaddr_un));
|
||||
if (ret < 0) {
|
||||
WPRINTF(("connect error %d\n", errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void
|
||||
virtio_hdcp_notify(void *vdev, struct virtio_vq_info *vq)
|
||||
{
|
||||
struct virtio_hdcp *vhdcp = vdev;
|
||||
|
||||
/* Any ring entries to process */
|
||||
if (!vq_has_descs(vq))
|
||||
return;
|
||||
|
||||
vhdcp->fd = (vhdcp->fd < 0) ? connect_hdcp_daemon() : vhdcp->fd;
|
||||
if (vhdcp->fd < 0)
|
||||
{
|
||||
WPRINTF(("Invalid HDCP daemon file descriptor\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Signal the thread for processing */
|
||||
pthread_mutex_lock(&vhdcp->rx_mtx);
|
||||
if (vhdcp->in_progress == 0)
|
||||
pthread_cond_signal(&vhdcp->rx_cond);
|
||||
pthread_mutex_unlock(&vhdcp->rx_mtx);
|
||||
}
|
||||
|
||||
static int
|
||||
virtio_hdcp_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
|
||||
{
|
||||
struct virtio_hdcp *vhdcp;
|
||||
|
||||
pthread_mutexattr_t attr;
|
||||
char tname[MAXCOMLEN + 1];
|
||||
int rc;
|
||||
|
||||
vhdcp = calloc(1, sizeof(struct virtio_hdcp));
|
||||
if (!vhdcp) {
|
||||
WPRINTF(("vhdcp init: fail to alloc virtio_hdcp\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(("vhdcp init: mutexattr init fail, erro %d\n", rc));
|
||||
rc = pthread_mutexattr_settype(&attr, mutexattr_type);
|
||||
if (rc)
|
||||
WPRINTF(("vhdcp init: mutexattr_settype fail, erro %d\n", rc));
|
||||
rc = pthread_mutex_init(&vhdcp->mtx, &attr);
|
||||
if (rc)
|
||||
WPRINTF(("vhdcp init: mutexattr_settype fail, erro %d\n", rc));
|
||||
|
||||
DPRINTF(("vhdcp init: using VBS-U...\n"));
|
||||
virtio_linkup(&vhdcp->base, &virtio_hdcp_ops,
|
||||
vhdcp, dev, vhdcp->queues);
|
||||
vhdcp->base.mtx = &vhdcp->mtx;
|
||||
|
||||
vhdcp->queues[0].qsize = VIRTIO_HDCP_RINGSZ;
|
||||
vhdcp->queues[0].notify = virtio_hdcp_notify;
|
||||
|
||||
/* initialize config space */
|
||||
pci_set_cfgdata16(dev, PCIR_DEVICE, VIRTIO_DEV_HDCP);
|
||||
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_HDCP);
|
||||
pci_set_cfgdata16(dev, PCIR_SUBVEND_0, INTEL_VENDOR_ID);
|
||||
|
||||
if (virtio_interrupt_init(&vhdcp->base, virtio_uses_msix())) {
|
||||
WPRINTF(("vhdcp init: interrupt init fail\n"));
|
||||
free(vhdcp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtio_set_io_bar(&vhdcp->base, 0);
|
||||
|
||||
/*
|
||||
* connect to hdcp daemon in init phase
|
||||
*
|
||||
* @FIXME if failed connecting to HDCP daemon, the return value should
|
||||
* be set appropriately for SOS not exposing the HDCP PCI device to UOS
|
||||
*/
|
||||
vhdcp->fd = connect_hdcp_daemon();
|
||||
if (vhdcp->fd < 0) {
|
||||
WPRINTF(("connection to server failed\n"));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
vhdcp->in_progress = 0;
|
||||
pthread_mutex_init(&vhdcp->rx_mtx, NULL);
|
||||
pthread_cond_init(&vhdcp->rx_cond, NULL);
|
||||
pthread_create(&vhdcp->rx_tid, NULL,
|
||||
virtio_hdcp_talk_to_daemon, (void *)vhdcp);
|
||||
snprintf(tname, sizeof(tname), "vthdcp-%d:%d tx",
|
||||
dev->slot, dev->func);
|
||||
pthread_setname_np(vhdcp->rx_tid, tname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
virtio_hdcp_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
|
||||
{
|
||||
struct virtio_hdcp *vhdcp = (struct virtio_hdcp *)dev->arg;
|
||||
|
||||
if (vhdcp) {
|
||||
DPRINTF(("free struct virtio_hdcp\n"));
|
||||
free(vhdcp);
|
||||
}
|
||||
}
|
||||
|
||||
struct pci_vdev_ops pci_ops_virtio_hdcp = {
|
||||
.class_name = "virtio-hdcp",
|
||||
.vdev_init = virtio_hdcp_init,
|
||||
.vdev_deinit = virtio_hdcp_deinit,
|
||||
.vdev_barwrite = virtio_pci_write,
|
||||
.vdev_barread = virtio_pci_read
|
||||
};
|
||||
|
||||
DEFINE_PCI_DEVTYPE(pci_ops_virtio_hdcp);
|
Loading…
Reference in New Issue
Block a user