static int VMCIDatagramDispatchAsHost(VMCIId contextID, // IN: VMCIDatagram *dg) // IN: { int retval; size_t dgSize; VMCIPrivilegeFlags srcPrivFlags; ASSERT(dg); ASSERT(VMCI_HostPersonalityActive()); dgSize = VMCI_DG_SIZE(dg); if (contextID == VMCI_HOST_CONTEXT_ID && dg->dst.context == VMCI_HYPERVISOR_CONTEXT_ID) { VMCI_DEBUG_LOG(4, (LGPFX"Host cannot talk to hypervisor\n")); return VMCI_ERROR_DST_UNREACHABLE; } ASSERT(dg->dst.context != VMCI_HYPERVISOR_CONTEXT_ID); /* Chatty. */ // VMCI_DEBUG_LOG(10, (LGPFX"Sending from (handle=0x%x:0x%x) to " // "(handle=0x%x:0x%x) (size=%u bytes).\n", // dg->src.context, dg->src.resource, // dg->dst.context, dg->dst.resource, (uint32)dgSize)); /* * Check that source handle matches sending context. */ if (dg->src.context != contextID) { VMCI_DEBUG_LOG(4, (LGPFX"Sender context (ID=0x%x) is not owner of src " "datagram entry (handle=0x%x:0x%x).\n", contextID, dg->src.context, dg->src.resource)); return VMCI_ERROR_NO_ACCESS; } /* * Get hold of privileges of sending endpoint. */ retval = VMCIDatagramGetPrivFlagsInt(contextID, dg->src, &srcPrivFlags); if (retval != VMCI_SUCCESS) { VMCI_WARNING((LGPFX"Couldn't get privileges (handle=0x%x:0x%x).\n", dg->src.context, dg->src.resource)); return retval; } /* Determine if we should route to host or guest destination. */ if (dg->dst.context == VMCI_HOST_CONTEXT_ID) { /* Route to host datagram entry. */ DatagramEntry *dstEntry; VMCIResource *resource; if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID && dg->dst.resource == VMCI_EVENT_HANDLER) { return VMCIEvent_Dispatch(dg); } resource = VMCIResource_Get(dg->dst, VMCI_RESOURCE_TYPE_DATAGRAM); if (resource == NULL) { VMCI_DEBUG_LOG(4, (LGPFX"Sending to invalid destination " "(handle=0x%x:0x%x).\n", dg->dst.context, dg->dst.resource)); return VMCI_ERROR_INVALID_RESOURCE; } dstEntry = RESOURCE_CONTAINER(resource, DatagramEntry, resource); if (VMCIDenyInteraction(srcPrivFlags, dstEntry->privFlags)) { VMCIResource_Release(resource); return VMCI_ERROR_NO_ACCESS; } ASSERT(dstEntry->recvCB); /* * If a VMCI datagram destined for the host is also sent by the * host, we always run it delayed. This ensures that no locks * are held when the datagram callback runs. */ if (dstEntry->runDelayed || (dg->src.context == VMCI_HOST_CONTEXT_ID && VMCI_CanScheduleDelayedWork())) { VMCIDelayedDatagramInfo *dgInfo; if (Atomic_FetchAndAdd(&delayedDGHostQueueSize, 1) == VMCI_MAX_DELAYED_DG_HOST_QUEUE_SIZE) { Atomic_Dec(&delayedDGHostQueueSize); VMCIResource_Release(resource); return VMCI_ERROR_NO_MEM; } dgInfo = VMCI_AllocKernelMem(sizeof *dgInfo + (size_t)dg->payloadSize, (VMCI_MEMORY_ATOMIC | VMCI_MEMORY_NONPAGED)); if (NULL == dgInfo) { Atomic_Dec(&delayedDGHostQueueSize); VMCIResource_Release(resource); return VMCI_ERROR_NO_MEM; } dgInfo->inDGHostQueue = TRUE; dgInfo->entry = dstEntry; memcpy(&dgInfo->msg, dg, dgSize); retval = VMCI_ScheduleDelayedWork(VMCIDatagramDelayedDispatchCB, dgInfo); if (retval < VMCI_SUCCESS) { VMCI_WARNING((LGPFX"Failed to schedule delayed work for datagram " "(result=%d).\n", retval)); VMCI_FreeKernelMem(dgInfo, sizeof *dgInfo + (size_t)dg->payloadSize); VMCIResource_Release(resource); Atomic_Dec(&delayedDGHostQueueSize); return retval; } } else { retval = dstEntry->recvCB(dstEntry->clientData, dg); VMCIResource_Release(resource); if (retval < VMCI_SUCCESS) { return retval; } } } else { /* * Route to destination VM context. */ VMCIDatagram *newDG; if (contextID != dg->dst.context) { if (VMCIDenyInteraction(srcPrivFlags, vmci_context_get_priv_flags(dg->dst.context))) { VMCI_DEBUG_LOG(4, (LGPFX"Interaction denied (%X/%X - %X/%X)\n", contextID, srcPrivFlags, dg->dst.context, vmci_context_get_priv_flags(dg->dst.context))); return VMCI_ERROR_NO_ACCESS; } else if (VMCI_CONTEXT_IS_VM(contextID)) { /* * If the sending context is a VM, it cannot reach another VM. */ if (!vmkernel) { VMCI_DEBUG_LOG(4, (LGPFX"Datagram communication between VMs not " "supported (src=0x%x, dst=0x%x).\n", contextID, dg->dst.context)); return VMCI_ERROR_DST_UNREACHABLE; } } } /* We make a copy to enqueue. */ newDG = VMCI_AllocKernelMem(dgSize, VMCI_MEMORY_NORMAL); if (newDG == NULL) { VMCI_DEBUG_LOG(4, (LGPFX"No memory for datagram\n")); return VMCI_ERROR_NO_MEM; } memcpy(newDG, dg, dgSize); retval = VMCIContext_EnqueueDatagram(dg->dst.context, newDG); if (retval < VMCI_SUCCESS) { VMCI_FreeKernelMem(newDG, dgSize); VMCI_DEBUG_LOG(4, (LGPFX"Enqueue failed\n")); return retval; } } /* The datagram is freed when the context reads it. */ /* Chatty. */ // VMCI_DEBUG_LOG(10, (LGPFX"Sent datagram (size=%u bytes).\n", // (uint32)dgSize)); /* * We currently truncate the size to signed 32 bits. This doesn't * matter for this handler as it only support 4Kb messages. */ return (int)dgSize; }
void VMCI_ReadDatagramsFromPort(VMCIIoHandle ioHandle, // IN VMCIIoPort dgInPort, // IN uint8 *dgInBuffer, // IN size_t dgInBufferSize) // IN { VMCIDatagram *dg; size_t currentDgInBufferSize = PAGE_SIZE; size_t remainingBytes; ASSERT(dgInBufferSize >= PAGE_SIZE); VMCI_ReadPortBytes(ioHandle, dgInPort, dgInBuffer, currentDgInBufferSize); dg = (VMCIDatagram *)dgInBuffer; remainingBytes = currentDgInBufferSize; while (dg->dst.resource != VMCI_INVALID_ID || remainingBytes > PAGE_SIZE) { unsigned dgInSize; /* * When the input buffer spans multiple pages, a datagram can * start on any page boundary in the buffer. */ if (dg->dst.resource == VMCI_INVALID_ID) { ASSERT(remainingBytes > PAGE_SIZE); dg = (VMCIDatagram *)ROUNDUP((uintptr_t)dg + 1, PAGE_SIZE); ASSERT((uint8 *)dg < dgInBuffer + currentDgInBufferSize); remainingBytes = (size_t)(dgInBuffer + currentDgInBufferSize - (uint8 *)dg); continue; } dgInSize = VMCI_DG_SIZE_ALIGNED(dg); if (dgInSize <= dgInBufferSize) { int result; /* * If the remaining bytes in the datagram buffer doesn't * contain the complete datagram, we first make sure we have * enough room for it and then we read the reminder of the * datagram and possibly any following datagrams. */ if (dgInSize > remainingBytes) { if (remainingBytes != currentDgInBufferSize) { /* * We move the partial datagram to the front and read * the reminder of the datagram and possibly following * calls into the following bytes. */ memmove(dgInBuffer, dgInBuffer + currentDgInBufferSize - remainingBytes, remainingBytes); dg = (VMCIDatagram *)dgInBuffer; } if (currentDgInBufferSize != dgInBufferSize) { currentDgInBufferSize = dgInBufferSize; } VMCI_ReadPortBytes(ioHandle, dgInPort, dgInBuffer + remainingBytes, currentDgInBufferSize - remainingBytes); } /* We special case event datagrams from the hypervisor. */ if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID && dg->dst.resource == VMCI_EVENT_HANDLER) { result = VMCIEvent_Dispatch(dg); } else { result = VMCIDatagram_InvokeGuestHandler(dg); } if (result < VMCI_SUCCESS) { VMCI_DEBUG_LOG(4, (LGPFX"Datagram with resource (ID=0x%x) failed " "(err=%d).\n", dg->dst.resource, result)); } /* On to the next datagram. */ dg = (VMCIDatagram *)((uint8 *)dg + dgInSize); } else { size_t bytesToSkip; /* * Datagram doesn't fit in datagram buffer of maximal size. We drop it. */ VMCI_DEBUG_LOG(4, (LGPFX"Failed to receive datagram (size=%u bytes).\n", dgInSize)); bytesToSkip = dgInSize - remainingBytes; if (currentDgInBufferSize != dgInBufferSize) { currentDgInBufferSize = dgInBufferSize; } for (;;) { VMCI_ReadPortBytes(ioHandle, dgInPort, dgInBuffer, currentDgInBufferSize); if (bytesToSkip <= currentDgInBufferSize) { break; } bytesToSkip -= currentDgInBufferSize; } dg = (VMCIDatagram *)(dgInBuffer + bytesToSkip); } remainingBytes = (size_t) (dgInBuffer + currentDgInBufferSize - (uint8 *)dg); if (remainingBytes < VMCI_DG_HEADERSIZE) { /* Get the next batch of datagrams. */ VMCI_ReadPortBytes(ioHandle, dgInPort, dgInBuffer, currentDgInBufferSize); dg = (VMCIDatagram *)dgInBuffer; remainingBytes = currentDgInBufferSize; } } }