From: VMware, Inc <> Date: Mon, 21 May 2012 22:21:36 +0000 (-0700) Subject: Internal branch sync. Included in this change: X-Git-Tag: 2012.05.21-724730~42 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=161e4907028ce9dde80030f24f85a6e8fe659bee;p=thirdparty%2Fopen-vm-tools.git Internal branch sync. Included in this change: . VMCI: increase pinned QP limit to support larger vPageChannel . VMCI: Doxygen vPageChannel API . VMCI: fix leaking shm pages in vPageChannel Destroy() . fix foundry code to use the higher order bits of VixError correcly . turn on VGAuth code in VIX . changes in shared code that don't affect open-vm-tools functionality Signed-off-by: Dmitry Torokhov --- diff --git a/open-vm-tools/lib/foundryMsg/foundryMsg.c b/open-vm-tools/lib/foundryMsg/foundryMsg.c index 6969a208c..f6308a76a 100644 --- a/open-vm-tools/lib/foundryMsg/foundryMsg.c +++ b/open-vm-tools/lib/foundryMsg/foundryMsg.c @@ -655,7 +655,8 @@ VixMsg_AllocRequestMsg(size_t msgHeaderAndBodyLength, // IN || (VIX_USER_CREDENTIAL_HOST_CONFIG_SECRET == credentialType) || (VIX_USER_CREDENTIAL_HOST_CONFIG_HASHED_SECRET == credentialType) || (VIX_USER_CREDENTIAL_TICKETED_SESSION == credentialType) - || (VIX_USER_CREDENTIAL_SSPI == credentialType)) { + || (VIX_USER_CREDENTIAL_SSPI == credentialType) + || (VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN == credentialType)) { /* * All of these are optional. */ @@ -707,7 +708,8 @@ VixMsg_AllocRequestMsg(size_t msgHeaderAndBodyLength, // IN || (VIX_USER_CREDENTIAL_HOST_CONFIG_SECRET == credentialType) || (VIX_USER_CREDENTIAL_HOST_CONFIG_HASHED_SECRET == credentialType) || (VIX_USER_CREDENTIAL_TICKETED_SESSION == credentialType) - || (VIX_USER_CREDENTIAL_SSPI == credentialType)) { + || (VIX_USER_CREDENTIAL_SSPI == credentialType) + || (VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN == credentialType)) { destPtr = (char *) commandRequest; destPtr += commandRequest->commonHeader.headerLength; destPtr += commandRequest->commonHeader.bodyLength; diff --git a/open-vm-tools/lib/include/vixCommands.h b/open-vm-tools/lib/include/vixCommands.h index 156c85ac6..b386b6693 100644 --- a/open-vm-tools/lib/include/vixCommands.h +++ b/open-vm-tools/lib/include/vixCommands.h @@ -61,6 +61,7 @@ #define VIX_USER_CREDENTIAL_NAMED_INTERACTIVE_USER 8 #define VIX_USER_CREDENTIAL_TICKETED_SESSION 9 #define VIX_USER_CREDENTIAL_SSPI 10 +#define VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN 11 #define VIX_SHARED_SECRET_CONFIG_USER_NAME "__VMware_Vix_Shared_Secret_1__" @@ -315,6 +316,20 @@ struct VixCommandSSPI { VixCommandSSPI; +/* + * ********************************************************** + * This is a SAML bearer token with optional userName to specify + * an IdProvider store. + */ +typedef +#include "vmware_pack_begin.h" +struct VixCommandSAMLToken { + uint32 tokenLength; + uint32 nameLength; +} +#include "vmware_pack_end.h" +VixCommandSAMLToken; + /* * ********************************************************** * Basic power op request. The response is just a generic diff --git a/open-vm-tools/lib/include/vixOpenSource.h b/open-vm-tools/lib/include/vixOpenSource.h index 9d5156f57..1ff6b44f3 100644 --- a/open-vm-tools/lib/include/vixOpenSource.h +++ b/open-vm-tools/lib/include/vixOpenSource.h @@ -92,6 +92,88 @@ VixError Vix_TranslateGuestRegistryError(int systemError); #endif // VIX_HIDE_BORA_DEPENDENCIES + +/* + * This is an expanded view of a VixError + * Every VixError is a 64-bit int, so it can fit into this struct. + * + * The flags, extraErrorType, and extraError are all optional. They + * do not have to be set for any error. In fact, these are guaranteed + * to be all 0 when the error is VIX_OK. This means that any program + * that checks (VIX_OK == err) or (VIX_OK != err) will always work. + * + * The basic error field is a Vix error value, and it's the lsb + * of the new struct. This means that a 64-bit error can be assigned + * enum constant, like an integer. For example, err = VIX_E_FAIL; works. + * This just leaves the flags and extraError fields as 0. + */ +typedef +#include "vmware_pack_begin.h" +struct VixErrorFields { + uint16 error; + + uint8 flags; + + uint8 extraErrorType; + uint32 extraError; +} +#include "vmware_pack_end.h" +VixErrorFields; + +/* + * These are the flags for a Vix error. + */ +enum { + VIX_ERRORFLAG_GUEST = 0x0001, + VIX_ERRORFLAG_REMOTE = 0x0002, +}; + +/* + * These are the types of extra error in a Vix error. + */ +enum { + VIX_ERROREXTRATYPE_NONE = 0, + VIX_ERROREXTRATYPE_SNAPSHOT = 1, + VIX_ERROREXTRATYPE_DISKLIB = 2, + VIX_ERROREXTRATYPE_WINDOWS = 3, + VIX_ERROREXTRATYPE_LINUX = 4, + VIX_ERROREXTRATYPE_FILE = 5, + VIX_ERROREXTRATYPE_VMDB = 6, + VIX_ERROREXTRATYPE_AIO = 7, + VIX_ERROREXTRATYPE_CRYPTO = 8, + VIX_ERROREXTRATYPE_KEYSAFE = 9, + VIX_ERROREXTRATYPE_BLOCKLIST = 10, + VIX_ERROREXTRATYPE_V2I = 11, + VIX_ERROREXTRATYPE_MSGPOST = 12, +}; + + +/* + * These are the types of extra error in a Vix error. + */ +#define VIX_ERROR_BASE_ERROR(err) ((VixErrorFields *) &err)->error +#define VIX_ERROR_EXTRA_ERROR(err) ((VixErrorFields *) &err)->extraError +#define VIX_ERROR_EXTRA_ERROR_TYPE(err) ((VixErrorFields *) &err)->extraErrorType +#define VIX_ERROR_FROM_GUEST(err) (((VixErrorFields *) &err)->flags & VIX_ERRORFLAG_GUEST) +#define VIX_ERROR_FROM_REMOTE(err) (((VixErrorFields *) &err)->flags & VIX_ERRORFLAG_REMOTE) +#define VIX_ERROR_SET_FROM_GUEST(err) (((VixErrorFields *) &err)->flags |= VIX_ERRORFLAG_GUEST) +#define VIX_ERROR_SET_FROM_REMOTE(err) (((VixErrorFields *) &err)->flags |= VIX_ERRORFLAG_REMOTE) + +#define VIX_SET_GUEST_WINDOWS_ERROR(err, vixError, winError) \ + do { \ + err = 0; \ + VIX_ERROR_BASE_ERROR(err) = vixError; \ + VIX_ERROR_EXTRA_ERROR(err) = winError; \ + VIX_ERROR_EXTRA_ERROR_TYPE(err) = VIX_ERROREXTRATYPE_WINDOWS; \ + VIX_ERROR_SET_FROM_GUEST(err); \ + } while (0) + +#define VIX_ERROR_SET_ADDITIONAL_ERROR(err, vixError, additionalError) \ + do { \ + err = additionalError; \ + err = (err << 32) | vixError; \ + } while (0) + /* * This defines additional error codes. * The public error codes are defined in vix.h @@ -112,6 +194,7 @@ enum { VIX_E_REG_KEY_INVALID = 20008, VIX_E_REG_KEY_HAS_SUBKEYS = 20009, VIX_E_REG_VALUE_NOT_FOUND = 20010, + VIX_E_REG_KEY_ALREADY_EXISTS = 20011, /* Reg Errors*/ VIX_E_REG_INCORRECT_VALUE_TYPE = 25000 diff --git a/open-vm-tools/lib/include/vm_atomic.h b/open-vm-tools/lib/include/vm_atomic.h index 2c8be0906..67a2c3925 100644 --- a/open-vm-tools/lib/include/vm_atomic.h +++ b/open-vm-tools/lib/include/vm_atomic.h @@ -1410,6 +1410,30 @@ Atomic_ReadAdd64(Atomic_uint64 *var, // IN #error No compiler defined for Atomic_ReadAdd64 #endif } + +/* + *----------------------------------------------------------------------------- + * + * Atomic_ReadSub64 -- + * + * Atomic read (returned), sub a value, write. + * + * Results: + * The value of the variable before the operation. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static INLINE uint64 +Atomic_ReadSub64(Atomic_uint64 *var, // IN + uint64 val) // IN +{ + // Do an sub by an add and a overflow + return Atomic_ReadAdd64(var, -val); +} #endif diff --git a/open-vm-tools/lib/include/vm_basic_asm_x86_64.h b/open-vm-tools/lib/include/vm_basic_asm_x86_64.h index 64d95122c..cd1f9e52a 100644 --- a/open-vm-tools/lib/include/vm_basic_asm_x86_64.h +++ b/open-vm-tools/lib/include/vm_basic_asm_x86_64.h @@ -322,7 +322,7 @@ Mul64x6464(uint64 multiplicand, uint64 result, dummy; __asm__("mulq %3 \n\t" - "shrdq %1, %0 \n\t" + "shrdq %b4, %1, %0 \n\t" : "=a" (result), "=d" (dummy) : "0" (multiplier), @@ -380,7 +380,7 @@ Muls64x64s64(int64 multiplicand, int64 multiplier, uint32 shift) int64 result, dummy; __asm__("imulq %3 \n\t" - "shrdq %1, %0 \n\t" + "shrdq %b4, %1, %0 \n\t" : "=a" (result), "=d" (dummy) : "0" (multiplier), diff --git a/open-vm-tools/lib/include/vm_basic_types.h b/open-vm-tools/lib/include/vm_basic_types.h index 75ecfbbb6..15ef0aba6 100644 --- a/open-vm-tools/lib/include/vm_basic_types.h +++ b/open-vm-tools/lib/include/vm_basic_types.h @@ -587,6 +587,7 @@ typedef void * UserVA; #define INVALID_LPN64 ((LPN64)-1) #define INVALID_PAGENUM ((PageNum)-1) +#define INVALID_MPN64 ((MPN64)-1) /* * Format modifier for printing VA, LA, and VPN. diff --git a/open-vm-tools/lib/include/vmci_defs.h b/open-vm-tools/lib/include/vmci_defs.h index 6791b46cc..bbfb32e49 100644 --- a/open-vm-tools/lib/include/vmci_defs.h +++ b/open-vm-tools/lib/include/vmci_defs.h @@ -99,8 +99,12 @@ typedef enum VMCIIntrType { * Queues with pre-mapped data pages must be small, so that we don't pin * too much kernel memory (especially on vmkernel). We limit a queuepair to * 32 KB, or 16 KB per queue for symmetrical pairs. + * + * XXX, we are raising this limit to 4MB to support high-throughput workloads + * with vioi-filter. Once we switch to rings instead of queuepairs for the + * page channel, we will drop this limit again. See PR 852983. */ -#define VMCI_MAX_PINNED_QP_MEMORY (32 * 1024) +#define VMCI_MAX_PINNED_QP_MEMORY (4 * 1024 * 1024) /* * We have a fixed set of resource IDs available in the VMX. @@ -149,6 +153,12 @@ typedef uint32 VMCI_Resource; */ #define VMCI_CORE_DEVICE_RESOURCE_MAX VMCI_QUEUEPAIR_DETACH +/* + * VMCI reserved host datagram resource IDs. + * vsock control channel has resource id 1. + */ +#define VMCI_DVFILTER_DATA_PATH_DATAGRAM 2 + /* VMCI Ids. */ typedef uint32 VMCIId; diff --git a/open-vm-tools/modules/linux/shared/vmci_defs.h b/open-vm-tools/modules/linux/shared/vmci_defs.h index 8222af726..e97c7e9a9 100644 --- a/open-vm-tools/modules/linux/shared/vmci_defs.h +++ b/open-vm-tools/modules/linux/shared/vmci_defs.h @@ -99,8 +99,12 @@ typedef enum VMCIIntrType { * Queues with pre-mapped data pages must be small, so that we don't pin * too much kernel memory (especially on vmkernel). We limit a queuepair to * 32 KB, or 16 KB per queue for symmetrical pairs. + * + * XXX, we are raising this limit to 4MB to support high-throughput workloads + * with vioi-filter. Once we switch to rings instead of queuepairs for the + * page channel, we will drop this limit again. See PR 852983. */ -#define VMCI_MAX_PINNED_QP_MEMORY (32 * 1024) +#define VMCI_MAX_PINNED_QP_MEMORY (4 * 1024 * 1024) /* * We have a fixed set of resource IDs available in the VMX. @@ -149,6 +153,12 @@ typedef uint32 VMCI_Resource; */ #define VMCI_CORE_DEVICE_RESOURCE_MAX VMCI_QUEUEPAIR_DETACH +/* + * VMCI reserved host datagram resource IDs. + * vsock control channel has resource id 1. + */ +#define VMCI_DVFILTER_DATA_PATH_DATAGRAM 2 + /* VMCI Ids. */ typedef uint32 VMCIId; diff --git a/open-vm-tools/modules/linux/vmci/common/vmciPageChannel.c b/open-vm-tools/modules/linux/vmci/common/vmciPageChannel.c index 257750205..ebdf47d7a 100644 --- a/open-vm-tools/modules/linux/vmci/common/vmciPageChannel.c +++ b/open-vm-tools/modules/linux/vmci/common/vmciPageChannel.c @@ -43,6 +43,14 @@ #define VMCI_PACKET_RECV_THRESHOLD 150 +/* + * Maximum number of elements per DGRAM packet (for setting receive buffers). + */ + +#define VMCI_PACKET_DGRAM_MAX_ELEMS \ + ((VMCI_MAX_DG_PAYLOAD_SIZE - sizeof(VPageChannelPacket)) / \ + sizeof(VPageChannelElem)) + /* * All flags. We use this to check the validity of the flags, so put it here * instead of in the header, otherwise people might assume we mean for them @@ -60,6 +68,8 @@ */ struct VPageChannel { + VPageChannelState state; + VMCIHandle dgHandle; uint32 flags; VPageChannelRecvCB recvCB; @@ -79,7 +89,6 @@ struct VPageChannel { uint64 consumeQSize; VMCIId attachSubId; VMCIId detachSubId; - Bool qpConnected; Bool useSpinLock; spinlock_t qpRecvLock; spinlock_t qpSendLock; @@ -97,7 +106,7 @@ struct VPageChannel { * Receiving buffer. */ - int curRecvBufs; + Atomic_Int curRecvBufs; int recvBufsTarget; int defaultRecvBufs; int maxRecvBufs; @@ -110,9 +119,9 @@ struct VPageChannel { static int VPageChannelSendControl(VPageChannel *channel, + VPageChannelPacketType type, char *message, int len, - VPageChannelPacketType type, int numElems, VPageChannelElem *elems); @@ -242,12 +251,15 @@ VPageChannelReleaseRecvLock(VPageChannel *channel, // IN /* *----------------------------------------------------------------------------- * - * VPageChannelSetRecvBuffers -- + * VPageChannelAddRecvBuffers -- * - * Set the receiving buffers for the channel. + * Add receiving buffers for the channel. This will ask the client to + * to allocate the required elements and then pass those to the peer. + * If "byControl" is TRUE, then the DGRAM control channel will be used, + * and multiple packets will be sent if necessary. * * Results: - * VMCI_SUCCESS if set, negative error code otherwise. + * The number of buffers actually sent to the peer. * * Side effects: * None. @@ -256,74 +268,109 @@ VPageChannelReleaseRecvLock(VPageChannel *channel, // IN */ static int -VPageChannelSetRecvBuffers(VPageChannel *channel, // IN +VPageChannelAddRecvBuffers(VPageChannel *channel, // IN int numElems, // IN Bool byControl) // IN { - int retval; - int allocNum; - size_t size = sizeof(VPageChannelPacket) + - numElems * sizeof(VPageChannelElem); + int n; + int sent; + size_t size; VPageChannelElem *elems; VPageChannelPacket *packet; ASSERT(channel); - packet = (VPageChannelPacket *)VMCI_AllocKernelMem(size, VMCI_MEMORY_ATOMIC); - if (packet == NULL) { - VMCI_WARNING((LGPFX"Failed to allocate packet (channel=%p) " - "(size=%"FMTSZ"u).\n", - channel, - size)); - return VMCI_ERROR_NO_MEM; - } + sent = 0; + size = 0; + elems = NULL; + packet = NULL; - packet->type = VPCPacket_SetRecvBuffer; - packet->msgLen = 0; - packet->numElems = numElems; + n = min_t(int, VMCI_PACKET_DGRAM_MAX_ELEMS, numElems); + while (n > 0) { + int retval; + int allocNum; - elems = VPAGECHANNEL_PACKET_ELEMS(packet); - allocNum = channel->elemAllocFn(channel->allocClientData, elems, numElems); - if (allocNum != numElems) { - VMCI_WARNING((LGPFX"Failed to allocate receive buffer (channel=%p) " - "(expected=%d) (actual=%d).\n", - channel, - numElems, - allocNum)); - retval = VMCI_ERROR_NO_MEM; - goto error; - } + /* + * First packet is always big enough to cover any remaining elements, + * so just allocate it once. + */ - if (byControl || !channel->qpConnected) { - retval = VPageChannelSendControl(channel, NULL, 0, - VPCPacket_SetRecvBuffer, - numElems, elems); - } else { - retval = VPageChannel_SendPacket(channel, packet); - } - if (retval < VMCI_SUCCESS) { - VMCI_WARNING((LGPFX"Failed to set receive buffers (channel=%p) " - "(err=%d).\n", - channel, - retval)); - goto error; - } + if (NULL == packet) { + size = sizeof(VPageChannelPacket) + (n * sizeof(VPageChannelElem)); + packet = (VPageChannelPacket *) + VMCI_AllocKernelMem(size, VMCI_MEMORY_ATOMIC); + if (packet == NULL) { + VMCI_WARNING((LGPFX"Failed to allocate packet (channel=%p) " + "(size=%"FMTSZ"u).\n", + channel, + size)); + goto exit; + } + + packet->type = VPCPacket_SetRecvBuffer; + packet->msgLen = 0; + elems = VPAGECHANNEL_PACKET_ELEMS(packet); + } - channel->curRecvBufs += numElems; + allocNum = channel->elemAllocFn(channel->allocClientData, elems, n); + if (0 == allocNum) { + /* + * If the client failed to allocate any elements at all then just + * bail out and return whatever number we managed to send so far + * (if any). + */ - VMCI_FreeKernelMem(packet, size); + VMCI_WARNING((LGPFX"Failed to allocate receive buffer (channel=%p) " + "(expected=%d).\n", + channel, + n)); + goto exit; + } - return VMCI_SUCCESS; + /* + * We wanted "n" elements, but we might only have "allocNum" because + * that's all the client could allocate. Pass down whatever we got. + */ - error: - if (packet != NULL) { - if (allocNum) { + packet->numElems = allocNum; + + if (byControl || VPCState_Connected != channel->state) { + retval = VPageChannelSendControl(channel, VPCPacket_SetRecvBuffer, + NULL, 0, allocNum, elems); + } else { + retval = VPageChannel_SendPacket(channel, packet); + /* + * XXX, what if this is a non-blocking queuepair and we fail to + * send because it's full and we can't wait? Is it even worth it + * to loop? + */ + } + if (retval < VMCI_SUCCESS) { + /* + * Failure to send is fatal. Release the client's elements and + * bail out. + */ + + VMCI_WARNING((LGPFX"Failed to set receive buffers (channel=%p) " + "(err=%d).\n", + channel, + retval)); channel->elemFreeFn(channel->freeClientData, elems, allocNum); + goto exit; } - VMCI_FreeKernelMem(packet, size); + + Atomic_Add32(&channel->curRecvBufs, allocNum); + + sent += allocNum; + numElems -= allocNum; + n = min_t(int, VMCI_PACKET_DGRAM_MAX_ELEMS, numElems); } - return retval; +exit: + if (NULL != packet) { + VMCI_FreeKernelMem(packet, size); + } + return sent; } @@ -347,6 +394,7 @@ static int VPageChannelRecvPacket(VPageChannel *channel, // IN VPageChannelPacket *packet) // IN { + int curRecvBufs; int recvBufsTarget; ASSERT(channel); @@ -355,7 +403,8 @@ VPageChannelRecvPacket(VPageChannel *channel, // IN if (packet->type != VPCPacket_Data && packet->type != VPCPacket_Completion_Notify && packet->type != VPCPacket_RequestBuffer && - packet->type != VPCPacket_HyperConnect) { + packet->type != VPCPacket_HyperConnect && + packet->type != VPCPacket_HyperDisconnect) { VMCI_WARNING((LGPFX"Received invalid packet (channel=%p) " "(type=%d).\n", channel, @@ -408,7 +457,7 @@ VPageChannelRecvPacket(VPageChannel *channel, // IN (LGPFX"Requested more buffers (channel=%p) " "(cur=%d) (target=%d) (max=%d).\n", channel, - channel->curRecvBufs, + Atomic_Read32(&channel->curRecvBufs), channel->recvBufsTarget, channel->maxRecvBufs)); @@ -419,13 +468,32 @@ VPageChannelRecvPacket(VPageChannel *channel, // IN case VPCPacket_Data: channel->recvCB(channel->clientRecvData, packet); - channel->curRecvBufs -= packet->numElems; + Atomic_Sub32(&channel->curRecvBufs, packet->numElems); + ASSERT(Atomic_Read32(&channel->curRecvBufs) > 0); break; case VPCPacket_Completion_Notify: channel->recvCB(channel->clientRecvData, packet); break; + case VPCPacket_HyperDisconnect: + VMCI_DEBUG_LOG(10, + (LGPFX"Hypervisor requested disconnection " + "(channel=%p) (numElems=%d).\n", + channel, + packet->numElems)); + if (packet->numElems > 0) { + channel->elemFreeFn(channel->freeClientData, + VPAGECHANNEL_PACKET_ELEMS(packet), + packet->numElems); + } + (void)VPageChannelSendControl(channel, VPCPacket_GuestDisconnect, + NULL, 0, 0, NULL); + if (channel->state < VPCState_Disconnecting) { + channel->state = VPCState_Disconnecting; + } + return VMCI_SUCCESS; + default: ASSERT_NOT_IMPLEMENTED(FALSE); break; @@ -439,14 +507,12 @@ VPageChannelRecvPacket(VPageChannel *channel, // IN * in the hope that we won't hit this again. */ - if (channel->curRecvBufs < (recvBufsTarget - VMCI_PACKET_RECV_THRESHOLD)) { - int numElems = recvBufsTarget + VMCI_PACKET_RECV_THRESHOLD - - channel->curRecvBufs; + curRecvBufs = Atomic_Read32(&channel->curRecvBufs); + if (curRecvBufs < (recvBufsTarget - VMCI_PACKET_RECV_THRESHOLD)) { + int numElems = recvBufsTarget + VMCI_PACKET_RECV_THRESHOLD - curRecvBufs; - if (VPageChannelSetRecvBuffers(channel, numElems, FALSE) == - VMCI_SUCCESS) { - channel->recvBufsTarget = recvBufsTarget; - } + (void)VPageChannelAddRecvBuffers(channel, numElems, FALSE); + channel->recvBufsTarget = recvBufsTarget; } return VMCI_SUCCESS; @@ -531,7 +597,7 @@ VPageChannelDoDoorbellCallback(VPageChannel *channel) // IN/OUT ASSERT(channel); - if (!channel->qpConnected) { + if (VPCState_Connected != channel->state) { VMCI_WARNING((LGPFX"Not connected (channel=%p).\n", channel)); return; @@ -696,6 +762,8 @@ VPageChannelSendConnectionMessage(VPageChannel *channel) // IN ASSERT(channel); + channel->state = VPCState_Connecting; + memset(&message, 0, sizeof message); message.dgHandle = channel->dgHandle; message.qpHandle = channel->qpHandle; @@ -710,9 +778,8 @@ VPageChannelSendConnectionMessage(VPageChannel *channel) // IN channel->qpHandle.context, channel->qpHandle.resource)); - return VPageChannelSendControl(channel, - (char *)&message, sizeof message, - VPCPacket_GuestConnect, 0, NULL); + return VPageChannelSendControl(channel, VPCPacket_GuestConnect, + (char *)&message, sizeof message, 0, NULL); } @@ -753,7 +820,7 @@ VPageChannelPeerAttachCB(VMCIId subId, // IN channel, ePayload->handle.context, ePayload->handle.resource)); - channel->qpConnected = TRUE; + channel->state = VPCState_Connected; } } @@ -795,7 +862,7 @@ VPageChannelPeerDetachCB(VMCIId subId, // IN channel, ePayload->handle.context, ePayload->handle.resource)); - channel->qpConnected = FALSE; + channel->state = VPCState_Disconnected; } } @@ -838,7 +905,7 @@ VPageChannelDestroyQueuePair(VPageChannel *channel) // IN/OUT channel->qpair = NULL; } - channel->qpConnected = FALSE; + channel->state = VPCState_Disconnected; } @@ -995,6 +1062,7 @@ VPageChannel_CreateInVM(VPageChannel **channel, // IN/OUT */ memset(pageChannel, 0, sizeof *pageChannel); + pageChannel->state = VPCState_Unconnected; pageChannel->dgHandle = VMCI_INVALID_HANDLE; pageChannel->attachSubId = VMCI_INVALID_ID; pageChannel->detachSubId = VMCI_INVALID_ID; @@ -1002,7 +1070,6 @@ VPageChannel_CreateInVM(VPageChannel **channel, // IN/OUT pageChannel->qpair = NULL; pageChannel->doorbellHandle = VMCI_INVALID_HANDLE; pageChannel->peerDoorbellHandle = VMCI_INVALID_HANDLE; - pageChannel->qpConnected = FALSE; pageChannel->flags = channelFlags; pageChannel->recvCB = recvCB; pageChannel->clientRecvData = clientRecvData; @@ -1013,7 +1080,7 @@ VPageChannel_CreateInVM(VPageChannel **channel, // IN/OUT pageChannel->resourceId = resourceId; pageChannel->peerDgHandle = VMCI_MAKE_HANDLE(VMCI_HOST_CONTEXT_ID, peerResourceId); - pageChannel->curRecvBufs = 0; + Atomic_Write32(&pageChannel->curRecvBufs, 0); pageChannel->recvBufsTarget = defaultRecvBuffers; pageChannel->defaultRecvBufs = defaultRecvBuffers; pageChannel->maxRecvBufs = maxRecvBuffers + VMCI_PACKET_RECV_THRESHOLD; @@ -1089,8 +1156,13 @@ VPageChannel_CreateInVM(VPageChannel **channel, // IN/OUT if (defaultRecvBuffers) { int numElems = defaultRecvBuffers + VMCI_PACKET_RECV_THRESHOLD; - retval = VPageChannelSetRecvBuffers(pageChannel, numElems, TRUE); - if (retval < VMCI_SUCCESS) { + if (0 == VPageChannelAddRecvBuffers(pageChannel, numElems, TRUE)) { + /* + * AddRecvBuffers() returns the number of buffers actually added. If + * we failed to add any at all, then fail. + */ + + retval = VMCI_ERROR_NO_MEM; goto error; } } @@ -1148,6 +1220,7 @@ VPageChannel_Destroy(VPageChannel *channel) // IN/OUT VMCIDatagram_DestroyHnd(channel->dgHandle); } + channel->state = VPCState_Free; VMCI_FreeKernelMem(channel, sizeof *channel); VMCI_DEBUG_LOG(10, @@ -1247,9 +1320,9 @@ VPageChannelAllocDatagram(VPageChannel *channel, // IN static int VPageChannelSendControl(VPageChannel *channel, // IN + VPageChannelPacketType type, // IN char *message, // IN int len, // IN - VPageChannelPacketType type, // IN int numElems, // IN VPageChannelElem *elems) // IN { @@ -1260,7 +1333,8 @@ VPageChannelSendControl(VPageChannel *channel, // IN ASSERT(channel); ASSERT(type == VPCPacket_Data || type == VPCPacket_GuestConnect || - type == VPCPacket_SetRecvBuffer); + type == VPCPacket_SetRecvBuffer || + type == VPCPacket_GuestDisconnect); dg = NULL; retval = VPageChannelAllocDatagram(channel, len, numElems, &dg); @@ -1336,7 +1410,7 @@ VPageChannel_SendPacket(VPageChannel *channel, // IN ASSERT(channel); - if (!channel->qpConnected) { + if (VPCState_Connected != channel->state) { VMCI_WARNING((LGPFX"Not connected (channel=%p).\n", channel)); return VMCI_ERROR_DST_UNREACHABLE; @@ -1439,7 +1513,7 @@ VPageChannel_Send(VPageChannel *channel, // IN/OUT ASSERT(channel); - if (!channel->qpConnected) { + if (VPCState_Connected != channel->state) { VMCI_WARNING((LGPFX"Not connected (channel=%p).\n", channel)); return VMCI_ERROR_DST_UNREACHABLE; @@ -1507,7 +1581,7 @@ EXPORT_SYMBOL(VPageChannel_Send); void VPageChannel_PollRecvQ(VPageChannel *channel) // IN { - if (channel->qpConnected) { + if (VPCState_Connected != channel->state) { VPageChannelDoDoorbellCallback(channel); } } diff --git a/open-vm-tools/modules/linux/vmci/linux/vmci_version.h b/open-vm-tools/modules/linux/vmci/linux/vmci_version.h index 893651427..5c8e8f24b 100644 --- a/open-vm-tools/modules/linux/vmci/linux/vmci_version.h +++ b/open-vm-tools/modules/linux/vmci/linux/vmci_version.h @@ -25,8 +25,8 @@ #ifndef _VMCI_VERSION_H_ #define _VMCI_VERSION_H_ -#define VMCI_DRIVER_VERSION 9.5.5.0 -#define VMCI_DRIVER_VERSION_COMMAS 9,5,5,0 -#define VMCI_DRIVER_VERSION_STRING "9.5.5.0" +#define VMCI_DRIVER_VERSION 9.5.6.0 +#define VMCI_DRIVER_VERSION_COMMAS 9,5,6,0 +#define VMCI_DRIVER_VERSION_STRING "9.5.6.0" #endif /* _VMCI_VERSION_H_ */ diff --git a/open-vm-tools/modules/linux/vmci/shared/vmci_page_channel.h b/open-vm-tools/modules/linux/vmci/shared/vmci_page_channel.h index 230120cff..fdd47e782 100644 --- a/open-vm-tools/modules/linux/vmci/shared/vmci_page_channel.h +++ b/open-vm-tools/modules/linux/vmci/shared/vmci_page_channel.h @@ -1,5 +1,5 @@ /********************************************************* - * Copyright (C) 2011 VMware, Inc. All rights reserved. + * Copyright (C) 2011-2012 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -33,96 +33,218 @@ #include "vmci_defs.h" #include "vmci_call_defs.h" -/* Max size of a single tx buffer. */ +/** \cond PRIVATE */ #define VPAGECHANNEL_MAX_TX_BUF_SIZE (1 << 14) #define VPAGECHANNEL_MAX_PAGES_PER_TX_BUFFER \ (VPAGECHANNEL_MAX_TX_BUF_SIZE / PAGE_SIZE + 1) +/** \endcond */ + +/** + * \brief Get a pointer to the elements in a packet. + * + * Returns a pointer to the elements at the end of a page channel packet. + * + * \see VPageChannelElem + * \see VPageChannelPacket + */ #define VPAGECHANNEL_PACKET_ELEMS(packet) \ (VPageChannelElem *)((char *)(packet) + \ sizeof(VPageChannelPacket) + \ packet->msgLen) + +/** + * \brief Get a pointer to the message in a packet. + * + * Returns a pointer to the message embedded in a page channel packet. + * + * \see VPageChannelPacket + */ + #define VPAGECHANNEL_PACKET_MESSAGE(packet) \ (char *)((char *)(packet) + sizeof(VPageChannelPacket)) -/* - * Flags for channel creation. - * - * NOTIFY_ONLY means invoke the channel's receive callback directly when a - * notification is received. If not specified, then when a notification is - * received, packets are read from the queuepair and the callback invoked for - * each one separately. Not applicable on VMKernel. - * - * DELAYED_CB means that all callbacks run in a delayed context, and the - * caller is allowed to block. If not specified, then callbacks run in - * interrupt context and the channel will not block. This can only be - * specified on the guest side for now. - * - * SEND_WHILE_ATOMIC indicates that the client wishes to call Send() from - * an atomic context and that the channel should not block. If the channel - * is not allowed to block, then the channel's pages are permanently mapped - * and pinned. Note that this will limit the total size of the channel to +/** + * \brief Notify client directly, and do not read packets. + * + * This flag indicates that the channel should invoke the client's receive + * callback directly when any packets are available. If not specified, then + * when a notification is received, packets are read from the channel and the + * callback invoked for each one separately. + * + * \note Not applicable to VMKernel. + * + * \see VPageChannel_CreateInVM() + */ + +#define VPAGECHANNEL_FLAGS_NOTIFY_ONLY 0x1 + +/** + * \brief Invoke client's receive callback in delayed context. + * + * This flag indicates that all callbacks run in a delayed context, and the + * caller and callback are allowed to block. If not specified, then callbacks + * run in interrupt context and the channel will not block, and the caller + * is not allowed to block. + * + * \note Not applicable to VMKernel. + * + * \see VPageChannel_CreateInVM() + */ + +#define VPAGECHANNEL_FLAGS_RECV_DELAYED 0x2 + +/** + * \brief Send from an atomic context. + * + * This flag indicates that the client wishes to call Send() from an atomic + * context and that the channel should not block. If the channel is not + * allowed to block, then the channel's pages are permanently mapped and + * pinned. Note that this will limit the total size of the channel to * VMCI_MAX_PINNED_QP_MEMORY. + * + * \note Not applicable to VMKernel. + * + * \see VPageChannel_CreateInVM() */ -#define VPAGECHANNEL_FLAGS_NOTIFY_ONLY 0x1 -#define VPAGECHANNEL_FLAGS_RECV_DELAYED 0x2 #define VPAGECHANNEL_FLAGS_SEND_WHILE_ATOMIC 0x4 +/** + * \brief An element describing a data range. + * + * Describes a data range, starting at a base address and for a given + * length, i.e., an element of a scatter-gather list. Indicates physical + * address for the guest and machine address for hypervisor. Can be passed + * via packets or buffers. + * + * \note Structure is packed. + * + * \see VPageChannelPacket + * \see VPageChanelBuffer + */ typedef #include "vmware_pack_begin.h" struct VPageChannelElem { union { - uint64 pa; // For guest - uint64 ma; // For hypervisor + /** \brief Physical address for guest. */ + uint64 pa; + + /** \brief Machine address for hypervisor. */ + uint64 ma; }; + + /** \brief Length of range. */ uint32 le; } #include "vmware_pack_end.h" VPageChannelElem; +/** + * \brief Page channel page types. + * + * The various types of page channel packets. + * + * \see VPageChannelPacket + */ typedef enum { + /** \brief Data packet. */ VPCPacket_Data = 1, - VPCPacket_Completion_Notify, // Hypervisor to guest only. - VPCPacket_GuestConnect, // Connect to hypervisor. Internal use only. - VPCPacket_HyperConnect, // Complete connection handshake. Internal. - VPCPacket_RequestBuffer, // Request buffers. Internal use only. - VPCPacket_SetRecvBuffer, // Set buffers. Internal use only. + + /** \brief Completion notification, from hypervisor to guest. */ + VPCPacket_Completion_Notify, + + /** \cond PRIVATE */ + /** \brief Connect to hypervisor, internal. */ + VPCPacket_GuestConnect, + + /** \brief Complete connection handshake, internal. */ + VPCPacket_HyperConnect, + + /** \brief Request buffers, internal. */ + VPCPacket_RequestBuffer, + + /** \brief Set buffers, internal. */ + VPCPacket_SetRecvBuffer, + + /** \brief Hypervisor channel disconnect, internal. */ + VPCPacket_HyperDisconnect, + + /** \brief Guest channel ACK hypervisor disconnect, internal. */ + VPCPacket_GuestDisconnect, + /** \endcond */ } VPageChannelPacketType; +/** + * \brief Page channel packet structure. + * + * A packet structure for passing control/data between guest and hypervisor. + * Can optionally contain a message and also a number of elements. + * + * \note Structure is packed. + * + * \see VPageChannelPacketType + */ typedef #include "vmware_pack_begin.h" struct VPageChannelPacket { + /** \brief Type of packet. */ VPageChannelPacketType type; + + /** \brief Length of optional message. */ uint32 msgLen; + + /** \brief Number of optional elements in packet. */ uint32 numElems; - /* - * Followed by msgLen of message and numElems of VPageChannelElem. - */ + + /** \brief Followed by msgLen of message and numElems VPageChannelElem. */ } #include "vmware_pack_end.h" VPageChannelPacket; +/** + * \brief Page channel buffer structure. + * + * A buffer of elements (a scatter-gather list). + * + * \note Structure is packed. + * + * \see VPageChannelElem + */ + typedef #include "vmware_pack_begin.h" struct VPageChannelBuffer { + /** \brief Number of elements. */ uint32 numElems; + + /** \brief First element. */ VPageChannelElem elems[1]; - /* - * Followed by numElems - 1 of VPageChannelElem. - */ + + /** \brief Followed by numElems - 1 of VPageChannelElem. */ } #include "vmware_pack_end.h" VPageChannelBuffer; +/** \cond PRIVATE */ typedef #include "vmware_pack_begin.h" struct VPageChannelGuestConnectMessage { + + /** \brief Guest channel's datagram handle for control channel. */ VMCIHandle dgHandle; + + /** \brief Guest channel's queuepair handle. */ VMCIHandle qpHandle; + + /** \brief Size of producer queue in queuepair in bytes. */ uint64 produceQSize; + + /** \brief Size of consumer queue in queuepair in bytes. */ uint64 consumeQSize; + + /** \brief Guest channel's doorbell handle. */ VMCIHandle doorbellHandle; } #include "vmware_pack_end.h" @@ -131,28 +253,129 @@ VPageChannelGuestConnectMessage; typedef #include "vmware_pack_begin.h" struct VPageChannelHyperConnectMessage { + /** \brief Hypervisor's doorbell handle. */ VMCIHandle doorbellHandle; } #include "vmware_pack_end.h" VPageChannelHyperConnectMessage; +/** \endcond PRIVATE */ + +/** \cond PRIVATE */ +typedef enum VPageChannelState { + VPCState_Free = 0, + VPCState_Unconnected, + VPCState_Connecting, + VPCState_Connected, + VPCState_Disconnecting, + VPCState_Disconnected, +} VPageChannelState; +/** \endcond PRIVATE */ + +/** + * \brief Opaque page channel type. + */ struct VPageChannel; typedef struct VPageChannel VPageChannel; +/** + * \brief Client receive callback type. + * + * Type of receive callback, invoked when there are data packets in the + * channel. The client provides a callback with this type to + * VPageChannel_CreateInVM(). If VPAGECHANNEL_FLAGS_NOTIFY_ONLY is specified + * in the channel creation flags, then \c packet is \c NULL; otherwise, + * \c packet points to a channel packet. + * + * \see VPageChannel_CreateInVM() + * \see VPageChannelPacket + */ + typedef void (*VPageChannelRecvCB)(void *clientData, VPageChannelPacket *packet); #if !defined(VMKERNEL) +/** + * \brief Client element allocation callback type. + * + * Type of element allocation callback, invoked when the channel needs + * elements. The client provides a callback of this type to + * VPageChannel_CreateInVM(). + * + * \see VPageChannel_CreateInVM() + * \see VPageChannelElem + * \see VPageChannelFreeElemFn + */ + typedef int (*VPageChannelAllocElemFn)(void *clientData, VPageChannelElem *elems, int numElems); +/** + * \brief Client element release callback type. + * + * Type of element release callback, invoked when the channel releases + * elements. The client provides a callback of this type to + * VPageChannel_CreateInVM(). + * + * \see VPageChannel_CreateInVM() + * \see VPageChannelElem + * \see VPageChannelAllocElemFn + */ + typedef void (*VPageChannelFreeElemFn)(void *clientData, VPageChannelElem *elems, int numElems); +/* + ************************************************************************ + * VPageChannel_CreateInVM */ /** + * + * \brief Create guest page channel. + * + * Creates a page channel in the guest. The channel should be released + * with VPageChannel_Destroy(). + * + * \note Only applicable in the guest. + * + * \see VPageChannel_CreateInVMK() + * \see VPageChannel_Destroy() + * + * \param[out] channel Pointer to a newly constructed page + * channel if successful. + * \param[in] resourceId Resource ID on which the channel should + * register its control channel. + * \param[in] peerResourceId Resource ID of peer's control channel. + * \param[in] produceQSize Size of producer queue in queuepair in + * bytes. + * \param[in] consumeQSize Size of consumer queue in queuepair in + * bytes. + * \param[in] flags Channel flags. + * \param[in] recvCB Client's receive callback. + * \param[in] clientRecvData Client data for client's receive + * callback. + * \param[in] elemAlloc Element allocation callback for + * allocating page ranges (scatter-gather + * elements). + * \param[in] allocClientData Client data for element allocation + * callback. + * \param[in] elemFree Element release callback for elements. + * \param[in] freeClientData Client data for element release + * callback. + * \param[in] defRecvBufs Default number of elements sent to + * hypervisor channel. + * \param[in] maxRecvBufs Maximum number of elements that can be + * sent to the hypervisor channel. + * + * \retval VMCI_SUCCESS Creation succeeded, \c *channel contains + * a pointer to a valid channel. + * \retval other Failure. + * + ************************************************************************ + */ + int VPageChannel_CreateInVM(VPageChannel **channel, VMCIId resourceId, VMCIId peerResourceId, @@ -165,28 +388,128 @@ int VPageChannel_CreateInVM(VPageChannel **channel, void *allocClientData, VPageChannelFreeElemFn elemFree, void *freeClientData, - int defaultRecvBuffers, - int maxRecvBuffers); + int defRecvBufs, + int maxRecvBufs); #else // VMKERNEL +/* + ************************************************************************ + * VPageChannel_CreateInVMK */ /** + * + * \brief Create a page channel in VMKernel. + * + * Creates a page channel. The channel should be released with + * VPageChannel_Destroy(). + * + * \note Only applicable on VMKernel. + * + * \see VPageChannel_CreateInVM() + * \see VPageChannel_Destroy() + * + * \param[out] channel Pointer to a newly constructed page + * channel if successful. + * \param[in] resourceId Resource ID on which to register + * control channel. + * \param[in] recvCB Client's receive callback. + * \param[in] clientRecvData Client data for receive callback. + * + * \retval VMCI_SUCCESS Creation succeeded, \c *channel + * contains a pointer to a valid channel. + * \retval other Failure. + * + *********************************************************************** + */ + int VPageChannel_CreateInVMK(VPageChannel **channel, VMCIId resourceId, VPageChannelRecvCB recvCB, void *clientRecvData); +/* + ************************************************************************ + * VPageChannel_ReserveBuffers */ /** + * + * \brief Reserve guest elements. + * + * Reserve sufficient guest elements to cover the given length. The + * buffers can then be posted to the guest. This allocates both the + * buffer and the elements within the buffer. + * + * \note Only applicable on VMKernel. + * + * \see VPageChannel_ReleaseBuffers() + * + * \param[in] channel Page channel. + * \param[in] dataLen Length to reserve in bytes. + * \param[out] buffer Pointer to a buffer containing elements to cover + * the given length if successful. + * + * \retval VMCI_SUCCESS Reservation succeeded, \c *buffer contains + * a pointer to a valid buffer. + * \retval other Failure. + * + ************************************************************************ + */ + int VPageChannel_ReserveBuffers(VPageChannel *channel, size_t dataLen, VPageChannelBuffer **buffer); + +/* + ************************************************************************ + * VPageChannel_ReleaseBuffers */ /** + * + * \brief Release guest elements. + * + * \note Only applicable on VMKernel. + * + * \see VPageChannel_ReserveBuffers() + * + * Release guest elements previous reserved with + * VPageChannel_ReserveBuffers(). If the buffers were sent to the guest, + * then only the buffer itself should be released, i.e., + * \c returnToFreePool should be \c FALSE; the guest will release the + * buffers on completion. Otherwise, if for some reason they are not + * sent after reserving them, then \c returnToFreePool should be set to + * \c TRUE. + * + * \param[in] channel Page channel. + * \param[in] buffer Buffer to be released. + * \param[in] returnToFreePool If \c TRUE, then release the elements + * of the buffer along with the buffer + * itself. If \c FALSE, then release only + * the buffer pointer itself. + * + ************************************************************************ + */ + void VPageChannel_ReleaseBuffers(VPageChannel *channel, VPageChannelBuffer *buffer, Bool returnToFreePool); /* - * This function is called when the client is finished using the - * scatter-gather list of a packet. This will generate a notification to the - * guest to pass the ownership of buffers back to the guest. This can also be - * used to read back the data from hypervisor and send it the to guest. + ************************************************************************ + * VPageChannel_CompletionNotify */ /** + * + * \brief Notify channel of completion. + * + * This function is called when the client is finished using the elements + * (scatter-gather list) of a packet. This will generated a notification + * to the guest to pass ownership of the buffers back to the guest. This + * can also be used to read back the data from the hypervisor and send + * it to the guest. + * + * \note Only applicable on VMKernel. + * + * \see VPageChannel_ReserveBuffers + * + * \param[in] channel Channel on which I/O is complete. + * \param[in] message Optional message to send to guest. + * \param[in] len Length of optional message. + * \param[in] buffer Buffer used for I/O. + * + ************************************************************************ */ int VPageChannel_CompletionNotify(VPageChannel *channel, @@ -194,10 +517,55 @@ int VPageChannel_CompletionNotify(VPageChannel *channel, int len, VPageChannelBuffer *buffer); +/* + ************************************************************************ + * VPageChannel_MapToMa */ /** + * + * \brief Map guest PA in an element to a list of MAs. + * + * Map a guest physical address to a list of hypervisor machine + * addresses. + * + * \note Only applicable on VMKernel. + * + * \param[in] channel Channel on which to map. + * \param[in] paElem Guest's physical address. + * \param[out] maElems Hypervisor machine addresses. + * \param[in] numElems Max number of hypervisor elements. + * + * \retval elems Number of mapped elements. + * + ************************************************************************ + */ + int VPageChannel_MapToMa(VPageChannel *channel, VPageChannelElem paElem, VPageChannelElem *maElems, uint32 numElems); + +/* + ************************************************************************ + * VPageChannel_UnmapMa */ /** + * + * \brief Unmap MA for a buffer. + * + * Unmap hypervisor machine addresses referring to a guest physical + * addresses. + * + * \note Only applicable on VMKernel. + * + * \see VPageChannel_MapToMa + * + * \param[in] channel Channel for which to unmap. + * \param[in] buffer Buffer containing elements to unmap. + * \param[in] numElems Number of elements to unmap. + * + * \retval 0 Unmap successful. + * \retval -1 World not found for channel. + * + ************************************************************************ + */ + int VPageChannel_UnmapMa(VPageChannel *channel, VPageChannelBuffer *buffer, int numElems); @@ -205,34 +573,113 @@ int VPageChannel_UnmapMa(VPageChannel *channel, #endif // VMKERNEL /* - * Common functions. + ************************************************************************ + * VPageChannel_Destroy */ /** + * + * \brief Destroy the given channel. + * + * Destroy the given channel. This will disconnect from the peer + * channel (if connected) and release all resources. + * + * \see VPageChannel_CreateInVMK + * \see VPageChannel_CreateInVM + * + * \param[in] channel The channel to be destroyed. + * + ************************************************************************ */ void VPageChannel_Destroy(VPageChannel *channel); + +/* + ************************************************************************ + * VPageChannel_Send */ /** + * + * \brief Send a packet to the channel's peer. + * + * Send a packet to the channel's peer. A message and a number of + * elements may optionally be sent. If the send is successful, the + * elements are owned by the peer and only the buffer itself should + * be released, but not the elements within. If the send fails, the + * client should release the buffer and the elements. + * + * \see VPageChannel_SendPacket + * + * \param[in] channel Channel on which to send. + * \param[in] type Type of packet to send. + * \param[in] message Optional message to send. + * \param[in] len Length of optional message. + * \param[in] buffer Buffer (of elements) to send. + * + * \retval VMCI_SUCCESS Packet successfully sent, buffer elements + * owned by peer. + * \retval other Failure to send, client should release + * elements. + * + ************************************************************************ + */ + int VPageChannel_Send(VPageChannel *channel, VPageChannelPacketType type, char *message, int len, VPageChannelBuffer *buffer); + +/* + ************************************************************************ + * VPageChannel_SendPacket */ /** + * + * \brief Send the given packet to the channel's peer. + * + * Send a client-constructed packet to the channel's peer. If the + * send is successful, any elements in the packet are owned by the + * peer. Otherwise, the client retains ownership. + * + * \see VPageChannel_Send + * + * \param[in] channel Channel on which to send. + * \param[in] packet Packet to be sent. + * + * \retval VMCI_SUCCESS Packet successfully sent, buffer elements + * owned by peer. + * \retval other Failure to send, client should release + * elements. + * + ************************************************************************ + */ + int VPageChannel_SendPacket(VPageChannel *channel, VPageChannelPacket *packet); -void VPageChannel_PollRecvQ(VPageChannel *channel); +/* + ************************************************************************ + * VPageChannel_PollRecvQ */ /** + * + * \brief Poll the channel's receive queue for packets. + * + * Poll the channel's receive queue for packets from the peer. If any + * packets are available, the channel's receive callback will be invoked. + * + * \param[in] channel Channel to poll. + * + ************************************************************************ + */ + +void VPageChannel_PollRecvQ(VPageChannel *channel); /* - *----------------------------------------------------------------------------- + ************************************************************************ + * VPageChannel_BufferLen */ /** * - * VPageChannelPacket_BufferLen -- + * \brief Determine the length of a packet. * - * Calculate the length of the given packet. + * Determine the length of the given packet in bytes. * - * Results: - * The length of the given packet in bytes. + * \param[in] packet Packet for which length is to be determined. * - * Side effects: - * None. + * \retval bytes Size of the packet in bytes. * - *----------------------------------------------------------------------------- + ************************************************************************ */ static INLINE size_t @@ -252,7 +699,7 @@ VPageChannelPacket_BufferLen(VPageChannelPacket *packet) // IN return len; } - +/** \cond PRIVATE */ #if defined(linux) && !defined(VMKERNEL) #include "compat_pci.h" #define vmci_pci_map_page(_pg, _off, _sz, _dir) \ @@ -260,5 +707,6 @@ VPageChannelPacket_BufferLen(VPageChannelPacket *packet) // IN #define vmci_pci_unmap_page(_dma, _sz, _dir) \ pci_unmap_page(NULL, (_dma), (_sz), (_dir)) #endif // linux && !VMKERNEL +/** \endcond PRIVATE */ #endif // _VMCI_PACKET_H_ diff --git a/open-vm-tools/services/plugins/vix/foundryToolsDaemon.c b/open-vm-tools/services/plugins/vix/foundryToolsDaemon.c index b721994e5..0b700c279 100644 --- a/open-vm-tools/services/plugins/vix/foundryToolsDaemon.c +++ b/open-vm-tools/services/plugins/vix/foundryToolsDaemon.c @@ -1050,6 +1050,7 @@ gboolean ToolsDaemonTcloReceiveVixCommand(RpcInData *data) // IN { VixError err = VIX_OK; + uint32 additionalError; char *requestName = NULL; VixCommandRequestHeader *requestMsg = NULL; size_t maxResultBufferSize; @@ -1117,12 +1118,22 @@ abort: /* * All Foundry tools commands return results that start with a foundry error * and a guest-OS-specific error. + * + * NOTE: We have always been returning an additional 32 bit error (errno, + * or GetLastError() for Windows) along with the 64 bit VixError. The VMX + * side has been dropping the higher order 32 bits of VixError (by copying + * it onto a 32 bit error). They do save the additional error but as far + * as we can tell, it was not getting used by foundry. So at this place, + * for certain guest commands that have extra error information tucked into + * the higher order 32 bits of the VixError, we use that extra error as the + * additional error to be sent back to VMX. */ + additionalError = VixTools_GetAdditionalError(requestMsg->opCode, err); Str_Sprintf(tcloBuffer, sizeof tcloBuffer, "%"FMT64"d %d ", err, - Err_Errno()); + additionalError); destPtr = tcloBuffer + strlen(tcloBuffer); /* diff --git a/open-vm-tools/services/plugins/vix/vixTools.c b/open-vm-tools/services/plugins/vix/vixTools.c index e7365ca38..fb5a71c80 100644 --- a/open-vm-tools/services/plugins/vix/vixTools.c +++ b/open-vm-tools/services/plugins/vix/vixTools.c @@ -106,6 +106,7 @@ #include "impersonate.h" #include "vixOpenSource.h" #include "vixToolsInt.h" +#include "vmware/tools/plugin.h" #ifdef _WIN32 #include "registryWin32.h" @@ -123,8 +124,7 @@ * No support for open-vm-tools. */ #if (defined(_WIN32) || defined(linux)) && !defined(OPEN_VM_TOOLS) -// XXX Disabled until VGAuth API signature changes work through GoBuild -#define SUPPORT_VGAUTH 0 +#define SUPPORT_VGAUTH 1 #else #define SUPPORT_VGAUTH 0 #endif @@ -137,8 +137,20 @@ #include "VGAuthIdProvider.h" #define VMTOOLSD_APP_NAME "vmtoolsd" + +#define VIXTOOLS_CONFIG_USE_VGAUTH_NAME "useVGAuth" +/* + * XXX Leave this off by default until the VGAuth service is being + * officially installed. + */ +#define USE_VGAUTH_DEFAULT FALSE + +static gboolean gSupportVGAuth = USE_VGAUTH_DEFAULT; +static gboolean QueryVGAuthConfig(GKeyFile *confDictRef); + #endif + #define SECONDS_BETWEEN_POLL_TEST_FINISHED 1 /* @@ -576,6 +588,10 @@ VixError GuestAuthPasswordAuthenticateImpersonate( char const *obfuscatedNamePassword, void **userToken); +VixError GuestAuthSAMLAuthenticateAndImpersonate( + char const *obfuscatedNamePassword, + void **userToken); + void GuestAuthUnimpersonate(); #if SUPPORT_VGAUTH @@ -607,6 +623,9 @@ VixTools_Initialize(Bool thisProcessRunsAsRootParam, void *clientData) // IN { VixError err = VIX_OK; +#if SUPPORT_VGAUTH + ToolsAppCtx *ctx = (ToolsAppCtx *) clientData; +#endif /* * Run unit tests on DEVEL builds. @@ -640,6 +659,10 @@ VixTools_Initialize(Bool thisProcessRunsAsRootParam, */ #endif +#if SUPPORT_VGAUTH + gSupportVGAuth = QueryVGAuthConfig(ctx->config); +#endif + #ifdef _WIN32 err = VixToolsInitializeWin32(); if (VIX_FAILED(err)) { @@ -2230,6 +2253,9 @@ VixToolsTranslateVGAuthError(VGAuthError vgErr) __FUNCTION__, vgErr); break; } + Debug("%s: translated VGAuth err "VGAUTHERR_FMT64X" to Vix err %"FMT64"d\n", + __FUNCTION__, vgErr, err); + return err; } @@ -7240,6 +7266,18 @@ VixToolsImpersonateUser(VixCommandRequestHeader *requestMsg, // IN } break; } + case VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN: + { + VixCommandSAMLToken *samlStruct = + (VixCommandSAMLToken *) credentialField; + credentialField += sizeof(*samlStruct); + + err = VixToolsImpersonateUserImplEx(NULL, + credentialType, + credentialField, + userToken); + break; + } case VIX_USER_CREDENTIAL_SSPI: /* * SSPI currently only supported in ticketed sessions @@ -7424,11 +7462,16 @@ VixToolsImpersonateUserImplEx(char const *credentialTypeStr, // IN */ if ((VIX_USER_CREDENTIAL_NAME_PASSWORD != credentialType) && (VIX_USER_CREDENTIAL_NAME_PASSWORD_OBFUSCATED != credentialType) - && (VIX_USER_CREDENTIAL_TICKETED_SESSION != credentialType)) { + && (VIX_USER_CREDENTIAL_TICKETED_SESSION != credentialType) +#if SUPPORT_VGAUTH + && (VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN != credentialType) +#endif + ) { err = VIX_E_NOT_SUPPORTED; goto abort; } + /* * Use the GuestAuth library to do name-password authentication * and impersonation. @@ -7444,6 +7487,16 @@ VixToolsImpersonateUserImplEx(char const *credentialTypeStr, // IN goto abort; } + if (VIX_USER_CREDENTIAL_SAML_BEARER_TOKEN == credentialType) { + if (GuestAuthEnabled()) { + err = GuestAuthSAMLAuthenticateAndImpersonate(obfuscatedNamePassword, + userToken); + } else { + err = VIX_E_NOT_SUPPORTED; + } + goto abort; + } + /* Get the authToken and impersonate */ if (VIX_USER_CREDENTIAL_TICKETED_SESSION == credentialType) { #ifdef _WIN32 @@ -8749,7 +8802,8 @@ VixToolsAddAuthPrincipal(VixCommandRequestHeader *requestMsg) // IN si.subject.val.name = (char *) principalName; si.comment = (char *) principalComment; - vgErr = VGAuth_AddSubject(ctx, userName, req->addMapping, pemCert, &si); + vgErr = VGAuth_AddSubject(ctx, userName, req->addMapping, pemCert, &si, + 0, NULL); if (VGAUTH_FAILED(vgErr)) { err = VixToolsTranslateVGAuthError(vgErr); } @@ -8860,13 +8914,13 @@ VixToolsRemoveAuthPrincipal(VixCommandRequestHeader *requestMsg) // IN } if (VIX_GUEST_AUTH_PRINCIPAL_TYPE_NONE == req->principalType) { - vgErr = VGAuth_RemoveCert(ctx, userName, pemCert); + vgErr = VGAuth_RemoveCert(ctx, userName, pemCert, 0, NULL); } else { subj.type = (req->principalType == VIX_GUEST_AUTH_PRINCIPAL_TYPE_NAMED) ? VGAUTH_SUBJECT_NAMED : VGAUTH_SUBJECT_ANY; subj.val.name = (char *) principalName; - vgErr = VGAuth_RemoveSubject(ctx, userName, pemCert, &subj); + vgErr = VGAuth_RemoveSubject(ctx, userName, pemCert, &subj, 0, NULL); } if (VGAUTH_FAILED(vgErr)) { err = VixToolsTranslateVGAuthError(vgErr); @@ -8966,7 +9020,7 @@ VixToolsListAuthPrincipals(VixCommandRequestHeader *requestMsg, // IN goto abort; } - vgErr = VGAuth_QueryIdProviders(ctx, userName, &num, &idList); + vgErr = VGAuth_QueryIdProviders(ctx, userName, 0, NULL, &num, &idList); if (VGAUTH_FAILED(vgErr)) { err = VixToolsTranslateVGAuthError(vgErr); goto abort; @@ -9137,7 +9191,7 @@ VixToolsListMappedPrincipals(VixCommandRequestHeader *requestMsg, // IN goto abort; } - vgErr = VGAuth_QueryMappedIdentities(ctx, &num, &miList); + vgErr = VGAuth_QueryMappedIdentities(ctx, 0, NULL, &num, &miList); if (VGAUTH_FAILED(vgErr)) { err = VixToolsTranslateVGAuthError(vgErr); goto abort; @@ -10300,8 +10354,6 @@ VixToolsRewriteError(uint32 opCode, // IN { VixError newError = origError; - ASSERT(VIX_ERROR_CODE(origError) == origError); - switch (opCode) { /* * This should include all non-VI guest operations. @@ -10334,6 +10386,7 @@ VixToolsRewriteError(uint32 opCode, // IN case VIX_COMMAND_LIST_FILESYSTEMS: case VIX_COMMAND_WAIT_FOR_TOOLS: case VIX_COMMAND_CAPTURE_SCREEN: + ASSERT(VIX_ERROR_CODE(origError) == origError); switch (origError) { case VIX_E_INVALID_LOGIN_CREDENTIALS: newError = VIX_E_GUEST_USER_PERMISSIONS; @@ -10346,6 +10399,48 @@ VixToolsRewriteError(uint32 opCode, // IN } +/* + *----------------------------------------------------------------------------- + * + * VixTools_GetAdditionalError -- + * + * Gets the vix extra/additional error if any. + * + * Some errors returned by tools may have extra error in + * the higher order 32 bits. We need to pass that back. + * + * Results: + * uint32 + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +uint32 +VixTools_GetAdditionalError(uint32 opCode, // IN + VixError error) // IN +{ + uint32 err; + + switch (opCode) { + case VIX_COMMAND_CREATE_REGISTRY_KEY: + case VIX_COMMAND_LIST_REGISTRY_KEYS: + case VIX_COMMAND_DELETE_REGISTRY_KEY: + case VIX_COMMAND_SET_REGISTRY_VALUE: + case VIX_COMMAND_LIST_REGISTRY_VALUES: + case VIX_COMMAND_DELETE_REGISTRY_VALUE: + err = VIX_ERROR_EXTRA_ERROR(error); + break; + default: + err = Err_Errno(); + } + + return err; +} + + /* *----------------------------------------------------------------------------- * @@ -10631,7 +10726,7 @@ static Bool GuestAuthEnabled(void) { #if SUPPORT_VGAUTH - return TRUE; + return gSupportVGAuth; #else return FALSE; #endif @@ -10684,13 +10779,94 @@ GuestAuthPasswordAuthenticateImpersonate( } vgErr = VGAuth_ValidateUsernamePassword(ctx, username, password, + 0, NULL, &newHandle); if (VGAUTH_FAILED(vgErr)) { err = VixToolsTranslateVGAuthError(vgErr); goto done; } - vgErr = VGAuth_Impersonate(ctx, newHandle); + vgErr = VGAuth_Impersonate(ctx, newHandle, 0, NULL); + if (VGAUTH_FAILED(vgErr)) { + err = VixToolsTranslateVGAuthError(vgErr); + goto done; + } + + *userToken = VGAUTH_GENERIC_USER_TOKEN; + + err = VIX_OK; + +done: + + if (newHandle) { + VGAuth_UserHandleFree(newHandle); + } + + return err; +#else + return VIX_E_NOT_SUPPORTED; +#endif +} + + +/* + *----------------------------------------------------------------------------- + * + * GuestAuthSAMLAuthenticateAndImpersonate + * + * Do SAML bearer token authentication and impersonation using + * the GuestAuth library. + * + * Results: + * VIX_OK if successful. Other VixError code otherwise. + * + * Side effects: + * Current process impersonates. + * + *----------------------------------------------------------------------------- + */ + +VixError +GuestAuthSAMLAuthenticateAndImpersonate( + char const *obfuscatedNamePassword, // IN + void **userToken) // OUT +{ +#if SUPPORT_VGAUTH + VixError err; + char *token; + char *username; + VGAuthContext *ctx = NULL; + VGAuthError vgErr; + VGAuthUserHandle *newHandle = NULL; + + Debug(">%s\n", __FUNCTION__); + err = VixMsg_DeObfuscateNamePassword(obfuscatedNamePassword, + &token, + &username); + if (err != VIX_OK) { + goto done; + } + + err = VIX_E_INVALID_LOGIN_CREDENTIALS; + + vgErr = TheVGAuthContext(&ctx); + if (VGAUTH_FAILED(vgErr)) { + err = VixToolsTranslateVGAuthError(vgErr); + goto done; + } + + vgErr = VGAuth_ValidateSamlBearerToken(ctx, + token, + username, + 0, + NULL, + &newHandle); + if (VGAUTH_FAILED(vgErr)) { + err = VixToolsTranslateVGAuthError(vgErr); + goto done; + } + + vgErr = VGAuth_Impersonate(ctx, newHandle, 0, NULL); if (VGAUTH_FAILED(vgErr)) { err = VixToolsTranslateVGAuthError(vgErr); goto done; @@ -10706,6 +10882,8 @@ done: VGAuth_UserHandleFree(newHandle); } + Debug("<%s\n", __FUNCTION__); + return err; #else return VIX_E_NOT_SUPPORTED; @@ -10746,6 +10924,54 @@ GuestAuthUnimpersonate(void) #if SUPPORT_VGAUTH +/* + *----------------------------------------------------------------------------- + * + * QueryVGAuthConfig + * + * Check the tools configuration to see if VGAuth should be used. + * + * Results: + * TRUE if vgauth should be used, FALSE if not. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static gboolean +QueryVGAuthConfig(GKeyFile *confDictRef) // IN +{ + gboolean useVGAuth; + gboolean retVal = USE_VGAUTH_DEFAULT; + GError *gErr = NULL; + + if (confDictRef != NULL) { + useVGAuth = g_key_file_get_boolean(confDictRef, + VIX_TOOLS_CONFIG_API_GROUPNAME, + VIXTOOLS_CONFIG_USE_VGAUTH_NAME, + &gErr); + + /* + * g_key_file_get_boolean() will return FALSE and set an error + * if the value isn't in config, so use the default in that + * case. + */ + if (!useVGAuth && (NULL != gErr)) { + g_error_free(gErr); + retVal = USE_VGAUTH_DEFAULT; + } else { + retVal = useVGAuth; + } + } + + Debug("%s: vgauth usage is: %d\n", __FUNCTION__, retVal); + + return retVal; +} + + /* *----------------------------------------------------------------------------- * @@ -10772,8 +10998,16 @@ TheVGAuthContext(VGAuthContext **ctx) // OUT static VGAuthContext *vgaCtx = NULL; VGAuthError vgaCode = VGAUTH_E_OK; + /* + * XXX This needs to handle errors better -- if the service gets + * reset, the context will point to junk and anything using it will + * fail. + * + * Maybe add a no-op API here to poke it? Or make the underlying + * VGAuth code smarter. + */ if (vgaCtx == NULL) { - vgaCode = VGAuth_Init(VMTOOLSD_APP_NAME, 0, NULL, 0, &vgaCtx); + vgaCode = VGAuth_Init(VMTOOLSD_APP_NAME, 0, NULL, &vgaCtx); } *ctx = vgaCtx; diff --git a/open-vm-tools/services/plugins/vix/vixToolsInt.h b/open-vm-tools/services/plugins/vix/vixToolsInt.h index fa5b9a6d2..5b37c5c89 100644 --- a/open-vm-tools/services/plugins/vix/vixToolsInt.h +++ b/open-vm-tools/services/plugins/vix/vixToolsInt.h @@ -92,6 +92,8 @@ VixError VixTools_ProcessVixCommand(VixCommandRequestHeader *requestMsg, size_t *resultLen, Bool *deleteResultBufferResult); +uint32 VixTools_GetAdditionalError(uint32 opCode, + VixError error); Bool VixToolsImpersonateUserImpl(char const *credentialTypeStr, int credentialType,