diff --git a/devicemodel/hw/pci/virtio/virtio_mei.c b/devicemodel/hw/pci/virtio/virtio_mei.c index 7513517a5..1399bcf81 100644 --- a/devicemodel/hw/pci/virtio/virtio_mei.c +++ b/devicemodel/hw/pci/virtio/virtio_mei.c @@ -153,6 +153,57 @@ refcnt_put(const struct refcnt *ref) ref->destroy(ref); } +static int vmei_debug; +static FILE *vmei_dbg_file; + +#define DPRINTF(format, arg...) do { \ + if (vmei_debug && vmei_dbg_file) { \ + fprintf(vmei_dbg_file, "vmei: %s: " format, \ + __func__, ##arg); \ + fflush(vmei_dbg_file); \ + } \ +} while (0) + +#define WPRINTF(format, arg...) do { \ + fprintf(stderr, "vmei: %s: " format, __func__, ##arg); \ + if (vmei_dbg_file) { \ + fprintf(vmei_dbg_file, "vmei: %s: " format, \ + __func__, ##arg); \ + fflush(vmei_dbg_file); \ + } \ +} while (0) + +#define MEI_HCL_FMT(format) "CL:[%02d:%02d](%04d): " format +#define MEI_HCL_PRM(_hcl) (_hcl)->host_addr, (_hcl)->me_addr, (_hcl)->client_fd +#define HCL_DBG(_hcl, format, arg...) \ + DPRINTF(MEI_HCL_FMT(format), MEI_HCL_PRM(_hcl), ##arg) +#define HCL_WARN(_hcl, format, arg...) \ + WPRINTF(MEI_HCL_FMT(format), MEI_HCL_PRM(_hcl), ##arg) + +static void vmei_dbg_print_hex(const char *title, + const void *data, size_t length) +{ + const unsigned char *bytes = data; + FILE *dbg_file; + size_t i; + + if (vmei_debug < 2) + return; + + dbg_file = (vmei_dbg_file) ? vmei_dbg_file : stdout; + + if (title) + fprintf(dbg_file, "%s ", title); + + for (i = 0; i < length; i++) { + if (i % 16 == 0 && i != 0) + fprintf(dbg_file, "\n"); + fprintf(dbg_file, "%02x ", bytes[i]); + } + fprintf(dbg_file, "\n"); +} + + #define MEI_FW_STATUS_MAX 6 #define VMEI_VQ_NUM 2 @@ -179,6 +230,369 @@ struct mei_virtio_cfg { uint32_t fw_status[MEI_FW_STATUS_MAX]; } __attribute__((packed)); +#define VMEI_IOBUFS_MAX 8 +struct vmei_circular_iobufs { + struct iovec bufs[VMEI_IOBUFS_MAX]; + uint8_t complete[VMEI_IOBUFS_MAX]; + size_t buf_sz; + uint8_t i_idx; /* insert index */ + uint8_t r_idx; /* remove index */ +}; + +struct virtio_mei; + +struct vmei_host_client { + struct refcnt ref; + struct vmei_me_client *mclient; + LIST_ENTRY(vmei_host_client) list; + + uint8_t me_addr; + uint8_t host_addr; + uint8_t reserved[2]; + int client_fd; + + struct mevent *rx_mevp; + uint8_t *recv_buf; + size_t recv_buf_sz; + int recv_offset; + int recv_handled; + int recv_creds; + + struct vmei_circular_iobufs send_bufs; +}; + +struct vmei_me_client { + struct refcnt ref; + struct virtio_mei *vmei; + LIST_ENTRY(vmei_me_client) list; + + uint8_t client_id; + struct mei_client_properties props; + pthread_mutex_t list_mutex; + LIST_HEAD(connhead, vmei_host_client) connections; +}; + +enum vmei_status { + VMEI_STS_READY, + VMEI_STS_PENDING_RESET, + VMEI_STS_RESET, + VMEI_STST_DEINIT +}; + +struct virtio_mei { + struct virtio_base base; + char name[16]; + char devnode[32]; + struct mei_enumerate_me_clients me_clients_map; + struct virtio_vq_info vqs[VMEI_VQ_NUM]; + volatile enum vmei_status status; + uint8_t vtag; + + pthread_mutex_t mutex; + + struct mevent *reset_mevp; + + struct mei_virtio_cfg *config; + + pthread_t tx_thread; + pthread_mutex_t tx_mutex; + pthread_cond_t tx_cond; + + pthread_t rx_thread; + pthread_mutex_t rx_mutex; + pthread_cond_t rx_cond; + bool rx_need_sched; + + pthread_mutex_t list_mutex; + LIST_HEAD(clhead, vmei_me_client) active_clients; + struct vmei_me_client *hbm_client; + int hbm_fd; +}; + +static inline void +vmei_set_status(struct virtio_mei *vmei, enum vmei_status status) +{ + if (status == VMEI_STST_DEINIT || + status == VMEI_STS_READY || + status > vmei->status) + vmei->status = status; +} + +static void +vmei_host_client_destroy(const struct refcnt *ref) +{ + struct vmei_host_client *hclient; + struct vmei_me_client *mclient; + unsigned int i; + + hclient = container_of(ref, struct vmei_host_client, ref); + + mclient = hclient->mclient; + + pthread_mutex_lock(&mclient->list_mutex); + LIST_REMOVE(hclient, list); + pthread_mutex_unlock(&mclient->list_mutex); + + if (hclient->rx_mevp) + mevent_delete(hclient->rx_mevp); + if (hclient->client_fd > -1) + close(hclient->client_fd); + for (i = 0; i < VMEI_IOBUFS_MAX; i++) + free(hclient->send_bufs.bufs[i].iov_base); + free(hclient->recv_buf); + free(hclient); +} + +static struct vmei_host_client * +vmei_host_client_create(struct vmei_me_client *mclient, uint8_t host_addr) +{ + struct vmei_host_client *hclient; + size_t size = mclient->props.max_msg_length; + unsigned int i; + + hclient = calloc(1, sizeof(*hclient)); + if (!hclient) + return NULL; + + hclient->ref = (struct refcnt){vmei_host_client_destroy, 1}; + + hclient->me_addr = mclient->client_id; + hclient->host_addr = host_addr; + hclient->mclient = mclient; + hclient->client_fd = -1; + hclient->rx_mevp = NULL; + + /* HBM and fixed address doesn't provide flow control + * make the receiving part always available. + */ + if (host_addr == 0) + hclient->recv_creds = 1; + + /* setup send_buf and recv_buf for the client */ + for (i = 0; i < VMEI_IOBUFS_MAX; i++) { + hclient->send_bufs.bufs[i].iov_base = calloc(1, size); + if (!hclient->send_bufs.bufs[i].iov_base) { + HCL_WARN(hclient, "allocation failed\n"); + goto fail; + } + } + + hclient->recv_buf = calloc(1, size); + if (!hclient->recv_buf) { + HCL_WARN(hclient, "allocation failed\n"); + goto fail; + } + hclient->send_bufs.buf_sz = size; + hclient->recv_buf_sz = size; + + pthread_mutex_lock(&mclient->list_mutex); + LIST_INSERT_HEAD(&mclient->connections, hclient, list); + pthread_mutex_unlock(&mclient->list_mutex); + + return hclient; + +fail: + for (i = 0; i < VMEI_IOBUFS_MAX; i++) + free(hclient->send_bufs.bufs[i].iov_base); + memset(&hclient->send_bufs, 0, sizeof(hclient->send_bufs)); + free(hclient->recv_buf); + free(hclient); + + return NULL; +} + +static struct vmei_host_client* +vmei_host_client_get(struct vmei_host_client *hclient) +{ + refcnt_get(&hclient->ref); + return hclient; +} + +static void +vmei_host_client_put(struct vmei_host_client *hclient) +{ + refcnt_put(&hclient->ref); +} + +struct virtio_mei * +vmei_host_client_to_vmei(struct vmei_host_client *hclient) +{ + if (hclient && hclient->mclient) + return hclient->mclient->vmei; + + return NULL; +} + +static void +vmei_del_me_client(struct vmei_me_client *mclient) +{ + struct virtio_mei *vmei = mclient->vmei; + + pthread_mutex_lock(&vmei->list_mutex); + LIST_REMOVE(mclient, list); + pthread_mutex_unlock(&vmei->list_mutex); +} + +static void +vmei_me_client_destroy_host_clients(struct vmei_me_client *mclient) +{ + struct vmei_host_client *e, *n; + + pthread_mutex_lock(&mclient->list_mutex); + e = LIST_FIRST(&mclient->connections); + while (e) { + n = LIST_NEXT(e, list); + vmei_host_client_put(e); + e = n; + } + pthread_mutex_unlock(&mclient->list_mutex); + LIST_INIT(&mclient->connections); +} + +static void +vmei_me_client_destroy(const struct refcnt *ref) +{ + struct vmei_me_client *mclient; + + mclient = container_of(ref, struct vmei_me_client, ref); + + vmei_del_me_client(mclient); + vmei_me_client_destroy_host_clients(mclient); + pthread_mutex_destroy(&mclient->list_mutex); + free(mclient); +} + +static struct vmei_me_client * +vmei_me_client_create(struct virtio_mei *vmei, uint8_t client_id, + const struct mei_client_properties *props) +{ + struct vmei_me_client *mclient; + pthread_mutexattr_t attr; + + if (!props) + return NULL; + + mclient = calloc(1, sizeof(*mclient)); + if (!mclient) + return mclient; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mclient->list_mutex, &attr); + pthread_mutexattr_destroy(&attr); + + mclient->vmei = vmei; + mclient->client_id = client_id; + mclient->ref = (struct refcnt){vmei_me_client_destroy, 1}; + + memcpy(&mclient->props, props, sizeof(*props)); + vmei_dbg_client_properties(&mclient->props); + + DPRINTF("ME client created %d\n", client_id); + + return mclient; +} + +static struct vmei_me_client* +vmei_me_client_get(struct vmei_me_client *mclient) +{ + refcnt_get(&mclient->ref); + return mclient; +} + +static void +vmei_me_client_put(struct vmei_me_client *mclient) +{ + refcnt_put(&mclient->ref); +} + +static struct vmei_host_client* +vmei_me_client_get_host_client(struct vmei_me_client *mclient, + uint8_t host_addr) +{ + struct vmei_host_client *e, *hclient = NULL; + + pthread_mutex_lock(&mclient->list_mutex); + LIST_FOREACH(e, &mclient->connections, list) { + if (e->host_addr == host_addr) { + hclient = vmei_host_client_get(e); + break; + } + } + pthread_mutex_unlock(&mclient->list_mutex); + + return hclient; +} + +static void vmei_add_me_client(struct vmei_me_client *mclient) +{ + struct virtio_mei *vmei = mclient->vmei; + + pthread_mutex_lock(&vmei->list_mutex); + if (mclient->client_id == 0) + /* make sure hbm client at the head of the list */ + LIST_INSERT_HEAD(&vmei->active_clients, mclient, list); + else + LIST_INSERT_AFTER(LIST_FIRST(&vmei->active_clients), + mclient, list); + pthread_mutex_unlock(&vmei->list_mutex); +} + +static struct vmei_me_client * +vmei_find_me_client(struct virtio_mei *vmei, uint8_t client_id) +{ + struct vmei_me_client *e, *mclient = NULL; + + pthread_mutex_lock(&vmei->list_mutex); + LIST_FOREACH(e, &vmei->active_clients, list) { + if (e->client_id == client_id) { + mclient = vmei_me_client_get(e); + break; + } + } + pthread_mutex_unlock(&vmei->list_mutex); + return mclient; +} + + +static struct vmei_host_client* +vmei_find_host_client(struct virtio_mei *vmei, + uint8_t me_addr, uint8_t host_addr) +{ + struct vmei_me_client *mclient; + struct vmei_host_client *hclient; + + mclient = vmei_find_me_client(vmei, me_addr); + if (!mclient) { + DPRINTF("client with me address %d was not found\n", me_addr); + return NULL; + } + + hclient = vmei_me_client_get_host_client(mclient, host_addr); + if (!hclient) + DPRINTF("host client %d does not exists!\n", host_addr); + + vmei_me_client_put(mclient); + + return hclient; +} + +static void vmei_free_me_clients(struct virtio_mei *vmei) +{ + struct vmei_me_client *e, *n; + + pthread_mutex_lock(&vmei->list_mutex); + e = LIST_FIRST(&vmei->active_clients); + while (e) { + n = LIST_NEXT(e, list); + vmei_me_client_put(e); + e = n; + } + LIST_INIT(&vmei->active_clients); + pthread_mutex_unlock(&vmei->list_mutex); +} + + static int mei_sysfs_read_property_file(const char *fname, char *buf, size_t sz) { int fd;