VirtualBox

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

Last change on this file since 65129 was 64650, checked in by vboxsync, 8 years ago

bugref:8614: Additions/common/VBoxVideo: make the code more self-contained: my last attempt at removing all assertions from debug versions of the Linux video driver was not correct: I missed some places, and forgot that LINUX_VERSION_CODE is only defined when Linux headers are included. Hopefully fix that.

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