VirtualBox

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

Last change on this file since 106199 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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