VirtualBox

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

Last change on this file since 55342 was 55342, checked in by vboxsync, 10 years ago

HGSMI: cleanup, comments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.7 KB
Line 
1/* $Id: HGSMICommon.cpp 55342 2015-04-20 14:19:53Z vboxsync $ */
2/** @file
3 * VBox Host Guest Shared Memory Interface (HGSMI) - Functions common to both host and guest.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_DISABLED /* Maybe we can enabled it all the time now? */
19#define LOG_GROUP LOG_GROUP_HGSMI
20#include <iprt/heap.h>
21#include <iprt/string.h>
22
23#include <VBox/HGSMI/HGSMI.h>
24#include <VBox/log.h>
25
26
27/* Channel flags. */
28#define HGSMI_CH_F_REGISTERED 0x01
29
30/* Assertions for situations which could happen and normally must be processed properly
31 * but must be investigated during development: guest misbehaving, etc.
32 */
33#ifdef HGSMI_STRICT
34#define HGSMI_STRICT_ASSERT_FAILED() AssertFailed()
35#define HGSMI_STRICT_ASSERT(expr) Assert(expr)
36#else
37#define HGSMI_STRICT_ASSERT_FAILED() do {} while (0)
38#define HGSMI_STRICT_ASSERT(expr) do {} while (0)
39#endif /* !HGSMI_STRICT */
40
41/* One-at-a-Time Hash from
42 * http://www.burtleburtle.net/bob/hash/doobs.html
43 *
44 * ub4 one_at_a_time(char *key, ub4 len)
45 * {
46 * ub4 hash, i;
47 * for (hash=0, i=0; i<len; ++i)
48 * {
49 * hash += key[i];
50 * hash += (hash << 10);
51 * hash ^= (hash >> 6);
52 * }
53 * hash += (hash << 3);
54 * hash ^= (hash >> 11);
55 * hash += (hash << 15);
56 * return hash;
57 * }
58 */
59
60static uint32_t hgsmiHashBegin (void)
61{
62 return 0;
63}
64
65static uint32_t hgsmiHashProcess (uint32_t hash,
66 const void *pvData,
67 size_t cbData)
68{
69 const uint8_t *pu8Data = (const uint8_t *)pvData;
70
71 while (cbData--)
72 {
73 hash += *pu8Data++;
74 hash += (hash << 10);
75 hash ^= (hash >> 6);
76 }
77
78 return hash;
79}
80
81static uint32_t hgsmiHashEnd (uint32_t hash)
82{
83 hash += (hash << 3);
84 hash ^= (hash >> 11);
85 hash += (hash << 15);
86
87 return hash;
88}
89
90uint32_t HGSMIChecksum (HGSMIOFFSET offBuffer,
91 const HGSMIBUFFERHEADER *pHeader,
92 const HGSMIBUFFERTAIL *pTail)
93{
94 uint32_t u32Checksum = hgsmiHashBegin ();
95
96 u32Checksum = hgsmiHashProcess (u32Checksum, &offBuffer, sizeof (offBuffer));
97 u32Checksum = hgsmiHashProcess (u32Checksum, pHeader, sizeof (HGSMIBUFFERHEADER));
98 u32Checksum = hgsmiHashProcess (u32Checksum, pTail, RT_OFFSETOF(HGSMIBUFFERTAIL, u32Checksum));
99
100 return hgsmiHashEnd (u32Checksum);
101}
102
103static HGSMIOFFSET hgsmiBufferInitializeSingle (const HGSMIAREA *pArea,
104 HGSMIBUFFERHEADER *pHeader,
105 uint32_t u32DataSize,
106 uint8_t u8Channel,
107 uint16_t u16ChannelInfo)
108{
109 if ( !pArea
110 || !pHeader)
111 {
112 return HGSMIOFFSET_VOID;
113 }
114
115 /* Buffer must be within the area:
116 * * header data size do not exceed the maximum data size;
117 * * buffer address is greater than the area base address;
118 * * buffer address is lower than the maximum allowed for the given data size.
119 */
120 HGSMISIZE cbMaximumDataSize = pArea->offLast - pArea->offBase;
121
122 if ( u32DataSize > cbMaximumDataSize
123 || (uint8_t *)pHeader < pArea->pu8Base
124 || (uint8_t *)pHeader > pArea->pu8Base + cbMaximumDataSize - u32DataSize)
125 {
126 return HGSMIOFFSET_VOID;
127 }
128
129 HGSMIOFFSET offBuffer = HGSMIPointerToOffset (pArea, pHeader);
130
131 pHeader->u8Flags = HGSMI_BUFFER_HEADER_F_SEQ_SINGLE;
132 pHeader->u32DataSize = u32DataSize;
133 pHeader->u8Channel = u8Channel;
134 pHeader->u16ChannelInfo = u16ChannelInfo;
135 memset (pHeader->u.au8Union, 0, sizeof (pHeader->u.au8Union));
136
137 HGSMIBUFFERTAIL *pTail = HGSMIBufferTail (pHeader);
138
139 pTail->u32Reserved = 0;
140 pTail->u32Checksum = HGSMIChecksum (offBuffer, pHeader, pTail);
141
142 return offBuffer;
143}
144
145int HGSMIAreaInitialize (HGSMIAREA *pArea, void *pvBase, HGSMISIZE cbArea, HGSMIOFFSET offBase)
146{
147 uint8_t *pu8Base = (uint8_t *)pvBase;
148
149 if ( !pArea /* Check that the area: */
150 || cbArea < HGSMIBufferMinimumSize () /* Large enough. */
151 || pu8Base + cbArea < pu8Base /* No address space wrap. */
152 || offBase > UINT32_C(0xFFFFFFFF) - cbArea /* Area within the 32 bit space: offBase + cbMem <= 0xFFFFFFFF */
153 )
154 {
155 return VERR_INVALID_PARAMETER;
156 }
157
158 pArea->pu8Base = pu8Base;
159 pArea->offBase = offBase;
160 pArea->offLast = cbArea - HGSMIBufferMinimumSize () + offBase;
161 pArea->cbArea = cbArea;
162
163 return VINF_SUCCESS;
164}
165
166void HGSMIAreaClear (HGSMIAREA *pArea)
167{
168 if (pArea)
169 {
170 memset (pArea, 0, sizeof (HGSMIAREA));
171 }
172}
173
174/* Initialize the memory buffer including its checksum.
175 * No changes alloed to the header and the tail after that.
176 */
177HGSMIOFFSET HGSMIBufferInitializeSingle (const HGSMIAREA *pArea,
178 HGSMIBUFFERHEADER *pHeader,
179 HGSMISIZE cbBuffer,
180 uint8_t u8Channel,
181 uint16_t u16ChannelInfo)
182{
183 if (cbBuffer < HGSMIBufferMinimumSize ())
184 {
185 return HGSMIOFFSET_VOID;
186 }
187
188 return hgsmiBufferInitializeSingle (pArea, pHeader, cbBuffer - HGSMIBufferMinimumSize (), u8Channel, u16ChannelInfo);
189}
190
191void HGSMIHeapSetupUninitialized(HGSMIHEAP *pHeap)
192{
193 RT_ZERO(*pHeap);
194 pHeap->u32HeapType = HGSMI_HEAP_TYPE_NULL;
195}
196
197int HGSMIHeapRelocate (HGSMIHEAP *pHeap,
198 uint32_t u32HeapType,
199 void *pvBase,
200 uint32_t offHeapHandle,
201 uintptr_t offDelta,
202 HGSMISIZE cbArea,
203 HGSMIOFFSET offBase)
204{
205 if ( !pHeap
206 || !pvBase)
207 {
208 return VERR_INVALID_PARAMETER;
209 }
210
211 int rc = HGSMIAreaInitialize (&pHeap->area, pvBase, cbArea, offBase);
212
213 if (RT_SUCCESS (rc))
214 {
215 if (u32HeapType == HGSMI_HEAP_TYPE_OFFSET)
216 {
217 pHeap->u.hOff = (RTHEAPOFFSET)((uint8_t *)pvBase + offHeapHandle);
218 }
219 else if (u32HeapType == HGSMI_HEAP_TYPE_POINTER)
220 {
221 pHeap->u.hPtr = (RTHEAPSIMPLE)((uint8_t *)pvBase + offHeapHandle);
222 rc = RTHeapSimpleRelocate (pHeap->u.hPtr, offDelta); AssertRC(rc);
223 }
224 else
225 {
226 /* HGSMI_HEAP_TYPE_MA does not need the relocation. */
227 rc = VERR_NOT_SUPPORTED;
228 }
229
230 if (RT_SUCCESS(rc))
231 {
232 pHeap->cRefs = 0;
233 pHeap->u32HeapType = u32HeapType;
234 }
235 else
236 {
237 HGSMIAreaClear (&pHeap->area);
238 }
239 }
240
241 return rc;
242}
243
244int HGSMIHeapRestoreMA(HGSMIHEAP *pHeap,
245 void *pvBase,
246 HGSMISIZE cbArea,
247 HGSMIOFFSET offBase,
248 uint32_t cBlocks,
249 HGSMIOFFSET *paDescriptors,
250 HGSMISIZE cbMaxBlock,
251 HGSMIENV *pEnv)
252{
253 int rc = HGSMIAreaInitialize(&pHeap->area, pvBase, cbArea, offBase);
254
255 if (RT_SUCCESS (rc))
256 {
257 rc = HGSMIMAInit(&pHeap->u.ma, &pHeap->area, paDescriptors, cBlocks, cbMaxBlock, pEnv);
258
259 if (RT_SUCCESS(rc))
260 {
261 pHeap->cRefs = 0;
262 pHeap->u32HeapType = HGSMI_HEAP_TYPE_MA;
263 }
264 else
265 {
266 HGSMIAreaClear(&pHeap->area);
267 }
268 }
269
270 return rc;
271}
272
273int HGSMIHeapSetup (HGSMIHEAP *pHeap,
274 uint32_t u32HeapType,
275 void *pvBase,
276 HGSMISIZE cbArea,
277 HGSMIOFFSET offBase,
278 const HGSMIENV *pEnv)
279{
280 if ( !pHeap
281 || !pvBase)
282 {
283 return VERR_INVALID_PARAMETER;
284 }
285
286 int rc = HGSMIAreaInitialize (&pHeap->area, pvBase, cbArea, offBase);
287
288 if (RT_SUCCESS (rc))
289 {
290 if (u32HeapType == HGSMI_HEAP_TYPE_MA)
291 {
292 rc = HGSMIMAInit(&pHeap->u.ma, &pHeap->area, NULL, 0, 0, pEnv);
293 }
294 else if (u32HeapType == HGSMI_HEAP_TYPE_POINTER)
295 {
296 rc = RTHeapSimpleInit (&pHeap->u.hPtr, pvBase, cbArea);
297 }
298 else if (u32HeapType == HGSMI_HEAP_TYPE_OFFSET)
299 {
300 rc = RTHeapOffsetInit (&pHeap->u.hOff, pvBase, cbArea);
301 }
302 else
303 {
304 rc = VERR_NOT_SUPPORTED;
305 }
306
307 if (RT_SUCCESS (rc))
308 {
309 pHeap->cRefs = 0;
310 pHeap->u32HeapType = u32HeapType;
311 }
312 else
313 {
314 HGSMIAreaClear (&pHeap->area);
315 }
316 }
317
318 return rc;
319}
320
321void HGSMIHeapDestroy (HGSMIHEAP *pHeap)
322{
323 if (pHeap)
324 {
325 if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_MA)
326 {
327 HGSMIMAUninit(&pHeap->u.ma);
328 }
329 Assert(!pHeap->cRefs);
330 HGSMIHeapSetupUninitialized(pHeap);
331 }
332}
333
334void *HGSMIHeapAlloc (HGSMIHEAP *pHeap,
335 HGSMISIZE cbData,
336 uint8_t u8Channel,
337 uint16_t u16ChannelInfo)
338{
339 HGSMISIZE cbAlloc = HGSMIBufferRequiredSize (cbData);
340
341 HGSMIBUFFERHEADER *pHeader = (HGSMIBUFFERHEADER *)HGSMIHeapBufferAlloc (pHeap, cbAlloc);
342 if (!pHeader)
343 return NULL;
344
345 hgsmiBufferInitializeSingle (&pHeap->area, pHeader, cbData, u8Channel, u16ChannelInfo);
346
347 return HGSMIBufferData (pHeader);
348}
349
350HGSMIOFFSET HGSMIHeapBufferOffset (HGSMIHEAP *pHeap,
351 void *pvData)
352{
353 HGSMIBUFFERHEADER *pHeader = HGSMIBufferHeaderFromData (pvData);
354
355 HGSMIOFFSET offBuffer = HGSMIPointerToOffset (&pHeap->area, pHeader);
356
357 return offBuffer;
358}
359
360void HGSMIHeapFree (HGSMIHEAP *pHeap,
361 void *pvData)
362{
363 if ( pvData
364 && pHeap->u32HeapType != HGSMI_HEAP_TYPE_NULL)
365 {
366 HGSMIBUFFERHEADER *pHeader = HGSMIBufferHeaderFromData (pvData);
367
368 HGSMIHeapBufferFree (pHeap, pHeader);
369 }
370}
371
372void* HGSMIHeapBufferAlloc (HGSMIHEAP *pHeap, HGSMISIZE cbBuffer)
373{
374 void* pvBuf = NULL;
375 if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_MA)
376 {
377 pvBuf = HGSMIMAAlloc(&pHeap->u.ma, cbBuffer);
378 }
379 else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_POINTER)
380 {
381 pvBuf = RTHeapSimpleAlloc (pHeap->u.hPtr, cbBuffer, 0);
382 }
383 else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_OFFSET)
384 {
385 pvBuf = RTHeapOffsetAlloc (pHeap->u.hOff, cbBuffer, 0);
386 }
387
388 if (pvBuf)
389 {
390 ++pHeap->cRefs;
391 }
392
393 return pvBuf;
394}
395
396void HGSMIHeapBufferFree(HGSMIHEAP *pHeap,
397 void *pvBuf)
398{
399 if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_MA)
400 {
401 HGSMIMAFree(&pHeap->u.ma, pvBuf);
402 }
403 else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_POINTER)
404 {
405 RTHeapSimpleFree (pHeap->u.hPtr, pvBuf);
406 }
407 else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_OFFSET)
408 {
409 RTHeapOffsetFree (pHeap->u.hOff, pvBuf);
410 }
411
412 --pHeap->cRefs;
413}
414
415typedef struct HGSMIBUFFERCONTEXT
416{
417 const HGSMIBUFFERHEADER *pHeader; /* The original buffer header. */
418 void *pvData; /* Payload data in the buffer./ */
419 uint32_t cbData; /* Size of data */
420} HGSMIBUFFERCONTEXT;
421
422/* Verify that the given offBuffer points to a valid buffer, which is within the area.
423 */
424static int hgsmiVerifyBuffer(const HGSMIAREA *pArea,
425 HGSMIOFFSET offBuffer,
426 HGSMIBUFFERCONTEXT *pBufferContext)
427{
428 LogFlowFunc(("buffer 0x%x, area %p %x [0x%x;0x%x]\n",
429 offBuffer, pArea->pu8Base, pArea->cbArea, pArea->offBase, pArea->offLast));
430
431 int rc = VINF_SUCCESS;
432
433 if ( offBuffer < pArea->offBase
434 || offBuffer > pArea->offLast)
435 {
436 LogFunc(("offset 0x%x is outside the area [0x%x;0x%x]!!!\n",
437 offBuffer, pArea->offBase, pArea->offLast));
438 rc = VERR_INVALID_PARAMETER;
439 HGSMI_STRICT_ASSERT_FAILED();
440 }
441 else
442 {
443 void *pvBuffer = HGSMIOffsetToPointer(pArea, offBuffer);
444 HGSMIBUFFERHEADER header = *HGSMIBufferHeaderFromPtr(pvBuffer);
445
446 /* Quick check of the data size, it should be less than the maximum
447 * data size for the buffer at this offset.
448 */
449 LogFlowFunc(("datasize check: header.u32DataSize = 0x%x pArea->offLast - offBuffer = 0x%x\n",
450 header.u32DataSize, pArea->offLast - offBuffer));
451
452 if (header.u32DataSize <= pArea->offLast - offBuffer)
453 {
454 HGSMIBUFFERTAIL tail = *HGSMIBufferTailFromPtr(pvBuffer, header.u32DataSize);
455
456 /* At least both header and tail structures are in the area. Check the checksum. */
457 uint32_t u32Checksum = HGSMIChecksum(offBuffer, &header, &tail);
458 LogFlowFunc(("checksum check: u32Checksum = 0x%x pTail->u32Checksum = 0x%x\n",
459 u32Checksum, pTail->u32Checksum));
460 if (u32Checksum == tail.u32Checksum)
461 {
462 /* Success. */
463 pBufferContext->pHeader = HGSMIBufferHeaderFromPtr(pvBuffer);
464 pBufferContext->pvData = HGSMIBufferDataFromPtr(pvBuffer);
465 pBufferContext->cbData = header.u32DataSize;
466 }
467 else
468 {
469 LogFunc(("invalid checksum 0x%x, expected 0x%x!!!\n",
470 u32Checksum, pTail->u32Checksum));
471 rc = VERR_INVALID_STATE;
472 HGSMI_STRICT_ASSERT_FAILED();
473 }
474 }
475 else
476 {
477 LogFunc(("invalid data size 0x%x, maximum is 0x%x!!!\n",
478 header.u32DataSize, pArea->offLast - offBuffer));
479 rc = VERR_TOO_MUCH_DATA;
480 HGSMI_STRICT_ASSERT_FAILED();
481 }
482 }
483
484 return rc;
485}
486
487/* A wrapper to safely call the handler.
488 */
489static int hgsmiChannelHandlerCall(const HGSMICHANNELHANDLER *pHandler,
490 const HGSMIBUFFERCONTEXT *pBufferContext)
491{
492 LogFlowFunc(("pHandler %p\n", pHandler));
493
494 int rc;
495
496 if ( pHandler
497 && pHandler->pfnHandler)
498 {
499 rc = pHandler->pfnHandler(pHandler->pvHandler, pBufferContext->pHeader->u16ChannelInfo,
500 pBufferContext->pvData, pBufferContext->cbData);
501 }
502 else
503 {
504 /* It is a NOOP case here. */
505 rc = VINF_SUCCESS;
506 }
507
508 LogFlowFunc(("leave rc = %Rrc\n", rc));
509 return rc;
510}
511
512/** Helper to convert HGSMI channel index to the channel structure pointer.
513 *
514 * @returns Pointer to the channel data.
515 * @param pChannelInfo The channel pool.
516 * @param u8Channel The channel index.
517 */
518HGSMICHANNEL *HGSMIChannelFindById(HGSMICHANNELINFO *pChannelInfo,
519 uint8_t u8Channel)
520{
521 AssertCompile(RT_ELEMENTS(pChannelInfo->Channels) >= 0x100);
522 HGSMICHANNEL *pChannel = &pChannelInfo->Channels[u8Channel];
523
524 if (pChannel->u8Flags & HGSMI_CH_F_REGISTERED)
525 {
526 return pChannel;
527 }
528
529 return NULL;
530}
531
532/** Process a guest buffer.
533 *
534 * @returns VBox status.
535 * @param pArea Area which supposed to contain the buffer.
536 * @param pChannelInfo The channel pool.
537 * @param offBuffer The buffer location in the area.
538 */
539int HGSMIBufferProcess(HGSMIAREA *pArea,
540 HGSMICHANNELINFO *pChannelInfo,
541 HGSMIOFFSET offBuffer)
542{
543 LogFlowFunc(("pArea %p, offBuffer 0x%x\n", pArea, offBuffer));
544
545 AssertPtrReturn(pArea, VERR_INVALID_PARAMETER);
546 AssertPtrReturn(pChannelInfo, VERR_INVALID_PARAMETER);
547
548 /* Guest has prepared a command description at 'offBuffer'. */
549 HGSMIBUFFERCONTEXT bufferContext;
550 int rc = hgsmiVerifyBuffer(pArea, offBuffer, &bufferContext);
551 if (RT_SUCCESS(rc))
552 {
553 /* Pass the command to the appropriate handler registered with this instance.
554 * Start with the handler list head, which is the preallocated HGSMI setup channel.
555 */
556 HGSMICHANNEL *pChannel = HGSMIChannelFindById(pChannelInfo, bufferContext.pHeader->u8Channel);
557 if (pChannel)
558 {
559 rc = hgsmiChannelHandlerCall(&pChannel->handler, &bufferContext);
560 HGSMI_STRICT_ASSERT(RT_SUCCESS(hgsmiVerifyBuffer(pArea, offBuffer, &bufferContext)));
561 }
562 else
563 {
564 rc = VERR_INVALID_FUNCTION;
565 HGSMI_STRICT_ASSERT_FAILED();
566 }
567 }
568
569 return rc;
570}
571
572/** Register a new HGSMI channel by index.
573 *
574 * @returns VBox status.
575 * @param pChannelInfo The channel pool managed by the caller.
576 * @param u8Channel Index of the channel.
577 * @param pszName Name of the channel (optional, allocated by the caller).
578 * @param pfnChannelHandler The channel callback.
579 * @param pvChannelHandler The callback pointer.
580 */
581int HGSMIChannelRegister(HGSMICHANNELINFO *pChannelInfo,
582 uint8_t u8Channel,
583 const char *pszName,
584 PFNHGSMICHANNELHANDLER pfnChannelHandler,
585 void *pvChannelHandler)
586{
587 /* Check whether the channel is already registered. */
588 HGSMICHANNEL *pChannel = HGSMIChannelFindById(pChannelInfo, u8Channel);
589 if (pChannel)
590 {
591 HGSMI_STRICT_ASSERT_FAILED();
592 return VERR_ALREADY_EXISTS;
593 }
594
595 /* Channel is not yet registered. */
596 pChannel = &pChannelInfo->Channels[u8Channel];
597
598 pChannel->u8Flags = HGSMI_CH_F_REGISTERED;
599 pChannel->u8Channel = u8Channel;
600
601 pChannel->handler.pfnHandler = pfnChannelHandler;
602 pChannel->handler.pvHandler = pvChannelHandler;
603
604 pChannel->pszName = pszName;
605
606 return VINF_SUCCESS;
607}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette