virtio-heci: Add ME client mapping in backend service

ME client mapping is for tracking ME clients operations between frontend
driver and backend service. It contains buffers for send/recv packing
and forwarding.

Signed-off-by: Liu Shuo <shuo.a.liu@intel.com>
Reviewed-by: Li Hao <hao.l.li@intel.com>
Reviewed-by: Wang Yu <yu1.wang@intel.com>
Reviewed-by: Zhao, Yakui <yakui.zhao@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Liu Shuo 2018-04-02 13:33:02 +08:00 committed by Jack Ren
parent 23c3fbd485
commit 418c266bfc
2 changed files with 431 additions and 2 deletions

View File

@ -34,7 +34,10 @@
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <linux/mei.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@ -42,8 +45,10 @@
#include <pthread.h>
#include "dm.h"
#include "mevent.h"
#include "pci_core.h"
#include "virtio.h"
#include "heci.h"
#define VIRTIO_HECI_RXQ 0
#define VIRTIO_HECI_TXQ 1
@ -59,18 +64,69 @@
#define VIRTIO_HECI_RINGSZ 64
#define VIRTIO_HECI_VQNUM 2
#define HECI_NATIVE_DEVICE_NODE "/dev/mei0"
/*
* Different type has differnt emulation
*
* TYPE_HBM: to emulate HBM commands' request and response
* TYPE_NORAML: to emulate ME clients request and response
*/
enum heci_client_type {
TYPE_NORMAL,
TYPE_HBM,
TYPE_MAX
};
struct virtio_heci_config {
int buf_depth;
uint8_t hw_ready;
uint8_t host_reset;
} __attribute__((packed));
/*
* abstract virtio_heci_client for ME client and host client mapping
* client_fd - SOS kernel mei_me_client
* client_addr - UOS kernel FE driver mei_me_client
* client_id - native ME client's client_id, used acrossing native, BE and FE
* props - ME client's properties
*/
struct virtio_heci_client {
struct virtio_heci *vheci;
LIST_ENTRY(virtio_heci_client) list;
uuid_t client_uuid;
int client_fd;
int client_addr;
uint8_t client_id;
enum heci_client_type type;
struct heci_client_properties props;
struct mevent *rx_mevp;
uint8_t *recv_buf;
int recv_buf_sz;
int recv_offset;
int recv_handled;
int recv_creds;
uint8_t *send_buf;
int send_buf_sz;
int send_idx;
int ref;
};
struct virtio_heci {
struct virtio_base base;
struct virtio_vq_info vqs[VIRTIO_HECI_VQNUM];
pthread_mutex_t mutex;
struct mei_enumerate_me_clients me_clients_map;
struct virtio_heci_config *config;
pthread_mutex_t list_mutex;
LIST_HEAD(listhead, virtio_heci_client) active_clients;
struct virtio_heci_client *hbm_client;
};
/*
@ -81,10 +137,16 @@ static int virtio_heci_debug;
#define WPRINTF(params) (printf params)
static void virtio_heci_reset(void *);
static void virtio_heci_rx_callback(int fd, enum ev_type type, void *param);
static void virtio_heci_notify_rx(void *, struct virtio_vq_info *);
static void virtio_heci_notify_tx(void *, struct virtio_vq_info *);
static int virtio_heci_cfgread(void *, int, int, uint32_t *);
static int virtio_heci_cfgwrite(void *, int, int, uint32_t);
static struct virtio_heci_client *virtio_heci_create_client(
struct virtio_heci *vheci,
int client_id, int client_addr, int *status);
static void virtio_heci_destroy_client(struct virtio_heci *vheci,
struct virtio_heci_client *client);
static struct virtio_ops virtio_heci_ops = {
"virtio_heci", /* our name */
@ -99,6 +161,261 @@ static struct virtio_ops virtio_heci_ops = {
0, /* our capabilities */
};
static void
print_hex(uint8_t *bytes, int length)
{
int i;
if (!virtio_heci_debug)
return;
for (i = 0; i < length; i++) {
if (i % 16 == 0 && i != 0)
printf("\r\n");
printf("%02x ", bytes[i]);
}
printf("\r\n");
}
/*
* Convert client connect ioctl errno to client connection response code
* we might lost some exact response code because the errno of ioctl is rough
* However, FE driver will get same errno for UOS's userspace.
*/
static inline int
err_to_native_heci_resno(int err)
{
switch (err) {
case 0: return HECI_HBM_SUCCESS;
case -ENOTTY: return HECI_HBM_CLIENT_NOT_FOUND;
case -EBUSY: return HECI_HBM_ALREADY_STARTED;
case -EINVAL: return HECI_HBM_INVALID_PARAMETER;
default: return HECI_HBM_INVALID_PARAMETER;
}
}
static struct virtio_heci_client *
virtio_heci_client_get(struct virtio_heci_client *client)
{
if (__sync_fetch_and_add(&client->ref, 1) == 0)
return NULL;
return client;
}
static void
virtio_heci_client_put(struct virtio_heci *vheci,
struct virtio_heci_client *client)
{
assert(client->ref > 0);
if (__sync_sub_and_fetch(&client->ref, 1) == 0)
virtio_heci_destroy_client(vheci, client);
}
static void
virtio_heci_add_client(struct virtio_heci *vheci,
struct virtio_heci_client *client)
{
pthread_mutex_lock(&vheci->list_mutex);
LIST_INSERT_HEAD(&vheci->active_clients, client, list);
pthread_mutex_unlock(&vheci->list_mutex);
}
static void
virtio_heci_del_client(struct virtio_heci *vheci,
struct virtio_heci_client *client)
{
pthread_mutex_lock(&vheci->list_mutex);
LIST_REMOVE(client, list);
pthread_mutex_unlock(&vheci->list_mutex);
}
static struct virtio_heci_client *
virtio_heci_create_client(struct virtio_heci *vheci, int client_id,
int client_addr, int *status)
{
struct virtio_heci_client *client;
struct mei_connect_client_data connect;
struct mei_request_client_params params_req;
struct heci_client_properties *props;
int ret = 0;
int size = VIRTIO_HECI_FIFOSZ * sizeof(struct heci_msg_hdr);
client = calloc(1, sizeof(struct virtio_heci_client));
if (!client)
return client;
client->vheci = vheci;
client->client_id = client_id;
client->client_addr = client_addr;
/* open mei node */
client->client_fd = open(HECI_NATIVE_DEVICE_NODE, O_RDWR);
if (client->client_fd < 0) {
DPRINTF(("vheci: create_client: open mei failed!\r\n"));
goto open_fail;
}
if (client_id == 0 && client_addr == 0) {
/* TYPE_HBM */
/* HBM don't need get prop
* but we get clients_map here
*/
ret = ioctl(client->client_fd, IOCTL_MEI_ENUMERATE_ME_CLIENTS,
&vheci->me_clients_map);
if (ret < 0)
goto connect_fail;
print_hex((uint8_t *)&vheci->me_clients_map,
sizeof(vheci->me_clients_map));
/* setup send_buf and recv_buf for client 0 */
client->send_buf = calloc(1, size);
client->recv_buf = calloc(1, size);
if (!client->send_buf || !client->recv_buf) {
DPRINTF(("vheci: hbm bufs calloc fail!\r\n"));
goto alloc_buf_fail;
}
client->recv_buf_sz = size;
client->send_buf_sz = size;
client->type = TYPE_HBM;
} else {
/* get this client's props */
params_req.client_id = client_id;
ret = ioctl(client->client_fd,
IOCTL_MEI_REQUEST_CLIENT_PROP, &params_req);
if (ret < 0)
goto connect_fail;
props = (struct heci_client_properties *)params_req.data;
memcpy(&client->props, props, sizeof(client->props));
/* TYPE_NORMAL */
/* connect to ME client */
memcpy(&connect, &props->protocol_name,
sizeof(props->protocol_name));
ret = ioctl(client->client_fd,
IOCTL_MEI_CONNECT_CLIENT, &connect);
/* connecting might fail. */
if (ret) {
*status = err_to_native_heci_resno(ret);
DPRINTF(("vheci: client connect fail!\r\n"));
goto connect_fail;
}
size = client->props.max_msg_length;
/*
* setup send_buf and recv_buf,
* need before adding client fd to mevent
*/
client->send_buf = calloc(1, size);
client->recv_buf = calloc(1, size);
if (!client->send_buf || !client->recv_buf) {
DPRINTF(("vheci: client bufs calloc failed!\r\n"));
goto alloc_buf_fail;
}
client->recv_buf_sz = size;
client->send_buf_sz = size;
client->type = TYPE_NORMAL;
/* add READ event into mevent */
client->rx_mevp = mevent_add(client->client_fd, EVF_READ,
virtio_heci_rx_callback, client);
}
DPRINTF(("vheci: NEW client mapping: ME[%d]fd[%d]:FE[%d] "
"protocol_ver[%d]|max_connections[%d]|fixed_addr[%d]|"
"single_recv_buf[%d]|max_msg_len[%d]\n\r",
client_id, client->client_fd, client_addr,
client->props.protocol_version,
client->props.max_connections,
client->props.fixed_address,
client->props.single_recv_buf,
client->props.max_msg_length));
client->ref = 1;
virtio_heci_add_client(vheci, client);
return client;
alloc_buf_fail:
free(client->send_buf);
free(client->recv_buf);
connect_fail:
close(client->client_fd);
open_fail:
free(client);
return NULL;
}
static void
virtio_heci_destroy_client(struct virtio_heci *vheci,
struct virtio_heci_client *client)
{
if (!client || !vheci)
return;
DPRINTF(("vheci: Destroy client mapping: ME[%d]fd[%d]:FE[%d]\r\n",
client->client_id, client->client_fd,
client->client_addr));
virtio_heci_del_client(vheci, client);
if (client->type != TYPE_HBM)
mevent_delete_close(client->rx_mevp);
free(client->send_buf);
free(client->recv_buf);
free(client);
}
/*
* A completed read guarantees that a client message is completed,
* transmission of client message is started by a flow control message of HBM
*/
static int
native_heci_read(struct virtio_heci_client *client)
{
int len;
if (client->client_fd <= 0) {
DPRINTF(("vheci: write failed! invalid client_fd[%d]\r\n",
client->client_fd));
return -1;
}
/* read with max_message_length */
len = read(client->client_fd, client->recv_buf, client->recv_buf_sz);
if (len < 0) {
WPRINTF(("vheci: read failed! read error[%d]\r\n", errno));
return len;
}
DPRINTF(("vheci: RX: ME[%d]fd[%d] Append data(len[%d]) "
"at recv_buf(off[%d]).\n\r",
client->client_id, client->client_fd,
len, client->recv_offset));
client->recv_offset = len;
return client->recv_offset;
}
static void
virtio_heci_rx_callback(int fd, enum ev_type type, void *param)
{
struct virtio_heci_client *client = param;
struct virtio_heci *vheci = client->vheci;
int ret;
if (!virtio_heci_client_get(client)) {
DPRINTF(("vheci: client has been released, ignore data.\r\n"));
return;
}
/* read data from mei driver */
ret = native_heci_read(client);
if (ret < 0)
/* read failed, no data available */
goto out;
DPRINTF(("vheci: RX: ME[%d]fd[%d] read %d bytes from MEI.\r\n",
client->client_id, fd, ret));
out:
virtio_heci_client_put(vheci, client);
}
static void
virtio_heci_reset(void *vth)
{
@ -151,7 +468,7 @@ virtio_heci_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
struct virtio_heci *vheci;
pthread_mutexattr_t attr;
int i, rc;
int i, rc, status = 0;
vheci = calloc(1, sizeof(struct virtio_heci));
if (!vheci) {
@ -214,6 +531,21 @@ virtio_heci_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
goto setup_fail;
virtio_set_io_bar(&vheci->base, 0);
/*
* init clients
*/
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&vheci->list_mutex, &attr);
LIST_INIT(&vheci->active_clients);
/*
* create a client for HBM
*/
vheci->hbm_client = virtio_heci_create_client(vheci, 0, 0, &status);
if (!vheci->hbm_client)
goto fail;
return 0;
setup_fail:
pthread_mutex_destroy(&vheci->mutex);
@ -228,6 +560,8 @@ virtio_heci_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
struct virtio_heci *vheci = (struct virtio_heci *)dev->arg;
virtio_heci_client_put(vheci, vheci->hbm_client);
pthread_mutex_destroy(&vheci->list_mutex);
pthread_mutex_destroy(&vheci->mutex);
free(vheci->config);
free(vheci);

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _HECI_H_
#define _HECI_H_
#include <uuid/uuid.h>
/*
* enum heci_hbm_status - heci host bus messages return values
*
* @HECI_HBM_SUCCESS : status success
* @HECI_HBM_CLIENT_NOT_FOUND : client not found
* @HECI_HBM_ALREADY_EXISTS : connection already established
* @HECI_HBM_REJECTED : connection is rejected
* @HECI_HBM_INVALID_PARAMETER : invalid parameter
* @HECI_HBM_NOT_ALLOWED : operation not allowed
* @HECI_HBM_ALREADY_STARTED : system is already started
* @HECI_HBM_NOT_STARTED : system not started
*
*/
enum heci_hbm_status {
HECI_HBM_SUCCESS = 0,
HECI_HBM_CLIENT_NOT_FOUND = 1,
HECI_HBM_ALREADY_EXISTS = 2,
HECI_HBM_REJECTED = 3,
HECI_HBM_INVALID_PARAMETER = 4,
HECI_HBM_NOT_ALLOWED = 5,
HECI_HBM_ALREADY_STARTED = 6,
HECI_HBM_NOT_STARTED = 7,
HECI_HBM_MAX
};
struct mei_enumerate_me_clients {
uint8_t valid_addresses[32];
};
struct mei_request_client_params {
uint8_t client_id;
uint8_t reserved[3];
uint8_t data[64];
} __attribute__((packed));
struct heci_client_properties {
uuid_t protocol_name;
uint8_t protocol_version;
uint8_t max_connections;
uint8_t fixed_address;
uint8_t single_recv_buf;
uint32_t max_msg_length;
} __attribute__((packed));
/* message header is same in native and virtual */
struct heci_msg_hdr {
uint32_t me_addr:8;
uint32_t host_addr:8;
uint32_t length:9;
uint32_t reserved:5;
uint32_t internal:1;
uint32_t msg_complete:1;
} __attribute__((packed));
#define IOCTL_MEI_ENUMERATE_ME_CLIENTS \
_IOWR('H', 0x04, struct mei_enumerate_me_clients)
#define IOCTL_MEI_REQUEST_CLIENT_PROP \
_IOWR('H', 0x05, struct mei_request_client_params)
#endif /* _HECI_H_ */