VirtualBox

source: vbox/trunk/src/VBox/GuestHost/HGSMI/HGSMICommon.cpp@ 82781

Last change on this file since 82781 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.9 KB
RevLine 
[38736]1/* $Id: HGSMICommon.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
[22619]2/** @file
[38736]3 * VBox Host Guest Shared Memory Interface (HGSMI) - Functions common to both host and guest.
[22619]4 */
5
6/*
[76553]7 * Copyright (C) 2006-2019 Oracle Corporation
[22619]8 *
[69391]9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use,
13 * copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following
16 * conditions:
[66544]17 *
[69391]18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
[66544]20 *
[69391]21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
[66544]28 * OTHER DEALINGS IN THE SOFTWARE.
[22619]29 */
30
[38736]31#define LOG_DISABLED /* Maybe we can enabled it all the time now? */
[64156]32/** @note commented out all logging statements to avoid pulling the logging
33 * sub-system into places like the Linux kernel driver. Perhaps the best
34 * thing would be to use return enough information for callers to log what
35 * is needed. */
[38736]36#define LOG_GROUP LOG_GROUP_HGSMI
[64164]37
[66506]38#include <VBoxVideoIPRT.h>
[22619]39
[65381]40#include <HGSMI.h>
[64156]41// #include <VBox/log.h>
[22619]42
43
44/* Channel flags. */
45#define HGSMI_CH_F_REGISTERED 0x01
46
47/* Assertions for situations which could happen and normally must be processed properly
48 * but must be investigated during development: guest misbehaving, etc.
49 */
50#ifdef HGSMI_STRICT
51#define HGSMI_STRICT_ASSERT_FAILED() AssertFailed()
52#define HGSMI_STRICT_ASSERT(expr) Assert(expr)
53#else
54#define HGSMI_STRICT_ASSERT_FAILED() do {} while (0)
55#define HGSMI_STRICT_ASSERT(expr) do {} while (0)
56#endif /* !HGSMI_STRICT */
57
[64650]58/*
59 * We do not want assertions in Linux kernel code to reduce symbol dependencies.
60 */
61#if defined(IN_RING0) && defined(RT_OS_LINUX)
62# define HGSMI_ASSERT_PTR_RETURN(a, b) if (!(a)) return (b)
63#else
64# define HGSMI_ASSERT_PTR_RETURN(a, b) if (!(a)) return (b)
65#endif /* !IN_RING0 && RT_OS_LINUX */
66
[22619]67/* One-at-a-Time Hash from
68 * http://www.burtleburtle.net/bob/hash/doobs.html
69 *
70 * ub4 one_at_a_time(char *key, ub4 len)
71 * {
72 * ub4 hash, i;
73 * for (hash=0, i=0; i<len; ++i)
74 * {
75 * hash += key[i];
76 * hash += (hash << 10);
77 * hash ^= (hash >> 6);
78 * }
79 * hash += (hash << 3);
80 * hash ^= (hash >> 11);
81 * hash += (hash << 15);
82 * return hash;
83 * }
84 */
85
[55474]86static uint32_t hgsmiHashBegin(void)
[22619]87{
88 return 0;
89}
90
[71591]91static uint32_t hgsmiHashProcess(uint32_t hash, const void RT_UNTRUSTED_VOLATILE_HSTGST *pvData, size_t cbData)
[22619]92{
93 const uint8_t *pu8Data = (const uint8_t *)pvData;
94
95 while (cbData--)
96 {
97 hash += *pu8Data++;
98 hash += (hash << 10);
99 hash ^= (hash >> 6);
100 }
101
102 return hash;
103}
104
[55474]105static uint32_t hgsmiHashEnd(uint32_t hash)
[22619]106{
107 hash += (hash << 3);
108 hash ^= (hash >> 11);
109 hash += (hash << 15);
110
111 return hash;
112}
113
[71591]114uint32_t HGSMIChecksum(HGSMIOFFSET offBuffer, const HGSMIBUFFERHEADER RT_UNTRUSTED_VOLATILE_HSTGST *pHeader,
115 const HGSMIBUFFERTAIL RT_UNTRUSTED_VOLATILE_HSTGST *pTail)
[22619]116{
[55474]117 uint32_t u32Checksum = hgsmiHashBegin();
[22619]118
[55474]119 u32Checksum = hgsmiHashProcess(u32Checksum, &offBuffer, sizeof(offBuffer));
120 u32Checksum = hgsmiHashProcess(u32Checksum, pHeader, sizeof(HGSMIBUFFERHEADER));
[73097]121 u32Checksum = hgsmiHashProcess(u32Checksum, pTail, RT_UOFFSETOF(HGSMIBUFFERTAIL, u32Checksum));
[22619]122
[55474]123 return hgsmiHashEnd(u32Checksum);
[22619]124}
125
[71591]126int HGSMIAreaInitialize(HGSMIAREA *pArea, void *pvBase, HGSMISIZE cbArea, HGSMIOFFSET offBase)
[22619]127{
128 uint8_t *pu8Base = (uint8_t *)pvBase;
129
[25062]130 if ( !pArea /* Check that the area: */
[55474]131 || cbArea < HGSMIBufferMinimumSize() /* large enough; */
132 || pu8Base + cbArea < pu8Base /* no address space wrap; */
133 || offBase > UINT32_C(0xFFFFFFFF) - cbArea /* area within the 32 bit space: offBase + cbMem <= 0xFFFFFFFF. */
[22619]134 )
135 {
136 return VERR_INVALID_PARAMETER;
137 }
138
139 pArea->pu8Base = pu8Base;
140 pArea->offBase = offBase;
[55474]141 pArea->offLast = cbArea - HGSMIBufferMinimumSize() + offBase;
[22619]142 pArea->cbArea = cbArea;
143
144 return VINF_SUCCESS;
145}
146
[55474]147void HGSMIAreaClear(HGSMIAREA *pArea)
[22619]148{
149 if (pArea)
150 {
[55474]151 RT_ZERO(*pArea);
[22619]152 }
153}
154
155/* Initialize the memory buffer including its checksum.
156 * No changes alloed to the header and the tail after that.
157 */
[55421]158HGSMIOFFSET HGSMIBufferInitializeSingle(const HGSMIAREA *pArea,
159 HGSMIBUFFERHEADER *pHeader,
160 HGSMISIZE cbBuffer,
161 uint8_t u8Channel,
162 uint16_t u16ChannelInfo)
[22619]163{
[55421]164 if ( !pArea
165 || !pHeader
166 || cbBuffer < HGSMIBufferMinimumSize())
[22619]167 {
168 return HGSMIOFFSET_VOID;
169 }
170
[55421]171 /* Buffer must be within the area:
172 * * header data size do not exceed the maximum data size;
173 * * buffer address is greater than the area base address;
174 * * buffer address is lower than the maximum allowed for the given data size.
175 */
176 HGSMISIZE cbMaximumDataSize = pArea->offLast - pArea->offBase;
177 uint32_t u32DataSize = cbBuffer - HGSMIBufferMinimumSize();
[22619]178
[55421]179 if ( u32DataSize > cbMaximumDataSize
180 || (uint8_t *)pHeader < pArea->pu8Base
181 || (uint8_t *)pHeader > pArea->pu8Base + cbMaximumDataSize - u32DataSize)
[22619]182 {
[55421]183 return HGSMIOFFSET_VOID;
[22619]184 }
185
[55474]186 HGSMIOFFSET offBuffer = HGSMIPointerToOffset(pArea, pHeader);
[22619]187
[55421]188 pHeader->u8Flags = HGSMI_BUFFER_HEADER_F_SEQ_SINGLE;
189 pHeader->u32DataSize = u32DataSize;
190 pHeader->u8Channel = u8Channel;
191 pHeader->u16ChannelInfo = u16ChannelInfo;
[55474]192 RT_ZERO(pHeader->u.au8Union);
[50482]193
[71591]194 HGSMIBUFFERTAIL RT_UNTRUSTED_VOLATILE_HSTGST *pTail = HGSMIBufferTailFromPtr(pHeader, u32DataSize);
[55421]195 pTail->u32Reserved = 0;
[55474]196 pTail->u32Checksum = HGSMIChecksum(offBuffer, pHeader, pTail);
[22619]197
[55421]198 return offBuffer;
[50518]199}
200
[71591]201int HGSMIHeapSetup(HGSMIHEAP *pHeap, void *pvBase, HGSMISIZE cbArea, HGSMIOFFSET offBase, const HGSMIENV *pEnv)
[22619]202{
[64650]203 HGSMI_ASSERT_PTR_RETURN(pHeap, VERR_INVALID_PARAMETER);
204 HGSMI_ASSERT_PTR_RETURN(pvBase, VERR_INVALID_PARAMETER);
[22619]205
[55474]206 int rc = HGSMIAreaInitialize(&pHeap->area, pvBase, cbArea, offBase);
207 if (RT_SUCCESS(rc))
[22619]208 {
[55421]209 rc = HGSMIMAInit(&pHeap->ma, &pHeap->area, NULL, 0, 0, pEnv);
210 if (RT_FAILURE(rc))
[50482]211 {
[55474]212 HGSMIAreaClear(&pHeap->area);
[22619]213 }
214 }
215
216 return rc;
217}
218
[55474]219void HGSMIHeapDestroy(HGSMIHEAP *pHeap)
[22619]220{
221 if (pHeap)
222 {
[55421]223 HGSMIMAUninit(&pHeap->ma);
[55474]224 RT_ZERO(*pHeap);
[22619]225 }
226}
227
[71591]228void RT_UNTRUSTED_VOLATILE_HOST *HGSMIHeapAlloc(HGSMIHEAP *pHeap,
229 HGSMISIZE cbData,
230 uint8_t u8Channel,
231 uint16_t u16ChannelInfo)
[22619]232{
[55474]233 HGSMISIZE cbAlloc = HGSMIBufferRequiredSize(cbData);
234 HGSMIBUFFERHEADER *pHeader = (HGSMIBUFFERHEADER *)HGSMIHeapBufferAlloc(pHeap, cbAlloc);
235 if (pHeader)
[55421]236 {
[55474]237 HGSMIOFFSET offBuffer = HGSMIBufferInitializeSingle(HGSMIHeapArea(pHeap), pHeader,
238 cbAlloc, u8Channel, u16ChannelInfo);
239 if (offBuffer == HGSMIOFFSET_VOID)
240 {
241 HGSMIHeapBufferFree(pHeap, pHeader);
242 pHeader = NULL;
243 }
[55421]244 }
[22619]245
[55474]246 return pHeader? HGSMIBufferDataFromPtr(pHeader): NULL;
[22619]247}
248
[71591]249void HGSMIHeapFree(HGSMIHEAP *pHeap, void RT_UNTRUSTED_VOLATILE_GUEST *pvData)
[22619]250{
[55421]251 if (pvData)
[22619]252 {
[71591]253 HGSMIBUFFERHEADER RT_UNTRUSTED_VOLATILE_HOST *pHeader = HGSMIBufferHeaderFromData(pvData);
[55474]254 HGSMIHeapBufferFree(pHeap, pHeader);
[22619]255 }
256}
257
[71591]258void RT_UNTRUSTED_VOLATILE_HSTGST *HGSMIHeapBufferAlloc(HGSMIHEAP *pHeap, HGSMISIZE cbBuffer)
[41109]259{
[71591]260 return HGSMIMAAlloc(&pHeap->ma, cbBuffer);
[41109]261}
262
[71591]263void HGSMIHeapBufferFree(HGSMIHEAP *pHeap, void RT_UNTRUSTED_VOLATILE_GUEST *pvBuf)
[41109]264{
[55421]265 HGSMIMAFree(&pHeap->ma, pvBuf);
[41109]266}
267
[55342]268typedef struct HGSMIBUFFERCONTEXT
269{
[71591]270 /** The original buffer header. */
271 const HGSMIBUFFERHEADER RT_UNTRUSTED_VOLATILE_HSTGST *pHeader;
272 /** Payload data in the buffer. */
273 void RT_UNTRUSTED_VOLATILE_HSTGST *pvData;
274 /** Size of data */
275 uint32_t cbData;
[55342]276} HGSMIBUFFERCONTEXT;
277
[55474]278/** Verify that the given offBuffer points to a valid buffer, which is within the area.
[56320]279 *
[55474]280 * @returns VBox status and the buffer information in pBufferContext.
281 * @param pArea Area which supposed to contain the buffer.
282 * @param offBuffer The buffer location in the area.
283 * @param pBufferContext Where to write information about the buffer.
[22619]284 */
[71591]285static int hgsmiVerifyBuffer(const HGSMIAREA *pArea, HGSMIOFFSET offBuffer, HGSMIBUFFERCONTEXT *pBufferContext)
[22619]286{
[64156]287 // LogFlowFunc(("buffer 0x%x, area %p %x [0x%x;0x%x]\n",
288 // offBuffer, pArea->pu8Base, pArea->cbArea, pArea->offBase, pArea->offLast));
[22619]289
[55342]290 int rc = VINF_SUCCESS;
[22619]291
292 if ( offBuffer < pArea->offBase
293 || offBuffer > pArea->offLast)
294 {
[64156]295 // LogFunc(("offset 0x%x is outside the area [0x%x;0x%x]!!!\n",
296 // offBuffer, pArea->offBase, pArea->offLast));
[55342]297 rc = VERR_INVALID_PARAMETER;
[22619]298 HGSMI_STRICT_ASSERT_FAILED();
299 }
[55342]300 else
[22619]301 {
[71591]302 void RT_UNTRUSTED_VOLATILE_HSTGST *pvBuffer = HGSMIOffsetToPointer(pArea, offBuffer);
303 HGSMIBUFFERHEADER header;
304 memcpy(&header, (void *)HGSMIBufferHeaderFromPtr(pvBuffer), sizeof(header));
[70605]305 ASMCompilerBarrier();
[22619]306
[55342]307 /* Quick check of the data size, it should be less than the maximum
308 * data size for the buffer at this offset.
309 */
[64156]310 // LogFlowFunc(("datasize check: header.u32DataSize = 0x%x pArea->offLast - offBuffer = 0x%x\n",
311 // header.u32DataSize, pArea->offLast - offBuffer));
[22619]312
[55342]313 if (header.u32DataSize <= pArea->offLast - offBuffer)
[22619]314 {
[71591]315 HGSMIBUFFERTAIL tail;
316 memcpy(&tail, (void *)HGSMIBufferTailFromPtr(pvBuffer, header.u32DataSize), sizeof(tail));
[70605]317 ASMCompilerBarrier();
[55342]318
319 /* At least both header and tail structures are in the area. Check the checksum. */
320 uint32_t u32Checksum = HGSMIChecksum(offBuffer, &header, &tail);
[64156]321 // LogFlowFunc(("checksum check: u32Checksum = 0x%x pTail->u32Checksum = 0x%x\n",
322 // u32Checksum, tail.u32Checksum));
[55342]323 if (u32Checksum == tail.u32Checksum)
324 {
325 /* Success. */
326 pBufferContext->pHeader = HGSMIBufferHeaderFromPtr(pvBuffer);
327 pBufferContext->pvData = HGSMIBufferDataFromPtr(pvBuffer);
328 pBufferContext->cbData = header.u32DataSize;
329 }
330 else
331 {
[64156]332 // LogFunc(("invalid checksum 0x%x, expected 0x%x!!!\n",
333 // u32Checksum, tail.u32Checksum));
[55342]334 rc = VERR_INVALID_STATE;
335 HGSMI_STRICT_ASSERT_FAILED();
336 }
[22619]337 }
338 else
339 {
[64156]340 // LogFunc(("invalid data size 0x%x, maximum is 0x%x!!!\n",
341 // header.u32DataSize, pArea->offLast - offBuffer));
[55342]342 rc = VERR_TOO_MUCH_DATA;
[22619]343 HGSMI_STRICT_ASSERT_FAILED();
344 }
345 }
346
[55342]347 return rc;
[22619]348}
349
[55342]350/** Helper to convert HGSMI channel index to the channel structure pointer.
351 *
352 * @returns Pointer to the channel data.
353 * @param pChannelInfo The channel pool.
354 * @param u8Channel The channel index.
[22619]355 */
[55342]356HGSMICHANNEL *HGSMIChannelFindById(HGSMICHANNELINFO *pChannelInfo,
357 uint8_t u8Channel)
[22619]358{
[55342]359 AssertCompile(RT_ELEMENTS(pChannelInfo->Channels) >= 0x100);
[22619]360 HGSMICHANNEL *pChannel = &pChannelInfo->Channels[u8Channel];
361
362 if (pChannel->u8Flags & HGSMI_CH_F_REGISTERED)
363 {
364 return pChannel;
365 }
366
367 return NULL;
368}
369
[55342]370/** Process a guest buffer.
371 *
[58170]372 * @returns VBox status code.
[55342]373 * @param pArea Area which supposed to contain the buffer.
374 * @param pChannelInfo The channel pool.
375 * @param offBuffer The buffer location in the area.
376 */
[55474]377int HGSMIBufferProcess(const HGSMIAREA *pArea,
[55342]378 HGSMICHANNELINFO *pChannelInfo,
379 HGSMIOFFSET offBuffer)
[22619]380{
[64156]381 // LogFlowFunc(("pArea %p, offBuffer 0x%x\n", pArea, offBuffer));
[22619]382
[64650]383 HGSMI_ASSERT_PTR_RETURN(pArea, VERR_INVALID_PARAMETER);
384 HGSMI_ASSERT_PTR_RETURN(pChannelInfo, VERR_INVALID_PARAMETER);
[22619]385
386 /* Guest has prepared a command description at 'offBuffer'. */
[63546]387 HGSMIBUFFERCONTEXT bufferContext = { NULL, NULL, 0 }; /* Makes old GCC happier. */
[55342]388 int rc = hgsmiVerifyBuffer(pArea, offBuffer, &bufferContext);
389 if (RT_SUCCESS(rc))
[22619]390 {
391 /* Pass the command to the appropriate handler registered with this instance.
392 * Start with the handler list head, which is the preallocated HGSMI setup channel.
393 */
[55474]394 const HGSMICHANNEL *pChannel = HGSMIChannelFindById(pChannelInfo, bufferContext.pHeader->u8Channel);
[22619]395 if (pChannel)
396 {
[55474]397 const HGSMICHANNELHANDLER *pHandler = &pChannel->handler;
398 if (pHandler->pfnHandler)
399 {
400 pHandler->pfnHandler(pHandler->pvHandler, bufferContext.pHeader->u16ChannelInfo,
401 bufferContext.pvData, bufferContext.cbData);
402 }
[55342]403 HGSMI_STRICT_ASSERT(RT_SUCCESS(hgsmiVerifyBuffer(pArea, offBuffer, &bufferContext)));
[22619]404 }
405 else
406 {
[26514]407 rc = VERR_INVALID_FUNCTION;
[55342]408 HGSMI_STRICT_ASSERT_FAILED();
[22619]409 }
410 }
[55342]411
[22619]412 return rc;
413}
414
[55342]415/** Register a new HGSMI channel by index.
[22619]416 *
[58170]417 * @returns VBox status code.
[55342]418 * @param pChannelInfo The channel pool managed by the caller.
419 * @param u8Channel Index of the channel.
420 * @param pszName Name of the channel (optional, allocated by the caller).
421 * @param pfnChannelHandler The channel callback.
422 * @param pvChannelHandler The callback pointer.
[22619]423 */
[55342]424int HGSMIChannelRegister(HGSMICHANNELINFO *pChannelInfo,
425 uint8_t u8Channel,
426 const char *pszName,
427 PFNHGSMICHANNELHANDLER pfnChannelHandler,
428 void *pvChannelHandler)
[22619]429{
430 /* Check whether the channel is already registered. */
[55342]431 HGSMICHANNEL *pChannel = HGSMIChannelFindById(pChannelInfo, u8Channel);
432 if (pChannel)
[22619]433 {
[55342]434 HGSMI_STRICT_ASSERT_FAILED();
435 return VERR_ALREADY_EXISTS;
436 }
[22619]437
[55342]438 /* Channel is not yet registered. */
439 pChannel = &pChannelInfo->Channels[u8Channel];
[22619]440
[55342]441 pChannel->u8Flags = HGSMI_CH_F_REGISTERED;
442 pChannel->u8Channel = u8Channel;
[22619]443
444 pChannel->handler.pfnHandler = pfnChannelHandler;
445 pChannel->handler.pvHandler = pvChannelHandler;
446
[55342]447 pChannel->pszName = pszName;
448
[22619]449 return VINF_SUCCESS;
450}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use