VirtualBox

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

Last change on this file since 69222 was 66544, checked in by vboxsync, 8 years ago

bugref:8524: Additions/linux: play nicely with distribution-installed Additions
Change header of files which are expected to end up in the Linux kernel to the MIT licence to simplify life for people wanting to port vboxvideo to other kernels and to simplify synchronising changes back to VirtualBox. Update author information in files which have it, but do not add it to files which do not.

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