#include <sys/cdefs.h>
#include "vmci.h"
#include "vmci_doorbell.h"
#include "vmci_driver.h"
#include "vmci_event.h"
#include "vmci_kernel_api.h"
#include "vmci_kernel_defs.h"
#include "vmci_resource.h"
#define LGPFX "vmci: "
#define VMCI_UTIL_NUM_RESOURCES 1
static vmci_id ctx_update_sub_id = VMCI_INVALID_ID;
static volatile int vm_context_id = VMCI_INVALID_ID;
static void
vmci_util_cid_update(vmci_id sub_id, struct vmci_event_data *event_data,
void *client_data)
{
struct vmci_event_payload_context *ev_payload;
ev_payload = vmci_event_data_payload(event_data);
if (sub_id != ctx_update_sub_id) {
VMCI_LOG_DEBUG(LGPFX"Invalid subscriber (ID=0x%x).\n", sub_id);
return;
}
if (event_data == NULL || ev_payload->context_id == VMCI_INVALID_ID) {
VMCI_LOG_DEBUG(LGPFX"Invalid event data.\n");
return;
}
VMCI_LOG_INFO(LGPFX"Updating context from (ID=0x%x) to (ID=0x%x) on "
"event (type=%d).\n", atomic_load_int(&vm_context_id),
ev_payload->context_id, event_data->event);
atomic_store_int(&vm_context_id, ev_payload->context_id);
}
void
vmci_util_init(void)
{
if (vmci_event_subscribe(VMCI_EVENT_CTX_ID_UPDATE,
vmci_util_cid_update, NULL, &ctx_update_sub_id) < VMCI_SUCCESS) {
VMCI_LOG_WARNING(LGPFX"Failed to subscribe to event "
"(type=%d).\n", VMCI_EVENT_CTX_ID_UPDATE);
}
}
void
vmci_util_exit(void)
{
if (vmci_event_unsubscribe(ctx_update_sub_id) < VMCI_SUCCESS)
VMCI_LOG_WARNING(LGPFX"Failed to unsubscribe to event "
"(type=%d) with subscriber (ID=0x%x).\n",
VMCI_EVENT_CTX_ID_UPDATE, ctx_update_sub_id);
}
static bool
vmci_util_check_host_capabilities(void)
{
struct vmci_resources_query_msg *msg;
struct vmci_datagram *check_msg;
int result;
uint32_t msg_size;
msg_size = sizeof(struct vmci_resources_query_hdr) +
VMCI_UTIL_NUM_RESOURCES * sizeof(vmci_resource);
check_msg = vmci_alloc_kernel_mem(msg_size, VMCI_MEMORY_NORMAL);
if (check_msg == NULL) {
VMCI_LOG_WARNING(LGPFX"Check host: Insufficient memory.\n");
return (false);
}
check_msg->dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
VMCI_RESOURCES_QUERY);
check_msg->src = VMCI_ANON_SRC_HANDLE;
check_msg->payload_size = msg_size - VMCI_DG_HEADERSIZE;
msg = (struct vmci_resources_query_msg *)VMCI_DG_PAYLOAD(check_msg);
msg->num_resources = VMCI_UTIL_NUM_RESOURCES;
msg->resources[0] = VMCI_GET_CONTEXT_ID;
result = vmci_send_datagram(check_msg);
vmci_free_kernel_mem(check_msg, msg_size);
return (result == 0x1);
}
bool
vmci_check_host_capabilities(void)
{
bool result;
result = vmci_event_check_host_capabilities();
result &= vmci_datagram_check_host_capabilities();
result &= vmci_util_check_host_capabilities();
if (!result) {
VMCI_LOG_WARNING(LGPFX"Host capability checked failed.\n");
} else
VMCI_LOG_DEBUG(LGPFX"Host capability check passed.\n");
return (result);
}
void
vmci_read_datagrams_from_port(vmci_io_handle io_handle, vmci_io_port dg_in_port,
uint8_t *dg_in_buffer, size_t dg_in_buffer_size)
{
struct vmci_datagram *dg;
size_t current_dg_in_buffer_size;
size_t remaining_bytes;
current_dg_in_buffer_size = PAGE_SIZE;
ASSERT(dg_in_buffer_size >= PAGE_SIZE);
vmci_read_port_bytes(io_handle, dg_in_port, dg_in_buffer,
current_dg_in_buffer_size);
dg = (struct vmci_datagram *)dg_in_buffer;
remaining_bytes = current_dg_in_buffer_size;
while (dg->dst.resource != VMCI_INVALID_ID ||
remaining_bytes > PAGE_SIZE) {
size_t dg_in_size;
if (dg->dst.resource == VMCI_INVALID_ID) {
ASSERT(remaining_bytes > PAGE_SIZE);
dg = (struct vmci_datagram *)ROUNDUP((uintptr_t)dg + 1,
PAGE_SIZE);
ASSERT((uint8_t *)dg < dg_in_buffer +
current_dg_in_buffer_size);
remaining_bytes = (size_t)(dg_in_buffer +
current_dg_in_buffer_size - (uint8_t *)dg);
continue;
}
dg_in_size = VMCI_DG_SIZE_ALIGNED(dg);
if (dg_in_size <= dg_in_buffer_size) {
int result;
if (dg_in_size > remaining_bytes) {
if (remaining_bytes !=
current_dg_in_buffer_size) {
memmove(dg_in_buffer, dg_in_buffer +
current_dg_in_buffer_size -
remaining_bytes,
remaining_bytes);
dg = (struct vmci_datagram *)
dg_in_buffer;
}
if (current_dg_in_buffer_size !=
dg_in_buffer_size)
current_dg_in_buffer_size =
dg_in_buffer_size;
vmci_read_port_bytes(io_handle, dg_in_port,
dg_in_buffer + remaining_bytes,
current_dg_in_buffer_size -
remaining_bytes);
}
if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID &&
dg->dst.resource == VMCI_EVENT_HANDLER)
result = vmci_event_dispatch(dg);
else
result =
vmci_datagram_invoke_guest_handler(dg);
if (result < VMCI_SUCCESS)
VMCI_LOG_DEBUG(LGPFX"Datagram with resource"
" (ID=0x%x) failed (err=%d).\n",
dg->dst.resource, result);
dg = (struct vmci_datagram *)((uint8_t *)dg +
dg_in_size);
} else {
size_t bytes_to_skip;
VMCI_LOG_DEBUG(LGPFX"Failed to receive datagram "
"(size=%zu bytes).\n", dg_in_size);
bytes_to_skip = dg_in_size - remaining_bytes;
if (current_dg_in_buffer_size != dg_in_buffer_size)
current_dg_in_buffer_size = dg_in_buffer_size;
for (;;) {
vmci_read_port_bytes(io_handle, dg_in_port,
dg_in_buffer, current_dg_in_buffer_size);
if (bytes_to_skip <=
current_dg_in_buffer_size)
break;
bytes_to_skip -= current_dg_in_buffer_size;
}
dg = (struct vmci_datagram *)(dg_in_buffer +
bytes_to_skip);
}
remaining_bytes = (size_t) (dg_in_buffer +
current_dg_in_buffer_size - (uint8_t *)dg);
if (remaining_bytes < VMCI_DG_HEADERSIZE) {
vmci_read_port_bytes(io_handle, dg_in_port,
dg_in_buffer, current_dg_in_buffer_size);
dg = (struct vmci_datagram *)dg_in_buffer;
remaining_bytes = current_dg_in_buffer_size;
}
}
}
vmci_id
vmci_get_context_id(void)
{
if (atomic_load_int(&vm_context_id) == VMCI_INVALID_ID) {
uint32_t result;
struct vmci_datagram get_cid_msg;
get_cid_msg.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
VMCI_GET_CONTEXT_ID);
get_cid_msg.src = VMCI_ANON_SRC_HANDLE;
get_cid_msg.payload_size = 0;
result = vmci_send_datagram(&get_cid_msg);
atomic_store_int(&vm_context_id, result);
}
return (atomic_load_int(&vm_context_id));
}
int
vmci_components_init(void)
{
int result;
result = vmci_resource_init();
if (result < VMCI_SUCCESS) {
VMCI_LOG_WARNING(LGPFX"Failed to initialize vmci_resource "
"(result=%d).\n", result);
goto error_exit;
}
result = vmci_event_init();
if (result < VMCI_SUCCESS) {
VMCI_LOG_WARNING(LGPFX"Failed to initialize vmci_event "
"(result=%d).\n", result);
goto resource_exit;
}
result = vmci_doorbell_init();
if (result < VMCI_SUCCESS) {
VMCI_LOG_WARNING(LGPFX"Failed to initialize vmci_doorbell "
"(result=%d).\n", result);
goto event_exit;
}
VMCI_LOG_DEBUG(LGPFX"components initialized.\n");
return (VMCI_SUCCESS);
event_exit:
vmci_event_exit();
resource_exit:
vmci_resource_exit();
error_exit:
return (result);
}
void
vmci_components_cleanup(void)
{
vmci_doorbell_exit();
vmci_event_exit();
vmci_resource_exit();
}