VirtualBox

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

Last change on this file since 64484 was 64164, checked in by vboxsync, 8 years ago

#8614: Additions/common/VBoxVideo: make the code more self-contained: remove some more unneeded header files from drm vboxvideo.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 KB
Line 
1/* $Id: HGSMICommon.cpp 64164 2016-10-06 14:02:52Z 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-2016 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/** @note commented out all logging statements to avoid pulling the logging
20 * sub-system into places like the Linux kernel driver. Perhaps the best
21 * thing would be to use return enough information for callers to log what
22 * is needed. */
23#define LOG_GROUP LOG_GROUP_HGSMI
24
25#include <iprt/string.h>
26
27#include <VBox/HGSMI/HGSMI.h>
28// #include <VBox/log.h>
29
30
31/* Channel flags. */
32#define HGSMI_CH_F_REGISTERED 0x01
33
34/* Assertions for situations which could happen and normally must be processed properly
35 * but must be investigated during development: guest misbehaving, etc.
36 */
37#ifdef HGSMI_STRICT
38#define HGSMI_STRICT_ASSERT_FAILED() AssertFailed()
39#define HGSMI_STRICT_ASSERT(expr) Assert(expr)
40#else
41#define HGSMI_STRICT_ASSERT_FAILED() do {} while (0)
42#define HGSMI_STRICT_ASSERT(expr) do {} while (0)
43#endif /* !HGSMI_STRICT */
44
45/* One-at-a-Time Hash from
46 * http://www.burtleburtle.net/bob/hash/doobs.html
47 *
48 * ub4 one_at_a_time(char *key, ub4 len)
49 * {
50 * ub4 hash, i;
51 * for (hash=0, i=0; i<len; ++i)
52 * {
53 * hash += key[i];
54 * hash += (hash << 10);
55 * hash ^= (hash >> 6);
56 * }
57 * hash += (hash << 3);
58 * hash ^= (hash >> 11);
59 * hash += (hash << 15);
60 * return hash;
61 * }
62 */
63
64static uint32_t hgsmiHashBegin(void)
65{
66 return 0;
67}
68
69static uint32_t hgsmiHashProcess(uint32_t hash,
70 const void *pvData,
71 size_t cbData)
72{
73 const uint8_t *pu8Data = (const uint8_t *)pvData;
74
75 while (cbData--)
76 {
77 hash += *pu8Data++;
78 hash += (hash << 10);
79 hash ^= (hash >> 6);
80 }
81
82 return hash;
83}
84
85static uint32_t hgsmiHashEnd(uint32_t hash)
86{
87 hash += (hash << 3);
88 hash ^= (hash >> 11);
89 hash += (hash << 15);
90
91 return hash;
92}
93
94uint32_t HGSMIChecksum(HGSMIOFFSET offBuffer,
95 const HGSMIBUFFERHEADER *pHeader,
96 const HGSMIBUFFERTAIL *pTail)
97{
98 uint32_t u32Checksum = hgsmiHashBegin();
99
100 u32Checksum = hgsmiHashProcess(u32Checksum, &offBuffer, sizeof(offBuffer));
101 u32Checksum = hgsmiHashProcess(u32Checksum, pHeader, sizeof(HGSMIBUFFERHEADER));
102 u32Checksum = hgsmiHashProcess(u32Checksum, pTail, RT_OFFSETOF(HGSMIBUFFERTAIL, u32Checksum));
103
104 return hgsmiHashEnd(u32Checksum);
105}
106
107int HGSMIAreaInitialize(HGSMIAREA *pArea,
108 void *pvBase,
109 HGSMISIZE cbArea,
110 HGSMIOFFSET offBase)
111{
112 uint8_t *pu8Base = (uint8_t *)pvBase;
113
114 if ( !pArea /* Check that the area: */
115 || cbArea < HGSMIBufferMinimumSize() /* large enough; */
116 || pu8Base + cbArea < pu8Base /* no address space wrap; */
117 || offBase > UINT32_C(0xFFFFFFFF) - cbArea /* area within the 32 bit space: offBase + cbMem <= 0xFFFFFFFF. */
118 )
119 {
120 return VERR_INVALID_PARAMETER;
121 }
122
123 pArea->pu8Base = pu8Base;
124 pArea->offBase = offBase;
125 pArea->offLast = cbArea - HGSMIBufferMinimumSize() + offBase;
126 pArea->cbArea = cbArea;
127
128 return VINF_SUCCESS;
129}
130
131void HGSMIAreaClear(HGSMIAREA *pArea)
132{
133 if (pArea)
134 {
135 RT_ZERO(*pArea);
136 }
137}
138
139/* Initialize the memory buffer including its checksum.
140 * No changes alloed to the header and the tail after that.
141 */
142HGSMIOFFSET HGSMIBufferInitializeSingle(const HGSMIAREA *pArea,
143 HGSMIBUFFERHEADER *pHeader,
144 HGSMISIZE cbBuffer,
145 uint8_t u8Channel,
146 uint16_t u16ChannelInfo)
147{
148 if ( !pArea
149 || !pHeader
150 || cbBuffer < HGSMIBufferMinimumSize())
151 {
152 return HGSMIOFFSET_VOID;
153 }
154
155 /* Buffer must be within the area:
156 * * header data size do not exceed the maximum data size;
157 * * buffer address is greater than the area base address;
158 * * buffer address is lower than the maximum allowed for the given data size.
159 */
160 HGSMISIZE cbMaximumDataSize = pArea->offLast - pArea->offBase;
161 uint32_t u32DataSize = cbBuffer - HGSMIBufferMinimumSize();
162
163 if ( u32DataSize > cbMaximumDataSize
164 || (uint8_t *)pHeader < pArea->pu8Base
165 || (uint8_t *)pHeader > pArea->pu8Base + cbMaximumDataSize - u32DataSize)
166 {
167 return HGSMIOFFSET_VOID;
168 }
169
170 HGSMIOFFSET offBuffer = HGSMIPointerToOffset(pArea, pHeader);
171
172 pHeader->u8Flags = HGSMI_BUFFER_HEADER_F_SEQ_SINGLE;
173 pHeader->u32DataSize = u32DataSize;
174 pHeader->u8Channel = u8Channel;
175 pHeader->u16ChannelInfo = u16ChannelInfo;
176 RT_ZERO(pHeader->u.au8Union);
177
178 HGSMIBUFFERTAIL *pTail = HGSMIBufferTailFromPtr(pHeader, u32DataSize);
179 pTail->u32Reserved = 0;
180 pTail->u32Checksum = HGSMIChecksum(offBuffer, pHeader, pTail);
181
182 return offBuffer;
183}
184
185int HGSMIHeapSetup(HGSMIHEAP *pHeap,
186 void *pvBase,
187 HGSMISIZE cbArea,
188 HGSMIOFFSET offBase,
189 const HGSMIENV *pEnv)
190{
191 AssertPtrReturn(pHeap, VERR_INVALID_PARAMETER);
192 AssertPtrReturn(pvBase, VERR_INVALID_PARAMETER);
193
194 int rc = HGSMIAreaInitialize(&pHeap->area, pvBase, cbArea, offBase);
195 if (RT_SUCCESS(rc))
196 {
197 rc = HGSMIMAInit(&pHeap->ma, &pHeap->area, NULL, 0, 0, pEnv);
198 if (RT_FAILURE(rc))
199 {
200 HGSMIAreaClear(&pHeap->area);
201 }
202 }
203
204 return rc;
205}
206
207void HGSMIHeapDestroy(HGSMIHEAP *pHeap)
208{
209 if (pHeap)
210 {
211 HGSMIMAUninit(&pHeap->ma);
212 RT_ZERO(*pHeap);
213 }
214}
215
216void *HGSMIHeapAlloc(HGSMIHEAP *pHeap,
217 HGSMISIZE cbData,
218 uint8_t u8Channel,
219 uint16_t u16ChannelInfo)
220{
221 HGSMISIZE cbAlloc = HGSMIBufferRequiredSize(cbData);
222 HGSMIBUFFERHEADER *pHeader = (HGSMIBUFFERHEADER *)HGSMIHeapBufferAlloc(pHeap, cbAlloc);
223 if (pHeader)
224 {
225 HGSMIOFFSET offBuffer = HGSMIBufferInitializeSingle(HGSMIHeapArea(pHeap), pHeader,
226 cbAlloc, u8Channel, u16ChannelInfo);
227 if (offBuffer == HGSMIOFFSET_VOID)
228 {
229 HGSMIHeapBufferFree(pHeap, pHeader);
230 pHeader = NULL;
231 }
232 }
233
234 return pHeader? HGSMIBufferDataFromPtr(pHeader): NULL;
235}
236
237void HGSMIHeapFree(HGSMIHEAP *pHeap,
238 void *pvData)
239{
240 if (pvData)
241 {
242 HGSMIBUFFERHEADER *pHeader = HGSMIBufferHeaderFromData(pvData);
243 HGSMIHeapBufferFree(pHeap, pHeader);
244 }
245}
246
247void *HGSMIHeapBufferAlloc(HGSMIHEAP *pHeap,
248 HGSMISIZE cbBuffer)
249{
250 void *pvBuf = HGSMIMAAlloc(&pHeap->ma, cbBuffer);
251 return pvBuf;
252}
253
254void HGSMIHeapBufferFree(HGSMIHEAP *pHeap,
255 void *pvBuf)
256{
257 HGSMIMAFree(&pHeap->ma, pvBuf);
258}
259
260typedef struct HGSMIBUFFERCONTEXT
261{
262 const HGSMIBUFFERHEADER *pHeader; /* The original buffer header. */
263 void *pvData; /* Payload data in the buffer./ */
264 uint32_t cbData; /* Size of data */
265} HGSMIBUFFERCONTEXT;
266
267/** Verify that the given offBuffer points to a valid buffer, which is within the area.
268 *
269 * @returns VBox status and the buffer information in pBufferContext.
270 * @param pArea Area which supposed to contain the buffer.
271 * @param offBuffer The buffer location in the area.
272 * @param pBufferContext Where to write information about the buffer.
273 */
274static int hgsmiVerifyBuffer(const HGSMIAREA *pArea,
275 HGSMIOFFSET offBuffer,
276 HGSMIBUFFERCONTEXT *pBufferContext)
277{
278 // LogFlowFunc(("buffer 0x%x, area %p %x [0x%x;0x%x]\n",
279 // offBuffer, pArea->pu8Base, pArea->cbArea, pArea->offBase, pArea->offLast));
280
281 int rc = VINF_SUCCESS;
282
283 if ( offBuffer < pArea->offBase
284 || offBuffer > pArea->offLast)
285 {
286 // LogFunc(("offset 0x%x is outside the area [0x%x;0x%x]!!!\n",
287 // offBuffer, pArea->offBase, pArea->offLast));
288 rc = VERR_INVALID_PARAMETER;
289 HGSMI_STRICT_ASSERT_FAILED();
290 }
291 else
292 {
293 void *pvBuffer = HGSMIOffsetToPointer(pArea, offBuffer);
294 HGSMIBUFFERHEADER header = *HGSMIBufferHeaderFromPtr(pvBuffer);
295
296 /* Quick check of the data size, it should be less than the maximum
297 * data size for the buffer at this offset.
298 */
299 // LogFlowFunc(("datasize check: header.u32DataSize = 0x%x pArea->offLast - offBuffer = 0x%x\n",
300 // header.u32DataSize, pArea->offLast - offBuffer));
301
302 if (header.u32DataSize <= pArea->offLast - offBuffer)
303 {
304 HGSMIBUFFERTAIL tail = *HGSMIBufferTailFromPtr(pvBuffer, header.u32DataSize);
305
306 /* At least both header and tail structures are in the area. Check the checksum. */
307 uint32_t u32Checksum = HGSMIChecksum(offBuffer, &header, &tail);
308 // LogFlowFunc(("checksum check: u32Checksum = 0x%x pTail->u32Checksum = 0x%x\n",
309 // u32Checksum, tail.u32Checksum));
310 if (u32Checksum == tail.u32Checksum)
311 {
312 /* Success. */
313 pBufferContext->pHeader = HGSMIBufferHeaderFromPtr(pvBuffer);
314 pBufferContext->pvData = HGSMIBufferDataFromPtr(pvBuffer);
315 pBufferContext->cbData = header.u32DataSize;
316 }
317 else
318 {
319 // LogFunc(("invalid checksum 0x%x, expected 0x%x!!!\n",
320 // u32Checksum, tail.u32Checksum));
321 rc = VERR_INVALID_STATE;
322 HGSMI_STRICT_ASSERT_FAILED();
323 }
324 }
325 else
326 {
327 // LogFunc(("invalid data size 0x%x, maximum is 0x%x!!!\n",
328 // header.u32DataSize, pArea->offLast - offBuffer));
329 rc = VERR_TOO_MUCH_DATA;
330 HGSMI_STRICT_ASSERT_FAILED();
331 }
332 }
333
334 return rc;
335}
336
337/** Helper to convert HGSMI channel index to the channel structure pointer.
338 *
339 * @returns Pointer to the channel data.
340 * @param pChannelInfo The channel pool.
341 * @param u8Channel The channel index.
342 */
343HGSMICHANNEL *HGSMIChannelFindById(HGSMICHANNELINFO *pChannelInfo,
344 uint8_t u8Channel)
345{
346 AssertCompile(RT_ELEMENTS(pChannelInfo->Channels) >= 0x100);
347 HGSMICHANNEL *pChannel = &pChannelInfo->Channels[u8Channel];
348
349 if (pChannel->u8Flags & HGSMI_CH_F_REGISTERED)
350 {
351 return pChannel;
352 }
353
354 return NULL;
355}
356
357/** Process a guest buffer.
358 *
359 * @returns VBox status code.
360 * @param pArea Area which supposed to contain the buffer.
361 * @param pChannelInfo The channel pool.
362 * @param offBuffer The buffer location in the area.
363 */
364int HGSMIBufferProcess(const HGSMIAREA *pArea,
365 HGSMICHANNELINFO *pChannelInfo,
366 HGSMIOFFSET offBuffer)
367{
368 // LogFlowFunc(("pArea %p, offBuffer 0x%x\n", pArea, offBuffer));
369
370 AssertPtrReturn(pArea, VERR_INVALID_PARAMETER);
371 AssertPtrReturn(pChannelInfo, VERR_INVALID_PARAMETER);
372
373 /* Guest has prepared a command description at 'offBuffer'. */
374 HGSMIBUFFERCONTEXT bufferContext = { NULL, NULL, 0 }; /* Makes old GCC happier. */
375 int rc = hgsmiVerifyBuffer(pArea, offBuffer, &bufferContext);
376 if (RT_SUCCESS(rc))
377 {
378 /* Pass the command to the appropriate handler registered with this instance.
379 * Start with the handler list head, which is the preallocated HGSMI setup channel.
380 */
381 const HGSMICHANNEL *pChannel = HGSMIChannelFindById(pChannelInfo, bufferContext.pHeader->u8Channel);
382 if (pChannel)
383 {
384 const HGSMICHANNELHANDLER *pHandler = &pChannel->handler;
385 if (pHandler->pfnHandler)
386 {
387 pHandler->pfnHandler(pHandler->pvHandler, bufferContext.pHeader->u16ChannelInfo,
388 bufferContext.pvData, bufferContext.cbData);
389 }
390 HGSMI_STRICT_ASSERT(RT_SUCCESS(hgsmiVerifyBuffer(pArea, offBuffer, &bufferContext)));
391 }
392 else
393 {
394 rc = VERR_INVALID_FUNCTION;
395 HGSMI_STRICT_ASSERT_FAILED();
396 }
397 }
398
399 return rc;
400}
401
402/** Register a new HGSMI channel by index.
403 *
404 * @returns VBox status code.
405 * @param pChannelInfo The channel pool managed by the caller.
406 * @param u8Channel Index of the channel.
407 * @param pszName Name of the channel (optional, allocated by the caller).
408 * @param pfnChannelHandler The channel callback.
409 * @param pvChannelHandler The callback pointer.
410 */
411int HGSMIChannelRegister(HGSMICHANNELINFO *pChannelInfo,
412 uint8_t u8Channel,
413 const char *pszName,
414 PFNHGSMICHANNELHANDLER pfnChannelHandler,
415 void *pvChannelHandler)
416{
417 /* Check whether the channel is already registered. */
418 HGSMICHANNEL *pChannel = HGSMIChannelFindById(pChannelInfo, u8Channel);
419 if (pChannel)
420 {
421 HGSMI_STRICT_ASSERT_FAILED();
422 return VERR_ALREADY_EXISTS;
423 }
424
425 /* Channel is not yet registered. */
426 pChannel = &pChannelInfo->Channels[u8Channel];
427
428 pChannel->u8Flags = HGSMI_CH_F_REGISTERED;
429 pChannel->u8Channel = u8Channel;
430
431 pChannel->handler.pfnHandler = pfnChannelHandler;
432 pChannel->handler.pvHandler = pvChannelHandler;
433
434 pChannel->pszName = pszName;
435
436 return VINF_SUCCESS;
437}
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