VirtualBox

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

Last change on this file since 70693 was 70605, checked in by vboxsync, 7 years ago

DevVGA: cleanup in progress.

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