diff options
Diffstat (limited to 'src/VBox/GuestHost/HGSMI/HGSMICommon.cpp')
-rw-r--r-- | src/VBox/GuestHost/HGSMI/HGSMICommon.cpp | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/src/VBox/GuestHost/HGSMI/HGSMICommon.cpp b/src/VBox/GuestHost/HGSMI/HGSMICommon.cpp new file mode 100644 index 00000000..31b8eba5 --- /dev/null +++ b/src/VBox/GuestHost/HGSMI/HGSMICommon.cpp @@ -0,0 +1,450 @@ +/* $Id: HGSMICommon.cpp $ */ +/** @file + * VBox Host Guest Shared Memory Interface (HGSMI) - Functions common to both host and guest. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#define LOG_DISABLED /* Maybe we can enabled it all the time now? */ +/** @note commented out all logging statements to avoid pulling the logging + * sub-system into places like the Linux kernel driver. Perhaps the best + * thing would be to use return enough information for callers to log what + * is needed. */ +#define LOG_GROUP LOG_GROUP_HGSMI + +#include <VBoxVideoIPRT.h> + +#include <HGSMI.h> +// #include <VBox/log.h> + + +/* Channel flags. */ +#define HGSMI_CH_F_REGISTERED 0x01 + +/* Assertions for situations which could happen and normally must be processed properly + * but must be investigated during development: guest misbehaving, etc. + */ +#ifdef HGSMI_STRICT +#define HGSMI_STRICT_ASSERT_FAILED() AssertFailed() +#define HGSMI_STRICT_ASSERT(expr) Assert(expr) +#else +#define HGSMI_STRICT_ASSERT_FAILED() do {} while (0) +#define HGSMI_STRICT_ASSERT(expr) do {} while (0) +#endif /* !HGSMI_STRICT */ + +/* + * We do not want assertions in Linux kernel code to reduce symbol dependencies. + */ +#if defined(IN_RING0) && defined(RT_OS_LINUX) +# define HGSMI_ASSERT_PTR_RETURN(a, b) if (!(a)) return (b) +#else +# define HGSMI_ASSERT_PTR_RETURN(a, b) if (!(a)) return (b) +#endif /* !IN_RING0 && RT_OS_LINUX */ + +/* One-at-a-Time Hash from + * http://www.burtleburtle.net/bob/hash/doobs.html + * + * ub4 one_at_a_time(char *key, ub4 len) + * { + * ub4 hash, i; + * for (hash=0, i=0; i<len; ++i) + * { + * hash += key[i]; + * hash += (hash << 10); + * hash ^= (hash >> 6); + * } + * hash += (hash << 3); + * hash ^= (hash >> 11); + * hash += (hash << 15); + * return hash; + * } + */ + +static uint32_t hgsmiHashBegin(void) +{ + return 0; +} + +static uint32_t hgsmiHashProcess(uint32_t hash, const void RT_UNTRUSTED_VOLATILE_HSTGST *pvData, size_t cbData) +{ + const uint8_t *pu8Data = (const uint8_t *)pvData; + + while (cbData--) + { + hash += *pu8Data++; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + return hash; +} + +static uint32_t hgsmiHashEnd(uint32_t hash) +{ + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; +} + +uint32_t HGSMIChecksum(HGSMIOFFSET offBuffer, const HGSMIBUFFERHEADER RT_UNTRUSTED_VOLATILE_HSTGST *pHeader, + const HGSMIBUFFERTAIL RT_UNTRUSTED_VOLATILE_HSTGST *pTail) +{ + uint32_t u32Checksum = hgsmiHashBegin(); + + u32Checksum = hgsmiHashProcess(u32Checksum, &offBuffer, sizeof(offBuffer)); + u32Checksum = hgsmiHashProcess(u32Checksum, pHeader, sizeof(HGSMIBUFFERHEADER)); + u32Checksum = hgsmiHashProcess(u32Checksum, pTail, RT_UOFFSETOF(HGSMIBUFFERTAIL, u32Checksum)); + + return hgsmiHashEnd(u32Checksum); +} + +int HGSMIAreaInitialize(HGSMIAREA *pArea, void *pvBase, HGSMISIZE cbArea, HGSMIOFFSET offBase) +{ + uint8_t *pu8Base = (uint8_t *)pvBase; + + if ( !pArea /* Check that the area: */ + || cbArea < HGSMIBufferMinimumSize() /* large enough; */ + || pu8Base + cbArea < pu8Base /* no address space wrap; */ + || offBase > UINT32_C(0xFFFFFFFF) - cbArea /* area within the 32 bit space: offBase + cbMem <= 0xFFFFFFFF. */ + ) + { + return VERR_INVALID_PARAMETER; + } + + pArea->pu8Base = pu8Base; + pArea->offBase = offBase; + pArea->offLast = cbArea - HGSMIBufferMinimumSize() + offBase; + pArea->cbArea = cbArea; + + return VINF_SUCCESS; +} + +void HGSMIAreaClear(HGSMIAREA *pArea) +{ + if (pArea) + { + RT_ZERO(*pArea); + } +} + +/* Initialize the memory buffer including its checksum. + * No changes alloed to the header and the tail after that. + */ +HGSMIOFFSET HGSMIBufferInitializeSingle(const HGSMIAREA *pArea, + HGSMIBUFFERHEADER *pHeader, + HGSMISIZE cbBuffer, + uint8_t u8Channel, + uint16_t u16ChannelInfo) +{ + if ( !pArea + || !pHeader + || cbBuffer < HGSMIBufferMinimumSize()) + { + return HGSMIOFFSET_VOID; + } + + /* Buffer must be within the area: + * * header data size do not exceed the maximum data size; + * * buffer address is greater than the area base address; + * * buffer address is lower than the maximum allowed for the given data size. + */ + HGSMISIZE cbMaximumDataSize = pArea->offLast - pArea->offBase; + uint32_t u32DataSize = cbBuffer - HGSMIBufferMinimumSize(); + + if ( u32DataSize > cbMaximumDataSize + || (uint8_t *)pHeader < pArea->pu8Base + || (uint8_t *)pHeader > pArea->pu8Base + cbMaximumDataSize - u32DataSize) + { + return HGSMIOFFSET_VOID; + } + + HGSMIOFFSET offBuffer = HGSMIPointerToOffset(pArea, pHeader); + + pHeader->u8Flags = HGSMI_BUFFER_HEADER_F_SEQ_SINGLE; + pHeader->u32DataSize = u32DataSize; + pHeader->u8Channel = u8Channel; + pHeader->u16ChannelInfo = u16ChannelInfo; + RT_ZERO(pHeader->u.au8Union); + + HGSMIBUFFERTAIL RT_UNTRUSTED_VOLATILE_HSTGST *pTail = HGSMIBufferTailFromPtr(pHeader, u32DataSize); + pTail->u32Reserved = 0; + pTail->u32Checksum = HGSMIChecksum(offBuffer, pHeader, pTail); + + return offBuffer; +} + +int HGSMIHeapSetup(HGSMIHEAP *pHeap, void *pvBase, HGSMISIZE cbArea, HGSMIOFFSET offBase, const HGSMIENV *pEnv) +{ + HGSMI_ASSERT_PTR_RETURN(pHeap, VERR_INVALID_PARAMETER); + HGSMI_ASSERT_PTR_RETURN(pvBase, VERR_INVALID_PARAMETER); + + int rc = HGSMIAreaInitialize(&pHeap->area, pvBase, cbArea, offBase); + if (RT_SUCCESS(rc)) + { + rc = HGSMIMAInit(&pHeap->ma, &pHeap->area, NULL, 0, 0, pEnv); + if (RT_FAILURE(rc)) + { + HGSMIAreaClear(&pHeap->area); + } + } + + return rc; +} + +void HGSMIHeapDestroy(HGSMIHEAP *pHeap) +{ + if (pHeap) + { + HGSMIMAUninit(&pHeap->ma); + RT_ZERO(*pHeap); + } +} + +void RT_UNTRUSTED_VOLATILE_HOST *HGSMIHeapAlloc(HGSMIHEAP *pHeap, + HGSMISIZE cbData, + uint8_t u8Channel, + uint16_t u16ChannelInfo) +{ + HGSMISIZE cbAlloc = HGSMIBufferRequiredSize(cbData); + HGSMIBUFFERHEADER *pHeader = (HGSMIBUFFERHEADER *)HGSMIHeapBufferAlloc(pHeap, cbAlloc); + if (pHeader) + { + HGSMIOFFSET offBuffer = HGSMIBufferInitializeSingle(HGSMIHeapArea(pHeap), pHeader, + cbAlloc, u8Channel, u16ChannelInfo); + if (offBuffer == HGSMIOFFSET_VOID) + { + HGSMIHeapBufferFree(pHeap, pHeader); + pHeader = NULL; + } + } + + return pHeader? HGSMIBufferDataFromPtr(pHeader): NULL; +} + +void HGSMIHeapFree(HGSMIHEAP *pHeap, void RT_UNTRUSTED_VOLATILE_GUEST *pvData) +{ + if (pvData) + { + HGSMIBUFFERHEADER RT_UNTRUSTED_VOLATILE_HOST *pHeader = HGSMIBufferHeaderFromData(pvData); + HGSMIHeapBufferFree(pHeap, pHeader); + } +} + +void RT_UNTRUSTED_VOLATILE_HSTGST *HGSMIHeapBufferAlloc(HGSMIHEAP *pHeap, HGSMISIZE cbBuffer) +{ + return HGSMIMAAlloc(&pHeap->ma, cbBuffer); +} + +void HGSMIHeapBufferFree(HGSMIHEAP *pHeap, void RT_UNTRUSTED_VOLATILE_GUEST *pvBuf) +{ + HGSMIMAFree(&pHeap->ma, pvBuf); +} + +typedef struct HGSMIBUFFERCONTEXT +{ + /** The original buffer header. */ + const HGSMIBUFFERHEADER RT_UNTRUSTED_VOLATILE_HSTGST *pHeader; + /** Payload data in the buffer. */ + void RT_UNTRUSTED_VOLATILE_HSTGST *pvData; + /** Size of data */ + uint32_t cbData; +} HGSMIBUFFERCONTEXT; + +/** Verify that the given offBuffer points to a valid buffer, which is within the area. + * + * @returns VBox status and the buffer information in pBufferContext. + * @param pArea Area which supposed to contain the buffer. + * @param offBuffer The buffer location in the area. + * @param pBufferContext Where to write information about the buffer. + */ +static int hgsmiVerifyBuffer(const HGSMIAREA *pArea, HGSMIOFFSET offBuffer, HGSMIBUFFERCONTEXT *pBufferContext) +{ + // LogFlowFunc(("buffer 0x%x, area %p %x [0x%x;0x%x]\n", + // offBuffer, pArea->pu8Base, pArea->cbArea, pArea->offBase, pArea->offLast)); + + int rc = VINF_SUCCESS; + + if ( offBuffer < pArea->offBase + || offBuffer > pArea->offLast) + { + // LogFunc(("offset 0x%x is outside the area [0x%x;0x%x]!!!\n", + // offBuffer, pArea->offBase, pArea->offLast)); + rc = VERR_INVALID_PARAMETER; + HGSMI_STRICT_ASSERT_FAILED(); + } + else + { + void RT_UNTRUSTED_VOLATILE_HSTGST *pvBuffer = HGSMIOffsetToPointer(pArea, offBuffer); + HGSMIBUFFERHEADER header; + memcpy(&header, (void *)HGSMIBufferHeaderFromPtr(pvBuffer), sizeof(header)); + ASMCompilerBarrier(); + + /* Quick check of the data size, it should be less than the maximum + * data size for the buffer at this offset. + */ + // LogFlowFunc(("datasize check: header.u32DataSize = 0x%x pArea->offLast - offBuffer = 0x%x\n", + // header.u32DataSize, pArea->offLast - offBuffer)); + + if (header.u32DataSize <= pArea->offLast - offBuffer) + { + HGSMIBUFFERTAIL tail; + memcpy(&tail, (void *)HGSMIBufferTailFromPtr(pvBuffer, header.u32DataSize), sizeof(tail)); + ASMCompilerBarrier(); + + /* At least both header and tail structures are in the area. Check the checksum. */ + uint32_t u32Checksum = HGSMIChecksum(offBuffer, &header, &tail); + // LogFlowFunc(("checksum check: u32Checksum = 0x%x pTail->u32Checksum = 0x%x\n", + // u32Checksum, tail.u32Checksum)); + if (u32Checksum == tail.u32Checksum) + { + /* Success. */ + pBufferContext->pHeader = HGSMIBufferHeaderFromPtr(pvBuffer); + pBufferContext->pvData = HGSMIBufferDataFromPtr(pvBuffer); + pBufferContext->cbData = header.u32DataSize; + } + else + { + // LogFunc(("invalid checksum 0x%x, expected 0x%x!!!\n", + // u32Checksum, tail.u32Checksum)); + rc = VERR_INVALID_STATE; + HGSMI_STRICT_ASSERT_FAILED(); + } + } + else + { + // LogFunc(("invalid data size 0x%x, maximum is 0x%x!!!\n", + // header.u32DataSize, pArea->offLast - offBuffer)); + rc = VERR_TOO_MUCH_DATA; + HGSMI_STRICT_ASSERT_FAILED(); + } + } + + return rc; +} + +/** Helper to convert HGSMI channel index to the channel structure pointer. + * + * @returns Pointer to the channel data. + * @param pChannelInfo The channel pool. + * @param u8Channel The channel index. + */ +HGSMICHANNEL *HGSMIChannelFindById(HGSMICHANNELINFO *pChannelInfo, + uint8_t u8Channel) +{ + AssertCompile(RT_ELEMENTS(pChannelInfo->Channels) >= 0x100); + HGSMICHANNEL *pChannel = &pChannelInfo->Channels[u8Channel]; + + if (pChannel->u8Flags & HGSMI_CH_F_REGISTERED) + { + return pChannel; + } + + return NULL; +} + +/** Process a guest buffer. + * + * @returns VBox status code. + * @param pArea Area which supposed to contain the buffer. + * @param pChannelInfo The channel pool. + * @param offBuffer The buffer location in the area. + */ +int HGSMIBufferProcess(const HGSMIAREA *pArea, + HGSMICHANNELINFO *pChannelInfo, + HGSMIOFFSET offBuffer) +{ + // LogFlowFunc(("pArea %p, offBuffer 0x%x\n", pArea, offBuffer)); + + HGSMI_ASSERT_PTR_RETURN(pArea, VERR_INVALID_PARAMETER); + HGSMI_ASSERT_PTR_RETURN(pChannelInfo, VERR_INVALID_PARAMETER); + + /* Guest has prepared a command description at 'offBuffer'. */ + HGSMIBUFFERCONTEXT bufferContext = { NULL, NULL, 0 }; /* Makes old GCC happier. */ + int rc = hgsmiVerifyBuffer(pArea, offBuffer, &bufferContext); + if (RT_SUCCESS(rc)) + { + /* Pass the command to the appropriate handler registered with this instance. + * Start with the handler list head, which is the preallocated HGSMI setup channel. + */ + const HGSMICHANNEL *pChannel = HGSMIChannelFindById(pChannelInfo, bufferContext.pHeader->u8Channel); + if (pChannel) + { + const HGSMICHANNELHANDLER *pHandler = &pChannel->handler; + if (pHandler->pfnHandler) + { + pHandler->pfnHandler(pHandler->pvHandler, bufferContext.pHeader->u16ChannelInfo, + bufferContext.pvData, bufferContext.cbData); + } + HGSMI_STRICT_ASSERT(RT_SUCCESS(hgsmiVerifyBuffer(pArea, offBuffer, &bufferContext))); + } + else + { + rc = VERR_INVALID_FUNCTION; + HGSMI_STRICT_ASSERT_FAILED(); + } + } + + return rc; +} + +/** Register a new HGSMI channel by index. + * + * @returns VBox status code. + * @param pChannelInfo The channel pool managed by the caller. + * @param u8Channel Index of the channel. + * @param pszName Name of the channel (optional, allocated by the caller). + * @param pfnChannelHandler The channel callback. + * @param pvChannelHandler The callback pointer. + */ +int HGSMIChannelRegister(HGSMICHANNELINFO *pChannelInfo, + uint8_t u8Channel, + const char *pszName, + PFNHGSMICHANNELHANDLER pfnChannelHandler, + void *pvChannelHandler) +{ + /* Check whether the channel is already registered. */ + HGSMICHANNEL *pChannel = HGSMIChannelFindById(pChannelInfo, u8Channel); + if (pChannel) + { + HGSMI_STRICT_ASSERT_FAILED(); + return VERR_ALREADY_EXISTS; + } + + /* Channel is not yet registered. */ + pChannel = &pChannelInfo->Channels[u8Channel]; + + pChannel->u8Flags = HGSMI_CH_F_REGISTERED; + pChannel->u8Channel = u8Channel; + + pChannel->handler.pfnHandler = pfnChannelHandler; + pChannel->handler.pvHandler = pvChannelHandler; + + pChannel->pszName = pszName; + + return VINF_SUCCESS; +} |