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