/*- * Copyright (c) 2018 VMware, Inc. * * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0) */ /* This file implements the VMCI Simple Datagram API on the host. */ #include __FBSDID("$FreeBSD$"); #include #include #include "vmci_datagram.h" #include "vmci_driver.h" #include "vmci_kernel_api.h" #include "vmci_kernel_defs.h" #include "vmci_resource.h" #define LGPFX "vmci_datagram: " /* * datagram_entry describes the datagram entity. It is used for datagram * entities created only on the host. */ struct datagram_entry { struct vmci_resource resource; uint32_t flags; bool run_delayed; vmci_datagram_recv_cb recv_cb; void *client_data; vmci_event destroy_event; vmci_privilege_flags priv_flags; }; struct vmci_delayed_datagram_info { struct datagram_entry *entry; struct vmci_datagram msg; }; static int vmci_datagram_get_priv_flags_int(vmci_id contextID, struct vmci_handle handle, vmci_privilege_flags *priv_flags); static void datagram_free_cb(void *resource); static int datagram_release_cb(void *client_data); /*------------------------------ Helper functions ----------------------------*/ /* *------------------------------------------------------------------------------ * * datagram_free_cb -- * * Callback to free datagram structure when resource is no longer used, * ie. the reference count reached 0. * * Result: * None. * * Side effects: * None. * *------------------------------------------------------------------------------ */ static void datagram_free_cb(void *client_data) { struct datagram_entry *entry = (struct datagram_entry *)client_data; ASSERT(entry); vmci_signal_event(&entry->destroy_event); /* * The entry is freed in vmci_datagram_destroy_hnd, who is waiting for * the above signal. */ } /* *------------------------------------------------------------------------------ * * datagram_release_cb -- * * Callback to release the resource reference. It is called by the * vmci_wait_on_event function before it blocks. * * Result: * None. * * Side effects: * None. * *------------------------------------------------------------------------------ */ static int datagram_release_cb(void *client_data) { struct datagram_entry *entry; entry = (struct datagram_entry *)client_data; ASSERT(entry); vmci_resource_release(&entry->resource); return (0); } /* *------------------------------------------------------------------------------ * * datagram_create_hnd -- * * Internal function to create a datagram entry given a handle. * * Results: * VMCI_SUCCESS if created, negative errno value otherwise. * * Side effects: * None. * *------------------------------------------------------------------------------ */ static int datagram_create_hnd(vmci_id resource_id, uint32_t flags, vmci_privilege_flags priv_flags, vmci_datagram_recv_cb recv_cb, void *client_data, struct vmci_handle *out_handle) { struct datagram_entry *entry; struct vmci_handle handle; vmci_id context_id; int result; ASSERT(recv_cb != NULL); ASSERT(out_handle != NULL); ASSERT(!(priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS)); if ((flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) return (VMCI_ERROR_INVALID_ARGS); else { if ((flags & VMCI_FLAG_ANYCID_DG_HND) != 0) context_id = VMCI_INVALID_ID; else { context_id = vmci_get_context_id(); if (context_id == VMCI_INVALID_ID) return (VMCI_ERROR_NO_RESOURCES); } if (resource_id == VMCI_INVALID_ID) { resource_id = vmci_resource_get_id(context_id); if (resource_id == VMCI_INVALID_ID) return (VMCI_ERROR_NO_HANDLE); } handle = VMCI_MAKE_HANDLE(context_id, resource_id); } entry = vmci_alloc_kernel_mem(sizeof(*entry), VMCI_MEMORY_NORMAL); if (entry == NULL) { VMCI_LOG_WARNING(LGPFX"Failed allocating memory for datagram " "entry.\n"); return (VMCI_ERROR_NO_MEM); } if (!vmci_can_schedule_delayed_work()) { if (flags & VMCI_FLAG_DG_DELAYED_CB) { vmci_free_kernel_mem(entry, sizeof(*entry)); return (VMCI_ERROR_INVALID_ARGS); } entry->run_delayed = false; } else entry->run_delayed = (flags & VMCI_FLAG_DG_DELAYED_CB) ? true : false; entry->flags = flags; entry->recv_cb = recv_cb; entry->client_data = client_data; vmci_create_event(&entry->destroy_event); entry->priv_flags = priv_flags; /* Make datagram resource live. */ result = vmci_resource_add(&entry->resource, VMCI_RESOURCE_TYPE_DATAGRAM, handle, datagram_free_cb, entry); if (result != VMCI_SUCCESS) { VMCI_LOG_WARNING(LGPFX"Failed to add new resource " "(handle=0x%x:0x%x).\n", handle.context, handle.resource); vmci_destroy_event(&entry->destroy_event); vmci_free_kernel_mem(entry, sizeof(*entry)); return (result); } *out_handle = handle; return (VMCI_SUCCESS); } /*------------------------------ Public API functions ------------------------*/ /* *------------------------------------------------------------------------------ * * vmci_datagram_create_handle -- * * Creates a host context datagram endpoint and returns a handle to it. * * Results: * VMCI_SUCCESS if created, negative errno value otherwise. * * Side effects: * None. * *------------------------------------------------------------------------------ */ int vmci_datagram_create_handle(vmci_id resource_id, uint32_t flags, vmci_datagram_recv_cb recv_cb, void *client_data, struct vmci_handle *out_handle) { if (out_handle == NULL) return (VMCI_ERROR_INVALID_ARGS); if (recv_cb == NULL) { VMCI_LOG_DEBUG(LGPFX"Client callback needed when creating " "datagram.\n"); return (VMCI_ERROR_INVALID_ARGS); } return (datagram_create_hnd(resource_id, flags, VMCI_DEFAULT_PROC_PRIVILEGE_FLAGS, recv_cb, client_data, out_handle)); } /* *------------------------------------------------------------------------------ * * vmci_datagram_create_handle_priv -- * * Creates a host context datagram endpoint and returns a handle to it. * * Results: * VMCI_SUCCESS if created, negative errno value otherwise. * * Side effects: * None. * *------------------------------------------------------------------------------ */ int vmci_datagram_create_handle_priv(vmci_id resource_id, uint32_t flags, vmci_privilege_flags priv_flags, vmci_datagram_recv_cb recv_cb, void *client_data, struct vmci_handle *out_handle) { if (out_handle == NULL) return (VMCI_ERROR_INVALID_ARGS); if (recv_cb == NULL) { VMCI_LOG_DEBUG(LGPFX"Client callback needed when creating " "datagram.\n"); return (VMCI_ERROR_INVALID_ARGS); } if (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS) return (VMCI_ERROR_INVALID_ARGS); return (datagram_create_hnd(resource_id, flags, priv_flags, recv_cb, client_data, out_handle)); } /* *------------------------------------------------------------------------------ * * vmci_datagram_destroy_handle -- * * Destroys a handle. * * Results: * None. * * Side effects: * None. * *------------------------------------------------------------------------------ */ int vmci_datagram_destroy_handle(struct vmci_handle handle) { struct datagram_entry *entry; struct vmci_resource *resource; resource = vmci_resource_get(handle, VMCI_RESOURCE_TYPE_DATAGRAM); if (resource == NULL) { VMCI_LOG_DEBUG(LGPFX"Failed to destroy datagram " "(handle=0x%x:0x%x).\n", handle.context, handle.resource); return (VMCI_ERROR_NOT_FOUND); } entry = RESOURCE_CONTAINER(resource, struct datagram_entry, resource); vmci_resource_remove(handle, VMCI_RESOURCE_TYPE_DATAGRAM); /* * We now wait on the destroyEvent and release the reference we got * above. */ vmci_wait_on_event(&entry->destroy_event, datagram_release_cb, entry); /* * We know that we are now the only reference to the above entry so * can safely free it. */ vmci_destroy_event(&entry->destroy_event); vmci_free_kernel_mem(entry, sizeof(*entry)); return (VMCI_SUCCESS); } /* *------------------------------------------------------------------------------ * * vmci_datagram_get_priv_flags_int -- * * Internal utilility function with the same purpose as * vmci_datagram_get_priv_flags that also takes a context_id. * * Result: * VMCI_SUCCESS on success, VMCI_ERROR_INVALID_ARGS if handle is invalid. * * Side effects: * None. * *------------------------------------------------------------------------------ */ static int vmci_datagram_get_priv_flags_int(vmci_id context_id, struct vmci_handle handle, vmci_privilege_flags *priv_flags) { ASSERT(priv_flags); ASSERT(context_id != VMCI_INVALID_ID); if (context_id == VMCI_HOST_CONTEXT_ID) { struct datagram_entry *src_entry; struct vmci_resource *resource; resource = vmci_resource_get(handle, VMCI_RESOURCE_TYPE_DATAGRAM); if (resource == NULL) return (VMCI_ERROR_INVALID_ARGS); src_entry = RESOURCE_CONTAINER(resource, struct datagram_entry, resource); *priv_flags = src_entry->priv_flags; vmci_resource_release(resource); } else if (context_id == VMCI_HYPERVISOR_CONTEXT_ID) *priv_flags = VMCI_MAX_PRIVILEGE_FLAGS; else *priv_flags = VMCI_NO_PRIVILEGE_FLAGS; return (VMCI_SUCCESS); } /* *------------------------------------------------------------------------------ * * vmci_datagram_fet_priv_flags -- * * Utility function that retrieves the privilege flags associated with a * given datagram handle. For hypervisor and guest endpoints, the * privileges are determined by the context ID, but for host endpoints * privileges are associated with the complete handle. * * Result: * VMCI_SUCCESS on success, VMCI_ERROR_INVALID_ARGS if handle is invalid. * * Side effects: * None. * *------------------------------------------------------------------------------ */ int vmci_datagram_get_priv_flags(struct vmci_handle handle, vmci_privilege_flags *priv_flags) { if (priv_flags == NULL || handle.context == VMCI_INVALID_ID) return (VMCI_ERROR_INVALID_ARGS); return (vmci_datagram_get_priv_flags_int(handle.context, handle, priv_flags)); } /* *------------------------------------------------------------------------------ * * vmci_datagram_delayed_dispatch_cb -- * * Calls the specified callback in a delayed context. * * Results: * None. * * Side effects: * None. * *------------------------------------------------------------------------------ */ static void vmci_datagram_delayed_dispatch_cb(void *data) { struct vmci_delayed_datagram_info *dg_info; dg_info = (struct vmci_delayed_datagram_info *)data; ASSERT(data); dg_info->entry->recv_cb(dg_info->entry->client_data, &dg_info->msg); vmci_resource_release(&dg_info->entry->resource); vmci_free_kernel_mem(dg_info, sizeof(*dg_info) + (size_t)dg_info->msg.payload_size); } /* *------------------------------------------------------------------------------ * * vmci_datagram_dispatch_as_guest -- * * Dispatch datagram as a guest, down through the VMX and potentially to * the host. * * Result: * Number of bytes sent on success, appropriate error code otherwise. * * Side effects: * None. * *------------------------------------------------------------------------------ */ static int vmci_datagram_dispatch_as_guest(struct vmci_datagram *dg) { struct vmci_resource *resource; int retval; resource = vmci_resource_get(dg->src, VMCI_RESOURCE_TYPE_DATAGRAM); if (NULL == resource) return VMCI_ERROR_NO_HANDLE; retval = vmci_send_datagram(dg); vmci_resource_release(resource); return (retval); } /* *------------------------------------------------------------------------------ * * vmci_datagram_dispatch -- * * Dispatch datagram. This will determine the routing for the datagram and * dispatch it accordingly. * * Result: * Number of bytes sent on success, appropriate error code otherwise. * * Side effects: * None. * *------------------------------------------------------------------------------ */ int vmci_datagram_dispatch(vmci_id context_id, struct vmci_datagram *dg) { ASSERT(dg); ASSERT_ON_COMPILE(sizeof(struct vmci_datagram) == 24); if (VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) { VMCI_LOG_DEBUG(LGPFX"Payload (size=%lu bytes) too big to send." "\n", dg->payload_size); return (VMCI_ERROR_INVALID_ARGS); } return (vmci_datagram_dispatch_as_guest(dg)); } /* *------------------------------------------------------------------------------ * * vmci_datagram_invoke_guest_handler -- * * Invoke the handler for the given datagram. This is intended to be called * only when acting as a guest and receiving a datagram from the virtual * device. * * Result: * VMCI_SUCCESS on success, other error values on failure. * * Side effects: * None. * *------------------------------------------------------------------------------ */ int vmci_datagram_invoke_guest_handler(struct vmci_datagram *dg) { struct datagram_entry *dst_entry; struct vmci_resource *resource; int retval; ASSERT(dg); if (dg->payload_size > VMCI_MAX_DG_PAYLOAD_SIZE) { VMCI_LOG_DEBUG(LGPFX"Payload (size=%lu bytes) too large to " "deliver.\n", dg->payload_size); return (VMCI_ERROR_PAYLOAD_TOO_LARGE); } resource = vmci_resource_get(dg->dst, VMCI_RESOURCE_TYPE_DATAGRAM); if (NULL == resource) { VMCI_LOG_DEBUG(LGPFX"destination (handle=0x%x:0x%x) doesn't " "exist.\n", dg->dst.context, dg->dst.resource); return (VMCI_ERROR_NO_HANDLE); } dst_entry = RESOURCE_CONTAINER(resource, struct datagram_entry, resource); if (dst_entry->run_delayed) { struct vmci_delayed_datagram_info *dg_info; dg_info = vmci_alloc_kernel_mem(sizeof(*dg_info) + (size_t)dg->payload_size, VMCI_MEMORY_ATOMIC); if (NULL == dg_info) { vmci_resource_release(resource); retval = VMCI_ERROR_NO_MEM; goto exit; } dg_info->entry = dst_entry; memcpy(&dg_info->msg, dg, VMCI_DG_SIZE(dg)); retval = vmci_schedule_delayed_work( vmci_datagram_delayed_dispatch_cb, dg_info); if (retval < VMCI_SUCCESS) { VMCI_LOG_WARNING(LGPFX"Failed to schedule delayed " "work for datagram (result=%d).\n", retval); vmci_free_kernel_mem(dg_info, sizeof(*dg_info) + (size_t)dg->payload_size); vmci_resource_release(resource); dg_info = NULL; goto exit; } } else { dst_entry->recv_cb(dst_entry->client_data, dg); vmci_resource_release(resource); retval = VMCI_SUCCESS; } exit: return (retval); } /* *------------------------------------------------------------------------------ * * vmci_datagram_send -- * * Sends the payload to the destination datagram handle. * * Results: * Returns number of bytes sent if success, or error code if failure. * * Side effects: * None. * *------------------------------------------------------------------------------ */ int vmci_datagram_send(struct vmci_datagram *msg) { if (msg == NULL) return (VMCI_ERROR_INVALID_ARGS); return (vmci_datagram_dispatch(VMCI_INVALID_ID, msg)); } /* *------------------------------------------------------------------------------ * * vmci_datagram_sync -- * * Use this as a synchronization point when setting globals, for example, * during device shutdown. * * Results: * None. * * Side effects: * None. * *------------------------------------------------------------------------------ */ void vmci_datagram_sync(void) { vmci_resource_sync(); } /* *------------------------------------------------------------------------------ * * vmci_datagram_check_host_capabilities -- * * Verify that the host supports the resources we need. None are required * for datagrams since they are implicitly supported. * * Results: * true. * * Side effects: * None. * *------------------------------------------------------------------------------ */ bool vmci_datagram_check_host_capabilities(void) { return (true); }